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

Configure Feed

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

[๐Ÿด] Make status checks easier, fix load state (#4010)

* Make status checks easier, fix load state

* Make naming more clear

* Split up types for easier re-use

* Replace hacky usage

authored by

Eric Bailey and committed by
GitHub
1c51a487 bffb9b59

+154 -101
+2 -5
src/components/dms/MessageMenu.tsx
··· 7 7 8 8 import {richTextToString} from '#/lib/strings/rich-text-helpers' 9 9 import {isWeb} from 'platform/detection' 10 - import {useConvo} from 'state/messages/convo' 11 - import {ConvoStatus} from 'state/messages/convo/types' 10 + import {useConvoActive} from 'state/messages/convo' 12 11 import {useSession} from 'state/session' 13 12 import * as Toast from '#/view/com/util/Toast' 14 13 import {atoms as a, useTheme} from '#/alf' ··· 34 33 const {_} = useLingui() 35 34 const t = useTheme() 36 35 const {currentAccount} = useSession() 37 - const convo = useConvo() 36 + const convo = useConvoActive() 38 37 const deleteControl = usePromptControl() 39 38 const retryDeleteControl = usePromptControl() 40 39 const reportControl = usePromptControl() ··· 55 54 }, [_, message.text, message.facets]) 56 55 57 56 const onDelete = React.useCallback(() => { 58 - if (convo.status !== ConvoStatus.Ready) return 59 - 60 57 LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 61 58 convo 62 59 .deleteMessage(message.id)
+8 -10
src/screens/Messages/Conversation/MessagesList.tsx
··· 7 7 8 8 import {shortenLinks} from '#/lib/strings/rich-text-manip' 9 9 import {isNative} from '#/platform/detection' 10 - import {useConvo} from '#/state/messages/convo' 11 - import {ConvoItem, ConvoStatus} from '#/state/messages/convo/types' 10 + import {useConvoActive} from '#/state/messages/convo' 11 + import {ConvoItem} from '#/state/messages/convo/types' 12 12 import {useAgent} from '#/state/session' 13 13 import {ScrollProvider} from 'lib/ScrollContext' 14 14 import {isWeb} from 'platform/detection' ··· 60 60 } 61 61 62 62 export function MessagesList() { 63 - const convo = useConvo() 63 + const convo = useConvoActive() 64 64 const {getAgent} = useAgent() 65 65 const flatListRef = useRef<FlatList>(null) 66 66 ··· 128 128 // The check for `hasInitiallyScrolled` prevents an initial fetch on mount. FlatList triggers `onStartReached` 129 129 // immediately on mount, since we are in fact at an offset of zero, so we have to ignore those initial calls. 130 130 const onStartReached = useCallback(() => { 131 - if (convo.status === ConvoStatus.Ready && hasInitiallyScrolled.value) { 131 + if (hasInitiallyScrolled.value) { 132 132 convo.fetchMessageHistory() 133 133 } 134 134 }, [convo, hasInitiallyScrolled]) ··· 150 150 return true 151 151 }) 152 152 153 - if (convo.status === ConvoStatus.Ready) { 154 - convo.sendMessage({ 155 - text: rt.text, 156 - facets: rt.facets, 157 - }) 158 - } 153 + convo.sendMessage({ 154 + text: rt.text, 155 + facets: rt.facets, 156 + }) 159 157 }, 160 158 [convo, getAgent], 161 159 )
+7 -7
src/screens/Messages/Conversation/index.tsx
··· 15 15 import {useCurrentConvoId} from '#/state/messages/current-convo-id' 16 16 import {BACK_HITSLOP} from 'lib/constants' 17 17 import {isIOS, isWeb} from 'platform/detection' 18 - import {ConvoProvider, useConvo} from 'state/messages/convo' 18 + import {ConvoProvider, isConvoActive, useConvo} from 'state/messages/convo' 19 19 import {ConvoStatus} from 'state/messages/convo/types' 20 20 import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' 21 21 import {CenteredView} from 'view/com/util/Views' ··· 72 72 React.useEffect(() => { 73 73 if ( 74 74 !hasInitiallyRendered && 75 - convoState.status === ConvoStatus.Ready && 75 + isConvoActive(convoState) && 76 76 !convoState.isFetchingHistory 77 77 ) { 78 78 setTimeout(() => { 79 79 setHasInitiallyRendered(true) 80 80 }, 15) 81 81 } 82 - }, [convoState.isFetchingHistory, convoState.status, hasInitiallyRendered]) 82 + }, [convoState, hasInitiallyRendered]) 83 83 84 84 if (convoState.status === ConvoStatus.Error) { 85 85 return ( ··· 108 108 <CenteredView style={a.flex_1} sideBorders> 109 109 <Header profile={convoState.recipients?.[0]} /> 110 110 <View style={[a.flex_1]}> 111 - {convoState.status !== ConvoStatus.Ready ? ( 112 - <ListMaybePlaceholder isLoading /> 113 - ) : ( 111 + {isConvoActive(convoState) ? ( 114 112 <MessagesList /> 113 + ) : ( 114 + <ListMaybePlaceholder isLoading /> 115 115 )} 116 116 {!hasInitiallyRendered && ( 117 117 <View ··· 230 230 </> 231 231 )} 232 232 </View> 233 - {convoState.status === ConvoStatus.Ready && profile ? ( 233 + {isConvoActive(convoState) && profile ? ( 234 234 <ConvoMenu 235 235 convo={convoState.convo} 236 236 profile={profile}
+31 -1
src/state/messages/convo/index.tsx
··· 3 3 import {useFocusEffect, useIsFocused} from '@react-navigation/native' 4 4 5 5 import {Convo} from '#/state/messages/convo/agent' 6 - import {ConvoParams, ConvoState} from '#/state/messages/convo/types' 6 + import { 7 + ConvoParams, 8 + ConvoState, 9 + ConvoStateBackgrounded, 10 + ConvoStateReady, 11 + ConvoStateSuspended, 12 + } from '#/state/messages/convo/types' 13 + import {isConvoActive} from '#/state/messages/convo/util' 7 14 import {useMessagesEventBus} from '#/state/messages/events' 8 15 import {useMarkAsReadMutation} from '#/state/queries/messages/conversation' 9 16 import {useAgent} from '#/state/session' 17 + 18 + export * from '#/state/messages/convo/util' 10 19 11 20 const ChatContext = React.createContext<ConvoState | null>(null) 12 21 ··· 14 23 const ctx = useContext(ChatContext) 15 24 if (!ctx) { 16 25 throw new Error('useConvo must be used within a ConvoProvider') 26 + } 27 + return ctx 28 + } 29 + 30 + /** 31 + * This hook should only be used when the Convo is "active", meaning the chat 32 + * is loaded and ready to be used, or its in a suspended or background state, 33 + * and ready for resumption. 34 + */ 35 + export function useConvoActive() { 36 + const ctx = useContext(ChatContext) as 37 + | ConvoStateReady 38 + | ConvoStateBackgrounded 39 + | ConvoStateSuspended 40 + if (!ctx) { 41 + throw new Error('useConvo must be used within a ConvoProvider') 42 + } 43 + if (!isConvoActive(ctx)) { 44 + throw new Error( 45 + `useConvoActive must only be rendered when the Convo is ready.`, 46 + ) 17 47 } 18 48 return ctx 19 49 }
+84 -78
src/state/messages/convo/types.ts
··· 107 107 retry: () => void 108 108 } 109 109 110 + type DeleteMessage = (messageId: string) => Promise<void> 111 + type SendMessage = ( 112 + message: ChatBskyConvoSendMessage.InputSchema['message'], 113 + ) => Promise<void> 114 + type FetchMessageHistory = () => Promise<void> 115 + 116 + export type ConvoStateUninitialized = { 117 + status: ConvoStatus.Uninitialized 118 + items: [] 119 + convo: undefined 120 + error: undefined 121 + sender: undefined 122 + recipients: undefined 123 + isFetchingHistory: false 124 + deleteMessage: undefined 125 + sendMessage: undefined 126 + fetchMessageHistory: undefined 127 + } 128 + export type ConvoStateInitializing = { 129 + status: ConvoStatus.Initializing 130 + items: [] 131 + convo: undefined 132 + error: undefined 133 + sender: undefined 134 + recipients: undefined 135 + isFetchingHistory: boolean 136 + deleteMessage: undefined 137 + sendMessage: undefined 138 + fetchMessageHistory: undefined 139 + } 140 + export type ConvoStateReady = { 141 + status: ConvoStatus.Ready 142 + items: ConvoItem[] 143 + convo: ChatBskyConvoDefs.ConvoView 144 + error: undefined 145 + sender: AppBskyActorDefs.ProfileViewBasic 146 + recipients: AppBskyActorDefs.ProfileViewBasic[] 147 + isFetchingHistory: boolean 148 + deleteMessage: DeleteMessage 149 + sendMessage: SendMessage 150 + fetchMessageHistory: FetchMessageHistory 151 + } 152 + export type ConvoStateBackgrounded = { 153 + status: ConvoStatus.Backgrounded 154 + items: ConvoItem[] 155 + convo: ChatBskyConvoDefs.ConvoView 156 + error: undefined 157 + sender: AppBskyActorDefs.ProfileViewBasic 158 + recipients: AppBskyActorDefs.ProfileViewBasic[] 159 + isFetchingHistory: boolean 160 + deleteMessage: DeleteMessage 161 + sendMessage: SendMessage 162 + fetchMessageHistory: FetchMessageHistory 163 + } 164 + export type ConvoStateSuspended = { 165 + status: ConvoStatus.Suspended 166 + items: ConvoItem[] 167 + convo: ChatBskyConvoDefs.ConvoView 168 + error: undefined 169 + sender: AppBskyActorDefs.ProfileViewBasic 170 + recipients: AppBskyActorDefs.ProfileViewBasic[] 171 + isFetchingHistory: boolean 172 + deleteMessage: DeleteMessage 173 + sendMessage: SendMessage 174 + fetchMessageHistory: FetchMessageHistory 175 + } 176 + export type ConvoStateError = { 177 + status: ConvoStatus.Error 178 + items: [] 179 + convo: undefined 180 + error: any 181 + sender: undefined 182 + recipients: undefined 183 + isFetchingHistory: false 184 + deleteMessage: undefined 185 + sendMessage: undefined 186 + fetchMessageHistory: undefined 187 + } 110 188 export type ConvoState = 111 - | { 112 - status: ConvoStatus.Uninitialized 113 - items: [] 114 - convo: undefined 115 - error: undefined 116 - sender: undefined 117 - recipients: undefined 118 - isFetchingHistory: false 119 - deleteMessage: undefined 120 - sendMessage: undefined 121 - fetchMessageHistory: undefined 122 - } 123 - | { 124 - status: ConvoStatus.Initializing 125 - items: [] 126 - convo: undefined 127 - error: undefined 128 - sender: undefined 129 - recipients: undefined 130 - isFetchingHistory: boolean 131 - deleteMessage: undefined 132 - sendMessage: undefined 133 - fetchMessageHistory: undefined 134 - } 135 - | { 136 - status: ConvoStatus.Ready 137 - items: ConvoItem[] 138 - convo: ChatBskyConvoDefs.ConvoView 139 - error: undefined 140 - sender: AppBskyActorDefs.ProfileViewBasic 141 - recipients: AppBskyActorDefs.ProfileViewBasic[] 142 - isFetchingHistory: boolean 143 - deleteMessage: (messageId: string) => Promise<void> 144 - sendMessage: ( 145 - message: ChatBskyConvoSendMessage.InputSchema['message'], 146 - ) => void 147 - fetchMessageHistory: () => void 148 - } 149 - | { 150 - status: ConvoStatus.Suspended 151 - items: ConvoItem[] 152 - convo: ChatBskyConvoDefs.ConvoView 153 - error: undefined 154 - sender: AppBskyActorDefs.ProfileViewBasic 155 - recipients: AppBskyActorDefs.ProfileViewBasic[] 156 - isFetchingHistory: boolean 157 - deleteMessage: (messageId: string) => Promise<void> 158 - sendMessage: ( 159 - message: ChatBskyConvoSendMessage.InputSchema['message'], 160 - ) => Promise<void> 161 - fetchMessageHistory: () => Promise<void> 162 - } 163 - | { 164 - status: ConvoStatus.Backgrounded 165 - items: ConvoItem[] 166 - convo: ChatBskyConvoDefs.ConvoView 167 - error: undefined 168 - sender: AppBskyActorDefs.ProfileViewBasic 169 - recipients: AppBskyActorDefs.ProfileViewBasic[] 170 - isFetchingHistory: boolean 171 - deleteMessage: (messageId: string) => Promise<void> 172 - sendMessage: ( 173 - message: ChatBskyConvoSendMessage.InputSchema['message'], 174 - ) => Promise<void> 175 - fetchMessageHistory: () => Promise<void> 176 - } 177 - | { 178 - status: ConvoStatus.Error 179 - items: [] 180 - convo: undefined 181 - error: any 182 - sender: undefined 183 - recipients: undefined 184 - isFetchingHistory: false 185 - deleteMessage: undefined 186 - sendMessage: undefined 187 - fetchMessageHistory: undefined 188 - } 189 + | ConvoStateUninitialized 190 + | ConvoStateInitializing 191 + | ConvoStateReady 192 + | ConvoStateBackgrounded 193 + | ConvoStateSuspended 194 + | ConvoStateError
+22
src/state/messages/convo/util.ts
··· 1 + import { 2 + ConvoState, 3 + ConvoStateBackgrounded, 4 + ConvoStateReady, 5 + ConvoStateSuspended, 6 + ConvoStatus, 7 + } from './types' 8 + 9 + /** 10 + * Checks if a `Convo` has a `status` that is "active", meaning the chat is 11 + * loaded and ready to be used, or its in a suspended or background state, and 12 + * ready for resumption. 13 + */ 14 + export function isConvoActive( 15 + convo: ConvoState, 16 + ): convo is ConvoStateReady | ConvoStateBackgrounded | ConvoStateSuspended { 17 + return ( 18 + convo.status === ConvoStatus.Ready || 19 + convo.status === ConvoStatus.Backgrounded || 20 + convo.status === ConvoStatus.Suspended 21 + ) 22 + }