Live video on the AT Protocol
1import { DrawerNavigationOptions } from "@react-navigation/drawer";
2import { DrawerDescriptorMap } from "@react-navigation/drawer/lib/typescript/src/types";
3import {
4 CommonActions,
5 DrawerNavigationState,
6 ParamListBase,
7} from "@react-navigation/native";
8import { FileQuestion } from "@tamagui/lucide-icons";
9import { Platform } from "react-native";
10import { SharedValue, useAnimatedStyle } from "react-native-reanimated";
11import { Image, styled, Text, View, YStack } from "tamagui";
12import SidebarItem from "./sidebar-item";
13
14const AnimatedYStack = styled(YStack, {
15 name: "AnimatedYStack",
16});
17
18export interface ExternalDrawerItem {
19 item: React.NamedExoticComponent<any>;
20 label: React.ComponentType<any> | string;
21 onPress: () => void;
22}
23
24interface CustomSidebarProps {
25 collapsed: boolean;
26 hidden: boolean;
27 widthAnim: SharedValue<number>;
28 descriptors: DrawerDescriptorMap;
29 state: DrawerNavigationState<ParamListBase>;
30 externalItems?: ExternalDrawerItem[];
31}
32
33// Combine standard drawer props with custom props
34type SidebarProps = CustomSidebarProps & DrawerNavigationOptions;
35
36export default function Sidebar({
37 state,
38 descriptors,
39 collapsed,
40 hidden,
41 widthAnim,
42 externalItems = [],
43}: SidebarProps) {
44 // Apply the defined type to the component props
45 const animatedSidebarStyle = useAnimatedStyle(() => {
46 return {
47 minWidth: widthAnim.value,
48 maxWidth: widthAnim.value,
49 };
50 });
51
52 if (hidden) {
53 return <View />;
54 }
55
56 return (
57 <AnimatedYStack
58 style={animatedSidebarStyle} // Apply the animated style
59 padding="$2"
60 gap="$2"
61 >
62 <View
63 marginTop={Platform.OS === "ios" ? 29 : 12}
64 marginBottom="$5"
65 paddingLeft="$2.5"
66 gap="$3"
67 flexDirection="row"
68 justifyContent="flex-start"
69 alignItems="center"
70 >
71 <Image
72 source={require("../../assets/images/cube.png")}
73 height="$2"
74 width="$2"
75 />
76 {!collapsed && (
77 <Text fontSize="$7" minWidth={200} numberOfLines={1}>
78 Streamplace
79 </Text>
80 )}
81 </View>
82
83 {state.routes.map((route) => {
84 const descriptor = descriptors[route.key];
85 const options = descriptor?.options ?? {};
86
87 const label =
88 typeof options.drawerLabel === "function"
89 ? options.drawerLabel({ focused: false, color: "$color" })
90 : (options.drawerLabel ?? options.title ?? route.name);
91
92 const IconComponent = options.drawerIcon as
93 | React.ComponentType<any>
94 | undefined;
95
96 return (
97 <SidebarItem
98 key={route.key}
99 icon={IconComponent ? IconComponent : FileQuestion}
100 label={label}
101 active={descriptor.navigation.isFocused()}
102 collapsed={collapsed}
103 onPress={() => {
104 if (route.name === "Home") {
105 // copy logic for 'Home' to reset the stack
106 descriptor.navigation.dispatch(
107 CommonActions.reset({
108 index: 0,
109 routes: [
110 {
111 name: "Home",
112 state: {
113 routes: [{ name: "StreamList" }],
114 },
115 },
116 ],
117 }),
118 );
119 } else {
120 descriptor.navigation.navigate(route.name);
121 }
122 }}
123 style={options.drawerItemStyle}
124 tint={options.drawerActiveTintColor as string} // Assuming tint is a string color or undefined
125 />
126 );
127 })}
128 {externalItems.map((i) => {
129 return (
130 <SidebarItem
131 key={JSON.stringify(i.label)}
132 icon={i.item}
133 label={i.label || "Fix this label!"}
134 active={false}
135 collapsed={collapsed}
136 onPress={() => i.onPress()}
137 tint="rgba(189, 110, 134)"
138 />
139 );
140 })}
141 </AnimatedYStack>
142 );
143}