From 2bc8d26b4febf46636e9d10bc9a0adf283845708 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Fri, 22 Sep 2023 13:40:33 +0000 Subject: [PATCH 01/10] New Crowdin translations by GitHub Action --- app/src/main/res/values-cs/strings.xml | 10 ++-------- app/src/main/res/values-de/strings.xml | 11 ++--------- app/src/main/res/values-hu/strings.xml | 18 ++++++++++++++++++ app/src/main/res/values-sv-rSE/strings.xml | 10 ++-------- app/src/main/res/values-zh-rCN/strings.xml | 18 ++++++++++++++++++ app/src/main/res/values-zh-rTW/strings.xml | 18 ++++++++++++++++++ 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 7850db02f..57616d2f2 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -448,6 +448,7 @@ Členové této skupiny Vysvětlení členům Změna názvu pro nové cíle. + Vložit ze schránky Pro rozhraní aplikace Tmavé, světlé nebo systémové téma Automaticky načítat obrázky a GIFy @@ -458,6 +459,7 @@ Kopírovat ID poznámky do schránky Vytvořeno Pravidla + Přihlásit se pomocí Amber Aktualizovat svůj stav Chyba při zpracování chybové zprávy Hlasy jsou váženy podle hodnoty zapu. Můžete nastavit minimální částku, abyste se vyhnuli spammerům, a maximální částku, abyste zabránili velkým zapperům, kteří by mohli ovládnout hlasování. Použijte stejnou částku v obou polích, aby byla hodnota každého hlasu stejná. Nechte prázdné pro přijetí libovolné částky. @@ -473,27 +475,19 @@ Chaty Celosvětové Hledání - - Přihlásit se pomocí Amber - Vložit ze schránky - Rozdělit a přeposlat Zaps Podporované klienty budou Zapy rozdělovat a přeposílat uživatelům přidaným zde místo vám Hledat a přidat uživatele Uživatelské jméno nebo zobrazované jméno Uživatel %1$s nemá nastavenou bleskovou adresu pro přijímání satoshi Procenta - 25 Rozdělování Zapů s Přeposílání Zapů na - Bleskové peněženky nenalezený Zaplaceno Peněženka %1$s Chyba při otevírání aplikace pro podpis Žádost o podpis byla zamítnuta - Nebyly nalezeny žádné peněženky pro platbu bleskové faktury (Chyba: %1$s). Prosím, nainstalujte si bleskovou peněženku pro použití Zapů Nebyly nalezeny žádné peněženky pro platbu bleskové faktury. Prosím, nainstalujte si bleskovou peněženku pro použití Zapů - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index aaeed37f6..620bac1ed 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -446,6 +446,7 @@ anz der Bedingungen ist erforderlich Mitglieder dieser Gruppe Erklärung an Mitglieder Ändern des Namens für die neuen Ziele. + Aus Zwischenablage einfügen Für die App-Benutzeroberfläche Dunkles, helles oder Systemdesign Bilder und GIFs automatisch laden @@ -456,6 +457,7 @@ anz der Bedingungen ist erforderlich Notiz-ID in die Zwischenablage kopieren Erstellt am Regeln + Mit Amber anmelden Status aktualisieren Fehler beim Verarbeiten der Fehlermeldung Die Abstimmungen werden nach der Höhe des Zaps gewichtet. Sie können einen Mindestbetrag festlegen, um Spam zu verhindern, und einen Höchstbetrag, um zu verhindern, dass große Zapper die Abstimmung dominieren. Verwenden Sie denselben Betrag in beiden Feldern, um sicherzustellen, dass jeder Stimme der gleiche Wert zukommt. Lassen Sie es leer, um jeden Betrag zu akzeptieren. @@ -469,27 +471,18 @@ anz der Bedingungen ist erforderlich Startseite Nachrichten Suche - - Mit Amber anmelden - Aus Zwischenablage einfügen - Zaps aufteilen und weiterleiten Unterstützende Clients werden Zaps an die hier hinzugefügten Benutzer aufteilen und weiterleiten, anstatt an Sie Benutzer suchen und hinzufügen Benutzername oder Anzeigename Benutzer %1$s hat keine Lightning-Adresse eingerichtet, um Sats zu empfangen Prozent - 25 Zaps aufteilen mit Zaps weiterleiten an - Lightning-Wallets nicht gefunden Bezahlt - Wallet %1$s Fehler beim Öffnen der Signatur-App Signaturanfrage abgelehnt - Keine Wallets gefunden, um eine Lightning-Rechnung zu bezahlen (Fehler: %1$s). Installieren Sie eine Lightning-Wallet, um Zaps zu verwenden Keine Wallets gefunden, um eine Lightning-Rechnung zu bezahlen. Installieren Sie eine Lightning-Wallet, um Zaps zu verwenden - diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 18551bc0d..a09371f77 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -458,6 +458,7 @@ A csoport tagjai Magyarázat a csoport tagjainak Az új célok érdekében, a név megváltoztatása. + Beszúrás vágólapról Az applikáció felülete Sötét, Világos vagy Rendszer által használt téma Képek és GIF-ek automatikus betöltése @@ -468,6 +469,7 @@ A bejegyzésazonosító vágólapra másolása Létrehozva Szabályok + Bejelentkezés Amber-el Állapotod megjelenítése Hiba a hibaüzenet elemzésekor A szavazatokat a Zap-ek összegével súlyozzuk. Beállíthatsz egy minimális összeget, hogy a kéretlen leveleket elkerüld, és egy maximális összeget annak elkerülésére, hogy a szavazás feletti irányítást a nagy Zapperek vegyék át. Mindkét mezőben ugyanazt az összeget használd, hogy minden szavazat azonos értéket kapjon. Bármilyen összeg elfogadásához, hagyjd üresen. @@ -484,4 +486,20 @@ Chat Globális Keresés + Zap-ek megosztása és továbbítása + A támogató kliensek a zapokat, az itt hozzáadott felhasználóknak helyetted felosztják és továbbítják + Felhasználó keresése és hozzáadása + Felhasználónév vagy megjelenítendő név + A %1$s felhasználó a sat fogadáshoz nem rendelkezik LN cím beállítással + Százalék + 25 + Zap-ek megosztása + Zap-ek továbbítása + Lightning tárca nem található + Fizetve + Tárca: %1$s + Hiba az aláíró alkalmazás megnyitásakor + Aláírási kérés elutasítva + Nem található tárca a Lighning számla kifizetésére (Hiba: %1$s). Kérjük, a zap-ek használatához telepítsen egy Lightning pénztárcát + Nem található tárca a Lighning számla kifizetésére. Kérjük, a zap-ek használatához telepítsen egy Lightning pénztárcát diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 0c44173c2..9fedf9dc2 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -456,6 +456,7 @@ Medlemmar i denna grupp Förklaring till medlemmar Ändra namnet för de nya målen. + Klistra in från urklipp För appens gränssnitt Mörkt, Ljust eller Systemtema Ladda automatiskt bilder och GIFs @@ -466,6 +467,7 @@ Kopiera anteckningens ID till urklipp Skapad den Regler + Logga in med Amber Uppdatera din status Fel vid tolkning av felmeddelande Röster viktas av zap-beloppet. Du kan ställa in en minsta mängd för att undvika spammare och en maximal mängd för att undvika att en stor zapper tar över omröstningen. Använd samma belopp i båda fälten för att se till att varje röst värderas till samma belopp. Lämna tomt för att acceptera valfritt belopp. @@ -482,27 +484,19 @@ Chattar Globalt Sök - - Logga in med Amber - Klistra in från urklipp - Dela och vidarebefordra Zaps Stödjande klienter kommer att dela och vidarebefordra zaps till användarna som har lagts till här istället för dig Sök och lägg till användare Användarnamn eller visningsnamn Användaren %1$s har inte ställt in en blixtfakturaadress för att ta emot satoshis Procent - 25 Delar zaps med Vidarebefordrar zaps till - Blixtpengar hittades inte Betald Plånbok %1$s Fel vid öppning av signeringsapp Underteckningsbegäran avvisad - Inga plånböcker hittades för att betala en blixtfaktura (Fel: %1$s). Installera en blixtpengaplånbok för att använda zaps Inga plånböcker hittades för att betala en blixtfaktura. Installera en blixtpengaplånbok för att använda zaps - diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 86389fc81..16b946606 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -457,6 +457,7 @@ 此群组成员 对成员的解释 为新目标更改名称。 + 粘贴自剪贴板 用于应用程序界面 暗色、亮色或系统主题 自动加载图像和 GIF @@ -467,6 +468,7 @@ 复制笔记 ID 到剪贴板 创建于 规则 + 使用 Amber 登录 更新你的状态 错误解析错误消息 投票按照打闪金额进行加权。 你可以设置最小金额以避免垃圾邮件,也可以设置最大金额以避免大型攻击者攻击民意调查。在两个字段中使用相同的金额以确保每张投票的价值相同。 留空即可接受任何金额。 @@ -483,4 +485,20 @@ 聊天 全球 搜索 + 拆分及转发打闪 + 支持的客户端将会把打闪拆分及转发到在这里添加的用户,而不是你的 + 搜索并添加用户 + 用户名或显示名 + 用户 %1$s 尚未设置接收聪的闪电地址 + 百分比 + 25 + 与此用户拆分打闪 + 将打闪转发到 + 找不到闪电钱包 + 已支付 + 钱包 %1$s + 打开签名应用时出错 + 签名请求被拒绝了 + 找不到支付闪电发票的钱包(错误:%1$s)。请安装闪电钱包来使用打闪 + 找不到支付闪电发票的钱包。请安装闪电钱包来使用打闪 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index ef1ad4e6f..cf16170a2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -458,6 +458,7 @@ 此群組成員 對成員的解釋 為新目標更改名稱。 + 粘貼自剪貼板 用於應用程式界面 暗色、亮色或系統主題 自動加載圖像和 GIF @@ -468,6 +469,7 @@ 將筆記 ID 複製到剪貼簿 創建於 規則 + 使用 Amber 登錄 更新你的狀態 錯誤解析錯誤消息 投票按照打閃金額進行加權。你可以設置最小金額以避免垃圾郵件,也可以設置最大金額以避免大型攻擊者攻擊民意調查。在兩個字段中使用相同的金額則確保每張投票的價值相同。留空即可接受任何金額。 @@ -484,4 +486,20 @@ 聊天 全球 搜索 + 拆分及轉發打閃 + 支持的客戶端將會把打閃拆分及轉發到在這裡添加的用戶,而不是你的 + 搜索並添加用戶 + 用戶名或顯示名 + 用户 %1$s 尚未設置接收聰的閃電地址 + 百分比 + 25 + 與此用戶拆分打閃 + 將打閃轉發到 + 找不到閃電錢包 + 已支付 + 錢包:%1$s + 打開簽署程式時出錯 + 簽署請求被拒絕了 + 找不到支付閃電發票的錢包(錯誤:%1$s)。請安裝一個閃電錢包來使用打閃 + 找不到支付閃電發票的錢包。請安裝一個閃電錢包來使用打閃 From 856b4ca2c18adf1303d6c8fef75587f281a74a64 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Fri, 22 Sep 2023 15:55:45 +0000 Subject: [PATCH 02/10] New Crowdin translations by GitHub Action --- app/src/main/res/values-pt-rBR/strings.xml | 26 +++++----------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 5c4bbb6d1..12145b297 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -12,7 +12,6 @@ Não foi possível descriptografar a mensagem Imagem do grupo Conteúdo explícito - Spam Representação Comportamento ilegal Desconhecido @@ -36,7 +35,6 @@ Faça login com uma chave privada para poder enviar Zaps Faça login com uma chave privada para poder seguir Faça login com uma chave privada para poder remover seguidores - Zaps Contagem de visualizações Impulsionar impulsionado @@ -78,7 +76,6 @@ Falha ao enviar imagem Endereço do Relay Postagens - Bytes Erros Feed principal Feed de mensagens privadas @@ -116,7 +113,6 @@ "Seguindo" "Denúncias" Mais opções - " Relays" Site Endereço Lightning Copiar a chave Nsec (sua senha) para backup @@ -170,9 +166,6 @@ Endereço Nostr nunca agora - h - m - d Nudez Palavrões / Discurso de ódio Denunciar discurso de ódio @@ -267,7 +260,6 @@ Zap mínimo Zap máximo Consenso - (0–100)% Fechar depois dias Enquete está fechada para novos votos @@ -330,7 +322,6 @@ Não Lista de seguidores Seguindo - Global ## Conecte-se através do Tor com o Orbot \n\n1. Instale o [Orbot](https://play.google.com/store/apps/details?id=org.torproject.android) \n2. Iniciar Orbot @@ -347,7 +338,6 @@ Notifica você quando chega uma mensagem privada Zaps Recebidos Notifica quando alguém te manda um zap - %1$s sats De %1$s por %1$s Notificar: @@ -384,7 +374,6 @@ Limitações Países Línguas - Tags Política de postagem Tamanho da mensagem Assinaturas @@ -408,10 +397,8 @@ Transmissão ao vivo encerrada Sair exclui todas as suas informações locais. Certifique-se de fazer backup de suas chaves privadas para evitar a perda de sua conta. Você quer continuar? Tags Seguidas - Relays Ao vivo Comunidade - Chats Postagens Aprovadas Este grupo não tem uma descrição ou regras. Fale com o proprietário para adicionar Esta comunidade não tem uma descrição. Fale com o proprietário para adicionar @@ -442,7 +429,7 @@ Selecione um relay para continuar Encaminhar Zaps para: Os clientes que suportam encaminharão zaps para o endereço lightning ou perfil de usuário abaixo, em vez do seu - "Expor localização como " + Expor localização como Adiciona um Geohash da sua localização à postagem. O público saberá que você está a 5 km (3 milhas) do local atual Adiciona aviso de conteúdo sensível antes de mostrar seu conteúdo. Isso é ideal para qualquer conteúdo NSFW ou conteúdo que algumas pessoas possam considerar ofensivo ou perturbador Novo recurso @@ -453,10 +440,11 @@ Para Assunto Tema da conversa - \@Usuário1, @Usuário2, @Usuário3 + "\@Usuário1, @Usuário2, @Usuário3" Membros deste grupo Explicação aos membros Mudando o nome dos novos objetivos. + Colar da área de transferência Para a interface do aplicativo Tema Escuro, Claro ou Sistema Carregar automaticamente imagens e GIFs @@ -467,6 +455,7 @@ Copiar ID da nota para a área de transferência Criado em Regras + Login com Amber Atualize seu status Erro ao analisar mensagem de erro Os votos são ponderados pelo valor do zap. Você pode definir um valor mínimo para evitar spammers e um valor máximo para evitar que grandes zappers assumam o controle da enquete. Use o mesmo valor em ambos os campos para garantir que cada voto tenha o mesmo valor. Deixe em branco para aceitar qualquer valor. @@ -476,22 +465,17 @@ Falha ao acessar %1$s: %2$s Falha ao analisar o resultado de %1$s: %2$s %1$s falhou com o código %2$s - "Ativo para: " + Ativo para: Início Mensagens diretas Bate-papos - Global Pesquisa - Colar da área de transferência - Login com Amber - OK Dividir e encaminhar Zaps Os clientes que suportam dividirão e encaminharão zaps para os usuários adicionados aqui em vez dos seus Pesquisar e adicionar usuário Nome de usuário ou nome de exibição O usuário %1$s não possui um endereço lightning configurado para receber sats Percentual - 25 Dividindo zaps com Encaminhando zaps para Carteira Lightning não encontrada From d5220324c4c30d97b36d77ce92df033c85b9eb61 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 13:24:37 -0400 Subject: [PATCH 03/10] Turns out the lifecycle owner changes quite a bit and thus it's a requirement to make it the key of a Disposable Effect. --- .../ui/actions/JoinUserOrChannelView.kt | 2 +- .../ui/components/ZoomableContentView.kt | 2 +- .../amethyst/ui/navigation/AppNavigation.kt | 2 +- .../amethyst/ui/qrcode/QrCodeScanner.kt | 5 +---- .../amethyst/ui/screen/RelayFeedView.kt | 18 +++++++++++------- .../ui/screen/RememberForeverStates.kt | 2 +- .../ui/screen/loggedIn/ChannelScreen.kt | 14 +++++++++++--- .../ui/screen/loggedIn/ChatroomListScreen.kt | 2 +- .../ui/screen/loggedIn/ChatroomScreen.kt | 13 ++++++++++--- .../ui/screen/loggedIn/CommunityScreen.kt | 2 +- .../ui/screen/loggedIn/DiscoverScreen.kt | 2 +- .../ui/screen/loggedIn/GeoHashScreen.kt | 10 ++++++---- .../ui/screen/loggedIn/HashtagScreen.kt | 12 +++++++----- .../ui/screen/loggedIn/HiddenUsersScreen.kt | 2 +- .../amethyst/ui/screen/loggedIn/HomeScreen.kt | 2 +- .../amethyst/ui/screen/loggedIn/MainScreen.kt | 18 ++++++++++++++++++ .../ui/screen/loggedIn/NotificationScreen.kt | 2 +- .../ui/screen/loggedIn/ProfileScreen.kt | 18 +++++++++++++----- .../ui/screen/loggedIn/SearchScreen.kt | 2 +- .../ui/screen/loggedIn/ThreadScreen.kt | 9 ++++++--- .../amethyst/ui/screen/loggedIn/VideoScreen.kt | 2 +- 21 files changed, 95 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt index 7ff65c5e1..fb78a0ffb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt @@ -194,7 +194,7 @@ private fun RenderSearch( } } - DisposableEffect(Unit) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Join Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 2e154fa0d..003338d2c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -614,7 +614,7 @@ fun ZoomableImageDialog( ) { val view = LocalView.current - DisposableEffect(key1 = Unit) { + DisposableEffect(key1 = view) { if (Build.VERSION.SDK_INT >= 30) { view.windowInsetsController?.hide( android.view.WindowInsets.Type.systemBars() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index 167bbaf85..56feca6ae 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -284,7 +284,7 @@ fun AppNavigation( actionableNextPage = null } - DisposableEffect(navController) { + DisposableEffect(activity) { val consumer = Consumer { intent -> val uri = intent?.data?.toString() val newPage = uriToRoute(uri) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt index 6e76f2f3f..2ad62d8a4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/qrcode/QrCodeScanner.kt @@ -4,7 +4,6 @@ import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import com.google.zxing.client.android.Intents import com.journeyapps.barcodescanner.ScanContract @@ -40,8 +39,6 @@ fun NIP19QrCodeScanner(onScan: (String?) -> Unit) { @Composable fun SimpleQrCodeScanner(onScan: (String?) -> Unit) { - val lifecycleOwner = LocalLifecycleOwner.current - val qrLauncher = rememberLauncherForActivityResult(ScanContract()) { if (it.contents != null) { @@ -59,7 +56,7 @@ fun SimpleQrCodeScanner(onScan: (String?) -> Unit) { addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN) } - DisposableEffect(lifecycleOwner) { + DisposableEffect(Unit) { qrLauncher.launch(scanOptions) onDispose { } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayFeedView.kt index f41eb6f7f..e3cdcf7fa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayFeedView.kt @@ -70,16 +70,20 @@ class RelayFeedViewModel : ViewModel() { } fun subscribeTo(user: User) { - currentUser = user - user.live().relays.observeForever(listener) - user.live().relayInfo.observeForever(listener) - invalidateData() + if (currentUser != user) { + currentUser = user + user.live().relays.observeForever(listener) + user.live().relayInfo.observeForever(listener) + invalidateData() + } } fun unsubscribeTo(user: User) { - user.live().relays.removeObserver(listener) - user.live().relayInfo.removeObserver(listener) - currentUser = null + if (currentUser == user) { + user.live().relays.removeObserver(listener) + user.live().relayInfo.removeObserver(listener) + currentUser = null + } } private val bundler = BundledUpdate(250, Dispatchers.IO) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RememberForeverStates.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RememberForeverStates.kt index 849551e7e..9f79daf5a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RememberForeverStates.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RememberForeverStates.kt @@ -47,7 +47,7 @@ fun rememberForeverLazyListState( savedOffset.roundToInt() ) } - DisposableEffect(Unit) { + DisposableEffect(scrollState) { onDispose { val lastIndex = scrollState.firstVisibleItemIndex val lastOffset = scrollState.firstVisibleItemScrollOffset diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 84d875069..ae9ff651b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -196,9 +196,6 @@ fun ChannelScreen( val lifeCycleOwner = LocalLifecycleOwner.current LaunchedEffect(Unit) { - NostrChannelDataSource.start() - feedViewModel.invalidateData(true) - launch(Dispatchers.IO) { newPostModel.imageUploadingError.collect { error -> withContext(Dispatchers.Main) { @@ -209,6 +206,17 @@ fun ChannelScreen( } DisposableEffect(accountViewModel) { + NostrChannelDataSource.loadMessagesBetween(accountViewModel.account, channel) + NostrChannelDataSource.start() + feedViewModel.invalidateData(true) + + onDispose { + NostrChannelDataSource.clear() + NostrChannelDataSource.stop() + } + } + + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Channel Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt index 39b60a649..0051e737c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt @@ -67,7 +67,7 @@ fun ChatroomListScreen( WatchAccountForListScreen(knownFeedViewModel, newFeedViewModel, accountViewModel) val lifeCycleOwner = LocalLifecycleOwner.current - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { NostrChatroomListDataSource.start() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt index 7f02e78eb..0ac3bf90d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt @@ -234,9 +234,6 @@ fun ChatroomScreen( LaunchedEffect(room, accountViewModel) { launch(Dispatchers.IO) { - NostrChatroomDataSource.start() - feedViewModel.invalidateData() - newPostModel.imageUploadingError.collect { error -> withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() @@ -246,6 +243,16 @@ fun ChatroomScreen( } DisposableEffect(room, accountViewModel) { + NostrChatroomDataSource.loadMessagesBetween(accountViewModel.account, room) + NostrChatroomDataSource.start() + feedViewModel.invalidateData() + + onDispose { + NostrChatroomDataSource.stop() + } + } + + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Private Message Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/CommunityScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/CommunityScreen.kt index 011c6dd6b..bd80d9fa0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/CommunityScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/CommunityScreen.kt @@ -56,7 +56,7 @@ fun CommunityScreen(note: AddressableNote, feedViewModel: NostrCommunityFeedView feedViewModel.invalidateData() } - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Community Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DiscoverScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DiscoverScreen.kt index 65df80ee9..a9febcdca 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DiscoverScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DiscoverScreen.kt @@ -88,7 +88,7 @@ fun DiscoverScreen( accountViewModel = accountViewModel ) - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Discovery Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/GeoHashScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/GeoHashScreen.kt index 8de031058..36bec3b1d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/GeoHashScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/GeoHashScreen.kt @@ -65,12 +65,16 @@ fun GeoHashScreen(tag: String, feedViewModel: NostrGeoHashFeedViewModel, account NostrGeohashDataSource.loadHashtag(tag) - LaunchedEffect(tag) { + DisposableEffect(tag) { NostrGeohashDataSource.start() feedViewModel.invalidateData() + onDispose { + NostrGeohashDataSource.loadHashtag(null) + NostrGeohashDataSource.stop() + } } - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Hashtag Start") @@ -88,8 +92,6 @@ fun GeoHashScreen(tag: String, feedViewModel: NostrGeoHashFeedViewModel, account lifeCycleOwner.lifecycle.addObserver(observer) onDispose { lifeCycleOwner.lifecycle.removeObserver(observer) - NostrGeohashDataSource.loadHashtag(null) - NostrGeohashDataSource.stop() } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt index f0c8d3411..edd2cfe21 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.material.Divider import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -61,12 +60,17 @@ fun HashtagScreen(tag: String, feedViewModel: NostrHashtagFeedViewModel, account NostrHashtagDataSource.loadHashtag(tag) - LaunchedEffect(tag) { + DisposableEffect(tag) { NostrHashtagDataSource.start() feedViewModel.invalidateData() + + onDispose { + NostrHashtagDataSource.loadHashtag(null) + NostrHashtagDataSource.stop() + } } - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Hashtag Start") @@ -84,8 +88,6 @@ fun HashtagScreen(tag: String, feedViewModel: NostrHashtagFeedViewModel, account lifeCycleOwner.lifecycle.addObserver(observer) onDispose { lifeCycleOwner.lifecycle.removeObserver(observer) - NostrHashtagDataSource.loadHashtag(null) - NostrHashtagDataSource.stop() } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt index 7f78f7bbe..69938b796 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt @@ -61,7 +61,7 @@ fun HiddenUsersScreen( ) { val lifeCycleOwner = LocalLifecycleOwner.current - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Hidden Users Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt index 3b146279b..bc0de2b7e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt @@ -63,7 +63,7 @@ fun HomeScreen( } val lifeCycleOwner = LocalLifecycleOwner.current - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { NostrHomeDataSource.invalidateFilters() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt index 4bccf3ee7..1c47e186d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.State @@ -33,7 +34,10 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.compose.currentBackStackEntryAsState @@ -323,6 +327,20 @@ fun WatchNavStateToUpdateBarVisibility(navState: State, bott LaunchedEffect(key1 = navState.value) { bottomBarOffsetHeightPx.value = 0f } + + val lifeCycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifeCycleOwner) { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_RESUME) { + bottomBarOffsetHeightPx.value = 0f + } + } + + lifeCycleOwner.lifecycle.addObserver(observer) + onDispose { + lifeCycleOwner.lifecycle.removeObserver(observer) + } + } } @Composable diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt index cba29abb1..d6a644ad1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt @@ -75,7 +75,7 @@ fun NotificationScreen( CheckifItNeedsToRequestNotificationPermission() val lifeCycleOwner = LocalLifecycleOwner.current - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { NostrAccountDataSource.invalidateFilters() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index cd22683a5..2ef5d60cb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -238,11 +238,15 @@ fun ProfileScreen( val lifeCycleOwner = LocalLifecycleOwner.current - LaunchedEffect(Unit) { + DisposableEffect(accountViewModel) { NostrUserProfileDataSource.start() + onDispose { + NostrUserProfileDataSource.loadUserProfile(null) + NostrUserProfileDataSource.stop() + } } - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Profidle Start") @@ -259,9 +263,6 @@ fun ProfileScreen( lifeCycleOwner.lifecycle.addObserver(observer) onDispose { lifeCycleOwner.lifecycle.removeObserver(observer) - println("Profile Dispose") - NostrUserProfileDataSource.loadUserProfile(null) - NostrUserProfileDataSource.stop() } } @@ -1567,6 +1568,13 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel, nav: (String) -> U val lifeCycleOwner = LocalLifecycleOwner.current DisposableEffect(user) { + feedViewModel.subscribeTo(user) + onDispose { + feedViewModel.unsubscribeTo(user) + } + } + + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Profile Relay Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt index 49acad049..1b5636d72 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt @@ -104,7 +104,7 @@ fun SearchScreen( WatchAccountForSearchScreen(accountViewModel) - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Search Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt index 8df68e327..57eaa662d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner @@ -28,11 +27,15 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, nav: (Stri NostrThreadDataSource.loadThread(noteId) - LaunchedEffect(noteId) { + DisposableEffect(noteId) { feedViewModel.invalidateData(true) + onDispose { + NostrThreadDataSource.loadThread(null) + NostrThreadDataSource.stop() + } } - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Thread Start") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 0234adcd6..d2edde0df 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -89,7 +89,7 @@ fun VideoScreen( WatchAccountForVideoScreen(videoFeedView = videoFeedView, accountViewModel = accountViewModel) - DisposableEffect(accountViewModel) { + DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { println("Video Start") From 6c025f72749fe72b1f9199a31de0210c5b3d7647 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 14:19:07 -0400 Subject: [PATCH 04/10] Speeding up the display of unused hashtags --- .../amethyst/ui/note/NoteCompose.kt | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 09def4997..ae8e2ae40 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -3101,26 +3101,28 @@ fun DisplayUncitedHashtags( eventContent: String, nav: (String) -> Unit ) { - val hasHashtags = remember { + val hasHashtags = remember(eventContent) { hashtags.isNotEmpty() } if (hasHashtags) { + val unusedHashtags = remember(eventContent) { + hashtags.filter { !eventContent.contains(it, true) } + } + FlowRow( modifier = remember { Modifier.padding(top = 5.dp) } ) { - hashtags.forEach { hashtag -> - if (!eventContent.contains(hashtag, true)) { - ClickableText( - text = AnnotatedString("#$hashtag "), - onClick = { nav("Hashtag/$hashtag") }, - style = LocalTextStyle.current.copy( - color = MaterialTheme.colors.primary.copy( - alpha = 0.52f - ) + unusedHashtags.forEach { hashtag -> + ClickableText( + text = remember { AnnotatedString("#$hashtag ") }, + onClick = { nav("Hashtag/$hashtag") }, + style = LocalTextStyle.current.copy( + color = MaterialTheme.colors.primary.copy( + alpha = 0.52f ) ) - } + ) } } } From 7cc089f0ee4a943868f8317b661f553ad817e750 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 14:19:29 -0400 Subject: [PATCH 05/10] Video playback creation must be run in the main thread :( --- .../amethyst/ui/components/VideoView.kt | 173 +++++++++--------- 1 file changed, 83 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt index 84c02a0d4..454b89824 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt @@ -32,7 +32,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -206,34 +205,34 @@ fun VideoViewInner( ) } + val mediaItem = remember(videoUri) { + MediaItem.Builder() + .setMediaId(videoUri) + .setUri(videoUri) + .setMediaMetadata( + MediaMetadata.Builder() + .setArtist(authorName?.ifBlank { null }) + .setTitle(title?.ifBlank { null } ?: videoUri) + .setArtworkUri( + try { + if (artworkUri != null) { + Uri.parse(artworkUri) + } else { + null + } + } catch (e: Exception) { + null + } + ) + .build() + ) + .build() + } + if (!automaticallyStartPlayback.value) { ImageUrlWithDownloadButton(url = videoUri, showImage = automaticallyStartPlayback) } else { VideoPlayerActiveMutex(videoUri) { activeOnScreen -> - val mediaItem = remember(videoUri) { - MediaItem.Builder() - .setMediaId(videoUri) - .setUri(videoUri) - .setMediaMetadata( - MediaMetadata.Builder() - .setArtist(authorName?.ifBlank { null }) - .setTitle(title?.ifBlank { null } ?: videoUri) - .setArtworkUri( - try { - if (artworkUri != null) { - Uri.parse(artworkUri) - } else { - null - } - } catch (e: Exception) { - null - } - ) - .build() - ) - .build() - } - GetVideoController( mediaItem = mediaItem, videoUri = videoUri, @@ -282,56 +281,52 @@ fun GetVideoController( UUID.randomUUID().toString() } - val scope = rememberCoroutineScope() - // Prepares a VideoPlayer from the foreground service. DisposableEffect(key1 = videoUri) { // If it is not null, the user might have come back from a playing video, like clicking on // the notification of the video player. if (controller.value == null) { - scope.launch(Dispatchers.IO) { - PlaybackClientController.prepareController( - uid, - videoUri, - nostrUriCallback, - context - ) { - // checks again because of race conditions. - if (controller.value == null) { // still prone to race conditions. - controller.value = it + PlaybackClientController.prepareController( + uid, + videoUri, + nostrUriCallback, + context + ) { + // checks again because of race conditions. + if (controller.value == null) { // still prone to race conditions. + controller.value = it - if (!it.isPlaying) { - if (keepPlayingMutex?.isPlaying == true) { + if (!it.isPlaying) { + if (keepPlayingMutex?.isPlaying == true) { + // There is a video playing, start this one on mute. + controller.value?.volume = 0f + } else { + // There is no other video playing. Use the default mute state to + // decide if sound is on or not. + controller.value?.volume = if (defaultToStart) 0f else 1f + } + } + + controller.value?.setMediaItem(mediaItem) + controller.value?.prepare() + } else if (controller.value != it) { + // discards the new controller because there is an existing one + it.stop() + it.release() + + controller.value?.let { + if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) { + if (it.isPlaying) { // There is a video playing, start this one on mute. - controller.value?.volume = 0f + it.volume = 0f } else { // There is no other video playing. Use the default mute state to // decide if sound is on or not. - controller.value?.volume = if (defaultToStart) 0f else 1f + it.volume = if (defaultToStart) 0f else 1f } - } - controller.value?.setMediaItem(mediaItem) - controller.value?.prepare() - } else if (controller.value != it) { - // discards the new controller because there is an existing one - it.stop() - it.release() - - controller.value?.let { - if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) { - if (it.isPlaying) { - // There is a video playing, start this one on mute. - it.volume = 0f - } else { - // There is no other video playing. Use the default mute state to - // decide if sound is on or not. - it.volume = if (defaultToStart) 0f else 1f - } - - it.setMediaItem(mediaItem) - it.prepare() - } + it.setMediaItem(mediaItem) + it.prepare() } } } @@ -372,35 +367,33 @@ fun GetVideoController( // if the controller is null, restarts the controller with a new one // if the controller is not null, just continue playing what the controller was playing if (controller.value == null) { - scope.launch(Dispatchers.IO) { - PlaybackClientController.prepareController( - uid, - videoUri, - nostrUriCallback, - context - ) { - // checks again to make sure no other thread has created a controller. - if (controller.value == null) { - controller.value = it + PlaybackClientController.prepareController( + uid, + videoUri, + nostrUriCallback, + context + ) { + // checks again to make sure no other thread has created a controller. + if (controller.value == null) { + controller.value = it - if (!it.isPlaying) { - if (keepPlayingMutex?.isPlaying == true) { - // There is a video playing, start this one on mute. - controller.value?.volume = 0f - } else { - // There is no other video playing. Use the default mute state to - // decide if sound is on or not. - controller.value?.volume = if (defaultToStart) 0f else 1f - } + if (!it.isPlaying) { + if (keepPlayingMutex?.isPlaying == true) { + // There is a video playing, start this one on mute. + controller.value?.volume = 0f + } else { + // There is no other video playing. Use the default mute state to + // decide if sound is on or not. + controller.value?.volume = if (defaultToStart) 0f else 1f } - - controller.value?.setMediaItem(mediaItem) - controller.value?.prepare() - } else if (controller.value != it) { - // discards the new controller because there is an existing one - it.stop() - it.release() } + + controller.value?.setMediaItem(mediaItem) + controller.value?.prepare() + } else if (controller.value != it) { + // discards the new controller because there is an existing one + it.stop() + it.release() } } } @@ -697,7 +690,7 @@ fun ControlWhenPlayerIsActive( val view = LocalView.current // Keeps the screen on while playing and viewing videos. - DisposableEffect(key1 = controller) { + DisposableEffect(key1 = controller, key2 = view) { val listener = object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { // doesn't consider the mutex because the screen can turn off if the video From 6aff31886e089aee0fff0b79fab6fe0d882a927c Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 14:37:51 -0400 Subject: [PATCH 06/10] Fixes website URLs without schema (http://, https://, etc) --- .../amethyst/ui/screen/loggedIn/ProfileScreen.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index 2ef5d60cb..b3df05f20 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -955,7 +955,17 @@ private fun DrawAdditionalInfo( ClickableText( text = AnnotatedString(website.removePrefix("https://")), - onClick = { website.let { runCatching { uri.openUri(it) } } }, + onClick = { + website.let { + runCatching { + if (it.contains("://")) { + uri.openUri(it) + } else { + uri.openUri("http://$it") + } + } + } + }, style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary), modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp) ) From 82e2d15aafa7861c31eac5fb914a3dfb69a10ad4 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 14:51:21 -0400 Subject: [PATCH 07/10] Moves Live streaming from the top bar to the screen to avoid cancelling the video on scrolling. --- .../com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt | 7 ------- .../amethyst/ui/screen/loggedIn/ChannelScreen.kt | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt index e44dfa9ae..ef2a1dd2f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt @@ -64,7 +64,6 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS -import com.vitorpamplona.amethyst.model.LiveActivitiesChannel import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.NostrAccountDataSource import com.vitorpamplona.amethyst.service.NostrChannelDataSource @@ -110,7 +109,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.LongChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.LongRoomHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.RoomNameOnlyDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShortChannelHeader -import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowVideoStreaming import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer @@ -355,11 +353,6 @@ private fun ChannelTopBar( ) { LoadChannel(baseChannelHex = id, accountViewModel) { baseChannel -> FlexibleTopBarWithBackButton( - prefixRow = { - if (baseChannel is LiveActivitiesChannel) { - ShowVideoStreaming(baseChannel, accountViewModel) - } - }, title = { ShortChannelHeader( baseChannel = baseChannel, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index ae9ff651b..1c1cc0333 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -248,6 +248,9 @@ fun ChannelScreen( .weight(1f, true) } ) { + if (channel is LiveActivitiesChannel) { + ShowVideoStreaming(channel, accountViewModel) + } RefreshingChatroomFeedView( viewModel = feedViewModel, accountViewModel = accountViewModel, From bdc2cbbb99a5e72b53687607f037c450e6afe9f5 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 16:23:19 -0400 Subject: [PATCH 08/10] Adds Mutiny button --- .../amethyst/service/NostrAccountDataSource.kt | 2 ++ .../amethyst/ui/note/UpdateZapAmountDialog.kt | 13 +++++++++++++ .../amethyst/ui/screen/LnZapFeedViewModel.kt | 2 ++ .../amethyst/ui/screen/UserFeedViewModel.kt | 2 ++ app/src/main/res/mipmap-hdpi/mutiny.png | Bin 0 -> 4779 bytes app/src/main/res/mipmap-mdpi/mutiny.png | Bin 0 -> 2719 bytes app/src/main/res/mipmap-xhdpi/mutiny.png | Bin 0 -> 6520 bytes app/src/main/res/mipmap-xxhdpi/mutiny.png | Bin 0 -> 11025 bytes app/src/main/res/mipmap-xxxhdpi/mutiny.png | Bin 0 -> 16565 bytes 9 files changed, 19 insertions(+) create mode 100644 app/src/main/res/mipmap-hdpi/mutiny.png create mode 100644 app/src/main/res/mipmap-mdpi/mutiny.png create mode 100644 app/src/main/res/mipmap-xhdpi/mutiny.png create mode 100644 app/src/main/res/mipmap-xxhdpi/mutiny.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/mutiny.png diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt index df3e1f2cb..309a16ba3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt @@ -145,6 +145,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") { } override fun consume(event: Event, relay: Relay) { + checkNotInMainThread() + if (LocalCache.justVerify(event)) { if (event is GiftWrapEvent) { val privateKey = account.keyPair.privKey diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt index e82dfc6af..526ab3417 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt @@ -386,6 +386,19 @@ fun UpdateZapAmountDialog( Modifier.weight(1f) ) + /* TODO: Find a way to open this in the PWA + IconButton(onClick = { + onClose() + runCatching { uri.openUri("https://app.mutinywallet.com/settings/connections?callbackUri=nostr+walletconnect&name=Amethyst") } + }) { + Icon( + painter = painterResource(R.mipmap.mutiny), + null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + }*/ + IconButton(onClick = { onClose() runCatching { uri.openUri("https://nwc.getalby.com/apps/new?c=Amethyst") } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedViewModel.kt index e3a5af241..ee769b98c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedViewModel.kt @@ -87,6 +87,8 @@ open class LnZapFeedViewModel(val dataSource: FeedFilter) : View checkNotInMainThread() LocalCache.live.newEventBundles.collect { newNotes -> + checkNotInMainThread() + invalidateData() } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt index b076e54b5..608d86ce7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt @@ -118,6 +118,8 @@ open class UserFeedViewModel(val dataSource: FeedFilter) : ViewModel(), In checkNotInMainThread() LocalCache.live.newEventBundles.collect { newNotes -> + checkNotInMainThread() + invalidateData() } } diff --git a/app/src/main/res/mipmap-hdpi/mutiny.png b/app/src/main/res/mipmap-hdpi/mutiny.png new file mode 100644 index 0000000000000000000000000000000000000000..7f63351c7adfda11bd30419e935d4a414e1957de GIT binary patch literal 4779 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 FV1j2sS|$Jh literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/mutiny.png b/app/src/main/res/mipmap-mdpi/mutiny.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1771a733fe2176f37c19bf1061af71e763a2ba GIT binary patch literal 2719 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 Z{2wnfq}p)cL4N=M002ovPDHLkV1g?$F^~WN literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/mutiny.png b/app/src/main/res/mipmap-xhdpi/mutiny.png new file mode 100644 index 0000000000000000000000000000000000000000..795afb6bc54b47be1694123001c6f1b420bd9e15 GIT binary patch literal 6520 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 LNkvXXu0mjfsJpp{ literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/mutiny.png b/app/src/main/res/mipmap-xxxhdpi/mutiny.png new file mode 100644 index 0000000000000000000000000000000000000000..7352042ea0287a08fc8d9ae9dd54baeb276df8f5 GIT binary patch literal 16565 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 literal 0 HcmV?d00001 From 19557bf358ba317ddf3d26669113fbcad90876b7 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 16:50:25 -0400 Subject: [PATCH 09/10] Filters zap payments outside the established min-max parameters on polls. --- .../amethyst/ui/note/PollNoteViewModel.kt | 67 ++++++++++++++----- .../quartz/events/PollNoteEvent.kt | 24 +++++-- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt index e5cd1c436..b29068ac0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt @@ -33,9 +33,12 @@ class PollNoteViewModel : ViewModel() { private var pollEvent: PollNoteEvent? = null private var pollOptions: Map? = null - private var valueMaximum: Int? = null - private var valueMinimum: Int? = null - private var closedAt: Int? = null + private var valueMaximum: Long? = null + private var valueMinimum: Long? = null + private var valueMaximumBD: BigDecimal? = null + private var valueMinimumBD: BigDecimal? = null + + private var closedAt: Long? = null private var consensusThreshold: BigDecimal? = null private var totalZapped: BigDecimal = BigDecimal.ZERO @@ -49,10 +52,12 @@ class PollNoteViewModel : ViewModel() { pollNote = note pollEvent = pollNote?.event as PollNoteEvent pollOptions = pollEvent?.pollOptions() - valueMaximum = pollEvent?.getTagInt(VALUE_MAXIMUM) - valueMinimum = pollEvent?.getTagInt(VALUE_MINIMUM) - consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal() - closedAt = pollEvent?.getTagInt(CLOSED_AT) + valueMaximum = pollEvent?.getTagLong(VALUE_MAXIMUM) + valueMinimum = pollEvent?.getTagLong(VALUE_MINIMUM) + valueMinimumBD = valueMinimum?.let { BigDecimal(it) } + valueMaximumBD = valueMaximum?.let { BigDecimal(it) } + consensusThreshold = pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal() + closedAt = pollEvent?.getTagLong(CLOSED_AT) } fun refreshTallies() { @@ -110,6 +115,29 @@ class PollNoteViewModel : ViewModel() { } catch (e: Exception) { null } } + fun isValidInputVoteAmount(amount: BigDecimal?): Boolean { + if (amount == null) { + return false + } else if (valueMinimum == null && valueMaximum == null) { + if (amount > BigDecimal.ZERO) { + return true + } + } else if (valueMinimum == null) { + if (amount > BigDecimal.ZERO && amount <= valueMaximumBD!!) { + return true + } + } else if (valueMaximum == null) { + if (amount >= valueMinimumBD!!) { + return true + } + } else { + if ((valueMinimumBD!! <= amount) && (amount <= valueMaximumBD!!)) { + return true + } + } + return false + } + fun isValidInputVoteAmount(amount: Long?): Boolean { if (amount == null) { return false @@ -145,24 +173,29 @@ class PollNoteViewModel : ViewModel() { private fun zappedPollOptionAmount(option: Int): BigDecimal { return pollNote?.zaps?.values?.sumOf { val event = it?.event as? LnZapEvent - if (event?.zappedPollOption() == option) { - event.amount ?: BigDecimal(0) + val zapAmount = event?.amount ?: BigDecimal.ZERO + val isValidAmount = isValidInputVoteAmount(event?.amount) + + if (isValidAmount && event?.zappedPollOption() == option) { + zapAmount } else { - BigDecimal(0) + BigDecimal.ZERO } - } ?: BigDecimal(0) + } ?: BigDecimal.ZERO } private fun totalZapped(): BigDecimal { return pollNote?.zaps?.values?.sumOf { val zapEvent = (it?.event as? LnZapEvent) + val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO + val isValidAmount = isValidInputVoteAmount(zapEvent?.amount) - if (zapEvent?.zappedPollOption() != null) { - zapEvent.amount ?: BigDecimal(0) + if (isValidAmount && zapEvent?.zappedPollOption() != null) { + zapAmount } else { - BigDecimal(0) + BigDecimal.ZERO } - } ?: BigDecimal(0) + } ?: BigDecimal.ZERO } fun createZapOptionsThatMatchThePollingParameters(): List { @@ -176,8 +209,8 @@ class PollNoteViewModel : ViewModel() { } } } - valueMinimum?.let { options.add(it.toLong()) } - valueMaximum?.let { options.add(it.toLong()) } + valueMinimum?.let { options.add(it) } + valueMaximum?.let { options.add(it) } return options.toSet().sorted() } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt index 5414afe3b..739964019 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt @@ -27,13 +27,16 @@ class PollNoteEvent( tags.filter { it.size > 2 && it[0] == POLL_OPTION } .associate { it[1].toInt() to it[2] } - fun getTagInt(property: String): Int? { + fun minimumAmount() = tags.firstOrNull() { it.size > 1 && it[0] == VALUE_MINIMUM }?.getOrNull(1)?.toLongOrNull() + fun maximumAmount() = tags.firstOrNull() { it.size > 1 && it[0] == VALUE_MAXIMUM }?.getOrNull(1)?.toLongOrNull() + + fun getTagLong(property: String): Long? { val number = tags.firstOrNull() { it.size > 1 && it[0] == property }?.get(1) return if (number.isNullOrBlank() || number == "null") { null } else { - number.toInt() + number.toLong() } } @@ -71,11 +74,18 @@ class PollNoteEvent( pollOptions.forEach { poll_op -> tags.add(listOf(POLL_OPTION, poll_op.key.toString(), poll_op.value)) } - tags.add(listOf(VALUE_MAXIMUM, valueMaximum.toString())) - tags.add(listOf(VALUE_MINIMUM, valueMinimum.toString())) - tags.add(listOf(CONSENSUS_THRESHOLD, consensusThreshold.toString())) - tags.add(listOf(CLOSED_AT, closedAt.toString())) - + valueMaximum?.let { + tags.add(listOf(VALUE_MAXIMUM, valueMaximum.toString())) + } + valueMinimum?.let { + tags.add(listOf(VALUE_MINIMUM, valueMinimum.toString())) + } + consensusThreshold?.let { + tags.add(listOf(CONSENSUS_THRESHOLD, consensusThreshold.toString())) + } + closedAt?.let { + tags.add(listOf(CLOSED_AT, closedAt.toString())) + } zapReceiver?.forEach { tags.add(listOf("zap", it.lnAddressOrPubKeyHex, it.relay ?: "", it.weight.toString())) } From 30ded17581a651d0a831074e82cbda8d6a589e01 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 22 Sep 2023 16:57:14 -0400 Subject: [PATCH 10/10] v0.77.8 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d312576b3..cbc3868cb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.vitorpamplona.amethyst" minSdk 26 targetSdk 34 - versionCode 304 - versionName "0.77.7" + versionCode 305 + versionName "0.77.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables {