diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/state/models/me.ts | 8 | ||||
-rw-r--r-- | src/view/com/modals/InviteCodes.tsx | 27 | ||||
-rw-r--r-- | src/view/screens/Settings.tsx | 70 | ||||
-rw-r--r-- | src/view/shell/Drawer.tsx | 54 | ||||
-rw-r--r-- | src/view/shell/desktop/RightNav.tsx | 72 |
5 files changed, 142 insertions, 89 deletions
diff --git a/src/state/models/me.ts b/src/state/models/me.ts index 186e61cf6..8a7a4c851 100644 --- a/src/state/models/me.ts +++ b/src/state/models/me.ts @@ -25,13 +25,13 @@ export class MeModel { savedFeeds: SavedFeedsModel notifications: NotificationsFeedModel follows: MyFollowsCache - invites: ComAtprotoServerDefs.InviteCode[] = [] + invites: ComAtprotoServerDefs.InviteCode[] | null = [] appPasswords: ComAtprotoServerListAppPasswords.AppPassword[] = [] lastProfileStateUpdate = Date.now() lastNotifsUpdate = Date.now() get invitesAvailable() { - return this.invites.filter(isInviteAvailable).length + return this.invites?.filter(isInviteAvailable).length || null } constructor(public rootStore: RootStoreModel) { @@ -180,7 +180,9 @@ export class MeModel { } catch (e) { this.rootStore.log.error('Failed to fetch user invite codes', e) } - await this.rootStore.invitedUsers.fetch(this.invites) + if (this.invites) { + await this.rootStore.invitedUsers.fetch(this.invites) + } } } diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx index 09cfd4de7..0cb0c56aa 100644 --- a/src/view/com/modals/InviteCodes.tsx +++ b/src/view/com/modals/InviteCodes.tsx @@ -26,6 +26,33 @@ export function Component({}: {}) { store.shell.closeModal() }, [store]) + if (store.me.invites === null) { + return ( + <View style={[styles.container, pal.view]} testID="inviteCodesModal"> + <Text type="title-xl" style={[styles.title, pal.text]}> + Error + </Text> + <Text type="lg" style={[styles.description, pal.text]}> + An error occurred while loading invite codes. + </Text> + <View style={styles.flex1} /> + <View + style={[ + styles.btnContainer, + isTabletOrDesktop && styles.btnContainerDesktop, + ]}> + <Button + type="primary" + label="Done" + style={styles.btn} + labelStyle={styles.btnLabel} + onPress={onClose} + /> + </View> + </View> + ) + } + if (store.me.invites.length === 0) { return ( <View style={[styles.container, pal.view]} testID="inviteCodesModal"> diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 2112ec7d1..f75222c1f 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -322,37 +322,45 @@ export const SettingsScreen = withAuthRequired( <View style={styles.spacer20} /> - <Text type="xl-bold" style={[pal.text, styles.heading]}> - Invite a Friend - </Text> - <TouchableOpacity - testID="inviteFriendBtn" - style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} - onPress={isSwitching ? undefined : onPressInviteCodes} - accessibilityRole="button" - accessibilityLabel="Invite" - accessibilityHint="Opens invite code list"> - <View - style={[ - styles.iconContainer, - store.me.invitesAvailable > 0 ? primaryBg : pal.btn, - ]}> - <FontAwesomeIcon - icon="ticket" - style={ - (store.me.invitesAvailable > 0 - ? primaryText - : pal.text) as FontAwesomeIconStyle - } - /> - </View> - <Text - type="lg" - style={store.me.invitesAvailable > 0 ? pal.link : pal.text}> - {formatCount(store.me.invitesAvailable)} invite{' '} - {pluralize(store.me.invitesAvailable, 'code')} available - </Text> - </TouchableOpacity> + {store.me.invitesAvailable !== null && ( + <> + <Text type="xl-bold" style={[pal.text, styles.heading]}> + Invite a Friend + </Text> + <TouchableOpacity + testID="inviteFriendBtn" + style={[ + styles.linkCard, + pal.view, + isSwitching && styles.dimmed, + ]} + onPress={isSwitching ? undefined : onPressInviteCodes} + accessibilityRole="button" + accessibilityLabel="Invite" + accessibilityHint="Opens invite code list"> + <View + style={[ + styles.iconContainer, + store.me.invitesAvailable > 0 ? primaryBg : pal.btn, + ]}> + <FontAwesomeIcon + icon="ticket" + style={ + (store.me.invitesAvailable > 0 + ? primaryText + : pal.text) as FontAwesomeIconStyle + } + /> + </View> + <Text + type="lg" + style={store.me.invitesAvailable > 0 ? pal.link : pal.text}> + {formatCount(store.me.invitesAvailable)} invite{' '} + {pluralize(store.me.invitesAvailable, 'code')} available + </Text> + </TouchableOpacity> + </> + )} <View style={styles.spacer20} /> diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 51a846c4a..48341170c 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -426,32 +426,34 @@ const InviteCodes = observer(function InviteCodesImpl({ store.shell.openModal({name: 'invite-codes'}) }, [store, track]) return ( - <TouchableOpacity - testID="menuItemInviteCodes" - style={[styles.inviteCodes, style]} - onPress={onPress} - accessibilityRole="button" - accessibilityLabel={ - invitesAvailable === 1 - ? 'Invite codes: 1 available' - : `Invite codes: ${invitesAvailable} available` - } - accessibilityHint="Opens list of invite codes"> - <FontAwesomeIcon - icon="ticket" - style={[ - styles.inviteCodesIcon, - store.me.invitesAvailable > 0 ? pal.link : pal.textLight, - ]} - size={18} - /> - <Text - type="lg-medium" - style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> - {formatCount(store.me.invitesAvailable)} invite{' '} - {pluralize(store.me.invitesAvailable, 'code')} - </Text> - </TouchableOpacity> + store.me.invitesAvailable !== null && ( + <TouchableOpacity + testID="menuItemInviteCodes" + style={[styles.inviteCodes, style]} + onPress={onPress} + accessibilityRole="button" + accessibilityLabel={ + invitesAvailable === 1 + ? 'Invite codes: 1 available' + : `Invite codes: ${invitesAvailable} available` + } + accessibilityHint="Opens list of invite codes"> + <FontAwesomeIcon + icon="ticket" + style={[ + styles.inviteCodesIcon, + store.me.invitesAvailable > 0 ? pal.link : pal.textLight, + ]} + size={18} + /> + <Text + type="lg-medium" + style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> + {formatCount(store.me.invitesAvailable)} invite{' '} + {pluralize(store.me.invitesAvailable, 'code')} + </Text> + </TouchableOpacity> + ) ) }) diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index 84d7d7854..f0e986bf4 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -7,6 +7,7 @@ import {DesktopSearch} from './Search' import {DesktopFeeds} from './Feeds' import {Text} from 'view/com/util/text/Text' import {TextLink} from 'view/com/util/Link' +import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' import {s} from 'lib/styles' import {useStores} from 'state/index' @@ -89,32 +90,41 @@ const InviteCodes = observer(function InviteCodesImpl() { const onPress = React.useCallback(() => { store.shell.openModal({name: 'invite-codes'}) }, [store]) + return ( - <TouchableOpacity - style={[styles.inviteCodes, pal.border]} - onPress={onPress} - accessibilityRole="button" - accessibilityLabel={ - invitesAvailable === 1 - ? 'Invite codes: 1 available' - : `Invite codes: ${invitesAvailable} available` - } - accessibilityHint="Opens list of invite codes"> - <FontAwesomeIcon - icon="ticket" - style={[ - styles.inviteCodesIcon, - store.me.invitesAvailable > 0 ? pal.link : pal.textLight, - ]} - size={16} - /> - <Text - type="md-medium" - style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> - {formatCount(store.me.invitesAvailable)} invite{' '} - {pluralize(store.me.invitesAvailable, 'code')} available - </Text> - </TouchableOpacity> + <View style={[styles.separator, pal.border]}> + {store.me.invitesAvailable === null ? ( + <View style={[s.p10]}> + <LoadingPlaceholder width={186} height={32} style={[styles.br40]} /> + </View> + ) : ( + <TouchableOpacity + style={[styles.inviteCodes]} + onPress={onPress} + accessibilityRole="button" + accessibilityLabel={ + invitesAvailable === 1 + ? 'Invite codes: 1 available' + : `Invite codes: ${invitesAvailable} available` + } + accessibilityHint="Opens list of invite codes"> + <FontAwesomeIcon + icon="ticket" + style={[ + styles.inviteCodesIcon, + store.me.invitesAvailable > 0 ? pal.link : pal.textLight, + ]} + size={16} + /> + <Text + type="md-medium" + style={store.me.invitesAvailable > 0 ? pal.link : pal.textLight}> + {formatCount(store.me.invitesAvailable)} invite{' '} + {pluralize(store.me.invitesAvailable, 'code')} available + </Text> + </TouchableOpacity> + )} + </View> ) }) @@ -131,16 +141,20 @@ const styles = StyleSheet.create({ message: { paddingVertical: 18, - paddingHorizontal: 10, + paddingHorizontal: 12, }, messageLine: { marginBottom: 10, }, - inviteCodes: { + separator: { borderTopWidth: 1, - paddingHorizontal: 16, - paddingVertical: 12, + }, + br40: {borderRadius: 40}, + + inviteCodes: { + paddingHorizontal: 12, + paddingVertical: 16, flexDirection: 'row', alignItems: 'center', }, |