project setup
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { usePathname } from "expo-router";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Animated, Platform, View } from "react-native";
|
||||
|
||||
import { useAppSelector, useAppDispatch } from "@/store/hooks";
|
||||
import {
|
||||
selectIsMobile,
|
||||
setSidebarCollapsed,
|
||||
} from "@/store/screen/screenSizeSlice";
|
||||
import { SignOutButton } from "@/components/auth/SignOutButton";
|
||||
import { useAuthContext } from "@/auth/AuthContext";
|
||||
import { SidebarToggle } from "@/components/features/navigation/SidebarToggle";
|
||||
import { SidebarItem } from "@/components/features/navigation/SidebarItem";
|
||||
|
||||
const SIDEBAR_STATE_KEY = "sidebarCollapsedState";
|
||||
|
||||
const saveCollapsedState = async (isCollapsed: boolean) => {
|
||||
try {
|
||||
if (Platform.OS === "web") {
|
||||
localStorage.setItem(SIDEBAR_STATE_KEY, JSON.stringify(isCollapsed));
|
||||
} else {
|
||||
await AsyncStorage.setItem(
|
||||
SIDEBAR_STATE_KEY,
|
||||
JSON.stringify(isCollapsed)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving sidebar state:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadCollapsedState = async (): Promise<boolean | null> => {
|
||||
try {
|
||||
let value;
|
||||
if (Platform.OS === "web") {
|
||||
value = localStorage.getItem(SIDEBAR_STATE_KEY);
|
||||
} else {
|
||||
value = await AsyncStorage.getItem(SIDEBAR_STATE_KEY);
|
||||
}
|
||||
return value ? JSON.parse(value) : null;
|
||||
} catch (error) {
|
||||
console.error("Error loading sidebar state:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const Sidebar = ({
|
||||
mobileHidden = false,
|
||||
onRequestMobileToggle,
|
||||
}: {
|
||||
mobileHidden?: boolean;
|
||||
onRequestMobileToggle?: () => void;
|
||||
}) => {
|
||||
const { isAuthenticated } = useAuthContext();
|
||||
const pathname = usePathname();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [labelsVisible, setLabelsVisible] = useState(true);
|
||||
const animatedWidth = useRef(new Animated.Value(200)).current;
|
||||
const textOpacity = useRef(new Animated.Value(1)).current;
|
||||
const iconTranslateX = useRef(new Animated.Value(0)).current;
|
||||
const paddingLeft = useRef(new Animated.Value(16)).current; // 0.5rem = 8px
|
||||
const isMobile = useAppSelector(selectIsMobile);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const loadSavedState = async () => {
|
||||
if (isMobile) {
|
||||
setCollapsed(true);
|
||||
setLabelsVisible(false);
|
||||
textOpacity.setValue(0);
|
||||
iconTranslateX.setValue(12);
|
||||
paddingLeft.setValue(8);
|
||||
Animated.timing(animatedWidth, {
|
||||
toValue: mobileHidden ? 0 : 80,
|
||||
duration: 250,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
return;
|
||||
}
|
||||
const savedState = await loadCollapsedState();
|
||||
if (savedState !== null) {
|
||||
setCollapsed(savedState);
|
||||
dispatch(setSidebarCollapsed(savedState));
|
||||
setLabelsVisible(!savedState);
|
||||
textOpacity.setValue(savedState ? 0 : 1);
|
||||
iconTranslateX.setValue(savedState ? 12 : 0);
|
||||
paddingLeft.setValue(8);
|
||||
Animated.timing(animatedWidth, {
|
||||
toValue: savedState ? 80 : 200,
|
||||
duration: 300,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
loadSavedState();
|
||||
}, [
|
||||
animatedWidth,
|
||||
textOpacity,
|
||||
iconTranslateX,
|
||||
paddingLeft,
|
||||
isMobile,
|
||||
mobileHidden,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
const isActive = (path: string) => {
|
||||
if (path === "/user-dashboard" || path === "/dashboard") {
|
||||
return (
|
||||
pathname.includes("/user-dashboard") ||
|
||||
pathname === "/(tabs)" ||
|
||||
pathname === "/"
|
||||
);
|
||||
}
|
||||
|
||||
if (path === "/accounts") {
|
||||
return pathname.includes("/accounts");
|
||||
}
|
||||
|
||||
if (path === "/settings") {
|
||||
return (
|
||||
pathname.startsWith("/settings") ||
|
||||
pathname.startsWith("/(tabs)/settings")
|
||||
);
|
||||
}
|
||||
|
||||
if (path === "/data-protection") {
|
||||
return (
|
||||
pathname.startsWith("/data-protection") ||
|
||||
pathname.startsWith("/(tabs)/data-protection")
|
||||
);
|
||||
}
|
||||
|
||||
return pathname.includes(path);
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
if (isMobile) {
|
||||
onRequestMobileToggle?.();
|
||||
return;
|
||||
}
|
||||
const newCollapsedState = !collapsed;
|
||||
|
||||
if (!newCollapsedState) {
|
||||
setLabelsVisible(true);
|
||||
}
|
||||
|
||||
Animated.parallel([
|
||||
Animated.timing(animatedWidth, {
|
||||
toValue: newCollapsedState ? 80 : 200,
|
||||
duration: 300,
|
||||
useNativeDriver: false,
|
||||
}),
|
||||
Animated.timing(textOpacity, {
|
||||
toValue: newCollapsedState ? 0 : 1,
|
||||
duration: 250,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(iconTranslateX, {
|
||||
toValue: newCollapsedState ? 12 : 0,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(paddingLeft, {
|
||||
toValue: 8,
|
||||
duration: 300,
|
||||
useNativeDriver: false,
|
||||
}),
|
||||
]).start(() => {
|
||||
setLabelsVisible(!newCollapsedState);
|
||||
});
|
||||
if (newCollapsedState) {
|
||||
setLabelsVisible(true);
|
||||
}
|
||||
|
||||
setCollapsed(newCollapsedState);
|
||||
dispatch(setSidebarCollapsed(newCollapsedState));
|
||||
saveCollapsedState(newCollapsedState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
className="border-r px-2"
|
||||
style={{
|
||||
width: animatedWidth,
|
||||
backgroundColor: "#49385D",
|
||||
borderRightColor: "#49385D",
|
||||
borderRightWidth: undefined,
|
||||
paddingHorizontal: undefined,
|
||||
flex: isMobile ? undefined : 1,
|
||||
height: "100%",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<View className="flex-1 flex-col">
|
||||
{!(isMobile && mobileHidden) && (
|
||||
<View
|
||||
className="border-b-2 border-white"
|
||||
style={{
|
||||
paddingBottom: isMobile ? 6 : 0,
|
||||
paddingTop: isMobile ? 6 : 0,
|
||||
}}
|
||||
>
|
||||
<SidebarToggle
|
||||
onPress={toggleSidebar}
|
||||
textOpacity={textOpacity}
|
||||
iconTranslateX={iconTranslateX}
|
||||
showLabel={labelsVisible}
|
||||
className="h-[40px] w-full justify-start"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!(isMobile && mobileHidden) && (
|
||||
<View>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/dashboard/user-dashboard"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/dashboard/user-dashboard"
|
||||
icon="home-outline"
|
||||
label="Home"
|
||||
isFirst
|
||||
/>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/(tabs)/accounts/users"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/accounts"
|
||||
icon="person-outline"
|
||||
label="Accounts"
|
||||
/>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/(tabs)/logs/system"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/logs"
|
||||
icon="checkbox-outline"
|
||||
label="Logs"
|
||||
/>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/(tabs)/reports/inbox"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/reports"
|
||||
icon="copy-outline"
|
||||
label="Reports"
|
||||
/>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/(tabs)/data-protection/general"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/data-protection"
|
||||
icon="lock-closed-outline"
|
||||
label="Data Protection"
|
||||
/>
|
||||
<SidebarItem
|
||||
paddingLeft={paddingLeft}
|
||||
routerPushValue={"/(tabs)/settings/general"}
|
||||
isMobile={isMobile}
|
||||
isActive={isActive}
|
||||
iconTranslateX={iconTranslateX}
|
||||
textOpacity={textOpacity}
|
||||
showLabel={labelsVisible}
|
||||
path="/settings"
|
||||
icon="settings-outline"
|
||||
label="Settings"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!(isMobile && mobileHidden) && (
|
||||
<View className="mb-4 mt-auto px-2">
|
||||
{isAuthenticated && (
|
||||
<SignOutButton
|
||||
textOpacity={textOpacity}
|
||||
iconTranslateX={iconTranslateX}
|
||||
paddingLeft={paddingLeft}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user