Allows users to pick relays on the request to vanish screen
This commit is contained in:
@@ -2046,16 +2046,16 @@ class Account(
|
||||
suspend fun saveBlockedRelayList(blockedRelays: List<NormalizedRelayUrl>) = sendMyPublicAndPrivateOutbox(blockedRelayList.saveRelayList(blockedRelays))
|
||||
|
||||
suspend fun requestToVanish(
|
||||
relay: String,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
reason: String,
|
||||
createdAt: Long,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
if (!isWriteable() || relays.isEmpty()) return
|
||||
|
||||
val template = RequestToVanishEvent.build(relay, reason, createdAt)
|
||||
val template = RequestToVanishEvent.build(relays, reason, createdAt)
|
||||
val signedEvent = signer.sign(template)
|
||||
cache.justConsumeMyOwnEvent(signedEvent)
|
||||
client.send(signedEvent, setOf(NormalizedRelayUrl(relay)))
|
||||
client.send(signedEvent, relays.toSet())
|
||||
}
|
||||
|
||||
suspend fun requestToVanishFromEverywhere(
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.torState
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManager
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||
@@ -35,6 +36,7 @@ import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@Stable
|
||||
class TorRelayState(
|
||||
val okHttpClient: DualHttpClientManager,
|
||||
val torSettingsFlow: TorSettingsFlow,
|
||||
|
||||
+2
-2
@@ -961,10 +961,10 @@ class AccountViewModel(
|
||||
fun delete(note: Note) = launchSigner { account.delete(note) }
|
||||
|
||||
fun requestToVanish(
|
||||
relay: String,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
reason: String,
|
||||
createdAt: Long,
|
||||
) = launchSigner { account.requestToVanish(relay, reason, createdAt) }
|
||||
) = launchSigner { account.requestToVanish(relays, reason, createdAt) }
|
||||
|
||||
fun requestToVanishFromEverywhere(
|
||||
reason: String,
|
||||
|
||||
+15
-1
@@ -122,9 +122,23 @@ fun RelayUrlEditField(
|
||||
onNewRelay: (NormalizedRelayUrl) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
RelayUrlEditField(
|
||||
onNewRelay = onNewRelay,
|
||||
nip11CachedRetriever = Amethyst.instance.nip11Cache,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RelayUrlEditField(
|
||||
onNewRelay: (NormalizedRelayUrl) -> Unit,
|
||||
nip11CachedRetriever: Nip11CachedRetriever,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val relaySuggestions = remember { RelaySuggestionState() }
|
||||
val nip11CachedRetriever = remember { Amethyst.instance.nip11Cache }
|
||||
RelayUrlEditField(
|
||||
onNewRelay = onNewRelay,
|
||||
relaySuggestions = relaySuggestions,
|
||||
|
||||
+78
-38
@@ -65,16 +65,25 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.components.TextSpinner
|
||||
import com.vitorpamplona.amethyst.model.nip11RelayInfo.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.ui.components.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -90,12 +99,26 @@ import java.util.Locale
|
||||
fun RequestToVanishScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
RequestToVanishScreen(
|
||||
nip11CachedRetriever = Amethyst.instance.nip11Cache,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RequestToVanishScreen(
|
||||
nip11CachedRetriever: Nip11CachedRetriever,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val connectedRelays by accountViewModel.account.client
|
||||
.connectedRelaysFlow()
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
var selectedRelayUrl by remember { mutableStateOf<String?>(null) }
|
||||
var selectedRelayUrls by remember { mutableStateOf(emptyList<NormalizedRelayUrl>()) }
|
||||
var allRelaysSelected by remember { mutableStateOf(false) }
|
||||
var vanishDate by remember { mutableLongStateOf(TimeUtils.now()) }
|
||||
var reason by remember { mutableStateOf("") }
|
||||
@@ -149,43 +172,22 @@ fun RequestToVanishScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
// Relay Selection
|
||||
Text(
|
||||
text = stringRes(R.string.vanish_target_relay),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
TextSpinner(
|
||||
label = stringRes(R.string.vanish_target_relay),
|
||||
placeholder =
|
||||
if (allRelaysSelected) {
|
||||
stringRes(R.string.vanish_all_relays)
|
||||
} else {
|
||||
selectedRelayUrl ?: stringRes(R.string.vanish_select_relay)
|
||||
},
|
||||
options = relayOptions,
|
||||
onSelect = { index ->
|
||||
selectedRelayUrl = relayOptions[index].explainer
|
||||
allRelaysSelected = false
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// ALL RELAYS checkbox
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
// Relay Selection
|
||||
Text(
|
||||
text = stringRes(R.string.vanish_target_relay),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
Checkbox(
|
||||
checked = allRelaysSelected,
|
||||
onCheckedChange = {
|
||||
allRelaysSelected = it
|
||||
if (it) selectedRelayUrl = null
|
||||
},
|
||||
)
|
||||
Text(
|
||||
@@ -196,7 +198,33 @@ fun RequestToVanishScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (allRelaysSelected) {
|
||||
if (!allRelaysSelected) {
|
||||
selectedRelayUrls.forEach {
|
||||
val info =
|
||||
remember(it) {
|
||||
relaySetupInfoBuilder(it, false)
|
||||
}
|
||||
|
||||
BasicRelaySetupInfoDialog(
|
||||
info,
|
||||
onDelete = { selectedRelayUrls -= selectedRelayUrls },
|
||||
nip11CachedRetriever = nip11CachedRetriever,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
RelayUrlEditField(
|
||||
onNewRelay = {
|
||||
selectedRelayUrls += selectedRelayUrls + it
|
||||
allRelaysSelected = false
|
||||
},
|
||||
nip11CachedRetriever = nip11CachedRetriever,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
} else {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
@@ -279,7 +307,7 @@ fun RequestToVanishScreen(
|
||||
label = { Text(stringRes(R.string.vanish_reason_label)) },
|
||||
placeholder = { Text(stringRes(R.string.vanish_reason_placeholder)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
minLines = 2,
|
||||
minLines = 1,
|
||||
maxLines = 4,
|
||||
)
|
||||
|
||||
@@ -289,7 +317,7 @@ fun RequestToVanishScreen(
|
||||
Button(
|
||||
onClick = { showConfirmDialog = true },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = allRelaysSelected || selectedRelayUrl != null,
|
||||
enabled = allRelaysSelected || selectedRelayUrls.isNotEmpty(),
|
||||
colors =
|
||||
ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.error,
|
||||
@@ -354,14 +382,14 @@ fun RequestToVanishScreen(
|
||||
if (showConfirmDialog) {
|
||||
ConfirmVanishDialog(
|
||||
isAllRelays = allRelaysSelected,
|
||||
relayUrl = selectedRelayUrl,
|
||||
relayUrls = selectedRelayUrls,
|
||||
onConfirm = {
|
||||
showConfirmDialog = false
|
||||
if (allRelaysSelected) {
|
||||
accountViewModel.requestToVanishFromEverywhere(reason, vanishDate)
|
||||
} else {
|
||||
selectedRelayUrl?.let {
|
||||
accountViewModel.requestToVanish(it, reason, vanishDate)
|
||||
if (selectedRelayUrls.isNotEmpty()) {
|
||||
accountViewModel.requestToVanish(selectedRelayUrls, reason, vanishDate)
|
||||
}
|
||||
}
|
||||
accountViewModel.toastManager.toast(
|
||||
@@ -378,7 +406,7 @@ fun RequestToVanishScreen(
|
||||
@Composable
|
||||
private fun ConfirmVanishDialog(
|
||||
isAllRelays: Boolean,
|
||||
relayUrl: String?,
|
||||
relayUrls: List<NormalizedRelayUrl>,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
@@ -404,7 +432,7 @@ private fun ConfirmVanishDialog(
|
||||
if (isAllRelays) {
|
||||
stringRes(R.string.vanish_confirm_all_relays)
|
||||
} else {
|
||||
stringRes(R.string.vanish_confirm_single_relay, relayUrl ?: "")
|
||||
stringRes(R.string.vanish_confirm_single_relay, relayUrls.joinToString(", ") { it.displayUrl() })
|
||||
},
|
||||
)
|
||||
},
|
||||
@@ -431,3 +459,15 @@ private fun formatTimestamp(epochSeconds: Long): String {
|
||||
val sdf = SimpleDateFormat("MMM dd, yyyy hh:mm a", Locale.getDefault())
|
||||
return sdf.format(Date(epochSeconds * 1000))
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun RequestToVanishScreenPreview() {
|
||||
ThemeComparisonColumn {
|
||||
RequestToVanishScreen(
|
||||
nip11CachedRetriever = Nip11CachedRetriever { TODO() },
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = EmptyNav(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -142,17 +142,17 @@ fun AllSettingsScreen(
|
||||
SettingsNavigationRow(
|
||||
title = R.string.request_to_vanish,
|
||||
icon = Icons.Outlined.DeleteForever,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.RequestToVanish) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.vanish_history,
|
||||
icon = Icons.Outlined.History,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.VanishEvents) },
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.vanish_history,
|
||||
icon = Icons.Outlined.History,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.VanishEvents) },
|
||||
)
|
||||
HorizontalDivider(thickness = 4.dp)
|
||||
SettingsSectionHeader(R.string.app_settings)
|
||||
SettingsNavigationRow(
|
||||
|
||||
@@ -1978,7 +1978,7 @@
|
||||
<string name="request_to_vanish">Request to Vanish</string>
|
||||
<string name="request_to_vanish_description">Request relays to permanently delete all your data up to the selected date. This action is based on NIP-62 and is legally binding in some jurisdictions.</string>
|
||||
<string name="vanish_select_relay">Select a relay</string>
|
||||
<string name="vanish_target_relay">Target Relay</string>
|
||||
<string name="vanish_target_relay">Target Relays</string>
|
||||
<string name="vanish_all_relays">ALL RELAYS</string>
|
||||
<string name="vanish_all_relays_warning">This will request ALL relays to delete everything associated with your key up to the selected date. This event will be broadcast as widely as possible. This action cannot be undone.</string>
|
||||
<string name="vanish_date_label">Delete data up to</string>
|
||||
|
||||
+3
-2
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip62RequestToVanish
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.shouldVanishFrom
|
||||
@@ -48,7 +49,7 @@ class RequestToVanishEvent(
|
||||
const val ALT = "Request to vanish"
|
||||
|
||||
fun build(
|
||||
relay: String,
|
||||
relay: NormalizedRelayUrl,
|
||||
reason: String = "",
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<RequestToVanishEvent>.() -> Unit = {},
|
||||
@@ -59,7 +60,7 @@ class RequestToVanishEvent(
|
||||
}
|
||||
|
||||
fun build(
|
||||
relays: List<String>,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
reason: String = "",
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<RequestToVanishEvent>.() -> Unit = {},
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip62RequestToVanish.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RelayTag {
|
||||
@@ -44,7 +45,7 @@ class RelayTag {
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(relay: String) = arrayOf(TAG_NAME, relay)
|
||||
fun assemble(relay: NormalizedRelayUrl) = arrayOf(TAG_NAME, relay.url)
|
||||
|
||||
fun assembleEverywhere() = arrayOf(TAG_NAME, EVERYWHERE)
|
||||
}
|
||||
|
||||
+3
-2
@@ -21,10 +21,11 @@
|
||||
package com.vitorpamplona.quartz.nip62RequestToVanish.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
|
||||
fun TagArrayBuilder<RequestToVanishEvent>.vanishFromEverywhere() = add(RelayTag.assembleEverywhere())
|
||||
|
||||
fun TagArrayBuilder<RequestToVanishEvent>.vanishFrom(relay: String) = add(RelayTag.assemble(relay))
|
||||
fun TagArrayBuilder<RequestToVanishEvent>.vanishFrom(relay: NormalizedRelayUrl) = add(RelayTag.assemble(relay))
|
||||
|
||||
fun TagArrayBuilder<RequestToVanishEvent>.vanishFrom(relays: List<String>) = addAll(relays.map { RelayTag.assemble(it) })
|
||||
fun TagArrayBuilder<RequestToVanishEvent>.vanishFrom(relays: List<NormalizedRelayUrl>) = addAll(relays.map { RelayTag.assemble(it) })
|
||||
|
||||
Reference in New Issue
Block a user