This commit is contained in:
Avently 2025-01-01 22:41:51 +07:00
parent ee4f26248a
commit bc64d6fb36
9 changed files with 33 additions and 21 deletions

View file

@ -338,7 +338,7 @@ fun AndroidScreen(userPickerState: MutableStateFlow<AnimatedViewState>) {
.then(if (hasCutout) Modifier.clip(RectangleShape) else Modifier) .then(if (hasCutout) Modifier.clip(RectangleShape) else Modifier)
.graphicsLayer { translationX = maxWidth.toPx() - minOf(offset.value.dp, maxWidth).toPx() } .graphicsLayer { translationX = maxWidth.toPx() - minOf(offset.value.dp, maxWidth).toPx() }
) Box2@{ ) Box2@{
currentChatId.value?.let { currentChatId.collectAsState().value?.let {
ChatView(currentChatId, onComposed) ChatView(currentChatId, onComposed)
} }
} }

View file

@ -157,7 +157,7 @@ object ChatModel {
val updatingProgress = MutableStateFlow(null as Float?) val updatingProgress = MutableStateFlow(null as Float?)
var updatingRequest: Closeable? = null var updatingRequest: Closeable? = null
private val updatingChatsMutex: Mutex = Mutex() // private val updatingChatsMutex: Mutex = Mutex()
val changingActiveUserMutex: Mutex = Mutex() val changingActiveUserMutex: Mutex = Mutex()
val desktopNoUserNoRemote: Boolean @Composable get() = appPlatform.isDesktop && currentUser.value == null && currentRemoteHost.value == null val desktopNoUserNoRemote: Boolean @Composable get() = appPlatform.isDesktop && currentUser.value == null && currentRemoteHost.value == null
@ -348,7 +348,11 @@ object ChatModel {
} }
} }
suspend fun <T> withChats(action: suspend ChatsContext.() -> T): T = updatingChatsMutex.withLock { // suspend fun <T> withChats(action: suspend ChatsContext.() -> T): T = updatingChatsMutex.withLock {
// chatsContext.action()
// }
suspend fun <T> withChats(action: suspend ChatsContext.() -> T): T = withContext(Dispatchers.Main) {
chatsContext.action() chatsContext.action()
} }

View file

