An ATproto social media client -- with an independent Appview.
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix invite codes flash on desktop, use loading placeholder (#1591)

* Fix invite codes flashing untrue value before loaded

* Add loading placeholder for right nav invites

authored by

Patroll and committed by
GitHub
92788220 2ba0c6a7

+142 -89
+5 -3
src/state/models/me.ts
··· 25 25 savedFeeds: SavedFeedsModel 26 26 notifications: NotificationsFeedModel 27 27 follows: MyFollowsCache 28 - invites: ComAtprotoServerDefs.InviteCode[] = [] 28 + invites: ComAtprotoServerDefs.InviteCode[] | null = [] 29 29 appPasswords: ComAtprotoServerListAppPasswords.AppPassword[] = [] 30 30 lastProfileStateUpdate = Date.now() 31 31 lastNotifsUpdate = Date.now() 32 32 33 33 get invitesAvailable() { 34 - return this.invites.filter(isInviteAvailable).length 34 + return this.invites?.filter(isInviteAvailable).length || null 35 35 } 36 36 37 37 constructor(public rootStore: RootStoreModel) { ··· 180 180 } catch (e) { 181 181 this.rootStore.log.error('Failed to fetch user invite codes', e) 182 182 } 183 - await this.rootStore.invitedUsers.fetch(this.invites) 183 + if (this.invites) { 184 + await this.rootStore.invitedUsers.fetch(this.invites) 185 + } 184 186 } 185 187 } 186 188
+27
src/view/com/modals/InviteCodes.tsx
··· 26 26 store.shell.closeModal() 27 27 }, [store]) 28 28 29 + if (store.me.invites === null) { 30 + return ( 31 + <View style={[styles.container, pal.view]} testID="inviteCodesModal"> 32 + <Text type="title-xl" style={[styles.title, pal.text]}> 33 + Error 34 + </Text> 35 + <Text type="lg" style={[styles.description, pal.text]}> 36 + An error occurred while loading invite codes. 37 + </Text> 38 + <View style={styles.flex1} /> 39 + <View 40 + style={[ 41 + styles.btnContainer, 42 + isTabletOrDesktop && styles.btnContainerDesktop, 43 + ]}> 44 + <Button 45 + type="primary" 46 + label="Done" 47 + style={styles.btn} 48 + labelStyle={styles.btnLabel} 49 + onPress={onClose} 50 + /> 51 + </View> 52 + </View> 53 + ) 54 + } 55 + 29 56 if (store.me.invites.length === 0) { 30 57 return ( 31 58 <View style={[styles.container, pal.view]} testID="inviteCodesModal">
+39 -31
src/view/screens/Settings.tsx
··· 322 322 323 323 <View style={styles.spacer20} /> 324 324 325 - <Text type="xl-bold" style={[pal.text, styles.heading]}> 326 - Invite a Friend 327 - </Text> 328 - <TouchableOpacity 329 - testID="inviteFriendBtn" 330 - style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} 331 - onPress={isSwitching ? undefined : onPressInviteCodes} 332 - accessibilityRole="button" 333 - accessibilityLabel="Invite" 334 - accessibilityHint="Opens invite code list"> 335 - <View 336 - style={[ 337 - styles.iconContainer, 338 - store.me.invitesAvailable > 0 ? primaryBg : pal.btn, 339 - ]}> 340 - <FontAwesomeIcon 341 - icon="ticket" 342 - style={ 343 - (store.me.invitesAvailable > 0 344 - ? primaryText 345 - : pal.text) as FontAwesomeIconStyle 346 - } 347 - /> 348 - </View> 349 - <Text 350 - type="lg" 351 - style={store.me.invitesAvailable > 0 ? pal.link : pal.text}> 352 - {formatCount(store.me.invitesAvailable)} invite{' '} 353 - {pluralize(store.me.invitesAvailable, 'code')} available 354 - </Text> 355 - </TouchableOpacity> 325 + {store.me.invitesAvailable !== null && ( 326 + <> 327 + <Text type="xl-bold" style={[pal.text, styles.heading]}> 328 + Invite a Friend 329 + </Text> 330 + <TouchableOpacity 331 + testID="inviteFriendBtn" 332 + style={[ 333 + styles.linkCard, 334 + pal.view, 335 + isSwitching && styles.dimmed, 336 + ]} 337 + onPress={isSwitching ? undefined : onPressInviteCodes} 338 + accessibilityRole="button" 339 + accessibilityLabel="Invite" 340 + accessibilityHint="Opens invite code list"> 341 + <View 342 + style={[ 343 + styles.iconContainer, 344 + store.me.invitesAvailable > 0 ? primaryBg : pal.btn, 345 + ]}> 346 + <FontAwesomeIcon 347 + icon="ticket" 348 + style={ 349 + (store.me.invitesAvailable > 0 350 + ? primaryText 351 + : pal.text) as FontAwesomeIconStyle 352 + } 353 + /> 354 + </View> 355 + <Text 356 + type="lg" 357 + style={store.me.invitesAvailable > 0 ? pal.link : pal.text}> 358 + {formatCount(store.me.invitesAvailable)} invite{' '} 359 + {pluralize(store.me.invitesAvailable, 'code')} available 360 + </Text> 361 + </TouchableOpacity> 362 + </> 363 + )} 356 364 357 365 <View style={styles.spacer20} /> 358 366
+28 -26
src/view/shell/Drawer.tsx
··· 426 426 store.shell.openModal({name: 'invite-codes'}) 427 427 }, [store, track]) 428 428 return ( 429 - <TouchableOpacity 430 - testID="menuItemInviteCodes" 431 - style={[styles.inviteCodes, style]} 432 - onPress={onPress} 433 - accessibilityRole="button" 434 - accessibilityLabel={ 435 - invitesAvailable === 1 436 - ? 'Invite codes: 1 available' 437 - : `Invite codes: ${invitesAvailable} available` 438 - } 439 - accessibilityHint="Opens list of invite codes"> 440 - <FontAwesomeIcon 441 - icon="ticket" 442 - style={[ 443 - styles.inviteCodesIcon, 444 - store.me.invitesAvailable > 0 ? pal.link : pal.textLight, 445 - ]} 446 - size={18} 447 - /> 448 - <Text 449 - type="lg-medium" 450 - style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> 451 - {formatCount(store.me.invitesAvailable)} invite{' '} 452 - {pluralize(store.me.invitesAvailable, 'code')} 453 - </Text> 454 - </TouchableOpacity> 429 + store.me.invitesAvailable !== null && ( 430 + <TouchableOpacity 431 + testID="menuItemInviteCodes" 432 + style={[styles.inviteCodes, style]} 433 + onPress={onPress} 434 + accessibilityRole="button" 435 + accessibilityLabel={ 436 + invitesAvailable === 1 437 + ? 'Invite codes: 1 available' 438 + : `Invite codes: ${invitesAvailable} available` 439 + } 440 + accessibilityHint="Opens list of invite codes"> 441 + <FontAwesomeIcon 442 + icon="ticket" 443 + style={[ 444 + styles.inviteCodesIcon, 445 + store.me.invitesAvailable > 0 ? pal.link : pal.textLight, 446 + ]} 447 + size={18} 448 + /> 449 + <Text 450 + type="lg-medium" 451 + style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> 452 + {formatCount(store.me.invitesAvailable)} invite{' '} 453 + {pluralize(store.me.invitesAvailable, 'code')} 454 + </Text> 455 + </TouchableOpacity> 456 + ) 455 457 ) 456 458 }) 457 459
+43 -29
src/view/shell/desktop/RightNav.tsx
··· 7 7 import {DesktopFeeds} from './Feeds' 8 8 import {Text} from 'view/com/util/text/Text' 9 9 import {TextLink} from 'view/com/util/Link' 10 + import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' 10 11 import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' 11 12 import {s} from 'lib/styles' 12 13 import {useStores} from 'state/index' ··· 89 90 const onPress = React.useCallback(() => { 90 91 store.shell.openModal({name: 'invite-codes'}) 91 92 }, [store]) 93 + 92 94 return ( 93 - <TouchableOpacity 94 - style={[styles.inviteCodes, pal.border]} 95 - onPress={onPress} 96 - accessibilityRole="button" 97 - accessibilityLabel={ 98 - invitesAvailable === 1 99 - ? 'Invite codes: 1 available' 100 - : `Invite codes: ${invitesAvailable} available` 101 - } 102 - accessibilityHint="Opens list of invite codes"> 103 - <FontAwesomeIcon 104 - icon="ticket" 105 - style={[ 106 - styles.inviteCodesIcon, 107 - store.me.invitesAvailable > 0 ? pal.link : pal.textLight, 108 - ]} 109 - size={16} 110 - /> 111 - <Text 112 - type="md-medium" 113 - style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> 114 - {formatCount(store.me.invitesAvailable)} invite{' '} 115 - {pluralize(store.me.invitesAvailable, 'code')} available 116 - </Text> 117 - </TouchableOpacity> 95 + <View style={[styles.separator, pal.border]}> 96 + {store.me.invitesAvailable === null ? ( 97 + <View style={[s.p10]}> 98 + <LoadingPlaceholder width={186} height={32} style={[styles.br40]} /> 99 + </View> 100 + ) : ( 101 + <TouchableOpacity 102 + style={[styles.inviteCodes]} 103 + onPress={onPress} 104 + accessibilityRole="button" 105 + accessibilityLabel={ 106 + invitesAvailable === 1 107 + ? 'Invite codes: 1 available' 108 + : `Invite codes: ${invitesAvailable} available` 109 + } 110 + accessibilityHint="Opens list of invite codes"> 111 + <FontAwesomeIcon 112 + icon="ticket" 113 + style={[ 114 + styles.inviteCodesIcon, 115 + store.me.invitesAvailable > 0 ? pal.link : pal.textLight, 116 + ]} 117 + size={16} 118 + /> 119 + <Text 120 + type="md-medium" 121 + style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> 122 + {formatCount(store.me.invitesAvailable)} invite{' '} 123 + {pluralize(store.me.invitesAvailable, 'code')} available 124 + </Text> 125 + </TouchableOpacity> 126 + )} 127 + </View> 118 128 ) 119 129 }) 120 130 ··· 131 141 132 142 message: { 133 143 paddingVertical: 18, 134 - paddingHorizontal: 10, 144 + paddingHorizontal: 12, 135 145 }, 136 146 messageLine: { 137 147 marginBottom: 10, 138 148 }, 139 149 140 - inviteCodes: { 150 + separator: { 141 151 borderTopWidth: 1, 142 - paddingHorizontal: 16, 143 - paddingVertical: 12, 152 + }, 153 + br40: {borderRadius: 40}, 154 + 155 + inviteCodes: { 156 + paddingHorizontal: 12, 157 + paddingVertical: 16, 144 158 flexDirection: 'row', 145 159 alignItems: 'center', 146 160 },