diff options
author | Eric Bailey <git@esb.lol> | 2024-05-14 11:59:53 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-14 11:59:53 -0500 |
commit | 1c51a48764e4145679198f68368713410e28c8da (patch) | |
tree | 6f2840665b10dfbb3591f0a694c9d5165f383be3 | |
parent | bffb9b590672c1e636083bdf9873f5cd8ab97b57 (diff) | |
download | voidsky-1c51a48764e4145679198f68368713410e28c8da.tar.zst |
[🐴] 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
-rw-r--r-- | src/components/dms/MessageMenu.tsx | 7 | ||||
-rw-r--r-- | src/screens/Messages/Conversation/MessagesList.tsx | 18 | ||||
-rw-r--r-- | src/screens/Messages/Conversation/index.tsx | 14 | ||||
-rw-r--r-- | src/state/messages/convo/index.tsx | 32 | ||||
-rw-r--r-- | src/state/messages/convo/types.ts | 162 | ||||
-rw-r--r-- | src/state/messages/convo/util.ts | 22 |
6 files changed, 154 insertions, 101 deletions
diff --git a/src/components/dms/MessageMenu.tsx b/src/components/dms/MessageMenu.tsx index 3349b2ff8..55c3ac21b 100644 --- a/src/components/dms/MessageMenu.tsx +++ b/src/components/dms/MessageMenu.tsx @@ -7,8 +7,7 @@ import {useLingui} from '@lingui/react' import {richTextToString} from '#/lib/strings/rich-text-helpers' import {isWeb} from 'platform/detection' -import {useConvo} from 'state/messages/convo' -import {ConvoStatus} from 'state/messages/convo/types' +import {useConvoActive} from 'state/messages/convo' import {useSession} from 'state/session' import * as Toast from '#/view/com/util/Toast' import {atoms as a, useTheme} from '#/alf' @@ -34,7 +33,7 @@ export let MessageMenu = ({ const {_} = useLingui() const t = useTheme() const {currentAccount} = useSession() - const convo = useConvo() + const convo = useConvoActive() const deleteControl = usePromptControl() const retryDeleteControl = usePromptControl() const reportControl = usePromptControl() @@ -55,8 +54,6 @@ export let MessageMenu = ({ }, [_, message.text, message.facets]) const onDelete = React.useCallback(() => { - if (convo.status !== ConvoStatus.Ready) return - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) convo .deleteMessage(message.id) diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx index 5ba82eeff..dac534cd4 100644 --- a/src/screens/Messages/Conversation/MessagesList.tsx +++ b/src/screens/Messages/Conversation/MessagesList.tsx @@ -7,8 +7,8 @@ import {AppBskyRichtextFacet, RichText} from '@atproto/api' import {shortenLinks} from '#/lib/strings/rich-text-manip' import {isNative} from '#/platform/detection' -import {useConvo} from '#/state/messages/convo' -import {ConvoItem, ConvoStatus} from '#/state/messages/convo/types' +import {useConvoActive} from '#/state/messages/convo' +import {ConvoItem} from '#/state/messages/convo/types' import {useAgent} from '#/state/session' import {ScrollProvider} from 'lib/ScrollContext' import {isWeb} from 'platform/detection' @@ -60,7 +60,7 @@ function onScrollToIndexFailed() { } export function MessagesList() { - const convo = useConvo() + const convo = useConvoActive() const {getAgent} = useAgent() const flatListRef = useRef<FlatList>(null) @@ -128,7 +128,7 @@ export function MessagesList() { // The check for `hasInitiallyScrolled` prevents an initial fetch on mount. FlatList triggers `onStartReached` // immediately on mount, since we are in fact at an offset of zero, so we have to ignore those initial calls. const onStartReached = useCallback(() => { - if (convo.status === ConvoStatus.Ready && hasInitiallyScrolled.value) { + if (hasInitiallyScrolled.value) { convo.fetchMessageHistory() } }, [convo, hasInitiallyScrolled]) @@ -150,12 +150,10 @@ export function MessagesList() { return true }) - if (convo.status === ConvoStatus.Ready) { - convo.sendMessage({ - text: rt.text, - facets: rt.facets, - }) - } + convo.sendMessage({ + text: rt.text, + facets: rt.facets, + }) }, [convo, getAgent], ) diff --git a/src/screens/Messages/Conversation/index.tsx b/src/screens/Messages/Conversation/index.tsx index a783a0bd6..01c205ac8 100644 --- a/src/screens/Messages/Conversation/index.tsx +++ b/src/screens/Messages/Conversation/index.tsx @@ -15,7 +15,7 @@ import {useGate} from '#/lib/statsig/statsig' import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {BACK_HITSLOP} from 'lib/constants' import {isIOS, isWeb} from 'platform/detection' -import {ConvoProvider, useConvo} from 'state/messages/convo' +import {ConvoProvider, isConvoActive, useConvo} from 'state/messages/convo' import {ConvoStatus} from 'state/messages/convo/types' import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' import {CenteredView} from 'view/com/util/Views' @@ -72,14 +72,14 @@ function Inner() { React.useEffect(() => { if ( !hasInitiallyRendered && - convoState.status === ConvoStatus.Ready && + isConvoActive(convoState) && !convoState.isFetchingHistory ) { setTimeout(() => { setHasInitiallyRendered(true) }, 15) } - }, [convoState.isFetchingHistory, convoState.status, hasInitiallyRendered]) + }, [convoState, hasInitiallyRendered]) if (convoState.status === ConvoStatus.Error) { return ( @@ -108,10 +108,10 @@ function Inner() { <CenteredView style={a.flex_1} sideBorders> <Header profile={convoState.recipients?.[0]} /> <View style={[a.flex_1]}> - {convoState.status !== ConvoStatus.Ready ? ( - <ListMaybePlaceholder isLoading /> - ) : ( + {isConvoActive(convoState) ? ( <MessagesList /> + ) : ( + <ListMaybePlaceholder isLoading /> )} {!hasInitiallyRendered && ( <View @@ -230,7 +230,7 @@ let Header = ({ </> )} </View> - {convoState.status === ConvoStatus.Ready && profile ? ( + {isConvoActive(convoState) && profile ? ( <ConvoMenu convo={convoState.convo} profile={profile} diff --git a/src/state/messages/convo/index.tsx b/src/state/messages/convo/index.tsx index 9c5295832..e955d4118 100644 --- a/src/state/messages/convo/index.tsx +++ b/src/state/messages/convo/index.tsx @@ -3,11 +3,20 @@ import {AppState} from 'react-native' import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {Convo} from '#/state/messages/convo/agent' -import {ConvoParams, ConvoState} from '#/state/messages/convo/types' +import { + ConvoParams, + ConvoState, + ConvoStateBackgrounded, + ConvoStateReady, + ConvoStateSuspended, +} from '#/state/messages/convo/types' +import {isConvoActive} from '#/state/messages/convo/util' import {useMessagesEventBus} from '#/state/messages/events' import {useMarkAsReadMutation} from '#/state/queries/messages/conversation' import {useAgent} from '#/state/session' +export * from '#/state/messages/convo/util' + const ChatContext = React.createContext<ConvoState | null>(null) export function useConvo() { @@ -18,6 +27,27 @@ export function useConvo() { return ctx } +/** + * This hook should only be used when the Convo is "active", meaning the chat + * is loaded and ready to be used, or its in a suspended or background state, + * and ready for resumption. + */ +export function useConvoActive() { + const ctx = useContext(ChatContext) as + | ConvoStateReady + | ConvoStateBackgrounded + | ConvoStateSuspended + if (!ctx) { + throw new Error('useConvo must be used within a ConvoProvider') + } + if (!isConvoActive(ctx)) { + throw new Error( + `useConvoActive must only be rendered when the Convo is ready.`, + ) + } + return ctx +} + export function ConvoProvider({ children, convoId, diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts index 4615acc2d..6ce4d40bd 100644 --- a/src/state/messages/convo/types.ts +++ b/src/state/messages/convo/types.ts @@ -107,82 +107,88 @@ export type ConvoItem = retry: () => void } +type DeleteMessage = (messageId: string) => Promise<void> +type SendMessage = ( + message: ChatBskyConvoSendMessage.InputSchema['message'], +) => Promise<void> +type FetchMessageHistory = () => Promise<void> + +export type ConvoStateUninitialized = { + status: ConvoStatus.Uninitialized + items: [] + convo: undefined + error: undefined + sender: undefined + recipients: undefined + isFetchingHistory: false + deleteMessage: undefined + sendMessage: undefined + fetchMessageHistory: undefined +} +export type ConvoStateInitializing = { + status: ConvoStatus.Initializing + items: [] + convo: undefined + error: undefined + sender: undefined + recipients: undefined + isFetchingHistory: boolean + deleteMessage: undefined + sendMessage: undefined + fetchMessageHistory: undefined +} +export type ConvoStateReady = { + status: ConvoStatus.Ready + items: ConvoItem[] + convo: ChatBskyConvoDefs.ConvoView + error: undefined + sender: AppBskyActorDefs.ProfileViewBasic + recipients: AppBskyActorDefs.ProfileViewBasic[] + isFetchingHistory: boolean + deleteMessage: DeleteMessage + sendMessage: SendMessage + fetchMessageHistory: FetchMessageHistory +} +export type ConvoStateBackgrounded = { + status: ConvoStatus.Backgrounded + items: ConvoItem[] + convo: ChatBskyConvoDefs.ConvoView + error: undefined + sender: AppBskyActorDefs.ProfileViewBasic + recipients: AppBskyActorDefs.ProfileViewBasic[] + isFetchingHistory: boolean + deleteMessage: DeleteMessage + sendMessage: SendMessage + fetchMessageHistory: FetchMessageHistory +} +export type ConvoStateSuspended = { + status: ConvoStatus.Suspended + items: ConvoItem[] + convo: ChatBskyConvoDefs.ConvoView + error: undefined + sender: AppBskyActorDefs.ProfileViewBasic + recipients: AppBskyActorDefs.ProfileViewBasic[] + isFetchingHistory: boolean + deleteMessage: DeleteMessage + sendMessage: SendMessage + fetchMessageHistory: FetchMessageHistory +} +export type ConvoStateError = { + status: ConvoStatus.Error + items: [] + convo: undefined + error: any + sender: undefined + recipients: undefined + isFetchingHistory: false + deleteMessage: undefined + sendMessage: undefined + fetchMessageHistory: undefined +} export type ConvoState = - | { - status: ConvoStatus.Uninitialized - items: [] - convo: undefined - error: undefined - sender: undefined - recipients: undefined - isFetchingHistory: false - deleteMessage: undefined - sendMessage: undefined - fetchMessageHistory: undefined - } - | { - status: ConvoStatus.Initializing - items: [] - convo: undefined - error: undefined - sender: undefined - recipients: undefined - isFetchingHistory: boolean - deleteMessage: undefined - sendMessage: undefined - fetchMessageHistory: undefined - } - | { - status: ConvoStatus.Ready - items: ConvoItem[] - convo: ChatBskyConvoDefs.ConvoView - error: undefined - sender: AppBskyActorDefs.ProfileViewBasic - recipients: AppBskyActorDefs.ProfileViewBasic[] - isFetchingHistory: boolean - deleteMessage: (messageId: string) => Promise<void> - sendMessage: ( - message: ChatBskyConvoSendMessage.InputSchema['message'], - ) => void - fetchMessageHistory: () => void - } - | { - status: ConvoStatus.Suspended - items: ConvoItem[] - convo: ChatBskyConvoDefs.ConvoView - error: undefined - sender: AppBskyActorDefs.ProfileViewBasic - recipients: AppBskyActorDefs.ProfileViewBasic[] - isFetchingHistory: boolean - deleteMessage: (messageId: string) => Promise<void> - sendMessage: ( - message: ChatBskyConvoSendMessage.InputSchema['message'], - ) => Promise<void> - fetchMessageHistory: () => Promise<void> - } - | { - status: ConvoStatus.Backgrounded - items: ConvoItem[] - convo: ChatBskyConvoDefs.ConvoView - error: undefined - sender: AppBskyActorDefs.ProfileViewBasic - recipients: AppBskyActorDefs.ProfileViewBasic[] - isFetchingHistory: boolean - deleteMessage: (messageId: string) => Promise<void> - sendMessage: ( - message: ChatBskyConvoSendMessage.InputSchema['message'], - ) => Promise<void> - fetchMessageHistory: () => Promise<void> - } - | { - status: ConvoStatus.Error - items: [] - convo: undefined - error: any - sender: undefined - recipients: undefined - isFetchingHistory: false - deleteMessage: undefined - sendMessage: undefined - fetchMessageHistory: undefined - } + | ConvoStateUninitialized + | ConvoStateInitializing + | ConvoStateReady + | ConvoStateBackgrounded + | ConvoStateSuspended + | ConvoStateError diff --git a/src/state/messages/convo/util.ts b/src/state/messages/convo/util.ts new file mode 100644 index 000000000..ffaa4104a --- /dev/null +++ b/src/state/messages/convo/util.ts @@ -0,0 +1,22 @@ +import { + ConvoState, + ConvoStateBackgrounded, + ConvoStateReady, + ConvoStateSuspended, + ConvoStatus, +} from './types' + +/** + * Checks if a `Convo` has a `status` that is "active", meaning the chat is + * loaded and ready to be used, or its in a suspended or background state, and + * ready for resumption. + */ +export function isConvoActive( + convo: ConvoState, +): convo is ConvoStateReady | ConvoStateBackgrounded | ConvoStateSuspended { + return ( + convo.status === ConvoStatus.Ready || + convo.status === ConvoStatus.Backgrounded || + convo.status === ConvoStatus.Suspended + ) +} |