tangled
alpha
login
or
join now
jeanmachine.dev
/
witchsky.app
forked from
jollywhoppers.com/witchsky.app
0
fork
atom
Bluesky app fork with some witchin' additions 💫
0
fork
atom
overview
issues
pulls
pipelines
lint (#9802)
authored by
Spence Pope
and committed by
GitHub
1 month ago
45d6e50e
e2a56b01
+207
-205
2 changed files
expand all
collapse all
unified
split
src
view
shell
bottom-bar
BottomBar.tsx
BottomBarWeb.tsx
+32
-52
src/view/shell/bottom-bar/BottomBar.tsx
···
15
15
import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder'
16
16
import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform'
17
17
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
18
18
-
import {usePalette} from '#/lib/hooks/usePalette'
19
18
import {clamp} from '#/lib/numbers'
20
19
import {getTabState, TabState} from '#/lib/routes/helpers'
21
20
import {emitSoftReset} from '#/state/events'
···
57
56
58
57
export function BottomBar({navigation}: BottomTabBarProps) {
59
58
const {hasSession, currentAccount} = useSession()
60
60
-
const pal = usePalette('default')
59
59
+
const t = useTheme()
61
60
const {_} = useLingui()
62
61
const safeAreaInsets = useSafeAreaInsets()
63
62
const {footerHeight} = useShellLayout()
···
145
144
<Animated.View
146
145
style={[
147
146
styles.bottomBar,
148
148
-
pal.view,
149
149
-
hideBorder ? {borderColor: pal.view.backgroundColor} : pal.border,
147
147
+
t.atoms.bg,
148
148
+
hideBorder
149
149
+
? {borderColor: t.atoms.bg.backgroundColor}
150
150
+
: t.atoms.border_contrast_low,
150
151
{paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)},
151
152
footerMinimalShellTransform,
152
153
]}
···
161
162
isAtHome ? (
162
163
<HomeFilled
163
164
width={iconWidth + 1}
164
164
-
style={[styles.ctrlIcon, pal.text, styles.homeIcon]}
165
165
+
style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]}
165
166
/>
166
167
) : (
167
168
<Home
168
169
width={iconWidth + 1}
169
169
-
style={[styles.ctrlIcon, pal.text, styles.homeIcon]}
170
170
+
style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]}
170
171
/>
171
172
)
172
173
}
···
180
181
isAtSearch ? (
181
182
<MagnifyingGlassFilled
182
183
width={iconWidth + 2}
183
183
-
style={[styles.ctrlIcon, pal.text, styles.searchIcon]}
184
184
+
style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]}
184
185
/>
185
186
) : (
186
187
<MagnifyingGlass
187
188
testID="bottomBarSearchBtn"
188
189
width={iconWidth + 2}
189
189
-
style={[styles.ctrlIcon, pal.text, styles.searchIcon]}
190
190
+
style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]}
190
191
/>
191
192
)
192
193
}
···
201
202
isAtMessages ? (
202
203
<MessageFilled
203
204
width={iconWidth - 1}
204
204
-
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
205
205
+
style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
205
206
/>
206
207
) : (
207
208
<Message
208
209
width={iconWidth - 1}
209
209
-
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
210
210
+
style={[styles.ctrlIcon, t.atoms.text, styles.feedsIcon]}
210
211
/>
211
212
)
212
213
}
···
233
234
isAtNotifications ? (
234
235
<BellFilled
235
236
width={iconWidth}
236
236
-
style={[styles.ctrlIcon, pal.text, styles.bellIcon]}
237
237
+
style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]}
237
238
/>
238
239
) : (
239
240
<Bell
240
241
width={iconWidth}
241
241
-
style={[styles.ctrlIcon, pal.text, styles.bellIcon]}
242
242
+
style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]}
242
243
/>
243
244
)
244
245
}
···
262
263
testID="bottomBarProfileBtn"
263
264
icon={
264
265
<View style={styles.ctrlIconSizingWrapper}>
265
265
-
{isAtMyProfile ? (
266
266
-
<View
267
267
-
style={[
268
268
-
styles.ctrlIcon,
269
269
-
pal.text,
270
270
-
styles.profileIcon,
266
266
+
<View
267
267
+
style={[
268
268
+
styles.ctrlIcon,
269
269
+
styles.profileIcon,
270
270
+
isAtMyProfile && [
271
271
styles.onProfile,
272
272
{
273
273
-
borderColor: pal.text.color,
274
274
-
borderWidth: live ? 0 : 1,
275
275
-
},
276
276
-
]}>
277
277
-
<UserAvatar
278
278
-
avatar={demoMode ? BOTTOM_BAR_AVI : profile?.avatar}
279
279
-
size={iconWidth - 2}
280
280
-
// See https://github.com/bluesky-social/social-app/pull/1801:
281
281
-
usePlainRNImage={true}
282
282
-
type={profile?.associated?.labeler ? 'labeler' : 'user'}
283
283
-
live={live}
284
284
-
hideLiveBadge
285
285
-
/>
286
286
-
</View>
287
287
-
) : (
288
288
-
<View
289
289
-
style={[
290
290
-
styles.ctrlIcon,
291
291
-
pal.text,
292
292
-
styles.profileIcon,
293
293
-
{
273
273
+
borderColor: t.atoms.text.color,
294
274
borderWidth: live ? 0 : 1,
295
275
},
296
296
-
]}>
297
297
-
<UserAvatar
298
298
-
avatar={demoMode ? BOTTOM_BAR_AVI : profile?.avatar}
299
299
-
size={iconWidth - 2}
300
300
-
// See https://github.com/bluesky-social/social-app/pull/1801:
301
301
-
usePlainRNImage={true}
302
302
-
type={profile?.associated?.labeler ? 'labeler' : 'user'}
303
303
-
live={live}
304
304
-
hideLiveBadge
305
305
-
/>
306
306
-
</View>
307
307
-
)}
276
276
+
],
277
277
+
]}>
278
278
+
<UserAvatar
279
279
+
avatar={demoMode ? BOTTOM_BAR_AVI : profile?.avatar}
280
280
+
size={iconWidth - (isAtMyProfile ? 3 : 2)}
281
281
+
// See https://github.com/bluesky-social/social-app/pull/1801:
282
282
+
usePlainRNImage={true}
283
283
+
type={profile?.associated?.labeler ? 'labeler' : 'user'}
284
284
+
live={live}
285
285
+
hideLiveBadge
286
286
+
/>
287
287
+
</View>
308
288
</View>
309
289
}
310
290
onPress={onPressProfile}
···
332
312
style={{flexDirection: 'row', alignItems: 'center', gap: 8}}>
333
313
<Logo width={28} />
334
314
<View style={{paddingTop: 4}}>
335
335
-
<Logotype width={80} fill={pal.text.color} />
315
315
+
<Logotype width={80} fill={t.atoms.text.color} />
336
316
</View>
337
317
</View>
338
318
+175
-153
src/view/shell/bottom-bar/BottomBarWeb.tsx
···
12
12
import {type CommonNavigatorParams} from '#/lib/routes/types'
13
13
import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations'
14
14
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
15
15
+
import {useProfileQuery} from '#/state/queries/profile'
15
16
import {useSession} from '#/state/session'
16
17
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
17
18
import {useShellLayout} from '#/state/shell/shell-layout'
18
19
import {useCloseAllActiveElements} from '#/state/util'
19
20
import {Link} from '#/view/com/util/Link'
21
21
+
import {UserAvatar} from '#/view/com/util/UserAvatar'
20
22
import {Logo} from '#/view/icons/Logo'
21
23
import {Logotype} from '#/view/icons/Logotype'
22
24
import {atoms as a, useTheme} from '#/alf'
23
25
import {Button, ButtonText} from '#/components/Button'
26
26
+
import {useDialogControl} from '#/components/Dialog'
27
27
+
import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount'
24
28
import {
25
29
Bell_Filled_Corner0_Rounded as BellFilled,
26
30
Bell_Stroke2_Corner0_Rounded as Bell,
···
37
41
Message_Stroke2_Corner0_Rounded as Message,
38
42
Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
39
43
} from '#/components/icons/Message'
40
40
-
import {
41
41
-
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
42
42
-
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
43
43
-
} from '#/components/icons/UserCircle'
44
44
import {Text} from '#/components/Typography'
45
45
import {styles} from './BottomBarStyles'
46
46
···
53
53
const closeAllActiveElements = useCloseAllActiveElements()
54
54
const {footerHeight} = useShellLayout()
55
55
const hideBorder = useHideBottomBarBorder()
56
56
+
const accountSwitchControl = useDialogControl()
57
57
+
const {data: profile} = useProfileQuery({did: currentAccount?.did})
56
58
const iconWidth = 26
57
59
58
60
const unreadMessageCount = useUnreadMessageCount()
···
69
71
// setShowLoggedOut(true)
70
72
}, [requestSwitchToAccount, closeAllActiveElements])
71
73
74
74
+
const onLongPressProfile = React.useCallback(() => {
75
75
+
accountSwitchControl.open()
76
76
+
}, [accountSwitchControl])
77
77
+
72
78
return (
73
73
-
<Animated.View
74
74
-
role="navigation"
75
75
-
style={[
76
76
-
styles.bottomBar,
77
77
-
styles.bottomBarWeb,
78
78
-
t.atoms.bg,
79
79
-
hideBorder
80
80
-
? {borderColor: t.atoms.bg.backgroundColor}
81
81
-
: t.atoms.border_contrast_low,
82
82
-
footerMinimalShellTransform,
83
83
-
]}
84
84
-
onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}>
85
85
-
{hasSession ? (
86
86
-
<>
87
87
-
<NavItem routeName="Home" href="/">
88
88
-
{({isActive}) => {
89
89
-
const Icon = isActive ? HomeFilled : Home
90
90
-
return (
91
91
-
<Icon
92
92
-
aria-hidden={true}
93
93
-
width={iconWidth + 1}
94
94
-
style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]}
95
95
-
/>
96
96
-
)
97
97
-
}}
98
98
-
</NavItem>
99
99
-
<NavItem routeName="Search" href="/search">
100
100
-
{({isActive}) => {
101
101
-
const Icon = isActive ? MagnifyingGlassFilled : MagnifyingGlass
102
102
-
return (
103
103
-
<Icon
104
104
-
aria-hidden={true}
105
105
-
width={iconWidth + 2}
106
106
-
style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]}
107
107
-
/>
108
108
-
)
109
109
-
}}
110
110
-
</NavItem>
79
79
+
<>
80
80
+
<SwitchAccountDialog control={accountSwitchControl} />
111
81
112
112
-
{hasSession && (
113
113
-
<>
114
114
-
<NavItem
115
115
-
routeName="Messages"
116
116
-
href="/messages"
117
117
-
notificationCount={unreadMessageCount.numUnread}
118
118
-
hasNew={unreadMessageCount.hasNew}>
119
119
-
{({isActive}) => {
120
120
-
const Icon = isActive ? MessageFilled : Message
121
121
-
return (
122
122
-
<Icon
123
123
-
aria-hidden={true}
124
124
-
width={iconWidth - 1}
125
125
-
style={[
126
126
-
styles.ctrlIcon,
127
127
-
t.atoms.text,
128
128
-
styles.messagesIcon,
129
129
-
]}
130
130
-
/>
131
131
-
)
132
132
-
}}
133
133
-
</NavItem>
134
134
-
<NavItem
135
135
-
routeName="Notifications"
136
136
-
href="/notifications"
137
137
-
notificationCount={notificationCountStr}>
138
138
-
{({isActive}) => {
139
139
-
const Icon = isActive ? BellFilled : Bell
140
140
-
return (
141
141
-
<Icon
142
142
-
aria-hidden={true}
143
143
-
width={iconWidth}
144
144
-
style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]}
145
145
-
/>
146
146
-
)
147
147
-
}}
148
148
-
</NavItem>
149
149
-
<NavItem
150
150
-
routeName="Profile"
151
151
-
href={
152
152
-
currentAccount
153
153
-
? makeProfileLink({
154
154
-
did: currentAccount.did,
155
155
-
handle: currentAccount.handle,
156
156
-
})
157
157
-
: '/'
158
158
-
}>
159
159
-
{({isActive}) => {
160
160
-
const Icon = isActive ? UserCircleFilled : UserCircle
161
161
-
return (
162
162
-
<Icon
163
163
-
aria-hidden={true}
164
164
-
width={iconWidth}
165
165
-
style={[
166
166
-
styles.ctrlIcon,
167
167
-
t.atoms.text,
168
168
-
styles.profileIcon,
169
169
-
]}
170
170
-
/>
171
171
-
)
172
172
-
}}
173
173
-
</NavItem>
174
174
-
</>
175
175
-
)}
176
176
-
</>
177
177
-
) : (
178
178
-
<>
179
179
-
<View
180
180
-
style={{
181
181
-
width: '100%',
182
182
-
flexDirection: 'row',
183
183
-
alignItems: 'center',
184
184
-
justifyContent: 'space-between',
185
185
-
paddingTop: 14,
186
186
-
paddingBottom: 14,
187
187
-
paddingLeft: 14,
188
188
-
paddingRight: 6,
189
189
-
gap: 8,
190
190
-
}}>
191
191
-
<View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
192
192
-
<Logo width={32} />
193
193
-
<View style={{paddingTop: 4}}>
194
194
-
<Logotype width={80} fill={t.atoms.text.color} />
82
82
+
<Animated.View
83
83
+
role="navigation"
84
84
+
style={[
85
85
+
styles.bottomBar,
86
86
+
styles.bottomBarWeb,
87
87
+
t.atoms.bg,
88
88
+
hideBorder
89
89
+
? {borderColor: t.atoms.bg.backgroundColor}
90
90
+
: t.atoms.border_contrast_low,
91
91
+
footerMinimalShellTransform,
92
92
+
]}
93
93
+
onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}>
94
94
+
{hasSession ? (
95
95
+
<>
96
96
+
<NavItem routeName="Home" href="/">
97
97
+
{({isActive}) => {
98
98
+
const Icon = isActive ? HomeFilled : Home
99
99
+
return (
100
100
+
<Icon
101
101
+
aria-hidden={true}
102
102
+
width={iconWidth + 1}
103
103
+
style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]}
104
104
+
/>
105
105
+
)
106
106
+
}}
107
107
+
</NavItem>
108
108
+
<NavItem routeName="Search" href="/search">
109
109
+
{({isActive}) => {
110
110
+
const Icon = isActive ? MagnifyingGlassFilled : MagnifyingGlass
111
111
+
return (
112
112
+
<Icon
113
113
+
aria-hidden={true}
114
114
+
width={iconWidth + 2}
115
115
+
style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]}
116
116
+
/>
117
117
+
)
118
118
+
}}
119
119
+
</NavItem>
120
120
+
121
121
+
{hasSession && (
122
122
+
<>
123
123
+
<NavItem
124
124
+
routeName="Messages"
125
125
+
href="/messages"
126
126
+
notificationCount={unreadMessageCount.numUnread}
127
127
+
hasNew={unreadMessageCount.hasNew}>
128
128
+
{({isActive}) => {
129
129
+
const Icon = isActive ? MessageFilled : Message
130
130
+
return (
131
131
+
<Icon
132
132
+
aria-hidden={true}
133
133
+
width={iconWidth - 1}
134
134
+
style={[
135
135
+
styles.ctrlIcon,
136
136
+
t.atoms.text,
137
137
+
styles.messagesIcon,
138
138
+
]}
139
139
+
/>
140
140
+
)
141
141
+
}}
142
142
+
</NavItem>
143
143
+
<NavItem
144
144
+
routeName="Notifications"
145
145
+
href="/notifications"
146
146
+
notificationCount={notificationCountStr}>
147
147
+
{({isActive}) => {
148
148
+
const Icon = isActive ? BellFilled : Bell
149
149
+
return (
150
150
+
<Icon
151
151
+
aria-hidden={true}
152
152
+
width={iconWidth}
153
153
+
style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]}
154
154
+
/>
155
155
+
)
156
156
+
}}
157
157
+
</NavItem>
158
158
+
<NavItem
159
159
+
routeName="Profile"
160
160
+
href={
161
161
+
currentAccount
162
162
+
? makeProfileLink({
163
163
+
did: currentAccount.did,
164
164
+
handle: currentAccount.handle,
165
165
+
})
166
166
+
: '/'
167
167
+
}
168
168
+
onLongPress={onLongPressProfile}>
169
169
+
{({isActive}) => (
170
170
+
<View style={styles.ctrlIconSizingWrapper}>
171
171
+
<View
172
172
+
style={[
173
173
+
styles.ctrlIcon,
174
174
+
styles.profileIcon,
175
175
+
isActive && [
176
176
+
styles.onProfile,
177
177
+
{borderColor: t.atoms.text.color},
178
178
+
],
179
179
+
]}>
180
180
+
<UserAvatar
181
181
+
avatar={profile?.avatar}
182
182
+
size={iconWidth - 3}
183
183
+
type={
184
184
+
profile?.associated?.labeler ? 'labeler' : 'user'
185
185
+
}
186
186
+
/>
187
187
+
</View>
188
188
+
</View>
189
189
+
)}
190
190
+
</NavItem>
191
191
+
</>
192
192
+
)}
193
193
+
</>
194
194
+
) : (
195
195
+
<>
196
196
+
<View
197
197
+
style={[
198
198
+
a.w_full,
199
199
+
a.flex_row,
200
200
+
a.align_center,
201
201
+
a.justify_between,
202
202
+
a.gap_sm,
203
203
+
{
204
204
+
paddingTop: 14,
205
205
+
paddingBottom: 14,
206
206
+
paddingLeft: 14,
207
207
+
paddingRight: 6,
208
208
+
},
209
209
+
]}>
210
210
+
<View style={[a.flex_row, a.align_center, a.gap_md]}>
211
211
+
<Logo width={32} />
212
212
+
<View style={{paddingTop: 4}}>
213
213
+
<Logotype width={80} fill={t.atoms.text.color} />
214
214
+
</View>
195
215
</View>
196
196
-
</View>
197
216
198
198
-
<View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
199
199
-
<Button
200
200
-
onPress={showCreateAccount}
201
201
-
label={_(msg`Create account`)}
202
202
-
size="small"
203
203
-
variant="solid"
204
204
-
color="primary">
205
205
-
<ButtonText>
206
206
-
<Trans>Create account</Trans>
207
207
-
</ButtonText>
208
208
-
</Button>
209
209
-
<Button
210
210
-
onPress={showSignIn}
211
211
-
label={_(msg`Sign in`)}
212
212
-
size="small"
213
213
-
variant="solid"
214
214
-
color="secondary">
215
215
-
<ButtonText>
216
216
-
<Trans>Sign in</Trans>
217
217
-
</ButtonText>
218
218
-
</Button>
217
217
+
<View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
218
218
+
<Button
219
219
+
onPress={showCreateAccount}
220
220
+
label={_(msg`Create account`)}
221
221
+
size="small"
222
222
+
variant="solid"
223
223
+
color="primary">
224
224
+
<ButtonText>
225
225
+
<Trans>Create account</Trans>
226
226
+
</ButtonText>
227
227
+
</Button>
228
228
+
<Button
229
229
+
onPress={showSignIn}
230
230
+
label={_(msg`Sign in`)}
231
231
+
size="small"
232
232
+
variant="solid"
233
233
+
color="secondary">
234
234
+
<ButtonText>
235
235
+
<Trans>Sign in</Trans>
236
236
+
</ButtonText>
237
237
+
</Button>
238
238
+
</View>
219
239
</View>
220
220
-
</View>
221
221
-
</>
222
222
-
)}
223
223
-
</Animated.View>
240
240
+
</>
241
241
+
)}
242
242
+
</Animated.View>
243
243
+
</>
224
244
)
225
245
}
226
246
···
230
250
routeName: string
231
251
hasNew?: boolean
232
252
notificationCount?: string
233
233
-
}> = ({children, href, routeName, hasNew, notificationCount}) => {
253
253
+
onLongPress?: () => void
254
254
+
}> = ({children, href, routeName, hasNew, notificationCount, onLongPress}) => {
234
255
const t = useTheme()
235
256
const {_} = useLingui()
236
257
const {currentAccount} = useSession()
···
264
285
navigationAction={isOnDifferentProfile ? 'push' : 'navigate'}
265
286
aria-role="link"
266
287
aria-label={routeName}
267
267
-
accessible={true}>
288
288
+
accessible={true}
289
289
+
onLongPress={onLongPress}>
268
290
{children({isActive})}
269
291
{notificationCount ? (
270
292
<View