diff --git a/amethyst/build.gradle b/amethyst/build.gradle index 0f21d2d8d9..4128c68399 100644 --- a/amethyst/build.gradle +++ b/amethyst/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.vitorpamplona.amethyst" minSdk libs.versions.android.minSdk.get().toInteger() targetSdk libs.versions.android.targetSdk.get().toInteger() - versionCode 388 - versionName "0.89.2" + versionCode 392 + versionName "0.89.6" buildConfigField "String", "RELEASE_NOTES_ID", "\"1130badb252bdf62aaf93cb65bfa2bb09e7600c7d6764894f2954555b3b73018\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/Amethyst.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/Amethyst.kt index 370b651ed0..829a0fbca1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/Amethyst.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/Amethyst.kt @@ -30,6 +30,7 @@ import android.util.Log import androidx.security.crypto.EncryptedSharedPreferences import coil.ImageLoader import coil.disk.DiskCache +import coil.memory.MemoryCache import com.vitorpamplona.amethyst.service.ots.OkHttpBlockstreamExplorer import com.vitorpamplona.amethyst.service.ots.OkHttpCalendarBuilder import com.vitorpamplona.amethyst.service.playback.VideoCache @@ -66,7 +67,14 @@ class Amethyst : Application() { .Builder() .directory(applicationContext.safeCacheDir.resolve("image_cache")) .maxSizePercent(0.2) - .maximumMaxSizeBytes(500L * 1024 * 1024) // 250MB + .maximumMaxSizeBytes(1024 * 1024 * 1024) // 1GB + .build() + } + + val coilMemCache: MemoryCache by lazy { + MemoryCache + .Builder(this) + .maxSizePercent(0.40) // memory heavy app due to profile pics and videos. .build() } @@ -106,7 +114,7 @@ class Amethyst : Application() { } } - fun imageLoaderBuilder(): ImageLoader.Builder = ImageLoader.Builder(applicationContext).diskCache { coilCache } + fun imageLoaderBuilder(): ImageLoader.Builder = ImageLoader.Builder(this).diskCache { coilCache }.memoryCache { coilMemCache } fun encryptedStorage(npub: String? = null): EncryptedSharedPreferences = EncryptedStorage.preferences(instance, npub) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 07a0f0d6d0..0546d0806b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -2761,19 +2761,22 @@ class Account( // Takes a User's relay list and adds the types of feeds they are active for. fun activeRelays(): Array? { val usersRelayList = - userProfile().latestContactList?.relays()?.map { - val url = RelayUrlFormatter.normalize(it.key) + userProfile() + .latestContactList + ?.relays() + ?.map { + val url = RelayUrlFormatter.normalize(it.key) - val localFeedTypes = - localRelays.firstOrNull { localRelay -> RelayUrlFormatter.normalize(localRelay.url) == url }?.feedTypes?.minus(setOf(FeedType.SEARCH, FeedType.WALLET_CONNECT)) - ?: Constants.defaultRelays - .filter { defaultRelay -> defaultRelay.url == url } - .firstOrNull() - ?.feedTypes - ?: Constants.activeTypesGlobalChats + val localFeedTypes = + localRelays.firstOrNull { localRelay -> RelayUrlFormatter.normalize(localRelay.url) == url }?.feedTypes?.minus(setOf(FeedType.SEARCH, FeedType.WALLET_CONNECT)) + ?: Constants.defaultRelays + .filter { defaultRelay -> defaultRelay.url == url } + .firstOrNull() + ?.feedTypes + ?: Constants.activeTypesGlobalChats - RelaySetupInfo(url, it.value.read, it.value.write, localFeedTypes) - } ?: return null + RelaySetupInfo(url, it.value.read, it.value.write, localFeedTypes) + }?.ifEmpty { null } ?: return null return usersRelayList.toTypedArray() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrHomeDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrHomeDataSource.kt index df1a06ca1c..b224252c9b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrHomeDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrHomeDataSource.kt @@ -112,7 +112,7 @@ object NostrHomeDataSource : AmethystNostrDataSource("HomeFeed") { fun createFollowMetadataAndReleaseFilter(): TypedFilter? { val follows = account.liveHomeFollowLists.value?.users - val followSet = follows?.plus(account.userProfile().pubkeyHex)?.toList()?.ifEmpty { null } + val followSet = follows?.plus(account.userProfile().pubkeyHex)?.shuffled()?.ifEmpty { null } return if (followSet != null) { TypedFilter( @@ -124,7 +124,7 @@ object NostrHomeDataSource : AmethystNostrDataSource("HomeFeed") { MetadataEvent.KIND, AdvertisedRelayListEvent.KIND, ), - authors = followSet, + authors = followSet.take(500), since = latestEOSEs.users[account.userProfile()] ?.followList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt index c193a35aa6..d903cd903f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt @@ -54,9 +54,9 @@ fun stringRes( String .format( LocalConfiguration.current.locales.get(0), - resourceCache.get(id) ?: stringResource(id), + resourceCache.get(id) ?: stringResource(id).also { resourceCache.put(id, it) }, *args, - ).also { resourceCache.put(id, it) } + ) fun stringRes( ctx: Context, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt index 38800a8482..406eb997da 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.windowInsetsPadding @@ -135,6 +136,7 @@ private fun RenderBottomMenu( Modifier .fillMaxWidth() .windowInsetsPadding(windowInsets) + .consumeWindowInsets(windowInsets) .height(50.dp), ) { HorizontalDivider( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index 294146daee..c023f0ebe2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -100,7 +100,7 @@ fun AppNavigation( ) { val scope = rememberCoroutineScope() val nav = - remember { + remember(navController) { { route: String -> scope.launch { if (getRouteWithArguments(navController) != route) { @@ -114,8 +114,8 @@ fun AppNavigation( NavHost( navController, startDestination = Route.Home.route, - enterTransition = { fadeIn(animationSpec = tween(400)) }, - exitTransition = { fadeOut(animationSpec = tween(400)) }, + enterTransition = { fadeIn(animationSpec = tween(200)) }, + exitTransition = { fadeOut(animationSpec = tween(200)) }, ) { Route.Home.let { route -> composable( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt index 0d062381a3..b728d96d31 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt @@ -20,7 +20,9 @@ */ package com.vitorpamplona.amethyst.ui.navigation +import android.app.ActivityManager import android.content.Context +import android.content.pm.ApplicationInfo import android.os.Debug import android.util.Log import androidx.compose.foundation.clickable @@ -66,6 +68,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.core.content.getSystemService import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -1034,6 +1037,14 @@ fun debugState(context: Context) { Log.d("STATE DUMP", "Total Native Heap Allocated: " + nativeHeap + " MB") + val activityManager: ActivityManager? = context.getSystemService() + if (activityManager != null) { + val isLargeHeap = (context.applicationInfo.flags and ApplicationInfo.FLAG_LARGE_HEAP) != 0 + val memClass = if (isLargeHeap) activityManager.largeMemoryClass else activityManager.memoryClass + + Log.d("STATE DUMP", "Memory Class " + memClass + " MB (largeHeap $isLargeHeap)") + } + Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays()) val imageLoader = Coil.imageLoader(context) @@ -1092,6 +1103,13 @@ fun debugState(context: Context) { LocalCache.observablesByKindAndAuthor.size, ) + Log.d( + "STATE DUMP", + "Spam: " + + LocalCache.antiSpam.recentMessages.size() + + " / " + LocalCache.antiSpam.spamMessages.size(), + ) + Log.d( "STATE DUMP", "Memory used by Events: " + diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt index ace6774cf8..249f621861 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt @@ -34,10 +34,12 @@ import androidx.compose.animation.slideOutVertically import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet @@ -486,7 +488,14 @@ private fun MainScaffold( } }, ) { - Column(modifier = Modifier.padding(it).imePadding()) { + Column( + modifier = + Modifier + .padding(it) + .consumeWindowInsets(it) + .systemBarsPadding() + .imePadding(), + ) { AppNavigation( homeFeedViewModel = homeFeedViewModel, repliesFeedViewModel = repliesFeedViewModel, diff --git a/amethyst/src/main/res/values-es-rES/strings.xml b/amethyst/src/main/res/values-es-rES/strings.xml index c10bf9dc28..e97111c223 100644 --- a/amethyst/src/main/res/values-es-rES/strings.xml +++ b/amethyst/src/main/res/values-es-rES/strings.xml @@ -9,6 +9,7 @@ Esta publicación se ocultó porque menciona tus usuarios o palabras ocultas Post marcado como inapropiado por El evento se está cargando o no se puede encontrar en la lista de relés + 👀 Imagen del canal Evento referenciado no encontrado No se pudo desencriptar el mensaje @@ -132,6 +133,7 @@ Conversaciones Notas Respuestas + Galería "Siguiendo" "Reportes" Más opciones @@ -255,6 +257,8 @@ Eliminar Dejar de seguir Seguir + Eliminar de la galería + Elimina este contenido multimedia de la galería, aunque puedes volver a agregarlo luego. Solicitar eliminación Amethyst solicitará que se elimine su nota de los relays a los que está conectado actualmente. No hay garantía de que su nota se elimine permanentemente de esos relays, o de otros relays donde pueda almacenarse. Bloquear @@ -517,6 +521,7 @@ Compartir o guardar Copiar URL al portapapeles Copiar ID de la nota al portapapeles + Agregar contenido multimedia a la galería Creado Reglas Iniciar sesión con Amber @@ -720,6 +725,8 @@ Inserta entre 1 y 3 relés para almacenar eventos que nadie más pueda ver, como los borradores o la configuración de la app. Lo ideal es que estos relés sean locales o requieran autenticación antes de descargar el contenido de cada usuario. Relés generales Amethyst usa estos relés para descargar publicaciones por ti. + Relés recomendados + Agrega los siguientes relés a la lista de relés generales para recibir publicaciones de los usuarios de la lista. Relés de búsqueda Lista de relés que se usarán al buscar contenido o usuarios. El etiquetado y la búsqueda no funcionarán si no hay opciones disponibles. Asegúrate de que implementen NIP-50. Relés locales diff --git a/amethyst/src/main/res/values-es-rMX/strings.xml b/amethyst/src/main/res/values-es-rMX/strings.xml index 68e44875f1..135e156be2 100644 --- a/amethyst/src/main/res/values-es-rMX/strings.xml +++ b/amethyst/src/main/res/values-es-rMX/strings.xml @@ -9,6 +9,7 @@ Esta publicación se ocultó porque menciona tus usuarios o palabras ocultas La publicación fue reportada por El evento se está cargando o no se puede encontrar en la lista de relés + 👀 Imagen del canal No se encontró el evento referenciado No se pudo desencriptar el mensaje @@ -132,6 +133,7 @@ Conversaciones Notas Respuestas + Galería "Seguidos" "Reportes" Más opciones @@ -255,6 +257,8 @@ Eliminar Dejar de seguir Seguir + Eliminar de la galería + Elimina este contenido multimedia de la galería, aunque puedes volver a agregarlo luego. Solicitar eliminación Amethyst solicitará que se elimine la nota de los relés con los que tienes conexión actualmente. No hay garantía de que la nota se elimine permanentemente de esos relés o de otros donde pueda estar guardada. Bloquear @@ -517,6 +521,7 @@ Compartir o guardar Copiar URL al portapapeles Copiar ID de la nota al portapapeles + Agregar contenido multimedia a la galería Creado Reglas Iniciar sesión con Amber @@ -720,6 +725,8 @@ Inserta entre 1 y 3 relés para almacenar eventos que nadie más pueda ver, como los borradores o la configuración de la app. Lo ideal es que estos relés sean locales o requieran autenticación antes de descargar el contenido de cada usuario. Relés generales Amethyst usa estos relés para descargar publicaciones por ti. + Relés recomendados + Agrega los siguientes relés a la lista de relés generales para recibir publicaciones de los usuarios de la lista. Relés de búsqueda Lista de relés que se usarán al buscar contenido o usuarios. El etiquetado y la búsqueda no funcionarán si no hay opciones disponibles. Asegúrate de que implementen NIP-50. Relés locales diff --git a/amethyst/src/main/res/values-es-rUS/strings.xml b/amethyst/src/main/res/values-es-rUS/strings.xml index 8d6c99acc6..0925be4f5b 100644 --- a/amethyst/src/main/res/values-es-rUS/strings.xml +++ b/amethyst/src/main/res/values-es-rUS/strings.xml @@ -9,6 +9,7 @@ Esta publicación se ocultó porque menciona tus usuarios o palabras ocultas La publicación fue reportada por El evento se está cargando o no se puede encontrar en la lista de relés + 👀 Imagen del canal No se encontró el evento referenciado No se pudo desencriptar el mensaje @@ -132,6 +133,7 @@ Conversaciones Notas Respuestas + Galería "Seguidos" "Reportes" Más opciones @@ -255,6 +257,8 @@ Eliminar Dejar de seguir Seguir + Eliminar de la galería + Elimina este contenido multimedia de la galería, aunque puedes volver a agregarlo luego. Solicitar eliminación Amethyst solicitará que se elimine la nota de los relés con los que tienes conexión actualmente. No hay garantía de que la nota se elimine permanentemente de esos relés o de otros donde pueda estar guardada. Bloquear @@ -517,6 +521,7 @@ Compartir o guardar Copiar URL al portapapeles Copiar ID de la nota al portapapeles + Agregar contenido multimedia a la galería Creado Reglas Iniciar sesión con Amber @@ -720,6 +725,8 @@ Inserta entre 1 y 3 relés para almacenar eventos que nadie más pueda ver, como los borradores o la configuración de la app. Lo ideal es que estos relés sean locales o requieran autenticación antes de descargar el contenido de cada usuario. Relés generales Amethyst usa estos relés para descargar publicaciones por ti. + Relés recomendados + Agrega los siguientes relés a la lista de relés generales para recibir publicaciones de los usuarios de la lista. Relés de búsqueda Lista de relés que se usarán al buscar contenido o usuarios. El etiquetado y la búsqueda no funcionarán si no hay opciones disponibles. Asegúrate de que implementen NIP-50. Relés locales diff --git a/amethyst/src/main/res/values-hi-rIN/strings.xml b/amethyst/src/main/res/values-hi-rIN/strings.xml index f364765d4c..28c21b7e3b 100644 --- a/amethyst/src/main/res/values-hi-rIN/strings.xml +++ b/amethyst/src/main/res/values-hi-rIN/strings.xml @@ -725,6 +725,8 @@ १ - ३ पुनःप्रसारकों को जोडें उन घटनाओं को रखने के लिए जो कोई अन्य नहीं देख सकता जैसे कि पाण्डुलिपियाँ अथवा क्रमक के स्थापना विकल्प। आदर्शतः ये पुनःप्रसारक स्थानीय होने चाहिए अथवा प्रत्येक उपयोगकर्ता के विषयवस्तु अवरोहण के पूर्व परिचय प्रमाणीकरण अनिवार्य होना चाहिए। सामान्य पुनःप्रसारक अमेथिस्ट इन पुनःप्रसारकों का प्रयोग करता है आपके पत्रों का अवरोहण करने के लिए। + पुनःशंसित पुनःप्रसारक + सूचित उपयोगकर्ताओं के पत्र प्राप्त करने के लिए निम्नलिखित पुनःप्रसारकों को अपने सामान्य पुनःप्रसारक सूची में जोडें। खोज पुनःप्रसारक पुनःप्रसारकों की सूची जिनको किसी विषयवस्तु अथवा उपयोगकर्ताओं को खोजने में प्रयोग करना चाहिए। कोई विकल्प नहीं तो उपयोगकर्ता सूचक जोडना तथा खोज निष्क्रिय होंगे। सुनिश्चित करें कि वे निप॰-५० को कार्यान्वित किये हैं। स्थानीय पुनःप्रसारक diff --git a/amethyst/src/main/res/values-night/themes.xml b/amethyst/src/main/res/values-night/themes.xml index cd2b306aba..909b3e0a03 100644 --- a/amethyst/src/main/res/values-night/themes.xml +++ b/amethyst/src/main/res/values-night/themes.xml @@ -1,6 +1,6 @@ -