mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
refactor: JavaSerializable, shared notices in the NIP-46 signer, client-injected subscription stats
- JavaSerializable (commons): java.io.Serializable on JVM/Android, a marker on iOS, for the chat list's saveable lazy keys. - Nip46SignerScreen drops its Toasts (and an unused Context parameter) for the shared notices. - ActiveSubscriptionsViewModel polls the client the screen hands it instead of Amethyst.instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
+2
-2
@@ -64,6 +64,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.commons.util.JavaSerializable
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||
@@ -81,7 +82,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import java.io.Serializable
|
||||
|
||||
@Composable
|
||||
fun ChatroomListFeedView(
|
||||
@@ -302,7 +302,7 @@ private fun relayShortName(relay: NormalizedRelayUrl): String =
|
||||
// stores LazyColumn item keys in a SaveableStateHolder, which on Android
|
||||
// requires Bundle-storable types, so each variant is Serializable and only
|
||||
// holds primitives.
|
||||
private sealed interface ChatroomLazyKey : Serializable
|
||||
private sealed interface ChatroomLazyKey : JavaSerializable
|
||||
|
||||
private data class MarmotChatroomLazyKey(
|
||||
val groupId: HexKey,
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ fun ActiveSubscriptionsScreen(
|
||||
nav: INav,
|
||||
viewModel: ActiveSubscriptionsViewModel = viewModel(),
|
||||
) {
|
||||
LaunchedEffect(Unit) { viewModel.startPolling() }
|
||||
LaunchedEffect(Unit) { viewModel.startPolling(accountViewModel.account.client) }
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
Scaffold(
|
||||
|
||||
+6
-6
@@ -23,14 +23,15 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.subscriptions
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -143,18 +144,17 @@ class ActiveSubscriptionsViewModel : ViewModel() {
|
||||
val state: StateFlow<ActiveSubscriptionsState> = _state.asStateFlow()
|
||||
|
||||
/** Polls while the screen is on. [REFRESH_MS] is slow enough to be free, fast enough to feel live. */
|
||||
fun startPolling() {
|
||||
fun startPolling(client: INostrClient) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
while (isActive) {
|
||||
_state.value = snapshot()
|
||||
kotlinx.coroutines.delay(REFRESH_MS)
|
||||
_state.value = snapshot(client)
|
||||
delay(REFRESH_MS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun snapshot(): ActiveSubscriptionsState =
|
||||
private suspend fun snapshot(client: INostrClient): ActiveSubscriptionsState =
|
||||
withContext(Dispatchers.Default) {
|
||||
val client = Amethyst.instance.client
|
||||
aggregateSubscriptions(
|
||||
client.connectedRelaysFlow().value.associateWith { relay ->
|
||||
client.activeRequests(relay).values.flatten()
|
||||
|
||||
+8
-11
@@ -20,8 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
@@ -73,7 +71,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -119,6 +116,8 @@ import com.vitorpamplona.amethyst.commons.resources.nip46_signer_scan_connect
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_status_no_relays
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip46_signer_turn_on
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberLongNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
@@ -142,7 +141,8 @@ fun Nip46SignerScreen(
|
||||
val account = accountViewModel.account
|
||||
val signer = account.nip46Signer
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val shortNotice = rememberShortNotice()
|
||||
val longNotice = rememberLongNotice()
|
||||
val clipboard = LocalClipboard.current
|
||||
val copiedStr = stringRes(Res.string.nip46_signer_copied)
|
||||
val regeneratedStr = stringRes(Res.string.nip46_signer_regenerated)
|
||||
@@ -175,7 +175,7 @@ fun Nip46SignerScreen(
|
||||
fun onConnect(uri: String) {
|
||||
scope.launch {
|
||||
val result = signer.connectViaNostrConnect(uri.trim())
|
||||
Toast.makeText(context, describe(context, result), Toast.LENGTH_LONG).show()
|
||||
longNotice.show(describe(result))
|
||||
refreshKey++
|
||||
}
|
||||
}
|
||||
@@ -231,7 +231,7 @@ fun Nip46SignerScreen(
|
||||
uri = uri,
|
||||
onCopy = {
|
||||
scope.launch { clipboard.setText(uri) }
|
||||
Toast.makeText(context, copiedStr, Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(copiedStr)
|
||||
},
|
||||
onRegenerate = { confirmRotate = true },
|
||||
)
|
||||
@@ -273,7 +273,7 @@ fun Nip46SignerScreen(
|
||||
onConfirm = {
|
||||
confirmRotate = false
|
||||
signer.rotateAddress()
|
||||
Toast.makeText(context, regeneratedStr, Toast.LENGTH_SHORT).show()
|
||||
shortNotice.show(regeneratedStr)
|
||||
},
|
||||
onDismiss = { confirmRotate = false },
|
||||
)
|
||||
@@ -625,10 +625,7 @@ private fun ReadOnlyNotice() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun describe(
|
||||
context: Context,
|
||||
result: Nip46SignerState.ConnectResult,
|
||||
): String =
|
||||
private suspend fun describe(result: Nip46SignerState.ConnectResult): String =
|
||||
when (result) {
|
||||
is Nip46SignerState.ConnectResult.Connected ->
|
||||
result.name?.let { loadStringRes(Res.string.nip46_signer_connected_named, it) }
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* 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.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
/**
|
||||
* `java.io.Serializable` where there is a JVM, a marker elsewhere. For values Android must store in
|
||||
* a Bundle (saveable state, lazy-list keys) that shared code declares.
|
||||
*/
|
||||
expect interface JavaSerializable
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* 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.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
actual interface JavaSerializable
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* 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.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.util
|
||||
|
||||
actual typealias JavaSerializable = java.io.Serializable
|
||||
Reference in New Issue
Block a user