Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ sealed class Command {
sealed class ChatCommand {
data class AddMessage(
val chat: Id,
val message: Chat.Message
val message: Chat.Message,
) : ChatCommand()

data class DeleteMessage(
Expand All @@ -613,6 +613,11 @@ sealed class Command {
val limit: Int
) : ChatCommand()

data class GetMessagesByIds(
val chat: Id,
val messages: List<Id>
) : ChatCommand()

data class SubscribeLastMessages(
val chat: Id,
val limit: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@ sealed class Chat {
*/
fun new(
text: String,
attachments: List<Attachment> = emptyList()
) : Message = Chat.Message(
attachments: List<Attachment> = emptyList(),
replyToMessageId: Id? = null
) : Message = Message(
id = "",
createdAt = 0L,
modifiedAt = 0L,
attachments = attachments,
reactions = emptyMap(),
creator = "",
replyToMessageId = "",
content = Chat.Message.Content(
replyToMessageId = replyToMessageId,
content = Content(
text = text,
marks = emptyList(),
style = Block.Content.Text.Style.P
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,10 @@ class BlockDataRepository(
return remote.getChatMessages(command)
}

override suspend fun getChatMessagesByIds(command: Command.ChatCommand.GetMessagesByIds): List<Chat.Message> {
return remote.getChatMessagesByIds(command)
}

override suspend fun subscribeLastChatMessages(
command: Command.ChatCommand.SubscribeLastMessages
): Command.ChatCommand.SubscribeLastMessages.Response {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ interface BlockRemote {
suspend fun editChatMessage(command: Command.ChatCommand.EditMessage)
suspend fun deleteChatMessage(command: Command.ChatCommand.DeleteMessage)
suspend fun getChatMessages(command: Command.ChatCommand.GetMessages): List<Chat.Message>
suspend fun getChatMessagesByIds(command: Command.ChatCommand.GetMessagesByIds): List<Chat.Message>
suspend fun subscribeLastChatMessages(command: Command.ChatCommand.SubscribeLastMessages): Command.ChatCommand.SubscribeLastMessages.Response
suspend fun toggleChatMessageReaction(command: Command.ChatCommand.ToggleMessageReaction)
suspend fun unsubscribeChat(chat: Id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ interface BlockRepository {
suspend fun editChatMessage(command: Command.ChatCommand.EditMessage)
suspend fun deleteChatMessage(command: Command.ChatCommand.DeleteMessage)
suspend fun getChatMessages(command: Command.ChatCommand.GetMessages): List<Chat.Message>
suspend fun getChatMessagesByIds(command: Command.ChatCommand.GetMessagesByIds): List<Chat.Message>
suspend fun subscribeLastChatMessages(command: Command.ChatCommand.SubscribeLastMessages): Command.ChatCommand.SubscribeLastMessages.Response
suspend fun toggleChatMessageReaction(command: Command.ChatCommand.ToggleMessageReaction)
suspend fun unsubscribeChat(chat: Id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ChatContainer @Inject constructor(
private val payloads = MutableSharedFlow<List<Event.Command.Chats>>()

private val attachments = MutableSharedFlow<Set<Id>>(replay = 0)
private val replies = MutableSharedFlow<Set<Id>>(replay = 0)

@Deprecated("Naive implementation. Add caching logic - maybe store for wrappers")
fun fetchAttachments(space: Space) : Flow<Map<Id, ObjectWrapper.Basic>> {
Expand Down Expand Up @@ -64,18 +65,39 @@ class ChatContainer @Inject constructor(
.map { wrappers -> wrappers.associate { it.id to it } }
}

@Deprecated("Naive implementation. Add caching logic")
fun fetchReplies(chat: Id) : Flow<Map<Id, Chat.Message>> {
return replies
.distinctUntilChanged()
.map { ids ->
if (ids.isNotEmpty()) {
repo.getChatMessagesByIds(
command = Command.ChatCommand.GetMessagesByIds(
chat = chat,
messages = ids.toList()
)
)
} else {
emptyList()
}
}
.distinctUntilChanged()
.map { messages -> messages.associate { it.id to it } }
}

fun watchWhileTrackingAttachments(chat: Id): Flow<List<Chat.Message>> {
return watch(chat)
.onEach { messages ->
val ids = messages
.map { msg ->
msg.attachments.map {
it.target
}
val repliesIds = mutableSetOf<Id>()
val attachmentsIds = mutableSetOf<Id>()
messages.forEach { msg ->
attachmentsIds.addAll(msg.attachments.map { it.target })
if (!msg.replyToMessageId.isNullOrEmpty()) {
repliesIds.add(msg.replyToMessageId.orEmpty())
}
.flatten()
.toSet()
attachments.emit(ids)
}
attachments.emit(attachmentsIds)
replies.emit(repliesIds)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.anytypeio.anytype.domain.chats

import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.chats.Chat
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import javax.inject.Inject

class GetChatMessagesByIds @Inject constructor(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<Command.ChatCommand.GetMessagesByIds, List<Chat.Message>>(dispatchers.io) {

override suspend fun doWork(params: Command.ChatCommand.GetMessagesByIds): List<Chat.Message> {
return repo.getChatMessagesByIds(params)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ sealed interface DiscussionView {
val reactions: List<Reaction> = emptyList(),
val isUserAuthor: Boolean = false,
val isEdited: Boolean = false,
val avatar: Avatar = Avatar.Initials()
val avatar: Avatar = Avatar.Initials(),
val reply: Reply? = null
) : DiscussionView {

data class Content(val msg: String, val parts: List<Part>) {
Expand All @@ -32,6 +33,12 @@ sealed interface DiscussionView {
}
}

data class Reply(
val msg: Id,
val text: String,
val author: String
)

sealed class Attachment {
data class Image(
val target: Id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.anytypeio.anytype.core_models.primitives.Space
import com.anytypeio.anytype.core_ui.text.splitByMarks
import com.anytypeio.anytype.core_utils.ext.withLatestFrom
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.base.onFailure
import com.anytypeio.anytype.domain.base.onSuccess
Expand All @@ -33,6 +34,7 @@ import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import timber.log.Timber

Expand All @@ -48,7 +50,8 @@ class DiscussionViewModel @Inject constructor(
private val members: ActiveSpaceMemberSubscriptionContainer,
private val getAccount: GetAccount,
private val urlBuilder: UrlBuilder,
private val spaceViews: SpaceViewSubscriptionContainer
private val spaceViews: SpaceViewSubscriptionContainer,
private val dispatchers: AppCoroutineDispatchers
) : BaseViewModel() {

val name = MutableStateFlow<String?>(null)
Expand Down Expand Up @@ -102,15 +105,20 @@ class DiscussionViewModel @Inject constructor(
}
}

// TODO move to IO thread.
private suspend fun proceedWithObservingChatMessages(
account: Id,
chat: Id
) {
chatContainer
.watchWhileTrackingAttachments(chat = chat)
.withLatestFrom(chatContainer.fetchAttachments(vmParams.space)) { result, dependencies ->
.withLatestFrom(
chatContainer.fetchAttachments(vmParams.space),
chatContainer.fetchReplies(chat = chat)
) { result, dependencies, replies ->
result.map { msg ->
val member = members.get().let { type ->
val allMembers = members.get()
val member = allMembers.let { type ->
when (type) {
is Store.Data -> type.members.find { member ->
member.identity == msg.creator
Expand All @@ -121,6 +129,30 @@ class DiscussionViewModel @Inject constructor(

val content = msg.content

val replyToId = msg.replyToMessageId

val reply = if (replyToId.isNullOrEmpty()) {
null
} else {
val msg = replies[replyToId]
if (msg != null) {
DiscussionView.Message.Reply(
msg = msg.id,
text = msg.content?.text.orEmpty(),
author = allMembers.let { type ->
when (type) {
is Store.Data -> type.members.find { member ->
member.identity == msg.creator
}?.name.orEmpty()
is Store.Empty -> ""
}
}
)
} else {
null
}
}

DiscussionView.Message(
id = msg.id,
timestamp = msg.createdAt * 1000,
Expand All @@ -136,6 +168,7 @@ class DiscussionViewModel @Inject constructor(
)
}
),
reply = reply,
author = member?.name ?: msg.creator.takeLast(5),
isUserAuthor = msg.creator == account,
isEdited = msg.modifiedAt > msg.createdAt,
Expand Down Expand Up @@ -182,6 +215,7 @@ class DiscussionViewModel @Inject constructor(
)
}.reversed()
}
// .flowOn(dispatchers.io)
.collect { result ->
messages.value = result
}
Expand Down Expand Up @@ -215,7 +249,6 @@ class DiscussionViewModel @Inject constructor(
Timber.e(it, "Error while adding message")
}
}

is ChatBoxMode.EditMessage -> {
editChatMessage.async(
params = Command.ChatCommand.EditMessage(
Expand All @@ -234,6 +267,31 @@ class DiscussionViewModel @Inject constructor(
chatBoxMode.value = ChatBoxMode.Default
}
}
is ChatBoxMode.Reply -> {
addChatMessage.async(
params = Command.ChatCommand.AddMessage(
chat = chat,
message = Chat.Message.new(
text = msg,
replyToMessageId = mode.msg,
attachments = attachments.value.map { a ->
Chat.Message.Attachment(
target = a.id,
type = Chat.Message.Attachment.Type.Link
)
}
)
)
).onSuccess { (id, payload) ->
attachments.value = emptyList()
chatContainer.onPayload(payload)
delay(JUMP_TO_BOTTOM_DELAY)
commands.emit(UXCommand.JumpToBottom)
}.onFailure {
Timber.e(it, "Error while adding message")
}
chatBoxMode.value = ChatBoxMode.Default
}
}
}
}
Expand Down Expand Up @@ -272,6 +330,12 @@ class DiscussionViewModel @Inject constructor(
attachments.value = emptyList()
}

fun onClearReplyClicked() {
viewModelScope.launch {
chatBoxMode.value = ChatBoxMode.Default
}
}

fun onReacted(msg: Id, reaction: String) {
Timber.d("onReacted")
viewModelScope.launch {
Expand All @@ -292,6 +356,16 @@ class DiscussionViewModel @Inject constructor(
}
}

fun onReplyMessage(msg: DiscussionView.Message) {
viewModelScope.launch {
chatBoxMode.value = ChatBoxMode.Reply(
msg = msg.id,
text = msg.content.msg,
author = msg.author
)
}
}

fun onDeleteMessage(msg: DiscussionView.Message) {
Timber.d("onDeleteMessageClicked")
viewModelScope.launch {
Expand Down Expand Up @@ -339,6 +413,11 @@ class DiscussionViewModel @Inject constructor(
sealed class ChatBoxMode {
data object Default : ChatBoxMode()
data class EditMessage(val msg: Id) : ChatBoxMode()
data class Reply(
val msg: Id,
val text: String,
val author: String
): ChatBoxMode()
}

sealed class Params {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.anytypeio.anytype.feature_discussions.presentation
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.domain.auth.interactor.GetAccount
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.chats.AddChatMessage
import com.anytypeio.anytype.domain.chats.ChatContainer
import com.anytypeio.anytype.domain.chats.DeleteChatMessage
Expand All @@ -28,7 +29,8 @@ class DiscussionViewModelFactory @Inject constructor(
private val members: ActiveSpaceMemberSubscriptionContainer,
private val getAccount: GetAccount,
private val urlBuilder: UrlBuilder,
private val spaceViews: SpaceViewSubscriptionContainer
private val spaceViews: SpaceViewSubscriptionContainer,
private val dispatchers: AppCoroutineDispatchers
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = DiscussionViewModel(
Expand All @@ -43,6 +45,7 @@ class DiscussionViewModelFactory @Inject constructor(
deleteChatMessage = deleteChatMessage,
urlBuilder = urlBuilder,
editChatMessage = editChatMessage,
spaceViews = spaceViews
spaceViews = spaceViews,
dispatchers = dispatchers
) as T
}
Loading