Merge pull request #3694 from vitorpamplona/claude/remove-module-warnings-s7v2q0

Refactor persistent collections API and fix deprecation warnings
This commit is contained in:
Vitor Pamplona
2026-07-24 13:03:07 -04:00
committed by GitHub
39 changed files with 90 additions and 75 deletions
@@ -1034,6 +1034,8 @@ class AppModules(
)
}
// androidx.security.crypto's EncryptedSharedPreferences is deprecated with no drop-in successor.
@Suppress("DEPRECATION")
fun encryptedStorage(npub: String? = null): EncryptedSharedPreferences = EncryptedStorage.preferences(appContext, npub)
fun initiate(appContext: Context) {
@@ -1092,9 +1094,11 @@ class AppModules(
uiState
}
// LRUCache should not be instanciated in the Main thread due to blocking
// LRUCache should not be instanciated in the Main thread due to blocking.
// trimToSize is a no-op on the empty cache; it just forces the object's
// (blocking) initialization to happen off the main thread.
applicationIOScope.launch {
CachedRobohash
CachedRobohash.trimToSize(100)
resourceCacheInit()
}
@@ -31,6 +31,9 @@ class EncryptedStorage {
// returns the preferences for each account or a global file if null.
fun prefsFileName(npub: String? = null): String = if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
// androidx.security.crypto is deprecated with no drop-in successor; migrating the
// on-disk key store is a separate, security-sensitive effort.
@Suppress("DEPRECATION")
fun preferences(
applicationContext: Context,
npub: String? = null,
@@ -50,6 +50,7 @@ class ExoPlayerBuilder(
val renderersFactory =
object : DefaultRenderersFactory(context) {
@Suppress("DEPRECATION") // media3 DefaultAudioSink.Builder.setEnableAudioTrackPlaybackParams is deprecated.
override fun buildAudioSink(
context: Context,
enableFloatOutput: Boolean,
@@ -75,7 +75,7 @@ class ResourceUsageAccountant(
) {
if (amount <= 0) return
live.computeIfAbsent(key) { AtomicLong() }.addAndGet(amount)
if (inHookRun.get()) return
if (inHookRun.get() == true) return
if (flushScheduled.compareAndSet(false, true)) {
scope.launch {
delay(flushDebounceMs)
@@ -57,16 +57,16 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
@@ -79,6 +79,7 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -86,6 +87,7 @@ import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.quartz.nip56Reports.ReportType
import kotlinx.coroutines.launch
@Composable
fun BlossomBlobManagerScreen(
@@ -235,7 +237,8 @@ private fun BlobCard(
) {
var menuOpen by remember { mutableStateOf(false) }
var reportOpen by remember { mutableStateOf(false) }
val clipboard = LocalClipboardManager.current
val clipboard = LocalClipboard.current
val scope = rememberCoroutineScope()
val context = LocalContext.current
Column(
@@ -276,7 +279,8 @@ private fun BlobCard(
leadingIcon = { MenuIcon(MaterialSymbols.ContentCopy) },
onClick = {
menuOpen = false
clipboard.setText(AnnotatedString(row.url))
val url = row.url
scope.launch { clipboard.setText(url) }
},
)
DropdownMenuItem(
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.ui.screen.loggedIn.browser
import android.content.ComponentName
@@ -39,8 +39,8 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -125,7 +125,7 @@ fun AgentConsoleScreen(
},
) { padding ->
Column(modifier = Modifier.padding(padding).fillMaxSize()) {
TabRow(selectedTabIndex = selectedTab) {
SecondaryTabRow(selectedTabIndex = selectedTab) {
tabs.forEachIndexed { index, title ->
Tab(
selected = selectedTab == index,
@@ -108,7 +108,7 @@ fun BuzzCanvasScreen(
}
},
) { padding ->
if (content.isNullOrBlank() || canvas == null) {
if (content.isNullOrBlank()) {
Box(
modifier = Modifier.padding(padding).fillMaxSize().padding(32.dp),
contentAlignment = Alignment.Center,
@@ -38,16 +38,15 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.service.buzz.BuzzInviteMinter
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -73,7 +72,7 @@ fun BuzzWorkspaceOverflowMenu(
var error by remember { mutableStateOf<String?>(null) }
val scope = rememberCoroutineScope()
val context = LocalContext.current
val clipboard = LocalClipboardManager.current
val clipboard = LocalClipboard.current
IconButton(onClick = { menuOpen = true }) {
Icon(
@@ -145,13 +144,13 @@ fun BuzzWorkspaceOverflowMenu(
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, minted.url)
}
ContextCompat.startActivity(context, Intent.createChooser(send, null), null)
context.startActivity(Intent.createChooser(send, null))
result = null
}) { Text(stringRes(R.string.buzz_invite_share)) }
},
dismissButton = {
TextButton(onClick = {
clipboard.setText(AnnotatedString(minted.url))
scope.launch { clipboard.setText(minted.url) }
result = null
}) { Text(stringRes(R.string.buzz_invite_copy)) }
},
@@ -278,7 +278,7 @@ fun ChatMessageActionSheet(
SectionDivider()
TileRow {
ActionTile(MaterialSymbols.Edit, stringRes(R.string.buzz_edit_message)) {
onWantsToEditBuzz!!(note)
onWantsToEditBuzz(note)
onDismiss()
}
}
@@ -228,7 +228,7 @@ fun RenderBuzzDiff(note: Note) {
it.language?.let { lang -> append(if (isEmpty()) lang else " · $lang") }
it.prNumber?.let { pr -> append(" · PR #$pr") }
it.commitSha
?.takeIf { sha -> sha.isNotBlank() }
.takeIf { sha -> sha.isNotBlank() }
?.let { sha -> append(" · ${sha.take(8)}") }
}.ifBlank { null }
}
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.ui.screen.loggedIn.embed
import android.os.Build
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.ui.screen.loggedIn.embed
import android.content.ClipData
@@ -153,6 +153,7 @@ class RemoteImeView(
fun selectAllText(): Boolean = onTextContextMenuItem(android.R.id.selectAll)
/** A page field focused: configure the keyboard, seed the buffer, and raise the IME. */
@Suppress("DEPRECATION") // InputMethodManager.SHOW_IMPLICIT is deprecated; no equivalent flag on the newer API.
fun onPageFocus(focus: ImeEvent.Focus) {
configureFor(focus)
// Focus the EditText BEFORE seeding text/selection. An EditText jumps its caret to the end when it
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.ui.screen.loggedIn.favorites
import android.content.ComponentName
@@ -72,11 +72,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@@ -87,6 +86,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.model.nip46Signer.Nip46ActivityEntry
import com.vitorpamplona.amethyst.model.nip46Signer.Nip46SignerState
import com.vitorpamplona.amethyst.ui.components.util.setText
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
@@ -107,7 +107,7 @@ fun Nip46SignerScreen(
val signer = account.nip46Signer
val scope = rememberCoroutineScope()
val context = LocalContext.current
val clipboard = LocalClipboardManager.current
val clipboard = LocalClipboard.current
val enabled by account.settings.nip46SignerEnabled.collectAsStateWithLifecycle()
val secret by account.settings.nip46BunkerSecret.collectAsStateWithLifecycle()
@@ -187,7 +187,7 @@ fun Nip46SignerScreen(
QrHeroCard(
uri = uri,
onCopy = {
clipboard.setText(AnnotatedString(uri))
scope.launch { clipboard.setText(uri) }
Toast.makeText(context, R.string.nip46_signer_copied, Toast.LENGTH_SHORT).show()
},
onRegenerate = { confirmRotate = true },
@@ -33,6 +33,7 @@ object PushNotificationUtils {
var lastToken: String? = null
var hasInit: List<AccountInfo>? = null
@Suppress("DEPRECATION") // FirebaseMessaging.token is deprecated in the current SDK.
suspend fun checkAndInit(
accounts: List<AccountInfo>,
okHttpClient: (String) -> OkHttpClient,
@@ -300,7 +300,7 @@ class Context(
.header("Accept", "application/nostr+json")
.build()
okhttp.newCall(request).execute().use { resp ->
resp.body?.string()?.let { Nip11RelayInformation.fromJson(it) }
resp.body.string().let { Nip11RelayInformation.fromJson(it) }
}
}.getOrNull()
}
@@ -380,7 +380,7 @@ object BuzzCommands {
.get()
.build(),
).execute()
.use { it.code to (it.body?.string() ?: "") }
.use { it.code to it.body.string() }
}
private suspend fun httpPost(
@@ -392,7 +392,7 @@ object BuzzCommands {
withContext(Dispatchers.IO) {
val builder = Request.Builder().url(url).post(body.toRequestBody(jsonMedia))
if (auth != null) builder.header("Authorization", auth)
http.newCall(builder.build()).execute().use { it.code to (it.body?.string() ?: "") }
http.newCall(builder.build()).execute().use { it.code to it.body.string() }
}
/** `buzz post RELAY GID <text>` → publishes a kind-40002 stream message with an `h` tag. */
@@ -578,7 +578,7 @@ object BuzzCommands {
.groupBy { it.slug() }
.values
.mapNotNull { versions -> versions.maxByOrNull { it.createdAt } }
.sortedBy { it.personaOrNull()?.displayName ?: it.slug() ?: "" }
.sortedBy { it.personaOrNull()?.displayName ?: it.slug() }
.map {
val content = it.personaOrNull()
mapOf(
@@ -113,7 +113,7 @@ object GitPatchCommands {
"event_id" to signed.id,
"kind" to signed.kind,
"repository" to Address.assemble(addr.kind, addr.pubKeyHex, addr.dTag),
"subject" to (signed as? GitPatchEvent)?.subject(),
"subject" to signed.subject(),
) + RawEventSupport.ackFields(ack),
)
return 0
@@ -133,6 +133,7 @@ object GitReadCommands {
* comment trees and PR-update (1619) events (which use NIP-22 uppercase `E`)
* are out of scope here.
*/
@Suppress("DEPRECATION") // legacy kind:1622 GitReplyEvent is read for backward compatibility.
suspend fun thread(
dataDir: DataDir,
rest: Array<String>,
@@ -64,6 +64,9 @@ actual class SecureKeyStorage private actual constructor() {
}
}
// androidx.security.crypto is deprecated with no drop-in successor; migrating the
// on-disk key store is a separate, security-sensitive effort.
@Suppress("DEPRECATION")
private val masterKey: MasterKey by lazy {
MasterKey
.Builder(appContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
@@ -71,6 +74,7 @@ actual class SecureKeyStorage private actual constructor() {
.build()
}
@Suppress("DEPRECATION")
private val encryptedPrefs by lazy {
EncryptedSharedPreferences.create(
appContext,
@@ -57,7 +57,7 @@ object BuzzAgentActivityState {
) = lock.withLock {
val prev = mutableActivity.value[agent]
if (prev != null && atSecs <= prev) return@withLock
mutableActivity.value = mutableActivity.value.put(agent, atSecs)
mutableActivity.value = mutableActivity.value.putting(agent, atSecs)
}
/** Test-only: clears all activity so unit tests don't leak into each other. */
@@ -65,8 +65,8 @@ object BuzzPresenceState {
) = lock.withLock {
val prev = stampByUser[subject]
if (prev != null && atSecs <= prev) return@withLock
stampByUser = stampByUser.put(subject, atSecs)
mutablePresence.value = mutablePresence.value.put(subject, status)
stampByUser = stampByUser.putting(subject, atSecs)
mutablePresence.value = mutablePresence.value.putting(subject, status)
}
/** The latest known status string for [subject], or `null` if none has arrived. */
@@ -52,7 +52,7 @@ class AuthApprovalRequests {
/** Track a newly surfaced tier-2 challenge. Last write per relay wins. */
fun add(approval: PendingAuthApproval) {
_pending.update { it.put(approval.relayUrl, approval) }
_pending.update { it.putting(approval.relayUrl, approval) }
}
/**
@@ -65,7 +65,7 @@ class AuthApprovalRequests {
scope: AuthApprovalScope,
): Boolean {
val approval = _pending.value[relayUrl] ?: return false
_pending.update { it.remove(relayUrl) }
_pending.update { it.removing(relayUrl) }
return approval.decision.complete(scope)
}
@@ -391,7 +391,7 @@ fun RelayReachDetailDialog(
confirmButton = {
if (showRetry) {
TextButton(onClick = {
onRetry?.invoke()
onRetry()
onDismiss()
}) { Text(stringResource(Res.string.action_try_again)) }
} else {
@@ -46,7 +46,7 @@ class GeoRelayCsvLoader(
.build()
okHttpClient(url).newCall(request).execute().use { response ->
if (response.isSuccessful) {
GeoRelayDirectory.parseCsv(response.body?.string().orEmpty())
GeoRelayDirectory.parseCsv(response.body.string())
} else {
emptyList()
}
@@ -99,7 +99,7 @@ fun rememberNoteMenuActions(
scope.launch { snackbar?.showSnackbar("Broadcast to relays") }
},
)
if (canModerate && account != null) {
if (canModerate) {
add(
NoteMenuAction("Mute user") {
scope.launch {
@@ -275,31 +275,6 @@ fun WalletColumnScreen(
if (showReceiveDialog && nwcConnection != null) {
ReceiveDialog(
onDismiss = { showReceiveDialog = false },
onGenerate = { amountSats, description ->
scope.launch {
val result =
paymentHandler.makeInvoice(
nwcConnection = nwcConnection,
amountMsats = amountSats * 1000,
description = description.ifBlank { null },
)
when (result) {
is NwcPaymentHandler.InvoiceResult.Success -> {
result.invoice
}
is NwcPaymentHandler.InvoiceResult.Error -> {
snackbarHostState.showSnackbar("Error: ${result.message}")
null
}
is NwcPaymentHandler.InvoiceResult.Timeout -> {
snackbarHostState.showSnackbar("Invoice request timed out")
null
}
}
}
},
paymentHandler = paymentHandler,
nwcConnection = nwcConnection,
snackbarHostState = snackbarHostState,
@@ -833,7 +808,6 @@ private fun SendDialog(
@Composable
private fun ReceiveDialog(
onDismiss: () -> Unit,
onGenerate: (Long, String) -> Unit,
paymentHandler: NwcPaymentHandler,
nwcConnection: Nip47URINorm,
snackbarHostState: SnackbarHostState,
@@ -299,6 +299,7 @@ class NappletBrowserActivity : ComponentActivity() {
wv.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
@Suppress("DEPRECATION")
databaseEnabled = false
allowFileAccess = false
allowContentAccess = false
@@ -245,6 +245,7 @@ class NappletBrowserService : Service() {
}
/** Builds the SandboxedUiAdapter for [tab] and ships its cross-process handle (coreLibInfo) to the client. */
@Suppress("DEPRECATION") // androidx.privacysandbox.ui toCoreLibInfo is deprecated with no successor.
private fun replyWithAdapter(tab: BrowserTab) {
val adapter = NappletBrowserUiAdapter(this, tab.sessionId)
val coreLibInfo = adapter.toCoreLibInfo(this)
@@ -306,6 +307,7 @@ class NappletBrowserService : Service() {
wv.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
@Suppress("DEPRECATION")
databaseEnabled = false
allowFileAccess = false
allowContentAccess = false
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.napplethost
import android.content.Context
@@ -504,6 +504,7 @@ class NappletHostActivity : ComponentActivity() {
// per-applet origin (a napplet.local subdomain) — storage is scoped to that origin, isolated
// from other applets and from the shell. SPAs need it at boot; without it they crash-loop.
domStorageEnabled = true
@Suppress("DEPRECATION")
databaseEnabled = false
allowFileAccess = false
allowContentAccess = false
@@ -270,6 +270,7 @@ class NappletHostService : Service() {
}
/** Builds the SandboxedUiAdapter for [tab] and ships its cross-process handle (coreLibInfo) to the client. */
@Suppress("DEPRECATION") // androidx.privacysandbox.ui toCoreLibInfo is deprecated with no successor.
private fun replyWithAdapter(tab: NappletTab) {
val adapter = NappletHostUiAdapter(this, tab.sessionId)
val coreLibInfo = adapter.toCoreLibInfo(this)
@@ -330,6 +331,7 @@ class NappletHostService : Service() {
wv.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
@Suppress("DEPRECATION")
databaseEnabled = false
allowFileAccess = false
allowContentAccess = false
@@ -18,6 +18,8 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@file:Suppress("DEPRECATION")
package com.vitorpamplona.amethyst.napplethost
import android.content.Context
@@ -205,6 +205,10 @@ class AndroidBleTransport(
readCharacteristic = null
}
// The 5-arg connectGatt(context, autoConnect, callback, transport, phy) overload is deprecated
// in favor of the variant that also takes a Handler; keep this one to avoid changing BLE
// callback threading.
@Suppress("DEPRECATION")
override fun connectToPeer(peer: BlePeer) {
val device =
peer.platformHandle as? BluetoothDevice ?: run {
@@ -44,6 +44,7 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey
* assigned Role and holds [ConcordPermissions.MANAGE_ROLES]. Cycles that never
* touch the owner can never bootstrap themselves.
*/
@ConsistentCopyVisibility
data class AuthorityResolver private constructor(
private val ownerLower: String,
private val roles: Map<String, RoleEntity>,
@@ -86,7 +86,7 @@ object ConcordInviteLink {
if (useStock) {
out.add(FLAG_STOCK_RELAYS.toByte())
} else {
require(relays!!.size <= MAX_RELAYS) { "at most $MAX_RELAYS relays, was ${relays.size}" }
require(relays.size <= MAX_RELAYS) { "at most $MAX_RELAYS relays, was ${relays.size}" }
out.add(0)
out.add(relays.size.toByte())
for (r in relays) {
@@ -210,11 +210,11 @@ class FilterIndex<S : Any> {
is AuthorKey -> authors = authors.removeSub(key.author, subscriber)
is KindKey -> kinds = kinds.removeSub(key.kind, subscriber)
is TagKey -> tags = tags.removeTagSub(key.letter, key.value, subscriber)
Unindexed -> unindexed = unindexed.remove(subscriber)
Unindexed -> unindexed = unindexed.removing(subscriber)
}
}
val next =
State(ids, authors, tags, kinds, unindexed, current.assignments.remove(subscriber))
State(ids, authors, tags, kinds, unindexed, current.assignments.removing(subscriber))
if (state.compareAndSet(current, next)) return
}
}
@@ -275,12 +275,12 @@ class FilterIndex<S : Any> {
is AuthorKey -> authors = authors.addSub(key.author, subscriber)
is KindKey -> kinds = kinds.addSub(key.kind, subscriber)
is TagKey -> tags = tags.addTagSub(key.letter, key.value, subscriber)
Unindexed -> unindexed = unindexed.add(subscriber)
Unindexed -> unindexed = unindexed.adding(subscriber)
}
}
val existing = current.assignments[subscriber]
val merged = existing?.addAll(keySet) ?: keySet
val next = State(ids, authors, tags, kinds, unindexed, current.assignments.put(subscriber, merged))
val merged = existing?.addingAll(keySet) ?: keySet
val next = State(ids, authors, tags, kinds, unindexed, current.assignments.putting(subscriber, merged))
if (state.compareAndSet(current, next)) return
}
}
@@ -292,8 +292,8 @@ class FilterIndex<S : Any> {
sub: S,
): PersistentMap<K, PersistentSet<S>> {
val cur = this[key] ?: persistentHashSetOf()
val next = cur.add(sub)
return if (next === cur) this else put(key, next)
val next = cur.adding(sub)
return if (next === cur) this else putting(key, next)
}
private fun <K> PersistentMap<K, PersistentSet<S>>.removeSub(
@@ -301,11 +301,11 @@ class FilterIndex<S : Any> {
sub: S,
): PersistentMap<K, PersistentSet<S>> {
val cur = this[key] ?: return this
val next = cur.remove(sub)
val next = cur.removing(sub)
return when {
next === cur -> this
next.isEmpty() -> remove(key)
else -> put(key, next)
next.isEmpty() -> removing(key)
else -> putting(key, next)
}
}
@@ -316,7 +316,7 @@ class FilterIndex<S : Any> {
): PersistentMap<String, PersistentMap<String, PersistentSet<S>>> {
val inner = this[letter] ?: persistentHashMapOf()
val newInner = inner.addSub(value, sub)
return if (newInner === inner) this else put(letter, newInner)
return if (newInner === inner) this else putting(letter, newInner)
}
private fun PersistentMap<String, PersistentMap<String, PersistentSet<S>>>.removeTagSub(
@@ -328,8 +328,8 @@ class FilterIndex<S : Any> {
val newInner = inner.removeSub(value, sub)
return when {
newInner === inner -> this
newInner.isEmpty() -> remove(letter)
else -> put(letter, newInner)
newInner.isEmpty() -> removing(letter)
else -> putting(letter, newInner)
}
}