wait for list memberships to load before becoming interactive (#1140)

* wait for list memberships to load before becoming interactive

* add spinner

* remove secondary spinner
zio/stable
Eric Bailey 2023-08-09 17:50:40 -05:00 committed by GitHub
parent b5511e1450
commit 48813a96d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 31 deletions

View File

@ -1,6 +1,5 @@
import React, {MutableRefObject} from 'react' import React, {MutableRefObject} from 'react'
import { import {
ActivityIndicator,
RefreshControl, RefreshControl,
StyleProp, StyleProp,
StyleSheet, StyleSheet,
@ -166,18 +165,6 @@ export const ListsList = observer(
], ],
) )
const Footer = React.useCallback(
() =>
listsList.isLoading ? (
<View style={styles.feedFooter}>
<ActivityIndicator />
</View>
) : (
<View />
),
[listsList],
)
return ( return (
<View testID={testID} style={style}> <View testID={testID} style={style}>
{data.length > 0 && ( {data.length > 0 && (
@ -187,7 +174,6 @@ export const ListsList = observer(
data={data} data={data}
keyExtractor={item => item._reactKey} keyExtractor={item => item._reactKey}
renderItem={renderItemInner} renderItem={renderItemInner}
ListFooterComponent={Footer}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={isRefreshing} refreshing={isRefreshing}

View File

@ -1,6 +1,6 @@
import React, {useCallback} from 'react' import React, {useCallback} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {Pressable, StyleSheet, View} from 'react-native' import {Pressable, StyleSheet, View, ActivityIndicator} from 'react-native'
import {AppBskyGraphDefs as GraphDefs} from '@atproto/api' import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
import { import {
FontAwesomeIcon, FontAwesomeIcon,
@ -42,6 +42,7 @@ export const Component = observer(
string[] string[]
>([]) >([])
const [selected, setSelected] = React.useState<string[]>([]) const [selected, setSelected] = React.useState<string[]>([])
const [membershipsLoaded, setMembershipsLoaded] = React.useState(false)
const listsList: ListsListModel = React.useMemo( const listsList: ListsListModel = React.useMemo(
() => new ListsListModel(store, store.me.did), () => new ListsListModel(store, store.me.did),
@ -58,12 +59,13 @@ export const Component = observer(
const ids = memberships.memberships.map(m => m.value.list) const ids = memberships.memberships.map(m => m.value.list)
setOriginalSelections(ids) setOriginalSelections(ids)
setSelected(ids) setSelected(ids)
setMembershipsLoaded(true)
}, },
err => { err => {
store.log.error('Failed to fetch memberships', {err}) store.log.error('Failed to fetch memberships', {err})
}, },
) )
}, [memberships, listsList, store, setSelected]) }, [memberships, listsList, store, setSelected, setMembershipsLoaded])
const onPressCancel = useCallback(() => { const onPressCancel = useCallback(() => {
store.shell.closeModal() store.shell.closeModal()
@ -107,11 +109,16 @@ export const Component = observer(
return ( return (
<Pressable <Pressable
testID={`toggleBtn-${list.name}`} testID={`toggleBtn-${list.name}`}
style={[styles.listItem, pal.border]} style={[
styles.listItem,
pal.border,
{opacity: membershipsLoaded ? 1 : 0.5},
]}
accessibilityLabel={`${isSelected ? 'Remove from' : 'Add to'} ${ accessibilityLabel={`${isSelected ? 'Remove from' : 'Add to'} ${
list.name list.name
}`} }`}
accessibilityHint="" accessibilityHint=""
disabled={!membershipsLoaded}
onPress={() => onToggleSelected(list.uri)}> onPress={() => onToggleSelected(list.uri)}>
<View style={styles.listItemAvi}> <View style={styles.listItemAvi}>
<UserAvatar size={40} avatar={list.avatar} /> <UserAvatar size={40} avatar={list.avatar} />
@ -132,23 +139,33 @@ export const Component = observer(
: sanitizeHandle(list.creator.handle, '@')} : sanitizeHandle(list.creator.handle, '@')}
</Text> </Text>
</View> </View>
<View {membershipsLoaded && (
style={ <View
isSelected style={
? [styles.checkbox, palPrimary.border, palPrimary.view] isSelected
: [styles.checkbox, pal.borderDark] ? [styles.checkbox, palPrimary.border, palPrimary.view]
}> : [styles.checkbox, pal.borderDark]
{isSelected && ( }>
<FontAwesomeIcon {isSelected && (
icon="check" <FontAwesomeIcon
style={palInverted.text as FontAwesomeIconStyle} icon="check"
/> style={palInverted.text as FontAwesomeIconStyle}
)} />
</View> )}
</View>
)}
</Pressable> </Pressable>
) )
}, },
[pal, palPrimary, palInverted, onToggleSelected, selected, store.me.did], [
pal,
palPrimary,
palInverted,
onToggleSelected,
selected,
store.me.did,
membershipsLoaded,
],
) )
const renderEmptyState = React.useCallback(() => { const renderEmptyState = React.useCallback(() => {
@ -200,6 +217,12 @@ export const Component = observer(
label="Save Changes" label="Save Changes"
/> />
)} )}
{(listsList.isLoading || !membershipsLoaded) && (
<View style={styles.loadingContainer}>
<ActivityIndicator />
</View>
)}
</View> </View>
</View> </View>
) )
@ -221,6 +244,7 @@ const styles = StyleSheet.create({
borderTopWidth: 1, borderTopWidth: 1,
}, },
btns: { btns: {
position: 'relative',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@ -263,4 +287,11 @@ const styles = StyleSheet.create({
borderRadius: 6, borderRadius: 6,
marginRight: 8, marginRight: 8,
}, },
loadingContainer: {
position: 'absolute',
top: 10,
right: 0,
bottom: 0,
justifyContent: 'center',
},
}) })