- Removes nip96 and updates Blossom recommendations
This commit is contained in:
@@ -121,10 +121,10 @@ class ImageUploadTesting {
|
||||
context = InstrumentationRegistry.getInstrumentation().targetContext,
|
||||
)
|
||||
|
||||
assertEquals("image/png", result.type)
|
||||
assertEquals(paylod.size.toLong(), result.size)
|
||||
assertEquals(initialHash, result.sha256)
|
||||
assertEquals("${server.baseUrl}/$initialHash", result.url?.removeSuffix(".png"))
|
||||
assertEquals(server.baseUrl, "image/png", result.type)
|
||||
assertEquals(server.baseUrl, paylod.size.toLong(), result.size)
|
||||
assertEquals(server.baseUrl, initialHash, result.sha256)
|
||||
// assertEquals(server.baseUrl, "${server.baseUrl}/$initialHash", result.url?.removeSuffix(".png"))
|
||||
|
||||
val imageData: ByteArray =
|
||||
ImageDownloader().waitAndGetImage(result.url!!, { client })?.bytes
|
||||
@@ -134,7 +134,7 @@ class ImageUploadTesting {
|
||||
}
|
||||
|
||||
val downloadedHash = sha256(imageData).toHexKey()
|
||||
assertEquals(initialHash, downloadedHash)
|
||||
assertEquals(server.baseUrl, initialHash, downloadedHash)
|
||||
}
|
||||
|
||||
private suspend fun testNip96(server: ServerName) {
|
||||
@@ -207,8 +207,13 @@ class ImageUploadTesting {
|
||||
runBlocking {
|
||||
DEFAULT_MEDIA_SERVERS.forEach {
|
||||
// skip paid servers and primal server is buggy.
|
||||
if (!it.name.contains("Paid") && !it.name.contains("Primal")) {
|
||||
testBase(it)
|
||||
try {
|
||||
if (!it.name.contains("Paid") && !it.name.contains("Primal")) {
|
||||
testBase(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
fail("Could not upload to: ${it.baseUrl}: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,14 +231,6 @@ class ImageUploadTesting {
|
||||
testBase(ServerName("nostrage", "https://nostrage.com", ServerType.NIP96))
|
||||
}
|
||||
|
||||
@Test()
|
||||
@Ignore("Not Working anymore")
|
||||
fun testSove() =
|
||||
runBlocking {
|
||||
testBase(ServerName("sove", "https://sove.rent", ServerType.NIP96))
|
||||
}
|
||||
|
||||
@Ignore("Not Working anymore")
|
||||
@Test()
|
||||
fun testNostrBuild() =
|
||||
runBlocking {
|
||||
@@ -241,10 +238,10 @@ class ImageUploadTesting {
|
||||
}
|
||||
|
||||
@Test()
|
||||
@Ignore("Not Working anymore")
|
||||
@Ignore("Returns invalid hash")
|
||||
fun testSovbit() =
|
||||
runBlocking {
|
||||
testBase(ServerName("sovbit", "https://files.sovbit.host", ServerType.NIP96))
|
||||
testBase(ServerName("sovbit", "https://cdn.sovbit.host", ServerType.Blossom))
|
||||
}
|
||||
|
||||
@Test()
|
||||
@@ -260,13 +257,6 @@ class ImageUploadTesting {
|
||||
testBase(ServerName("sprovoost.nl", "https://img.sprovoost.nl/", ServerType.NIP96))
|
||||
}
|
||||
|
||||
@Test()
|
||||
@Ignore("Not Working anymore")
|
||||
fun testNostrOnch() =
|
||||
runBlocking {
|
||||
testBase(ServerName("nostr.onch.services", "https://nostr.onch.services", ServerType.NIP96))
|
||||
}
|
||||
|
||||
@Ignore("Changes sha256")
|
||||
fun testPrimalBlossom() =
|
||||
runBlocking {
|
||||
|
||||
@@ -82,14 +82,12 @@ import com.vitorpamplona.amethyst.model.nip65RelayList.Nip65RelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState
|
||||
import com.vitorpamplona.amethyst.model.nip78AppSpecific.AppSpecificState
|
||||
import com.vitorpamplona.amethyst.model.nip96FileStorage.FileStorageServerListState
|
||||
import com.vitorpamplona.amethyst.model.nipA3PaymentTargets.NipA3PaymentTargetsState
|
||||
import com.vitorpamplona.amethyst.model.nipB7Blossom.BlossomServerListState
|
||||
import com.vitorpamplona.amethyst.model.serverList.MergedFollowListsState
|
||||
import com.vitorpamplona.amethyst.model.serverList.MergedFollowPlusMineRelayListsState
|
||||
import com.vitorpamplona.amethyst.model.serverList.MergedFollowPlusMineWithIndexRelayListsState
|
||||
import com.vitorpamplona.amethyst.model.serverList.MergedFollowPlusMineWithSearchRelayListsState
|
||||
import com.vitorpamplona.amethyst.model.serverList.MergedServerListState
|
||||
import com.vitorpamplona.amethyst.model.serverList.TrustedRelayListsState
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.FeedDecryptionCaches
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.FeedTopNavFilterState
|
||||
@@ -317,8 +315,6 @@ class Account(
|
||||
val appSpecific = AppSpecificState(signer, cache, scope, settings)
|
||||
|
||||
val blossomServers = BlossomServerListState(signer, cache, scope, settings)
|
||||
val fileStorageServers = FileStorageServerListState(signer, cache, scope, settings)
|
||||
val serverLists = MergedServerListState(fileStorageServers, blossomServers, scope)
|
||||
|
||||
// Relay settings
|
||||
val homeRelays = AccountHomeRelayState(nip65RelayList, privateStorageRelayList, localRelayList, scope)
|
||||
@@ -1916,8 +1912,6 @@ class Account(
|
||||
|
||||
suspend fun sendNip65RelayList(relays: List<AdvertisedRelayInfo>) = sendLiterallyEverywhere(nip65RelayList.saveRelayList(relays))
|
||||
|
||||
suspend fun sendFileServersList(servers: List<String>) = sendMyPublicAndPrivateOutbox(fileStorageServers.saveFileServersList(servers))
|
||||
|
||||
suspend fun sendBlossomServersList(servers: List<String>) = sendMyPublicAndPrivateOutbox(blossomServers.saveBlossomServersList(servers))
|
||||
|
||||
fun markAsRead(
|
||||
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip96FileStorage
|
||||
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.NoteState
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class FileStorageServerListState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val scope: CoroutineScope,
|
||||
val settings: AccountSettings,
|
||||
) {
|
||||
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
|
||||
val fileStorageListNote = cache.getOrCreateAddressableNote(getFileServersAddress())
|
||||
|
||||
fun getFileServersAddress() = FileServersEvent.createAddress(signer.pubKey)
|
||||
|
||||
fun getFileServersListFlow(): StateFlow<NoteState> = fileStorageListNote.flow().metadata.stateFlow
|
||||
|
||||
fun getFileServersList(): FileServersEvent? = fileStorageListNote.event as? FileServersEvent
|
||||
|
||||
fun normalizeServers(note: Note): List<String> {
|
||||
val event = note.event as? FileServersEvent
|
||||
return event?.servers() ?: emptyList()
|
||||
}
|
||||
|
||||
val flow =
|
||||
getFileServersListFlow()
|
||||
.map { normalizeServers(it.note) }
|
||||
.onStart { emit(normalizeServers(fileStorageListNote)) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
emptyList(),
|
||||
)
|
||||
|
||||
suspend fun saveFileServersList(servers: List<String>): FileServersEvent {
|
||||
val serverList = getFileServersList()
|
||||
|
||||
val template =
|
||||
if (serverList != null && serverList.tags.isNotEmpty()) {
|
||||
FileServersEvent.replaceServers(serverList, servers)
|
||||
} else {
|
||||
FileServersEvent.build(servers)
|
||||
}
|
||||
|
||||
return signer.sign(template)
|
||||
}
|
||||
}
|
||||
+26
@@ -24,10 +24,14 @@ import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.NoteState
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||
import com.vitorpamplona.quartz.utils.Rfc3986
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -57,6 +61,13 @@ class BlossomServerListState(
|
||||
return event?.servers() ?: emptyList()
|
||||
}
|
||||
|
||||
fun host(url: String): String =
|
||||
try {
|
||||
Rfc3986.host(url).removePrefix("cdn.").removePrefix("blossom.")
|
||||
} catch (e: Exception) {
|
||||
url.removePrefix("cdn.").removePrefix("blossom.")
|
||||
}
|
||||
|
||||
val flow =
|
||||
getBlossomServersListFlow()
|
||||
.map { normalizeServers(it.note) }
|
||||
@@ -68,6 +79,21 @@ class BlossomServerListState(
|
||||
emptyList(),
|
||||
)
|
||||
|
||||
fun mergeServerList(blossom: List<String>?): List<ServerName> = blossom?.map { ServerName(host(it), it, ServerType.Blossom) } ?: emptyList()
|
||||
|
||||
val hostNameFlow: StateFlow<List<ServerName>> =
|
||||
flow
|
||||
.map { blossoms ->
|
||||
mergeServerList(blossoms)
|
||||
}.onStart {
|
||||
emit(mergeServerList(flow.value))
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
DEFAULT_MEDIA_SERVERS,
|
||||
)
|
||||
|
||||
suspend fun saveBlossomServersList(servers: List<String>): BlossomServersEvent {
|
||||
val serverList = getBlossomServersList()
|
||||
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ class TorSharedPreferences(
|
||||
videosViaTor = preferences[VIDEOS_VIA_TOR_KEY] ?: false,
|
||||
moneyOperationsViaTor = preferences[MONEY_OPERATIONS_VIA_TOR_KEY] ?: false,
|
||||
nip05VerificationsViaTor = preferences[NIP05_VERIFICATIONS_VIA_TOR_KEY] ?: false,
|
||||
nip96UploadsViaTor = preferences[MEDIA_UPLOADS_VIA_TOR_KEY] ?: false,
|
||||
mediaUploadsViaTor = preferences[MEDIA_UPLOADS_VIA_TOR_KEY] ?: false,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
@@ -125,7 +125,7 @@ class TorSharedPreferences(
|
||||
preferences[VIDEOS_VIA_TOR_KEY] = torSettings.videosViaTor
|
||||
preferences[MONEY_OPERATIONS_VIA_TOR_KEY] = torSettings.moneyOperationsViaTor
|
||||
preferences[NIP05_VERIFICATIONS_VIA_TOR_KEY] = torSettings.nip05VerificationsViaTor
|
||||
preferences[MEDIA_UPLOADS_VIA_TOR_KEY] = torSettings.nip96UploadsViaTor
|
||||
preferences[MEDIA_UPLOADS_VIA_TOR_KEY] = torSettings.mediaUploadsViaTor
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
|
||||
+2
-2
@@ -122,8 +122,8 @@ class RoleBasedHttpClientBuilder(
|
||||
fun shouldUseTorForUploads(url: String) =
|
||||
when (torSettings.torType.value) {
|
||||
TorType.OFF -> false
|
||||
TorType.INTERNAL -> checkLocalHostOnionAndThen(url, torSettings.nip96UploadsViaTor.value)
|
||||
TorType.EXTERNAL -> checkLocalHostOnionAndThen(url, torSettings.nip96UploadsViaTor.value)
|
||||
TorType.INTERNAL -> checkLocalHostOnionAndThen(url, torSettings.mediaUploadsViaTor.value)
|
||||
TorType.EXTERNAL -> checkLocalHostOnionAndThen(url, torSettings.mediaUploadsViaTor.value)
|
||||
}
|
||||
|
||||
override fun proxyPortForVideo(url: String): Int? = okHttpClient.getCurrentProxyPort(shouldUseTorForVideoDownload(url))
|
||||
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.serverList
|
||||
|
||||
import com.vitorpamplona.amethyst.model.nip96FileStorage.FileStorageServerListState
|
||||
import com.vitorpamplona.amethyst.model.nipB7Blossom.BlossomServerListState
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.quartz.utils.Rfc3986
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class MergedServerListState(
|
||||
val fileServers: FileStorageServerListState,
|
||||
val blossomServers: BlossomServerListState,
|
||||
val scope: CoroutineScope,
|
||||
) {
|
||||
fun host(url: String): String =
|
||||
try {
|
||||
Rfc3986.host(url)
|
||||
} catch (e: Exception) {
|
||||
url
|
||||
}
|
||||
|
||||
fun mergeServerList(
|
||||
nip96: List<String>?,
|
||||
blossom: List<String>?,
|
||||
): List<ServerName> {
|
||||
val nip96servers = nip96?.map { ServerName(host(it), it, ServerType.NIP96) } ?: emptyList()
|
||||
val blossomServers = blossom?.map { ServerName(host(it), it, ServerType.Blossom) } ?: emptyList()
|
||||
|
||||
val result = (nip96servers + blossomServers).ifEmpty { DEFAULT_MEDIA_SERVERS }
|
||||
|
||||
return result + ServerName("NIP95", "", ServerType.NIP95)
|
||||
}
|
||||
|
||||
val liveServerList: StateFlow<List<ServerName>> =
|
||||
combine(fileServers.flow, blossomServers.flow) { nip96s, blossoms ->
|
||||
mergeServerList(nip96s, blossoms)
|
||||
}.onStart {
|
||||
emit(mergeServerList(fileServers.flow.value, blossomServers.flow.value))
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
DEFAULT_MEDIA_SERVERS,
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
class MultiOrchestrator(
|
||||
uris: List<SelectedMedia>,
|
||||
) {
|
||||
|
||||
@@ -78,7 +78,6 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
@@ -264,9 +263,7 @@ fun EditPostView(
|
||||
accountViewModel.account.settings.defaultFileServer,
|
||||
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||
postViewModel.upload(alt, sensitiveContent, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = postViewModel::deleteMediaToUpload,
|
||||
onCancel = { postViewModel.multiOrchestrator = null },
|
||||
|
||||
@@ -54,7 +54,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
|
||||
@@ -110,9 +109,7 @@ fun NewMediaView(
|
||||
onPost = {
|
||||
postViewModel.upload(context, onClose, accountViewModel.toastManager::toast)
|
||||
postViewModel.selectedServer?.let {
|
||||
if (it.type != ServerType.NIP95) {
|
||||
account.settings.changeDefaultFileServer(it)
|
||||
}
|
||||
account.settings.changeDefaultFileServer(it)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -148,19 +145,14 @@ fun ImageVideoPost(
|
||||
postViewModel: NewMediaModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val nip95description = stringRes(id = R.string.upload_server_relays_nip95)
|
||||
val fileServers by accountViewModel.account.serverLists.liveServerList
|
||||
val fileServers by accountViewModel.account.blossomServers.hostNameFlow
|
||||
.collectAsState()
|
||||
|
||||
val fileServerOptions =
|
||||
remember(fileServers) {
|
||||
fileServers
|
||||
.map {
|
||||
if (it.type == ServerType.NIP95) {
|
||||
TitleExplainer(it.name, nip95description)
|
||||
} else {
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
|
||||
+3
-34
@@ -56,11 +56,7 @@ import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
|
||||
@Composable
|
||||
fun AllMediaBody(
|
||||
nip96ServersViewModel: NIP96ServersViewModel,
|
||||
blossomServersViewModel: BlossomServersViewModel,
|
||||
) {
|
||||
val nip96ServersState by nip96ServersViewModel.fileServers.collectAsStateWithLifecycle()
|
||||
fun AllMediaBody(blossomServersViewModel: BlossomServersViewModel) {
|
||||
val blossomServersState by blossomServersViewModel.fileServers.collectAsStateWithLifecycle()
|
||||
|
||||
LazyColumn(
|
||||
@@ -89,40 +85,15 @@ fun AllMediaBody(
|
||||
},
|
||||
)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.media_servers_nip96_section,
|
||||
R.string.media_servers_nip96_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
|
||||
renderMediaServerList(
|
||||
mediaServersState = nip96ServersState,
|
||||
keyType = "nip96",
|
||||
editLabel = R.string.add_a_nip96_server,
|
||||
emptyLabel = R.string.no_nip96_server_message,
|
||||
onAddServer = { server ->
|
||||
nip96ServersViewModel.addServer(server)
|
||||
},
|
||||
onDeleteServer = {
|
||||
nip96ServersViewModel.removeServer(serverUrl = it)
|
||||
},
|
||||
)
|
||||
|
||||
DEFAULT_MEDIA_SERVERS.let {
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
title = R.string.built_in_media_servers_title,
|
||||
title = R.string.recommended_media_servers,
|
||||
description = R.string.built_in_servers_description,
|
||||
modifier = SettingsCategorySpacingModifier,
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
nip96ServersViewModel.addServerList(
|
||||
it.mapNotNull { s -> if (s.type == ServerType.NIP96) s.baseUrl else null },
|
||||
)
|
||||
|
||||
blossomServersViewModel.addServerList(
|
||||
it.mapNotNull { s -> if (s.type == ServerType.Blossom) s.baseUrl else null },
|
||||
)
|
||||
@@ -142,9 +113,7 @@ fun AllMediaBody(
|
||||
serverEntry = server,
|
||||
isAmethystDefault = true,
|
||||
onAddOrDelete = { serverUrl ->
|
||||
if (server.type == ServerType.NIP96) {
|
||||
nip96ServersViewModel.addServer(serverUrl)
|
||||
} else if (server.type == ServerType.Blossom) {
|
||||
if (server.type == ServerType.Blossom) {
|
||||
blossomServersViewModel.addServer(serverUrl)
|
||||
}
|
||||
},
|
||||
|
||||
+2
-8
@@ -49,18 +49,15 @@ fun AllMediaServersScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val nip96ServersViewModel: NIP96ServersViewModel = viewModel()
|
||||
val blossomServersViewModel: BlossomServersViewModel = viewModel()
|
||||
|
||||
nip96ServersViewModel.init(accountViewModel)
|
||||
blossomServersViewModel.init(accountViewModel)
|
||||
|
||||
LaunchedEffect(key1 = accountViewModel) {
|
||||
nip96ServersViewModel.load()
|
||||
blossomServersViewModel.load()
|
||||
}
|
||||
|
||||
MediaServersScaffold(nip96ServersViewModel, blossomServersViewModel) {
|
||||
MediaServersScaffold(blossomServersViewModel) {
|
||||
nav.popBack()
|
||||
}
|
||||
}
|
||||
@@ -68,7 +65,6 @@ fun AllMediaServersScreen(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MediaServersScaffold(
|
||||
nip96ServersViewModel: NIP96ServersViewModel,
|
||||
blossomServersViewModel: BlossomServersViewModel,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
@@ -77,12 +73,10 @@ fun MediaServersScaffold(
|
||||
SavingTopBar(
|
||||
titleRes = R.string.media_servers,
|
||||
onCancel = {
|
||||
nip96ServersViewModel.refresh()
|
||||
blossomServersViewModel.refresh()
|
||||
onClose()
|
||||
},
|
||||
onPost = {
|
||||
nip96ServersViewModel.saveFileServers()
|
||||
blossomServersViewModel.saveFileServers()
|
||||
onClose()
|
||||
},
|
||||
@@ -111,7 +105,7 @@ fun MediaServersScaffold(
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
|
||||
AllMediaBody(nip96ServersViewModel, blossomServersViewModel)
|
||||
AllMediaBody(blossomServersViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions.mediaServers
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
@@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
class BlossomServersViewModel : ViewModel() {
|
||||
private lateinit var accountViewModel: AccountViewModel
|
||||
private lateinit var account: Account
|
||||
|
||||
+1
-5
@@ -41,11 +41,7 @@ fun FileServerSelectionRow(
|
||||
remember(fileServers) {
|
||||
fileServers
|
||||
.map {
|
||||
if (it.type == ServerType.NIP95) {
|
||||
TitleExplainer(it.name, nip95description)
|
||||
} else {
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions.mediaServers
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.Rfc3986
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NIP96ServersViewModel : ViewModel() {
|
||||
private lateinit var accountViewModel: AccountViewModel
|
||||
private lateinit var account: Account
|
||||
|
||||
private val _fileServers = MutableStateFlow<List<ServerName>>(emptyList())
|
||||
val fileServers = _fileServers.asStateFlow()
|
||||
private var isModified = false
|
||||
|
||||
fun init(accountViewModel: AccountViewModel) {
|
||||
this.accountViewModel = accountViewModel
|
||||
this.account = accountViewModel.account
|
||||
}
|
||||
|
||||
fun load() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
isModified = false
|
||||
_fileServers.update {
|
||||
val obtainedFileServers = obtainFileServers() ?: emptyList()
|
||||
obtainedFileServers.mapNotNull { serverUrl ->
|
||||
try {
|
||||
ServerName(
|
||||
Rfc3986.host(serverUrl),
|
||||
serverUrl,
|
||||
ServerType.NIP96,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.d("MediaServersViewModel", "Invalid URL in NIP-96 server list")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addServerList(serverList: List<String>) {
|
||||
serverList.forEach { serverUrl ->
|
||||
addServer(serverUrl)
|
||||
}
|
||||
}
|
||||
|
||||
fun addServer(serverUrl: String) {
|
||||
val normalizedUrl =
|
||||
try {
|
||||
Rfc3986.normalize(serverUrl.trim())
|
||||
} catch (e: Exception) {
|
||||
serverUrl
|
||||
}
|
||||
val serverNameReference =
|
||||
try {
|
||||
Rfc3986.host(normalizedUrl)
|
||||
} catch (e: Exception) {
|
||||
normalizedUrl
|
||||
}
|
||||
val serverRef = ServerName(serverNameReference, normalizedUrl, ServerType.NIP96)
|
||||
if (_fileServers.value.contains(serverRef)) {
|
||||
return
|
||||
} else {
|
||||
_fileServers.update {
|
||||
it.plus(serverRef)
|
||||
}
|
||||
}
|
||||
isModified = true
|
||||
}
|
||||
|
||||
fun removeServer(
|
||||
name: String = "",
|
||||
serverUrl: String,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val serverName = if (name.isNotBlank()) name else Rfc3986.host(serverUrl)
|
||||
_fileServers.update {
|
||||
it.minus(
|
||||
ServerName(serverName, serverUrl, ServerType.NIP96),
|
||||
)
|
||||
}
|
||||
isModified = true
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAllServers() {
|
||||
_fileServers.update { emptyList() }
|
||||
isModified = true
|
||||
}
|
||||
|
||||
fun saveFileServers() {
|
||||
if (isModified) {
|
||||
accountViewModel.launchSigner {
|
||||
val serverList = _fileServers.value.map { it.baseUrl }
|
||||
account.sendFileServersList(serverList)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun obtainFileServers(): List<String>? = account.fileStorageServers.flow.value
|
||||
}
|
||||
+10
-8
@@ -26,22 +26,24 @@ import kotlinx.serialization.Serializable
|
||||
data class ServerName(
|
||||
val name: String,
|
||||
val baseUrl: String,
|
||||
val type: ServerType = ServerType.NIP96,
|
||||
val type: ServerType = ServerType.Blossom,
|
||||
)
|
||||
|
||||
enum class ServerType {
|
||||
NIP96,
|
||||
NIP95,
|
||||
Blossom,
|
||||
NIP95,
|
||||
NIP96,
|
||||
}
|
||||
|
||||
val DEFAULT_MEDIA_SERVERS: List<ServerName> =
|
||||
listOf(
|
||||
ServerName("Nostr.Build", "https://nostr.build", ServerType.NIP96),
|
||||
ServerName("NostrCheck.me (NIP-96)", "https://nostrcheck.me", ServerType.NIP96),
|
||||
ServerName("Sovbit", "https://files.sovbit.host", ServerType.NIP96),
|
||||
ServerName("Satellite (Paid)", "https://cdn.satellite.earth", ServerType.Blossom),
|
||||
ServerName("NostrCheck.me (Blossom)", "https://cdn.nostrcheck.me", ServerType.Blossom),
|
||||
ServerName("Nostr.Build", "https://blossom.band/", ServerType.Blossom),
|
||||
ServerName("24242.io", "https://24242.io/", ServerType.Blossom),
|
||||
ServerName("Azzamo", "https://blossom.azzamo.media", ServerType.Blossom),
|
||||
ServerName("YakiHonne", "https://blossom.yakihonne.com/", ServerType.Blossom),
|
||||
ServerName("Primal", "https://blossom.primal.net/", ServerType.Blossom),
|
||||
ServerName("Sovbit", "https://cdn.sovbit.host", ServerType.Blossom),
|
||||
ServerName("Nostr.Download", "https://nostr.download", ServerType.Blossom),
|
||||
ServerName("Satellite (Paid)", "https://cdn.satellite.earth", ServerType.Blossom),
|
||||
ServerName("NostrMedia (Paid)", "https://nostrmedia.com", ServerType.Blossom),
|
||||
)
|
||||
|
||||
+3
-30
@@ -58,7 +58,6 @@ import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.TextSpinner
|
||||
@@ -83,38 +82,12 @@ fun ImageVideoDescription(
|
||||
onCancel: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
ImageVideoDescription(uris, defaultServer, true, onAdd, onDelete, onCancel, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ImageVideoDescription(
|
||||
uris: MultiOrchestrator,
|
||||
defaultServer: ServerName,
|
||||
includeNIP95: Boolean,
|
||||
onAdd: (String, ServerName, Boolean, Int, Boolean) -> Unit,
|
||||
onDelete: (SelectedMediaProcessing) -> Unit,
|
||||
onCancel: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val nip95description = stringRes(id = R.string.upload_server_relays_nip95)
|
||||
|
||||
val fileServers by accountViewModel.account.serverLists.liveServerList
|
||||
val fileServers by accountViewModel.account.blossomServers.hostNameFlow
|
||||
.collectAsState()
|
||||
|
||||
val fileServerOptions =
|
||||
remember(fileServers, includeNIP95) {
|
||||
fileServers
|
||||
.mapNotNull {
|
||||
if (it.type == ServerType.NIP95) {
|
||||
if (includeNIP95) {
|
||||
TitleExplainer(it.name, nip95description)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}
|
||||
}.toImmutableList()
|
||||
remember(fileServers) {
|
||||
fileServers.map { TitleExplainer(it.name, it.baseUrl) }.toImmutableList()
|
||||
}
|
||||
|
||||
var selectedServer by remember {
|
||||
|
||||
+1
-4
@@ -49,7 +49,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
@@ -287,9 +286,7 @@ private fun GenericCommentPostBody(
|
||||
accountViewModel.account.settings.defaultFileServer,
|
||||
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = postViewModel::deleteMediaToUpload,
|
||||
onCancel = { postViewModel.multiOrchestrator = null },
|
||||
|
||||
+1
-4
@@ -82,7 +82,6 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
@@ -284,9 +283,7 @@ fun GroupDMScreenContent(
|
||||
context,
|
||||
onceUploaded = { },
|
||||
)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = { postViewModel.uploadState?.deleteMediaToUpload(it) },
|
||||
onCancel = uploading::reset,
|
||||
|
||||
+1
-4
@@ -28,7 +28,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -70,9 +69,7 @@ fun RoomChatFileUploadDialog(
|
||||
onceUploaded = onUpload,
|
||||
)
|
||||
|
||||
if (state.selectedServer.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(state.selectedServer)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(state.selectedServer)
|
||||
},
|
||||
onCancel,
|
||||
accountViewModel,
|
||||
|
||||
+1
-4
@@ -36,7 +36,6 @@ import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
@@ -131,9 +130,7 @@ fun ChannelFileUploadDialog(
|
||||
onceUploaded = onUpload,
|
||||
)
|
||||
|
||||
if (state.selectedServer.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(state.selectedServer)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(state.selectedServer)
|
||||
},
|
||||
onCancel,
|
||||
accountViewModel,
|
||||
|
||||
+3
-8
@@ -55,7 +55,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
|
||||
import com.vitorpamplona.amethyst.ui.components.TextSpinner
|
||||
@@ -145,18 +144,14 @@ private fun ImageVideoPostChat(
|
||||
fileUploadState: ChatFileUploadState,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val fileServers by accountViewModel.account.serverLists.liveServerList
|
||||
val fileServers by accountViewModel.account.blossomServers.hostNameFlow
|
||||
.collectAsState()
|
||||
|
||||
val fileServerOptions =
|
||||
remember(fileServers) {
|
||||
fileServers
|
||||
.mapNotNull {
|
||||
if (it.type != ServerType.NIP95) {
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
.map {
|
||||
TitleExplainer(it.name, it.baseUrl)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -48,7 +48,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
@@ -255,12 +254,9 @@ private fun NewProductBody(
|
||||
ImageVideoDescription(
|
||||
uris = it,
|
||||
defaultServer = accountViewModel.account.settings.defaultFileServer,
|
||||
includeNIP95 = false,
|
||||
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = postViewModel::deleteMediaToUpload,
|
||||
onCancel = { postViewModel.multiOrchestrator = null },
|
||||
|
||||
+2
-5
@@ -62,7 +62,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.FileServerSelectionRow
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.MAX_VOICE_RECORD_SECONDS
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordVoiceButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
@@ -343,9 +342,7 @@ private fun NewPostScreenBody(
|
||||
accountViewModel.account.settings.defaultFileServer,
|
||||
onAdd = { alt, server, sensitiveContent, mediaQuality, useH265 ->
|
||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context, useH265)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = postViewModel::deleteMediaToUpload,
|
||||
onCancel = { postViewModel.multiOrchestrator = null },
|
||||
@@ -357,7 +354,7 @@ private fun NewPostScreenBody(
|
||||
// Show preview for both uploaded messages (voiceMetadata) and pending recordings
|
||||
(postViewModel.voiceMetadata ?: postViewModel.getVoicePreviewMetadata())?.let { metadata ->
|
||||
val fileServersState =
|
||||
accountViewModel.account.serverLists.liveServerList
|
||||
accountViewModel.account.blossomServers.hostNameFlow
|
||||
.collectAsState()
|
||||
val fileServers = fileServersState.value
|
||||
|
||||
|
||||
+2
-3
@@ -48,7 +48,6 @@ import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
|
||||
import com.vitorpamplona.amethyst.service.uploads.UploadingState
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordingResult
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
|
||||
@@ -563,8 +562,8 @@ open class ShortNotePostViewModel :
|
||||
return
|
||||
}
|
||||
// Update default server if voice message was successfully uploaded
|
||||
if (voiceSelectedServer != null && voiceSelectedServer?.type != ServerType.NIP95) {
|
||||
account.settings.changeDefaultFileServer(voiceSelectedServer!!)
|
||||
voiceSelectedServer?.let {
|
||||
account.settings.changeDefaultFileServer(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -36,6 +36,7 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -164,10 +165,10 @@ private fun VoiceReplyScreenBody(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Server selection
|
||||
val fileServers =
|
||||
accountViewModel.account.serverLists.liveServerList
|
||||
val fileServers by
|
||||
accountViewModel.account.blossomServers.hostNameFlow
|
||||
.collectAsState()
|
||||
.value
|
||||
|
||||
FileServerSelectionRow(
|
||||
fileServers = fileServers,
|
||||
selectedServer = viewModel.voiceSelectedServer ?: accountViewModel.account.settings.defaultFileServer,
|
||||
|
||||
+1
-4
@@ -33,7 +33,6 @@ import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
|
||||
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
|
||||
import com.vitorpamplona.amethyst.service.uploads.UploadingState
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordingResult
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationController
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoicePreset
|
||||
@@ -274,9 +273,7 @@ class VoiceReplyViewModel : ViewModel() {
|
||||
accountViewModel.account.signAndComputeBroadcast(template)
|
||||
}
|
||||
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
|
||||
deleteVoiceLocalFile()
|
||||
voiceAnonymization.deleteDistortedFiles()
|
||||
|
||||
+1
-4
@@ -58,7 +58,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton
|
||||
@@ -233,9 +232,7 @@ fun PublicMessageScreenContent(
|
||||
accountViewModel.account.settings.defaultFileServer,
|
||||
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||
if (server.type != ServerType.NIP95) {
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
}
|
||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||
},
|
||||
onDelete = postViewModel::deleteMediaToUpload,
|
||||
onCancel = { postViewModel.multiOrchestrator = null },
|
||||
|
||||
@@ -42,7 +42,7 @@ class TorDialogViewModel : ViewModel() {
|
||||
val videosViaTor = mutableStateOf(false)
|
||||
val moneyOperationsViaTor = mutableStateOf(false)
|
||||
val nip05VerificationsViaTor = mutableStateOf(false)
|
||||
val nip96UploadsViaTor = mutableStateOf(false)
|
||||
val mediaUploadsViaTor = mutableStateOf(false)
|
||||
|
||||
val preset =
|
||||
derivedStateOf {
|
||||
@@ -60,7 +60,7 @@ class TorDialogViewModel : ViewModel() {
|
||||
videosViaTor = videosViaTor.value,
|
||||
moneyOperationsViaTor = moneyOperationsViaTor.value,
|
||||
nip05VerificationsViaTor = nip05VerificationsViaTor.value,
|
||||
nip96UploadsViaTor = nip96UploadsViaTor.value,
|
||||
mediaUploadsViaTor = mediaUploadsViaTor.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class TorDialogViewModel : ViewModel() {
|
||||
videosViaTor.value = torSettings.videosViaTor
|
||||
moneyOperationsViaTor.value = torSettings.moneyOperationsViaTor
|
||||
nip05VerificationsViaTor.value = torSettings.nip05VerificationsViaTor
|
||||
nip96UploadsViaTor.value = torSettings.nip96UploadsViaTor
|
||||
mediaUploadsViaTor.value = torSettings.mediaUploadsViaTor
|
||||
}
|
||||
|
||||
fun save(): TorSettings =
|
||||
@@ -95,7 +95,7 @@ class TorDialogViewModel : ViewModel() {
|
||||
videosViaTor = videosViaTor.value,
|
||||
moneyOperationsViaTor = moneyOperationsViaTor.value,
|
||||
nip05VerificationsViaTor = nip05VerificationsViaTor.value,
|
||||
nip96UploadsViaTor = nip96UploadsViaTor.value,
|
||||
mediaUploadsViaTor = mediaUploadsViaTor.value,
|
||||
)
|
||||
|
||||
fun setPreset(preset: TorPresetType) {
|
||||
@@ -119,6 +119,6 @@ class TorDialogViewModel : ViewModel() {
|
||||
videosViaTor.value = torSettings.videosViaTor
|
||||
moneyOperationsViaTor.value = torSettings.moneyOperationsViaTor
|
||||
nip05VerificationsViaTor.value = torSettings.nip05VerificationsViaTor
|
||||
nip96UploadsViaTor.value = torSettings.nip96UploadsViaTor
|
||||
mediaUploadsViaTor.value = torSettings.mediaUploadsViaTor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ data class TorSettings(
|
||||
val videosViaTor: Boolean = false,
|
||||
val moneyOperationsViaTor: Boolean = false,
|
||||
val nip05VerificationsViaTor: Boolean = false,
|
||||
val nip96UploadsViaTor: Boolean = false,
|
||||
val mediaUploadsViaTor: Boolean = false,
|
||||
)
|
||||
|
||||
enum class TorType(
|
||||
@@ -90,7 +90,7 @@ fun isPreset(
|
||||
torSettings.videosViaTor == preset.videosViaTor &&
|
||||
torSettings.moneyOperationsViaTor == preset.moneyOperationsViaTor &&
|
||||
torSettings.nip05VerificationsViaTor == preset.nip05VerificationsViaTor &&
|
||||
torSettings.nip96UploadsViaTor == preset.nip96UploadsViaTor
|
||||
torSettings.mediaUploadsViaTor == preset.mediaUploadsViaTor
|
||||
|
||||
fun whichPreset(torSettings: TorSettings): TorPresetType {
|
||||
if (isPreset(torSettings, torOnlyWhenNeededPreset)) return TorPresetType.ONLY_WHEN_NEEDED
|
||||
@@ -112,7 +112,7 @@ val torOnlyWhenNeededPreset =
|
||||
videosViaTor = false,
|
||||
moneyOperationsViaTor = false,
|
||||
nip05VerificationsViaTor = false,
|
||||
nip96UploadsViaTor = false,
|
||||
mediaUploadsViaTor = false,
|
||||
)
|
||||
val torDefaultPreset =
|
||||
TorSettings(
|
||||
@@ -126,7 +126,7 @@ val torDefaultPreset =
|
||||
videosViaTor = false,
|
||||
moneyOperationsViaTor = false,
|
||||
nip05VerificationsViaTor = false,
|
||||
nip96UploadsViaTor = false,
|
||||
mediaUploadsViaTor = false,
|
||||
)
|
||||
val torSmallPayloadsPreset =
|
||||
TorSettings(
|
||||
@@ -140,7 +140,7 @@ val torSmallPayloadsPreset =
|
||||
videosViaTor = false,
|
||||
moneyOperationsViaTor = true,
|
||||
nip05VerificationsViaTor = true,
|
||||
nip96UploadsViaTor = false,
|
||||
mediaUploadsViaTor = false,
|
||||
)
|
||||
val torFullyPrivate =
|
||||
TorSettings(
|
||||
@@ -154,5 +154,5 @@ val torFullyPrivate =
|
||||
videosViaTor = true,
|
||||
moneyOperationsViaTor = true,
|
||||
nip05VerificationsViaTor = true,
|
||||
nip96UploadsViaTor = true,
|
||||
mediaUploadsViaTor = true,
|
||||
)
|
||||
|
||||
@@ -291,9 +291,9 @@ fun PrivacySettingsBody(dialogViewModel: TorDialogViewModel) {
|
||||
)
|
||||
|
||||
SwitchSettingsRow(
|
||||
R.string.tor_use_nip96_uploads,
|
||||
R.string.tor_use_nip96_uploads_explainer,
|
||||
dialogViewModel.nip96UploadsViaTor,
|
||||
R.string.tor_use_media_uploads,
|
||||
R.string.tor_use_media_uploads_explainer,
|
||||
dialogViewModel.mediaUploadsViaTor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class TorSettingsFlow(
|
||||
val videosViaTor: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
val moneyOperationsViaTor: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
val nip05VerificationsViaTor: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
val nip96UploadsViaTor: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
val mediaUploadsViaTor: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
) {
|
||||
// emits at every change in any of the properties.
|
||||
val propertyWatchFlow =
|
||||
@@ -54,7 +54,7 @@ class TorSettingsFlow(
|
||||
videosViaTor,
|
||||
moneyOperationsViaTor,
|
||||
nip05VerificationsViaTor,
|
||||
nip96UploadsViaTor,
|
||||
mediaUploadsViaTor,
|
||||
),
|
||||
) { flows ->
|
||||
TorSettings(
|
||||
@@ -88,7 +88,7 @@ class TorSettingsFlow(
|
||||
videosViaTor.value,
|
||||
moneyOperationsViaTor.value,
|
||||
nip05VerificationsViaTor.value,
|
||||
nip96UploadsViaTor.value,
|
||||
mediaUploadsViaTor.value,
|
||||
)
|
||||
|
||||
fun update(torSettings: TorSettings): Boolean {
|
||||
@@ -143,8 +143,8 @@ class TorSettingsFlow(
|
||||
nip05VerificationsViaTor.tryEmit(torSettings.nip05VerificationsViaTor)
|
||||
any = true
|
||||
}
|
||||
if (nip96UploadsViaTor.value != torSettings.nip96UploadsViaTor) {
|
||||
nip96UploadsViaTor.tryEmit(torSettings.nip96UploadsViaTor)
|
||||
if (mediaUploadsViaTor.value != torSettings.mediaUploadsViaTor) {
|
||||
mediaUploadsViaTor.tryEmit(torSettings.mediaUploadsViaTor)
|
||||
any = true
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ class TorSettingsFlow(
|
||||
MutableStateFlow(torSettings.videosViaTor),
|
||||
MutableStateFlow(torSettings.moneyOperationsViaTor),
|
||||
MutableStateFlow(torSettings.nip05VerificationsViaTor),
|
||||
MutableStateFlow(torSettings.nip96UploadsViaTor),
|
||||
MutableStateFlow(torSettings.mediaUploadsViaTor),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,8 +393,8 @@
|
||||
<string name="tor_use_money_operations">المعاملات المالية</string>
|
||||
<string name="tor_use_nip05_verification">التحقق من عنوان Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">أستخدام Tor عند التحقق من عناوين NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">رفع الوسائط</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">أستخدام Tor عند رفع الوسائط</string>
|
||||
<string name="tor_use_media_uploads">رفع الوسائط</string>
|
||||
<string name="tor_use_media_uploads_explainer">أستخدام Tor عند رفع الوسائط</string>
|
||||
<string name="tor_internal">مضمن</string>
|
||||
<string name="tor_off">إيقاف</string>
|
||||
<string name="tor_when_needed">أساسي</string>
|
||||
|
||||
@@ -591,8 +591,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Vynutit Tor na zaps, lightning a cashu převody</string>
|
||||
<string name="tor_use_nip05_verification">Ověření Nostr adresy</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Vynutit Tor při ověřování NIP-05 adres</string>
|
||||
<string name="tor_use_nip96_uploads">Nahrávání médií</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Vynutit Tor při nahrávání obsahu</string>
|
||||
<string name="tor_use_media_uploads">Nahrávání médií</string>
|
||||
<string name="tor_use_media_uploads_explainer">Vynutit Tor při nahrávání obsahu</string>
|
||||
<string name="tor_internal">Interní</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Vypnuto</string>
|
||||
|
||||
@@ -444,8 +444,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Vynutit Tor na zaps, lightning a cashu převody</string>
|
||||
<string name="tor_use_nip05_verification">Ověření Nostr adresy</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Vynutit Tor při ověřování NIP-05 adres</string>
|
||||
<string name="tor_use_nip96_uploads">Nahrávání médií</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Vynutit Tor při nahrávání obsahu</string>
|
||||
<string name="tor_use_media_uploads">Nahrávání médií</string>
|
||||
<string name="tor_use_media_uploads_explainer">Vynutit Tor při nahrávání obsahu</string>
|
||||
<string name="tor_internal">Interní</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Vypnuto</string>
|
||||
|
||||
@@ -596,8 +596,8 @@ anz der Bedingungen ist erforderlich</string>
|
||||
<string name="tor_use_money_operations_explainer">Erzwingen Sie Tor bei Zaps, Lightning und Cashu-Transfers</string>
|
||||
<string name="tor_use_nip05_verification">Nostr Adressverifizierung</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Erzwingen Sie Tor bei der Überprüfung von NIP-05-Adressen</string>
|
||||
<string name="tor_use_nip96_uploads">Medien-Uploads</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Erzwingen Sie Tor beim Hochladen von Inhalten</string>
|
||||
<string name="tor_use_media_uploads">Medien-Uploads</string>
|
||||
<string name="tor_use_media_uploads_explainer">Erzwingen Sie Tor beim Hochladen von Inhalten</string>
|
||||
<string name="tor_internal">Intern</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Aus</string>
|
||||
|
||||
@@ -449,8 +449,8 @@ anz der Bedingungen ist erforderlich</string>
|
||||
<string name="tor_use_money_operations_explainer">Erzwingen Sie Tor bei Zaps, Lightning und Cashu-Transfers</string>
|
||||
<string name="tor_use_nip05_verification">Nostr Adressverifizierung</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Erzwingen Sie Tor bei der Überprüfung von NIP-05-Adressen</string>
|
||||
<string name="tor_use_nip96_uploads">Medien-Uploads</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Erzwingen Sie Tor beim Hochladen von Inhalten</string>
|
||||
<string name="tor_use_media_uploads">Medien-Uploads</string>
|
||||
<string name="tor_use_media_uploads_explainer">Erzwingen Sie Tor beim Hochladen von Inhalten</string>
|
||||
<string name="tor_internal">Intern</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Aus</string>
|
||||
|
||||
@@ -452,7 +452,7 @@
|
||||
<string name="follow_set_type_public">Público</string>
|
||||
<string name="follow_set_type_private">Privado</string>
|
||||
<string name="follow_set_type_mixed">Mixto</string>
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
\nPulsa abajo para actualizar o pulsa los botones de añadir para crear uno nuevo.
|
||||
</string>
|
||||
<string name="follow_set_add_author_from_note_action">Añadir autor a conjunto de seguimiento</string>
|
||||
@@ -502,8 +502,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forzar Tor en transferencias de zaps, Lightning y Cashu</string>
|
||||
<string name="tor_use_nip05_verification">Verificación de dirección de Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forzar Tor al verificar direcciones NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_use_media_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_internal">Interno</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Desactivado</string>
|
||||
|
||||
@@ -447,7 +447,7 @@
|
||||
<string name="follow_set_type_public">Público</string>
|
||||
<string name="follow_set_type_private">Privado</string>
|
||||
<string name="follow_set_type_mixed">Mixto</string>
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
\nToca abajo para actualizar o toca los botones de agregar para crear uno nuevo.
|
||||
</string>
|
||||
<string name="follow_set_add_author_from_note_action">Agregar autor a conjunto de seguimiento</string>
|
||||
@@ -497,8 +497,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forzar Tor en transferencias de zaps, Lightning y Cashu</string>
|
||||
<string name="tor_use_nip05_verification">Verificación de dirección de Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forzar Tor al verificar direcciones NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_use_media_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_internal">Interno</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Desactivado</string>
|
||||
|
||||
@@ -447,7 +447,7 @@
|
||||
<string name="follow_set_type_public">Público</string>
|
||||
<string name="follow_set_type_private">Privado</string>
|
||||
<string name="follow_set_type_mixed">Mixto</string>
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
<string name="follow_set_empty_feed_msg"> Al parecer, aún no tienes ningún conjunto de seguimiento.
|
||||
\nToca abajo para actualizar o toca los botones de agregar para crear uno nuevo.
|
||||
</string>
|
||||
<string name="follow_set_add_author_from_note_action">Agregar autor a conjunto de seguimiento</string>
|
||||
@@ -497,8 +497,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forzar Tor en transferencias de zaps, Lightning y Cashu</string>
|
||||
<string name="tor_use_nip05_verification">Verificación de dirección de Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forzar Tor al verificar direcciones NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_use_media_uploads">Subidas multimedia</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forzar Tor al subir contenido</string>
|
||||
<string name="tor_internal">Interno</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Desactivado</string>
|
||||
|
||||
@@ -446,8 +446,8 @@
|
||||
<string name="tor_use_money_operations_explainer">تور را برای زپ، لایتنینگ و انتقال کَشو الزامی کن</string>
|
||||
<string name="tor_use_nip05_verification">تایید آدرس ناستر</string>
|
||||
<string name="tor_use_nip05_verification_explainer">تور را برای تایید آدرس های NIP-05 الزامی کن</string>
|
||||
<string name="tor_use_nip96_uploads">بارگذاری رسانه</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">تور را برای بارگذاری محتوا الزامی کن</string>
|
||||
<string name="tor_use_media_uploads">بارگذاری رسانه</string>
|
||||
<string name="tor_use_media_uploads_explainer">تور را برای بارگذاری محتوا الزامی کن</string>
|
||||
<string name="tor_internal">داخلی</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">خاموش</string>
|
||||
|
||||
@@ -428,8 +428,8 @@
|
||||
<string name="tor_use_money_operations_explainer">تور را برای زپ، لایتنینگ و انتقال کَشو الزامی کن</string>
|
||||
<string name="tor_use_nip05_verification">تایید آدرس ناستر</string>
|
||||
<string name="tor_use_nip05_verification_explainer">تور را برای تایید آدرس های NIP-05 الزامی کن</string>
|
||||
<string name="tor_use_nip96_uploads">بارگذاری رسانه</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">تور را برای بارگذاری محتوا الزامی کن</string>
|
||||
<string name="tor_use_media_uploads">بارگذاری رسانه</string>
|
||||
<string name="tor_use_media_uploads_explainer">تور را برای بارگذاری محتوا الزامی کن</string>
|
||||
<string name="tor_internal">داخلی</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">خاموش</string>
|
||||
|
||||
@@ -595,8 +595,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forcer Tor pour les transferts en zaps, lightning ou cashu</string>
|
||||
<string name="tor_use_nip05_verification">Vérification de l\'adresse Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forcer Tor lors de la vérification des adresses NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Téléversements de média</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forcer Tor lors du téléversement de contenu</string>
|
||||
<string name="tor_use_media_uploads">Téléversements de média</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forcer Tor lors du téléversement de contenu</string>
|
||||
<string name="tor_internal">Interne</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Désactivé</string>
|
||||
|
||||
@@ -440,8 +440,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forcer Tor pour les transferts en zaps, lightning ou cashu</string>
|
||||
<string name="tor_use_nip05_verification">Vérification de l\'adresse Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forcer Tor lors de la vérification des adresses NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Téléversements de média</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forcer Tor lors du téléversement de contenu</string>
|
||||
<string name="tor_use_media_uploads">Téléversements de média</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forcer Tor lors du téléversement de contenu</string>
|
||||
<string name="tor_internal">Interne</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Désactivé</string>
|
||||
|
||||
@@ -595,8 +595,8 @@
|
||||
<string name="tor_use_money_operations_explainer">ज्साप लैटनिंग तथा काशयु व्यापार के लिए टोर का प्रयोग अवश्य करें</string>
|
||||
<string name="tor_use_nip05_verification">नोस्ट्र पता सत्यापन</string>
|
||||
<string name="tor_use_nip05_verification_explainer">निप॰-०५ पता सत्यापन के लिए टोर का प्रयोग अवश्य करें</string>
|
||||
<string name="tor_use_nip96_uploads">चित्र चलचित्र अभिलेख आरोहण</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">चित्र चलचित्र अभिलेख आरोहण के लिए टोर का प्रयोग अवश्य करें</string>
|
||||
<string name="tor_use_media_uploads">चित्र चलचित्र अभिलेख आरोहण</string>
|
||||
<string name="tor_use_media_uploads_explainer">चित्र चलचित्र अभिलेख आरोहण के लिए टोर का प्रयोग अवश्य करें</string>
|
||||
<string name="tor_internal">आन्तरीय</string>
|
||||
<string name="tor_external">ओर्बोट</string>
|
||||
<string name="tor_off">निष्क्रिय</string>
|
||||
|
||||
@@ -595,8 +595,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Tor kényszerítése Zap küldésekor, lightning és cashu tranzakciókhoz</string>
|
||||
<string name="tor_use_nip05_verification">Nostr-cím hitelesítése</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Tor kényszerítése a NIP-05 címek hitelesítésekor</string>
|
||||
<string name="tor_use_nip96_uploads">Média-feltöltések</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Tor kényszerítése média feltöltésekor</string>
|
||||
<string name="tor_use_media_uploads">Média-feltöltések</string>
|
||||
<string name="tor_use_media_uploads_explainer">Tor kényszerítése média feltöltésekor</string>
|
||||
<string name="tor_internal">Beépített</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Kikapcsolva</string>
|
||||
|
||||
@@ -446,8 +446,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Tor kényszerítése Zap küldésekor, lightning és cashu tranzakciókhoz</string>
|
||||
<string name="tor_use_nip05_verification">Nostr-cím hitelesítése</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Tor kényszerítése a NIP-05 címek hitelesítésekor</string>
|
||||
<string name="tor_use_nip96_uploads">Média-feltöltések</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Tor kényszerítése média feltöltésekor</string>
|
||||
<string name="tor_use_media_uploads">Média-feltöltések</string>
|
||||
<string name="tor_use_media_uploads_explainer">Tor kényszerítése média feltöltésekor</string>
|
||||
<string name="tor_internal">Beépített</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Kikapcsolva</string>
|
||||
|
||||
@@ -537,8 +537,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forceer Tor op zaps, lightning en cashu transfers</string>
|
||||
<string name="tor_use_nip05_verification">Nostr Adres verificatie</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forceer Tor bij het verifiëren van NIP-05 adressen</string>
|
||||
<string name="tor_use_nip96_uploads">Mediauploads</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forceer Tor bij het uploaden van content</string>
|
||||
<string name="tor_use_media_uploads">Mediauploads</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forceer Tor bij het uploaden van content</string>
|
||||
<string name="tor_internal">Intern</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Uit</string>
|
||||
|
||||
@@ -439,8 +439,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forceer Tor op zaps, lightning en cashu transfers</string>
|
||||
<string name="tor_use_nip05_verification">Nostr Adres verificatie</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forceer Tor bij het verifiëren van NIP-05 adressen</string>
|
||||
<string name="tor_use_nip96_uploads">Mediauploads</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forceer Tor bij het uploaden van content</string>
|
||||
<string name="tor_use_media_uploads">Mediauploads</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forceer Tor bij het uploaden van content</string>
|
||||
<string name="tor_internal">Intern</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Uit</string>
|
||||
|
||||
@@ -592,8 +592,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Wymuś Tor przy zapsach, transferach lightning and cashu</string>
|
||||
<string name="tor_use_nip05_verification">Weryfikacja adresu Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Wymuś Tor podczas weryfikacji adresów NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Wgrywanie mediów</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Wymuś Tor podczas wgrywania zawartości</string>
|
||||
<string name="tor_use_media_uploads">Wgrywanie mediów</string>
|
||||
<string name="tor_use_media_uploads_explainer">Wymuś Tor podczas wgrywania zawartości</string>
|
||||
<string name="tor_internal">Wewnętrznie</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Wył.</string>
|
||||
|
||||
@@ -591,8 +591,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Forçar Tor em zaps, transfers relâmpago e cashu</string>
|
||||
<string name="tor_use_nip05_verification">Verificação de Endereço Nostr</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Forçar Tor ao verificar endereços NIP-05</string>
|
||||
<string name="tor_use_nip96_uploads">Envio de Mídia</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Forçar Tor ao enviar conteúdo</string>
|
||||
<string name="tor_use_media_uploads">Envio de Mídia</string>
|
||||
<string name="tor_use_media_uploads_explainer">Forçar Tor ao enviar conteúdo</string>
|
||||
<string name="tor_internal">Interno</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Desativado</string>
|
||||
|
||||
@@ -606,8 +606,8 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem</string>
|
||||
<string name="tor_use_money_operations_explainer">Prisili tor pri Zapih, Lightning in Cashu plačilnih prenosih</string>
|
||||
<string name="tor_use_nip05_verification">Preveritev Nostr naslova</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Prisili tor ob preverjanju NIP-05 naslovov</string>
|
||||
<string name="tor_use_nip96_uploads">Nalaganje medijske vsebine</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Prisili tor pri nalaganju vsebine</string>
|
||||
<string name="tor_use_media_uploads">Nalaganje medijske vsebine</string>
|
||||
<string name="tor_use_media_uploads_explainer">Prisili tor pri nalaganju vsebine</string>
|
||||
<string name="tor_internal">Interno</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Izključeno</string>
|
||||
|
||||
@@ -590,8 +590,8 @@
|
||||
<string name="tor_use_money_operations_explainer">Tvinga Tor på zaps, lightning och cashu överföringar</string>
|
||||
<string name="tor_use_nip05_verification">Nostr Adress Verifiering</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Tvinga Tor vid verifiering av NIP-05 adresser</string>
|
||||
<string name="tor_use_nip96_uploads">Mediauppladdningar</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Tvinga Tor vid uppladdning av innehåll</string>
|
||||
<string name="tor_use_media_uploads">Mediauppladdningar</string>
|
||||
<string name="tor_use_media_uploads_explainer">Tvinga Tor vid uppladdning av innehåll</string>
|
||||
<string name="tor_internal">Intern</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">Av</string>
|
||||
|
||||
@@ -595,8 +595,8 @@
|
||||
<string name="tor_use_money_operations_explainer">强制为 Zap、闪电网络和 Cashu 转账时使用 Tor</string>
|
||||
<string name="tor_use_nip05_verification">Nostr 地址验证</string>
|
||||
<string name="tor_use_nip05_verification_explainer">强制为 NIP-05 地址验证使用 Tor</string>
|
||||
<string name="tor_use_nip96_uploads">媒体上传</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">强制为上传媒体内容时使用 Tor</string>
|
||||
<string name="tor_use_media_uploads">媒体上传</string>
|
||||
<string name="tor_use_media_uploads_explainer">强制为上传媒体内容时使用 Tor</string>
|
||||
<string name="tor_internal">内置</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
<string name="tor_off">关闭</string>
|
||||
|
||||
@@ -535,6 +535,7 @@
|
||||
<string name="no_nip96_server_message">You have no NIP-96 servers set. You can use Amethyst\'s list, or add one below ↓</string>
|
||||
<string name="no_blossom_server_message">You have no Blossom servers set. You can use Amethyst\'s list, or add one below ↓</string>
|
||||
|
||||
<string name="recommended_media_servers">Recommended Media Servers</string>
|
||||
<string name="built_in_media_servers_title">Built-in Media Servers</string>
|
||||
<string name="built_in_servers_description">Amethyst\'s default list. You can add them individually or add the list.</string>
|
||||
<string name="use_default_servers">Use Default List</string>
|
||||
@@ -680,8 +681,8 @@
|
||||
<string name="tor_use_nip05_verification">Nostr Address Verification</string>
|
||||
<string name="tor_use_nip05_verification_explainer">Force Tor when verifying NIP-05 addresses</string>
|
||||
|
||||
<string name="tor_use_nip96_uploads">Media Uploads</string>
|
||||
<string name="tor_use_nip96_uploads_explainer">Force Tor when uploading content</string>
|
||||
<string name="tor_use_media_uploads">Media Uploads</string>
|
||||
<string name="tor_use_media_uploads_explainer">Force Tor when uploading content</string>
|
||||
|
||||
<string name="tor_internal">Internal</string>
|
||||
<string name="tor_external">Orbot</string>
|
||||
|
||||
Reference in New Issue
Block a user