diff --git a/App.tsx b/App.tsx index 5ce588e97..03169612a 100644 --- a/App.tsx +++ b/App.tsx @@ -5,7 +5,6 @@ import { configure as configureCoinbase } from "@coinbase/wallet-mobile-sdk"; import DebugButton from "@components/DebugButton"; import { BottomSheetModalProvider } from "@design-system/BottomSheet/BottomSheetModalProvider"; import { ActionSheetProvider } from "@expo/react-native-action-sheet"; -import { PortalProvider } from "@gorhom/portal"; import { useAppStateHandlers } from "@hooks/useAppStateHandlers"; import { PrivyProvider } from "@privy-io/expo"; import { queryClient } from "@queries/queryClient"; @@ -144,10 +143,8 @@ export default function AppWithProviders() { - - - - + + diff --git a/components/Drawer.tsx b/components/Drawer.tsx index 925cba15d..30c22514d 100644 --- a/components/Drawer.tsx +++ b/components/Drawer.tsx @@ -16,21 +16,22 @@ import { ViewStyle, useColorScheme, useWindowDimensions, + Modal, } from "react-native"; import { Gesture, GestureDetector, GestureHandlerRootView, } from "react-native-gesture-handler"; -import { Portal } from "react-native-paper"; -import { - LinearTransition, +import Animated, { interpolateColor, runOnJS, useAnimatedKeyboard, useAnimatedStyle, useSharedValue, withTiming, + FadeIn, + FadeOut, } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -166,26 +167,32 @@ export const Drawer = forwardRef(function Drawer( } return ( - - - {/* A bit of a pain but have to wrap this in a gesture handler */} - - - - - - - {showHandle && } - {children} - - - - - + + + + + + + + + + {showHandle && } + {children} + + + + + + ); }); diff --git a/design-system/BlurView.tsx b/design-system/BlurView.tsx index d308e3475..b33fa26ac 100644 --- a/design-system/BlurView.tsx +++ b/design-system/BlurView.tsx @@ -1,6 +1,7 @@ import { BlurViewProps, BlurView as RNBlurView } from "expo-blur"; import { memo } from "react"; import Animated, { AnimatedProps, FadeIn } from "react-native-reanimated"; +import { useAppTheme } from "@theme/useAppTheme"; import { IVStackProps } from "./VStack"; @@ -13,12 +14,15 @@ export const BlurView = memo(function BlurView({ children, isAbsolute, style, + tint, ...rest }: IBlurViewProps) { + const { theme } = useAppTheme(); + return ( ( + function BottomSheet(props, ref) { + const { + children, + backdropComponent = BottomSheetBackdropOpacity, + handleIndicatorStyle, + handleStyle, + backgroundStyle, + ...rest + } = props; + + const { theme } = useAppTheme(); + + const combinedHandleIndicatorStyle = useMemo( + () => [ + { + backgroundColor: theme.colors.background.raised, + }, + handleIndicatorStyle, + ], + [theme.colors.background.raised, handleIndicatorStyle] + ); + + const combinedHandleStyle = useMemo( + () => [ + { + backgroundColor: theme.colors.background.raised, + borderTopLeftRadius: theme.borderRadius.sm, + borderTopRightRadius: theme.borderRadius.sm, + }, + handleStyle, + ], + [theme.colors.background.raised, theme.borderRadius.sm, handleStyle] + ); + + const combinedBackgroundStyle = useMemo( + () => [ + { + backgroundColor: theme.colors.background.raised, + }, + backgroundStyle, + ], + [theme.colors.background.raised, backgroundStyle] + ); + + return ( + + {children} + + ); + } + ) +); diff --git a/features/conversation/conversation-message/conversation-message-context-menu/conversation-message-context-menu.tsx b/features/conversation/conversation-message/conversation-message-context-menu/conversation-message-context-menu.tsx index e123e08f1..9a4d71222 100644 --- a/features/conversation/conversation-message/conversation-message-context-menu/conversation-message-context-menu.tsx +++ b/features/conversation/conversation-message/conversation-message-context-menu/conversation-message-context-menu.tsx @@ -17,21 +17,19 @@ import { getMessageById, useConversationMessageReactions, } from "@/features/conversation/conversation-message/conversation-message.utils"; -import { - ConversationStoreProvider, - useCurrentConversationTopic, -} from "@/features/conversation/conversation.store-context"; +import { useCurrentConversationTopic } from "@/features/conversation/conversation.store-context"; import { useReactOnMessage } from "@/features/conversation/hooks/use-react-on-message"; import { useRemoveReactionOnMessage } from "@/features/conversation/hooks/use-remove-reaction-on-message"; import { messageIsFromCurrentAccountInboxId } from "@/features/conversation/utils/message-is-from-current-user"; import { useConversationQuery } from "@/queries/useConversationQuery"; import { calculateMenuHeight } from "@design-system/ContextMenu/ContextMenu.utils"; -import { Portal } from "@gorhom/portal"; import { memo, useCallback } from "react"; -import { StyleSheet } from "react-native"; +import { StyleSheet, Modal, View, Platform } from "react-native"; import { MessageContextMenuAboveMessageReactions } from "./conversation-message-context-menu-above-message-reactions"; import { MessageContextMenuContainer } from "./conversation-message-context-menu-container"; import { useMessageContextMenuItems } from "./conversation-message-context-menu.utils"; +import { useAppTheme } from "@theme/useAppTheme"; +import Animated from "react-native-reanimated"; export const MESSAGE_CONTEXT_MENU_SPACE_BETWEEN_ABOVE_MESSAGE_REACTIONS_AND_MESSAGE = 16; @@ -53,6 +51,7 @@ const Content = memo(function Content(props: { >; }) { const { messageContextMenuData } = props; + const { theme } = useAppTheme(); const { messageId, itemRectX, itemRectY, itemRectHeight, itemRectWidth } = messageContextMenuData; @@ -124,11 +123,13 @@ const Content = memo(function Content(props: { return ( <> - - + + {!!bySender && } @@ -179,110 +180,9 @@ const Content = memo(function Content(props: { - - + + ); }); - -/** - * Tried different approaches to implement native context menu, but it's not - * working as expected. Still missing some pieces from libraries to achieve what we want - */ - -// import { MessageContextMenu } from "@/components/Chat/Message/MessageContextMenu"; -// import ContextMenu from "react-native-context-menu-view"; -// import * as ContextMenu from "zeego/context-menu"; - -{ - /* { - console.log("onMenuWillShow"); - }} - menuConfig={{ - menuTitle: "Message Options", - menuItems: [ - { - actionKey: "copy", - actionTitle: "Copy", - icon: { - type: "IMAGE_SYSTEM", - imageValue: { - systemName: "doc.on.doc", - }, - }, - }, - { - actionKey: "delete", - actionTitle: "Delete", - menuAttributes: ["destructive"], - icon: { - type: "IMAGE_SYSTEM", - imageValue: { - systemName: "trash", - }, - }, - }, - ], - }} - auxiliaryPreviewConfig={{ - transitionEntranceDelay: "RECOMMENDED", - anchorPosition: "top", - // alignmentHorizontal: "previewTrailing", - verticalAnchorPosition: "top", - // height: 600, - // preferredHeight: { - // mode: "percentRelativeToWindow", - // percent: 50, - // }, - }} - previewConfig={{ previewType: "CUSTOM" }} - // renderPreview={() => ( - // - // {textContent} - // - // )} - isAuxiliaryPreviewEnabled={true} - renderPreview={() => ( - // - - {textContent} - - )} - renderAuxiliaryPreview={() => ( - - 😅 - 🤣 - 😂 - 🤩 - 🤗 - 🤔 - - )} - > */ -} diff --git a/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.service.ts b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.service.ts index fe7221bd1..e07dc3cec 100644 --- a/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.service.ts +++ b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.service.ts @@ -1,36 +1,21 @@ -import { createBottomSheetModalRef } from "@design-system/BottomSheet/BottomSheet.utils"; import { RolledUpReactions } from "../conversation-message-reactions.types"; import { resetMessageReactionsStore, useMessageReactionsStore, + IMessageReactionsStore, } from "./conversation-message-reaction-drawer.store"; -export const bottomSheetModalRef = createBottomSheetModalRef(); - export function openMessageReactionsDrawer( rolledUpReactions: RolledUpReactions ) { - try { - if (!bottomSheetModalRef.current) { - throw new Error( - "Bottom sheet modal reference is not initialized. Ensure the component is mounted." - ); - } - const setReactions = - useMessageReactionsStore.getState().setRolledUpReactions; - setReactions(rolledUpReactions); - bottomSheetModalRef.current.present(); - } catch (error) { - console.error("Failed to open message reactions drawer:", error); - resetMessageReactionsDrawer(); - } + const store = useMessageReactionsStore.getState(); + store.setRolledUpReactions(rolledUpReactions); } export function closeMessageReactionsDrawer(arg?: { resetStore?: boolean }) { const { resetStore = true } = arg ?? {}; - bottomSheetModalRef.current?.dismiss(); if (resetStore) { - resetMessageReactionsDrawer(); + resetMessageReactionsStore(); } } diff --git a/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.store.ts b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.store.ts new file mode 100644 index 000000000..b33e68139 --- /dev/null +++ b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.store.ts @@ -0,0 +1,30 @@ +import { create } from "zustand"; +import { RolledUpReactions } from "../conversation-message-reactions.types"; + +export type IMessageReactionsStore = { + rolledUpReactions: RolledUpReactions; + setRolledUpReactions: (reactions: RolledUpReactions) => void; +}; + +export const useMessageReactionsStore = create( + (set) => ({ + rolledUpReactions: { + preview: [], + detailed: [], + totalCount: 0, + userReacted: false, + }, + setRolledUpReactions: (reactions) => set({ rolledUpReactions: reactions }), + }) +); + +export function resetMessageReactionsStore() { + useMessageReactionsStore.setState({ + rolledUpReactions: { + preview: [], + detailed: [], + totalCount: 0, + userReacted: false, + }, + }); +} diff --git a/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.tsx b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.tsx index 18c4a5e1c..6ad629836 100644 --- a/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.tsx +++ b/features/conversation/conversation-message/conversation-message-reactions/conversation-message-reaction-drawer/conversation-message-reaction-drawer.tsx @@ -1,20 +1,21 @@ import Avatar from "@components/Avatar"; import { BottomSheetContentContainer } from "@design-system/BottomSheet/BottomSheetContentContainer"; import { BottomSheetHeader } from "@design-system/BottomSheet/BottomSheetHeader"; -import { BottomSheetModal } from "@design-system/BottomSheet/BottomSheetModal"; +import { BottomSheet } from "@design-system/BottomSheet/BottomSheet"; import { HStack } from "@design-system/HStack"; import { Text } from "@design-system/Text"; import { BottomSheetScrollView } from "@gorhom/bottom-sheet"; import { FlashList } from "@shopify/flash-list"; import { ThemedStyle, useAppTheme } from "@theme/useAppTheme"; -import { memo, useCallback, useState } from "react"; -import { TextStyle, TouchableHighlight, ViewStyle } from "react-native"; +import { memo, useCallback, useState, useRef, useEffect } from "react"; +import { TextStyle, ViewStyle, Modal, Platform } from "react-native"; +import { TouchableHighlight } from "@design-system/touchable-highlight"; import { ScrollView } from "react-native-gesture-handler"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { BottomSheetMethods } from "@gorhom/bottom-sheet/lib/typescript/types"; import { - bottomSheetModalRef, - resetMessageReactionsDrawer, + closeMessageReactionsDrawer, useMessageReactionsRolledUpReactions, } from "./conversation-message-reaction-drawer.service"; @@ -22,121 +23,137 @@ export const MessageReactionsDrawer = memo(function MessageReactionsDrawer() { const { theme, themed } = useAppTheme(); const insets = useSafeAreaInsets(); const rolledUpReactions = useMessageReactionsRolledUpReactions(); + const bottomSheetRef = useRef(null); + const [filterReactions, setFilterReactions] = useState(null); + // Centralized dismiss handler that: + // 1. Closes the bottom sheet UI component + // 2. Resets the global drawer state + // 3. Clears any active reaction filters + // This ensures consistent cleanup and prevents UI state mismatches const handleDismiss = useCallback(() => { - resetMessageReactionsDrawer(); + bottomSheetRef.current?.close(); + closeMessageReactionsDrawer(); setFilterReactions(null); }, []); - // State for managing the active filter, e.g., ❤️, 👍, etc. - const [filterReactions, setFilterReactions] = useState(null); + const isVisible = !!rolledUpReactions.totalCount; return ( - - - + { + if (index === -1) { + handleDismiss(); + } + }} + topInset={insets.top} + > + + - {/* Preview of all reactions with counts and filter buttons */} - - {/* "All" button to clear the filter */} - setFilterReactions(null)} - style={[ - themed($chip), - filterReactions === null && themed($chipActive), - ]} - underlayColor={theme.colors.border.subtle} - accessible - accessibilityRole="button" - accessibilityLabel="Show all reactions" + {/* Preview of all reactions with counts and filter buttons */} + - - - All {rolledUpReactions.totalCount} - - - - - {/* Buttons for each unique reaction type with counts */} - {rolledUpReactions.preview.map((reaction, index) => ( + {/* "All" button to clear the filter */} - setFilterReactions( - reaction.content === filterReactions ? null : reaction.content - ) - } + onPress={() => setFilterReactions(null)} style={[ themed($chip), - filterReactions === reaction.content && themed($chipActive), + filterReactions === null && themed($chipActive), ]} - underlayColor={theme.colors.border.subtle} - accessible - accessibilityRole="button" - accessibilityLabel={`${reaction.count} ${reaction.content} reactions`} > - - {reaction.content} {reaction.count} - + + + All {rolledUpReactions.totalCount} + + - ))} - - - - {/* Detailed list of each reaction, sorted and filtered with all own reactions on top */} - - !filterReactions || item.content === filterReactions - )} - renderItem={({ item, index }) => ( - - - {item.reactor.userName} - {item.content} - - )} - keyExtractor={(item, index) => - `${item.content}-${item.reactor.address}-${index}` - } - /> - - + {/* Buttons for each unique reaction type with counts */} + {rolledUpReactions.preview.map((reaction, index) => ( + + setFilterReactions( + reaction.content === filterReactions + ? null + : reaction.content + ) + } + style={[ + themed($chip), + filterReactions === reaction.content && themed($chipActive), + ]} + > + + {reaction.content} {reaction.count} + + + ))} + + + + + {/* Detailed list of each reaction, sorted and filtered with all own reactions on top */} + + !filterReactions || item.content === filterReactions + )} + renderItem={({ item, index }) => ( + + + {item.reactor.userName} + {item.content} + + )} + keyExtractor={(item, index) => + `${item.content}-${item.reactor.address}-${index}` + } + /> + + + ); }); diff --git a/package.json b/package.json index 5a6f414b5..812877bab 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,7 @@ "@expo/metro-runtime": "~4.0.0", "@expo/react-native-action-sheet": "^4.0.1", "@expo/vector-icons": "^14.0.3", - "@gorhom/bottom-sheet": "^5.0.2", - "@gorhom/portal": "^1.0.14", + "@gorhom/bottom-sheet": "5.0.6", "@material-symbols/svg-400": "^0.5.0", "@mfellner/react-native-fast-create-hash": "^1.0.0-alpha.2", "@noble/secp256k1": "^1.5.2", diff --git a/yarn.lock b/yarn.lock index 14b1d6172..d36cbe598 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2060,7 +2060,7 @@ "@babel/parser" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.9": version "7.26.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== @@ -2102,19 +2102,6 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.25.9": - version "7.26.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" - integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.3" - "@babel/parser" "^7.26.3" - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.3" - debug "^4.3.1" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.24.7", "@babel/types@^7.3.3": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" @@ -4079,15 +4066,15 @@ lit "^2.2.3" three "^0.146.0" -"@gorhom/bottom-sheet@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.0.2.tgz#28675943f24fa9787ba58bcf592028af171cdb41" - integrity sha512-bctrT0PDeVUdUG4xm/d9A7ikHFgQ9wchhU0oIE1vIBncFri501GTs/8Bf16ryyKHo3Q0220Dnj5CKm3Ed6ejEg== +"@gorhom/bottom-sheet@5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.0.6.tgz#f20736502399c7bcf8c73ea09e6b571dc07fe0eb" + integrity sha512-SI/AhPvgRfnCWN6/+wbE6TXwRE4X8F2fLyE4L/0bRwgE34Zenq585qLT139uEcfCIyovC2swC3ICqQpkmWEcFA== dependencies: "@gorhom/portal" "1.0.14" invariant "^2.2.4" -"@gorhom/portal@1.0.14", "@gorhom/portal@^1.0.14": +"@gorhom/portal@1.0.14": version "1.0.14" resolved "https://registry.yarnpkg.com/@gorhom/portal/-/portal-1.0.14.tgz#1953edb76aaba80fb24021dc774550194a18e111" integrity sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A==