-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: V3 Split Logic #1089
feat: V3 Split Logic #1089
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,248 @@ | ||||||||||||||||||||||||||
import { FlashList, ListRenderItem } from "@shopify/flash-list"; | ||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||
backgroundColor, | ||||||||||||||||||||||||||
itemSeparatorColor, | ||||||||||||||||||||||||||
tertiaryBackgroundColor, | ||||||||||||||||||||||||||
} from "@styles/colors"; | ||||||||||||||||||||||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react"; | ||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||
Dimensions, | ||||||||||||||||||||||||||
FlatList, | ||||||||||||||||||||||||||
Platform, | ||||||||||||||||||||||||||
StyleSheet, | ||||||||||||||||||||||||||
View, | ||||||||||||||||||||||||||
useColorScheme, | ||||||||||||||||||||||||||
} from "react-native"; | ||||||||||||||||||||||||||
import Animated, { | ||||||||||||||||||||||||||
useAnimatedStyle, | ||||||||||||||||||||||||||
useDerivedValue, | ||||||||||||||||||||||||||
useSharedValue, | ||||||||||||||||||||||||||
} from "react-native-reanimated"; | ||||||||||||||||||||||||||
import { useSafeAreaInsets } from "react-native-safe-area-context"; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import ChatInput from "./Input/Input"; | ||||||||||||||||||||||||||
import TransactionInput from "./Transaction/TransactionInput"; | ||||||||||||||||||||||||||
import { ReanimatedFlashList, ReanimatedView } from "../../utils/animations"; | ||||||||||||||||||||||||||
import { useKeyboardAnimation } from "../../utils/animations/keyboardAnimation"; | ||||||||||||||||||||||||||
import { converseEventEmitter } from "../../utils/events"; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
type ChatDumbProps<T> = { | ||||||||||||||||||||||||||
onReadyToFocus: () => void; | ||||||||||||||||||||||||||
transactionMode: boolean; | ||||||||||||||||||||||||||
frameTextInputFocused: boolean; | ||||||||||||||||||||||||||
items: T[]; | ||||||||||||||||||||||||||
renderItem: ListRenderItem<T>; | ||||||||||||||||||||||||||
keyExtractor: (item: T) => string; | ||||||||||||||||||||||||||
showChatInput: boolean; | ||||||||||||||||||||||||||
ListFooterComponent: React.JSX.Element | null; | ||||||||||||||||||||||||||
showPlaceholder: boolean; | ||||||||||||||||||||||||||
key?: string; | ||||||||||||||||||||||||||
displayList: boolean; | ||||||||||||||||||||||||||
refreshing: boolean; | ||||||||||||||||||||||||||
getItemType: ( | ||||||||||||||||||||||||||
item: T, | ||||||||||||||||||||||||||
index: number, | ||||||||||||||||||||||||||
extraData?: any | ||||||||||||||||||||||||||
) => string | number | undefined; | ||||||||||||||||||||||||||
placeholderComponent: React.JSX.Element | null; | ||||||||||||||||||||||||||
extraData?: any; | ||||||||||||||||||||||||||
itemToId: (id: T) => string; | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const useStyles = () => { | ||||||||||||||||||||||||||
const colorScheme = useColorScheme(); | ||||||||||||||||||||||||||
return useMemo( | ||||||||||||||||||||||||||
() => | ||||||||||||||||||||||||||
StyleSheet.create({ | ||||||||||||||||||||||||||
chatContainer: { | ||||||||||||||||||||||||||
flex: 1, | ||||||||||||||||||||||||||
justifyContent: "flex-end", | ||||||||||||||||||||||||||
backgroundColor: backgroundColor(colorScheme), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
chatContent: { | ||||||||||||||||||||||||||
backgroundColor: backgroundColor(colorScheme), | ||||||||||||||||||||||||||
flex: 1, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
chatPreviewContent: { | ||||||||||||||||||||||||||
backgroundColor: backgroundColor(colorScheme), | ||||||||||||||||||||||||||
flex: 1, | ||||||||||||||||||||||||||
paddingBottom: 0, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
chat: { | ||||||||||||||||||||||||||
backgroundColor: backgroundColor(colorScheme), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
inputBottomFiller: { | ||||||||||||||||||||||||||
position: "absolute", | ||||||||||||||||||||||||||
width: "100%", | ||||||||||||||||||||||||||
bottom: 0, | ||||||||||||||||||||||||||
backgroundColor: backgroundColor(colorScheme), | ||||||||||||||||||||||||||
zIndex: 0, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
inChatRecommendations: { | ||||||||||||||||||||||||||
borderBottomWidth: 0.5, | ||||||||||||||||||||||||||
borderBottomColor: itemSeparatorColor(colorScheme), | ||||||||||||||||||||||||||
marginHorizontal: 20, | ||||||||||||||||||||||||||
marginBottom: 10, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||
[colorScheme] | ||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
export function ChatDumb<T>({ | ||||||||||||||||||||||||||
onReadyToFocus, | ||||||||||||||||||||||||||
transactionMode, | ||||||||||||||||||||||||||
frameTextInputFocused, | ||||||||||||||||||||||||||
items, | ||||||||||||||||||||||||||
renderItem, | ||||||||||||||||||||||||||
keyExtractor, | ||||||||||||||||||||||||||
showChatInput, | ||||||||||||||||||||||||||
showPlaceholder, | ||||||||||||||||||||||||||
key, | ||||||||||||||||||||||||||
displayList, | ||||||||||||||||||||||||||
refreshing, | ||||||||||||||||||||||||||
ListFooterComponent, | ||||||||||||||||||||||||||
getItemType, | ||||||||||||||||||||||||||
placeholderComponent, | ||||||||||||||||||||||||||
extraData, | ||||||||||||||||||||||||||
itemToId, | ||||||||||||||||||||||||||
}: ChatDumbProps<T>) { | ||||||||||||||||||||||||||
const colorScheme = useColorScheme(); | ||||||||||||||||||||||||||
const styles = useStyles(); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const hideInputIfFrameFocused = Platform.OS !== "web"; | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve platform check robustness. The platform check could be more explicit to handle web platforms consistently. -const hideInputIfFrameFocused = Platform.OS !== "web";
+const hideInputIfFrameFocused = Platform.OS === "ios" || Platform.OS === "android"; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const DEFAULT_INPUT_HEIGHT = 58; | ||||||||||||||||||||||||||
const chatInputHeight = useSharedValue(0); | ||||||||||||||||||||||||||
const chatInputDisplayedHeight = useDerivedValue(() => { | ||||||||||||||||||||||||||
return frameTextInputFocused && hideInputIfFrameFocused | ||||||||||||||||||||||||||
? 0 | ||||||||||||||||||||||||||
: chatInputHeight.value + DEFAULT_INPUT_HEIGHT; | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
Comment on lines
+116
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider memoizing shared values. The shared values and derived values should be memoized to prevent unnecessary recalculations during re-renders. -const chatInputHeight = useSharedValue(0);
-const chatInputDisplayedHeight = useDerivedValue(() => {
+const chatInputHeight = useMemo(() => useSharedValue(0), []);
+const chatInputDisplayedHeight = useMemo(() => useDerivedValue(() => {
return frameTextInputFocused && hideInputIfFrameFocused
? 0
: chatInputHeight.value + DEFAULT_INPUT_HEIGHT;
-});
+}), [frameTextInputFocused, hideInputIfFrameFocused, chatInputHeight]); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const insets = useSafeAreaInsets(); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const { height: keyboardHeight } = useKeyboardAnimation(); | ||||||||||||||||||||||||||
const tertiary = tertiaryBackgroundColor(colorScheme); | ||||||||||||||||||||||||||
const textInputStyle = useAnimatedStyle( | ||||||||||||||||||||||||||
() => ({ | ||||||||||||||||||||||||||
position: "absolute", | ||||||||||||||||||||||||||
width: "100%", | ||||||||||||||||||||||||||
backgroundColor: tertiary, | ||||||||||||||||||||||||||
height: "auto", | ||||||||||||||||||||||||||
zIndex: 1, | ||||||||||||||||||||||||||
transform: [ | ||||||||||||||||||||||||||
{ translateY: -Math.max(insets.bottom, keyboardHeight.value) }, | ||||||||||||||||||||||||||
] as any, | ||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||
[keyboardHeight, colorScheme, insets.bottom] | ||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const chatContentStyle = useAnimatedStyle( | ||||||||||||||||||||||||||
() => ({ | ||||||||||||||||||||||||||
...styles.chatContent, | ||||||||||||||||||||||||||
paddingBottom: showChatInput | ||||||||||||||||||||||||||
? chatInputDisplayedHeight.value + | ||||||||||||||||||||||||||
Math.max(insets.bottom, keyboardHeight.value) | ||||||||||||||||||||||||||
: insets.bottom, | ||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||
[showChatInput, keyboardHeight, chatInputDisplayedHeight, insets.bottom] | ||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const messageListRef = useRef<FlatList<T> | FlashList<T> | undefined>(); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const scrollToMessage = useCallback( | ||||||||||||||||||||||||||
(data: { messageId?: string; index?: number; animated?: boolean }) => { | ||||||||||||||||||||||||||
let index = data.index; | ||||||||||||||||||||||||||
if (index === undefined && data.messageId) { | ||||||||||||||||||||||||||
index = items.findIndex((m) => itemToId(m) === data.messageId); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
if (index !== undefined) { | ||||||||||||||||||||||||||
messageListRef.current?.scrollToIndex({ | ||||||||||||||||||||||||||
index, | ||||||||||||||||||||||||||
viewPosition: 0.5, | ||||||||||||||||||||||||||
animated: !!data.animated, | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
[itemToId, items] | ||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||
converseEventEmitter.on("scrollChatToMessage", scrollToMessage); | ||||||||||||||||||||||||||
return () => { | ||||||||||||||||||||||||||
converseEventEmitter.off("scrollChatToMessage", scrollToMessage); | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
}, [scrollToMessage]); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const handleOnLayout = useCallback(() => { | ||||||||||||||||||||||||||
setTimeout(() => { | ||||||||||||||||||||||||||
onReadyToFocus(); | ||||||||||||||||||||||||||
}, 50); | ||||||||||||||||||||||||||
}, [onReadyToFocus]); | ||||||||||||||||||||||||||
Comment on lines
+178
to
+182
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clean up setTimeout on unmount. The setTimeout in handleOnLayout should be cleaned up to prevent potential memory leaks. const handleOnLayout = useCallback(() => {
- setTimeout(() => {
- onReadyToFocus();
- }, 50);
+ const timer = setTimeout(() => {
+ onReadyToFocus();
+ }, 50);
+ return () => clearTimeout(timer);
}, [onReadyToFocus]);
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||
<View style={styles.chatContainer} key={key}> | ||||||||||||||||||||||||||
<Animated.View style={chatContentStyle}> | ||||||||||||||||||||||||||
{displayList && ( | ||||||||||||||||||||||||||
<ReanimatedFlashList | ||||||||||||||||||||||||||
contentContainerStyle={styles.chat} | ||||||||||||||||||||||||||
data={items} | ||||||||||||||||||||||||||
refreshing={refreshing} | ||||||||||||||||||||||||||
extraData={extraData} | ||||||||||||||||||||||||||
renderItem={renderItem} | ||||||||||||||||||||||||||
onLayout={handleOnLayout} | ||||||||||||||||||||||||||
ref={(r) => { | ||||||||||||||||||||||||||
// if (r) { | ||||||||||||||||||||||||||
// messageListRef.current = r; | ||||||||||||||||||||||||||
// } | ||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||
Comment on lines
+196
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncomment and assign The Apply this diff to fix the issue: 196 ref={(r) => {
197- // if (r) {
198- // messageListRef.current = r;
199- // }
197+ if (r) {
198+ messageListRef.current = r;
199+ }
200 }} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
keyboardDismissMode="interactive" | ||||||||||||||||||||||||||
automaticallyAdjustContentInsets={false} | ||||||||||||||||||||||||||
contentInsetAdjustmentBehavior="never" | ||||||||||||||||||||||||||
// Causes a glitch on Android, no sure we need it for now | ||||||||||||||||||||||||||
// maintainVisibleContentPosition={{ | ||||||||||||||||||||||||||
// minIndexForVisible: 0, | ||||||||||||||||||||||||||
// autoscrollToTopThreshold: 100, | ||||||||||||||||||||||||||
// }} | ||||||||||||||||||||||||||
estimatedListSize={Dimensions.get("screen")} | ||||||||||||||||||||||||||
inverted | ||||||||||||||||||||||||||
keyExtractor={keyExtractor} | ||||||||||||||||||||||||||
getItemType={getItemType} | ||||||||||||||||||||||||||
keyboardShouldPersistTaps="handled" | ||||||||||||||||||||||||||
estimatedItemSize={80} | ||||||||||||||||||||||||||
// Size glitch on Android | ||||||||||||||||||||||||||
showsVerticalScrollIndicator={Platform.OS === "ios"} | ||||||||||||||||||||||||||
pointerEvents="auto" | ||||||||||||||||||||||||||
ListFooterComponent={ListFooterComponent} | ||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||
{showPlaceholder && placeholderComponent} | ||||||||||||||||||||||||||
</Animated.View> | ||||||||||||||||||||||||||
{showChatInput && ( | ||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||
<ReanimatedView | ||||||||||||||||||||||||||
style={[ | ||||||||||||||||||||||||||
textInputStyle, | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
display: | ||||||||||||||||||||||||||
frameTextInputFocused && hideInputIfFrameFocused | ||||||||||||||||||||||||||
? "none" | ||||||||||||||||||||||||||
: "flex", | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
]} | ||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||
{!transactionMode && <ChatInput inputHeight={chatInputHeight} />} | ||||||||||||||||||||||||||
{transactionMode && <TransactionInput />} | ||||||||||||||||||||||||||
</ReanimatedView> | ||||||||||||||||||||||||||
<View | ||||||||||||||||||||||||||
style={[ | ||||||||||||||||||||||||||
styles.inputBottomFiller, | ||||||||||||||||||||||||||
{ height: insets.bottom + DEFAULT_INPUT_HEIGHT }, | ||||||||||||||||||||||||||
]} | ||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||
</View> | ||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,10 +9,9 @@ | |
import FrameButton from "./FrameButton"; | ||
import FrameTextInput from "./FrameTextInput"; | ||
import { FrameButtonType, FrameToDisplay } from "../../../utils/frames"; | ||
import { MessageToDisplay } from "../Message/Message"; | ||
|
||
export default function FrameBottom({ | ||
message, | ||
messageFromMe, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification The prop renaming is incomplete across the codebase There are inconsistencies in how message properties are accessed:
The prop renaming should be consistent throughout the codebase to avoid confusion and potential bugs. 🔗 Analysis chainProps interface refactoring looks good, but verify message.fromMe usage. The prop renaming from Let's verify all message.fromMe references are updated: Also applies to: 24-24 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for any remaining message.fromMe references
rg "message\.fromMe" --type tsx
Length of output: 61 Script: #!/bin/bash
# Search for any remaining message.fromMe references with correct file extension
rg "message\.fromMe"
# Also search for messageFromMe to verify the new prop usage
rg "messageFromMe"
# Use ast-grep to find component props and their usage
ast-grep --pattern 'interface $_ {
$$$
messageFromMe: $_
$$$
}'
Length of output: 5555 |
||
frame, | ||
textInput, | ||
buttons, | ||
|
@@ -22,7 +21,7 @@ | |
postingActionForButton, | ||
onButtonPress, | ||
}: { | ||
message: MessageToDisplay; | ||
messageFromMe: boolean; | ||
frame: FrameToDisplay; | ||
textInput: string | undefined; | ||
buttons: FrameButtonType[]; | ||
|
@@ -39,7 +38,7 @@ | |
style={[ | ||
styles.frameBottom, | ||
{ | ||
backgroundColor: message.fromMe | ||
? myMessageInnerBubbleColor(colorScheme) | ||
: messageInnerBubbleColor(colorScheme), | ||
paddingVertical: | ||
|
@@ -55,7 +54,7 @@ | |
setFrameTextInputFocused={setFrameTextInputFocused} | ||
setFrameTextInputValue={setFrameTextInputValue} | ||
frameTextInputValue={frameTextInputValue} | ||
messageFromMe={message.fromMe} | ||
messageFromMe={messageFromMe} | ||
/> | ||
)} | ||
{buttons.length > 0 && | ||
|
@@ -72,7 +71,7 @@ | |
Haptics.impactAsync(); | ||
onButtonPress(button); | ||
}} | ||
messageFromMe={message.fromMe} | ||
messageFromMe={messageFromMe} | ||
/> | ||
))} | ||
</> | ||
|
@@ -82,7 +81,7 @@ | |
style={[ | ||
styles.frameBottomText, | ||
{ | ||
color: message.fromMe ? "white" : textPrimaryColor(colorScheme), | ||
color: messageFromMe ? "white" : textPrimaryColor(colorScheme), | ||
fontWeight: frame.type === "PREVIEW" ? "600" : "400", | ||
}, | ||
]} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding common list interaction props.
The
ChatDumbProps
interface could benefit from additional props commonly needed in chat interfaces:onRefresh?: () => void
- Callback for pull-to-refreshonEndReached?: () => void
- Callback for paginationerror?: Error
- Error state handlingonRetry?: () => void
- Error retry callback📝 Committable suggestion