Merge branch 'vitorpamplona:main' into profiles-list-management

This commit is contained in:
KotlinGeekDev
2025-03-31 17:50:33 +00:00
committed by GitHub
14 changed files with 244 additions and 41 deletions
@@ -71,6 +71,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.concurrent.atomic.AtomicBoolean
@Stable
class ServiceManager(
@@ -84,6 +85,8 @@ class ServiceManager(
private var collectorJob: Job? = null
var isTrimmingMemoryMutex = AtomicBoolean(false)
private fun start(account: Account) {
this.account = account
start()
@@ -103,10 +106,21 @@ class ServiceManager(
if (myAccount != null) {
when (myAccount.settings.torSettings.torType.value) {
TorType.INTERNAL -> {
Log.d("TorManager", "Relays Service Connected ${TorManager.socksPort()}")
HttpClientManager.setDefaultProxyOnPort(TorManager.socksPort())
// Tor's lib will automatically set this port.
if (TorManager.isSocksReady()) {
HttpClientManager.setDefaultProxyOnPort(TorManager.socksPort())
} else {
HttpClientManager.setProxyNotReady()
}
}
TorType.EXTERNAL -> {
val port = myAccount.settings.torSettings.externalSocksPort.value
if (port > 0) {
HttpClientManager.setDefaultProxyOnPort(port)
} else {
HttpClientManager.setProxyNotReady()
}
}
TorType.EXTERNAL -> HttpClientManager.setDefaultProxyOnPort(myAccount.settings.torSettings.externalSocksPort.value)
else -> HttpClientManager.setDefaultProxy(null)
}
@@ -248,20 +262,26 @@ class ServiceManager(
}
suspend fun trimMemory() {
LocalCache.cleanObservers()
if (isTrimmingMemoryMutex.compareAndSet(false, true)) {
try {
LocalCache.cleanObservers()
val accounts =
LocalPreferences.allSavedAccounts().mapNotNull { decodePublicKeyAsHexOrNull(it.npub) }.toSet()
val accounts =
LocalPreferences.allSavedAccounts().mapNotNull { decodePublicKeyAsHexOrNull(it.npub) }.toSet()
account?.let {
LocalCache.pruneOldAndHiddenMessages(it)
NostrChatroomDataSource.clearEOSEs(it)
account?.let {
LocalCache.pruneOldAndHiddenMessages(it)
NostrChatroomDataSource.clearEOSEs(it)
LocalCache.pruneHiddenMessages(it)
LocalCache.pruneContactLists(accounts)
LocalCache.pruneRepliesAndReactions(accounts)
LocalCache.prunePastVersionsOfReplaceables()
LocalCache.pruneExpiredEvents()
LocalCache.pruneHiddenMessages(it)
LocalCache.pruneContactLists(accounts)
LocalCache.pruneRepliesAndReactions(accounts)
LocalCache.prunePastVersionsOfReplaceables()
LocalCache.pruneExpiredEvents()
}
} finally {
isTrimmingMemoryMutex.getAndSet(false)
}
}
}
@@ -36,7 +36,6 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
import com.vitorpamplona.amethyst.service.uploads.FileHeader
import com.vitorpamplona.amethyst.tryAndWait
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
@@ -3667,12 +3666,6 @@ class Account(
fun markDonatedInThisVersion() = settings.markDonatedInThisVersion(BuildConfig.VERSION_NAME)
fun httpClientForCoil() = HttpClientManager.getHttpClient(shouldUseTorForImageDownload())
fun httpClientForRelay(dirtyUrl: String) = HttpClientManager.getHttpClient(shouldUseTorForDirty(dirtyUrl))
fun httpClientForPreviewUrl(url: String) = HttpClientManager.getHttpClient(shouldUseTorForPreviewUrl(url))
fun shouldUseTorForImageDownload() =
when (settings.torSettings.torType.value) {
TorType.OFF -> false
@@ -20,6 +20,7 @@
*/
package com.vitorpamplona.amethyst.service.okhttp
import android.R.attr.port
import android.util.Log
import com.vitorpamplona.quartz.nip17Dm.files.encryption.NostrCipher
import okhttp3.OkHttpClient
@@ -35,6 +36,8 @@ object HttpClientManager {
.followSslRedirects(true)
.build()
val DEFAULT_TOR_PROXY = Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 9050))
val DEFAULT_TIMEOUT_ON_WIFI: Duration = Duration.ofSeconds(10L)
val DEFAULT_TIMEOUT_ON_MOBILE: Duration = Duration.ofSeconds(30L)
@@ -43,13 +46,13 @@ object HttpClientManager {
private var defaultHttpClientWithoutProxy: OkHttpClient? = null
private var userAgent: String = "Amethyst"
private var currentProxy: Proxy? = null
private var currentProxy: Proxy? = DEFAULT_TOR_PROXY
private val cache = EncryptionKeyCache()
fun setDefaultProxy(proxy: Proxy?) {
if (currentProxy != proxy) {
Log.d("HttpClient", "Changing proxy to: ${proxy != null}")
Log.d("HttpClient", "Changing proxy to: $proxy")
currentProxy = proxy
// recreates singleton
@@ -117,6 +120,11 @@ object HttpClientManager {
defaultHttpClientWithoutProxy!!
}
fun setProxyNotReady() {
// this blocks all connections unless Orbot is live.
setDefaultProxy(DEFAULT_TOR_PROXY)
}
fun setDefaultProxyOnPort(port: Int) {
setDefaultProxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", port)))
}
@@ -46,7 +46,6 @@ import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
@@ -112,8 +111,6 @@ class MainActivity : AppCompatActivity() {
checkLanguage(locales.get(0).language)
}
TorManager.startTor(this)
Log.d("Lifetime Event", "MainActivity.onResume")
// starts muted every time
@@ -160,8 +157,6 @@ class MainActivity : AppCompatActivity() {
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
.unregisterNetworkCallback(networkCallback)
TorManager.stopTor(this)
super.onPause()
}
@@ -50,6 +50,7 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextFieldDefaults.contentPadding
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -246,7 +247,9 @@ private fun DialogContent(
val writeStoragePermissionState =
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
if (isGranted) {
saveMediaToGallery(myContent, localContext, accountViewModel)
scope.launch {
saveMediaToGallery(myContent, localContext, accountViewModel)
}
scope.launch {
Toast
.makeText(
@@ -264,7 +267,9 @@ private fun DialogContent(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
writeStoragePermissionState.status.isGranted
) {
saveMediaToGallery(myContent, localContext, accountViewModel)
scope.launch {
saveMediaToGallery(myContent, localContext, accountViewModel)
}
scope.launch {
Toast
.makeText(
@@ -39,13 +39,16 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -58,8 +61,13 @@ import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun AccountScreen(
@@ -136,6 +144,8 @@ fun LoggedInPage(
accountViewModel.restartServices()
}
ManageTorInstance(accountViewModel)
ListenToExternalSignerIfNeeded(accountViewModel)
AppNavigation(
@@ -145,6 +155,55 @@ fun LoggedInPage(
)
}
@Composable
fun ManageTorInstance(accountViewModel: AccountViewModel) {
val torSettings by accountViewModel.account.settings.torSettings.torType
.collectAsStateWithLifecycle()
if (torSettings == TorType.INTERNAL) {
ManageTorInstanceInner(accountViewModel)
}
}
@Composable
fun ManageTorInstanceInner(accountViewModel: AccountViewModel) {
val context = LocalContext.current.applicationContext
val lifeCycleOwner = LocalLifecycleOwner.current
val scope = rememberCoroutineScope()
var job = remember<Job?> { null }
DisposableEffect(key1 = accountViewModel) {
job?.cancel()
job = null
TorManager.startTorIfNotAlreadyOn(context)
val observer =
LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
job?.cancel()
job = null
TorManager.startTorIfNotAlreadyOn(context)
}
Lifecycle.Event.ON_PAUSE -> {
job =
scope.launch {
delay(5000) // 5 seconds
TorManager.stopTor(context)
}
}
else -> {}
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
TorManager.stopTor(context)
}
}
}
@Composable
private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
if (accountViewModel.account.signer is NostrSignerExternal) {
@@ -30,20 +30,40 @@ import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
import org.torproject.jni.TorService
import org.torproject.jni.TorService.LocalBinder
import java.util.concurrent.atomic.AtomicBoolean
object TorManager {
var runningIntent: Intent? = null
var torService: TorService? = null
// To make sure we don't start two services.
var isConnectingMutex = AtomicBoolean(false)
fun startTorIfNotAlreadyOn(ctx: Context) {
if (torService == null) {
startTor(ctx)
if (runningIntent == null && isConnectingMutex.compareAndSet(false, true)) {
try {
startTor(ctx)
} finally {
isConnectingMutex.set(false)
}
}
}
fun startTor(ctx: Context) {
fun stopTor(ctx: Context) {
Log.d("TorManager", "Stopping Tor Service")
runningIntent?.let {
ctx.stopService(runningIntent)
}
runningIntent = null
torService = null
}
private fun startTor(ctx: Context) {
Log.d("TorManager", "Binding Tor Service")
val currentIntent = Intent(ctx, TorService::class.java)
runningIntent = currentIntent
ctx.bindService(
Intent(ctx, TorService::class.java),
currentIntent,
object : ServiceConnection {
override fun onServiceConnected(
name: ComponentName,
@@ -66,6 +86,7 @@ object TorManager {
}
override fun onServiceDisconnected(name: ComponentName) {
runningIntent = null
torService = null
Log.d("TorManager", "Tor Service Disconected")
}
@@ -74,12 +95,6 @@ object TorManager {
)
}
fun stopTor(ctx: Context) {
Log.d("TorManager", "Stopping Tor Service")
torService = null
ctx.stopService(Intent(ctx, TorService::class.java))
}
fun isSocksReady() =
torService?.let {
it.socksPort > 0
@@ -98,6 +98,7 @@
<string name="description">Popis</string>
<string name="about_us">"O nás.. "</string>
<string name="what_s_on_your_mind">Co vás napadá?</string>
<string name="write_a_message">Napište zprávu...</string>
<string name="post">Příspěvek</string>
<string name="save">Uložit</string>
<string name="create">Vytvořit</string>
@@ -205,6 +206,7 @@
<string name="with_description_of">s popisem</string>
<string name="and_picture">a obrázkem</string>
<string name="changed_chat_name_to">změnil název chatu na</string>
<string name="changed_chat_profile_to">Nový chatový profil:</string>
<string name="description_to">popis na</string>
<string name="and_picture_to">a obrázek na</string>
<string name="leave">Opustit</string>
@@ -524,6 +526,7 @@
<string name="cashu_redeem_to_zap">Poslat do Zap peněženky</string>
<string name="cashu_redeem_to_cashu">Otevřít v Cashu Wallet</string>
<string name="cashu_copy_token">Kopírovat token</string>
<string name="quick_actions_open_in_another_app">Otevřít v jiné aplikaci</string>
<string name="no_lightning_address_set">Není nastavena žádná Lightning adresa</string>
<string name="copied_token_to_clipboard">Token zkopírován do schránky</string>
<string name="live_stream_live_tag">ŽIVĚ</string>
@@ -603,6 +606,8 @@
<string name="messages_new_message_subject">Předmět</string>
<string name="messages_new_message_subject_caption">Téma konverzace</string>
<string name="messages_new_message_to_caption">"\@Uživatel1, @Uživatel2, @Uživatel3"</string>
<string name="messages_cant_upload_title">Nelze nahrát</string>
<string name="messages_cant_upload_explainer">Nejdříve vložte cíl zprávy</string>
<string name="messages_group_descriptor">Členové této skupiny</string>
<string name="messages_new_subject_message">Vysvětlení členům</string>
<string name="messages_new_subject_message_placeholder">Změna názvu pro nové cíle.</string>
@@ -936,4 +941,5 @@
<string name="torrent_no_apps">Pro otevření a stažení souboru nejsou nainstalovány žádné torrent aplikace.</string>
<string name="select_list_to_filter">Vyberte seznam pro filtrování kanálu</string>
<string name="temporary_account">Odhlásit se na zámek zařízení</string>
<string name="private_message">Soukromá zpráva</string>
</resources>
@@ -98,6 +98,7 @@
<string name="description">Beschreibung</string>
<string name="about_us">"Über uns.. "</string>
<string name="what_s_on_your_mind">Was beschäftigt dich?</string>
<string name="write_a_message">Eine Nachricht schreiben...</string>
<string name="post">Beitrag</string>
<string name="save">Speichern</string>
<string name="create">Erstellen</string>
@@ -209,6 +210,7 @@ anz der Bedingungen ist erforderlich</string>
<string name="with_description_of">mit Beschreibung von</string>
<string name="and_picture">und Bild</string>
<string name="changed_chat_name_to">Chat-Namen geändert zu</string>
<string name="changed_chat_profile_to">Neues Chat-Profil:</string>
<string name="description_to">Beschreibung zu</string>
<string name="and_picture_to">und Bild zu</string>
<string name="leave">Verlassen</string>
@@ -529,6 +531,7 @@ anz der Bedingungen ist erforderlich</string>
<string name="cashu_redeem_to_zap">An Zap Wallet senden</string>
<string name="cashu_redeem_to_cashu">In Cashu Wallet öffnen</string>
<string name="cashu_copy_token">Token kopieren</string>
<string name="quick_actions_open_in_another_app">In anderer App öffnen</string>
<string name="no_lightning_address_set">Keine Lightning-Adresse festgelegt</string>
<string name="copied_token_to_clipboard">Token in die Zwischenablage kopiert</string>
<string name="live_stream_live_tag">LIVE</string>
@@ -608,6 +611,8 @@ anz der Bedingungen ist erforderlich</string>
<string name="messages_new_message_subject">Betreff</string>
<string name="messages_new_message_subject_caption">Gesprächsthema</string>
<string name="messages_new_message_to_caption">"\@Benutzer1, @Benutzer2, @Benutzer3"</string>
<string name="messages_cant_upload_title">Kann nicht hochladen</string>
<string name="messages_cant_upload_explainer">Füge das Ziel der Nachricht zuerst ein</string>
<string name="messages_group_descriptor">Mitglieder dieser Gruppe</string>
<string name="messages_new_subject_message">Erklärung an Mitglieder</string>
<string name="messages_new_subject_message_placeholder">Ändern des Namens für die neuen Ziele.</string>
@@ -941,4 +946,5 @@ anz der Bedingungen ist erforderlich</string>
<string name="torrent_no_apps">Keine Torrent-Apps installiert, um die Datei zu öffnen und herunterzuladen.</string>
<string name="select_list_to_filter">Liste zum Filtern des Feeds auswählen</string>
<string name="temporary_account">Beim Sperren des Geräts abmelden</string>
<string name="private_message">Private Nachricht</string>
</resources>
@@ -20,6 +20,7 @@
<string name="impersonation">Suplantación de identidad</string>
<string name="illegal_behavior">Comportamiento ilegal</string>
<string name="other">Otro</string>
<string name="harassment">Acoso</string>
<string name="unknown">Desconocido</string>
<string name="relay_icon">Icono del transmisor</string>
<string name="unknown_author">Autor desconocido</string>
@@ -81,6 +82,13 @@
<string name="thank_you_so_much">¡Muchas gracias!</string>
<string name="amount_in_sats">Cantidad en sats</string>
<string name="send_sats">Enviar sats</string>
<string name="secret_emoji_maker">Creador de emojis secretos</string>
<string name="secret_emoji_maker_explainer">Agrega un emoji con mensaje oculto para publicar.</string>
<string name="secret_note_to_receiver">Nota secreta al destinatario</string>
<string name="secret_note_to_receiver_placeholder">Mi mensaje oculto</string>
<string name="secret_visible_text">Prefijo visible</string>
<string name="secret_visible_text_placeholder">😎</string>
<string name="secret_add_to_text">Agregar a publicación</string>
<string name="error_parsing_preview_for">"Error al analizar la vista previa de %1$s : %2$s"</string>
<string name="preview_card_image_for">"Imagen de vista previa para %1$s"</string>
<string name="new_channel">Nuevo canal</string>
@@ -90,6 +98,7 @@
<string name="description">Descripción</string>
<string name="about_us">"Sobre nosotros… "</string>
<string name="what_s_on_your_mind">¿Qué tienes en mente?</string>
<string name="write_a_message">Escribir un mensaje...</string>
<string name="post">Publicar</string>
<string name="save">Guardar</string>
<string name="create">Crear</string>
@@ -106,6 +115,7 @@
<string name="global_feed">Fuente global</string>
<string name="search_feed">Fuente de búsqueda</string>
<string name="add_a_relay">Añadir transmisor</string>
<string name="profile_name">Nombre</string>
<string name="display_name">Nombre para mostrar</string>
<string name="my_display_name">Mi nombre para mostrar</string>
<string name="my_awesome_name">Avestruz Maravillosa</string>
@@ -141,6 +151,7 @@
<string name="conversations">Conversaciones</string>
<string name="notes">Notas</string>
<string name="replies">Respuestas</string>
<string name="mutual">Tuyo</string>
<string name="gallery">Galería</string>
<string name="follows">"Siguiendo"</string>
<string name="reports">"Reportes"</string>
@@ -195,6 +206,7 @@
<string name="with_description_of">con descripción de</string>
<string name="and_picture">y foto</string>
<string name="changed_chat_name_to">cambió el nombre del chat a</string>
<string name="changed_chat_profile_to">Nuevo perfil de chat:</string>
<string name="description_to">descripción a</string>
<string name="and_picture_to">e imagen a</string>
<string name="leave">Salir</string>
@@ -202,6 +214,13 @@
<string name="channel_created">Canal creado</string>
<string name="channel_information_changed_to">"La información del canal cambió a"</string>
<string name="public_chat">Chat público</string>
<string name="public_chat_title">Metadatos públicos del chat</string>
<string name="public_chat_explainer">Los chats públicos son visibles para todos en Nostr y cualquiera
puede participar en ellos. Son ideales para comunidades abiertas alrededor de temas específicos.
La moderación puede controlarse eliminando publicaciones en sus relés.</string>
<string name="public_chat_relays_title">Relés</string>
<string name="public_chat_relays_explainer">Inserta entre 1 y 3 relés que alojan este grupo.
Los clientes de Nostr utilizan esta configuración para saber de dónde descargar y adónde enviar los mensajes.</string>
<string name="posts_received">publicaciones recibidas</string>
<string name="remove">Eliminar</string>
<string name="translations_auto">Automático</string>
@@ -509,6 +528,7 @@
<string name="cashu_redeem_to_zap">Enviar a monedero de zaps</string>
<string name="cashu_redeem_to_cashu">Abrir en monedero de Cashu</string>
<string name="cashu_copy_token">Copiar token</string>
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
<string name="no_lightning_address_set">La dirección de Lightning no se ha configurado</string>
<string name="copied_token_to_clipboard">Token copiado al portapapeles</string>
<string name="live_stream_live_tag">EN VIVO</string>
@@ -539,6 +559,8 @@
<string name="ui_feature_set_type_complete">Completo</string>
<string name="ui_feature_set_type_simplified">Simplificado</string>
<string name="ui_feature_set_type_performance">Rendimiento</string>
<string name="gallery_type_classic">Clásico</string>
<string name="gallery_type_modern">Moderno</string>
<string name="system">Sistema</string>
<string name="light">Claro</string>
<string name="dark">Oscuro</string>
@@ -553,6 +575,8 @@
<string name="automatically_hide_nav_bars_description">Ocultar barras de navegación al desplazarse</string>
<string name="ui_style">Modo de interfaz</string>
<string name="ui_style_description">Elegir el estilo de publicación</string>
<string name="gallery_style">Estilo de galería de perfil</string>
<string name="gallery_style_description">Elige el estilo de galería</string>
<string name="load_image">Cargar imagen</string>
<string name="spamming_users">Spammers</string>
<string name="muted_button">Silenciado. Hacer clic para reactivar el sonido.</string>
@@ -584,6 +608,8 @@
<string name="messages_new_message_subject">Asunto</string>
<string name="messages_new_message_subject_caption">Tema de la conversación</string>
<string name="messages_new_message_to_caption">"\@Usuario1, @Usuario2, @Usuario3"</string>
<string name="messages_cant_upload_title">No se puede subir</string>
<string name="messages_cant_upload_explainer">Ingresa primero el destino del mensaje</string>
<string name="messages_group_descriptor">Miembros de este grupo</string>
<string name="messages_new_subject_message">Explicación para los miembros</string>
<string name="messages_new_subject_message_placeholder">Cambio de nombre para los nuevos objetivos.</string>
@@ -851,6 +877,7 @@
<string name="message_to_author">Resumen de cambios</string>
<string name="message_to_author_placeholder">Correcciones rápidas…</string>
<string name="accept_the_suggestion">Aceptar la sugerencia</string>
<string name="enter_picture_in_picture">Iniciar video en una ventana emergente</string>
<string name="accessibility_download_for_offline">Descargar</string>
<string name="accessibility_lyrics_on">Letra activada</string>
<string name="accessibility_lyrics_off">Letra desactivada</string>
@@ -916,4 +943,5 @@
<string name="torrent_no_apps">No hay aplicaciones torrent instaladas para abrir y descargar el archivo.</string>
<string name="select_list_to_filter">Selecciona una lista para filtrar el tablón</string>
<string name="temporary_account">Cerrar sesión al bloquear dispositivo</string>
<string name="private_message">Mensaje privado</string>
</resources>
@@ -20,6 +20,7 @@
<string name="impersonation">Suplantación de identidad</string>
<string name="illegal_behavior">Comportamiento ilegal</string>
<string name="other">Otro</string>
<string name="harassment">Acoso</string>
<string name="unknown">Desconocido</string>
<string name="relay_icon">Ícono de relé</string>
<string name="unknown_author">Autor desconocido</string>
@@ -81,6 +82,13 @@
<string name="thank_you_so_much">¡Muchas gracias!</string>
<string name="amount_in_sats">Cantidad en sats</string>
<string name="send_sats">Enviar sats</string>
<string name="secret_emoji_maker">Creador de emojis secretos</string>
<string name="secret_emoji_maker_explainer">Agrega un emoji con mensaje oculto para publicar.</string>
<string name="secret_note_to_receiver">Nota secreta al destinatario</string>
<string name="secret_note_to_receiver_placeholder">Mi mensaje oculto</string>
<string name="secret_visible_text">Prefijo visible</string>
<string name="secret_visible_text_placeholder">😎</string>
<string name="secret_add_to_text">Agregar a publicación</string>
<string name="error_parsing_preview_for">"Error al analizar la vista previa de %1$s : %2$s"</string>
<string name="preview_card_image_for">"Vista previa de imagen de tarjeta para %1$s"</string>
<string name="new_channel">Nuevo canal</string>
@@ -90,6 +98,7 @@
<string name="description">Descripción</string>
<string name="about_us">"Quiénes somos..."</string>
<string name="what_s_on_your_mind">¿Qué estás pensando?</string>
<string name="write_a_message">Escribir un mensaje...</string>
<string name="post">Publicar</string>
<string name="save">Guardar</string>
<string name="create">Crear</string>
@@ -106,6 +115,7 @@
<string name="global_feed">Feed global</string>
<string name="search_feed">Feed de búsqueda</string>
<string name="add_a_relay">Agregar un relé</string>
<string name="profile_name">Nombre</string>
<string name="display_name">Nombre para mostrar</string>
<string name="my_display_name">Mi nombre para mostrar</string>
<string name="my_awesome_name">Avestruz Maravillosa</string>
@@ -141,6 +151,7 @@
<string name="conversations">Conversaciones</string>
<string name="notes">Notas</string>
<string name="replies">Respuestas</string>
<string name="mutual">Tuyo</string>
<string name="gallery">Galería</string>
<string name="follows">"Seguidos"</string>
<string name="reports">"Reportes"</string>
@@ -195,6 +206,7 @@
<string name="with_description_of">con la descripción de</string>
<string name="and_picture">e imagen</string>
<string name="changed_chat_name_to">cambió el nombre del chat a</string>
<string name="changed_chat_profile_to">Nuevo perfil de chat:</string>
<string name="description_to">descripción a</string>
<string name="and_picture_to">e imagen a</string>
<string name="leave">Salir</string>
@@ -202,6 +214,13 @@
<string name="channel_created">Canal creado</string>
<string name="channel_information_changed_to">"La información del canal cambió a"</string>
<string name="public_chat">Chat público</string>
<string name="public_chat_title">Metadatos públicos del chat</string>
<string name="public_chat_explainer">Los chats públicos son visibles para todos en Nostr y cualquiera
puede participar en ellos. Son ideales para comunidades abiertas alrededor de temas específicos.
La moderación puede controlarse eliminando publicaciones en sus relés.</string>
<string name="public_chat_relays_title">Relés</string>
<string name="public_chat_relays_explainer">Inserta entre 1 y 3 relés que alojan este grupo.
Los clientes de Nostr utilizan esta configuración para saber de dónde descargar y adónde enviar los mensajes.</string>
<string name="posts_received">publicaciones recibidas</string>
<string name="remove">Eliminar</string>
<string name="translations_auto">Automático</string>
@@ -509,6 +528,7 @@
<string name="cashu_redeem_to_zap">Enviar a billetera de zaps</string>
<string name="cashu_redeem_to_cashu">Abrir en billetera de Cashu</string>
<string name="cashu_copy_token">Copiar token</string>
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
<string name="no_lightning_address_set">La dirección de Lightning no está configurada</string>
<string name="copied_token_to_clipboard">Token copiado al portapapeles</string>
<string name="live_stream_live_tag">EN VIVO</string>
@@ -539,6 +559,8 @@
<string name="ui_feature_set_type_complete">Completo</string>
<string name="ui_feature_set_type_simplified">Simplificado</string>
<string name="ui_feature_set_type_performance">Rendimiento</string>
<string name="gallery_type_classic">Clásico</string>
<string name="gallery_type_modern">Moderno</string>
<string name="system">Sistema</string>
<string name="light">Claro</string>
<string name="dark">Oscuro</string>
@@ -553,6 +575,8 @@
<string name="automatically_hide_nav_bars_description">Ocultar barras de navegación al desplazarse</string>
<string name="ui_style">Modo de interfaz</string>
<string name="ui_style_description">Elegir el estilo de publicación</string>
<string name="gallery_style">Estilo de galería de perfil</string>
<string name="gallery_style_description">Elige el estilo de galería</string>
<string name="load_image">Cargar imagen</string>
<string name="spamming_users">Spammers</string>
<string name="muted_button">Silenciado. Hacer clic para reactivar el sonido.</string>
@@ -584,6 +608,8 @@
<string name="messages_new_message_subject">Asunto</string>
<string name="messages_new_message_subject_caption">Tema de la conversación</string>
<string name="messages_new_message_to_caption">"\@Usuario1, @Usuario2, @Usuario3"</string>
<string name="messages_cant_upload_title">No se puede subir</string>
<string name="messages_cant_upload_explainer">Ingresa primero el destino del mensaje</string>
<string name="messages_group_descriptor">Miembros de este grupo</string>
<string name="messages_new_subject_message">Explicación para los miembros</string>
<string name="messages_new_subject_message_placeholder">Cambio de nombre para los nuevos objetivos.</string>
@@ -851,6 +877,7 @@
<string name="message_to_author">Resumen de cambios</string>
<string name="message_to_author_placeholder">Correcciones rápidas…</string>
<string name="accept_the_suggestion">Aceptar la sugerencia</string>
<string name="enter_picture_in_picture">Iniciar video en una ventana emergente</string>
<string name="accessibility_download_for_offline">Descargar</string>
<string name="accessibility_lyrics_on">Letra activada</string>
<string name="accessibility_lyrics_off">Letra desactivada</string>
@@ -916,4 +943,5 @@
<string name="torrent_no_apps">No hay aplicaciones torrent instaladas para abrir y descargar el archivo.</string>
<string name="select_list_to_filter">Selecciona una lista para filtrar el feed</string>
<string name="temporary_account">Cerrar sesión al bloquear dispositivo</string>
<string name="private_message">Mensaje privado</string>
</resources>
@@ -20,6 +20,7 @@
<string name="impersonation">Suplantación de identidad</string>
<string name="illegal_behavior">Comportamiento ilegal</string>
<string name="other">Otro</string>
<string name="harassment">Acoso</string>
<string name="unknown">Desconocido</string>
<string name="relay_icon">Ícono de relé</string>
<string name="unknown_author">Autor desconocido</string>
@@ -81,6 +82,13 @@
<string name="thank_you_so_much">¡Muchas gracias!</string>
<string name="amount_in_sats">Cantidad en sats</string>
<string name="send_sats">Enviar sats</string>
<string name="secret_emoji_maker">Creador de emojis secretos</string>
<string name="secret_emoji_maker_explainer">Agrega un emoji con mensaje oculto para publicar.</string>
<string name="secret_note_to_receiver">Nota secreta al destinatario</string>
<string name="secret_note_to_receiver_placeholder">Mi mensaje oculto</string>
<string name="secret_visible_text">Prefijo visible</string>
<string name="secret_visible_text_placeholder">😎</string>
<string name="secret_add_to_text">Agregar a publicación</string>
<string name="error_parsing_preview_for">"Error al analizar la vista previa de %1$s : %2$s"</string>
<string name="preview_card_image_for">"Vista previa de imagen de tarjeta para %1$s"</string>
<string name="new_channel">Nuevo canal</string>
@@ -90,6 +98,7 @@
<string name="description">Descripción</string>
<string name="about_us">"Quiénes somos..."</string>
<string name="what_s_on_your_mind">¿Qué estás pensando?</string>
<string name="write_a_message">Escribir un mensaje...</string>
<string name="post">Publicar</string>
<string name="save">Guardar</string>
<string name="create">Crear</string>
@@ -106,6 +115,7 @@
<string name="global_feed">Feed global</string>
<string name="search_feed">Feed de búsqueda</string>
<string name="add_a_relay">Agregar un relé</string>
<string name="profile_name">Nombre</string>
<string name="display_name">Nombre para mostrar</string>
<string name="my_display_name">Mi nombre para mostrar</string>
<string name="my_awesome_name">Avestruz Maravillosa</string>
@@ -141,6 +151,7 @@
<string name="conversations">Conversaciones</string>
<string name="notes">Notas</string>
<string name="replies">Respuestas</string>
<string name="mutual">Tuyo</string>
<string name="gallery">Galería</string>
<string name="follows">"Seguidos"</string>
<string name="reports">"Reportes"</string>
@@ -195,6 +206,7 @@
<string name="with_description_of">con la descripción de</string>
<string name="and_picture">e imagen</string>
<string name="changed_chat_name_to">cambió el nombre del chat a</string>
<string name="changed_chat_profile_to">Nuevo perfil de chat:</string>
<string name="description_to">descripción a</string>
<string name="and_picture_to">e imagen a</string>
<string name="leave">Salir</string>
@@ -202,6 +214,13 @@
<string name="channel_created">Canal creado</string>
<string name="channel_information_changed_to">"La información del canal cambió a"</string>
<string name="public_chat">Chat público</string>
<string name="public_chat_title">Metadatos públicos del chat</string>
<string name="public_chat_explainer">Los chats públicos son visibles para todos en Nostr y cualquiera
puede participar en ellos. Son ideales para comunidades abiertas alrededor de temas específicos.
La moderación puede controlarse eliminando mensajes en sus relés.</string>
<string name="public_chat_relays_title">Relés</string>
<string name="public_chat_relays_explainer">Inserta entre 1 y 3 relés que alojan este grupo.
Los clientes de Nostr utilizan esta configuración para saber de dónde descargar y adónde enviar los mensajes.</string>
<string name="posts_received">publicaciones recibidas</string>
<string name="remove">Eliminar</string>
<string name="translations_auto">Automático</string>
@@ -509,6 +528,7 @@
<string name="cashu_redeem_to_zap">Enviar a billetera de zaps</string>
<string name="cashu_redeem_to_cashu">Abrir en billetera de Cashu</string>
<string name="cashu_copy_token">Copiar token</string>
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
<string name="no_lightning_address_set">La dirección de Lightning no está configurada</string>
<string name="copied_token_to_clipboard">Token copiado al portapapeles</string>
<string name="live_stream_live_tag">EN VIVO</string>
@@ -539,6 +559,8 @@
<string name="ui_feature_set_type_complete">Completo</string>
<string name="ui_feature_set_type_simplified">Simplificado</string>
<string name="ui_feature_set_type_performance">Rendimiento</string>
<string name="gallery_type_classic">Clásico</string>
<string name="gallery_type_modern">Moderno</string>
<string name="system">Sistema</string>
<string name="light">Claro</string>
<string name="dark">Oscuro</string>
@@ -553,6 +575,8 @@
<string name="automatically_hide_nav_bars_description">Ocultar barras de navegación al desplazarse</string>
<string name="ui_style">Modo de interfaz</string>
<string name="ui_style_description">Elegir el estilo de publicación</string>
<string name="gallery_style">Estilo de galería de perfil</string>
<string name="gallery_style_description">Elige el estilo de galería</string>
<string name="load_image">Cargar imagen</string>
<string name="spamming_users">Spammers</string>
<string name="muted_button">Silenciado. Hacer clic para reactivar el sonido.</string>
@@ -584,6 +608,8 @@
<string name="messages_new_message_subject">Asunto</string>
<string name="messages_new_message_subject_caption">Tema de la conversación</string>
<string name="messages_new_message_to_caption">"\@Usuario1, @Usuario2, @Usuario3"</string>
<string name="messages_cant_upload_title">No se puede subir</string>
<string name="messages_cant_upload_explainer">Ingresa primero el destino del mensaje</string>
<string name="messages_group_descriptor">Miembros de este grupo</string>
<string name="messages_new_subject_message">Explicación para los miembros</string>
<string name="messages_new_subject_message_placeholder">Cambio de nombre para los nuevos objetivos.</string>
@@ -851,6 +877,7 @@
<string name="message_to_author">Resumen de cambios</string>
<string name="message_to_author_placeholder">Correcciones rápidas…</string>
<string name="accept_the_suggestion">Aceptar la sugerencia</string>
<string name="enter_picture_in_picture">Iniciar video en una ventana emergente</string>
<string name="accessibility_download_for_offline">Descargar</string>
<string name="accessibility_lyrics_on">Letra activada</string>
<string name="accessibility_lyrics_off">Letra desactivada</string>
@@ -916,4 +943,5 @@
<string name="torrent_no_apps">No hay aplicaciones torrent instaladas para abrir y descargar el archivo.</string>
<string name="select_list_to_filter">Selecciona una lista para filtrar el feed</string>
<string name="temporary_account">Cerrar sesión al bloquear dispositivo</string>
<string name="private_message">Mensaje privado</string>
</resources>
@@ -98,6 +98,7 @@
<string name="description">Descrição</string>
<string name="about_us">"Sobre nós.. "</string>
<string name="what_s_on_your_mind">O que você está pensando?</string>
<string name="write_a_message">Escreva uma mensagem…</string>
<string name="post">Salvar</string>
<string name="save">Salvar</string>
<string name="create">Criar</string>
@@ -205,6 +206,7 @@
<string name="with_description_of">com descrição de</string>
<string name="and_picture">e foto</string>
<string name="changed_chat_name_to">mudou nome do canal para</string>
<string name="changed_chat_profile_to">Novo perfil de chat:</string>
<string name="description_to">descrição para</string>
<string name="and_picture_to">e foto para</string>
<string name="leave">Sair</string>
@@ -524,6 +526,7 @@
<string name="cashu_redeem_to_zap">Enviar para Zap Wallet</string>
<string name="cashu_redeem_to_cashu">Abrir na Carteira Cashu</string>
<string name="cashu_copy_token">Copiar Token</string>
<string name="quick_actions_open_in_another_app">Abrir em outro aplicativo</string>
<string name="no_lightning_address_set">Nenhum endereço Lightning definido</string>
<string name="copied_token_to_clipboard">Token copiado para a área de transferência</string>
<string name="live_stream_live_tag">AO VIVO</string>
@@ -603,6 +606,8 @@
<string name="messages_new_message_subject">Assunto</string>
<string name="messages_new_message_subject_caption">Tema da conversa</string>
<string name="messages_new_message_to_caption">"\@Usuário1, @Usuário2, @Usuário3"</string>
<string name="messages_cant_upload_title">Não pode enviar</string>
<string name="messages_cant_upload_explainer">Insira o destino da mensagem primeiro</string>
<string name="messages_group_descriptor">Membros deste grupo</string>
<string name="messages_new_subject_message">Explicação aos membros</string>
<string name="messages_new_subject_message_placeholder">Mudando o nome dos novos objetivos.</string>
@@ -936,4 +941,5 @@
<string name="torrent_no_apps">Nenhum aplicativo torrent instalado para abrir e baixar o arquivo.</string>
<string name="select_list_to_filter">Selecione uma lista para filtrar o feed</string>
<string name="temporary_account">Terminar sessão no bloqueio do dispositivo</string>
<string name="private_message">Mensagem Privada</string>
</resources>
@@ -98,6 +98,7 @@
<string name="description">Beskrivning</string>
<string name="about_us">"Om oss.. "</string>
<string name="what_s_on_your_mind">Vad tänker du på?</string>
<string name="write_a_message">Skriv ett meddelande...</string>
<string name="post">Dela</string>
<string name="save">Spara</string>
<string name="create">Skapa</string>
@@ -205,6 +206,7 @@
<string name="with_description_of">med beskrivning av</string>
<string name="and_picture">och bild</string>
<string name="changed_chat_name_to">ändra chat namn till</string>
<string name="changed_chat_profile_to">Ny chattprofil:</string>
<string name="description_to">beskrivning till</string>
<string name="and_picture_to">och bild till</string>
<string name="leave">Lämna</string>
@@ -523,6 +525,7 @@
<string name="cashu_redeem_to_zap">Skicka till Zap Plånbok</string>
<string name="cashu_redeem_to_cashu">Öppna i Cashu Plånbok</string>
<string name="cashu_copy_token">Kopiera Token</string>
<string name="quick_actions_open_in_another_app">Öppna i annan app</string>
<string name="no_lightning_address_set">Ingen Lightning-adress angiven</string>
<string name="copied_token_to_clipboard">Kopierade token till urklipp</string>
<string name="live_stream_live_tag">LIVE</string>
@@ -602,6 +605,8 @@
<string name="messages_new_message_subject">Ämne</string>
<string name="messages_new_message_subject_caption">Samtalsämne</string>
<string name="messages_new_message_to_caption">"\@Användare1, @Användare2, @Användare3"</string>
<string name="messages_cant_upload_title">Kan inte ladda upp</string>
<string name="messages_cant_upload_explainer">Infoga destinationen för meddelandet först</string>
<string name="messages_group_descriptor">Medlemmar i denna grupp</string>
<string name="messages_new_subject_message">Förklaring till medlemmar</string>
<string name="messages_new_subject_message_placeholder">Ändra namnet för de nya målen.</string>
@@ -935,4 +940,5 @@
<string name="torrent_no_apps">Inga torrent-appar installerade för att öppna och ladda ner filen.</string>
<string name="select_list_to_filter">Välj en lista för att filtrera flödet</string>
<string name="temporary_account">Logga ut när enheten låses</string>
<string name="private_message">Privat meddelande</string>
</resources>