@@ -48,22 +48,32 @@ private val previewWaveform = listOf(
4848private const val QUOTE_HIGHLIGHT_HOLD_MILLIS = 700L
4949private const val QUOTE_HIGHLIGHT_FADE_OUT_MILLIS = 1500
5050
51+ data class ChatMessageContext (
52+ val isOneToOneConversation : Boolean = false ,
53+ val conversationThreadId : Long? = null ,
54+ val hasChatPermission : Boolean = true
55+ )
56+
57+ class ChatMessageCallbacks (
58+ val onLongClick : ((Int ) -> Unit? )? = null ,
59+ val onSwipeReply : ((Int ) -> Unit )? = null ,
60+ val onFileClick : (Int ) -> Unit = {},
61+ val onPollClick : (String , String ) -> Unit = { _, _ -> },
62+ val onVoicePlayPauseClick : (Int ) -> Unit = {},
63+ val onVoiceSeek : (Int , Int ) -> Unit = { _, _ -> },
64+ val onVoiceSpeedClick : (Int ) -> Unit = {},
65+ val onReactionClick : (Int , String ) -> Unit = { _, _ -> },
66+ val onReactionLongClick : (Int ) -> Unit = {},
67+ val onOpenThreadClick : (Int ) -> Unit = {},
68+ val onQuotedMessageClick : (Int ) -> Unit = {}
69+ )
70+
5171@Composable
5272fun ChatMessageView (
5373 message : ChatMessageUi ,
5474 highlightTriggerKey : Long? = null,
55- isOneToOneConversation : Boolean = false,
56- conversationThreadId : Long? = null,
57- onLongClick : ((Int ) -> Unit? )? = null,
58- onFileClick : (Int ) -> Unit = {},
59- onPollClick : (pollId: String , pollName: String ) -> Unit = { _, _ -> },
60- onVoicePlayPauseClick : (Int ) -> Unit = {},
61- onVoiceSeek : (messageId: Int , progress: Int ) -> Unit = { _, _ -> },
62- onVoiceSpeedClick : (Int ) -> Unit = {},
63- onReactionClick : (messageId: Int , emoji: String ) -> Unit = { _, _ -> },
64- onReactionLongClick : (messageId: Int ) -> Unit = {},
65- onOpenThreadClick : (messageId: Int ) -> Unit = {},
66- onQuotedMessageClick : (messageId: Int ) -> Unit = {}
75+ context : ChatMessageContext = ChatMessageContext (),
76+ callbacks : ChatMessageCallbacks = ChatMessageCallbacks ()
6777) {
6878 val interactionSource = remember { MutableInteractionSource () }
6979 val lastHandledHighlightKey = rememberSaveable(message.id) { mutableStateOf<Long ?>(null ) }
@@ -82,102 +92,107 @@ fun ChatMessageView(
8292 }
8393
8494 CompositionLocalProvider (
85- LocalReactionClickHandler provides onReactionClick,
86- LocalReactionLongClickHandler provides onReactionLongClick,
87- LocalOpenThreadHandler provides onOpenThreadClick,
88- LocalQuotedMessageClickHandler provides onQuotedMessageClick
95+ LocalReactionClickHandler provides callbacks. onReactionClick,
96+ LocalReactionLongClickHandler provides callbacks. onReactionLongClick,
97+ LocalOpenThreadHandler provides callbacks. onOpenThreadClick,
98+ LocalQuotedMessageClickHandler provides callbacks. onQuotedMessageClick
8999 ) {
90- Box (
91- modifier = Modifier
92- .combinedClickable(
93- interactionSource = interactionSource,
94- indication = ripple(),
95- onClick = { onLongClick?.invoke(message.id) },
96- onLongClick = { onLongClick?.invoke(message.id) }
97- )
100+ SwipeToReplyContainer (
101+ replyable = message.replyable && context.hasChatPermission,
102+ onSwipeReply = { callbacks.onSwipeReply?.invoke(message.id) }
98103 ) {
99- when (val content = message.content) {
100- MessageTypeContent .RegularText -> {
101- TextMessage (
102- uiMessage = message,
103- isOneToOneConversation = isOneToOneConversation,
104- conversationThreadId = conversationThreadId
104+ Box (
105+ modifier = Modifier
106+ .combinedClickable(
107+ interactionSource = interactionSource,
108+ indication = ripple(),
109+ onClick = { callbacks.onLongClick?.invoke(message.id) },
110+ onLongClick = { callbacks.onLongClick?.invoke(message.id) }
105111 )
106- }
112+ ) {
113+ when (val content = message.content) {
114+ MessageTypeContent .RegularText -> {
115+ TextMessage (
116+ uiMessage = message,
117+ isOneToOneConversation = context.isOneToOneConversation,
118+ conversationThreadId = context.conversationThreadId
119+ )
120+ }
107121
108- MessageTypeContent .SystemMessage -> {
109- SystemMessage (message)
110- }
122+ MessageTypeContent .SystemMessage -> {
123+ SystemMessage (message)
124+ }
111125
112- is MessageTypeContent .Media -> {
113- MediaMessage (
114- typeContent = content,
115- message = message,
116- isOneToOneConversation = isOneToOneConversation,
117- conversationThreadId = conversationThreadId,
118- onImageClick = onFileClick
119- )
120- }
126+ is MessageTypeContent .Media -> {
127+ MediaMessage (
128+ typeContent = content,
129+ message = message,
130+ isOneToOneConversation = context. isOneToOneConversation,
131+ conversationThreadId = context. conversationThreadId,
132+ onImageClick = callbacks. onFileClick
133+ )
134+ }
121135
122- is MessageTypeContent .LinkPreview -> {
123- LinkMessage (
124- typeContent = content,
125- message = message,
126- isOneToOneConversation = isOneToOneConversation,
127- conversationThreadId = conversationThreadId
128- )
129- }
136+ is MessageTypeContent .LinkPreview -> {
137+ LinkMessage (
138+ typeContent = content,
139+ message = message,
140+ isOneToOneConversation = context. isOneToOneConversation,
141+ conversationThreadId = context. conversationThreadId
142+ )
143+ }
130144
131- is MessageTypeContent .Geolocation -> {
132- GeolocationMessage (
133- typeContent = content,
134- message = message,
135- isOneToOneConversation = isOneToOneConversation,
136- conversationThreadId = conversationThreadId
137- )
138- }
145+ is MessageTypeContent .Geolocation -> {
146+ GeolocationMessage (
147+ typeContent = content,
148+ message = message,
149+ isOneToOneConversation = context. isOneToOneConversation,
150+ conversationThreadId = context. conversationThreadId
151+ )
152+ }
139153
140- is MessageTypeContent .Voice -> {
141- VoiceMessage (
142- typeContent = content,
143- message = message,
144- isOneToOneConversation = isOneToOneConversation,
145- conversationThreadId = conversationThreadId,
146- onPlayPauseClick = onVoicePlayPauseClick,
147- onSeek = onVoiceSeek,
148- onSpeedClick = onVoiceSpeedClick
149- )
150- }
154+ is MessageTypeContent .Voice -> {
155+ VoiceMessage (
156+ typeContent = content,
157+ message = message,
158+ isOneToOneConversation = context. isOneToOneConversation,
159+ conversationThreadId = context. conversationThreadId,
160+ onPlayPauseClick = callbacks. onVoicePlayPauseClick,
161+ onSeek = callbacks. onVoiceSeek,
162+ onSpeedClick = callbacks. onVoiceSpeedClick
163+ )
164+ }
151165
152- is MessageTypeContent .Poll -> {
153- PollMessage (
154- typeContent = content,
155- message = message,
156- isOneToOneConversation = isOneToOneConversation,
157- conversationThreadId = conversationThreadId,
158- onPollClick = onPollClick
159- )
160- }
166+ is MessageTypeContent .Poll -> {
167+ PollMessage (
168+ typeContent = content,
169+ message = message,
170+ isOneToOneConversation = context. isOneToOneConversation,
171+ conversationThreadId = context. conversationThreadId,
172+ onPollClick = callbacks. onPollClick
173+ )
174+ }
161175
162- is MessageTypeContent .Deck -> {
163- DeckMessage (
164- typeContent = content,
165- message = message,
166- isOneToOneConversation = isOneToOneConversation,
167- conversationThreadId = conversationThreadId
168- )
169- }
176+ is MessageTypeContent .Deck -> {
177+ DeckMessage (
178+ typeContent = content,
179+ message = message,
180+ isOneToOneConversation = context. isOneToOneConversation,
181+ conversationThreadId = context. conversationThreadId
182+ )
183+ }
170184
171- else -> {
172- Log .d(" ChatView" , " Unknown message type: ${' $' } content" )
185+ else -> {
186+ Log .d(" ChatView" , " Unknown message type: ${' $' } content" )
187+ }
188+ }
189+ if (highlightAlpha.value > 0f ) {
190+ Box (
191+ modifier = Modifier
192+ .matchParentSize()
193+ .background(MaterialTheme .colorScheme.primary.copy(alpha = highlightAlpha.value))
194+ )
173195 }
174- }
175- if (highlightAlpha.value > 0f ) {
176- Box (
177- modifier = Modifier
178- .matchParentSize()
179- .background(MaterialTheme .colorScheme.primary.copy(alpha = highlightAlpha.value))
180- )
181196 }
182197 }
183198 }
0 commit comments