308 lines
8.9 KiB
TypeScript
308 lines
8.9 KiB
TypeScript
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>
|
|
);
|
|
};
|