mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 00:17:17 +01:00
Merge branch 'main' of https://github.com/vitorpamplona/amethyst
* 'main' of https://github.com/vitorpamplona/amethyst: optimise imports synchronize all cache mutations and supply sinceRelaySet so callers get snapshot copies rework groupByRelayPresence to build relay snapshots via sinceRelaySet, filtering with immutable lists to prevent ConcurrentModificationException when relays update mid-iteration Hardened EOSEAccountFast against concurrent access so callers no longer iterate over live mutable maps Adjusted subscription cleanup to avoid mutating the watcher map while iterating it, preventing the ConcurrentModificationException when accounts switch # Conflicts: # amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt
This commit is contained in:
@@ -46,7 +46,6 @@ import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlin.collections.map
|
||||
|
||||
/**
|
||||
* Maintains several stateflows for each step in processing PeopleLists
|
||||
|
||||
@@ -186,11 +186,11 @@ class AccountFollowsLoaderSubAssembler(
|
||||
}
|
||||
|
||||
// removes accounts that are not being subscribed anymore.
|
||||
accountUpdatesJobMap.forEach {
|
||||
if (it.key !in uniqueSubscribedAccounts.keys) {
|
||||
endWatcher(it.key)
|
||||
}
|
||||
}
|
||||
// Cancel watchers for accounts no longer observed using a snapshot to avoid CME
|
||||
accountUpdatesJobMap.keys
|
||||
.toList()
|
||||
.filter { it !in uniqueSubscribedAccounts.keys }
|
||||
.forEach { endWatcher(it) }
|
||||
}
|
||||
|
||||
private val accountUpdatesJobMap = mutableMapOf<User, Job>()
|
||||
|
||||
@@ -90,19 +90,30 @@ class UserReportsSubAssembler(
|
||||
users: Iterable<User>,
|
||||
eoseCache: EOSEAccountFast<User>,
|
||||
inRelays: Set<NormalizedRelayUrl>,
|
||||
): Collection<List<User>> =
|
||||
users
|
||||
.groupBy {
|
||||
eoseCache
|
||||
.since(it)
|
||||
?.keys
|
||||
?.intersect(inRelays)
|
||||
?.hashCode()
|
||||
): Collection<List<User>> {
|
||||
if (users.none()) return emptyList()
|
||||
|
||||
val relaySnapshot = inRelays.toSet()
|
||||
|
||||
return users
|
||||
.groupBy { user ->
|
||||
val relaysForUser = eoseCache.sinceRelaySet(user)
|
||||
if (relaysForUser.isNullOrEmpty() || relaySnapshot.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
val intersection = relaysForUser.filter { it in relaySnapshot }.sorted()
|
||||
if (intersection.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
intersection.hashCode()
|
||||
}
|
||||
}
|
||||
}.values
|
||||
.map {
|
||||
// important to keep in order otherwise the Relay thinks the filter has changed and we REQ again
|
||||
it.sortedBy { it.pubkeyHex }
|
||||
}
|
||||
}
|
||||
|
||||
fun findMinimumEOSEsForUsers(
|
||||
users: List<User>,
|
||||
|
||||
@@ -125,36 +125,51 @@ class EOSEAccountFast<T : Any>(
|
||||
cacheSize: Int = 20,
|
||||
) {
|
||||
private val users: LruCache<T, EOSERelayList> = LruCache<T, EOSERelayList>(cacheSize)
|
||||
private val lock = Any()
|
||||
|
||||
fun addOrUpdate(
|
||||
user: T,
|
||||
relayUrl: NormalizedRelayUrl,
|
||||
time: Long,
|
||||
) {
|
||||
val relayList = users[user]
|
||||
if (relayList == null) {
|
||||
val newList = EOSERelayList()
|
||||
users.put(user, newList)
|
||||
synchronized(lock) {
|
||||
val relayList = users[user]
|
||||
if (relayList == null) {
|
||||
val newList = EOSERelayList()
|
||||
users.put(user, newList)
|
||||
|
||||
newList.addOrUpdate(relayUrl, time)
|
||||
} else {
|
||||
relayList.addOrUpdate(relayUrl, time)
|
||||
newList.addOrUpdate(relayUrl, time)
|
||||
} else {
|
||||
relayList.addOrUpdate(relayUrl, time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeEveryoneBut(list: Set<T>) {
|
||||
users.snapshot().forEach {
|
||||
if (it.key !in list) {
|
||||
users.remove(it.key)
|
||||
synchronized(lock) {
|
||||
users.snapshot().forEach {
|
||||
if (it.key !in list) {
|
||||
users.remove(it.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeDataFor(user: T) {
|
||||
users.remove(user)
|
||||
synchronized(lock) {
|
||||
users.remove(user)
|
||||
}
|
||||
}
|
||||
|
||||
fun since(key: T) = users[key]?.relayList
|
||||
fun since(key: T): SincePerRelayMap? =
|
||||
synchronized(lock) {
|
||||
users[key]?.relayList?.toMutableMap()
|
||||
}
|
||||
|
||||
fun sinceRelaySet(key: T): Set<NormalizedRelayUrl>? =
|
||||
synchronized(lock) {
|
||||
users[key]?.relayList?.keys?.toSet()
|
||||
}
|
||||
|
||||
fun newEose(
|
||||
user: T,
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowing
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav.nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserComposeNoAction
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list.PeopleListItem
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfHalfHorzModifier
|
||||
|
||||
@@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Groups
|
||||
import androidx.compose.material.icons.outlined.Lock
|
||||
|
||||
Reference in New Issue
Block a user