Skip to content

Commit a1c257b

Browse files
committed
feat(fc/conversation): add long press action for reply
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 5b01d48 commit a1c257b

5 files changed

Lines changed: 69 additions & 26 deletions

File tree

flipchatApp/src/main/kotlin/xyz/flipchat/app/features/chat/conversation/ConversationItem.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package xyz.flipchat.app.features.chat.conversation
22

33
import androidx.compose.runtime.Stable
4+
import com.getcode.model.ID
45
import com.getcode.model.Kin
56
import com.getcode.model.KinAmount
67
import com.getcode.model.chat.MessageContent
8+
import com.getcode.model.chat.Sender
79
import xyz.flipchat.services.domain.model.chat.ConversationMember
810
import xyz.flipchat.services.domain.model.chat.ConversationMessage
911

@@ -20,4 +22,10 @@ sealed interface ChattableState {
2022
data class Spectator(val cover: Kin): ChattableState
2123

2224
fun isActiveMember() = this is Enabled || this is DisabledByMute
23-
}
25+
}
26+
27+
data class MessageReplyAnchor(
28+
val id: ID,
29+
val sender: Sender,
30+
val message: MessageContent
31+
)

flipchatApp/src/main/kotlin/xyz/flipchat/app/features/chat/conversation/ConversationMessages.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package xyz.flipchat.app.features.chat.conversation
22

3-
import androidx.compose.animation.core.animateFloatAsState
43
import androidx.compose.foundation.Image
54
import androidx.compose.foundation.layout.Box
65
import androidx.compose.foundation.layout.fillMaxSize
@@ -13,18 +12,13 @@ import androidx.compose.material.icons.Icons
1312
import androidx.compose.material.icons.outlined.KeyboardArrowDown
1413
import androidx.compose.runtime.Composable
1514
import androidx.compose.runtime.getValue
16-
import androidx.compose.runtime.mutableStateOf
17-
import androidx.compose.runtime.remember
1815
import androidx.compose.runtime.rememberCoroutineScope
19-
import androidx.compose.runtime.setValue
2016
import androidx.compose.ui.Alignment
2117
import androidx.compose.ui.Modifier
22-
import androidx.compose.ui.draw.alpha
2318
import androidx.compose.ui.focus.FocusRequester
2419
import androidx.compose.ui.graphics.ColorFilter
2520
import androidx.compose.ui.graphics.graphicsLayer
2621
import androidx.compose.ui.platform.LocalContext
27-
import androidx.compose.ui.platform.LocalDensity
2822
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
2923
import androidx.compose.ui.platform.LocalUriHandler
3024
import androidx.paging.compose.LazyPagingItems

