From f93c22ee142ec132a8197066f4848d576d0d85b4 Mon Sep 17 00:00:00 2001 From: infi Date: Sun, 24 May 2026 23:42:27 +0200 Subject: [PATCH] fix: race condition when loading into a dm channel on startup --- .../views/channel/ChannelScreenViewModel.kt | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreenViewModel.kt b/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreenViewModel.kt index 36ce737e..66d2b144 100644 --- a/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreenViewModel.kt +++ b/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreenViewModel.kt @@ -429,79 +429,78 @@ class ChannelScreenViewModel( ignoreExisting: Boolean = false, markLastAsRead: Boolean = false ) { - channel?.id?.let { channelId -> - loadMessagesJob = viewModelScope.launch { - try { - val messages = arrayListOf() + val currentChannelId = channelId ?: return + loadMessagesJob = viewModelScope.launch { + try { + val messages = arrayListOf() - fetchMessagesFromChannel(channelId, amount, true, before, after, around).let { - if (it.messages.isNullOrEmpty() || it.messages!!.size < 50) { - endOfChannel = true - } + fetchMessagesFromChannel(currentChannelId, amount, true, before, after, around).let { + if (it.messages.isNullOrEmpty() || it.messages!!.size < 50) { + endOfChannel = true + } - it.users?.forEach { user -> - if (!StoatAPI.userCache.containsKey(user.id)) { - StoatAPI.userCache[user.id!!] = user - } - } - - it.messages?.forEach { message -> - addUserIfUnknown(message.author ?: return@forEach) - if (!StoatAPI.messageCache.containsKey(message.id)) { - StoatAPI.messageCache[message.id!!] = message - } - messages.add(message) - } - - it.members?.forEach { member -> - if (!StoatAPI.members.hasMember(member.id!!.server, member.id!!.user)) { - StoatAPI.members.setMember(member.id!!.server, member) - } - } - - if (markLastAsRead) { - ackMessage(messages.firstOrNull()?.id ?: return@launch) + it.users?.forEach { user -> + if (!StoatAPI.userCache.containsKey(user.id)) { + StoatAPI.userCache[user.id!!] = user } } - val newItems = messages.filter { - if (ignoreExisting) { - items.none { m -> - when (m) { - is ChannelScreenItem.RegularMessage -> m.message.id == it.id - is ChannelScreenItem.ProspectiveMessage -> m.message.id == it.id - is ChannelScreenItem.SystemMessage -> m.message.id == it.id - is ChannelScreenItem.FailedMessage -> m.message.id == it.id - else -> false - } - } - } else { - true + it.messages?.forEach { message -> + addUserIfUnknown(message.author ?: return@forEach) + if (!StoatAPI.messageCache.containsKey(message.id)) { + StoatAPI.messageCache[message.id!!] = message } - }.map { - when { - it.system != null -> ChannelScreenItem.SystemMessage(it) - else -> ChannelScreenItem.RegularMessage(it) + messages.add(message) + } + + it.members?.forEach { member -> + if (!StoatAPI.members.hasMember(member.id!!.server, member.id!!.user)) { + StoatAPI.members.setMember(member.id!!.server, member) } } - // Place items according to whether above/below/around was specified. - // TODO: Aditionally, place LoadTriggers at the beginning and end of the list. - val newItemsWithPosition = when { - before != null -> items + newItems - after != null -> newItems + items - // TODO around, which should place the new items in the middle of the list - else -> newItems + if (markLastAsRead) { + ackMessage(messages.firstOrNull()?.id ?: return@launch) } - - updateItems(newItemsWithPosition) - - if (!didInitialChannelFetch) { - didInitialChannelFetch = true - } - } catch (e: Exception) { - Log.e("ChannelScreenViewModel", "Failed to fetch messages", e) } + + val newItems = messages.filter { + if (ignoreExisting) { + items.none { m -> + when (m) { + is ChannelScreenItem.RegularMessage -> m.message.id == it.id + is ChannelScreenItem.ProspectiveMessage -> m.message.id == it.id + is ChannelScreenItem.SystemMessage -> m.message.id == it.id + is ChannelScreenItem.FailedMessage -> m.message.id == it.id + else -> false + } + } + } else { + true + } + }.map { + when { + it.system != null -> ChannelScreenItem.SystemMessage(it) + else -> ChannelScreenItem.RegularMessage(it) + } + } + + // Place items according to whether above/below/around was specified. + // TODO: Aditionally, place LoadTriggers at the beginning and end of the list. + val newItemsWithPosition = when { + before != null -> items + newItems + after != null -> newItems + items + // TODO around, which should place the new items in the middle of the list + else -> newItems + } + + updateItems(newItemsWithPosition) + + if (!didInitialChannelFetch) { + didInitialChannelFetch = true + } + } catch (e: Exception) { + Log.e("ChannelScreenViewModel", "Failed to fetch messages", e) } } }