Merge pull request #2112 from greenart7c3/claude/add-relay-setting-kxeIb

Add option to forward profile events to local relays
This commit is contained in:
Vitor Pamplona
2026-04-03 16:10:13 -04:00
committed by GitHub
8 changed files with 101 additions and 0 deletions
@@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.model.edits.PrivateStorageRelayListDecryptionCache
import com.vitorpamplona.amethyst.model.edits.PrivateStorageRelayListState
import com.vitorpamplona.amethyst.model.localRelays.ForwardKind0ToLocalRelayState
import com.vitorpamplona.amethyst.model.localRelays.LocalRelayListState
import com.vitorpamplona.amethyst.model.nip01UserMetadata.AccountHomeRelayState
import com.vitorpamplona.amethyst.model.nip01UserMetadata.AccountOutboxRelayState
@@ -267,6 +268,8 @@ class Account(
val nip65RelayList = Nip65RelayListState(signer, cache, scope, settings)
val localRelayList = LocalRelayListState(signer, cache, scope, settings)
val forwardKind0ToLocalRelay = ForwardKind0ToLocalRelayState(client, localRelayList, settings)
val dmRelayList = DmRelayListState(signer, cache, scope, settings)
val privateStorageDecryptionCache = PrivateStorageRelayListDecryptionCache(signer)
@@ -433,6 +436,14 @@ class Account(
return false
}
suspend fun updateSendKind0EventsToLocalRelay(send: Boolean): Boolean {
if (settings.changeSendKind0EventsToLocalRelay(send)) {
sendNewAppSpecificData()
return true
}
return false
}
suspend fun updateFilterSpam(filterSpam: Boolean): Boolean {
if (settings.updateFilterSpam(filterSpam)) {
if (!settings.syncedSettings.security.filterSpamFromStrangers.value) {
@@ -424,6 +424,14 @@ class AccountSettings(
}
}
fun changeSendKind0EventsToLocalRelay(send: Boolean): Boolean {
if (syncedSettings.security.updateSendKind0EventsToLocalRelay(send)) {
saveAccountSettings()
return true
}
return false
}
fun updateUserMetadata(newMetadata: MetadataEvent?) {
if (newMetadata == null) return
@@ -54,6 +54,7 @@ class AccountSyncedSettings(
internalSettings.security.warnAboutPostsWithReports,
MutableStateFlow(internalSettings.security.filterSpamFromStrangers),
MutableStateFlow(internalSettings.security.maxHashtagLimit),
MutableStateFlow(internalSettings.security.sendKind0EventsToLocalRelay),
)
fun toInternal(): AccountSyncedSettingsInternal =
@@ -76,6 +77,7 @@ class AccountSyncedSettings(
security.warnAboutPostsWithReports,
security.filterSpamFromStrangers.value,
security.maxHashtagLimit.value,
security.sendKind0EventsToLocalRelay.value,
),
)
@@ -126,6 +128,10 @@ class AccountSyncedSettings(
if (security.maxHashtagLimit.value != syncedSettingsInternal.security.maxHashtagLimit) {
security.maxHashtagLimit.tryEmit(syncedSettingsInternal.security.maxHashtagLimit)
}
if (security.sendKind0EventsToLocalRelay.value != syncedSettingsInternal.security.sendKind0EventsToLocalRelay) {
security.sendKind0EventsToLocalRelay.tryEmit(syncedSettingsInternal.security.sendKind0EventsToLocalRelay)
}
}
fun dontTranslateFromFilteredBySpokenLanguages(): Set<String> = languages.dontTranslateFrom.value - getLanguagesSpokenByUser()
@@ -211,6 +217,7 @@ class AccountSecurityPreferences(
var warnAboutPostsWithReports: Boolean = true,
var filterSpamFromStrangers: MutableStateFlow<Boolean> = MutableStateFlow(true),
val maxHashtagLimit: MutableStateFlow<Int> = MutableStateFlow(5),
var sendKind0EventsToLocalRelay: MutableStateFlow<Boolean> = MutableStateFlow(false),
) {
fun updateShowSensitiveContent(show: Boolean?): Boolean {
if (showSensitiveContent.value != show) {
@@ -243,4 +250,12 @@ class AccountSecurityPreferences(
} else {
false
}
fun updateSendKind0EventsToLocalRelay(send: Boolean): Boolean =
if (send != sendKind0EventsToLocalRelay.value) {
sendKind0EventsToLocalRelay.tryEmit(send)
true
} else {
false
}
}
@@ -106,4 +106,5 @@ class AccountSecurityPreferencesInternal(
var warnAboutPostsWithReports: Boolean = true,
var filterSpamFromStrangers: Boolean = true,
val maxHashtagLimit: Int = 5,
var sendKind0EventsToLocalRelay: Boolean = false,
)
@@ -0,0 +1,42 @@
/*
* 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.model.localRelays
import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.EventCollector
class ForwardKind0ToLocalRelayState(
val client: INostrClient,
val localRelayList: LocalRelayListState,
val settings: AccountSettings,
) {
private val eventCollector =
EventCollector(client) { event, _ ->
if (event is MetadataEvent && settings.syncedSettings.security.sendKind0EventsToLocalRelay.value) {
val localRelays = localRelayList.flow.value
if (localRelays.isNotEmpty()) {
client.publish(event, localRelays)
}
}
}
}
@@ -1144,6 +1144,8 @@ class AccountViewModel(
fun filterSpamFromStrangers() = account.settings.syncedSettings.security.filterSpamFromStrangers
fun toggleSendKind0ToLocalRelay(enabled: Boolean) = launchSigner { account.updateSendKind0EventsToLocalRelay(enabled) }
fun updateWarnReports(warnReports: Boolean) = launchSigner { account.updateWarnReports(warnReports) }
fun updateFilterSpam(filterSpam: Boolean) =
@@ -37,6 +37,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -88,6 +89,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayLi
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.renderSearchItems
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.trusted.TrustedRelayListViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.trusted.renderTrustedItems
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
@@ -351,6 +353,24 @@ fun MappedAllRelayListView(
SettingsCategorySpacingModifier,
)
}
item {
val sendKind0 by accountViewModel.account.settings.syncedSettings.security.sendKind0EventsToLocalRelay
.collectAsStateWithLifecycle()
var checked by remember(sendKind0) { mutableStateOf(sendKind0) }
SettingsRow(
R.string.send_kind0_to_local_relay_title,
R.string.send_kind0_to_local_relay_description,
) {
Switch(
checked = checked,
onCheckedChange = {
checked = it
accountViewModel.toggleSendKind0ToLocalRelay(it)
},
)
}
}
renderLocalItems(localFeedState, localViewModel, accountViewModel, nav)
item {
+2
View File
@@ -1517,6 +1517,8 @@
<string name="search_section_explainer">List of relays to use when searching content or users. Tagging and search will not work if no options are available. Make sure they implement NIP-50.</string>
<string name="local_section">Local Relays</string>
<string name="local_section_explainer">List of relays that are running in this device.</string>
<string name="send_kind0_to_local_relay_title">Forward profiles to local relay</string>
<string name="send_kind0_to_local_relay_description">Send all received profile (kind 0) events to your local relay</string>
<string name="trusted_relays_title">Trusted Relays</string>
<string name="trusted_section">Trusted Relays</string>