mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-10-11 03:34:45 +02:00
Uses NIP-78 to start syncing some data between instances of Amethyst
This commit is contained in:
@@ -105,7 +105,7 @@ height="70">](https://github.com/vitorpamplona/amethyst/releases)
|
|||||||
- [x] Video Events (NIP-71)
|
- [x] Video Events (NIP-71)
|
||||||
- [x] Moderated Communities (NIP-72)
|
- [x] Moderated Communities (NIP-72)
|
||||||
- [ ] Zap Goals (NIP-75)
|
- [ ] Zap Goals (NIP-75)
|
||||||
- [ ] Arbitrary Custom App Data (NIP-78)
|
- [x] Arbitrary Custom App Data (NIP-78)
|
||||||
- [x] Highlights (NIP-84)
|
- [x] Highlights (NIP-84)
|
||||||
- [x] Notify Request (NIP-88/Draft)
|
- [x] Notify Request (NIP-88/Draft)
|
||||||
- [x] Recommended Application Handlers (NIP-89)
|
- [x] Recommended Application Handlers (NIP-89)
|
||||||
|
@@ -26,7 +26,12 @@ import android.content.SharedPreferences
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.vitorpamplona.amethyst.model.AccountLanguagePreferencesInternal
|
||||||
|
import com.vitorpamplona.amethyst.model.AccountReactionPreferencesInternal
|
||||||
|
import com.vitorpamplona.amethyst.model.AccountSecurityPreferencesInternal
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
|
import com.vitorpamplona.amethyst.model.AccountSyncedSettingsInternal
|
||||||
|
import com.vitorpamplona.amethyst.model.AccountZapPreferencesInternal
|
||||||
import com.vitorpamplona.amethyst.model.DefaultReactions
|
import com.vitorpamplona.amethyst.model.DefaultReactions
|
||||||
import com.vitorpamplona.amethyst.model.DefaultZapAmounts
|
import com.vitorpamplona.amethyst.model.DefaultZapAmounts
|
||||||
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||||
@@ -45,6 +50,7 @@ import com.vitorpamplona.quartz.encoders.hexToByteArray
|
|||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.encoders.toNpub
|
import com.vitorpamplona.quartz.encoders.toNpub
|
||||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppSpecificDataEvent
|
||||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
@@ -53,11 +59,9 @@ import com.vitorpamplona.quartz.events.MetadataEvent
|
|||||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||||
import com.vitorpamplona.quartz.events.PrivateOutboxRelayListEvent
|
import com.vitorpamplona.quartz.events.PrivateOutboxRelayListEvent
|
||||||
import com.vitorpamplona.quartz.events.SearchRelayListEvent
|
import com.vitorpamplona.quartz.events.SearchRelayListEvent
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -104,6 +108,7 @@ private object PrefKeys {
|
|||||||
const val LATEST_SEARCH_RELAY_LIST = "latestSearchRelayList"
|
const val LATEST_SEARCH_RELAY_LIST = "latestSearchRelayList"
|
||||||
const val LATEST_MUTE_LIST = "latestMuteList"
|
const val LATEST_MUTE_LIST = "latestMuteList"
|
||||||
const val LATEST_PRIVATE_HOME_RELAY_LIST = "latestPrivateHomeRelayList"
|
const val LATEST_PRIVATE_HOME_RELAY_LIST = "latestPrivateHomeRelayList"
|
||||||
|
const val LATEST_APP_SPECIFIC_DATA = "latestAppSpecificData"
|
||||||
const val HIDE_DELETE_REQUEST_DIALOG = "hide_delete_request_dialog"
|
const val HIDE_DELETE_REQUEST_DIALOG = "hide_delete_request_dialog"
|
||||||
const val HIDE_BLOCK_ALERT_DIALOG = "hide_block_alert_dialog"
|
const val HIDE_BLOCK_ALERT_DIALOG = "hide_block_alert_dialog"
|
||||||
const val HIDE_NIP_17_WARNING_DIALOG = "hide_nip24_warning_dialog" // delete later
|
const val HIDE_NIP_17_WARNING_DIALOG = "hide_nip24_warning_dialog" // delete later
|
||||||
@@ -310,19 +315,7 @@ object LocalPreferences {
|
|||||||
}
|
}
|
||||||
settings.keyPair.pubKey.let { putString(PrefKeys.NOSTR_PUBKEY, it.toHexKey()) }
|
settings.keyPair.pubKey.let { putString(PrefKeys.NOSTR_PUBKEY, it.toHexKey()) }
|
||||||
putString(PrefKeys.RELAYS, Event.mapper.writeValueAsString(settings.localRelays))
|
putString(PrefKeys.RELAYS, Event.mapper.writeValueAsString(settings.localRelays))
|
||||||
putStringSet(PrefKeys.DONT_TRANSLATE_FROM, settings.dontTranslateFrom)
|
|
||||||
putStringSet(PrefKeys.LOCAL_RELAY_SERVERS, settings.localRelayServers)
|
|
||||||
putString(
|
|
||||||
PrefKeys.LANGUAGE_PREFS,
|
|
||||||
Event.mapper.writeValueAsString(settings.languagePreferences),
|
|
||||||
)
|
|
||||||
putString(PrefKeys.TRANSLATE_TO, settings.translateTo)
|
|
||||||
putString(PrefKeys.ZAP_AMOUNTS, Event.mapper.writeValueAsString(settings.zapAmountChoices.value))
|
|
||||||
putString(
|
|
||||||
PrefKeys.REACTION_CHOICES,
|
|
||||||
Event.mapper.writeValueAsString(settings.reactionChoices.value),
|
|
||||||
)
|
|
||||||
putString(PrefKeys.DEFAULT_ZAPTYPE, settings.defaultZapType.value.name)
|
|
||||||
putString(
|
putString(
|
||||||
PrefKeys.DEFAULT_FILE_SERVER,
|
PrefKeys.DEFAULT_FILE_SERVER,
|
||||||
Event.mapper.writeValueAsString(settings.defaultFileServer),
|
Event.mapper.writeValueAsString(settings.defaultFileServer),
|
||||||
@@ -404,6 +397,15 @@ object LocalPreferences {
|
|||||||
remove(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST)
|
remove(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.backupAppSpecificData != null) {
|
||||||
|
putString(
|
||||||
|
PrefKeys.LATEST_APP_SPECIFIC_DATA,
|
||||||
|
Event.mapper.writeValueAsString(settings.backupAppSpecificData),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
remove(PrefKeys.LATEST_APP_SPECIFIC_DATA)
|
||||||
|
}
|
||||||
|
|
||||||
putBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, settings.hideDeleteRequestDialog)
|
putBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, settings.hideDeleteRequestDialog)
|
||||||
putBoolean(PrefKeys.HIDE_NIP_17_WARNING_DIALOG, settings.hideNIP17WarningDialog)
|
putBoolean(PrefKeys.HIDE_NIP_17_WARNING_DIALOG, settings.hideNIP17WarningDialog)
|
||||||
putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, settings.hideBlockAlertDialog)
|
putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, settings.hideBlockAlertDialog)
|
||||||
@@ -414,9 +416,6 @@ object LocalPreferences {
|
|||||||
|
|
||||||
putString(PrefKeys.TOR_SETTINGS, Event.mapper.writeValueAsString(settings.torSettings.toSettings()))
|
putString(PrefKeys.TOR_SETTINGS, Event.mapper.writeValueAsString(settings.torSettings.toSettings()))
|
||||||
|
|
||||||
putBoolean(PrefKeys.WARN_ABOUT_REPORTS, settings.warnAboutPostsWithReports)
|
|
||||||
putBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, settings.filterSpamFromStrangers)
|
|
||||||
|
|
||||||
val regularMap =
|
val regularMap =
|
||||||
settings.lastReadPerRoute.value.mapValues {
|
settings.lastReadPerRoute.value.mapValues {
|
||||||
it.value.value
|
it.value.value
|
||||||
@@ -428,12 +427,6 @@ object LocalPreferences {
|
|||||||
)
|
)
|
||||||
putStringSet(PrefKeys.HAS_DONATED_IN_VERSION, settings.hasDonatedInVersion.value)
|
putStringSet(PrefKeys.HAS_DONATED_IN_VERSION, settings.hasDonatedInVersion.value)
|
||||||
|
|
||||||
if (settings.showSensitiveContent.value == null) {
|
|
||||||
remove(PrefKeys.SHOW_SENSITIVE_CONTENT)
|
|
||||||
} else {
|
|
||||||
putBoolean(PrefKeys.SHOW_SENSITIVE_CONTENT, settings.showSensitiveContent.value!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
putString(
|
putString(
|
||||||
PrefKeys.PENDING_ATTESTATIONS,
|
PrefKeys.PENDING_ATTESTATIONS,
|
||||||
Event.mapper.writeValueAsString(settings.pendingAttestations.value),
|
Event.mapper.writeValueAsString(settings.pendingAttestations.value),
|
||||||
@@ -510,9 +503,6 @@ object LocalPreferences {
|
|||||||
getString(PrefKeys.SIGNER_PACKAGE_NAME, null)
|
getString(PrefKeys.SIGNER_PACKAGE_NAME, null)
|
||||||
?: if (getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false)) "com.greenart7c3.nostrsigner" else null
|
?: if (getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false)) "com.greenart7c3.nostrsigner" else null
|
||||||
|
|
||||||
val dontTranslateFrom = getStringSet(PrefKeys.DONT_TRANSLATE_FROM, null) ?: setOf()
|
|
||||||
val localRelayServers = getStringSet(PrefKeys.LOCAL_RELAY_SERVERS, null) ?: setOf()
|
|
||||||
val translateTo = getString(PrefKeys.TRANSLATE_TO, null) ?: Locale.getDefault().language
|
|
||||||
val defaultHomeFollowList =
|
val defaultHomeFollowList =
|
||||||
getString(PrefKeys.DEFAULT_HOME_FOLLOW_LIST, null) ?: KIND3_FOLLOWS
|
getString(PrefKeys.DEFAULT_HOME_FOLLOW_LIST, null) ?: KIND3_FOLLOWS
|
||||||
val defaultStoriesFollowList =
|
val defaultStoriesFollowList =
|
||||||
@@ -528,13 +518,12 @@ object LocalPreferences {
|
|||||||
} ?: LnZapEvent.ZapType.PUBLIC
|
} ?: LnZapEvent.ZapType.PUBLIC
|
||||||
|
|
||||||
val localRelays = parseOrNull<Set<RelaySetupInfo>>(PrefKeys.RELAYS) ?: emptySet()
|
val localRelays = parseOrNull<Set<RelaySetupInfo>>(PrefKeys.RELAYS) ?: emptySet()
|
||||||
val reactionChoices = parseOrNull<List<String>>(PrefKeys.REACTION_CHOICES)?.ifEmpty { DefaultReactions } ?: DefaultReactions
|
|
||||||
val zapAmountChoices = parseOrNull<List<Long>>(PrefKeys.ZAP_AMOUNTS)?.ifEmpty { DefaultZapAmounts } ?: DefaultZapAmounts
|
|
||||||
|
|
||||||
val defaultFileServer = parseOrNull<Nip96MediaServers.ServerName>(PrefKeys.DEFAULT_FILE_SERVER) ?: Nip96MediaServers.DEFAULT[0]
|
|
||||||
val zapPaymentRequestServer = parseOrNull<Nip47WalletConnect.Nip47URI>(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER)
|
val zapPaymentRequestServer = parseOrNull<Nip47WalletConnect.Nip47URI>(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER)
|
||||||
|
val defaultFileServer = parseOrNull<Nip96MediaServers.ServerName>(PrefKeys.DEFAULT_FILE_SERVER) ?: Nip96MediaServers.DEFAULT[0]
|
||||||
|
|
||||||
val pendingAttestations = parseOrNull<Map<HexKey, String>>(PrefKeys.PENDING_ATTESTATIONS) ?: mapOf()
|
val pendingAttestations = parseOrNull<Map<HexKey, String>>(PrefKeys.PENDING_ATTESTATIONS) ?: mapOf()
|
||||||
val languagePreferences = parseOrNull<Map<String, String>>(PrefKeys.LANGUAGE_PREFS) ?: mapOf()
|
val localRelayServers = getStringSet(PrefKeys.LOCAL_RELAY_SERVERS, null) ?: setOf()
|
||||||
|
|
||||||
val latestUserMetadata = parseEventOrNull<MetadataEvent>(PrefKeys.LATEST_USER_METADATA)
|
val latestUserMetadata = parseEventOrNull<MetadataEvent>(PrefKeys.LATEST_USER_METADATA)
|
||||||
val latestContactList = parseEventOrNull<ContactListEvent>(PrefKeys.LATEST_CONTACT_LIST)
|
val latestContactList = parseEventOrNull<ContactListEvent>(PrefKeys.LATEST_CONTACT_LIST)
|
||||||
@@ -543,6 +532,54 @@ object LocalPreferences {
|
|||||||
val latestSearchRelayList = parseEventOrNull<SearchRelayListEvent>(PrefKeys.LATEST_SEARCH_RELAY_LIST)
|
val latestSearchRelayList = parseEventOrNull<SearchRelayListEvent>(PrefKeys.LATEST_SEARCH_RELAY_LIST)
|
||||||
val latestMuteList = parseEventOrNull<MuteListEvent>(PrefKeys.LATEST_MUTE_LIST)
|
val latestMuteList = parseEventOrNull<MuteListEvent>(PrefKeys.LATEST_MUTE_LIST)
|
||||||
val latestPrivateHomeRelayList = parseEventOrNull<PrivateOutboxRelayListEvent>(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST)
|
val latestPrivateHomeRelayList = parseEventOrNull<PrivateOutboxRelayListEvent>(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST)
|
||||||
|
val latestAppSpecificData = parseEventOrNull<AppSpecificDataEvent>(PrefKeys.LATEST_APP_SPECIFIC_DATA)
|
||||||
|
|
||||||
|
val syncedSettings =
|
||||||
|
if (latestAppSpecificData != null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
// previous version. Delete this when ready.
|
||||||
|
val reactionChoices = parseOrNull<List<String>>(PrefKeys.REACTION_CHOICES)?.ifEmpty { DefaultReactions } ?: DefaultReactions
|
||||||
|
val zapAmountChoices = parseOrNull<List<Long>>(PrefKeys.ZAP_AMOUNTS)?.ifEmpty { DefaultZapAmounts } ?: DefaultZapAmounts
|
||||||
|
|
||||||
|
val languagePreferences = parseOrNull<Map<String, String>>(PrefKeys.LANGUAGE_PREFS) ?: mapOf()
|
||||||
|
|
||||||
|
val showSensitiveContent =
|
||||||
|
if (contains(PrefKeys.SHOW_SENSITIVE_CONTENT)) {
|
||||||
|
getBoolean(PrefKeys.SHOW_SENSITIVE_CONTENT, false)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
val filterSpam = getBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, true)
|
||||||
|
val warnAboutReports = getBoolean(PrefKeys.WARN_ABOUT_REPORTS, true)
|
||||||
|
|
||||||
|
val dontTranslateFrom = getStringSet(PrefKeys.DONT_TRANSLATE_FROM, null) ?: setOf()
|
||||||
|
val translateTo = getString(PrefKeys.TRANSLATE_TO, null) ?: Locale.getDefault().language
|
||||||
|
|
||||||
|
AccountSyncedSettingsInternal(
|
||||||
|
reactions =
|
||||||
|
AccountReactionPreferencesInternal(
|
||||||
|
reactionChoices = reactionChoices,
|
||||||
|
),
|
||||||
|
zaps =
|
||||||
|
AccountZapPreferencesInternal(
|
||||||
|
zapAmountChoices = zapAmountChoices,
|
||||||
|
defaultZapType = defaultZapType,
|
||||||
|
),
|
||||||
|
languages =
|
||||||
|
AccountLanguagePreferencesInternal(
|
||||||
|
dontTranslateFrom = dontTranslateFrom,
|
||||||
|
languagePreferences = languagePreferences,
|
||||||
|
translateTo = translateTo,
|
||||||
|
),
|
||||||
|
security =
|
||||||
|
AccountSecurityPreferencesInternal(
|
||||||
|
showSensitiveContent = showSensitiveContent,
|
||||||
|
warnAboutPostsWithReports = warnAboutReports,
|
||||||
|
filterSpamFromStrangers = filterSpam,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val hideDeleteRequestDialog = getBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, false)
|
val hideDeleteRequestDialog = getBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, false)
|
||||||
val hideBlockAlertDialog = getBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, false)
|
val hideBlockAlertDialog = getBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, false)
|
||||||
@@ -571,15 +608,6 @@ object LocalPreferences {
|
|||||||
parseOrNull<TorSettings>(PrefKeys.TOR_SETTINGS) ?: TorSettings()
|
parseOrNull<TorSettings>(PrefKeys.TOR_SETTINGS) ?: TorSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
val showSensitiveContent =
|
|
||||||
if (contains(PrefKeys.SHOW_SENSITIVE_CONTENT)) {
|
|
||||||
getBoolean(PrefKeys.SHOW_SENSITIVE_CONTENT, false)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
val filterSpam = getBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, true)
|
|
||||||
val warnAboutReports = getBoolean(PrefKeys.WARN_ABOUT_REPORTS, true)
|
|
||||||
|
|
||||||
val lastReadPerRoute =
|
val lastReadPerRoute =
|
||||||
parseOrNull<Map<String, Long>>(PrefKeys.LAST_READ_PER_ROUTE)?.mapValues {
|
parseOrNull<Map<String, Long>>(PrefKeys.LAST_READ_PER_ROUTE)?.mapValues {
|
||||||
MutableStateFlow(it.value)
|
MutableStateFlow(it.value)
|
||||||
@@ -594,12 +622,6 @@ object LocalPreferences {
|
|||||||
externalSignerPackageName = externalSignerPackageName,
|
externalSignerPackageName = externalSignerPackageName,
|
||||||
localRelays = localRelays,
|
localRelays = localRelays,
|
||||||
localRelayServers = localRelayServers,
|
localRelayServers = localRelayServers,
|
||||||
dontTranslateFrom = dontTranslateFrom,
|
|
||||||
languagePreferences = languagePreferences,
|
|
||||||
translateTo = translateTo,
|
|
||||||
zapAmountChoices = MutableStateFlow(zapAmountChoices.toImmutableList()),
|
|
||||||
reactionChoices = MutableStateFlow(reactionChoices.toImmutableList()),
|
|
||||||
defaultZapType = MutableStateFlow(defaultZapType),
|
|
||||||
defaultFileServer = defaultFileServer,
|
defaultFileServer = defaultFileServer,
|
||||||
defaultHomeFollowList = MutableStateFlow(defaultHomeFollowList),
|
defaultHomeFollowList = MutableStateFlow(defaultHomeFollowList),
|
||||||
defaultStoriesFollowList = MutableStateFlow(defaultStoriesFollowList),
|
defaultStoriesFollowList = MutableStateFlow(defaultStoriesFollowList),
|
||||||
@@ -616,10 +638,9 @@ object LocalPreferences {
|
|||||||
backupSearchRelayList = latestSearchRelayList,
|
backupSearchRelayList = latestSearchRelayList,
|
||||||
backupPrivateHomeRelayList = latestPrivateHomeRelayList,
|
backupPrivateHomeRelayList = latestPrivateHomeRelayList,
|
||||||
backupMuteList = latestMuteList,
|
backupMuteList = latestMuteList,
|
||||||
|
backupAppSpecificData = latestAppSpecificData,
|
||||||
|
backupSyncedSettings = syncedSettings,
|
||||||
torSettings = TorSettingsFlow.build(torSettings),
|
torSettings = TorSettingsFlow.build(torSettings),
|
||||||
showSensitiveContent = MutableStateFlow(showSensitiveContent),
|
|
||||||
warnAboutPostsWithReports = warnAboutReports,
|
|
||||||
filterSpamFromStrangers = filterSpam,
|
|
||||||
lastReadPerRoute = MutableStateFlow(lastReadPerRoute),
|
lastReadPerRoute = MutableStateFlow(lastReadPerRoute),
|
||||||
hasDonatedInVersion = MutableStateFlow(hasDonatedInVersion),
|
hasDonatedInVersion = MutableStateFlow(hasDonatedInVersion),
|
||||||
pendingAttestations = MutableStateFlow(pendingAttestations),
|
pendingAttestations = MutableStateFlow(pendingAttestations),
|
||||||
|
@@ -115,7 +115,12 @@ class ServiceManager(
|
|||||||
HttpClientManager.setDefaultProxy(null)
|
HttpClientManager.setDefaultProxy(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalCache.antiSpam.active = account?.settings?.filterSpamFromStrangers ?: true
|
// Convert this into a flow
|
||||||
|
LocalCache.antiSpam.active = account
|
||||||
|
?.settings
|
||||||
|
?.syncedSettings
|
||||||
|
?.security
|
||||||
|
?.filterSpamFromStrangers ?: true
|
||||||
Coil.setImageLoader {
|
Coil.setImageLoader {
|
||||||
Amethyst.instance
|
Amethyst.instance
|
||||||
.imageLoaderBuilder()
|
.imageLoaderBuilder()
|
||||||
|
@@ -27,6 +27,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.liveData
|
import androidx.lifecycle.liveData
|
||||||
import androidx.lifecycle.switchMap
|
import androidx.lifecycle.switchMap
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.amethyst.service.FileHeader
|
import com.vitorpamplona.amethyst.service.FileHeader
|
||||||
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
||||||
@@ -44,9 +45,11 @@ import com.vitorpamplona.ammolite.service.HttpClientManager
|
|||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.ATag
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
|
import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
||||||
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
||||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
||||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppSpecificDataEvent
|
||||||
import com.vitorpamplona.quartz.events.BookmarkListEvent
|
import com.vitorpamplona.quartz.events.BookmarkListEvent
|
||||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||||
@@ -128,7 +131,9 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
import java.util.Locale
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@@ -138,6 +143,10 @@ class Account(
|
|||||||
val signer: NostrSigner = settings.createSigner(),
|
val signer: NostrSigner = settings.createSigner(),
|
||||||
val scope: CoroutineScope,
|
val scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
|
companion object {
|
||||||
|
const val APP_SPECIFIC_DATA_D_TAG = "AmethystSettings"
|
||||||
|
}
|
||||||
|
|
||||||
var transientHiddenUsers: MutableStateFlow<Set<String>> = MutableStateFlow(setOf())
|
var transientHiddenUsers: MutableStateFlow<Set<String>> = MutableStateFlow(setOf())
|
||||||
|
|
||||||
data class PaymentRequest(
|
data class PaymentRequest(
|
||||||
@@ -959,7 +968,7 @@ class Account(
|
|||||||
getBlockListNote().flow().metadata.stateFlow,
|
getBlockListNote().flow().metadata.stateFlow,
|
||||||
getMuteListNote().flow().metadata.stateFlow,
|
getMuteListNote().flow().metadata.stateFlow,
|
||||||
transientHiddenUsers,
|
transientHiddenUsers,
|
||||||
settings.showSensitiveContent,
|
settings.syncedSettings.security.showSensitiveContent,
|
||||||
) { blockList, muteList, transientHiddenUsers, showSensitiveContent ->
|
) { blockList, muteList, transientHiddenUsers, showSensitiveContent ->
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
emit(assembleLiveHiddenUsers(blockList.note, muteList.note, transientHiddenUsers, showSensitiveContent))
|
emit(assembleLiveHiddenUsers(blockList.note, muteList.note, transientHiddenUsers, showSensitiveContent))
|
||||||
@@ -972,7 +981,7 @@ class Account(
|
|||||||
getBlockListNote(),
|
getBlockListNote(),
|
||||||
getMuteListNote(),
|
getMuteListNote(),
|
||||||
transientHiddenUsers.value,
|
transientHiddenUsers.value,
|
||||||
settings.showSensitiveContent.value,
|
settings.syncedSettings.security.showSensitiveContent.value,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -1025,14 +1034,83 @@ class Account(
|
|||||||
fun updateOptOutOptions(
|
fun updateOptOutOptions(
|
||||||
warnReports: Boolean,
|
warnReports: Boolean,
|
||||||
filterSpam: Boolean,
|
filterSpam: Boolean,
|
||||||
) {
|
): Boolean {
|
||||||
if (settings.updateOptOutOptions(warnReports, filterSpam)) {
|
if (settings.updateOptOutOptions(warnReports, filterSpam)) {
|
||||||
LocalCache.antiSpam.active = settings.filterSpamFromStrangers
|
if (!settings.syncedSettings.security.filterSpamFromStrangers) {
|
||||||
if (!settings.filterSpamFromStrangers) {
|
|
||||||
transientHiddenUsers.update {
|
transientHiddenUsers.update {
|
||||||
emptySet()
|
emptySet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateShowSensitiveContent(show: Boolean?) {
|
||||||
|
if (settings.updateShowSensitiveContent(show)) {
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeReactionTypes(reactionSet: List<String>) {
|
||||||
|
if (settings.changeReactionTypes(reactionSet)) {
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateZapAmounts(
|
||||||
|
amountSet: List<Long>,
|
||||||
|
selectedZapType: LnZapEvent.ZapType,
|
||||||
|
nip47Update: Nip47WalletConnect.Nip47URI?,
|
||||||
|
) {
|
||||||
|
var changed = false
|
||||||
|
|
||||||
|
if (settings.changeZapAmounts(amountSet)) changed = true
|
||||||
|
if (settings.changeDefaultZapType(selectedZapType)) changed = true
|
||||||
|
if (settings.changeZapPaymentRequest(nip47Update)) changed = true
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleDontTranslateFrom(languageCode: String) {
|
||||||
|
settings.toggleDontTranslateFrom(languageCode)
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTranslateTo(languageCode: Locale) {
|
||||||
|
if (settings.updateTranslateTo(languageCode)) {
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun prefer(
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
preference: String,
|
||||||
|
) {
|
||||||
|
settings.prefer(source, target, preference)
|
||||||
|
sendNewAppSpecificData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendNewAppSpecificData() {
|
||||||
|
sendNewAppSpecificData(settings.syncedSettings.toInternal())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendNewAppSpecificData(toInternal: AccountSyncedSettingsInternal) {
|
||||||
|
signer.nip44Encrypt(Event.mapper.writeValueAsString(toInternal), signer.pubKey) { encrypted ->
|
||||||
|
AppSpecificDataEvent.create(
|
||||||
|
dTag = APP_SPECIFIC_DATA_D_TAG,
|
||||||
|
description = encrypted,
|
||||||
|
otherTags = emptyArray(),
|
||||||
|
signer = signer,
|
||||||
|
) {
|
||||||
|
Client.send(it)
|
||||||
|
LocalCache.justConsume(it, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2788,6 +2866,13 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAppSpecificDataNote(): AddressableNote {
|
||||||
|
val aTag = AppSpecificDataEvent.createTag(userProfile().pubkeyHex, APP_SPECIFIC_DATA_D_TAG)
|
||||||
|
return LocalCache.getOrCreateAddressableNote(aTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAppSpecificDataFlow(): StateFlow<NoteState> = getAppSpecificDataNote().flow().metadata.stateFlow
|
||||||
|
|
||||||
fun getBlockListNote(): AddressableNote {
|
fun getBlockListNote(): AddressableNote {
|
||||||
val aTag =
|
val aTag =
|
||||||
ATag(
|
ATag(
|
||||||
@@ -3118,7 +3203,7 @@ class Account(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.warnAboutPostsWithReports) {
|
if (!settings.syncedSettings.security.warnAboutPostsWithReports) {
|
||||||
return !isHidden(user) &&
|
return !isHidden(user) &&
|
||||||
// if user hasn't hided this author
|
// if user hasn't hided this author
|
||||||
user.reportsBy(userProfile()).isEmpty() // if user has not reported this post
|
user.reportsBy(userProfile()).isEmpty() // if user has not reported this post
|
||||||
@@ -3131,7 +3216,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isAcceptableDirect(note: Note): Boolean {
|
private fun isAcceptableDirect(note: Note): Boolean {
|
||||||
if (!settings.warnAboutPostsWithReports) {
|
if (!settings.syncedSettings.security.warnAboutPostsWithReports) {
|
||||||
return !note.hasReportsBy(userProfile())
|
return !note.hasReportsBy(userProfile())
|
||||||
}
|
}
|
||||||
return !note.hasReportsBy(userProfile()) &&
|
return !note.hasReportsBy(userProfile()) &&
|
||||||
@@ -3355,8 +3440,6 @@ class Account(
|
|||||||
(event.hasAnyTaggedUser() || event.publicAndPrivateUserCache?.isNotEmpty() == true)
|
(event.hasAnyTaggedUser() || event.publicAndPrivateUserCache?.isNotEmpty() == true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShowSensitiveContent(show: Boolean?) = settings.updateShowSensitiveContent(show)
|
|
||||||
|
|
||||||
fun markAsRead(
|
fun markAsRead(
|
||||||
route: String,
|
route: String,
|
||||||
timestampInSecs: Long,
|
timestampInSecs: Long,
|
||||||
@@ -3516,7 +3599,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
settings.backupPrivateHomeRelayList?.let { event ->
|
settings.backupPrivateHomeRelayList?.let { event ->
|
||||||
Log.d("AccountRegisterObservers", "Loading saved search relay list ${event.toJson()}")
|
Log.d("AccountRegisterObservers", "Loading saved private home relay list ${event.toJson()}")
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
event.privateTags(signer) {
|
event.privateTags(signer) {
|
||||||
LocalCache.verifyAndConsume(event, null)
|
LocalCache.verifyAndConsume(event, null)
|
||||||
@@ -3524,6 +3607,24 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings.backupAppSpecificData?.let { event ->
|
||||||
|
Log.d("AccountRegisterObservers", "Loading saved app specific data ${event.toJson()}")
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
LocalCache.verifyAndConsume(event, null)
|
||||||
|
signer.decrypt(event.content, event.pubKey) { decrypted ->
|
||||||
|
try {
|
||||||
|
val syncedSettings = Event.mapper.readValue<AccountSyncedSettingsInternal>(decrypted)
|
||||||
|
settings.syncedSettings.updateFrom(syncedSettings)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
Log.w("LocalPreferences", "Error Decoding latestAppSpecificData from Preferences with value $decrypted", e)
|
||||||
|
e.printStackTrace()
|
||||||
|
AccountSyncedSettingsInternal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
settings.backupMuteList?.let {
|
settings.backupMuteList?.let {
|
||||||
Log.d("AccountRegisterObservers", "Loading saved mute list ${it.toJson()}")
|
Log.d("AccountRegisterObservers", "Loading saved mute list ${it.toJson()}")
|
||||||
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) }
|
GlobalScope.launch(Dispatchers.IO) { LocalCache.verifyAndConsume(it, null) }
|
||||||
@@ -3597,6 +3698,28 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.launch(Dispatchers.Default) {
|
||||||
|
Log.d("AccountRegisterObservers", "AppSpecificData Collector Start")
|
||||||
|
getAppSpecificDataFlow().collect {
|
||||||
|
Log.d("AccountRegisterObservers", "Updating AppSpecificData for ${userProfile().toBestDisplayName()}")
|
||||||
|
(it.note.event as? AppSpecificDataEvent)?.let {
|
||||||
|
signer.decrypt(it.content, it.pubKey) { decrypted ->
|
||||||
|
val syncedSettings =
|
||||||
|
try {
|
||||||
|
Event.mapper.readValue<AccountSyncedSettingsInternal>(decrypted)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
Log.w("LocalPreferences", "Error Decoding latestAppSpecificData from Preferences with value $decrypted", e)
|
||||||
|
e.printStackTrace()
|
||||||
|
AccountSyncedSettingsInternal()
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.updateAppSpecificData(it, syncedSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scope.launch(Dispatchers.Default) {
|
scope.launch(Dispatchers.Default) {
|
||||||
LocalCache.antiSpam.flowSpam.collect {
|
LocalCache.antiSpam.flowSpam.collect {
|
||||||
it.cache.spamMessages.snapshot().values.forEach { spammer ->
|
it.cache.spamMessages.snapshot().values.forEach { spammer ->
|
||||||
|
@@ -20,9 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.model
|
package com.vitorpamplona.amethyst.model
|
||||||
|
|
||||||
import android.content.res.Resources
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.core.os.ConfigurationCompat
|
|
||||||
import com.vitorpamplona.amethyst.service.Nip96MediaServers
|
import com.vitorpamplona.amethyst.service.Nip96MediaServers
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
@@ -34,6 +32,7 @@ import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
|||||||
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppSpecificDataEvent
|
||||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||||
import com.vitorpamplona.quartz.events.LnZapEvent
|
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||||
@@ -44,8 +43,6 @@ import com.vitorpamplona.quartz.events.SearchRelayListEvent
|
|||||||
import com.vitorpamplona.quartz.signers.ExternalSignerLauncher
|
import com.vitorpamplona.quartz.signers.ExternalSignerLauncher
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerExternal
|
import com.vitorpamplona.quartz.signers.NostrSignerExternal
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -61,17 +58,6 @@ val DefaultChannels =
|
|||||||
"42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5",
|
"42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5",
|
||||||
)
|
)
|
||||||
|
|
||||||
val DefaultReactions =
|
|
||||||
persistentListOf(
|
|
||||||
"\uD83D\uDE80",
|
|
||||||
"\uD83E\uDEC2",
|
|
||||||
"\uD83D\uDC40",
|
|
||||||
"\uD83D\uDE02",
|
|
||||||
"\uD83C\uDF89",
|
|
||||||
"\uD83E\uDD14",
|
|
||||||
"\uD83D\uDE31",
|
|
||||||
)
|
|
||||||
|
|
||||||
val DefaultNIP65List =
|
val DefaultNIP65List =
|
||||||
listOf(
|
listOf(
|
||||||
AdvertisedRelayListEvent.AdvertisedRelayInfo(RelayUrlFormatter.normalize("wss://nostr.mom/"), AdvertisedRelayListEvent.AdvertisedRelayType.BOTH),
|
AdvertisedRelayListEvent.AdvertisedRelayInfo(RelayUrlFormatter.normalize("wss://nostr.mom/"), AdvertisedRelayListEvent.AdvertisedRelayType.BOTH),
|
||||||
@@ -93,17 +79,6 @@ val DefaultSearchRelayList =
|
|||||||
RelayUrlFormatter.normalize("wss://relay.noswhere.com"),
|
RelayUrlFormatter.normalize("wss://relay.noswhere.com"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val DefaultZapAmounts = persistentListOf(100L, 500L, 1000L)
|
|
||||||
|
|
||||||
fun getLanguagesSpokenByUser(): Set<String> {
|
|
||||||
val languageList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration())
|
|
||||||
val codedList = mutableSetOf<String>()
|
|
||||||
for (i in 0 until languageList.size()) {
|
|
||||||
languageList.get(i)?.let { codedList.add(it.language) }
|
|
||||||
}
|
|
||||||
return codedList
|
|
||||||
}
|
|
||||||
|
|
||||||
// This has spaces to avoid mixing with a potential NIP-51 list with the same name.
|
// This has spaces to avoid mixing with a potential NIP-51 list with the same name.
|
||||||
val GLOBAL_FOLLOWS = " Global "
|
val GLOBAL_FOLLOWS = " Global "
|
||||||
|
|
||||||
@@ -117,12 +92,6 @@ class AccountSettings(
|
|||||||
var externalSignerPackageName: String? = null,
|
var externalSignerPackageName: String? = null,
|
||||||
var localRelays: Set<RelaySetupInfo> = Constants.defaultRelays.toSet(),
|
var localRelays: Set<RelaySetupInfo> = Constants.defaultRelays.toSet(),
|
||||||
var localRelayServers: Set<String> = setOf(),
|
var localRelayServers: Set<String> = setOf(),
|
||||||
var dontTranslateFrom: Set<String> = getLanguagesSpokenByUser(),
|
|
||||||
var languagePreferences: Map<String, String> = mapOf(),
|
|
||||||
var translateTo: String = Locale.getDefault().language,
|
|
||||||
var zapAmountChoices: MutableStateFlow<ImmutableList<Long>> = MutableStateFlow(DefaultZapAmounts),
|
|
||||||
var reactionChoices: MutableStateFlow<ImmutableList<String>> = MutableStateFlow(DefaultReactions),
|
|
||||||
val defaultZapType: MutableStateFlow<LnZapEvent.ZapType> = MutableStateFlow(LnZapEvent.ZapType.PUBLIC),
|
|
||||||
var defaultFileServer: Nip96MediaServers.ServerName = Nip96MediaServers.DEFAULT[0],
|
var defaultFileServer: Nip96MediaServers.ServerName = Nip96MediaServers.DEFAULT[0],
|
||||||
val defaultHomeFollowList: MutableStateFlow<String> = MutableStateFlow(KIND3_FOLLOWS),
|
val defaultHomeFollowList: MutableStateFlow<String> = MutableStateFlow(KIND3_FOLLOWS),
|
||||||
val defaultStoriesFollowList: MutableStateFlow<String> = MutableStateFlow(GLOBAL_FOLLOWS),
|
val defaultStoriesFollowList: MutableStateFlow<String> = MutableStateFlow(GLOBAL_FOLLOWS),
|
||||||
@@ -139,16 +108,19 @@ class AccountSettings(
|
|||||||
var backupSearchRelayList: SearchRelayListEvent? = null,
|
var backupSearchRelayList: SearchRelayListEvent? = null,
|
||||||
var backupMuteList: MuteListEvent? = null,
|
var backupMuteList: MuteListEvent? = null,
|
||||||
var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null,
|
var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null,
|
||||||
|
var backupAppSpecificData: AppSpecificDataEvent? = null,
|
||||||
|
backupSyncedSettings: AccountSyncedSettingsInternal? = null, // only exist for migration purposes
|
||||||
val torSettings: TorSettingsFlow = TorSettingsFlow(),
|
val torSettings: TorSettingsFlow = TorSettingsFlow(),
|
||||||
val showSensitiveContent: MutableStateFlow<Boolean?> = MutableStateFlow(null),
|
|
||||||
var warnAboutPostsWithReports: Boolean = true,
|
|
||||||
var filterSpamFromStrangers: Boolean = true,
|
|
||||||
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
||||||
var hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf<String>()),
|
var hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf<String>()),
|
||||||
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow<Map<HexKey, String>>(mapOf()),
|
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow<Map<HexKey, String>>(mapOf()),
|
||||||
) {
|
) {
|
||||||
val saveable = MutableStateFlow(AccountSettingsUpdater(this))
|
val saveable = MutableStateFlow(AccountSettingsUpdater(this))
|
||||||
|
|
||||||
|
val syncedSettings: AccountSyncedSettings =
|
||||||
|
backupSyncedSettings?.let { AccountSyncedSettings(it) }
|
||||||
|
?: AccountSyncedSettings(AccountSyncedSettingsInternal())
|
||||||
|
|
||||||
class AccountSettingsUpdater(
|
class AccountSettingsUpdater(
|
||||||
val accountSettings: AccountSettings,
|
val accountSettings: AccountSettings,
|
||||||
)
|
)
|
||||||
@@ -173,32 +145,40 @@ class AccountSettings(
|
|||||||
// Zaps and Reactions
|
// Zaps and Reactions
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
fun changeDefaultZapType(zapType: LnZapEvent.ZapType) {
|
fun changeDefaultZapType(zapType: LnZapEvent.ZapType): Boolean {
|
||||||
if (defaultZapType.value != zapType) {
|
if (syncedSettings.zaps.defaultZapType.value != zapType) {
|
||||||
defaultZapType.tryEmit(zapType)
|
syncedSettings.zaps.defaultZapType.tryEmit(zapType)
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeZapAmounts(newAmounts: List<Long>) {
|
fun changeZapAmounts(newAmounts: List<Long>): Boolean {
|
||||||
if (zapAmountChoices.value != newAmounts) {
|
if (syncedSettings.zaps.zapAmountChoices.value != newAmounts) {
|
||||||
zapAmountChoices.tryEmit(newAmounts.toImmutableList())
|
syncedSettings.zaps.zapAmountChoices.tryEmit(newAmounts.toImmutableList())
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeZapPaymentRequest(newServer: Nip47WalletConnect.Nip47URI?) {
|
fun changeReactionTypes(newTypes: List<String>): Boolean {
|
||||||
|
if (syncedSettings.reactions.reactionChoices.value != newTypes) {
|
||||||
|
syncedSettings.reactions.reactionChoices.tryEmit(newTypes.toImmutableList())
|
||||||
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeZapPaymentRequest(newServer: Nip47WalletConnect.Nip47URI?): Boolean {
|
||||||
if (zapPaymentRequest != newServer) {
|
if (zapPaymentRequest != newServer) {
|
||||||
zapPaymentRequest = newServer
|
zapPaymentRequest = newServer
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
return false
|
||||||
|
|
||||||
fun changeReactionTypes(newTypes: List<String>) {
|
|
||||||
if (reactionChoices.value != newTypes) {
|
|
||||||
reactionChoices.tryEmit(newTypes.toImmutableList())
|
|
||||||
saveAccountSettings()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
@@ -260,22 +240,18 @@ class AccountSettings(
|
|||||||
// language services
|
// language services
|
||||||
// ---
|
// ---
|
||||||
fun toggleDontTranslateFrom(languageCode: String) {
|
fun toggleDontTranslateFrom(languageCode: String) {
|
||||||
if (!dontTranslateFrom.contains(languageCode)) {
|
syncedSettings.languages.toggleDontTranslateFrom(languageCode)
|
||||||
dontTranslateFrom = dontTranslateFrom.plus(languageCode)
|
saveAccountSettings()
|
||||||
saveAccountSettings()
|
|
||||||
} else {
|
|
||||||
dontTranslateFrom = dontTranslateFrom.minus(languageCode)
|
|
||||||
saveAccountSettings()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateToContains(languageCode: Locale) = translateTo.contains(languageCode.language)
|
fun translateToContains(languageCode: Locale) = syncedSettings.languages.translateTo.contains(languageCode.language)
|
||||||
|
|
||||||
fun updateTranslateTo(languageCode: Locale) {
|
fun updateTranslateTo(languageCode: Locale): Boolean {
|
||||||
if (translateTo != languageCode.language) {
|
if (syncedSettings.languages.updateTranslateTo(languageCode)) {
|
||||||
translateTo = languageCode.language
|
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prefer(
|
fun prefer(
|
||||||
@@ -283,23 +259,14 @@ class AccountSettings(
|
|||||||
target: String,
|
target: String,
|
||||||
preference: String,
|
preference: String,
|
||||||
) {
|
) {
|
||||||
val key = "$source,$target"
|
syncedSettings.languages.prefer(source, target, preference)
|
||||||
if (key !in languagePreferences) {
|
saveAccountSettings()
|
||||||
languagePreferences = languagePreferences + Pair(key, preference)
|
|
||||||
saveAccountSettings()
|
|
||||||
} else {
|
|
||||||
if (languagePreferences.get(key) == preference) {
|
|
||||||
languagePreferences = languagePreferences.minus(key)
|
|
||||||
} else {
|
|
||||||
languagePreferences = languagePreferences + Pair(key, preference)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun preferenceBetween(
|
fun preferenceBetween(
|
||||||
source: String,
|
source: String,
|
||||||
target: String,
|
target: String,
|
||||||
): String? = languagePreferences["$source,$target"]
|
): String? = syncedSettings.languages.preferenceBetween(source, target)
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// Backup Lists
|
// Backup Lists
|
||||||
@@ -382,6 +349,22 @@ class AccountSettings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateAppSpecificData(
|
||||||
|
appSettings: AppSpecificDataEvent?,
|
||||||
|
newSyncedSettings: AccountSyncedSettingsInternal,
|
||||||
|
) {
|
||||||
|
if (appSettings == null || appSettings.content().isEmpty()) return
|
||||||
|
|
||||||
|
// Events might be different objects, we have to compare their ids.
|
||||||
|
if (backupAppSpecificData?.id != appSettings.id) {
|
||||||
|
println("AABBCC Update App Specific Data")
|
||||||
|
backupAppSpecificData = appSettings
|
||||||
|
syncedSettings.updateFrom(newSyncedSettings)
|
||||||
|
|
||||||
|
saveAccountSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// Warning dialogs
|
// Warning dialogs
|
||||||
// ----
|
// ----
|
||||||
@@ -407,15 +390,6 @@ class AccountSettings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShowSensitiveContent(show: Boolean?): Boolean {
|
|
||||||
if (showSensitiveContent.value != show) {
|
|
||||||
showSensitiveContent.update { show }
|
|
||||||
saveAccountSettings()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
// donations
|
// donations
|
||||||
// ---
|
// ---
|
||||||
@@ -509,16 +483,20 @@ class AccountSettings(
|
|||||||
// ---
|
// ---
|
||||||
// filters
|
// filters
|
||||||
// ---
|
// ---
|
||||||
|
fun updateShowSensitiveContent(show: Boolean?): Boolean {
|
||||||
|
if (syncedSettings.security.updateShowSensitiveContent(show)) {
|
||||||
|
saveAccountSettings()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun updateOptOutOptions(
|
fun updateOptOutOptions(
|
||||||
warnReports: Boolean,
|
warnReports: Boolean,
|
||||||
filterSpam: Boolean,
|
filterSpam: Boolean,
|
||||||
): Boolean =
|
): Boolean =
|
||||||
if (warnAboutPostsWithReports != warnReports || filterSpam != filterSpamFromStrangers) {
|
if (syncedSettings.security.updateOptOutOptions(warnReports, filterSpam)) {
|
||||||
warnAboutPostsWithReports = warnReports
|
|
||||||
filterSpamFromStrangers = filterSpam
|
|
||||||
|
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
|
||||||
|
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AccountSyncedSettings(
|
||||||
|
internalSettings: AccountSyncedSettingsInternal,
|
||||||
|
) {
|
||||||
|
val reactions = AccountReactionPreferences(MutableStateFlow(internalSettings.reactions.reactionChoices.toImmutableList()))
|
||||||
|
val zaps =
|
||||||
|
AccountZapPreferences(
|
||||||
|
MutableStateFlow(internalSettings.zaps.zapAmountChoices.toImmutableList()),
|
||||||
|
MutableStateFlow(internalSettings.zaps.defaultZapType),
|
||||||
|
)
|
||||||
|
val languages =
|
||||||
|
AccountLanguagePreferences(
|
||||||
|
internalSettings.languages.dontTranslateFrom,
|
||||||
|
internalSettings.languages.languagePreferences,
|
||||||
|
internalSettings.languages.translateTo,
|
||||||
|
)
|
||||||
|
val security =
|
||||||
|
AccountSecurityPreferences(
|
||||||
|
MutableStateFlow(internalSettings.security.showSensitiveContent),
|
||||||
|
internalSettings.security.warnAboutPostsWithReports,
|
||||||
|
internalSettings.security.filterSpamFromStrangers,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toInternal(): AccountSyncedSettingsInternal =
|
||||||
|
AccountSyncedSettingsInternal(
|
||||||
|
reactions = AccountReactionPreferencesInternal(reactions.reactionChoices.value),
|
||||||
|
zaps =
|
||||||
|
AccountZapPreferencesInternal(
|
||||||
|
zaps.zapAmountChoices.value,
|
||||||
|
zaps.defaultZapType.value,
|
||||||
|
),
|
||||||
|
languages =
|
||||||
|
AccountLanguagePreferencesInternal(
|
||||||
|
languages.dontTranslateFrom,
|
||||||
|
languages.languagePreferences,
|
||||||
|
languages.translateTo,
|
||||||
|
),
|
||||||
|
security =
|
||||||
|
AccountSecurityPreferencesInternal(
|
||||||
|
security.showSensitiveContent.value,
|
||||||
|
security.warnAboutPostsWithReports,
|
||||||
|
security.filterSpamFromStrangers,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun updateFrom(syncedSettingsInternal: AccountSyncedSettingsInternal) {
|
||||||
|
val newReactionChoices = syncedSettingsInternal.reactions.reactionChoices.toImmutableList()
|
||||||
|
if (!equalImmutableLists(reactions.reactionChoices.value, newReactionChoices)) {
|
||||||
|
reactions.reactionChoices.tryEmit(newReactionChoices)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newZapChoices = syncedSettingsInternal.zaps.zapAmountChoices.toImmutableList()
|
||||||
|
if (!equalImmutableLists(zaps.zapAmountChoices.value, newZapChoices)) {
|
||||||
|
zaps.zapAmountChoices.tryEmit(newZapChoices)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zaps.defaultZapType.value != syncedSettingsInternal.zaps.defaultZapType) {
|
||||||
|
zaps.defaultZapType.tryEmit(syncedSettingsInternal.zaps.defaultZapType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languages.dontTranslateFrom != syncedSettingsInternal.languages.dontTranslateFrom) {
|
||||||
|
languages.dontTranslateFrom = syncedSettingsInternal.languages.dontTranslateFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languages.languagePreferences != syncedSettingsInternal.languages.languagePreferences) {
|
||||||
|
languages.languagePreferences = syncedSettingsInternal.languages.languagePreferences
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languages.translateTo != syncedSettingsInternal.languages.translateTo) {
|
||||||
|
languages.translateTo = syncedSettingsInternal.languages.translateTo
|
||||||
|
}
|
||||||
|
|
||||||
|
if (security.showSensitiveContent.value != syncedSettingsInternal.security.showSensitiveContent) {
|
||||||
|
security.showSensitiveContent.tryEmit(syncedSettingsInternal.security.showSensitiveContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (security.filterSpamFromStrangers != syncedSettingsInternal.security.filterSpamFromStrangers) {
|
||||||
|
security.filterSpamFromStrangers = syncedSettingsInternal.security.filterSpamFromStrangers
|
||||||
|
}
|
||||||
|
|
||||||
|
if (security.warnAboutPostsWithReports != syncedSettingsInternal.security.warnAboutPostsWithReports) {
|
||||||
|
security.warnAboutPostsWithReports = syncedSettingsInternal.security.warnAboutPostsWithReports
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AccountReactionPreferences(
|
||||||
|
var reactionChoices: MutableStateFlow<ImmutableList<String>>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AccountZapPreferences(
|
||||||
|
var zapAmountChoices: MutableStateFlow<ImmutableList<Long>>,
|
||||||
|
val defaultZapType: MutableStateFlow<LnZapEvent.ZapType>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AccountLanguagePreferences(
|
||||||
|
var dontTranslateFrom: Set<String>,
|
||||||
|
var languagePreferences: Map<String, String>,
|
||||||
|
var translateTo: String,
|
||||||
|
) {
|
||||||
|
// ---
|
||||||
|
// language services
|
||||||
|
// ---
|
||||||
|
fun toggleDontTranslateFrom(languageCode: String) {
|
||||||
|
if (!dontTranslateFrom.contains(languageCode)) {
|
||||||
|
dontTranslateFrom = dontTranslateFrom.plus(languageCode)
|
||||||
|
} else {
|
||||||
|
dontTranslateFrom = dontTranslateFrom.minus(languageCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateToContains(languageCode: Locale) = translateTo.contains(languageCode.language)
|
||||||
|
|
||||||
|
fun updateTranslateTo(languageCode: Locale): Boolean {
|
||||||
|
if (translateTo != languageCode.language) {
|
||||||
|
translateTo = languageCode.language
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun prefer(
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
preference: String,
|
||||||
|
) {
|
||||||
|
val key = "$source,$target"
|
||||||
|
if (key !in languagePreferences) {
|
||||||
|
languagePreferences = languagePreferences + Pair(key, preference)
|
||||||
|
} else {
|
||||||
|
if (languagePreferences.get(key) == preference) {
|
||||||
|
languagePreferences = languagePreferences.minus(key)
|
||||||
|
} else {
|
||||||
|
languagePreferences = languagePreferences + Pair(key, preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun preferenceBetween(
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
): String? = languagePreferences["$source,$target"]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AccountSecurityPreferences(
|
||||||
|
val showSensitiveContent: MutableStateFlow<Boolean?> = MutableStateFlow(null),
|
||||||
|
var warnAboutPostsWithReports: Boolean = true,
|
||||||
|
var filterSpamFromStrangers: Boolean = true,
|
||||||
|
) {
|
||||||
|
fun updateShowSensitiveContent(show: Boolean?): Boolean {
|
||||||
|
if (showSensitiveContent.value != show) {
|
||||||
|
showSensitiveContent.update { show }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
// filters
|
||||||
|
// ---
|
||||||
|
fun updateOptOutOptions(
|
||||||
|
warnReports: Boolean,
|
||||||
|
filterSpam: Boolean,
|
||||||
|
): Boolean =
|
||||||
|
if (warnAboutPostsWithReports != warnReports || filterSpam != filterSpamFromStrangers) {
|
||||||
|
warnAboutPostsWithReports = warnReports
|
||||||
|
filterSpamFromStrangers = filterSpam
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import androidx.core.os.ConfigurationCompat
|
||||||
|
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
val DefaultReactions =
|
||||||
|
listOf(
|
||||||
|
"\uD83D\uDE80",
|
||||||
|
"\uD83E\uDEC2",
|
||||||
|
"\uD83D\uDC40",
|
||||||
|
"\uD83D\uDE02",
|
||||||
|
"\uD83C\uDF89",
|
||||||
|
"\uD83E\uDD14",
|
||||||
|
"\uD83D\uDE31",
|
||||||
|
)
|
||||||
|
|
||||||
|
val DefaultZapAmounts = listOf(100L, 500L, 1000L)
|
||||||
|
|
||||||
|
fun getLanguagesSpokenByUser(): Set<String> {
|
||||||
|
val languageList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration())
|
||||||
|
val codedList = mutableSetOf<String>()
|
||||||
|
for (i in 0 until languageList.size()) {
|
||||||
|
languageList.get(i)?.let { codedList.add(it.language) }
|
||||||
|
}
|
||||||
|
return codedList
|
||||||
|
}
|
||||||
|
|
||||||
|
class AccountSyncedSettingsInternal(
|
||||||
|
val reactions: AccountReactionPreferencesInternal = AccountReactionPreferencesInternal(),
|
||||||
|
val zaps: AccountZapPreferencesInternal = AccountZapPreferencesInternal(),
|
||||||
|
val languages: AccountLanguagePreferencesInternal = AccountLanguagePreferencesInternal(),
|
||||||
|
val security: AccountSecurityPreferencesInternal = AccountSecurityPreferencesInternal(),
|
||||||
|
)
|
||||||
|
|
||||||
|
class AccountReactionPreferencesInternal(
|
||||||
|
var reactionChoices: List<String> = DefaultReactions,
|
||||||
|
)
|
||||||
|
|
||||||
|
class AccountZapPreferencesInternal(
|
||||||
|
var zapAmountChoices: List<Long> = DefaultZapAmounts,
|
||||||
|
val defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC,
|
||||||
|
)
|
||||||
|
|
||||||
|
class AccountLanguagePreferencesInternal(
|
||||||
|
var dontTranslateFrom: Set<String> = getLanguagesSpokenByUser(),
|
||||||
|
var languagePreferences: Map<String, String> = mapOf(),
|
||||||
|
var translateTo: String = Locale.getDefault().language,
|
||||||
|
)
|
||||||
|
|
||||||
|
class AccountSecurityPreferencesInternal(
|
||||||
|
val showSensitiveContent: Boolean? = null,
|
||||||
|
var warnAboutPostsWithReports: Boolean = true,
|
||||||
|
var filterSpamFromStrangers: Boolean = true,
|
||||||
|
)
|
@@ -40,6 +40,7 @@ import com.vitorpamplona.quartz.events.AddressableEvent
|
|||||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||||
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.events.AppRecommendationEvent
|
import com.vitorpamplona.quartz.events.AppRecommendationEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppSpecificDataEvent
|
||||||
import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
||||||
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
||||||
import com.vitorpamplona.quartz.events.BadgeAwardEvent
|
import com.vitorpamplona.quartz.events.BadgeAwardEvent
|
||||||
@@ -1232,6 +1233,13 @@ object LocalCache {
|
|||||||
consumeBaseReplaceable(event, relay)
|
consumeBaseReplaceable(event, relay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun consume(
|
||||||
|
event: AppSpecificDataEvent,
|
||||||
|
relay: Relay?,
|
||||||
|
) {
|
||||||
|
consumeBaseReplaceable(event, relay)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun consume(event: RecommendRelayEvent) {
|
fun consume(event: RecommendRelayEvent) {
|
||||||
// // Log.d("RR", event.toJson())
|
// // Log.d("RR", event.toJson())
|
||||||
@@ -2645,6 +2653,7 @@ object LocalCache {
|
|||||||
is AdvertisedRelayListEvent -> consume(event, relay)
|
is AdvertisedRelayListEvent -> consume(event, relay)
|
||||||
is AppDefinitionEvent -> consume(event, relay)
|
is AppDefinitionEvent -> consume(event, relay)
|
||||||
is AppRecommendationEvent -> consume(event, relay)
|
is AppRecommendationEvent -> consume(event, relay)
|
||||||
|
is AppSpecificDataEvent -> consume(event, relay)
|
||||||
is AudioHeaderEvent -> consume(event, relay)
|
is AudioHeaderEvent -> consume(event, relay)
|
||||||
is AudioTrackEvent -> consume(event, relay)
|
is AudioTrackEvent -> consume(event, relay)
|
||||||
is BadgeAwardEvent -> consume(event)
|
is BadgeAwardEvent -> consume(event)
|
||||||
|
@@ -33,6 +33,7 @@ import com.vitorpamplona.ammolite.relays.filters.EOSETime
|
|||||||
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppSpecificDataEvent
|
||||||
import com.vitorpamplona.quartz.events.BadgeAwardEvent
|
import com.vitorpamplona.quartz.events.BadgeAwardEvent
|
||||||
import com.vitorpamplona.quartz.events.BadgeProfilesEvent
|
import com.vitorpamplona.quartz.events.BadgeProfilesEvent
|
||||||
import com.vitorpamplona.quartz.events.BookmarkListEvent
|
import com.vitorpamplona.quartz.events.BookmarkListEvent
|
||||||
@@ -78,35 +79,15 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
val latestEOSEs = EOSEAccount()
|
val latestEOSEs = EOSEAccount()
|
||||||
val hasLoadedTheBasics = mutableMapOf<User, Boolean>()
|
val hasLoadedTheBasics = mutableMapOf<User, Boolean>()
|
||||||
|
|
||||||
fun createAccountContactListFilter(): TypedFilter =
|
|
||||||
TypedFilter(
|
|
||||||
types = COMMON_FEED_TYPES,
|
|
||||||
filter =
|
|
||||||
SincePerRelayFilter(
|
|
||||||
kinds = listOf(ContactListEvent.KIND),
|
|
||||||
authors = listOf(account.userProfile().pubkeyHex),
|
|
||||||
limit = 1,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun createAccountMetadataFilter(): TypedFilter =
|
fun createAccountMetadataFilter(): TypedFilter =
|
||||||
TypedFilter(
|
|
||||||
types = COMMON_FEED_TYPES,
|
|
||||||
filter =
|
|
||||||
SincePerRelayFilter(
|
|
||||||
kinds = listOf(MetadataEvent.KIND),
|
|
||||||
authors = listOf(account.userProfile().pubkeyHex),
|
|
||||||
limit = 1,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun createAccountRelayListFilter(): TypedFilter =
|
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = COMMON_FEED_TYPES,
|
||||||
filter =
|
filter =
|
||||||
SincePerRelayFilter(
|
SincePerRelayFilter(
|
||||||
kinds =
|
kinds =
|
||||||
listOf(
|
listOf(
|
||||||
|
MetadataEvent.KIND,
|
||||||
|
ContactListEvent.KIND,
|
||||||
StatusEvent.KIND,
|
StatusEvent.KIND,
|
||||||
AdvertisedRelayListEvent.KIND,
|
AdvertisedRelayListEvent.KIND,
|
||||||
ChatMessageRelayListEvent.KIND,
|
ChatMessageRelayListEvent.KIND,
|
||||||
@@ -138,7 +119,7 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
PeopleListEvent.KIND,
|
PeopleListEvent.KIND,
|
||||||
),
|
),
|
||||||
authors = otherAuthors,
|
authors = otherAuthors,
|
||||||
limit = 100,
|
limit = otherAuthors.size * 10,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -154,6 +135,18 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun createAccountSettings2Filter(): TypedFilter =
|
||||||
|
TypedFilter(
|
||||||
|
types = COMMON_FEED_TYPES,
|
||||||
|
filter =
|
||||||
|
SincePerRelayFilter(
|
||||||
|
kinds = listOf(AppSpecificDataEvent.KIND),
|
||||||
|
authors = listOf(account.userProfile().pubkeyHex),
|
||||||
|
tags = mapOf("d" to listOf(Account.APP_SPECIFIC_DATA_D_TAG)),
|
||||||
|
limit = 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
fun createAccountReportsFilter(): TypedFilter =
|
fun createAccountReportsFilter(): TypedFilter =
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = COMMON_FEED_TYPES,
|
||||||
@@ -465,8 +458,7 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
accountChannel.typedFilters =
|
accountChannel.typedFilters =
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
createAccountMetadataFilter(),
|
createAccountMetadataFilter(),
|
||||||
createAccountContactListFilter(),
|
createAccountSettings2Filter(),
|
||||||
createAccountRelayListFilter(),
|
|
||||||
createNotificationFilter(),
|
createNotificationFilter(),
|
||||||
createNotificationFilter2(),
|
createNotificationFilter2(),
|
||||||
createGiftWrapsToMeFilter(),
|
createGiftWrapsToMeFilter(),
|
||||||
@@ -480,9 +472,8 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
accountChannel.typedFilters =
|
accountChannel.typedFilters =
|
||||||
listOf(
|
listOf(
|
||||||
createAccountMetadataFilter(),
|
createAccountMetadataFilter(),
|
||||||
createAccountContactListFilter(),
|
|
||||||
createAccountRelayListFilter(),
|
|
||||||
createAccountSettingsFilter(),
|
createAccountSettingsFilter(),
|
||||||
|
createAccountSettings2Filter(),
|
||||||
).ifEmpty { null }
|
).ifEmpty { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -97,9 +97,7 @@ fun SensitivityWarning(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val accountState =
|
val accountState = accountViewModel.showSensitiveContent().collectAsStateWithLifecycle()
|
||||||
accountViewModel.account.settings.showSensitiveContent
|
|
||||||
.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
|
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
|
||||||
|
|
||||||
|
@@ -525,16 +525,12 @@ fun ZapVote(
|
|||||||
)
|
)
|
||||||
return@combinedClickable
|
return@combinedClickable
|
||||||
} else if (
|
} else if (
|
||||||
accountViewModel.account.settings.zapAmountChoices.value.size == 1 &&
|
accountViewModel.zapAmountChoices().size == 1 &&
|
||||||
pollViewModel.isValidInputVoteAmount(
|
pollViewModel.isValidInputVoteAmount(accountViewModel.zapAmountChoices().first())
|
||||||
accountViewModel.account.settings.zapAmountChoices.value
|
|
||||||
.first(),
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
accountViewModel.zap(
|
accountViewModel.zap(
|
||||||
baseNote,
|
baseNote,
|
||||||
accountViewModel.account.settings.zapAmountChoices.value
|
accountViewModel.zapAmountChoices().first() * 1000,
|
||||||
.first() * 1000,
|
|
||||||
poolOption.option,
|
poolOption.option,
|
||||||
"",
|
"",
|
||||||
context,
|
context,
|
||||||
@@ -667,7 +663,7 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
// TODO: Move this to the viewModel
|
// TODO: Move this to the viewModel
|
||||||
val zapPaymentChoices by accountViewModel.account.settings.zapAmountChoices
|
val zapPaymentChoices by accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices
|
||||||
.collectAsStateWithLifecycle()
|
.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val zapMessage = ""
|
val zapMessage = ""
|
||||||
|
@@ -965,9 +965,9 @@ private fun likeClick(
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (accountViewModel.account.settings.reactionChoices.value
|
|
||||||
.isEmpty()
|
val choices = accountViewModel.reactionChoices()
|
||||||
) {
|
if (choices.isEmpty()) {
|
||||||
accountViewModel.toast(
|
accountViewModel.toast(
|
||||||
R.string.no_reactions_setup,
|
R.string.no_reactions_setup,
|
||||||
R.string.no_reaction_type_setup_long_press_to_change,
|
R.string.no_reaction_type_setup_long_press_to_change,
|
||||||
@@ -977,9 +977,9 @@ private fun likeClick(
|
|||||||
R.string.read_only_user,
|
R.string.read_only_user,
|
||||||
R.string.login_with_a_private_key_to_like_posts,
|
R.string.login_with_a_private_key_to_like_posts,
|
||||||
)
|
)
|
||||||
} else if (accountViewModel.account.settings.reactionChoices.value.size == 1) {
|
} else if (choices.size == 1) {
|
||||||
onWantsToSignReaction()
|
onWantsToSignReaction()
|
||||||
} else if (accountViewModel.account.settings.reactionChoices.value.size > 1) {
|
} else if (choices.size > 1) {
|
||||||
onMultipleChoices()
|
onMultipleChoices()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1181,9 +1181,9 @@ fun zapClick(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountViewModel.account.settings.zapAmountChoices.value
|
val choices = accountViewModel.zapAmountChoices()
|
||||||
.isEmpty()
|
|
||||||
) {
|
if (choices.isEmpty()) {
|
||||||
accountViewModel.toast(
|
accountViewModel.toast(
|
||||||
R.string.error_dialog_zap_error,
|
R.string.error_dialog_zap_error,
|
||||||
R.string.no_zap_amount_setup_long_press_to_change,
|
R.string.no_zap_amount_setup_long_press_to_change,
|
||||||
@@ -1193,11 +1193,10 @@ fun zapClick(
|
|||||||
R.string.error_dialog_zap_error,
|
R.string.error_dialog_zap_error,
|
||||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||||
)
|
)
|
||||||
} else if (accountViewModel.account.settings.zapAmountChoices.value.size == 1) {
|
} else if (choices.size == 1) {
|
||||||
accountViewModel.zap(
|
accountViewModel.zap(
|
||||||
baseNote,
|
baseNote,
|
||||||
accountViewModel.account.settings.zapAmountChoices.value
|
choices.first() * 1000,
|
||||||
.first() * 1000,
|
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
context,
|
context,
|
||||||
@@ -1205,7 +1204,7 @@ fun zapClick(
|
|||||||
onProgress = { onZappingProgress(it) },
|
onProgress = { onZappingProgress(it) },
|
||||||
onPayViaIntent = onPayViaIntent,
|
onPayViaIntent = onPayViaIntent,
|
||||||
)
|
)
|
||||||
} else if (accountViewModel.account.settings.zapAmountChoices.value.size > 1) {
|
} else if (choices.size > 1) {
|
||||||
onMultipleChoices()
|
onMultipleChoices()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1408,8 +1407,7 @@ fun ReactionChoicePopup(
|
|||||||
) {
|
) {
|
||||||
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
|
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
|
||||||
|
|
||||||
val reactions by accountViewModel.account.settings.reactionChoices
|
val reactions by accountViewModel.reactionChoicesFlow().collectAsStateWithLifecycle()
|
||||||
.collectAsStateWithLifecycle()
|
|
||||||
val toRemove = remember { baseNote.reactedBy(accountViewModel.userProfile()).toImmutableSet() }
|
val toRemove = remember { baseNote.reactedBy(accountViewModel.userProfile()).toImmutableSet() }
|
||||||
|
|
||||||
Popup(
|
Popup(
|
||||||
@@ -1573,7 +1571,7 @@ fun ZapAmountChoicePopup(
|
|||||||
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
|
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
|
||||||
) {
|
) {
|
||||||
val zapAmountChoices by
|
val zapAmountChoices by
|
||||||
accountViewModel.account.settings.zapAmountChoices
|
accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices
|
||||||
.collectAsStateWithLifecycle()
|
.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, onDismiss, onChangeAmount, onError, onProgress, onPayViaIntent)
|
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, onDismiss, onChangeAmount, onError, onProgress, onPayViaIntent)
|
||||||
|
@@ -49,7 +49,6 @@ import androidx.compose.material3.OutlinedTextField
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
@@ -69,12 +68,12 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.distinctUntilChanged
|
import androidx.lifecycle.distinctUntilChanged
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||||
import com.vitorpamplona.amethyst.service.firstFullChar
|
import com.vitorpamplona.amethyst.service.firstFullChar
|
||||||
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
||||||
@@ -94,16 +93,17 @@ import com.vitorpamplona.quartz.events.EmojiUrl
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class UpdateReactionTypeViewModel(
|
class UpdateReactionTypeViewModel : ViewModel() {
|
||||||
val accountSettings: AccountSettings,
|
var account: Account? = null
|
||||||
) : ViewModel() {
|
|
||||||
var nextChoice by mutableStateOf(TextFieldValue(""))
|
var nextChoice by mutableStateOf(TextFieldValue(""))
|
||||||
var reactionSet by mutableStateOf(listOf<String>())
|
var reactionSet by mutableStateOf(listOf<String>())
|
||||||
|
|
||||||
fun load() {
|
fun load(myAccount: Account) {
|
||||||
this.reactionSet = accountSettings.reactionChoices.value
|
this.account = myAccount
|
||||||
|
this.reactionSet = myAccount.settings.syncedSettings.reactions.reactionChoices.value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toListOfChoices(commaSeparatedAmounts: String): List<Long> = commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
fun toListOfChoices(commaSeparatedAmounts: String): List<Long> = commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
||||||
@@ -124,21 +124,24 @@ class UpdateReactionTypeViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sendPost() {
|
fun sendPost() {
|
||||||
accountSettings.changeReactionTypes(reactionSet)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
nextChoice = TextFieldValue("")
|
account?.changeReactionTypes(reactionSet)
|
||||||
|
nextChoice = TextFieldValue("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancel() {
|
fun cancel() {
|
||||||
nextChoice = TextFieldValue("")
|
nextChoice = TextFieldValue("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasChanged(): Boolean = reactionSet != accountSettings.reactionChoices.value
|
fun hasChanged(): Boolean =
|
||||||
|
reactionSet !=
|
||||||
class Factory(
|
account
|
||||||
val accountSettings: AccountSettings,
|
?.settings
|
||||||
) : ViewModelProvider.Factory {
|
?.syncedSettings
|
||||||
override fun <UpdateReactionTypeViewModel : ViewModel> create(modelClass: Class<UpdateReactionTypeViewModel>): UpdateReactionTypeViewModel = UpdateReactionTypeViewModel(accountSettings) as UpdateReactionTypeViewModel
|
?.reactions
|
||||||
}
|
?.reactionChoices
|
||||||
|
?.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@@ -148,14 +151,20 @@ fun UpdateReactionTypeDialog(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val postViewModel: UpdateReactionTypeViewModel =
|
val postViewModel: UpdateReactionTypeViewModel = viewModel()
|
||||||
viewModel(
|
postViewModel.load(accountViewModel.account)
|
||||||
key = "UpdateReactionTypeViewModel",
|
|
||||||
factory = UpdateReactionTypeViewModel.Factory(accountViewModel.account.settings),
|
|
||||||
)
|
|
||||||
|
|
||||||
LaunchedEffect(accountViewModel) { postViewModel.load() }
|
UpdateReactionTypeDialog(postViewModel, onClose, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun UpdateReactionTypeDialog(
|
||||||
|
postViewModel: UpdateReactionTypeViewModel,
|
||||||
|
onClose: () -> Unit,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
properties =
|
properties =
|
||||||
|
@@ -82,10 +82,10 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
|
||||||
@@ -106,10 +106,12 @@ import com.vitorpamplona.quartz.encoders.toHexKey
|
|||||||
import com.vitorpamplona.quartz.events.LnZapEvent
|
import com.vitorpamplona.quartz.events.LnZapEvent
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class UpdateZapAmountViewModel : ViewModel() {
|
||||||
|
var account: Account? = null
|
||||||
|
|
||||||
class UpdateZapAmountViewModel(
|
|
||||||
val accountSettings: AccountSettings,
|
|
||||||
) : ViewModel() {
|
|
||||||
var nextAmount by mutableStateOf(TextFieldValue(""))
|
var nextAmount by mutableStateOf(TextFieldValue(""))
|
||||||
var amountSet by mutableStateOf(listOf<Long>())
|
var amountSet by mutableStateOf(listOf<Long>())
|
||||||
var walletConnectRelay by mutableStateOf(TextFieldValue(""))
|
var walletConnectRelay by mutableStateOf(TextFieldValue(""))
|
||||||
@@ -124,15 +126,23 @@ class UpdateZapAmountViewModel(
|
|||||||
updateNIP47(text)
|
updateNIP47(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun load(myAccount: Account) {
|
||||||
this.amountSet = accountSettings.zapAmountChoices.value
|
this.account = myAccount
|
||||||
|
this.amountSet = myAccount.settings.syncedSettings.zaps.zapAmountChoices.value
|
||||||
|
this.selectedZapType = myAccount.settings.syncedSettings.zaps.defaultZapType.value
|
||||||
|
|
||||||
this.walletConnectPubkey =
|
this.walletConnectPubkey =
|
||||||
accountSettings.zapPaymentRequest?.pubKeyHex?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
myAccount.settings.zapPaymentRequest
|
||||||
|
?.pubKeyHex
|
||||||
|
?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
||||||
this.walletConnectRelay =
|
this.walletConnectRelay =
|
||||||
accountSettings.zapPaymentRequest?.relayUri?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
myAccount.settings.zapPaymentRequest
|
||||||
|
?.relayUri
|
||||||
|
?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
||||||
this.walletConnectSecret =
|
this.walletConnectSecret =
|
||||||
accountSettings.zapPaymentRequest?.secret?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
myAccount.settings.zapPaymentRequest
|
||||||
this.selectedZapType = accountSettings.defaultZapType.value
|
?.secret
|
||||||
|
?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toListOfAmounts(commaSeparatedAmounts: String): List<Long> = commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
fun toListOfAmounts(commaSeparatedAmounts: String): List<Long> = commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
||||||
@@ -151,37 +161,37 @@ class UpdateZapAmountViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sendPost() {
|
fun sendPost() {
|
||||||
accountSettings.changeZapAmounts(amountSet)
|
val nip47Update =
|
||||||
accountSettings.changeDefaultZapType(selectedZapType)
|
if (walletConnectRelay.text.isNotBlank() && walletConnectPubkey.text.isNotBlank()) {
|
||||||
|
val pubkeyHex =
|
||||||
|
try {
|
||||||
|
decodePublicKey(walletConnectPubkey.text.trim()).toHexKey()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
if (walletConnectRelay.text.isNotBlank() && walletConnectPubkey.text.isNotBlank()) {
|
val relayUrl = walletConnectRelay.text.ifBlank { null }?.let { RelayUrlFormatter.normalize(it) }
|
||||||
val pubkeyHex =
|
val privKeyHex = walletConnectSecret.text.ifBlank { null }?.let { decodePrivateKeyAsHexOrNull(it) }
|
||||||
try {
|
|
||||||
decodePublicKey(walletConnectPubkey.text.trim()).toHexKey()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e is CancellationException) throw e
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val relayUrl = walletConnectRelay.text.ifBlank { null }?.let { RelayUrlFormatter.normalize(it) }
|
if (pubkeyHex != null && relayUrl != null) {
|
||||||
val privKeyHex = walletConnectSecret.text.ifBlank { null }?.let { decodePrivateKeyAsHexOrNull(it) }
|
|
||||||
|
|
||||||
if (pubkeyHex != null && relayUrl != null) {
|
|
||||||
accountSettings.changeZapPaymentRequest(
|
|
||||||
Nip47WalletConnect.Nip47URI(
|
Nip47WalletConnect.Nip47URI(
|
||||||
pubkeyHex,
|
pubkeyHex,
|
||||||
relayUrl,
|
relayUrl,
|
||||||
privKeyHex,
|
privKeyHex,
|
||||||
),
|
)
|
||||||
)
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
accountSettings.changeZapPaymentRequest(null)
|
null
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
accountSettings.changeZapPaymentRequest(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
nextAmount = TextFieldValue("")
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
account?.updateZapAmounts(amountSet, selectedZapType, nip47Update)
|
||||||
|
|
||||||
|
nextAmount = TextFieldValue("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancel() {
|
fun cancel() {
|
||||||
@@ -190,11 +200,23 @@ class UpdateZapAmountViewModel(
|
|||||||
|
|
||||||
fun hasChanged(): Boolean =
|
fun hasChanged(): Boolean =
|
||||||
(
|
(
|
||||||
selectedZapType != accountSettings.defaultZapType.value ||
|
selectedZapType !=
|
||||||
amountSet != accountSettings.zapAmountChoices.value ||
|
account
|
||||||
walletConnectPubkey.text != (accountSettings.zapPaymentRequest?.pubKeyHex ?: "") ||
|
?.settings
|
||||||
walletConnectRelay.text != (accountSettings.zapPaymentRequest?.relayUri ?: "") ||
|
?.syncedSettings
|
||||||
walletConnectSecret.text != (accountSettings.zapPaymentRequest?.secret ?: "")
|
?.zaps
|
||||||
|
?.defaultZapType
|
||||||
|
?.value ||
|
||||||
|
amountSet !=
|
||||||
|
account
|
||||||
|
?.settings
|
||||||
|
?.syncedSettings
|
||||||
|
?.zaps
|
||||||
|
?.zapAmountChoices
|
||||||
|
?.value ||
|
||||||
|
walletConnectPubkey.text != (account?.settings?.zapPaymentRequest?.pubKeyHex ?: "") ||
|
||||||
|
walletConnectRelay.text != (account?.settings?.zapPaymentRequest?.relayUri ?: "") ||
|
||||||
|
walletConnectSecret.text != (account?.settings?.zapPaymentRequest?.secret ?: "")
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateNIP47(uri: String) {
|
fun updateNIP47(uri: String) {
|
||||||
@@ -205,20 +227,25 @@ class UpdateZapAmountViewModel(
|
|||||||
walletConnectSecret = TextFieldValue(contact.secret ?: "")
|
walletConnectSecret = TextFieldValue(contact.secret ?: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(
|
|
||||||
val accountSettings: AccountSettings,
|
|
||||||
) : ViewModelProvider.Factory {
|
|
||||||
override fun <UpdateZapAmountViewModel : ViewModel> create(modelClass: Class<UpdateZapAmountViewModel>): UpdateZapAmountViewModel = UpdateZapAmountViewModel(accountSettings) as UpdateZapAmountViewModel
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UpdateZapAmountDialog(
|
fun UpdateZapAmountDialog(
|
||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
nip47uri: String? = null,
|
nip47uri: String? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
|
) {
|
||||||
|
val postViewModel: UpdateZapAmountViewModel = viewModel()
|
||||||
|
postViewModel.load(accountViewModel.account)
|
||||||
|
UpdateZapAmountDialog(postViewModel, onClose, nip47uri, accountViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UpdateZapAmountDialog(
|
||||||
|
postViewModel: UpdateZapAmountViewModel,
|
||||||
|
onClose: () -> Unit,
|
||||||
|
nip47uri: String? = null,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
@@ -233,12 +260,6 @@ fun UpdateZapAmountDialog(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
val postViewModel: UpdateZapAmountViewModel =
|
|
||||||
viewModel(
|
|
||||||
key = "UpdateZapAmountViewModel",
|
|
||||||
factory = UpdateZapAmountViewModel.Factory(accountViewModel.account.settings),
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
@@ -311,7 +332,6 @@ fun UpdateZapAmountContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(accountViewModel, nip47uri) {
|
LaunchedEffect(accountViewModel, nip47uri) {
|
||||||
postViewModel.load()
|
|
||||||
if (nip47uri != null) {
|
if (nip47uri != null) {
|
||||||
try {
|
try {
|
||||||
postViewModel.updateNIP47(nip47uri)
|
postViewModel.updateNIP47(nip47uri)
|
||||||
|
@@ -153,7 +153,7 @@ fun ZapCustomDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selectedZapType by
|
var selectedZapType by
|
||||||
remember(accountViewModel) { mutableStateOf(accountViewModel.account.settings.defaultZapType.value) }
|
remember(accountViewModel) { mutableStateOf(accountViewModel.defaultZapType()) }
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
@@ -224,7 +224,7 @@ fun ZapCustomDialog(
|
|||||||
label = stringRes(id = R.string.zap_type),
|
label = stringRes(id = R.string.zap_type),
|
||||||
placeholder =
|
placeholder =
|
||||||
zapTypes
|
zapTypes
|
||||||
.filter { it.first == accountViewModel.account.settings.defaultZapType.value }
|
.filter { it.first == accountViewModel.defaultZapType() }
|
||||||
.first()
|
.first()
|
||||||
.second,
|
.second,
|
||||||
options = zapOptions,
|
options = zapOptions,
|
||||||
|
@@ -315,10 +315,8 @@ fun NoteDropDownMenu(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringRes(R.string.content_warning_hide_all_sensitive_content)) },
|
text = { Text(stringRes(R.string.content_warning_hide_all_sensitive_content)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
accountViewModel.hideSensitiveContent()
|
||||||
accountViewModel.hideSensitiveContent()
|
onDismiss()
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -326,10 +324,8 @@ fun NoteDropDownMenu(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringRes(R.string.content_warning_show_all_sensitive_content)) },
|
text = { Text(stringRes(R.string.content_warning_show_all_sensitive_content)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
accountViewModel.disableContentWarnings()
|
||||||
accountViewModel.disableContentWarnings()
|
onDismiss()
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -337,10 +333,8 @@ fun NoteDropDownMenu(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringRes(R.string.content_warning_see_warnings)) },
|
text = { Text(stringRes(R.string.content_warning_see_warnings)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
accountViewModel.seeContentWarnings()
|
||||||
accountViewModel.seeContentWarnings()
|
onDismiss()
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -385,7 +379,8 @@ fun WatchBookmarksFollowsAndAccount(
|
|||||||
.live()
|
.live()
|
||||||
.bookmarks
|
.bookmarks
|
||||||
.observeAsState()
|
.observeAsState()
|
||||||
val showSensitiveContent by accountViewModel.account.settings.showSensitiveContent
|
val showSensitiveContent by accountViewModel
|
||||||
|
.showSensitiveContent()
|
||||||
.collectAsStateWithLifecycle()
|
.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(key1 = followState, key2 = bookmarkState, key3 = showSensitiveContent) {
|
LaunchedEffect(key1 = followState, key2 = bookmarkState, key3 = showSensitiveContent) {
|
||||||
|
@@ -455,9 +455,9 @@ fun customZapClick(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountViewModel.account.settings.zapAmountChoices.value
|
val choices = accountViewModel.zapAmountChoices()
|
||||||
.isEmpty()
|
|
||||||
) {
|
if (choices.isEmpty()) {
|
||||||
accountViewModel.toast(
|
accountViewModel.toast(
|
||||||
stringRes(context, R.string.error_dialog_zap_error),
|
stringRes(context, R.string.error_dialog_zap_error),
|
||||||
stringRes(context, R.string.no_zap_amount_setup_long_press_to_change),
|
stringRes(context, R.string.no_zap_amount_setup_long_press_to_change),
|
||||||
@@ -467,10 +467,8 @@ fun customZapClick(
|
|||||||
stringRes(context, R.string.error_dialog_zap_error),
|
stringRes(context, R.string.error_dialog_zap_error),
|
||||||
stringRes(context, R.string.login_with_a_private_key_to_be_able_to_send_zaps),
|
stringRes(context, R.string.login_with_a_private_key_to_be_able_to_send_zaps),
|
||||||
)
|
)
|
||||||
} else if (accountViewModel.account.settings.zapAmountChoices.value.size == 1) {
|
} else if (choices.size == 1) {
|
||||||
val amount =
|
val amount = choices.first()
|
||||||
accountViewModel.account.settings.zapAmountChoices.value
|
|
||||||
.first()
|
|
||||||
|
|
||||||
if (amount > 1100) {
|
if (amount > 1100) {
|
||||||
accountViewModel.zap(
|
accountViewModel.zap(
|
||||||
@@ -488,11 +486,9 @@ fun customZapClick(
|
|||||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||||
// recommends amounts for a monthly release.
|
// recommends amounts for a monthly release.
|
||||||
}
|
}
|
||||||
} else if (accountViewModel.account.settings.zapAmountChoices.value.size > 1) {
|
} else if (choices.size > 1) {
|
||||||
if (accountViewModel.account.settings.zapAmountChoices.value
|
if (choices.any { it > 1100 }) {
|
||||||
.any { it > 1100 }
|
onMultipleChoices(choices)
|
||||||
) {
|
|
||||||
onMultipleChoices(accountViewModel.account.settings.zapAmountChoices.value)
|
|
||||||
} else {
|
} else {
|
||||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||||
}
|
}
|
||||||
|
@@ -120,6 +120,7 @@ import kotlinx.coroutines.flow.flatMapLatest
|
|||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.joinAll
|
import kotlinx.coroutines.joinAll
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@@ -328,9 +329,7 @@ class AccountViewModel(
|
|||||||
|
|
||||||
fun reactToOrDelete(note: Note) {
|
fun reactToOrDelete(note: Note) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val reaction =
|
val reaction = reactionChoices().first()
|
||||||
account.settings.reactionChoices.value
|
|
||||||
.first()
|
|
||||||
if (hasReactedTo(note, reaction)) {
|
if (hasReactedTo(note, reaction)) {
|
||||||
deleteReactionTo(note, reaction)
|
deleteReactionTo(note, reaction)
|
||||||
} else {
|
} else {
|
||||||
@@ -714,7 +713,7 @@ class AccountViewModel(
|
|||||||
onProgress(it)
|
onProgress(it)
|
||||||
},
|
},
|
||||||
onPayViaIntent = onPayViaIntent,
|
onPayViaIntent = onPayViaIntent,
|
||||||
zapType = zapType ?: account.settings.defaultZapType.value,
|
zapType = zapType ?: defaultZapType(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -885,24 +884,55 @@ class AccountViewModel(
|
|||||||
fun isFollowing(user: HexKey): Boolean = account.isFollowing(user)
|
fun isFollowing(user: HexKey): Boolean = account.isFollowing(user)
|
||||||
|
|
||||||
fun hideSensitiveContent() {
|
fun hideSensitiveContent() {
|
||||||
account.updateShowSensitiveContent(false)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
}
|
account.updateShowSensitiveContent(false)
|
||||||
|
|
||||||
fun disableContentWarnings() {
|
|
||||||
account.updateShowSensitiveContent(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun seeContentWarnings() {
|
|
||||||
account.updateShowSensitiveContent(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markDonatedInThisVersion() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
account.markDonatedInThisVersion()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun defaultZapType(): LnZapEvent.ZapType = account.settings.defaultZapType.value
|
fun disableContentWarnings() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
account.updateShowSensitiveContent(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun seeContentWarnings() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
account.updateShowSensitiveContent(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markDonatedInThisVersion() {
|
||||||
|
account.markDonatedInThisVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dontTranslateFrom() = account.settings.syncedSettings.languages.dontTranslateFrom
|
||||||
|
|
||||||
|
fun translateTo() = account.settings.syncedSettings.languages.translateTo
|
||||||
|
|
||||||
|
fun defaultZapType() = account.settings.syncedSettings.zaps.defaultZapType.value
|
||||||
|
|
||||||
|
fun showSensitiveContent(): MutableStateFlow<Boolean?> = account.settings.syncedSettings.security.showSensitiveContent
|
||||||
|
|
||||||
|
fun zapAmountChoicesFlow() = account.settings.syncedSettings.zaps.zapAmountChoices
|
||||||
|
|
||||||
|
fun zapAmountChoices() = zapAmountChoicesFlow().value
|
||||||
|
|
||||||
|
fun reactionChoicesFlow() = account.settings.syncedSettings.reactions.reactionChoices
|
||||||
|
|
||||||
|
fun reactionChoices() = reactionChoicesFlow().value
|
||||||
|
|
||||||
|
fun filterSpamFromStrangers() = account.settings.syncedSettings.security.filterSpamFromStrangers
|
||||||
|
|
||||||
|
fun updateOptOutOptions(
|
||||||
|
warnReports: Boolean,
|
||||||
|
filterSpam: Boolean,
|
||||||
|
) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
if (account.updateOptOutOptions(warnReports, filterSpam)) {
|
||||||
|
LocalCache.antiSpam.active = filterSpamFromStrangers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun unwrap(
|
fun unwrap(
|
||||||
event: GiftWrapEvent,
|
event: GiftWrapEvent,
|
||||||
@@ -1539,7 +1569,7 @@ class AccountViewModel(
|
|||||||
context: Context,
|
context: Context,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
if (account.settings.defaultZapType.value == LnZapEvent.ZapType.NONZAP) {
|
if (defaultZapType() == LnZapEvent.ZapType.NONZAP) {
|
||||||
LightningAddressResolver()
|
LightningAddressResolver()
|
||||||
.lnAddressInvoice(
|
.lnAddressInvoice(
|
||||||
lnaddress,
|
lnaddress,
|
||||||
@@ -1553,7 +1583,7 @@ class AccountViewModel(
|
|||||||
context = context,
|
context = context,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
account.createZapRequestFor(toUserPubKeyHex, message, account.settings.defaultZapType.value) { zapRequest ->
|
account.createZapRequestFor(toUserPubKeyHex, message, defaultZapType()) { zapRequest ->
|
||||||
LocalCache.justConsume(zapRequest, null)
|
LocalCache.justConsume(zapRequest, null)
|
||||||
LightningAddressResolver()
|
LightningAddressResolver()
|
||||||
.lnAddressInvoice(
|
.lnAddressInvoice(
|
||||||
|
@@ -42,19 +42,25 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NIP47SetupScreen(
|
fun NIP47SetupScreen(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
nip47: String?,
|
nip47: String?,
|
||||||
) {
|
) {
|
||||||
val postViewModel: UpdateZapAmountViewModel =
|
val postViewModel: UpdateZapAmountViewModel = viewModel()
|
||||||
viewModel(
|
postViewModel.load(accountViewModel.account)
|
||||||
key = "UpdateZapAmountViewModel",
|
NIP47SetupScreen(postViewModel, accountViewModel, nav, nip47)
|
||||||
factory = UpdateZapAmountViewModel.Factory(accountViewModel.account.settings),
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun NIP47SetupScreen(
|
||||||
|
postViewModel: UpdateZapAmountViewModel,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
nip47: String?,
|
||||||
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
@@ -157,15 +157,15 @@ fun SecurityFiltersScreen(
|
|||||||
Column(Modifier.padding(it).fillMaxHeight()) {
|
Column(Modifier.padding(it).fillMaxHeight()) {
|
||||||
val pagerState = rememberPagerState { 3 }
|
val pagerState = rememberPagerState { 3 }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var warnAboutReports by remember { mutableStateOf(accountViewModel.account.settings.warnAboutPostsWithReports) }
|
var warnAboutReports by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.warnAboutPostsWithReports) }
|
||||||
var filterSpam by remember { mutableStateOf(accountViewModel.account.settings.filterSpamFromStrangers) }
|
var filterSpam by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.filterSpamFromStrangers) }
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = warnAboutReports,
|
checked = warnAboutReports,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
warnAboutReports = it
|
warnAboutReports = it
|
||||||
accountViewModel.account.updateOptOutOptions(warnAboutReports, filterSpam)
|
accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ fun SecurityFiltersScreen(
|
|||||||
checked = filterSpam,
|
checked = filterSpam,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
filterSpam = it
|
filterSpam = it
|
||||||
accountViewModel.account.updateOptOutOptions(warnAboutReports, filterSpam)
|
accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -234,7 +234,7 @@ private fun TranslationMessage(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
if (source in accountViewModel.account.settings.dontTranslateFrom) {
|
if (source in accountViewModel.dontTranslateFrom()) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -255,7 +255,7 @@ private fun TranslationMessage(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
accountViewModel.account.settings.toggleDontTranslateFrom(source)
|
accountViewModel.account.toggleDontTranslateFrom(source)
|
||||||
langSettingsPopupExpanded = false
|
langSettingsPopupExpanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -285,7 +285,7 @@ private fun TranslationMessage(
|
|||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.account.settings.prefer(source, target, source)
|
accountViewModel.account.prefer(source, target, source)
|
||||||
langSettingsPopupExpanded = false
|
langSettingsPopupExpanded = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -293,7 +293,9 @@ private fun TranslationMessage(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
if (accountViewModel.account.settings.preferenceBetween(source, target) == target) {
|
if (accountViewModel.account.settings.syncedSettings.languages
|
||||||
|
.preferenceBetween(source, target) == target
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -315,7 +317,7 @@ private fun TranslationMessage(
|
|||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.account.settings.prefer(source, target, target)
|
accountViewModel.account.prefer(source, target, target)
|
||||||
langSettingsPopupExpanded = false
|
langSettingsPopupExpanded = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -350,7 +352,7 @@ private fun TranslationMessage(
|
|||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
accountViewModel.account.settings.updateTranslateTo(lang)
|
accountViewModel.account.updateTranslateTo(lang)
|
||||||
langSettingsPopupExpanded = false
|
langSettingsPopupExpanded = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -377,8 +379,8 @@ fun TranslateAndWatchLanguageChanges(
|
|||||||
LanguageTranslatorService
|
LanguageTranslatorService
|
||||||
.autoTranslate(
|
.autoTranslate(
|
||||||
content,
|
content,
|
||||||
accountViewModel.account.settings.dontTranslateFrom,
|
accountViewModel.dontTranslateFrom(),
|
||||||
accountViewModel.account.settings.translateTo,
|
accountViewModel.translateTo(),
|
||||||
).addOnCompleteListener { task ->
|
).addOnCompleteListener { task ->
|
||||||
if (task.isSuccessful && !content.equals(task.result.result, true)) {
|
if (task.isSuccessful && !content.equals(task.result.result, true)) {
|
||||||
if (task.result.sourceLang != null && task.result.targetLang != null) {
|
if (task.result.sourceLang != null && task.result.targetLang != null) {
|
||||||
|
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 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.quartz.events
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
|
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
class AppSpecificDataEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30078
|
||||||
|
const val ALT = "Arbitrary app data"
|
||||||
|
|
||||||
|
fun createTag(
|
||||||
|
pubkey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
): ATag = ATag(KIND, pubkey, dTag, null)
|
||||||
|
|
||||||
|
fun create(
|
||||||
|
dTag: String,
|
||||||
|
description: String,
|
||||||
|
otherTags: Array<Array<String>>,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
onReady: (AppSpecificDataEvent) -> Unit,
|
||||||
|
) {
|
||||||
|
val withD =
|
||||||
|
if (otherTags.any { it.size > 1 && it[0] == "d" && it[1] == dTag }) {
|
||||||
|
otherTags
|
||||||
|
} else {
|
||||||
|
otherTags.filter { it.size > 0 && it[0] != "d" }.toTypedArray() + arrayOf("d", dTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newTags =
|
||||||
|
if (withD.none { it.size > 0 && it[0] == "alt" }) {
|
||||||
|
withD + arrayOf("alt", ALT)
|
||||||
|
} else {
|
||||||
|
withD
|
||||||
|
}
|
||||||
|
|
||||||
|
signer.sign(createdAt, KIND, newTags, description, onReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -39,26 +39,22 @@ class EventFactory {
|
|||||||
content: String,
|
content: String,
|
||||||
sig: String,
|
sig: String,
|
||||||
) = when (kind) {
|
) = when (kind) {
|
||||||
AdvertisedRelayListEvent.KIND ->
|
AdvertisedRelayListEvent.KIND -> AdvertisedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AdvertisedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
AppDefinitionEvent.KIND -> AppDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
AppDefinitionEvent.KIND -> AppDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AppRecommendationEvent.KIND ->
|
AppRecommendationEvent.KIND -> AppRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AppRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
AppSpecificDataEvent.KIND -> AppSpecificDataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AudioHeaderEvent.KIND -> AudioHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
AudioHeaderEvent.KIND -> AudioHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AudioTrackEvent.KIND -> AudioTrackEvent(id, pubKey, createdAt, tags, content, sig)
|
AudioTrackEvent.KIND -> AudioTrackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
BadgeAwardEvent.KIND -> BadgeAwardEvent(id, pubKey, createdAt, tags, content, sig)
|
BadgeAwardEvent.KIND -> BadgeAwardEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
BadgeDefinitionEvent.KIND -> BadgeDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
BadgeDefinitionEvent.KIND -> BadgeDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
BadgeProfilesEvent.KIND -> BadgeProfilesEvent(id, pubKey, createdAt, tags, content, sig)
|
BadgeProfilesEvent.KIND -> BadgeProfilesEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
BookmarkListEvent.KIND -> BookmarkListEvent(id, pubKey, createdAt, tags, content, sig)
|
BookmarkListEvent.KIND -> BookmarkListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CalendarDateSlotEvent.KIND ->
|
CalendarDateSlotEvent.KIND -> CalendarDateSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CalendarDateSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
CalendarEvent.KIND -> CalendarEvent(id, pubKey, createdAt, tags, content, sig)
|
CalendarEvent.KIND -> CalendarEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CalendarTimeSlotEvent.KIND ->
|
CalendarTimeSlotEvent.KIND -> CalendarTimeSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CalendarTimeSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
CalendarRSVPEvent.KIND -> CalendarRSVPEvent(id, pubKey, createdAt, tags, content, sig)
|
CalendarRSVPEvent.KIND -> CalendarRSVPEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ChannelCreateEvent.KIND -> ChannelCreateEvent(id, pubKey, createdAt, tags, content, sig)
|
ChannelCreateEvent.KIND -> ChannelCreateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ChannelHideMessageEvent.KIND ->
|
ChannelHideMessageEvent.KIND -> ChannelHideMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ChannelHideMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
ChannelListEvent.KIND -> ChannelListEvent(id, pubKey, createdAt, tags, content, sig)
|
ChannelListEvent.KIND -> ChannelListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ChannelMessageEvent.KIND -> ChannelMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
ChannelMessageEvent.KIND -> ChannelMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ChannelMetadataEvent.KIND -> ChannelMetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
ChannelMetadataEvent.KIND -> ChannelMetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -79,23 +75,19 @@ class EventFactory {
|
|||||||
}
|
}
|
||||||
ChatMessageRelayListEvent.KIND -> ChatMessageRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
ChatMessageRelayListEvent.KIND -> ChatMessageRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ClassifiedsEvent.KIND -> ClassifiedsEvent(id, pubKey, createdAt, tags, content, sig)
|
ClassifiedsEvent.KIND -> ClassifiedsEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CommunityDefinitionEvent.KIND ->
|
CommunityDefinitionEvent.KIND -> CommunityDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CommunityDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
CommunityListEvent.KIND -> CommunityListEvent(id, pubKey, createdAt, tags, content, sig)
|
CommunityListEvent.KIND -> CommunityListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CommunityPostApprovalEvent.KIND ->
|
CommunityPostApprovalEvent.KIND -> CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
ContactListEvent.KIND -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
|
ContactListEvent.KIND -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
DeletionEvent.KIND -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
DeletionEvent.KIND -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
DraftEvent.KIND -> DraftEvent(id, pubKey, createdAt, tags, content, sig)
|
DraftEvent.KIND -> DraftEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EmojiPackEvent.KIND -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
|
EmojiPackEvent.KIND -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EmojiPackSelectionEvent.KIND ->
|
EmojiPackSelectionEvent.KIND -> EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
FileHeaderEvent.KIND -> FileHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
FileHeaderEvent.KIND -> FileHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ProfileGalleryEntryEvent.KIND -> ProfileGalleryEntryEvent(id, pubKey, createdAt, tags, content, sig)
|
ProfileGalleryEntryEvent.KIND -> ProfileGalleryEntryEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
FileServersEvent.KIND -> FileServersEvent(id, pubKey, createdAt, tags, content, sig)
|
FileServersEvent.KIND -> FileServersEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
FileStorageEvent.KIND -> FileStorageEvent(id, pubKey, createdAt, tags, content, sig)
|
FileStorageEvent.KIND -> FileStorageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
FileStorageHeaderEvent.KIND ->
|
FileStorageHeaderEvent.KIND -> FileStorageHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
FileStorageHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
FhirResourceEvent.KIND -> FhirResourceEvent(id, pubKey, createdAt, tags, content, sig)
|
FhirResourceEvent.KIND -> FhirResourceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GenericRepostEvent.KIND -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
|
GenericRepostEvent.KIND -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GiftWrapEvent.KIND -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
GiftWrapEvent.KIND -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -105,16 +97,12 @@ class EventFactory {
|
|||||||
GitRepositoryEvent.KIND -> GitRepositoryEvent(id, pubKey, createdAt, tags, content, sig)
|
GitRepositoryEvent.KIND -> GitRepositoryEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GoalEvent.KIND -> GoalEvent(id, pubKey, createdAt, tags, content, sig)
|
GoalEvent.KIND -> GoalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HighlightEvent.KIND -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
|
HighlightEvent.KIND -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HTTPAuthorizationEvent.KIND ->
|
HTTPAuthorizationEvent.KIND -> HTTPAuthorizationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HTTPAuthorizationEvent(id, pubKey, createdAt, tags, content, sig)
|
LiveActivitiesChatMessageEvent.KIND -> LiveActivitiesChatMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LiveActivitiesChatMessageEvent.KIND ->
|
|
||||||
LiveActivitiesChatMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
LiveActivitiesEvent.KIND -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
|
LiveActivitiesEvent.KIND -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapEvent.KIND -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapEvent.KIND -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapPaymentRequestEvent.KIND ->
|
LnZapPaymentRequestEvent.KIND -> LnZapPaymentRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapPaymentRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapPaymentResponseEvent.KIND -> LnZapPaymentResponseEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapPaymentResponseEvent.KIND ->
|
|
||||||
LnZapPaymentResponseEvent(id, pubKey, createdAt, tags, content, sig)
|
|
||||||
LnZapPrivateEvent.KIND -> LnZapPrivateEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapPrivateEvent.KIND -> LnZapPrivateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapRequestEvent.KIND -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapRequestEvent.KIND -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LongTextNoteEvent.KIND -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
LongTextNoteEvent.KIND -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
Reference in New Issue
Block a user