Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

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

[๐Ÿด] Remove extra spinner states from chat screen (#3947)

* remove extra loading states from chat

* nits

* fix scrolling animation to bottom

* nit

* move spinner to top

authored by

Hailey and committed by
GitHub
1a904260 195c9f10

+79 -32
+14 -12
src/screens/Messages/Conversation/MessagesList.tsx
··· 10 10 import {AppBskyRichtextFacet, RichText} from '@atproto/api' 11 11 12 12 import {shortenLinks} from '#/lib/strings/rich-text-manip' 13 - import {isIOS} from '#/platform/detection' 13 + import {isIOS, isNative} from '#/platform/detection' 14 14 import {useConvo} from '#/state/messages/convo' 15 15 import {ConvoItem, ConvoStatus} from '#/state/messages/convo/types' 16 16 import {useAgent} from '#/state/session' ··· 85 85 // Instead, we use `onMomentumScrollEnd` and this value to determine if we need to start scrolling or not. 86 86 const isMomentumScrolling = useSharedValue(false) 87 87 88 - const [hasInitiallyScrolled, setHasInitiallyScrolled] = React.useState(false) 88 + const hasInitiallyScrolled = useSharedValue(false) 89 89 90 90 // Every time the content size changes, that means one of two things is happening: 91 91 // 1. New messages are being added from the log or from a message you have sent ··· 101 101 (_: number, height: number) => { 102 102 // Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the 103 103 // previous offset whenever we add new content to the previous offset whenever we add new content to the list. 104 - if (isWeb && isAtTop.value && hasInitiallyScrolled) { 104 + if (isWeb && isAtTop.value && hasInitiallyScrolled.value) { 105 105 flatListRef.current?.scrollToOffset({ 106 106 animated: false, 107 107 offset: height - contentHeight.value, ··· 116 116 } 117 117 118 118 flatListRef.current?.scrollToOffset({ 119 - animated: hasInitiallyScrolled, 119 + animated: hasInitiallyScrolled.value, 120 120 offset: height, 121 121 }) 122 122 isMomentumScrolling.value = true ··· 133 133 // The check for `hasInitiallyScrolled` prevents an initial fetch on mount. FlatList triggers `onStartReached` 134 134 // immediately on mount, since we are in fact at an offset of zero, so we have to ignore those initial calls. 135 135 const onStartReached = useCallback(() => { 136 - if (convo.status === ConvoStatus.Ready && hasInitiallyScrolled) { 136 + if (convo.status === ConvoStatus.Ready && hasInitiallyScrolled.value) { 137 137 convo.fetchMessageHistory() 138 138 } 139 139 }, [convo, hasInitiallyScrolled]) ··· 178 178 // This number _must_ be the height of the MaybeLoader component. 179 179 // We don't check for zero, because the `MaybeLoader` component is always present, even when not visible, which 180 180 // adds a 50 pixel offset. 181 - if (contentHeight.value > 50 && !hasInitiallyScrolled) { 182 - runOnJS(setHasInitiallyScrolled)(true) 181 + if (contentHeight.value > 50 && !hasInitiallyScrolled.value) { 182 + hasInitiallyScrolled.value = true 183 183 } 184 184 }, 185 185 [contentHeight.value, hasInitiallyScrolled, isAtBottom, isAtTop], ··· 228 228 data={convo.items} 229 229 renderItem={renderItem} 230 230 keyExtractor={keyExtractor} 231 + containWeb={true} 232 + contentContainerStyle={{ 233 + paddingHorizontal: 10, 234 + }} 231 235 disableVirtualization={true} 232 - initialNumToRender={isWeb ? 50 : 25} 233 - maxToRenderPerBatch={isWeb ? 50 : 25} 236 + initialNumToRender={isNative ? 30 : 60} 237 + maxToRenderPerBatch={isWeb ? 30 : 60} 234 238 keyboardDismissMode="on-drag" 235 239 keyboardShouldPersistTaps="handled" 236 240 maintainVisibleContentPosition={{ 237 241 minIndexForVisible: 1, 238 242 }} 239 - containWeb={true} 240 - contentContainerStyle={{paddingHorizontal: 10}} 241 243 removeClippedSubviews={false} 244 + sideBorders={false} 242 245 onContentSizeChange={onContentSizeChange} 243 246 onStartReached={onStartReached} 244 247 onScrollToIndexFailed={onScrollToIndexFailed} ··· 246 249 ListHeaderComponent={ 247 250 <MaybeLoader isLoading={convo.isFetchingHistory} /> 248 251 } 249 - sideBorders={false} 250 252 /> 251 253 </ScrollProvider> 252 254 <MessageInput onSendMessage={onSendMessage} scrollToEnd={scrollToEnd} />
+64 -19
src/screens/Messages/Conversation/index.tsx
··· 22 22 import {ConvoMenu} from '#/components/dms/ConvoMenu' 23 23 import {Error} from '#/components/Error' 24 24 import {ListMaybePlaceholder} from '#/components/Lists' 25 + import {Loader} from '#/components/Loader' 25 26 import {Text} from '#/components/Typography' 26 27 import {ClipClopGate} from '../gate' 27 28 ··· 53 54 } 54 55 55 56 function Inner() { 57 + const t = useTheme() 56 58 const convo = useConvo() 57 59 const {_} = useLingui() 58 60 59 - if ( 60 - convo.status === ConvoStatus.Uninitialized || 61 - convo.status === ConvoStatus.Initializing 62 - ) { 63 - return ( 64 - <CenteredView style={a.flex_1} sideBorders> 65 - <Header /> 66 - <ListMaybePlaceholder isLoading /> 67 - </CenteredView> 68 - ) 69 - } 61 + const [hasInitiallyRendered, setHasInitiallyRendered] = React.useState(false) 62 + 63 + // HACK: Because we need to scroll to the bottom of the list once initial items are added to the list, we also have 64 + // to take into account that scrolling to the end of the list on native will happen asynchronously. This will cause 65 + // a little flicker when the items are first renedered at the top and immediately scrolled to the bottom. to prevent 66 + // this, we will wait until the first render has completed to remove the loading overlay. 67 + React.useEffect(() => { 68 + if ( 69 + !hasInitiallyRendered && 70 + convo.status === ConvoStatus.Ready && 71 + !convo.isFetchingHistory 72 + ) { 73 + setTimeout(() => { 74 + setHasInitiallyRendered(true) 75 + }, 15) 76 + } 77 + }, [convo.isFetchingHistory, convo.items, convo.status, hasInitiallyRendered]) 70 78 71 79 if (convo.status === ConvoStatus.Error) { 72 80 return ( ··· 88 96 return ( 89 97 <KeyboardProvider> 90 98 <CenteredView style={a.flex_1} sideBorders> 91 - <Header profile={convo.recipients[0]} /> 92 - <MessagesList /> 99 + <Header profile={convo.recipients?.[0]} /> 100 + <View style={[a.flex_1]}> 101 + {convo.status !== ConvoStatus.Ready ? ( 102 + <ListMaybePlaceholder isLoading /> 103 + ) : ( 104 + <MessagesList /> 105 + )} 106 + {!hasInitiallyRendered && ( 107 + <View 108 + style={[ 109 + a.absolute, 110 + a.z_10, 111 + a.w_full, 112 + a.h_full, 113 + a.justify_center, 114 + a.align_center, 115 + t.atoms.bg, 116 + ]}> 117 + <View style={[{marginBottom: 75}]}> 118 + <Loader size="xl" /> 119 + </View> 120 + </View> 121 + )} 122 + </View> 93 123 </CenteredView> 94 124 </KeyboardProvider> 95 125 ) ··· 128 158 a.justify_between, 129 159 a.align_start, 130 160 a.gap_lg, 131 - a.px_lg, 161 + a.pl_xl, 162 + a.pr_lg, 132 163 a.py_sm, 133 164 ]}> 134 165 {!gtTablet ? ( ··· 154 185 )} 155 186 <View style={[a.align_center, a.gap_sm, a.flex_1]}> 156 187 {profile ? ( 157 - <> 188 + <View style={[a.align_center]}> 158 189 <PreviewableUserAvatar size={32} profile={profile} /> 159 - <Text style={[a.text_lg, a.font_bold, a.text_center]}> 190 + <Text 191 + style={[a.text_lg, a.font_bold, isWeb ? a.mt_md : a.mt_sm]} 192 + numberOfLines={1}> 160 193 {profile.displayName} 161 194 </Text> 162 - </> 195 + <Text 196 + style={[t.atoms.text_contrast_medium, {fontSize: 15}]} 197 + numberOfLines={1}> 198 + @{profile.handle} 199 + </Text> 200 + </View> 163 201 ) : ( 164 202 <> 165 203 <View ··· 171 209 /> 172 210 <View 173 211 style={[ 174 - {width: 120, height: 18}, 212 + {width: 120, height: 16}, 175 213 a.rounded_xs, 176 214 t.atoms.bg_contrast_25, 177 - a.mb_2xs, 215 + a.mt_xs, 216 + ]} 217 + /> 218 + <View 219 + style={[ 220 + {width: 175, height: 12}, 221 + a.rounded_xs, 222 + t.atoms.bg_contrast_25, 178 223 ]} 179 224 /> 180 225 </>
+1 -1
src/state/messages/convo/agent.ts
··· 554 554 { 555 555 cursor: nextCursor, 556 556 convoId: this.convoId, 557 - limit: isNative ? 40 : 60, 557 + limit: isNative ? 30 : 60, 558 558 }, 559 559 { 560 560 headers: {