flipchatApp/src/main/kotlin/xyz/flipchat/app/features/chat/conversation/ConversationViewModel.kt

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ class ConversationViewModel @Inject constructor(
8888
private val paymentController: PaymentController,
8989
private val profileController: ProfileController,
9090
private val resources: ResourceHelper,
91+
private val currencyUtils: CurrencyUtils,
9192
clipboardManager: ClipboardManager,
92-
currencyUtils: CurrencyUtils,
9393
betaFeatures: BetaFlags,
9494
) : BaseViewModel2<ConversationViewModel.State, ConversationViewModel.Event>(
9595
initialState = State.Default,
@@ -107,7 +107,7 @@ class ConversationViewModel @Inject constructor(
107107
val chattableState: ChattableState?,
108108
val textFieldState: TextFieldState,
109109
val replyEnabled: Boolean,
110-
val replyMessage: ChatItem.Message?,
110+
val replyMessage: MessageReplyAnchor?,
111111
val startAtUnread: Boolean,
112112
val title: String,
113113
val imageUri: String?,
@@ -155,7 +155,7 @@ class ConversationViewModel @Inject constructor(
155155
data class OnConversationChanged(val conversationWithPointers: ConversationWithMembersAndLastPointers) :
156156
Event
157157

158-
data class OnInitialUnreadCountDetermined(val count: Int): Event
158+
data class OnInitialUnreadCountDetermined(val count: Int) : Event
159159
data class OnUserActivity(val activity: Instant) : Event
160160
data object SendCash : Event
161161
data object SendMessage : Event
@@ -167,7 +167,16 @@ class ConversationViewModel @Inject constructor(
167167
data class MarkDelivered(val messageId: ID) : Event
168168

169169
data class OnReplyEnabled(val enabled: Boolean) : Event
170-
data class ReplyTo(val message: ChatItem.Message) : Event
170+
data class ReplyTo(val anchor: MessageReplyAnchor) : Event {
171+
constructor(chatItem: ChatItem.Message) : this(
172+
MessageReplyAnchor(
173+
chatItem.chatMessageId,
174+
chatItem.sender,
175+
chatItem.message
176+
)
177+
)
178+
}
179+
171180
data object CancelReply : Event
172181

173182
data class OnStartAtUnread(val enabled: Boolean) : Event
@@ -337,7 +346,7 @@ class ConversationViewModel @Inject constructor(
337346
if (replyingTo != null) {
338347
roomController.sendReply(
339348
it.conversationId!!,
340-
replyingTo.chatMessageId,
349+
replyingTo.id,
341350
text
342351
)
343352
} else {
@@ -677,7 +686,9 @@ class ConversationViewModel @Inject constructor(
677686
message = it.content,
678687
sender = Sender(
679688
id = it.message.senderId,
680-
profileImage = it.member?.imageUri.takeIf { it.orEmpty().isNotEmpty() },
689+
profileImage = it.member?.imageUri.takeIf {
690+
it.orEmpty().isNotEmpty()
691+
},
681692
displayName = it.member?.memberName ?: "Deleted",
682693
isSelf = it.content.isFromSelf,
683694
isHost = it.message.senderId == currentState.hostId && !contents.isFromSelf,
@@ -707,18 +718,7 @@ class ConversationViewModel @Inject constructor(
707718
),
708719
originalMessage = anchor,
709720
messageControls = MessageControls(
710-
actions = listOf(
711-
MessageControlAction.Copy {
712-
dispatchEvent(
713-
Event.CopyMessage(
714-
contents.localizedText(
715-
resources = resources,
716-
currencyUtils = currencyUtils
717-
)
718-
)
719-
)
720-
}
721-
) + buildSelfDefenseControls(message, member, contents),
721+
actions = buildMessageActions(message, member, contents, enableReply),
722722
),
723723
key = message.id.uuid.toString()
724724
)
@@ -753,6 +753,44 @@ class ConversationViewModel @Inject constructor(
753753
}
754754
}
755755

756+
private fun buildMessageActions(
757+
message: ConversationMessage,
758+
member: ConversationMember?,
759+
contents: MessageContent,
760+
enableReply: Boolean,
761+
): List<MessageControlAction> {
762+
return mutableListOf<MessageControlAction>().apply {
763+
add(
764+
MessageControlAction.Copy {
765+
dispatchEvent(
766+
Event.CopyMessage(
767+
contents.localizedText(
768+
resources = resources,
769+
currencyUtils = currencyUtils
770+
)
771+
)
772+
)
773+
}
774+
)
775+
776+
if (enableReply) {
777+
add(
778+
MessageControlAction.Reply {
779+
val sender = Sender(
780+
id = message.senderId,
781+
profileImage = member?.imageUri.takeIf { it.orEmpty().isNotEmpty() },
782+
displayName = member?.memberName ?: "Deleted",
783+
isSelf = contents.isFromSelf,
784+
isHost = message.senderId == stateFlow.value.hostId && !contents.isFromSelf,
785+
)
786+
val anchor = MessageReplyAnchor(message.id, sender, contents)
787+
dispatchEvent(Event.ReplyTo(anchor))
788+
}
789+
)
790+
}
791+
} + buildSelfDefenseControls(message, member, contents)
792+
}
793+
756794
private fun buildSelfDefenseControls(
757795
message: ConversationMessage,
758796
member: ConversationMember?,
@@ -992,7 +1030,7 @@ class ConversationViewModel @Inject constructor(
9921030
is Event.OnReplyEnabled -> { state -> state.copy(replyEnabled = event.enabled) }
9931031
is Event.OnStartAtUnread -> { state -> state.copy(startAtUnread = event.enabled) }
9941032
is Event.ReplyTo -> { state ->
995-
state.copy(replyMessage = event.message)
1033+
state.copy(replyMessage = event.anchor)
9961034
}
9971035

9981036
is Event.CancelReply -> { state -> state.copy(replyMessage = null) }

flipchatApp/src/main/kotlin/xyz/flipchat/app/features/chat/conversation/MessageActionContextSheet.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ internal data class MessageActionContextSheet(val actions: List<MessageControlAc
4545
R.string.action_muteUser,
4646
action.name
4747
)
48+
49+
is MessageControlAction.Reply -> stringResource(R.string.action_reply)
4850
},
4951
style = CodeTheme.typography.textMedium,
5052
modifier = Modifier

ui/components/src/main/kotlin/com/getcode/ui/components/chat/messagecontents/MessageTextContent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ sealed interface MessageControlAction {
4646
val onSelect: () -> Unit
4747

4848
data class Copy(override val onSelect: () -> Unit) : MessageControlAction
49+
data class Reply(override val onSelect: () -> Unit) : MessageControlAction
4950
data class Delete(override val onSelect: () -> Unit) : MessageControlAction
5051
data class RemoveUser(val name: String, override val onSelect: () -> Unit) :
5152
MessageControlAction

0 commit comments

Comments
 (0)