diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 04ff884a5..12bac04e4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -2319,6 +2319,12 @@ class Account( } val outbound = manager.leaveGroup(nostrGroupId) + // manager.leaveGroup already wiped MLS state, relay subscriptions and + // the persisted message log. Drop the in-memory chatroom too — that + // releases the strong refs to the decrypted inner notes so LocalCache + // (which holds them weakly) can GC them, and the Notification feed + // (which iterates marmotGroupList.rooms) stops surfacing the group. + marmotGroupList.removeGroup(nostrGroupId) client.publish(outbound.signedEvent, groupRelays) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupInfoScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupInfoScreen.kt index b83de760a..46da171b5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupInfoScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupInfoScreen.kt @@ -316,7 +316,6 @@ fun MarmotGroupInfoScreen( scope.launch(Dispatchers.IO) { try { accountViewModel.leaveMarmotGroup(nostrGroupId) - accountViewModel.account.marmotGroupList.removeGroup(nostrGroupId) nav.nav(Route.Message) } catch (e: Exception) { isLeaving = false diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt index cd106aabd..140478395 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupChatroom.kt @@ -202,4 +202,21 @@ class MarmotGroupChatroom( changesFlow.get()?.tryEmit(ListChange.SetDeletion(toRemove)) return toRemove } + + /** + * Drop every message from this chatroom. Used when the local user leaves + * the group: cuts the strong references held in [messages] so the + * decrypted inner notes become eligible for GC out of LocalCache (which + * holds them weakly). + */ + @Synchronized + fun clearAllMessagesSync(): Set { + val toRemove = messages + if (toRemove.isEmpty()) return toRemove + messages = emptySet() + newestMessage = null + unreadCount.value = 0 + changesFlow.get()?.tryEmit(ListChange.SetDeletion(toRemove)) + return toRemove + } } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt index 5e622ead4..589a40a8e 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/marmotGroups/MarmotGroupList.kt @@ -127,8 +127,15 @@ class MarmotGroupList( } } + /** + * Drop a group from the in-memory list. Also clears the chatroom's own + * message set and the note→group index: LocalCache holds notes weakly, so + * once these strong references go away the decrypted inner messages + * become eligible for GC and stop appearing in the Notification feed. + */ fun removeGroup(nostrGroupId: HexKey) { - rooms.remove(nostrGroupId) + val chatroom = rooms.remove(nostrGroupId) + chatroom?.clearAllMessagesSync()?.forEach { noteToGroupIndex.remove(it.idHex) } _groupListChanges.tryEmit(nostrGroupId) }