@ -620,10 +620,13 @@ object ChatController {
val hasUser = chatModel.currentUser.value != null val hasUser = chatModel.currentUser.value != null
chatModel.userAddress.value = if (hasUser) apiGetUserAddress(rhId) else null chatModel.userAddress.value = if (hasUser) apiGetUserAddress(rhId) else null
chatModel.chatItemTTL.value = if (hasUser) getChatItemTTL(rhId) else ChatItemTTL.None chatModel.chatItemTTL.value = if (hasUser) getChatItemTTL(rhId) else ChatItemTTL.None
println("LALAL WAIT")
withChats { withChats {
val chats = apiGetChats(rhId) val chats = apiGetChats(rhId)
println("LALAL WAIT1 ${chats.size}")
updateChats(chats) updateChats(chats)
} }
println("LALAL WAIT2 ${chatModel.chats.size}")
chatModel.userTags.value = apiGetChatTags(rhId).takeIf { hasUser } ?: emptyList() chatModel.userTags.value = apiGetChatTags(rhId).takeIf { hasUser } ?: emptyList()
chatModel.activeChatTagFilter.value = null chatModel.activeChatTagFilter.value = null
chatModel.updateChatTags(rhId) chatModel.updateChatTags(rhId)

View file

@ -123,8 +123,9 @@ fun TerminalLayout(
@Composable @Composable
fun TerminalLog(floating: Boolean, composeViewHeight: State<Dp>) { fun TerminalLog(floating: Boolean, composeViewHeight: State<Dp>) {
val items = chatModel.terminalItems.collectAsState()
val reversedTerminalItems by remember { val reversedTerminalItems by remember {
derivedStateOf { chatModel.terminalItems.value.asReversed() } derivedStateOf { items.value.asReversed(); }
} }
val listState = LocalAppBarHandler.current?.listState ?: rememberLazyListState() val listState = LocalAppBarHandler.current?.listState ?: rememberLazyListState()
var autoScrollToBottom = rememberSaveable { mutableStateOf(true) } var autoScrollToBottom = rememberSaveable { mutableStateOf(true) }

View file

@ -58,9 +58,11 @@ data class ItemSeparation(val timestamp: Boolean, val largeGap: Boolean, val dat
// staleChatId means the id that was before chatModel.chatId becomes null. It's needed for Android only to make transition from chat // staleChatId means the id that was before chatModel.chatId becomes null. It's needed for Android only to make transition from chat
// to chat list smooth. Otherwise, chat view will become blank right before the transition starts // to chat list smooth. Otherwise, chat view will become blank right before the transition starts
fun ChatView(staleChatId: StateFlow<String?>, onComposed: suspend (chatId: String) -> Unit) { fun ChatView(staleChatId: StateFlow<String?>, onComposed: suspend (chatId: String) -> Unit) {
val remoteHostId = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.remoteHostId } } println("LALAL CHAT VIEW")
val chats = chatModel.chats.collectAsState()
val remoteHostId = remember { derivedStateOf { chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.remoteHostId } }
val showSearch = rememberSaveable { mutableStateOf(false) } val showSearch = rememberSaveable { mutableStateOf(false) }
val activeChatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo } } val activeChatInfo = remember { derivedStateOf { chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo } }
val user = chatModel.currentUser.value val user = chatModel.currentUser.value
val chatInfo = activeChatInfo.value val chatInfo = activeChatInfo.value
if (chatInfo == null || user == null) { if (chatInfo == null || user == null) {
@ -107,7 +109,7 @@ fun ChatView(staleChatId: StateFlow<String?>, onComposed: suspend (chatId: Strin
// Having activeChat reloaded on every change in it is inefficient (UI lags) // Having activeChat reloaded on every change in it is inefficient (UI lags)
val unreadCount = remember { val unreadCount = remember {
derivedStateOf { derivedStateOf {
chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == activeChatInfo.value?.id }?.chatStats?.unreadCount ?: 0 chats.value.firstOrNull { chat -> chat.chatInfo.id == activeChatInfo.value?.id }?.chatStats?.unreadCount ?: 0
} }
} }
val clipboard = LocalClipboardManager.current val clipboard = LocalClipboardManager.current

View file

@ -64,7 +64,7 @@ fun ModalData.GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: Strin
updateChatSettings(chat.remoteHostId, chat.chatInfo, chatSettings, chatModel) updateChatSettings(chat.remoteHostId, chat.chatInfo, chatSettings, chatModel)
sendReceipts.value = sendRcpts sendReceipts.value = sendRcpts
}, },
members = chatModel.groupMembers.value members = chatModel.groupMembers.collectAsState().value
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved } .filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
.sortedByDescending { it.memberRole }, .sortedByDescending { it.memberRole },
developerTools, developerTools,

View file

@ -44,8 +44,7 @@ import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.ImageResource
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -306,7 +305,7 @@ private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFiel
if (!chatModel.desktopNoUserNoRemote) { if (!chatModel.desktopNoUserNoRemote) {
ChatList(searchText = searchText, listState) ChatList(searchText = searchText, listState)
} }
if (chatModel.chats.value.isEmpty() && !chatModel.switchingUsersAndHosts.value && !chatModel.desktopNoUserNoRemote) { if (chatModel.chats.collectAsState().value.isEmpty() && !chatModel.switchingUsersAndHosts.value && !chatModel.desktopNoUserNoRemote) {
Text( Text(
stringResource( stringResource(
if (chatModel.chatRunning.value == null) MR.strings.loading_chats else MR.strings.you_have_no_chats if (chatModel.chatRunning.value == null) MR.strings.loading_chats else MR.strings.you_have_no_chats
@ -448,7 +447,8 @@ private fun ChatListToolbar(userPickerState: MutableStateFlow<AnimatedViewState>
} }
} }
} else { } else {
val users by remember { derivedStateOf { chatModel.users.value.filter { u -> u.user.activeUser || !u.user.hidden } } } val us = chatModel.users.collectAsState()
val users by remember { derivedStateOf { us.value.filter { u -> u.user.activeUser || !u.user.hidden } } }
val allRead = users val allRead = users
.filter { u -> !u.user.activeUser && !u.user.hidden } .filter { u -> !u.user.activeUser && !u.user.hidden }
.all { u -> u.unreadCount == 0 } .all { u -> u.unreadCount == 0 }
@ -777,7 +777,7 @@ private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listStat
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
} }
val allChats = remember { chatModel.chats } val allChats = chatModel.chats.collectAsState()
// In some not always reproducible situations this code produce IndexOutOfBoundsException on Compose's side // In some not always reproducible situations this code produce IndexOutOfBoundsException on Compose's side
// which is related to [derivedStateOf]. Using safe alternative instead // which is related to [derivedStateOf]. Using safe alternative instead
// val chats by remember(search, showUnreadAndFavorites) { derivedStateOf { filteredChats(showUnreadAndFavorites, search, allChats.toList()) } } // val chats by remember(search, showUnreadAndFavorites) { derivedStateOf { filteredChats(showUnreadAndFavorites, search, allChats.toList()) } }
@ -926,10 +926,10 @@ private val TAG_MIN_HEIGHT = 35.dp
@Composable @Composable
private fun TagsView() { private fun TagsView() {
val userTags = remember { chatModel.userTags } val userTags = chatModel.userTags.collectAsState()
val presetTags = remember { chatModel.presetTags } val presetTags = chatModel.presetTags.collectAsState()
val activeFilter = remember { chatModel.activeChatTagFilter } val activeFilter = chatModel.activeChatTagFilter.collectAsState()
val unreadTags = remember { chatModel.unreadTags } val unreadTags = chatModel.unreadTags.collectAsState()
val rhId = chatModel.remoteHostId() val rhId = chatModel.remoteHostId()
fun showTagList() { fun showTagList() {
@ -949,7 +949,7 @@ private fun TagsView() {
TagsRow { TagsRow {
if (presetTags.value.size > 1) { if (presetTags.value.size > 1) {
if (presetTags.value.size + userTags.value.size <= 3) { if (presetTags.value.size + userTags.value.size <= 3) {
PresetTagKind.entries.filter { t -> (presetTags[t] ?: 0) > 0 }.forEach { tag -> PresetTagKind.entries.filter { t -> (presetTags.value[t] ?: 0) > 0 }.forEach { tag ->
ExpandedTagFilterView(tag) ExpandedTagFilterView(tag)
} }
} else { } else {
@ -995,7 +995,7 @@ private fun TagsView() {
} }
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(4.dp))
Box { Box {
val badgeText = if ((unreadTags[tag.chatTagId] ?: 0) > 0) "" else "" val badgeText = if ((unreadTags.value[tag.chatTagId] ?: 0) > 0) "" else ""
val invisibleText = buildAnnotatedString { val invisibleText = buildAnnotatedString {
append(tag.chatTagText) append(tag.chatTagText)
withStyle(SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.SemiBold)) { withStyle(SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.SemiBold)) {

View file

@ -94,7 +94,8 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal
if (showSearch) { if (showSearch) {
BackHandler(onBack = hideSearchOnBack) BackHandler(onBack = hideSearchOnBack)
} }
val users by remember { derivedStateOf { chatModel.users.value.filter { u -> u.user.activeUser || !u.user.hidden } } } val us = chatModel.users.collectAsState()
val users by remember { derivedStateOf { us.value.filter { u -> u.user.activeUser || !u.user.hidden } } }
val navButton: @Composable RowScope.() -> Unit = { val navButton: @Composable RowScope.() -> Unit = {
when { when {
showSearch -> NavigationButtonBack(hideSearchOnBack) showSearch -> NavigationButtonBack(hideSearchOnBack)

View file

@ -53,9 +53,10 @@ fun UserPicker(
userPickerState.value = AnimatedViewState.HIDING userPickerState.value = AnimatedViewState.HIDING
} }
} }
val us = chatModel.users.collectAsState()
val users by remember { val users by remember {
derivedStateOf { derivedStateOf {
chatModel.users.value us.value
.filter { u -> u.user.activeUser || !u.user.hidden } .filter { u -> u.user.activeUser || !u.user.hidden }
.sortedByDescending { it.user.activeOrder } .sortedByDescending { it.user.activeOrder }
} }