|
1 | 1 | import React, { useCallback, useEffect, useMemo, useState } from 'react'; |
2 | | -import Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated'; |
3 | | -import { LayoutChangeEvent, Text, TouchableOpacity, View, Platform, StyleSheet } from 'react-native'; |
4 | | -import { Close, Notification, Check, Delete, useTheme } from 'stream-chat-react-native'; |
| 2 | +import Animated, { |
| 3 | + useAnimatedStyle, |
| 4 | + useSharedValue, |
| 5 | + withSpring, |
| 6 | + withTiming, |
| 7 | +} from 'react-native-reanimated'; |
| 8 | +import { |
| 9 | + LayoutChangeEvent, |
| 10 | + Text, |
| 11 | + TouchableOpacity, |
| 12 | + View, |
| 13 | + Platform, |
| 14 | + StyleSheet, |
| 15 | +} from 'react-native'; |
| 16 | +import { Close, Notification, Delete, useTheme } from 'stream-chat-react-native'; |
5 | 17 | import { styles as menuDrawerStyles } from './MenuDrawer.tsx'; |
6 | 18 | import AsyncStore from '../utils/AsyncStore.ts'; |
7 | 19 | import { StreamChat } from 'stream-chat'; |
| 20 | +import { LabeledTextInput } from '../screens/AdvancedUserSelectorScreen.tsx'; |
8 | 21 |
|
9 | | -export const SlideInView = ({ visible, children }: { visible: boolean; children: React.ReactNode }) => { |
| 22 | +export const SlideInView = ({ |
| 23 | + visible, |
| 24 | + children, |
| 25 | +}: { |
| 26 | + visible: boolean; |
| 27 | + children: React.ReactNode; |
| 28 | +}) => { |
10 | 29 | const animatedHeight = useSharedValue(0); |
11 | 30 |
|
12 | 31 | const onLayout = (event: LayoutChangeEvent) => { |
13 | 32 | const { height } = event.nativeEvent.layout; |
14 | 33 | animatedHeight.value = height; |
15 | 34 | }; |
16 | 35 |
|
17 | | - const animatedStyle = useAnimatedStyle(() => ({ |
18 | | - height: withSpring(visible ? animatedHeight.value : 0, { damping: 10 }), |
19 | | - opacity: withTiming(visible ? 1 : 0, { duration: 500 }), |
20 | | - }), [visible]); |
| 36 | + const animatedStyle = useAnimatedStyle( |
| 37 | + () => ({ |
| 38 | + height: withSpring(visible ? animatedHeight.value : 0, { damping: 10 }), |
| 39 | + opacity: withTiming(visible ? 1 : 0, { duration: 500 }), |
| 40 | + }), |
| 41 | + [visible], |
| 42 | + ); |
21 | 43 |
|
22 | 44 | return ( |
23 | 45 | <Animated.View style={animatedStyle}> |
24 | | - {visible ? <View onLayout={onLayout} style={{ position: 'absolute', width: '100%' }}> |
25 | | - {children} |
26 | | - </View> : null} |
| 46 | + {visible ? ( |
| 47 | + <View onLayout={onLayout} style={{ position: 'absolute', width: '100%' }}> |
| 48 | + {children} |
| 49 | + </View> |
| 50 | + ) : null} |
27 | 51 | </Animated.View> |
28 | 52 | ); |
29 | 53 | }; |
30 | 54 |
|
31 | 55 | const isAndroid = Platform.OS === 'android'; |
32 | 56 |
|
33 | | -export const SecretMenu = ({ close, visible, chatClient }: { close: () => void, visible: boolean, chatClient: StreamChat }) => { |
| 57 | +type NotificationConfigItem = { label: string; name: string; id: string }; |
| 58 | + |
| 59 | +const SecretMenuNotificationConfigItem = ({ |
| 60 | + notificationConfigItem, |
| 61 | + storeProvider, |
| 62 | + isSelected, |
| 63 | +}: { |
| 64 | + notificationConfigItem: NotificationConfigItem; |
| 65 | + storeProvider: (item: NotificationConfigItem) => void; |
| 66 | + isSelected: boolean; |
| 67 | +}) => { |
| 68 | + const [providerNameOverride, setProviderNameOverride] = useState<string>(''); |
| 69 | + const [lastSubmittedOverride, setLastSubmittedOverride] = useState<string | null>(null); |
| 70 | + |
| 71 | + const asyncStorageKey = useMemo( |
| 72 | + () => `@stream-rn-sampleapp-push-provider-${notificationConfigItem.id}-override`, |
| 73 | + [notificationConfigItem], |
| 74 | + ); |
| 75 | + |
| 76 | + useEffect(() => { |
| 77 | + const getProviderNameOverride = async () => { |
| 78 | + const nameOverride = await AsyncStore.getItem(asyncStorageKey, ''); |
| 79 | + setLastSubmittedOverride(nameOverride ?? ''); |
| 80 | + }; |
| 81 | + getProviderNameOverride(); |
| 82 | + }, [asyncStorageKey]); |
| 83 | + |
| 84 | + const storeProviderNameOverride = useCallback(async () => { |
| 85 | + await AsyncStore.setItem(asyncStorageKey, providerNameOverride); |
| 86 | + setLastSubmittedOverride(providerNameOverride); |
| 87 | + }, [asyncStorageKey, providerNameOverride]); |
| 88 | + |
| 89 | + return ( |
| 90 | + <View |
| 91 | + style={[styles.notificationItemContainer, { borderColor: isSelected ? 'green' : 'gray' }]} |
| 92 | + > |
| 93 | + <TouchableOpacity |
| 94 | + style={{ flexDirection: 'row' }} |
| 95 | + onPress={() => storeProvider(notificationConfigItem)} |
| 96 | + > |
| 97 | + <Text style={styles.notificationItem}>{notificationConfigItem.label}</Text> |
| 98 | + <Text |
| 99 | + numberOfLines={1} |
| 100 | + style={{ opacity: 0.7, fontSize: 14, marginLeft: 4, maxWidth: 145 }} |
| 101 | + > |
| 102 | + {lastSubmittedOverride && lastSubmittedOverride.length > 0 |
| 103 | + ? lastSubmittedOverride |
| 104 | + : notificationConfigItem.name} |
| 105 | + </Text> |
| 106 | + </TouchableOpacity> |
| 107 | + {isSelected ? ( |
| 108 | + <> |
| 109 | + <LabeledTextInput |
| 110 | + onChangeText={setProviderNameOverride} |
| 111 | + label={'PN Provider name override'} |
| 112 | + value={providerNameOverride} |
| 113 | + /> |
| 114 | + <TouchableOpacity style={styles.submitButton} onPress={storeProviderNameOverride}> |
| 115 | + <Text style={{ color: 'white', fontSize: 12 }}>Submit</Text> |
| 116 | + </TouchableOpacity> |
| 117 | + </> |
| 118 | + ) : null} |
| 119 | + </View> |
| 120 | + ); |
| 121 | +}; |
| 122 | + |
| 123 | +export const SecretMenu = ({ |
| 124 | + close, |
| 125 | + visible, |
| 126 | + chatClient, |
| 127 | +}: { |
| 128 | + close: () => void; |
| 129 | + visible: boolean; |
| 130 | + chatClient: StreamChat; |
| 131 | +}) => { |
34 | 132 | const [selectedProvider, setSelectedProvider] = useState<string | null>(null); |
35 | 133 | const { |
36 | 134 | theme: { |
37 | 135 | colors: { black, grey }, |
38 | 136 | }, |
39 | 137 | } = useTheme(); |
40 | 138 |
|
41 | | - const notificationConfigItems = useMemo(() => [{ label: 'Firebase', name: 'rn-fcm', id: 'firebase' }, { label: 'APNs', name: 'APN', id: 'apn' }], []); |
| 139 | + const notificationConfigItems = useMemo( |
| 140 | + () => [ |
| 141 | + { label: 'Firebase', name: 'rn-fcm', id: 'firebase' }, |
| 142 | + { label: 'APNs', name: 'APN', id: 'apn' }, |
| 143 | + ], |
| 144 | + [], |
| 145 | + ); |
42 | 146 |
|
43 | 147 | useEffect(() => { |
44 | 148 | const getSelectedProvider = async () => { |
45 | | - const provider = await AsyncStore.getItem('@stream-rn-sampleapp-push-provider', notificationConfigItems[0]); |
| 149 | + const provider = await AsyncStore.getItem( |
| 150 | + '@stream-rn-sampleapp-push-provider', |
| 151 | + notificationConfigItems[0], |
| 152 | + ); |
46 | 153 | setSelectedProvider(provider?.id ?? 'firebase'); |
47 | 154 | }; |
48 | 155 | getSelectedProvider(); |
49 | 156 | }, [notificationConfigItems]); |
50 | 157 |
|
51 | | - const storeProvider = useCallback(async (item: { label: string, name: string, id: string }) => { |
52 | | - await AsyncStore.setItem('@stream-rn-sampleapp-push-provider', item); |
53 | | - setSelectedProvider(item.id); |
| 158 | + const storeProvider = useCallback(async (item: NotificationConfigItem) => { |
| 159 | + await AsyncStore.setItem('@stream-rn-sampleapp-push-provider', item); |
| 160 | + setSelectedProvider(item.id); |
54 | 161 | }, []); |
55 | 162 |
|
56 | 163 | const removeAllDevices = useCallback(async () => { |
@@ -81,14 +188,18 @@ export const SecretMenu = ({ close, visible, chatClient }: { close: () => void, |
81 | 188 | > |
82 | 189 | Notification Provider |
83 | 190 | </Text> |
84 | | - {isAndroid ? null : <View style={{ marginLeft: 16 }}> |
85 | | - {notificationConfigItems.map((item) => ( |
86 | | - <TouchableOpacity key={item.id} style={{ paddingTop: 8, flexDirection: 'row' }} onPress={() => storeProvider(item)}> |
87 | | - <Text style={styles.notificationItem}>{item.label}</Text> |
88 | | - {item.id === selectedProvider ? <Check height={16} pathFill={'green'} width={16} style={{ marginLeft: 12 }} /> : null} |
89 | | - </TouchableOpacity> |
90 | | - ))} |
91 | | - </View>} |
| 191 | + {isAndroid ? null : ( |
| 192 | + <View style={{ marginLeft: 16 }}> |
| 193 | + {notificationConfigItems.map((item) => ( |
| 194 | + <SecretMenuNotificationConfigItem |
| 195 | + key={item.id} |
| 196 | + notificationConfigItem={item} |
| 197 | + storeProvider={storeProvider} |
| 198 | + isSelected={item.id === selectedProvider} |
| 199 | + /> |
| 200 | + ))} |
| 201 | + </View> |
| 202 | + )} |
92 | 203 | </View> |
93 | 204 | </View> |
94 | 205 | <TouchableOpacity onPress={removeAllDevices} style={menuDrawerStyles.menuItem}> |
@@ -126,9 +237,24 @@ export const SecretMenu = ({ close, visible, chatClient }: { close: () => void, |
126 | 237 |
|
127 | 238 | export const styles = StyleSheet.create({ |
128 | 239 | separator: { height: 1, width: '100%', opacity: 0.2 }, |
129 | | - notificationContainer: {}, |
| 240 | + notificationItemContainer: { |
| 241 | + paddingTop: 8, |
| 242 | + marginTop: 12, |
| 243 | + borderRadius: 15, |
| 244 | + borderWidth: 2, |
| 245 | + padding: 8, |
| 246 | + width: 225, |
| 247 | + }, |
130 | 248 | notificationItem: { |
131 | | - fontSize: 13, |
| 249 | + fontSize: 15, |
132 | 250 | fontWeight: '500', |
133 | 251 | }, |
| 252 | + submitButton: { |
| 253 | + flex: 1, |
| 254 | + marginTop: 8, |
| 255 | + borderRadius: 5, |
| 256 | + backgroundColor: 'lightskyblue', |
| 257 | + padding: 8, |
| 258 | + alignItems: 'center', |
| 259 | + }, |
134 | 260 | }); |
0 commit comments