mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 00:16:59 +00:00
refactor: share user-load and reason-string helpers across relay-auth UI
The auth prompt dialog (RelayAuthPromptHost) and the relay-auth settings screen each carried an identical copy of a pubkey→User loader and the AuthPurposeKind→reason-string mapping. Extract both into RelayAuthComposeHelpers (LoadRelayAuthUser, relayAuthReasonRes) and point both call sites at the shared versions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZjmYpgHP4pf79Sav5QT8a
This commit is contained in:
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.service.relayClient.authCommand.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.AuthPurposeKind
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
/**
|
||||
* Loads [pubkey] from the local cache, get-or-creating (and subscribing) if absent, then hands the
|
||||
* [User] (or null while it loads) to [content]. Shared by the auth prompt dialog and the relay-auth
|
||||
* settings screen, which both render a person by pubkey while their metadata streams in.
|
||||
*/
|
||||
@Composable
|
||||
internal fun LoadRelayAuthUser(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
content: @Composable (User?) -> Unit,
|
||||
) {
|
||||
var user by remember(pubkey) { mutableStateOf(accountViewModel.getUserIfExists(pubkey)) }
|
||||
if (user == null) {
|
||||
LaunchedEffect(pubkey) { user = accountViewModel.checkGetOrCreateUser(pubkey) }
|
||||
}
|
||||
content(user)
|
||||
}
|
||||
|
||||
/** The string explaining a single [AuthPurposeKind] ("To send DMs to", "To download posts from", …). */
|
||||
internal fun relayAuthReasonRes(kind: AuthPurposeKind): Int =
|
||||
when (kind) {
|
||||
AuthPurposeKind.SEND_DM -> R.string.relay_auth_reason_send_dm
|
||||
AuthPurposeKind.NOTIFY_INBOX -> R.string.relay_auth_reason_notify_inbox
|
||||
AuthPurposeKind.READ_OUTBOX -> R.string.relay_auth_reason_read_outbox
|
||||
AuthPurposeKind.POST_VENUE -> R.string.relay_auth_reason_post_venue
|
||||
AuthPurposeKind.READ_VENUE -> R.string.relay_auth_reason_read_venue
|
||||
AuthPurposeKind.MY_OWN_RELAY -> R.string.relay_auth_reason_my_own_relay
|
||||
AuthPurposeKind.OTHER -> R.string.relay_auth_reason_other
|
||||
}
|
||||
+3
-28
@@ -56,7 +56,6 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.Channel
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.AuthPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.AuthPurposeKind
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.model.RelayAuthPrompt
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.model.UserAuthChoice
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
@@ -143,7 +142,7 @@ private fun RelayAuthPromptDialog(
|
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (showLabels) {
|
||||
Text(
|
||||
text = stringRes(reasonRes(purpose.kind)),
|
||||
text = stringRes(relayAuthReasonRes(purpose.kind)),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -211,7 +210,7 @@ private fun CounterpartyRow(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
LoadUser(pubkey, accountViewModel) { user ->
|
||||
LoadRelayAuthUser(pubkey, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -234,7 +233,7 @@ private fun CounterpartyFacepile(
|
||||
horizontalArrangement = Arrangement.spacedBy((-8).dp),
|
||||
) {
|
||||
pubkeys.take(FACEPILE_MAX).forEach { pubkey ->
|
||||
LoadUser(pubkey, accountViewModel) { user ->
|
||||
LoadRelayAuthUser(pubkey, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
ClickableUserPicture(
|
||||
baseUser = user,
|
||||
@@ -257,30 +256,6 @@ private fun CounterpartyFacepile(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadUser(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
content: @Composable (User?) -> Unit,
|
||||
) {
|
||||
var user by remember(pubkey) { mutableStateOf(accountViewModel.getUserIfExists(pubkey)) }
|
||||
if (user == null) {
|
||||
LaunchedEffect(pubkey) { user = accountViewModel.checkGetOrCreateUser(pubkey) }
|
||||
}
|
||||
content(user)
|
||||
}
|
||||
|
||||
private fun reasonRes(kind: AuthPurposeKind): Int =
|
||||
when (kind) {
|
||||
AuthPurposeKind.SEND_DM -> R.string.relay_auth_reason_send_dm
|
||||
AuthPurposeKind.NOTIFY_INBOX -> R.string.relay_auth_reason_notify_inbox
|
||||
AuthPurposeKind.READ_OUTBOX -> R.string.relay_auth_reason_read_outbox
|
||||
AuthPurposeKind.POST_VENUE -> R.string.relay_auth_reason_post_venue
|
||||
AuthPurposeKind.READ_VENUE -> R.string.relay_auth_reason_read_venue
|
||||
AuthPurposeKind.MY_OWN_RELAY -> R.string.relay_auth_reason_my_own_relay
|
||||
AuthPurposeKind.OTHER -> R.string.relay_auth_reason_other
|
||||
}
|
||||
|
||||
/** The purpose that best describes what the user was doing (most user-facing first). */
|
||||
private fun List<AuthPurpose>.primaryNamed(): AuthPurpose? =
|
||||
listOf(
|
||||
|
||||
+4
-27
@@ -63,7 +63,8 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.AuthPurposeKind
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.RelayAuthDecision
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.RelayAuthPolicy
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.LoadRelayAuthUser
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.relayAuthReasonRes
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.model.DataStoreRelayAuthPermissionStore
|
||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.model.RelayAuthPermissionLedger
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
@@ -308,7 +309,7 @@ private fun RelayRationaleCard(
|
||||
}
|
||||
rationale.forEach { (kind, pubkeys) ->
|
||||
Text(
|
||||
text = stringResource(reasonRes(kind)),
|
||||
text = stringResource(relayAuthReasonRes(kind)),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -334,7 +335,7 @@ private fun RationaleUserRow(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
LoadUserForRationale(pubkey, accountViewModel) { user ->
|
||||
LoadRelayAuthUser(pubkey, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -348,30 +349,6 @@ private fun RationaleUserRow(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadUserForRationale(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
content: @Composable (User?) -> Unit,
|
||||
) {
|
||||
var user by remember(pubkey) { mutableStateOf(accountViewModel.getUserIfExists(pubkey)) }
|
||||
if (user == null) {
|
||||
LaunchedEffect(pubkey) { user = accountViewModel.checkGetOrCreateUser(pubkey) }
|
||||
}
|
||||
content(user)
|
||||
}
|
||||
|
||||
private fun reasonRes(kind: AuthPurposeKind): Int =
|
||||
when (kind) {
|
||||
AuthPurposeKind.SEND_DM -> R.string.relay_auth_reason_send_dm
|
||||
AuthPurposeKind.NOTIFY_INBOX -> R.string.relay_auth_reason_notify_inbox
|
||||
AuthPurposeKind.READ_OUTBOX -> R.string.relay_auth_reason_read_outbox
|
||||
AuthPurposeKind.POST_VENUE -> R.string.relay_auth_reason_post_venue
|
||||
AuthPurposeKind.READ_VENUE -> R.string.relay_auth_reason_read_venue
|
||||
AuthPurposeKind.MY_OWN_RELAY -> R.string.relay_auth_reason_my_own_relay
|
||||
AuthPurposeKind.OTHER -> R.string.relay_auth_reason_other
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PerRelayOverrideRow(
|
||||
url: String,
|
||||
|
||||
Reference in New Issue
Block a user