From bc7d0fd75847aa7a0d42d70c9033d38b3a4bc6f4 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Tue, 10 Mar 2026 14:07:56 +0000 Subject: [PATCH 1/3] New Crowdin translations by GitHub Action --- .../src/main/res/values-cs-rCZ/strings.xml | 24 ++++----- .../src/main/res/values-de-rDE/strings.xml | 24 ++++----- .../src/main/res/values-hu-rHU/strings.xml | 50 +++++++++++++++++++ .../src/main/res/values-pt-rBR/strings.xml | 24 ++++----- .../src/main/res/values-sv-rSE/strings.xml | 24 ++++----- 5 files changed, 98 insertions(+), 48 deletions(-) diff --git a/amethyst/src/main/res/values-cs-rCZ/strings.xml b/amethyst/src/main/res/values-cs-rCZ/strings.xml index f9811932d..09e2ade37 100644 --- a/amethyst/src/main/res/values-cs-rCZ/strings.xml +++ b/amethyst/src/main/res/values-cs-rCZ/strings.xml @@ -419,6 +419,15 @@ Tajemství Wallet Connect Zobrazit tajný klíč nsec / hex soukromý klíč + Připojeno + Nepřipojeno + Pokročilé: zadejte podrobnosti připojení ručně + Rychlé částky Zap + Zobrazí se při stisknutí tlačítka zap. Klepnutím na částku ji odeberete. Pokud ponecháte prázdné, otevře se pokaždé dialog pro zadání částky. + Soukromí Zap + Určuje, jak je zobrazena vaše identita při odesílání zapsu. + Připojit peněženku + Zobrazit kanál relé Částka záruky v sats Zveřejnit hlasování Povinná pole: @@ -658,6 +667,7 @@ Množství bajtů, které bylo přijato z tohoto relé, včetně filtrů a událostí Při pokusu o získání informací z Relay se vyskytla chyba z %1$s Vlastník + Používáno Servisní klíč Spuštěno %1$s Spuštěno %1$s (%2$s) @@ -853,6 +863,7 @@ Kdy načíst obrázek Kopírovat zásobník Kopírovat do schránky + Zkopírovat nprofile do schránky Kopírovat npub do schránky Sdílet nebo Uložit Kopírovat URL do schránky @@ -1076,6 +1087,7 @@ Přidat upozornění na obsah Odstranit upozornění na obsah Zobrazit npub jako QR kód + Zobrazit nprofile jako QR kód Neplatná adresa Amethyst obdržel URI k otevření, ale tento URI byl neplatný: %1$s Relé DM Schránky @@ -1458,16 +1470,4 @@ Hlasová zpráva Hlasová odpověď Wiki - Zobrazit kanál relé - Používáno - Zkopírovat nprofile do schránky - Zobrazit nprofile jako QR kód - Zobrazí se při stisknutí tlačítka zap. Klepnutím na částku ji odeberete. Pokud ponecháte prázdné, otevře se pokaždé dialog pro zadání částky. - Připojeno - Nepřipojeno - Pokročilé: zadejte podrobnosti připojení ručně - Rychlé částky Zap - Soukromí Zap - Určuje, jak je zobrazena vaše identita při odesílání zapsu. - Připojit peněženku diff --git a/amethyst/src/main/res/values-de-rDE/strings.xml b/amethyst/src/main/res/values-de-rDE/strings.xml index 7456ed882..0b7815322 100644 --- a/amethyst/src/main/res/values-de-rDE/strings.xml +++ b/amethyst/src/main/res/values-de-rDE/strings.xml @@ -425,6 +425,15 @@ anz der Bedingungen ist erforderlich Wallet-Verbindungs-Geheimnis Geheimen Schlüssel anzeigen nsec / hexadezimaler privater Schlüssel + Verbunden + Nicht verbunden + Erweitert: Verbindungsdetails manuell eingeben + Schnelle Zap-Beträge + Wird beim Drücken der Zap-Schaltfläche angezeigt. Tippe auf einen Betrag, um ihn zu entfernen. Wenn du es leer lässt, wird jedes Mal ein Dialog zur Eingabe eines Betrags geöffnet. + Zap-Datenschutz + Legt fest, wie deine Identität beim Senden eines Zaps angezeigt wird. + Wallet verbinden + Relay-Feed anzeigen Spendenbetrag in Sats Umfrage veröffentlichen Erforderliche Felder: @@ -663,6 +672,7 @@ anz der Bedingungen ist erforderlich Die Menge in Bytes, die von diesem Relais empfangen wurde, einschließlich Filter und Ereignisse Ein Fehler ist beim Abrufen von Relay-Informationen von %1$s aufgetreten Inhaber + Verwendet von Service-Schlüssel %1$s wird ausgeführt %1$s wird ausgeführt (%2$s) @@ -858,6 +868,7 @@ anz der Bedingungen ist erforderlich Wann Bilder geladen werden sollen Stapel kopieren In Zwischenablage kopieren + nprofile in die Zwischenablage kopieren Npub in Zwischenablage kopieren Teilen oder Speichern URL in die Zwischenablage kopieren @@ -1081,6 +1092,7 @@ anz der Bedingungen ist erforderlich Inhaltswarnung hinzufügen Inhaltswarnung entfernen Npub als QR-Code anzeigen + nprofile als QR-Code anzeigen Ungültige Adresse Amethyst hat eine URI zum Öffnen erhalten, aber diese URI war ungültig: %1$s DM Posteingangs-Relais @@ -1463,16 +1475,4 @@ anz der Bedingungen ist erforderlich Sprachnachricht Sprachantwort Wiki - Relay-Feed anzeigen - Verwendet von - nprofile in die Zwischenablage kopieren - nprofile als QR-Code anzeigen - Wird beim Drücken der Zap-Schaltfläche angezeigt. Tippe auf einen Betrag, um ihn zu entfernen. Wenn du es leer lässt, wird jedes Mal ein Dialog zur Eingabe eines Betrags geöffnet. - Verbunden - Nicht verbunden - Erweitert: Verbindungsdetails manuell eingeben - Schnelle Zap-Beträge - Zap-Datenschutz - Legt fest, wie deine Identität beim Senden eines Zaps angezeigt wird. - Wallet verbinden diff --git a/amethyst/src/main/res/values-hu-rHU/strings.xml b/amethyst/src/main/res/values-hu-rHU/strings.xml index 3af6f1fd2..829c8dd77 100644 --- a/amethyst/src/main/res/values-hu-rHU/strings.xml +++ b/amethyst/src/main/res/values-hu-rHU/strings.xml @@ -867,6 +867,7 @@ Mikor töltse be a képeket Köteg másolása Másolás a vágólapra + nprofile-kulcs vágólapra másolása Npub másolása a vágólapra Megosztás vagy mentés Webcím másolása a vágólapra @@ -1056,6 +1057,19 @@ Tetszik Zap Gyors reakciók megváltoztatása + Reakciósor + Állítsa be, hogy mely reakciógombok jelenjenek meg, azok sorrendjét, valamint a számlálók megjelenítését. + Engedélyezve + Számláló megjelenítése + Újrarendezés + Válasz + Válasz erre a bejegyzésre + Megtolás + Reagálás erre a bejegyzésre egy hangulatjellel + Zap + Lightning fizetés küldése a szerzőnek + Megosztás + Ennek a bejegyzésnek a megosztása külső alkalmazásban %1$s profilképe %1$s átjátszó Átjátszólista kibontása @@ -1075,6 +1089,7 @@ Tartalmi figyelmeztetés hozzáadása Tartalmi figyelmeztetés eltávolítása Az npub-kulcs megjelenítése QR-kódként + nprofile-kulcs megjelenítése QR-kódként Érvénytelen cím Az Amethyst kapott egy URI-t a megnyitáshoz, de az érvénytelen volt: %1$s Bejövő átjátszók a privát üzenetekhez @@ -1127,6 +1142,9 @@ Megbízható átjátszók Megbízható átjátszók Az átjátszóknak, amelyekben megbízik, nincs szükségük Tor kapcsolatra a következőhöz: + Kedvenc átjátszó-hírforrások + Kedvenc átjátszó-hírforrások + Átjátszók, amelyeket gyakran felkeres a globális hírfolyamuk megtekintéséhez Proxyzott átjátszók Proxyzott átjátszók Összesítő átjátszók, amelyeket az alkalmazásnak használnia kell a hírfolyamok letöltéséhez, például filter.nostr.wine. Ez helyettesíti a kimeneti modellt, és az alkalmazás csak a listákban szereplő átjátszókhoz fog csatlakozni. @@ -1172,6 +1190,7 @@ Kapcsolatrögzítő (Pushpin) QR-kód beolvasása Harmadik féltől származó pénztárca-szolgáltató felkeresése: Alby + Dátum kiválasztása A piszkozatokra nem lehet válaszolni A piszkozatokat nem lehet idézni A piszkozatokra nem lehet reagálni @@ -1308,5 +1327,36 @@ Megerősítés Következő Szavazati lehetőség hozzáadása + Vissza + Elfogadás + Kapcsolódva + Közösségi megerősítés + Beküldés + Újraindítás + Elfogadás + Elutasítás + Új játék + Új sakkjáték + Játék betöltése\u2026 + Nem található játék + Ez a játék már véget ért, vagy ellenfelet vár. + Játékazonosító: %1$s\u2026 + és %1$d további + %1$s eltávolítása + Kimenő átjátszók + Alkalmazások + Ajánlott alkalmazások + Felhasználói beállítások + Audio-fejléc + Hangsáv + Jelvénydíjak + Jelvények meghatározása + Profiljelvények + Letiltott átjátszók + Blossom kiszolgálók + Blossom-hitelesítés + Közvetítési átjátszók + Könyvjelzőlista + Napi bejegyzés diff --git a/amethyst/src/main/res/values-pt-rBR/strings.xml b/amethyst/src/main/res/values-pt-rBR/strings.xml index 97ff3adc6..bf5527842 100644 --- a/amethyst/src/main/res/values-pt-rBR/strings.xml +++ b/amethyst/src/main/res/values-pt-rBR/strings.xml @@ -419,6 +419,15 @@ Segredo do Wallet Connect Mostrar chave secreta chave privada nsec / hex + Conectado + Não conectado + Avançado: inserir detalhes de conexão manualmente + Valores Rápidos de Zap + Exibido ao pressionar o botão de zap. Toque em um valor para removê-lo. Se deixar vazio, abrirá o diálogo para inserir um valor todas as vezes. + Privacidade do Zap + Controla como sua identidade é exibida ao enviar um Zap. + Conectar Carteira + Ver feed do Relay Valor em Sats Postar enquete Campos obrigatórios: @@ -658,6 +667,7 @@ A quantidade em bytes que foi recebida deste relé, incluindo filtros e eventos Ocorreu um erro ao tentar obter informações do relay de %1$s Proprietário + Usado por Chave de serviço Executando %1$s Executando %1$s (%2$s) @@ -853,6 +863,7 @@ Quando carregar imagens Copiar a Pilha Copiar para a Área de Transferência + Copiar nprofile para a área de transferência Copiar npub para a área de transferência Compartilhar ou Salvar Copiar URL para a área de transferência @@ -1076,6 +1087,7 @@ Adicionar aviso de conteúdo Remover aviso de conteúdo Mostrar npub como um código QR + Mostrar nprofile como QR code Endereço inválido O Amethyst recebeu um URI para abrir, mas esse URI era inválido: %1$s Relés da Caixa de Entrada de DM @@ -1458,16 +1470,4 @@ Mensagem de voz Resposta de voz Wiki - Ver feed do Relay - Usado por - Copiar nprofile para a área de transferência - Mostrar nprofile como QR code - Exibido ao pressionar o botão de zap. Toque em um valor para removê-lo. Se deixar vazio, abrirá o diálogo para inserir um valor todas as vezes. - Conectado - Não conectado - Avançado: inserir detalhes de conexão manualmente - Valores Rápidos de Zap - Privacidade do Zap - Controla como sua identidade é exibida ao enviar um Zap. - Conectar Carteira diff --git a/amethyst/src/main/res/values-sv-rSE/strings.xml b/amethyst/src/main/res/values-sv-rSE/strings.xml index 87774947a..59cf542b2 100644 --- a/amethyst/src/main/res/values-sv-rSE/strings.xml +++ b/amethyst/src/main/res/values-sv-rSE/strings.xml @@ -419,6 +419,15 @@ Wallet Connect hemlighet Visa secret key nsec / hex privat nyckel + Ansluten + Inte ansluten + Avancerat: ange anslutningsinformation manuellt + Snabba Zap-belopp + Visas när du trycker på Zap-knappen. Tryck på ett belopp för att ta bort det. Om du lämnar det tomt öppnas en dialog för att ange ett belopp varje gång. + Zap-sekretess + Styr hur din identitet visas när du skickar ett Zap. + Anslut plånbok + Visa relä-flöde Pantbelopp i Sats Dela Poll Obligatoriska fält: @@ -657,6 +666,7 @@ Mängden data i byte som mottogs från detta relä, inklusive filter och händelser Ett fel inträffade vid försök att hämta information från Relay %1$s Ägare + Används av Service-nyckel Kör %1$s Kör %1$s (%2$s) @@ -852,6 +862,7 @@ När bilder ska laddas Kopiera stacken Kopiera till Urklipp + Kopiera nprofile till urklipp Kopiera npub till urklipp Dela eller Spara Kopiera URL till urklipp @@ -1075,6 +1086,7 @@ Lägg till varning för innehåll Ta bort varning för innehåll Visa npub som en QR-kod + Visa nprofile som QR-kod Ogiltig adress Amethyst fick en URI att öppna men den URI var ogiltig: %1$s DM Inkorg Reläer @@ -1457,16 +1469,4 @@ Röstmeddelande Röstsvar Wiki - Visa relä-flöde - Används av - Kopiera nprofile till urklipp - Visa nprofile som QR-kod - Visas när du trycker på Zap-knappen. Tryck på ett belopp för att ta bort det. Om du lämnar det tomt öppnas en dialog för att ange ett belopp varje gång. - Ansluten - Inte ansluten - Avancerat: ange anslutningsinformation manuellt - Snabba Zap-belopp - Zap-sekretess - Styr hur din identitet visas när du skickar ett Zap. - Anslut plånbok From a7cec6f5fd0554fcd725306d8072af6cd503e9a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Mar 2026 14:48:47 +0000 Subject: [PATCH 2/3] feat: add audio and PDF file upload support to ShortNotePostScreen Add a new "attach file" button that opens a document picker filtered to audio/* and application/pdf MIME types. This uses OpenMultipleDocuments contract since PickVisualMedia only supports images/videos. - Add SelectFromFiles composable with AttachFile icon button - Add isAudio() and isDocument() helpers to SelectedMedia - Add FilePreviewPlaceholder for audio/PDF thumbnail display - Add upload_file string resource https://claude.ai/code/session_018naEzfHjLRQLgaWAtY4aSz --- .../ui/actions/uploads/SelectFromFiles.kt | 129 ++++++++++++++++++ .../ui/actions/uploads/SelectFromGallery.kt | 4 + .../ui/actions/uploads/ShowImageUploadItem.kt | 50 +++++++ .../loggedIn/home/ShortNotePostScreen.kt | 9 ++ amethyst/src/main/res/values/strings.xml | 1 + 5 files changed, 193 insertions(+) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromFiles.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromFiles.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromFiles.kt new file mode 100644 index 000000000..776addedf --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromFiles.kt @@ -0,0 +1,129 @@ +/* + * 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.uploads + +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AttachFile +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.components.LoadingAnimation +import com.vitorpamplona.amethyst.ui.stringRes +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import java.util.concurrent.atomic.AtomicBoolean + +@Composable +fun SelectFromFiles( + isUploading: Boolean, + tint: Color, + modifier: Modifier, + onFilesChosen: (ImmutableList) -> Unit, +) { + var showFileSelect by remember { mutableStateOf(false) } + if (showFileSelect) { + FileSelect( + onFilesSelected = { files -> + showFileSelect = false + if (files.isNotEmpty()) { + onFilesChosen(files) + } + }, + ) + } + + FileSelectButton(isUploading, tint, modifier) { showFileSelect = true } +} + +@Composable +private fun FileSelectButton( + isUploading: Boolean, + tint: Color, + modifier: Modifier, + onClick: () -> Unit, +) { + IconButton( + modifier = modifier, + enabled = !isUploading, + onClick = { onClick() }, + ) { + if (!isUploading) { + Icon( + imageVector = Icons.Default.AttachFile, + contentDescription = stringRes(id = R.string.upload_file), + modifier = Modifier.height(25.dp), + tint = tint, + ) + } else { + LoadingAnimation() + } + } +} + +@Composable +fun FileSelect(onFilesSelected: (ImmutableList) -> Unit = {}) { + val hasLaunched by remember { mutableStateOf(AtomicBoolean(false)) } + val resolver = LocalContext.current.contentResolver + + val launcher = + rememberLauncherForActivityResult( + contract = ActivityResultContracts.OpenMultipleDocuments(), + onResult = { uris: List -> + onFilesSelected( + uris + .map { + SelectedMedia(it, resolver.getType(it)) + }.toImmutableList(), + ) + hasLaunched.set(false) + }, + ) + + @Composable + fun LaunchFilePicker() { + SideEffect { + if (!hasLaunched.getAndSet(true)) { + launcher.launch( + arrayOf( + "audio/*", + "application/pdf", + ), + ) + } + } + } + + LaunchFilePicker() +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt index d502dd069..1b2314668 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt @@ -55,6 +55,10 @@ class SelectedMedia( fun isImage() = mimeType?.startsWith("image") fun isVideo() = mimeType?.startsWith("video") + + fun isAudio() = mimeType?.startsWith("audio") + + fun isDocument() = mimeType == "application/pdf" } @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt index 04e6c1e34..261c9e8bf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt @@ -28,14 +28,21 @@ import android.os.Build import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AudioFile +import androidx.compose.material.icons.filled.PictureAsPdf import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProgressIndicatorDefaults @@ -117,6 +124,16 @@ fun ShowImageGallery( .fillMaxWidth() .windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)), ) + } else if (media.isAudio() == true) { + FilePreviewPlaceholder( + icon = Icons.Default.AudioFile, + label = media.mimeType ?: "audio/*", + ) + } else if (media.isDocument() == true) { + FilePreviewPlaceholder( + icon = Icons.Default.PictureAsPdf, + label = media.mimeType ?: "application/pdf", + ) } else if (media.isVideo() == true && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { var bitmap by remember { mutableStateOf(null) } val context = LocalContext.current @@ -254,3 +271,36 @@ fun UploadingState( ) } } + +@Composable +fun FilePreviewPlaceholder( + icon: androidx.compose.ui.graphics.vector.ImageVector, + label: String, +) { + Box( + modifier = + Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surfaceVariant), + contentAlignment = Alignment.Center, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.padding(8.dp), + ) { + Icon( + imageVector = icon, + contentDescription = label, + modifier = Modifier.size(40.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Text( + text = label, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 10.sp, + textAlign = TextAlign.Center, + ) + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index 29c146ae7..3b09dedd2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.actions.mediaServers.FileServerSelectionRow 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.SelectFromFiles import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton @@ -504,6 +505,14 @@ private fun BottomRowActions(postViewModel: ShortNotePostViewModel) { maxDurationSeconds = MAX_VOICE_RECORD_SECONDS, ) + SelectFromFiles( + isUploading = postViewModel.isUploadingImage, + tint = MaterialTheme.colorScheme.onBackground, + modifier = Modifier, + ) { + postViewModel.selectImage(it) + } + if (postViewModel.canUsePoll) { // These should be hashtag recommendations the user selects in the future. // val hashtag = stringRes(R.string.poll_hashtag) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 5ffd1b4e5..8ab251c40 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -170,6 +170,7 @@ Video saved to the phone\'s video gallery Failed to save the video Upload Image + Upload File Take a picture Record a video Record a message From a64e9bb3cf15a7ced78f24ee55b1cee4c5ceb88c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Mar 2026 15:15:27 +0000 Subject: [PATCH 3/3] fix: preserve search screen scroll position across navigation Replace ephemeral rememberLazyListState() with rememberForeverLazyListState() so the scroll position is persisted when navigating to a search result and back. https://claude.ai/code/session_01PmSFQqSMzP51mtqPLwFEtp --- .../vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt | 2 ++ .../amethyst/ui/screen/loggedIn/search/SearchScreen.kt | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt index fbc6b5135..2cc7b942c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt @@ -54,6 +54,8 @@ object ScrollStateKeys { const val DISCOVER_LIVE = "DiscoverLiveFeed" const val DISCOVER_COMMUNITY = "DiscoverCommunitiesFeed" const val DISCOVER_CHATS = "DiscoverChatsFeed" + + const val SEARCH_SCREEN = "SearchFeed" } object PagerStateKeys { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt index 12ac914aa..acf6814c6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt @@ -33,7 +33,8 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.lazy.rememberLazyListState +import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys +import com.vitorpamplona.amethyst.ui.feeds.rememberForeverLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.HorizontalDivider @@ -107,7 +108,7 @@ fun SearchScreen( ) { WatchLifecycleAndUpdateModel(searchBarViewModel) - val listState = rememberLazyListState() + val listState = rememberForeverLazyListState(ScrollStateKeys.SEARCH_SCREEN) LaunchedEffect(searchBarViewModel.focusRequester) { searchBarViewModel.focusRequester.requestFocus()