1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-2966 Chat | Fix | Misc. UX fixes (#2424)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Evgenii Kozlov 2025-05-21 14:17:12 +02:00 committed by GitHub
parent 8ec569a6c5
commit 6ae2d9a3b7
Signed by: github
GPG key ID: B5690EEEBB952194
6 changed files with 94 additions and 30 deletions

View file

@ -139,7 +139,9 @@ sealed interface ChatView {
): ChatBoxAttachment()
data class Bookmark(
val preview: LinkPreview
val preview: LinkPreview,
val isLoadingPreview: Boolean = false,
val isUploading: Boolean = false
) : ChatBoxAttachment()
sealed class State {

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.LinkPreview
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
import com.anytypeio.anytype.core_models.ObjectWrapper
@ -57,7 +58,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@ -282,7 +282,7 @@ class ChatViewModel @Inject constructor(
description = wrapper.description.orEmpty(),
imageUrl = wrapper.getSingleValue<String?>(Relations.PICTURE).let { hash ->
if (!hash.isNullOrEmpty())
urlBuilder.medium(hash)
urlBuilder.large(hash)
else
null
}
@ -516,6 +516,14 @@ class ChatViewModel @Inject constructor(
}
}
is ChatView.Message.ChatBoxAttachment.Bookmark -> {
chatBoxAttachments.value = currAttachments.toMutableList().apply {
set(
index = idx,
element = attachment.copy(
isUploading = true
)
)
}
createObjectFromUrl.async(
params = CreateObjectFromUrl.Params(
url = attachment.preview.url,
@ -600,13 +608,13 @@ class ChatViewModel @Inject constructor(
)
)
).onSuccess { (id, payload) ->
chatBoxAttachments.value = emptyList()
chatContainer.onPayload(payload)
delay(JUMP_TO_BOTTOM_DELAY)
uXCommands.emit(UXCommand.JumpToBottom)
}.onFailure {
Timber.e(it, "Error while adding message")
}
chatBoxAttachments.value = emptyList()
chatBoxMode.value = ChatBoxMode.Default()
}
is ChatBoxMode.EditMessage -> {
@ -623,12 +631,12 @@ class ChatViewModel @Inject constructor(
).onSuccess {
delay(JUMP_TO_BOTTOM_DELAY)
uXCommands.emit(UXCommand.JumpToBottom)
chatBoxAttachments.value = emptyList()
}.onFailure {
Timber.e(it, "Error while editing message")
}.onSuccess {
Timber.d("Message edited with success")
}
chatBoxAttachments.value = emptyList()
chatBoxMode.value = ChatBoxMode.Default()
}
is ChatBoxMode.Reply -> {
@ -643,13 +651,13 @@ class ChatViewModel @Inject constructor(
)
)
).onSuccess { (id, payload) ->
chatBoxAttachments.value = emptyList()
chatContainer.onPayload(payload)
delay(JUMP_TO_BOTTOM_DELAY)
uXCommands.emit(UXCommand.JumpToBottom)
}.onFailure {
Timber.e(it, "Error while adding message")
}
chatBoxAttachments.value = emptyList()
chatBoxMode.value = ChatBoxMode.Default()
}
}
@ -1053,14 +1061,27 @@ class ChatViewModel @Inject constructor(
fun onUrlPasted(url: Url) {
viewModelScope.launch {
val curr = chatBoxAttachments.value
chatBoxAttachments.value = buildList {
addAll(curr)
add(
ChatView.Message.ChatBoxAttachment.Bookmark(
preview = LinkPreview(
url = url
),
isLoadingPreview = true
)
)
}
getLinkPreview.async(
params = url
).onSuccess { preview ->
chatBoxAttachments.value = buildList {
addAll(chatBoxAttachments.value)
addAll(curr)
add(
ChatView.Message.ChatBoxAttachment.Bookmark(
preview = preview
preview = preview,
isLoadingPreview = false
)
)
}

View file

@ -1,10 +1,12 @@
package com.anytypeio.anytype.feature_chats.ui
import android.content.res.Configuration
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
@ -44,12 +46,12 @@ import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
@Composable
@OptIn(ExperimentalGlideComposeApi::class)
@OptIn(ExperimentalGlideComposeApi::class, ExperimentalFoundationApi::class)
fun BubbleAttachments(
attachments: List<ChatView.Message.Attachment>,
onAttachmentClicked: (ChatView.Message.Attachment) -> Unit,
onAttachmentLongClicked: (ChatView.Message.Attachment) -> Unit,
isUserAuthor: Boolean
) {
attachments.forEachIndexed { idx, attachment ->
@ -93,9 +95,14 @@ fun BubbleAttachments(
modifier = Modifier
.size(292.dp)
.clip(shape = RoundedCornerShape(12.dp))
.clickable {
onAttachmentClicked(attachment)
}
.combinedClickable(
onClick = {
onAttachmentClicked(attachment)
},
onLongClick = {
onAttachmentLongClicked(attachment)
}
)
) {
it
.override(1024, 1024)
@ -121,6 +128,9 @@ fun BubbleAttachments(
icon = attachment.icon,
onAttachmentClicked = {
onAttachmentClicked(attachment)
},
onAttachmentLongClicked = {
onAttachmentLongClicked(attachment)
}
)
}
@ -132,6 +142,9 @@ fun BubbleAttachments(
imageUrl = attachment.imageUrl,
onClick = {
onAttachmentClicked(attachment)
},
onLongClick = {
onAttachmentLongClicked(attachment)
}
)
}
@ -139,13 +152,15 @@ fun BubbleAttachments(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AttachedObject(
modifier: Modifier,
title: String,
type: String,
icon: ObjectIcon,
onAttachmentClicked: () -> Unit
onAttachmentClicked: () -> Unit = {},
onAttachmentLongClicked: () -> Unit = {}
) {
Box(
modifier = modifier
@ -159,9 +174,10 @@ fun AttachedObject(
.background(
color = colorResource(id = R.color.background_secondary)
)
.clickable {
onAttachmentClicked()
}
.combinedClickable(
onClick = onAttachmentClicked,
onLongClick = onAttachmentLongClicked
)
) {
ListWidgetObjectIcon(
icon = icon,
@ -210,20 +226,25 @@ fun AttachedObject(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Bookmark(
url: String,
title: String,
description: String,
imageUrl: String?,
onClick: () -> Unit
onClick: () -> Unit,
onLongClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
.clip(RoundedCornerShape(12.dp))
.clickable { onClick() }
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick
)
,
colors = CardDefaults.cardColors(
containerColor = colorResource(R.color.shape_transparent_secondary)
@ -273,7 +294,8 @@ fun BookmarkPreview() {
title = "Algo - Video Automation",
description = "Algo is a data-visualization studio specializing in video automation.",
imageUrl = null,
onClick = {}
onClick = {},
onLongClick = {}
)
}

View file

@ -367,6 +367,7 @@ fun ChatBox(
onMessageSent(text.text, spans)
clearText()
resetScroll()
showMarkup = false
}
}
)

View file

@ -1,7 +1,6 @@
package com.anytypeio.anytype.feature_chats.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@ -17,7 +16,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
@ -27,7 +25,6 @@ import coil3.compose.rememberAsyncImagePainter
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.feature_chats.R
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel
import com.anytypeio.anytype.feature_chats.presentation.ChatView
import com.anytypeio.anytype.presentation.objects.ObjectIcon
@ -220,17 +217,18 @@ internal fun ChatBoxAttachments(
end = 4.dp
)
.width(216.dp),
title = attachment.preview.title,
title = if (attachment.isLoadingPreview)
stringResource(R.string.three_dots_text_placeholder)
else
attachment.preview.title,
type = stringResource(R.string.bookmark),
icon = ObjectIcon.None,
onAttachmentClicked = {
// Do nothing
}
)
Image(
painter = painterResource(id = R.drawable.ic_clear_chatbox_attachment),
contentDescription = "Close icon",
modifier = Modifier
Box(
Modifier
.align(
Alignment.TopEnd
)
@ -238,7 +236,24 @@ internal fun ChatBoxAttachments(
.noRippleClickable {
onClearAttachmentClicked(attachment)
}
)
) {
Image(
painter = painterResource(id = R.drawable.ic_clear_chatbox_attachment),
contentDescription = "Close icon",
modifier = Modifier
.align(Alignment.Center)
)
if (attachment.isLoadingPreview || attachment.isUploading) {
CircularProgressIndicator(
modifier = Modifier
.size(16.dp)
.align(Alignment.Center),
color = colorResource(R.color.text_white),
trackColor = colorResource(R.color.glyph_active).copy(alpha = 0.5f),
strokeWidth = 1.5.dp
)
}
}
}
}
}

View file

@ -175,7 +175,10 @@ fun Bubble(
BubbleAttachments(
attachments = attachments,
isUserAuthor = isUserAuthor,
onAttachmentClicked = onAttachmentClicked
onAttachmentClicked = onAttachmentClicked,
onAttachmentLongClicked = {
showDropdownMenu = true
}
)
if (content.msg.isNotEmpty()) {
Box(