Bluesky app fork with some witchin' additions 馃挮
at readme-update 289 lines 9.8 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import Animated from 'react-native-reanimated' 4import {msg, plural, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6import {useNavigationState} from '@react-navigation/native' 7 8import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder' 9import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform' 10import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 11import {makeProfileLink} from '#/lib/routes/links' 12import {type CommonNavigatorParams} from '#/lib/routes/types' 13import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 14import {useUnreadNotifications} from '#/state/queries/notifications/unread' 15import {useSession} from '#/state/session' 16import {useLoggedOutViewControls} from '#/state/shell/logged-out' 17import {useShellLayout} from '#/state/shell/shell-layout' 18import {useCloseAllActiveElements} from '#/state/util' 19import {Link} from '#/view/com/util/Link' 20import {Logo} from '#/view/icons/Logo' 21import {Logotype} from '#/view/icons/Logotype' 22import {atoms as a, useTheme} from '#/alf' 23import {Button, ButtonText} from '#/components/Button' 24import { 25 Bell_Filled_Corner0_Rounded as BellFilled, 26 Bell_Stroke2_Corner0_Rounded as Bell, 27} from '#/components/icons/Bell' 28import { 29 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 30 HomeOpen_Stoke2_Corner0_Rounded as Home, 31} from '#/components/icons/HomeOpen' 32import { 33 MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 34 MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 35} from '#/components/icons/MagnifyingGlass' 36import { 37 Message_Stroke2_Corner0_Rounded as Message, 38 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, 39} from '#/components/icons/Message' 40import { 41 UserCircle_Filled_Corner0_Rounded as UserCircleFilled, 42 UserCircle_Stroke2_Corner0_Rounded as UserCircle, 43} from '#/components/icons/UserCircle' 44import {Text} from '#/components/Typography' 45import {styles} from './BottomBarStyles' 46 47export function BottomBarWeb() { 48 const {_} = useLingui() 49 const {hasSession, currentAccount} = useSession() 50 const t = useTheme() 51 const footerMinimalShellTransform = useMinimalShellFooterTransform() 52 const {requestSwitchToAccount} = useLoggedOutViewControls() 53 const closeAllActiveElements = useCloseAllActiveElements() 54 const {footerHeight} = useShellLayout() 55 const hideBorder = useHideBottomBarBorder() 56 const iconWidth = 26 57 58 const unreadMessageCount = useUnreadMessageCount() 59 const notificationCountStr = useUnreadNotifications() 60 61 const showSignIn = React.useCallback(() => { 62 closeAllActiveElements() 63 requestSwitchToAccount({requestedAccount: 'none'}) 64 }, [requestSwitchToAccount, closeAllActiveElements]) 65 66 const showCreateAccount = React.useCallback(() => { 67 closeAllActiveElements() 68 requestSwitchToAccount({requestedAccount: 'new'}) 69 // setShowLoggedOut(true) 70 }, [requestSwitchToAccount, closeAllActiveElements]) 71 72 return ( 73 <Animated.View 74 role="navigation" 75 style={[ 76 styles.bottomBar, 77 styles.bottomBarWeb, 78 t.atoms.bg, 79 hideBorder 80 ? {borderColor: t.atoms.bg.backgroundColor} 81 : t.atoms.border_contrast_low, 82 footerMinimalShellTransform, 83 ]} 84 onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}> 85 {hasSession ? ( 86 <> 87 <NavItem routeName="Home" href="/"> 88 {({isActive}) => { 89 const Icon = isActive ? HomeFilled : Home 90 return ( 91 <Icon 92 aria-hidden={true} 93 width={iconWidth + 1} 94 style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]} 95 /> 96 ) 97 }} 98 </NavItem> 99 <NavItem routeName="Search" href="/search"> 100 {({isActive}) => { 101 const Icon = isActive ? MagnifyingGlassFilled : MagnifyingGlass 102 return ( 103 <Icon 104 aria-hidden={true} 105 width={iconWidth + 2} 106 style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]} 107 /> 108 ) 109 }} 110 </NavItem> 111 112 {hasSession && ( 113 <> 114 <NavItem 115 routeName="Messages" 116 href="/messages" 117 notificationCount={unreadMessageCount.numUnread} 118 hasNew={unreadMessageCount.hasNew}> 119 {({isActive}) => { 120 const Icon = isActive ? MessageFilled : Message 121 return ( 122 <Icon 123 aria-hidden={true} 124 width={iconWidth - 1} 125 style={[ 126 styles.ctrlIcon, 127 t.atoms.text, 128 styles.messagesIcon, 129 ]} 130 /> 131 ) 132 }} 133 </NavItem> 134 <NavItem 135 routeName="Notifications" 136 href="/notifications" 137 notificationCount={notificationCountStr}> 138 {({isActive}) => { 139 const Icon = isActive ? BellFilled : Bell 140 return ( 141 <Icon 142 aria-hidden={true} 143 width={iconWidth} 144 style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]} 145 /> 146 ) 147 }} 148 </NavItem> 149 <NavItem 150 routeName="Profile" 151 href={ 152 currentAccount 153 ? makeProfileLink({ 154 did: currentAccount.did, 155 handle: currentAccount.handle, 156 }) 157 : '/' 158 }> 159 {({isActive}) => { 160 const Icon = isActive ? UserCircleFilled : UserCircle 161 return ( 162 <Icon 163 aria-hidden={true} 164 width={iconWidth} 165 style={[ 166 styles.ctrlIcon, 167 t.atoms.text, 168 styles.profileIcon, 169 ]} 170 /> 171 ) 172 }} 173 </NavItem> 174 </> 175 )} 176 </> 177 ) : ( 178 <> 179 <View 180 style={{ 181 width: '100%', 182 flexDirection: 'row', 183 alignItems: 'center', 184 justifyContent: 'space-between', 185 paddingTop: 14, 186 paddingBottom: 14, 187 paddingLeft: 14, 188 paddingRight: 6, 189 gap: 8, 190 }}> 191 <View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}> 192 <Logo width={32} /> 193 <View style={{paddingTop: 4}}> 194 <Logotype width={80} fill={t.atoms.text.color} /> 195 </View> 196 </View> 197 198 <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}> 199 <Button 200 onPress={showCreateAccount} 201 label={_(msg`Create account`)} 202 size="small" 203 variant="solid" 204 color="primary"> 205 <ButtonText> 206 <Trans>Create account</Trans> 207 </ButtonText> 208 </Button> 209 <Button 210 onPress={showSignIn} 211 label={_(msg`Sign in`)} 212 size="small" 213 variant="solid" 214 color="secondary"> 215 <ButtonText> 216 <Trans>Sign in</Trans> 217 </ButtonText> 218 </Button> 219 </View> 220 </View> 221 </> 222 )} 223 </Animated.View> 224 ) 225} 226 227const NavItem: React.FC<{ 228 children: (props: {isActive: boolean}) => React.ReactNode 229 href: string 230 routeName: string 231 hasNew?: boolean 232 notificationCount?: string 233}> = ({children, href, routeName, hasNew, notificationCount}) => { 234 const t = useTheme() 235 const {_} = useLingui() 236 const {currentAccount} = useSession() 237 const currentRoute = useNavigationState(state => { 238 if (!state) { 239 return {name: 'Home'} 240 } 241 return getCurrentRoute(state) 242 }) 243 244 // Checks whether we're on someone else's profile 245 const isOnDifferentProfile = 246 currentRoute.name === 'Profile' && 247 routeName === 'Profile' && 248 (currentRoute.params as CommonNavigatorParams['Profile']).name !== 249 currentAccount?.handle 250 251 const isActive = 252 currentRoute.name === 'Profile' 253 ? isTab(currentRoute.name, routeName) && 254 (currentRoute.params as CommonNavigatorParams['Profile']).name === 255 (routeName === 'Profile' 256 ? currentAccount?.handle 257 : (currentRoute.params as CommonNavigatorParams['Profile']).name) 258 : isTab(currentRoute.name, routeName) 259 260 return ( 261 <Link 262 href={href} 263 style={[styles.ctrl, a.pb_lg]} 264 navigationAction={isOnDifferentProfile ? 'push' : 'navigate'} 265 aria-role="link" 266 aria-label={routeName} 267 accessible={true}> 268 {children({isActive})} 269 {notificationCount ? ( 270 <View 271 style={[ 272 styles.notificationCount, 273 styles.notificationCountWeb, 274 {backgroundColor: t.palette.primary_500}, 275 ]} 276 aria-label={_( 277 msg`${plural(notificationCount, { 278 one: '# unread item', 279 other: '# unread items', 280 })}`, 281 )}> 282 <Text style={styles.notificationCountLabel}>{notificationCount}</Text> 283 </View> 284 ) : hasNew ? ( 285 <View style={styles.hasNewBadge} /> 286 ) : null} 287 </Link> 288 ) 289}