mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {useState} from 'react'
2import {
3 Pressable,
4 StyleProp,
5 StyleSheet,
6 TouchableOpacity,
7 View,
8 ViewStyle,
9} from 'react-native'
10import {Text} from '../util/text/Text'
11import {s, colors} from 'lib/styles'
12import {usePalette} from 'lib/hooks/usePalette'
13import {isWeb} from 'platform/detection'
14import {ScrollView} from 'view/com/modals/util'
15import {Trans, msg} from '@lingui/macro'
16import {useLingui} from '@lingui/react'
17import {useModalControls} from '#/state/modals'
18import {ThreadgateSetting} from '#/state/queries/threadgate'
19import {useMyListsQuery} from '#/state/queries/my-lists'
20import isEqual from 'lodash.isequal'
21import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
22
23export const snapPoints = ['60%']
24
25export function Component({
26 settings,
27 onChange,
28}: {
29 settings: ThreadgateSetting[]
30 onChange: (settings: ThreadgateSetting[]) => void
31}) {
32 const pal = usePalette('default')
33 const {closeModal} = useModalControls()
34 const [selected, setSelected] = useState(settings)
35 const {_} = useLingui()
36 const {data: lists} = useMyListsQuery('curate')
37
38 const onPressEverybody = () => {
39 setSelected([])
40 onChange([])
41 }
42
43 const onPressNobody = () => {
44 setSelected([{type: 'nobody'}])
45 onChange([{type: 'nobody'}])
46 }
47
48 const onPressAudience = (setting: ThreadgateSetting) => {
49 // remove nobody
50 let newSelected = selected.filter(v => v.type !== 'nobody')
51 // toggle
52 const i = newSelected.findIndex(v => isEqual(v, setting))
53 if (i === -1) {
54 newSelected.push(setting)
55 } else {
56 newSelected.splice(i, 1)
57 }
58 setSelected(newSelected)
59 onChange(newSelected)
60 }
61
62 return (
63 <View testID="threadgateModal" style={[pal.view, styles.container]}>
64 <View style={styles.titleSection}>
65 <Text type="title-lg" style={[pal.text, styles.title]}>
66 <Trans>Who can reply</Trans>
67 </Text>
68 </View>
69
70 <ScrollView>
71 <Text style={[pal.text, styles.description]}>
72 <Trans>Choose "Everybody" or "Nobody"</Trans>
73 </Text>
74 <View style={{flexDirection: 'row', gap: 6, paddingHorizontal: 6}}>
75 <Selectable
76 label={_(msg`Everybody`)}
77 isSelected={selected.length === 0}
78 onPress={onPressEverybody}
79 style={{flex: 1}}
80 />
81 <Selectable
82 label={_(msg`Nobody`)}
83 isSelected={!!selected.find(v => v.type === 'nobody')}
84 onPress={onPressNobody}
85 style={{flex: 1}}
86 />
87 </View>
88 <Text style={[pal.text, styles.description]}>
89 <Trans>Or combine these options:</Trans>
90 </Text>
91 <View style={{flexDirection: 'column', gap: 4, paddingHorizontal: 6}}>
92 <Selectable
93 label={_(msg`Mentioned users`)}
94 isSelected={!!selected.find(v => v.type === 'mention')}
95 onPress={() => onPressAudience({type: 'mention'})}
96 />
97 <Selectable
98 label={_(msg`Followed users`)}
99 isSelected={!!selected.find(v => v.type === 'following')}
100 onPress={() => onPressAudience({type: 'following'})}
101 />
102 {lists?.length
103 ? lists.map(list => (
104 <Selectable
105 key={list.uri}
106 label={_(msg`Users in "${list.name}"`)}
107 isSelected={
108 !!selected.find(
109 v => v.type === 'list' && v.list === list.uri,
110 )
111 }
112 onPress={() =>
113 onPressAudience({type: 'list', list: list.uri})
114 }
115 />
116 ))
117 : null}
118 </View>
119 </ScrollView>
120
121 <View style={[styles.btnContainer, pal.borderDark]}>
122 <TouchableOpacity
123 testID="confirmBtn"
124 onPress={() => {
125 closeModal()
126 }}
127 style={styles.btn}
128 accessibilityRole="button"
129 accessibilityLabel={_(msg({message: `Done`, context: 'action'}))}
130 accessibilityHint="">
131 <Text style={[s.white, s.bold, s.f18]}>
132 <Trans context="action">Done</Trans>
133 </Text>
134 </TouchableOpacity>
135 </View>
136 </View>
137 )
138}
139
140function Selectable({
141 label,
142 isSelected,
143 onPress,
144 style,
145}: {
146 label: string
147 isSelected: boolean
148 onPress: () => void
149 style?: StyleProp<ViewStyle>
150}) {
151 const pal = usePalette(isSelected ? 'inverted' : 'default')
152 return (
153 <Pressable
154 onPress={onPress}
155 accessibilityLabel={label}
156 accessibilityHint=""
157 style={[styles.selectable, pal.border, pal.view, style]}>
158 <Text type="xl" style={[pal.text]}>
159 {label}
160 </Text>
161 {isSelected ? (
162 <FontAwesomeIcon icon="check" color={pal.colors.text} size={18} />
163 ) : null}
164 </Pressable>
165 )
166}
167
168const styles = StyleSheet.create({
169 container: {
170 flex: 1,
171 paddingBottom: isWeb ? 0 : 40,
172 },
173 titleSection: {
174 paddingTop: isWeb ? 0 : 4,
175 },
176 title: {
177 textAlign: 'center',
178 fontWeight: '600',
179 },
180 description: {
181 textAlign: 'center',
182 paddingVertical: 16,
183 },
184 selectable: {
185 flexDirection: 'row',
186 justifyContent: 'space-between',
187 paddingHorizontal: 18,
188 paddingVertical: 16,
189 borderWidth: 1,
190 borderRadius: 6,
191 },
192 btn: {
193 flexDirection: 'row',
194 alignItems: 'center',
195 justifyContent: 'center',
196 borderRadius: 32,
197 padding: 14,
198 backgroundColor: colors.blue3,
199 },
200 btnContainer: {
201 paddingTop: 20,
202 paddingHorizontal: 20,
203 },
204})