Update loading state for chats (#10596)
This commit is contained in:
@@ -31,7 +31,7 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
|||||||
import {useConvoQuery} from '#/state/queries/messages/conversation'
|
import {useConvoQuery} from '#/state/queries/messages/conversation'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {MessagesList} from '#/screens/Messages/components/MessagesList'
|
import {MessagesList} from '#/screens/Messages/components/MessagesList'
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, web} from '#/alf'
|
||||||
import {AgeRestrictedScreen} from '#/components/ageAssurance/AgeRestrictedScreen'
|
import {AgeRestrictedScreen} from '#/components/ageAssurance/AgeRestrictedScreen'
|
||||||
import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy'
|
import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
@@ -44,7 +44,6 @@ import {MessagesListHeader} from '#/components/dms/MessagesListHeader'
|
|||||||
import {type ConvoWithDetails, parseConvoView} from '#/components/dms/util'
|
import {type ConvoWithDetails, parseConvoView} from '#/components/dms/util'
|
||||||
import {Error} from '#/components/Error'
|
import {Error} from '#/components/Error'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {Loader} from '#/components/Loader'
|
|
||||||
import * as Prompt from '#/components/Prompt'
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
@@ -100,7 +99,6 @@ export function MessagesConversationScreenInner({route}: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Inner({convoId}: {convoId: string}) {
|
function Inner({convoId}: {convoId: string}) {
|
||||||
const t = useTheme()
|
|
||||||
const convoState = useConvo()
|
const convoState = useConvo()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
@@ -114,15 +112,7 @@ function Inner({convoId}: {convoId: string}) {
|
|||||||
? parseConvoView(convoData, currentAccount?.did)
|
? parseConvoView(convoData, currentAccount?.did)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
// Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user,
|
|
||||||
// we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be
|
|
||||||
// empty. So, we also check for that possible state as well and render once we can.
|
|
||||||
const [hasScrolled, setHasScrolled] = useState(false)
|
const [hasScrolled, setHasScrolled] = useState(false)
|
||||||
const readyToShow =
|
|
||||||
hasScrolled ||
|
|
||||||
(isConvoActive(convoState) &&
|
|
||||||
!convoState.isFetchingHistory &&
|
|
||||||
convoState.items.length === 0)
|
|
||||||
|
|
||||||
// Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this
|
// Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this
|
||||||
// state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added.
|
// state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added.
|
||||||
@@ -153,11 +143,6 @@ function Inner({convoId}: {convoId: string}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Center style={[a.flex_1]}>
|
<Layout.Center style={[a.flex_1]}>
|
||||||
{!readyToShow && (
|
|
||||||
<View style={IS_LIQUID_GLASS && {paddingTop: topInset}}>
|
|
||||||
<MessagesListHeader convo={convo} />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
<InnerReady
|
<InnerReady
|
||||||
convo={convo}
|
convo={convo}
|
||||||
@@ -166,22 +151,6 @@ function Inner({convoId}: {convoId: string}) {
|
|||||||
isActive={isConvoActive(convoState)}
|
isActive={isConvoActive(convoState)}
|
||||||
isDisabled={convoState.status === ConvoStatus.Disabled}
|
isDisabled={convoState.status === ConvoStatus.Disabled}
|
||||||
/>
|
/>
|
||||||
{!readyToShow && (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.absolute,
|
|
||||||
a.z_10,
|
|
||||||
a.w_full,
|
|
||||||
a.h_full,
|
|
||||||
a.justify_center,
|
|
||||||
a.align_center,
|
|
||||||
t.atoms.bg,
|
|
||||||
]}>
|
|
||||||
<View style={[{marginBottom: 75}]}>
|
|
||||||
<Loader size="xl" />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</Layout.Center>
|
</Layout.Center>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export function MessageComposer({
|
|||||||
fallbackStyle={[t.atoms.bg_contrast_50]}>
|
fallbackStyle={[t.atoms.bg_contrast_50]}>
|
||||||
{children}
|
{children}
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
{IS_WEB && (
|
{IS_WEB && !loading ? (
|
||||||
<EmojiPicker.Root
|
<EmojiPicker.Root
|
||||||
onEmojiSelect={emoji =>
|
onEmojiSelect={emoji =>
|
||||||
composerInternalApiRef.current?.insert(emoji.native)
|
composerInternalApiRef.current?.insert(emoji.native)
|
||||||
@@ -207,7 +207,7 @@ export function MessageComposer({
|
|||||||
</EmojiPicker.Trigger>
|
</EmojiPicker.Trigger>
|
||||||
<EmojiPicker.Picker />
|
<EmojiPicker.Picker />
|
||||||
</EmojiPicker.Root>
|
</EmojiPicker.Root>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
<Composer
|
<Composer
|
||||||
nativeID={textInputId}
|
nativeID={textInputId}
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ export function MessageInput({
|
|||||||
hasEmbed,
|
hasEmbed,
|
||||||
setEmbed,
|
setEmbed,
|
||||||
children,
|
children,
|
||||||
|
loading = false,
|
||||||
}: {
|
}: {
|
||||||
onSendMessage: (message: string) => void
|
onSendMessage: (message: string) => void
|
||||||
hasEmbed: boolean
|
hasEmbed: boolean
|
||||||
setEmbed: (embedUrl: string | undefined) => void
|
setEmbed: (embedUrl: string | undefined) => void
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
loading?: boolean
|
||||||
}) {
|
}) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
@@ -139,47 +141,50 @@ export function MessageInput({
|
|||||||
// @ts-expect-error web only
|
// @ts-expect-error web only
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}>
|
onMouseLeave={() => setIsHovered(false)}>
|
||||||
<EmojiPicker.Root
|
{loading ? null : (
|
||||||
onEmojiSelect={onEmojiInserted}
|
<EmojiPicker.Root
|
||||||
nextFocusRef={textAreaRef}>
|
onEmojiSelect={onEmojiInserted}
|
||||||
<EmojiPicker.Trigger label={l`Open emoji picker`}>
|
nextFocusRef={textAreaRef}>
|
||||||
{({props, state}) => (
|
<EmojiPicker.Trigger label={l`Open emoji picker`}>
|
||||||
<Button
|
{({props, state}) => (
|
||||||
style={[
|
<Button
|
||||||
a.rounded_full,
|
|
||||||
a.overflow_hidden,
|
|
||||||
a.align_center,
|
|
||||||
a.justify_center,
|
|
||||||
{
|
|
||||||
marginTop: 5,
|
|
||||||
height: 30,
|
|
||||||
width: 30,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
label={props.accessibilityLabel}
|
|
||||||
{...props}>
|
|
||||||
<View
|
|
||||||
style={[
|
style={[
|
||||||
a.absolute,
|
a.rounded_full,
|
||||||
a.inset_0,
|
a.overflow_hidden,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
{
|
{
|
||||||
backgroundColor:
|
marginTop: 5,
|
||||||
state.hovered || state.focused || state.pressed
|
height: 30,
|
||||||
? t.atoms.bg.backgroundColor
|
width: 30,
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
]}>
|
]}
|
||||||
<EmojiSmile size="lg" />
|
label={props.accessibilityLabel}
|
||||||
</View>
|
{...props}>
|
||||||
</Button>
|
<View
|
||||||
)}
|
style={[
|
||||||
</EmojiPicker.Trigger>
|
a.absolute,
|
||||||
<EmojiPicker.Picker />
|
a.inset_0,
|
||||||
</EmojiPicker.Root>
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{
|
||||||
|
backgroundColor:
|
||||||
|
state.hovered || state.focused || state.pressed
|
||||||
|
? t.atoms.bg.backgroundColor
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<EmojiSmile size="lg" />
|
||||||
|
</View>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</EmojiPicker.Trigger>
|
||||||
|
<EmojiPicker.Picker />
|
||||||
|
</EmojiPicker.Root>
|
||||||
|
)}
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
ref={textAreaRef}
|
ref={textAreaRef}
|
||||||
|
disabled={loading}
|
||||||
style={flatten([
|
style={flatten([
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
a.px_sm,
|
a.px_sm,
|
||||||
@@ -192,7 +197,11 @@ export function MessageInput({
|
|||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
maxRows={12}
|
maxRows={12}
|
||||||
placeholder={l`Message`}
|
placeholder={
|
||||||
|
loading
|
||||||
|
? l({message: 'Loading chat…', context: 'placeholder'})
|
||||||
|
: l({message: 'Message', context: 'action'})
|
||||||
|
}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
value={message}
|
value={message}
|
||||||
dirName="ltr"
|
dirName="ltr"
|
||||||
@@ -215,6 +224,7 @@ export function MessageInput({
|
|||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={l`Send message`}
|
accessibilityLabel={l`Send message`}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
|
disabled={loading}
|
||||||
style={[
|
style={[
|
||||||
a.rounded_full,
|
a.rounded_full,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
|
|||||||
@@ -12,13 +12,15 @@ import {
|
|||||||
type KeyboardChatScrollViewProps,
|
type KeyboardChatScrollViewProps,
|
||||||
KeyboardGestureArea,
|
KeyboardGestureArea,
|
||||||
} from 'react-native-keyboard-controller'
|
} from 'react-native-keyboard-controller'
|
||||||
import {
|
import Animated, {
|
||||||
runOnJS,
|
runOnJS,
|
||||||
type ScrollEvent,
|
type ScrollEvent,
|
||||||
type SharedValue,
|
type SharedValue,
|
||||||
useAnimatedRef,
|
useAnimatedRef,
|
||||||
|
useAnimatedStyle,
|
||||||
useDerivedValue,
|
useDerivedValue,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {
|
import {
|
||||||
@@ -157,6 +159,16 @@ export function MessagesList({
|
|||||||
startContentOffset: 0,
|
startContentOffset: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const listOpacity = useSharedValue(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasScrolled) {
|
||||||
|
listOpacity.set(withTiming(1, {duration: 200}))
|
||||||
|
} else {
|
||||||
|
listOpacity.set(0)
|
||||||
|
}
|
||||||
|
}, [hasScrolled, listOpacity])
|
||||||
|
|
||||||
const inputHeightUI = useSharedValue(0)
|
const inputHeightUI = useSharedValue(0)
|
||||||
const [inputHeightJS, setInputHeightJS] = useState(0)
|
const [inputHeightJS, setInputHeightJS] = useState(0)
|
||||||
|
|
||||||
@@ -476,6 +488,10 @@ export function MessagesList({
|
|||||||
[inputHeightUI],
|
[inputHeightUI],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const animatedListStyle = useAnimatedStyle(() => ({
|
||||||
|
opacity: listOpacity.get(),
|
||||||
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InviteLinkDialogProvider convo={convoState.convo}>
|
<InviteLinkDialogProvider convo={convoState.convo}>
|
||||||
<KeyboardGestureArea
|
<KeyboardGestureArea
|
||||||
@@ -486,64 +502,69 @@ export function MessagesList({
|
|||||||
// textInputNativeID={textInputId}
|
// textInputNativeID={textInputId}
|
||||||
style={[a.flex_1]}>
|
style={[a.flex_1]}>
|
||||||
{/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */}
|
{/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */}
|
||||||
<ScrollProvider onScroll={onScroll}>
|
<Animated.View style={[a.flex_1, animatedListStyle]}>
|
||||||
<List
|
<ScrollProvider onScroll={onScroll}>
|
||||||
ref={flatListRef}
|
<List
|
||||||
data={renderItems}
|
ref={flatListRef}
|
||||||
renderItem={renderItem}
|
data={renderItems}
|
||||||
keyExtractor={keyExtractor}
|
renderItem={renderItem}
|
||||||
disableFullWindowScroll={true}
|
keyExtractor={keyExtractor}
|
||||||
disableVirtualization={true}
|
disableFullWindowScroll={true}
|
||||||
// The extra two items account for the header and the footer components
|
disableVirtualization={true}
|
||||||
initialNumToRender={IS_NATIVE ? 32 : 62}
|
// The extra two items account for the header and the footer components
|
||||||
maxToRenderPerBatch={IS_WEB ? 32 : 62}
|
initialNumToRender={IS_NATIVE ? 32 : 62}
|
||||||
keyboardDismissMode="interactive"
|
maxToRenderPerBatch={IS_WEB ? 32 : 62}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardDismissMode="interactive"
|
||||||
maintainVisibleContentPosition={{minIndexForVisible: 0}}
|
keyboardShouldPersistTaps="handled"
|
||||||
removeClippedSubviews={false}
|
maintainVisibleContentPosition={{minIndexForVisible: 0}}
|
||||||
sideBorders={false}
|
removeClippedSubviews={false}
|
||||||
onContentSizeChange={onContentSizeChange}
|
sideBorders={false}
|
||||||
onStartReached={onStartReached}
|
onContentSizeChange={onContentSizeChange}
|
||||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
onStartReached={onStartReached}
|
||||||
showsVerticalScrollIndicator={!IS_ANDROID}
|
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||||
scrollEventThrottle={100}
|
showsVerticalScrollIndicator={!IS_ANDROID}
|
||||||
ListHeaderComponent={
|
scrollEventThrottle={100}
|
||||||
<>
|
ListHeaderComponent={
|
||||||
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
<>
|
||||||
{convoState.hasAllHistory ? (
|
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
||||||
convoState.convo?.kind === 'group' ? (
|
{convoState.hasAllHistory ? (
|
||||||
<MessagesListGroupInfoPanel convo={convoState.convo} />
|
convoState.convo?.kind === 'group' ? (
|
||||||
) : (
|
<MessagesListGroupInfoPanel convo={convoState.convo} />
|
||||||
<MessagesListInfoPanel convo={convoState.convo} />
|
) : (
|
||||||
)
|
<MessagesListInfoPanel convo={convoState.convo} />
|
||||||
) : null}
|
)
|
||||||
</>
|
) : null}
|
||||||
}
|
</>
|
||||||
// native only (prop is not supported on web)
|
}
|
||||||
renderScrollComponent={renderScrollComponent}
|
// native only (prop is not supported on web)
|
||||||
contentContainerStyle={{
|
renderScrollComponent={renderScrollComponent}
|
||||||
paddingBottom: platform({
|
contentContainerStyle={{
|
||||||
// ios is slightly larger as the input has no top padding
|
paddingBottom: platform({
|
||||||
ios: tokens.space.lg,
|
// ios is slightly larger as the input has no top padding
|
||||||
android: tokens.space.md,
|
ios: tokens.space.lg,
|
||||||
web: 0, // web uses ListFooterComponent instead for scroll reasons
|
android: tokens.space.md,
|
||||||
}),
|
web: 0, // web uses ListFooterComponent instead for scroll reasons
|
||||||
}}
|
}),
|
||||||
ListFooterComponent={
|
}}
|
||||||
<View
|
ListFooterComponent={
|
||||||
style={web({height: tokens.space.md + inputHeightJS})}
|
<View
|
||||||
onLayout={onFooterLayout}
|
style={web({height: tokens.space.md + inputHeightJS})}
|
||||||
/>
|
onLayout={onFooterLayout}
|
||||||
}
|
/>
|
||||||
style={web({
|
}
|
||||||
scrollbarWidth: 'thin',
|
style={[
|
||||||
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
web({
|
||||||
scrollbarGutter: 'stable',
|
scrollbarWidth: 'thin',
|
||||||
})}
|
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
||||||
contentInset={{top: transparentHeaderHeight}}
|
scrollbarGutter: 'stable',
|
||||||
scrollIndicatorInsets={{top: transparentHeaderHeight}}
|
}),
|
||||||
/>
|
]}
|
||||||
</ScrollProvider>
|
pointerEvents={!hasScrolled ? 'none' : 'auto'}
|
||||||
|
contentInset={{top: transparentHeaderHeight}}
|
||||||
|
scrollIndicatorInsets={{top: transparentHeaderHeight}}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
|
</Animated.View>
|
||||||
<KeyboardStickyView
|
<KeyboardStickyView
|
||||||
style={[a.absolute, a.bottom_0, a.left_0, a.right_0]}
|
style={[a.absolute, a.bottom_0, a.left_0, a.right_0]}
|
||||||
onLayout={onInputLayout}
|
onLayout={onInputLayout}
|
||||||
@@ -559,25 +580,35 @@ export function MessagesList({
|
|||||||
<ConversationFooter
|
<ConversationFooter
|
||||||
convoState={convoState}
|
convoState={convoState}
|
||||||
hasAcceptOverride={hasAcceptOverride}>
|
hasAcceptOverride={hasAcceptOverride}>
|
||||||
{ax.features.enabled(ax.features.DmsNewMessageComposerEnable) ? (
|
{({loading}) =>
|
||||||
<MessageComposer
|
ax.features.enabled(ax.features.DmsNewMessageComposerEnable) ? (
|
||||||
textInputId={textInputId}
|
<MessageComposer
|
||||||
onSendMessage={(message: string) =>
|
textInputId={textInputId}
|
||||||
void onSendMessage(message)
|
onSendMessage={(message: string) =>
|
||||||
}
|
void onSendMessage(message)
|
||||||
hasEmbed={!!embedUri}
|
}
|
||||||
setEmbed={setEmbed}>
|
hasEmbed={!!embedUri}
|
||||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
setEmbed={setEmbed}
|
||||||
</MessageComposer>
|
loading={loading}>
|
||||||
) : (
|
<MessageInputEmbed
|
||||||
<MessageInput
|
embedUri={embedUri}
|
||||||
textInputId={textInputId}
|
setEmbed={setEmbed}
|
||||||
onSendMessage={onSendMessage}
|
/>
|
||||||
hasEmbed={!!embedUri}
|
</MessageComposer>
|
||||||
setEmbed={setEmbed}>
|
) : (
|
||||||
<MessageInputEmbed embedUri={embedUri} setEmbed={setEmbed} />
|
<MessageInput
|
||||||
</MessageInput>
|
textInputId={textInputId}
|
||||||
)}
|
onSendMessage={onSendMessage}
|
||||||
|
hasEmbed={!!embedUri}
|
||||||
|
setEmbed={setEmbed}
|
||||||
|
loading={loading}>
|
||||||
|
<MessageInputEmbed
|
||||||
|
embedUri={embedUri}
|
||||||
|
setEmbed={setEmbed}
|
||||||
|
/>
|
||||||
|
</MessageInput>
|
||||||
|
)
|
||||||
|
}
|
||||||
</ConversationFooter>
|
</ConversationFooter>
|
||||||
)}
|
)}
|
||||||
</KeyboardStickyView>
|
</KeyboardStickyView>
|
||||||
@@ -657,23 +688,25 @@ function ConversationFooter({
|
|||||||
}: {
|
}: {
|
||||||
convoState: ConvoState
|
convoState: ConvoState
|
||||||
hasAcceptOverride?: boolean
|
hasAcceptOverride?: boolean
|
||||||
children?: React.ReactNode // message input
|
children?: ((props: {loading?: boolean}) => React.ReactNode) | React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
if (!isConvoActive(convoState)) {
|
if (!isConvoActive(convoState)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const footerState = getFooterState(convoState, hasAcceptOverride)
|
const footerState = getFooterState(convoState, hasAcceptOverride)
|
||||||
|
const renderChildren = (loading?: boolean) =>
|
||||||
|
typeof children === 'function' ? children({loading}) : children
|
||||||
|
|
||||||
switch (footerState) {
|
switch (footerState) {
|
||||||
case 'loading':
|
case 'loading':
|
||||||
return null
|
return renderChildren(true)
|
||||||
case 'new-chat':
|
case 'new-chat':
|
||||||
// new chat pill goes here - removed for now
|
// new chat pill goes here - removed for now
|
||||||
return children
|
return renderChildren()
|
||||||
case 'request':
|
case 'request':
|
||||||
return <ChatStatusInfo convoState={convoState} />
|
return <ChatStatusInfo convoState={convoState} />
|
||||||
case 'standard':
|
case 'standard':
|
||||||
return children
|
return renderChildren()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user