11import { Button } from '@app/components/Button/Button' ;
22import { Icon } from '@app/components/Icon/Icon' ;
3+ import { Image } from '@app/components/Image/Image' ;
34import { Text } from '@app/components/Text/Text' ;
4- import { replaceEmotesWithText } from '@app/utils/chat/replaceEmotesWithText' ;
55import { ParsedPart } from '@app/utils/chat/replaceTextWithEmotes' ;
6- import { BottomSheetModal , BottomSheetView } from '@gorhom/bottom-sheet' ;
7- import { forwardRef , useCallback , useMemo } from 'react' ;
8- import { View } from 'react-native' ;
6+ import {
7+ BottomSheetBackdrop ,
8+ BottomSheetModal ,
9+ BottomSheetView ,
10+ } from '@gorhom/bottom-sheet' ;
11+ import { SymbolView } from 'expo-symbols' ;
12+ import { forwardRef , useCallback , useMemo , type ComponentProps } from 'react' ;
13+ import { Platform , View } from 'react-native' ;
14+ import { useSafeAreaInsets } from 'react-native-safe-area-context' ;
915import { StyleSheet } from 'react-native-unistyles' ;
1016
1117interface Props {
@@ -17,80 +23,218 @@ interface Props {
1723
1824export const ActionSheet = forwardRef < BottomSheetModal , Props > ( ( props , ref ) => {
1925 const { message, username, handleReply, handleCopy } = props ;
26+ const insets = useSafeAreaInsets ( ) ;
2027
21- const snapPoints = useMemo ( ( ) => [ '25%' , '50%' , '60 %'] , [ ] ) ;
28+ const snapPoints = useMemo ( ( ) => [ '40 %' ] , [ ] ) ;
2229
23- const messageText = useCallback (
24- ( ) => replaceEmotesWithText ( message ) ,
25- [ message ] ,
30+ const renderBackdrop = useCallback (
31+ ( backdropProps : ComponentProps < typeof BottomSheetBackdrop > ) => (
32+ < BottomSheetBackdrop
33+ { ...backdropProps }
34+ appearsOnIndex = { 0 }
35+ disappearsOnIndex = { - 1 }
36+ opacity = { 0.5 }
37+ pressBehavior = "close"
38+ />
39+ ) ,
40+ [ ] ,
2641 ) ;
2742
43+ const actions = useMemo (
44+ ( ) => [
45+ {
46+ id : 'copy' as const ,
47+ icon : 'copy' ,
48+ label : 'Copy Message' ,
49+ onPress : handleCopy ,
50+ } ,
51+ {
52+ id : 'reply' as const ,
53+ icon : 'arrowshape.turn.up.left' ,
54+ label : 'Reply' ,
55+ onPress : handleReply ,
56+ } ,
57+ {
58+ id : 'report' as const ,
59+ icon : 'arrow.up.right.square' ,
60+ label : 'Report message' ,
61+ onPress : ( ) => { } ,
62+ } ,
63+ ] ,
64+ [ handleCopy , handleReply ] ,
65+ ) ;
66+
67+ const getSFSymbolName = useCallback (
68+ ( actionId : 'copy' | 'reply' | 'report' ) => {
69+ switch ( actionId ) {
70+ case 'copy' :
71+ return 'doc.on.doc' as const ;
72+ case 'reply' :
73+ return 'arrowshape.turn.up.left' as const ;
74+ case 'report' :
75+ return 'arrow.up.right.square' as const ;
76+ default :
77+ return 'questionmark.circle' as const ;
78+ }
79+ } ,
80+ [ ] ,
81+ ) ;
82+
83+ const renderMessagePart = useCallback ( ( part : ParsedPart , index : number ) => {
84+ switch ( part . type ) {
85+ case 'emote' :
86+ if ( ! part . url ) return null ;
87+ return (
88+ < Image
89+ key = { `${ part . type } -${ part . id ?? index } -${ index } ` }
90+ useNitro
91+ source = { part . url }
92+ style = { styles . messageEmote }
93+ contentFit = "contain"
94+ transition = { 0 }
95+ />
96+ ) ;
97+ case 'mention' :
98+ case 'text' :
99+ return (
100+ < Text key = { `${ part . type } -${ index } ` } style = { styles . messageText } >
101+ { part . content }
102+ </ Text >
103+ ) ;
104+ default :
105+ if ( 'content' in part && typeof part . content === 'string' ) {
106+ return (
107+ < Text key = { `${ part . type } -${ index } ` } style = { styles . messageText } >
108+ { part . content }
109+ </ Text >
110+ ) ;
111+ }
112+ return null ;
113+ }
114+ } , [ ] ) ;
115+
28116 return (
29117 < BottomSheetModal
30118 ref = { ref }
31- style = { styles . contentContainer }
119+ detached
120+ enableDynamicSizing
121+ maxDynamicContentSize = { 460 }
122+ backdropComponent = { renderBackdrop }
123+ bottomInset = { Math . max ( insets . bottom + 8 , 16 ) }
124+ style = { styles . modalContainer }
32125 backgroundStyle = { styles . bottomSheet }
126+ handleIndicatorStyle = { styles . handle }
33127 enablePanDownToClose
34128 snapPoints = { snapPoints }
35129 >
36130 < BottomSheetView style = { styles . wrapper } >
37- < View style = { styles . info } >
38- < Text >
39- { username } : { messageText ( ) }
40- </ Text >
131+ < View style = { styles . previewCard } >
132+ < View style = { styles . messageLine } >
133+ { username ? (
134+ < Text style = { styles . usernameText } > { username } : </ Text >
135+ ) : null }
136+ { message . map ( renderMessagePart ) }
137+ </ View >
41138 </ View >
42139
43- < View style = { styles . actions } >
44- < Button onPress = { handleCopy } style = { styles . actionButton } >
45- < View style = { styles . actionContent } >
46- < Icon icon = "copy" color = "#fff" size = { 16 } />
47- < Text style = { styles . actionText } > Copy Message</ Text >
48- </ View >
49- </ Button >
50- < Button onPress = { handleReply } style = { styles . actionButton } >
51- < View style = { styles . actionContent } >
52- < Icon icon = "copy" color = "#fff" size = { 16 } />
53- < Text style = { styles . actionText } > Reply</ Text >
54- </ View >
55- </ Button >
56- < Button onPress = { ( ) => { } } style = { styles . actionButton } >
57- < View style = { styles . actionContent } >
58- < Icon icon = "external-link" color = "#fff" size = { 16 } />
59- < Text style = { styles . actionText } > Report message</ Text >
60- </ View >
61- </ Button >
140+ < View style = { styles . actionGroup } >
141+ { actions . map ( action => (
142+ < Button
143+ key = { action . label }
144+ onPress = { action . onPress }
145+ style = { styles . actionButton }
146+ >
147+ < View style = { styles . actionContent } >
148+ { Platform . OS === 'ios' ? (
149+ < SymbolView
150+ name = { getSFSymbolName ( action . id ) }
151+ size = { 18 }
152+ tintColor = "#b7bdc9"
153+ weight = "regular"
154+ style = { styles . actionIcon }
155+ />
156+ ) : (
157+ < Icon icon = { action . icon } color = "#b7bdc9" size = { 18 } />
158+ ) }
159+ < Text style = { styles . actionText } > { action . label } </ Text >
160+ </ View >
161+ </ Button >
162+ ) ) }
62163 </ View >
63164 </ BottomSheetView >
64165 </ BottomSheetModal >
65166 ) ;
66167} ) ;
67168
68169const styles = StyleSheet . create ( theme => ( {
69- contentContainer : {
70- paddingHorizontal : theme . spacing . xl ,
71- overflow : 'visible' ,
170+ modalContainer : {
171+ marginHorizontal : theme . spacing . md ,
72172 } ,
73173 bottomSheet : {
74- // backgroundColor: theme.colors.borderFaint,
174+ backgroundColor : '#171b23' ,
175+ borderRadius : 28 ,
176+ } ,
177+ handle : {
178+ backgroundColor : theme . colors . gray . accent ,
179+ width : 42 ,
180+ height : 5.5 ,
181+ borderRadius : theme . radii . full ,
182+ marginTop : 2 ,
75183 } ,
76184 wrapper : {
185+ paddingTop : theme . spacing . md ,
186+ paddingBottom : theme . spacing . xl ,
187+ backgroundColor : '#171b23' ,
188+ gap : theme . spacing . lg ,
189+ } ,
190+ previewCard : {
191+ paddingHorizontal : theme . spacing . md ,
77192 paddingVertical : theme . spacing . md ,
193+ backgroundColor : 'transparent' ,
194+ } ,
195+ messageLine : {
196+ flexDirection : 'row' ,
197+ flexWrap : 'wrap' ,
198+ alignItems : 'center' ,
199+ } ,
200+ usernameText : {
201+ color : theme . colors . gray . text ,
202+ fontWeight : '600' ,
203+ fontSize : theme . font . fontSize . lg ,
204+ } ,
205+ messageText : {
206+ color : theme . colors . gray . text ,
207+ fontSize : theme . font . fontSize . lg ,
208+ lineHeight : theme . font . fontSize . lg * 1.25 ,
209+ } ,
210+ messageEmote : {
211+ width : 24 ,
212+ height : 24 ,
213+ marginHorizontal : 2 ,
214+ } ,
215+ actionGroup : {
216+ borderRadius : theme . radii . xl ,
217+ backgroundColor : 'transparent' ,
218+ overflow : 'hidden' ,
78219 } ,
79- actions : { marginTop : theme . spacing . xl } ,
80220 actionButton : {
81- paddingVertical : theme . spacing . md ,
82- marginBottom : theme . spacing . sm ,
221+ minHeight : Platform . select ( { ios : 56 , android : 56 } ) ,
222+ paddingHorizontal : theme . spacing . md ,
223+ backgroundColor : 'transparent' ,
83224 } ,
84225 actionContent : {
85226 flexDirection : 'row' ,
86227 alignItems : 'center' ,
87228 gap : theme . spacing . sm ,
88229 } ,
230+ actionIcon : {
231+ opacity : 0.9 ,
232+ } ,
89233 actionText : {
90- // fontWeight: theme.font.fontWeight.thin,
91- // fontSize: theme.font.fontSize.sm,
234+ color : theme . colors . gray . text ,
235+ fontSize : theme . font . fontSize . md ,
236+ fontWeight : Platform . select ( { ios : '400' , android : '400' } ) ,
92237 } ,
93- info : { } ,
94238} ) ) ;
95239
96240ActionSheet . displayName = 'ActionSheet' ;
0 commit comments