Avoiding concurrent exceptions when looping through messages

This commit is contained in:
Vitor Pamplona 2023-01-27 14:50:20 -03:00
parent c29c14f82b
commit a934b42524
5 changed files with 26 additions and 13 deletions

View File

@ -86,7 +86,7 @@ class User(val pubkey: ByteArray) {
@Synchronized
fun getOrCreateChannel(user: User): MutableSet<Note> {
return messages[user] ?: run {
val channel = mutableSetOf<Note>()
val channel = Collections.synchronizedSet(mutableSetOf<Note>())
messages[user] = channel
channel
}
@ -137,6 +137,14 @@ class User(val pubkey: ByteArray) {
}
}
fun hasSentMessagesTo(user: User?): Boolean {
val messagesToUser = messages[user] ?: return false
return synchronized(messagesToUser) {
messagesToUser.firstOrNull { this == it.author } != null
}
}
// Model Observers
private var listeners = setOf<Listener>()

View File

@ -33,9 +33,13 @@ object NostrChatRoomDataSource: NostrDataSource<Note>("ChatroomFeed") {
// returns the last Note of each user.
override fun feed(): List<Note> {
val messages = account.userProfile().messages[withUser]?.filter { account.isAcceptable(it) }
val messages = account.userProfile().messages[withUser] ?: return emptyList()
return messages?.sortedBy { it.event?.createdAt }?.reversed() ?: emptyList()
val filteredMessages = synchronized(messages) {
messages.filter { account.isAcceptable(it) }
}
return filteredMessages.sortedBy { it.event?.createdAt }.reversed()
}
override fun updateChannelFilters() {

View File

@ -67,19 +67,20 @@ object NostrChatroomListDataSource: NostrDataSource<Note>("MailBoxFeed") {
val messagingWith = messages.keys().toList().filter { account.isAcceptable(it) }
val privateMessages = messagingWith.mapNotNull {
messages[it]?.sortedBy { it.event?.createdAt }?.lastOrNull { it.event != null }
val conversation = messages[it]
if (conversation != null) {
synchronized(conversation) {
conversation.sortedBy { it.event?.createdAt }.lastOrNull { it.event != null }
}
} else {
null
}
}
val publicChannels = account.followingChannels().map {
it.notes.values.filter { account.isAcceptable(it) }.sortedBy { it.event?.createdAt }.lastOrNull { it.event != null }
}
val channelsCreatedByMe = LocalCache.channels.values.filter {
it.creator == account.userProfile()
}.map {
it.notes.values.sortedBy { it.event?.createdAt }.lastOrNull { it.event != null }
}
return (privateMessages + publicChannels).filterNotNull().sortedBy { it.event?.createdAt }.reversed()
}

View File

@ -138,7 +138,7 @@ private fun messagesHasNewItems(cache: NotificationCache): Boolean {
return NostrChatroomListDataSource.feed().take(100).filter {
// only for known sources
val me = NostrChatroomListDataSource.account.userProfile()
it.channel != null || me.messages[it.author]?.firstOrNull { me == it.author } != null
it.channel != null || me.hasSentMessagesTo(it.author)
}.filter {
val lastTime = if (it.channel != null) {
cache.load("Channel/${it.channel!!.idHex}", context)

View File

@ -39,7 +39,7 @@ class NostrChatroomListKnownFeedViewModel: FeedViewModel(NostrChatroomListDataSo
// Filter: all channels + PMs the account has replied to
return super.newListFromDataSource().filter {
val me = NostrChatroomListDataSource.account.userProfile()
it.channel != null || me.messages[it.author]?.firstOrNull { me == it.author } != null
it.channel != null || me.hasSentMessagesTo(it.author)
}
}
}
@ -48,7 +48,7 @@ class NostrChatroomListNewFeedViewModel: FeedViewModel(NostrChatroomListDataSour
// Filter: no channels + PMs the account has never replied to
return super.newListFromDataSource().filter {
val me = NostrChatroomListDataSource.account.userProfile()
it.channel == null && me.messages[it.author]?.firstOrNull { me == it.author } == null
it.channel == null && !me.hasSentMessagesTo(it.author)
}
}
}