Fix invite codes flash on desktop, use loading placeholder (#1591)
* Fix invite codes flashing untrue value before loaded * Add loading placeholder for right nav inviteszio/stable
parent
2ba0c6a711
commit
9278822088
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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} />
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue