mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3098 Chats | Fix | Attachments misc fixes (#1903)
This commit is contained in:
parent
08e708a4ca
commit
59f8cca15c
5 changed files with 171 additions and 144 deletions
|
@ -59,6 +59,9 @@ sealed interface DiscussionView {
|
|||
data class Media(
|
||||
val uri: String
|
||||
): ChatBoxAttachment()
|
||||
data class File(
|
||||
val uri: String
|
||||
): ChatBoxAttachment()
|
||||
data class Link(
|
||||
val target: Id,
|
||||
val wrapper: GlobalSearchItemView
|
||||
|
|
|
@ -115,116 +115,117 @@ class DiscussionViewModel @Inject constructor(
|
|||
account: Id,
|
||||
chat: Id
|
||||
) {
|
||||
chatContainer
|
||||
.watchWhileTrackingAttachments(chat = chat)
|
||||
.withLatestFrom(
|
||||
chatContainer.fetchAttachments(vmParams.space),
|
||||
chatContainer.fetchReplies(chat = chat)
|
||||
) { result, dependencies, replies ->
|
||||
result.map { msg ->
|
||||
val allMembers = members.get()
|
||||
val member = allMembers.let { type ->
|
||||
when (type) {
|
||||
is Store.Data -> type.members.find { member ->
|
||||
member.identity == msg.creator
|
||||
}
|
||||
is Store.Empty -> null
|
||||
combine(
|
||||
chatContainer
|
||||
.watchWhileTrackingAttachments(chat = chat),
|
||||
chatContainer.fetchAttachments(vmParams.space),
|
||||
chatContainer.fetchReplies(chat = chat)
|
||||
) { result, dependencies, replies ->
|
||||
result.map { msg ->
|
||||
val allMembers = members.get()
|
||||
val member = allMembers.let { type ->
|
||||
when (type) {
|
||||
is Store.Data -> type.members.find { member ->
|
||||
member.identity == msg.creator
|
||||
}
|
||||
|
||||
is Store.Empty -> null
|
||||
}
|
||||
}
|
||||
|
||||
val content = msg.content
|
||||
val content = msg.content
|
||||
|
||||
val replyToId = msg.replyToMessageId
|
||||
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 -> ""
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
DiscussionView.Message(
|
||||
id = msg.id,
|
||||
timestamp = msg.createdAt * 1000,
|
||||
content = DiscussionView.Message.Content(
|
||||
msg = content?.text.orEmpty(),
|
||||
parts = content?.text
|
||||
.orEmpty()
|
||||
.splitByMarks(marks = content?.marks.orEmpty())
|
||||
.map { (part, styles) ->
|
||||
DiscussionView.Message.Content.Part(
|
||||
part = part,
|
||||
styles = styles
|
||||
DiscussionView.Message(
|
||||
id = msg.id,
|
||||
timestamp = msg.createdAt * 1000,
|
||||
content = DiscussionView.Message.Content(
|
||||
msg = content?.text.orEmpty(),
|
||||
parts = content?.text
|
||||
.orEmpty()
|
||||
.splitByMarks(marks = content?.marks.orEmpty())
|
||||
.map { (part, styles) ->
|
||||
DiscussionView.Message.Content.Part(
|
||||
part = part,
|
||||
styles = styles
|
||||
)
|
||||
}
|
||||
),
|
||||
reply = reply,
|
||||
author = member?.name ?: msg.creator.takeLast(5),
|
||||
isUserAuthor = msg.creator == account,
|
||||
isEdited = msg.modifiedAt > msg.createdAt,
|
||||
reactions = msg.reactions.map { (emoji, ids) ->
|
||||
DiscussionView.Message.Reaction(
|
||||
emoji = emoji,
|
||||
count = ids.size,
|
||||
isSelected = ids.contains(account)
|
||||
)
|
||||
},
|
||||
attachments = msg.attachments.map { attachment ->
|
||||
when (attachment.type) {
|
||||
Chat.Message.Attachment.Type.Image -> DiscussionView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.medium(path = attachment.target)
|
||||
)
|
||||
|
||||
else -> {
|
||||
val wrapper = dependencies[attachment.target]
|
||||
if (wrapper?.layout == ObjectType.Layout.IMAGE) {
|
||||
DiscussionView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.large(path = attachment.target)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Attachment.Link(
|
||||
target = attachment.target,
|
||||
wrapper = wrapper,
|
||||
icon = wrapper?.objectIcon(urlBuilder) ?: ObjectIcon.None
|
||||
)
|
||||
}
|
||||
),
|
||||
reply = reply,
|
||||
author = member?.name ?: msg.creator.takeLast(5),
|
||||
isUserAuthor = msg.creator == account,
|
||||
isEdited = msg.modifiedAt > msg.createdAt,
|
||||
reactions = msg.reactions.map { (emoji, ids) ->
|
||||
DiscussionView.Message.Reaction(
|
||||
emoji = emoji,
|
||||
count = ids.size,
|
||||
isSelected = ids.contains(account)
|
||||
)
|
||||
},
|
||||
attachments = msg.attachments.map { attachment ->
|
||||
when(attachment.type) {
|
||||
Chat.Message.Attachment.Type.Image -> DiscussionView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.medium(path = attachment.target)
|
||||
)
|
||||
else -> {
|
||||
val wrapper = dependencies[attachment.target]
|
||||
if (wrapper?.layout == ObjectType.Layout.IMAGE) {
|
||||
DiscussionView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.large(path = attachment.target)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Attachment.Link(
|
||||
target = attachment.target,
|
||||
wrapper = wrapper,
|
||||
icon = wrapper?.objectIcon(urlBuilder) ?: ObjectIcon.None
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
if (it.isNotEmpty()) {
|
||||
Timber.d("Chat attachments: $it")
|
||||
}
|
||||
},
|
||||
avatar = if (member != null && !member.iconImage.isNullOrEmpty()) {
|
||||
DiscussionView.Message.Avatar.Image(
|
||||
urlBuilder.thumbnail(member.iconImage!!)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
}
|
||||
)
|
||||
}.reversed()
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.collect { result ->
|
||||
messages.value = result
|
||||
}
|
||||
}.also {
|
||||
if (it.isNotEmpty()) {
|
||||
Timber.d("Chat attachments: $it")
|
||||
}
|
||||
},
|
||||
avatar = if (member != null && !member.iconImage.isNullOrEmpty()) {
|
||||
DiscussionView.Message.Avatar.Image(
|
||||
urlBuilder.thumbnail(member.iconImage!!)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
}
|
||||
)
|
||||
}.reversed()
|
||||
}.flowOn(dispatchers.io).collect {
|
||||
messages.value = it
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageSent(msg: String) {
|
||||
|
@ -256,6 +257,21 @@ class DiscussionViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
is DiscussionView.Message.ChatBoxAttachment.File -> {
|
||||
uploadFile.async(
|
||||
UploadFile.Params(
|
||||
space = vmParams.space,
|
||||
path = attachment.uri
|
||||
)
|
||||
).onSuccess { file ->
|
||||
add(
|
||||
Chat.Message.Attachment(
|
||||
target = file.id,
|
||||
type = Chat.Message.Attachment.Type.Image
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -446,6 +462,15 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onChatBoxFilePicked(uris: List<String>) {
|
||||
Timber.d("onChatBoxFilePicked: $uris")
|
||||
chatBoxAttachments.value = chatBoxAttachments.value + uris.map {
|
||||
DiscussionView.Message.ChatBoxAttachment.File(
|
||||
uri = it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onExitEditMessageMode() {
|
||||
viewModelScope.launch {
|
||||
chatBoxMode.value = ChatBoxMode.Default
|
||||
|
|
|
@ -122,7 +122,8 @@ fun DiscussionScreenPreview() {
|
|||
onReplyMessage = {},
|
||||
chatBoxMode = DiscussionViewModel.ChatBoxMode.Default,
|
||||
onClearReplyClicked = {},
|
||||
onChatBoxMediaPicked = {}
|
||||
onChatBoxMediaPicked = {},
|
||||
onChatBoxFilePicked = {}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,6 @@ import androidx.compose.ui.text.withStyle
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
|
@ -126,6 +125,7 @@ import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
|||
import com.anytypeio.anytype.core_utils.const.DateConst.TIME_H24
|
||||
import com.anytypeio.anytype.core_utils.ext.formatTimeInMillis
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.core_utils.ext.parsePath
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.feature_discussions.R
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionView
|
||||
|
@ -207,6 +207,9 @@ fun DiscussionScreenWrapper(
|
|||
onClearReplyClicked = vm::onClearReplyClicked,
|
||||
onChatBoxMediaPicked = { uris ->
|
||||
vm.onChatBoxMediaPicked(uris.map { it.parseImagePath(context = context) })
|
||||
},
|
||||
onChatBoxFilePicked = { uris ->
|
||||
// TODO parse path and path it vm.
|
||||
}
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -259,7 +262,8 @@ fun DiscussionScreen(
|
|||
onAttachMediaClicked: () -> Unit,
|
||||
onAttachFileClicked: () -> Unit,
|
||||
onUploadAttachmentClicked: () -> Unit,
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit,
|
||||
onChatBoxFilePicked: (List<Uri>) -> Unit
|
||||
) {
|
||||
var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||
mutableStateOf(TextFieldValue(""))
|
||||
|
@ -383,7 +387,8 @@ fun DiscussionScreen(
|
|||
onAttachObjectClicked = onAttachObjectClicked,
|
||||
onClearAttachmentClicked = onClearAttachmentClicked,
|
||||
onClearReplyClicked = onClearReplyClicked,
|
||||
onChatBoxMediaPicked = onChatBoxMediaPicked
|
||||
onChatBoxMediaPicked = onChatBoxMediaPicked,
|
||||
onChatBoxFilePicked = onChatBoxFilePicked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -451,13 +456,15 @@ private fun ChatBox(
|
|||
onUploadAttachmentClicked: () -> Unit,
|
||||
onClearAttachmentClicked: (DiscussionView.Message.ChatBoxAttachment) -> Unit,
|
||||
onClearReplyClicked: () -> Unit,
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit,
|
||||
onChatBoxFilePicked: (List<Uri>) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val result = remember { mutableStateOf<List<String>>(emptyList()) }
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) {
|
||||
result.value = it.map { it.parseImagePath(context) }
|
||||
onChatBoxMediaPicked(it.filterNotNull())
|
||||
val uploadMediaLauncher = rememberLauncherForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) {
|
||||
onChatBoxMediaPicked(it)
|
||||
}
|
||||
|
||||
val uploadFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) {
|
||||
onChatBoxFilePicked(it)
|
||||
}
|
||||
|
||||
var showDropdownMenu by remember { mutableStateOf(false) }
|
||||
|
@ -554,6 +561,11 @@ private fun ChatBox(
|
|||
}
|
||||
}
|
||||
}
|
||||
is DiscussionView.Message.ChatBoxAttachment.File -> {
|
||||
item {
|
||||
Text(text = attachment.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -709,43 +721,29 @@ private fun ChatBox(
|
|||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
launcher.launch(
|
||||
uploadMediaLauncher.launch(
|
||||
PickVisualMediaRequest(mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||
)
|
||||
}
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_attachment_file),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
context.toast("Coming soon")
|
||||
}
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_attachment_upload),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
context.toast("Coming soon")
|
||||
}
|
||||
)
|
||||
// Divider(
|
||||
// paddingStart = 0.dp,
|
||||
// paddingEnd = 0.dp
|
||||
// )
|
||||
// DropdownMenuItem(
|
||||
// text = {
|
||||
// Text(
|
||||
// text = stringResource(R.string.chat_attachment_file),
|
||||
// color = colorResource(id = R.color.text_primary)
|
||||
// )
|
||||
// },
|
||||
// onClick = {
|
||||
// showDropdownMenu = false
|
||||
// uploadFileLauncher.launch(
|
||||
// arrayOf("*/*")
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1833,9 +1833,9 @@ Please provide specific details of your needs here.</string>
|
|||
<string name="date_layout_alert_date_out_of_range">The selected date is out of the valid range (years %1$d to %2$d). Please select a date within this range.</string>
|
||||
<string name="date_layout_item_created_by">by</string>
|
||||
|
||||
<string name="chat_attachment_object">Object</string>
|
||||
<string name="chat_attachment_file">File</string>
|
||||
<string name="chat_attachment_media">Media</string>
|
||||
<string name="chat_attachment_object">Select existing object</string>
|
||||
<string name="chat_attachment_file">Upload file</string>
|
||||
<string name="chat_attachment_media">Upload media</string>
|
||||
<string name="chat_attachment_upload">Upload</string>
|
||||
<string name="chats_reply">Reply</string>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue