From 3b019b45d0dd055eb02deb27d14bdcf354715ca3 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sat, 26 Sep 2026 22:41:40 -0400 Subject: [PATCH 01/19] fix(browser): the pill's site-settings line only told the truth after you opened page info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sitePermissions` was filled in exactly one place — inside `showPageInfo`, from the answer the broker sends when the sheet is about to open. So a freshly opened window said "Nothing allowed yet" about a site it had already been granted the camera on, and only stopped saying it once you opened the sheet that made it ask. Page info itself was always right; the summary above it was not. The registry lives in the main process and does not call back, so the window has to ask. It now asks when the origin changes, which is when the answer can differ, and keeps the last origin so a same-site navigation is not a round trip per page. An edit made in the sheet also folds straight into the summary. The registry stays authoritative — this only stops the line behind the sheet disagreeing with the sheet in front of it until the next navigation. Found while testing "Open full screen": that window reported "Nothing allowed yet" for brainstorm.world while its own page info showed Camera = Allow. Verified on an SM-T220: the line now reads "Camera allowed" right after the edit, and again in a brand-new window without opening page info at all. Co-Authored-By: Claude Opus 5 (1M context) --- .../napplethost/NappletBrowserActivity.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt index a32f8f27d1..4525300d1b 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt @@ -155,6 +155,9 @@ class NappletBrowserActivity : ComponentActivity() { // same address don't each trigger a round-trip. private var lastFavoriteQueryUrl: String? = null + /** The origin whose site permissions the pill is already showing. */ + private var lastSitePermissionOrigin: String? = null + // What Recents shows for this task: the page's title, favicon and theme colour. private var pageTitle: String? = null private var pageIcon: Bitmap? = null @@ -771,6 +774,20 @@ class NappletBrowserActivity : ComponentActivity() { copy(chrome = chrome.copy(url = url), title = if (newSite) BrowserChrome.displayHost(url) else title, isFavorite = false) } } + // The pill's "Site settings" line summarises this origin's camera/mic/location answers, and the + // registry that holds them lives in the main process. Until this was asked for on navigation, the + // only thing that ever filled it was opening page info — so a window said "Nothing allowed yet" + // about a site it had already been granted the camera on, and only stopped lying once you opened + // the sheet that made it ask. + BrowserChrome.originOf(url)?.let { origin -> + if (origin != lastSitePermissionOrigin) { + lastSitePermissionOrigin = origin + querySitePermissions(origin) { decisions -> + updateUi { copy(sitePermissions = decisions.filterValues { it != Decision.ASK }) } + } + } + } + if (url == lastFavoriteQueryUrl) return lastFavoriteQueryUrl = url val msg = @@ -1383,6 +1400,14 @@ class NappletBrowserActivity : ComponentActivity() { decision: Decision, ) { BrowserChrome.originOf(currentUrl())?.let { rememberSitePermission(it, permission, decision) } + // Keep the pill's summary in step with the sheet the change was made in. The registry + // is authoritative and lives in the other process, but it does not call back, so an + // edit made here would otherwise not show up until the next navigation. + updateUi { + val next = sitePermissions.toMutableMap() + if (decision == Decision.ASK) next.remove(permission) else next[permission] = decision + copy(sitePermissions = next) + } } override fun onClearSiteData() { From 741d8e9cdbf9fafc74544b8fd0934b5d2364ee4c Mon Sep 17 00:00:00 2001 From: vitorpamplona <532031+vitorpamplona@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:46:51 +0000 Subject: [PATCH 02/19] chore: sync Crowdin translations and seed translator npub placeholders --- .../composeResources/values-ar-rSA/strings.xml | 1 + .../composeResources/values-bn-rBD/strings.xml | 1 + .../composeResources/values-cs/strings.xml | 1 + .../composeResources/values-de-rDE/strings.xml | 1 + .../composeResources/values-el-rGR/strings.xml | 1 + .../composeResources/values-en-rGB/strings.xml | 1 + .../composeResources/values-eo-rUY/strings.xml | 1 + .../composeResources/values-es-rES/strings.xml | 1 + .../composeResources/values-es-rMX/strings.xml | 1 + .../composeResources/values-es-rUS/strings.xml | 1 + .../composeResources/values-fa-rIR/strings.xml | 1 + .../composeResources/values-fi-rFI/strings.xml | 1 + .../composeResources/values-fr-rCA/strings.xml | 1 + .../composeResources/values-fr-rFR/strings.xml | 1 + .../composeResources/values-hi-rIN/strings.xml | 1 + .../composeResources/values-hu-rHU/strings.xml | 1 + .../composeResources/values-in-rID/strings.xml | 1 + .../composeResources/values-it-rIT/strings.xml | 1 + .../composeResources/values-ja-rJP/strings.xml | 1 + .../composeResources/values-ko-rKR/strings.xml | 1 + .../composeResources/values-lv-rLV/strings.xml | 1 + .../composeResources/values-nl-rNL/strings.xml | 1 + .../composeResources/values-pl-rPL/strings.xml | 1 + .../composeResources/values-pt-rBR/strings.xml | 1 + .../composeResources/values-pt-rPT/strings.xml | 1 + .../composeResources/values-ru-rRU/strings.xml | 1 + .../composeResources/values-ru-rUA/strings.xml | 1 + .../composeResources/values-sl-rSI/strings.xml | 1 + .../composeResources/values-sr-rSP/strings.xml | 1 + .../composeResources/values-sv-rSE/strings.xml | 1 + .../composeResources/values-sw/strings.xml | 1 + .../composeResources/values-ta-rIN/strings.xml | 1 + .../composeResources/values-th-rTH/strings.xml | 1 + .../composeResources/values-tr-rTR/strings.xml | 1 + .../composeResources/values-uk-rUA/strings.xml | 1 + .../composeResources/values-uz-rUZ/strings.xml | 1 + .../composeResources/values-vi-rVN/strings.xml | 1 + .../composeResources/values-zh-rCN/strings.xml | 1 + .../composeResources/values-zh-rHK/strings.xml | 1 + .../composeResources/values-zh-rSG/strings.xml | 1 + .../composeResources/values-zh-rTW/strings.xml | 1 + docs/changelog/translators.json | 12 ++++++------ 42 files changed, 47 insertions(+), 6 deletions(-) diff --git a/commonsUI/src/commonMain/composeResources/values-ar-rSA/strings.xml b/commonsUI/src/commonMain/composeResources/values-ar-rSA/strings.xml index c9b3131eff..e9970306ff 100644 --- a/commonsUI/src/commonMain/composeResources/values-ar-rSA/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ar-rSA/strings.xml @@ -3081,4 +3081,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-bn-rBD/strings.xml b/commonsUI/src/commonMain/composeResources/values-bn-rBD/strings.xml index 75664b26ee..b34056a786 100644 --- a/commonsUI/src/commonMain/composeResources/values-bn-rBD/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-bn-rBD/strings.xml @@ -2956,4 +2956,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml index b21d6ee05a..3f818c83b8 100644 --- a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml @@ -5139,4 +5139,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml index 17617a8ae7..94482ed9f7 100644 --- a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml @@ -4923,4 +4923,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-el-rGR/strings.xml b/commonsUI/src/commonMain/composeResources/values-el-rGR/strings.xml index ffe6a08835..2fe2f7373b 100644 --- a/commonsUI/src/commonMain/composeResources/values-el-rGR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-el-rGR/strings.xml @@ -2904,4 +2904,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-en-rGB/strings.xml b/commonsUI/src/commonMain/composeResources/values-en-rGB/strings.xml index 44a5f2359e..fa8183a377 100644 --- a/commonsUI/src/commonMain/composeResources/values-en-rGB/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-en-rGB/strings.xml @@ -34,4 +34,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-eo-rUY/strings.xml b/commonsUI/src/commonMain/composeResources/values-eo-rUY/strings.xml index 35481e4172..ce975e7024 100644 --- a/commonsUI/src/commonMain/composeResources/values-eo-rUY/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-eo-rUY/strings.xml @@ -2960,4 +2960,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-es-rES/strings.xml b/commonsUI/src/commonMain/composeResources/values-es-rES/strings.xml index 45b069c10c..3035a49c08 100644 --- a/commonsUI/src/commonMain/composeResources/values-es-rES/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-es-rES/strings.xml @@ -2976,4 +2976,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-es-rMX/strings.xml b/commonsUI/src/commonMain/composeResources/values-es-rMX/strings.xml index d941f9ea99..791a4dc908 100644 --- a/commonsUI/src/commonMain/composeResources/values-es-rMX/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-es-rMX/strings.xml @@ -2969,4 +2969,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-es-rUS/strings.xml b/commonsUI/src/commonMain/composeResources/values-es-rUS/strings.xml index 3e6060ab9c..23d497b889 100644 --- a/commonsUI/src/commonMain/composeResources/values-es-rUS/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-es-rUS/strings.xml @@ -2969,4 +2969,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-fa-rIR/strings.xml b/commonsUI/src/commonMain/composeResources/values-fa-rIR/strings.xml index df5f3b4363..3ecb2f982d 100644 --- a/commonsUI/src/commonMain/composeResources/values-fa-rIR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-fa-rIR/strings.xml @@ -2970,4 +2970,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-fi-rFI/strings.xml b/commonsUI/src/commonMain/composeResources/values-fi-rFI/strings.xml index f2784cced4..b9116115b5 100644 --- a/commonsUI/src/commonMain/composeResources/values-fi-rFI/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-fi-rFI/strings.xml @@ -2936,4 +2936,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-fr-rCA/strings.xml b/commonsUI/src/commonMain/composeResources/values-fr-rCA/strings.xml index 677d4afb32..e31bf3e41c 100644 --- a/commonsUI/src/commonMain/composeResources/values-fr-rCA/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-fr-rCA/strings.xml @@ -2860,4 +2860,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-fr-rFR/strings.xml b/commonsUI/src/commonMain/composeResources/values-fr-rFR/strings.xml index 76a4f93daf..15fe140875 100644 --- a/commonsUI/src/commonMain/composeResources/values-fr-rFR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-fr-rFR/strings.xml @@ -3153,4 +3153,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml index cde8a721e1..ab38725872 100644 --- a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml @@ -5263,6 +5263,7 @@ हस्ताक्षररहित। इसलिए यह कर्ता केवल एक कथन है प्रकार %1$d का वस्तु। जिसे यह ग्राहक चित्रित नहीं करता %1$d अष्टकों का अमुक जिसे यह ग्राहक पढता नहीं + पुनःबाधित करें %1$s को रखें %1$s पुनःजुडें %1$s diff --git a/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml b/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml index f7872f3f26..bc0ba670c6 100644 --- a/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml @@ -5263,6 +5263,7 @@ Aláírás nélküli, így ez a szerző csak állítás Egy %1$d típusú elem, amelyet ez a kliens nem jelenít meg %1$d bájt valamiből, amit ez a kliens nem olvas + %1$s újbóli letiltása %1$s megtartása Újbóli csatlakozás ehhez: %1$s diff --git a/commonsUI/src/commonMain/composeResources/values-in-rID/strings.xml b/commonsUI/src/commonMain/composeResources/values-in-rID/strings.xml index 3ce5510f41..9ff7d96b62 100644 --- a/commonsUI/src/commonMain/composeResources/values-in-rID/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-in-rID/strings.xml @@ -2862,4 +2862,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-it-rIT/strings.xml b/commonsUI/src/commonMain/composeResources/values-it-rIT/strings.xml index 54245dec39..e097e17c7f 100644 --- a/commonsUI/src/commonMain/composeResources/values-it-rIT/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-it-rIT/strings.xml @@ -2900,4 +2900,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-ja-rJP/strings.xml b/commonsUI/src/commonMain/composeResources/values-ja-rJP/strings.xml index 089c9f9857..61af0a4940 100644 --- a/commonsUI/src/commonMain/composeResources/values-ja-rJP/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ja-rJP/strings.xml @@ -2920,4 +2920,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-ko-rKR/strings.xml b/commonsUI/src/commonMain/composeResources/values-ko-rKR/strings.xml index 4e876d3cf0..68998b3497 100644 --- a/commonsUI/src/commonMain/composeResources/values-ko-rKR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ko-rKR/strings.xml @@ -2904,4 +2904,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-lv-rLV/strings.xml b/commonsUI/src/commonMain/composeResources/values-lv-rLV/strings.xml index 2cb28ecf8f..0c9953b12e 100644 --- a/commonsUI/src/commonMain/composeResources/values-lv-rLV/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-lv-rLV/strings.xml @@ -2988,4 +2988,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-nl-rNL/strings.xml b/commonsUI/src/commonMain/composeResources/values-nl-rNL/strings.xml index 3ea2d52fc0..a47c701a37 100644 --- a/commonsUI/src/commonMain/composeResources/values-nl-rNL/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-nl-rNL/strings.xml @@ -4632,4 +4632,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml index 570f8fc2b1..ccd2e12d38 100644 --- a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml @@ -5485,6 +5485,7 @@ Brak podpisu, więc autor to tylko domysł Element typu %1$d, którego ten klient nie wyświetla %1$d bajtów danych, których ten klient nie odczytuje + Zablokuj %1$s ponownie Zachowaj %1$s Ponów %1$s diff --git a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml index 1a156efffd..fdc35e9309 100644 --- a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml @@ -4921,4 +4921,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-pt-rPT/strings.xml b/commonsUI/src/commonMain/composeResources/values-pt-rPT/strings.xml index d722a0814b..6df337c8f7 100644 --- a/commonsUI/src/commonMain/composeResources/values-pt-rPT/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pt-rPT/strings.xml @@ -2920,4 +2920,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-ru-rRU/strings.xml b/commonsUI/src/commonMain/composeResources/values-ru-rRU/strings.xml index d89352fd93..2d9214c475 100644 --- a/commonsUI/src/commonMain/composeResources/values-ru-rRU/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ru-rRU/strings.xml @@ -3007,4 +3007,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-ru-rUA/strings.xml b/commonsUI/src/commonMain/composeResources/values-ru-rUA/strings.xml index 4b2c8833b7..4656ddd56e 100644 --- a/commonsUI/src/commonMain/composeResources/values-ru-rUA/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ru-rUA/strings.xml @@ -2988,4 +2988,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-sl-rSI/strings.xml b/commonsUI/src/commonMain/composeResources/values-sl-rSI/strings.xml index 8ae6e95125..94bd73c074 100644 --- a/commonsUI/src/commonMain/composeResources/values-sl-rSI/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sl-rSI/strings.xml @@ -5007,4 +5007,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-sr-rSP/strings.xml b/commonsUI/src/commonMain/composeResources/values-sr-rSP/strings.xml index 9aee9b1e4d..79291d0563 100644 --- a/commonsUI/src/commonMain/composeResources/values-sr-rSP/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sr-rSP/strings.xml @@ -2965,4 +2965,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml index 0b434776f0..64cf4922d4 100644 --- a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -4921,4 +4921,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-sw/strings.xml b/commonsUI/src/commonMain/composeResources/values-sw/strings.xml index 439b9c7be0..a5f776dbef 100644 --- a/commonsUI/src/commonMain/composeResources/values-sw/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sw/strings.xml @@ -2940,4 +2940,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-ta-rIN/strings.xml b/commonsUI/src/commonMain/composeResources/values-ta-rIN/strings.xml index 106be142e3..4b61e32475 100644 --- a/commonsUI/src/commonMain/composeResources/values-ta-rIN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-ta-rIN/strings.xml @@ -2929,4 +2929,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-th-rTH/strings.xml b/commonsUI/src/commonMain/composeResources/values-th-rTH/strings.xml index ae6a8e1778..fccf86948f 100644 --- a/commonsUI/src/commonMain/composeResources/values-th-rTH/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-th-rTH/strings.xml @@ -2770,4 +2770,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-tr-rTR/strings.xml b/commonsUI/src/commonMain/composeResources/values-tr-rTR/strings.xml index 59fd97ae80..f73a393d7d 100644 --- a/commonsUI/src/commonMain/composeResources/values-tr-rTR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-tr-rTR/strings.xml @@ -2936,4 +2936,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-uk-rUA/strings.xml b/commonsUI/src/commonMain/composeResources/values-uk-rUA/strings.xml index 5e60c8e1da..334b4cb8fd 100644 --- a/commonsUI/src/commonMain/composeResources/values-uk-rUA/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-uk-rUA/strings.xml @@ -2994,4 +2994,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-uz-rUZ/strings.xml b/commonsUI/src/commonMain/composeResources/values-uz-rUZ/strings.xml index 3c8e9daa2a..1584c9d77e 100644 --- a/commonsUI/src/commonMain/composeResources/values-uz-rUZ/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-uz-rUZ/strings.xml @@ -2945,4 +2945,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-vi-rVN/strings.xml b/commonsUI/src/commonMain/composeResources/values-vi-rVN/strings.xml index e39e556680..9a29f36685 100644 --- a/commonsUI/src/commonMain/composeResources/values-vi-rVN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-vi-rVN/strings.xml @@ -2890,4 +2890,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-zh-rCN/strings.xml b/commonsUI/src/commonMain/composeResources/values-zh-rCN/strings.xml index 36527eeeaf..993bad59a6 100644 --- a/commonsUI/src/commonMain/composeResources/values-zh-rCN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -3906,4 +3906,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-zh-rHK/strings.xml b/commonsUI/src/commonMain/composeResources/values-zh-rHK/strings.xml index 4ee4682e0a..af8d37327a 100644 --- a/commonsUI/src/commonMain/composeResources/values-zh-rHK/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-zh-rHK/strings.xml @@ -2955,4 +2955,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-zh-rSG/strings.xml b/commonsUI/src/commonMain/composeResources/values-zh-rSG/strings.xml index 0d0a9f7f9e..5d8c0b7ca4 100644 --- a/commonsUI/src/commonMain/composeResources/values-zh-rSG/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-zh-rSG/strings.xml @@ -2955,4 +2955,5 @@ + diff --git a/commonsUI/src/commonMain/composeResources/values-zh-rTW/strings.xml b/commonsUI/src/commonMain/composeResources/values-zh-rTW/strings.xml index 65c10efd5d..8f327fa1bf 100644 --- a/commonsUI/src/commonMain/composeResources/values-zh-rTW/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-zh-rTW/strings.xml @@ -2926,4 +2926,5 @@ + diff --git a/docs/changelog/translators.json b/docs/changelog/translators.json index 018e4cd32a..18d1805a8c 100644 --- a/docs/changelog/translators.json +++ b/docs/changelog/translators.json @@ -123,18 +123,18 @@ "Vietnamese" ] }, - { - "user": "rajs19420616", - "languages": [ - "Hindi" - ] - }, { "user": "maxblake2015", "languages": [ "Polish" ] }, + { + "user": "rajs19420616", + "languages": [ + "Hindi" + ] + }, { "user": "summoner001", "languages": [ From ca7ffd2ad21ff0eab5bf8225770b90b050734b6b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 03:07:39 +0000 Subject: [PATCH 03/19] fix(quartz): give search the release notes, list roles and caption words it was missing Three gaps between what a kind's indexableContent() says and what a field-weighting search engine got from SearchFieldExtractor. Kind 30063: the NIP-82 release notes reached indexableContent() but not the extractor, whose branch passed title and description only. So an engine reading the extractor could not find a release by what changed in it, while SQLiteEventStore could. searchableReleaseNotes() becomes public and the branch passes it as the body. The NIP-51 encrypted-content guard lives in that one function, so both paths apply the same check. Kinds 9998/39998/9999/39999 (Decentralized Lists): no branch, so the catch-all put the whole blob in the body, titles included. The `t` values it appends were ALSO carried by the funnel's hashtag role, so every list hashtag was indexed twice, the doubling already removed for 1111/1311/30382. One `is DecentralizedListEvent` branch now puts names and titles in the title tier and description and comments in the secondary tier, and leaves hashtags to the funnel. The role helpers sit beside forEachSearchableListField in SearchExt.kt so the two stay in sync. Kind 39307 (text tracks): the whole WebVTT document was indexed, so the header, every `-->` timing line, cue settings, ids, NOTE/STYLE blocks and inline tags sat in the index beside the dialogue. A search for "00" or "align" matched every caption. WebVttText reads the cue payload lines in one pass, drops tags, and decodes character references. SRT fits the same shape. Content with no timing line is not a caption file and is indexed as written. indexableContent() and forEachIndexableField() both go through it, so the SQLite FTS index is fixed too. Stores should run reindexFullTextSearch() for 39307; the searchable-kinds table is updated. Tests: SearchFieldExtractorTest covers both 30063 shapes, a list header, a list item's single-counted hashtags and a text track. WebVttTextTest covers cue selection, markup, references, CRLF/SRT, the visitor rejoin contract and early stop. The golden file is unchanged: its fixture has no timing line. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AdSGU88PYrRVyVHjiEc5sw --- .../references/searchable-kinds.md | 2 +- .../decentralizedLists/SearchExt.kt | 19 ++ .../nip50Search/SearchFieldExtractor.kt | 19 +- .../ReleaseArtifactSetEvent.kt | 12 +- .../nip71Video/textTrack/TextTrackEvent.kt | 10 +- .../quartz/nip71Video/textTrack/WebVttText.kt | 219 ++++++++++++++++++ .../nip50Search/SearchFieldExtractorTest.kt | 71 ++++++ .../nip71Video/textTrack/WebVttTextTest.kt | 111 +++++++++ 8 files changed, 454 insertions(+), 9 deletions(-) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttText.kt create mode 100644 quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttTextTest.kt diff --git a/.claude/skills/searchable-events/references/searchable-kinds.md b/.claude/skills/searchable-events/references/searchable-kinds.md index d9942a6530..532ecc3d68 100644 --- a/.claude/skills/searchable-events/references/searchable-kinds.md +++ b/.claude/skills/searchable-events/references/searchable-kinds.md @@ -140,7 +140,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`. | 39000 | GroupMetadataEvent | nip29RelayGroups/metadata | `listOfNotNull(name(), about())` NL | | 39089 | StarterPackEvent | nip51Lists/starterPack | `listOfNotNull(title(), description())` NL | | 39092 | MediaStarterPackEvent | nip51Lists/mediaStarterPack | `listOfNotNull(title(), description())` NL | -| 39307 | TextTrackEvent | nip71Video/textTrack | `content` (the WebVTT cue text) | +| 39307 | TextTrackEvent | nip71Video/textTrack | `WebVttText.cueText(content)` NL — each cue's payload lines with tags removed and character references decoded; no header, timings, cue settings, ids, `NOTE`/`STYLE`/`REGION` blocks. Content with no `-->` timing line is not a caption file and is indexed as written | | 39701 | WebBookmarkEvent | nipB0WebBookmarks | `listOfNotNull(title(), description())` NL | | 39998 | AddressableListHeaderEvent | experimental/decentralizedLists/header | same as 9998 | | 39999 | AddressableListItemEvent | experimental/decentralizedLists/item | same as 9998 | diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/decentralizedLists/SearchExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/decentralizedLists/SearchExt.kt index e866f645b9..27557c2bbd 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/decentralizedLists/SearchExt.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/decentralizedLists/SearchExt.kt @@ -20,12 +20,17 @@ */ package com.vitorpamplona.quartz.experimental.decentralizedLists +import com.vitorpamplona.quartz.experimental.decentralizedLists.header.names import com.vitorpamplona.quartz.experimental.decentralizedLists.header.tags.NamesTag import com.vitorpamplona.quartz.experimental.decentralizedLists.header.tags.SingularPlural import com.vitorpamplona.quartz.experimental.decentralizedLists.header.tags.TitlesTag +import com.vitorpamplona.quartz.experimental.decentralizedLists.header.titles +import com.vitorpamplona.quartz.experimental.decentralizedLists.item.comments +import com.vitorpamplona.quartz.experimental.decentralizedLists.item.name import com.vitorpamplona.quartz.experimental.decentralizedLists.item.tags.CommentsTag import com.vitorpamplona.quartz.experimental.decentralizedLists.item.tags.NameTag import com.vitorpamplona.quartz.experimental.decentralizedLists.item.tags.TitleTag +import com.vitorpamplona.quartz.experimental.decentralizedLists.item.title import com.vitorpamplona.quartz.experimental.decentralizedLists.tags.DescriptionTag import com.vitorpamplona.quartz.nip01Core.core.TagArray import com.vitorpamplona.quartz.nip01Core.core.fastForEach @@ -84,6 +89,20 @@ fun TagArray.forEachSearchableListField(visitor: IndexableFieldVisitor): Boolean return true } +/** + * The TITLE role of [forEachSearchableListField], for search engines that weight fields: what the + * list or item is called — the header's `names` and `titles` (singular, then plural) and the + * item's `name` and `title`. Same first-well-formed-tag rule as the walk. + */ +fun TagArray.searchableListTitles(): List { + val names = names() + val titles = titles() + return listOf(names?.singular, names?.plural, titles?.singular, titles?.plural, name(), title()) +} + +/** The DESCRIPTION role of [forEachSearchableListField]: `description`, then `comments`. */ +fun TagArray.searchableListDescriptions(): List = listOf(description(), comments()) + /** The write-path join of [forEachSearchableListField]: one field per line. */ fun TagArray.searchableListContent() = buildString { diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractor.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractor.kt index 139530d089..b9a679d8b6 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractor.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractor.kt @@ -29,6 +29,9 @@ import com.vitorpamplona.quartz.experimental.agora.FundraiserEvent import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent import com.vitorpamplona.quartz.experimental.birdstar.BirdDetectionEvent import com.vitorpamplona.quartz.experimental.birdstar.BirdexEvent +import com.vitorpamplona.quartz.experimental.decentralizedLists.DecentralizedListEvent +import com.vitorpamplona.quartz.experimental.decentralizedLists.searchableListDescriptions +import com.vitorpamplona.quartz.experimental.decentralizedLists.searchableListTitles import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent import com.vitorpamplona.quartz.experimental.fitness.workout.ExerciseTemplateEvent import com.vitorpamplona.quartz.experimental.fitness.workout.WorkoutRecordEvent @@ -380,8 +383,11 @@ object SearchFieldExtractor { tiers(event, event.title(), event.description(), null) } + // kind 30063 -- the body is a NIP-82 release's notes, and nothing for a + // NIP-51 set (its content may be encrypted private items); the event + // owns that guard, so both search paths apply the same one. is ReleaseArtifactSetEvent -> { - tiers(event, event.title(), event.description(), null) + tiers(event, event.title(), event.description(), event.searchableReleaseNotes()) } is AppCurationSetEvent -> { @@ -617,6 +623,17 @@ object SearchFieldExtractor { tiers(event, null, null, event.content) } + // kinds 9998/39998/9999/39999 -- a list's or item's names and titles are + // what it is called, its description and comments what it is about. The + // `t` values indexableContent() appends are the funnel's hashtag role + // already, so -- as for 1111/1311/30382 -- they are not passed again, and + // content is not part of the spec. One branch for all four: an item may + // carry header tags (the spec's nonstandard method), and the tag helpers + // read whichever are present. + is DecentralizedListEvent -> { + tiers(event, event.tags.searchableListTitles(), event.tags.searchableListDescriptions(), null) + } + // kind 1 LAST among the explicit branches, defensively: a future // kind extending the text-note base must hit its own branch first. is TextNoteEvent -> { diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip51Lists/releaseArtifactSet/ReleaseArtifactSetEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip51Lists/releaseArtifactSet/ReleaseArtifactSetEvent.kt index 8926ed25cb..bd151437b0 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip51Lists/releaseArtifactSet/ReleaseArtifactSetEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip51Lists/releaseArtifactSet/ReleaseArtifactSetEvent.kt @@ -81,10 +81,14 @@ class ReleaseArtifactSetEvent( visitor.visit(searchableReleaseNotes()) } - // Only NIP-82 releases carry release notes in `content`. A NIP-51 set may keep - // encrypted private items there, which must never reach the index. The blank - // check runs first so empty sets skip the two tag scans on every search. - private fun searchableReleaseNotes() = if (content.isNotBlank() && isNip82SoftwareRelease()) content else null + /** + * The release notes when they are safe to index, else null. Only NIP-82 releases carry + * release notes in `content`; a NIP-51 set may keep encrypted private items there, which + * must never reach the index. The blank check runs first so empty sets skip the two tag + * scans on every search. `SearchFieldExtractor` reads this too, so both search paths + * apply the same guard. + */ + fun searchableReleaseNotes() = if (content.isNotBlank() && isNip82SoftwareRelease()) content else null override fun eventHints() = tags.mapNotNull(EventBookmark::parseAsHint) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/TextTrackEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/TextTrackEvent.kt index 91e0acab39..e9e31c9e13 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/TextTrackEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/TextTrackEvent.kt @@ -59,11 +59,15 @@ class TextTrackEvent( AddressHintProvider, SearchableEvent { // The cue text is the only searchable thing here: the `d` tag is a slug and the rest is - // plumbing. Indexing it makes a video findable by what is said in it. - override fun indexableContent() = content + // plumbing. Indexing it makes a video findable by what is said in it — the words only, + // not the timings, cue settings and markup around them (see WebVttText). Content with no + // cue at all is not a caption file and is indexed as written. + override fun indexableContent() = if (WebVttText.hasCues(content)) WebVttText.cueText(content) else content + // The read path: the same lines cueText() joins, one visit per line so a hit early in the + // track stops the walk before the rest is cleaned. override fun forEachIndexableField(visitor: IndexableFieldVisitor) { - visitor.visit(content) + if (WebVttText.hasCues(content)) WebVttText.forEachCueLine(content, visitor) else visitor.visit(content) } override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttText.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttText.kt new file mode 100644 index 0000000000..9fdf875374 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttText.kt @@ -0,0 +1,219 @@ +/* + * 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.quartz.nip71Video.textTrack + +import com.vitorpamplona.quartz.nip50Search.IndexableFieldVisitor + +/** + * The words a WebVTT document says: every cue's payload lines with their markup removed, and + * nothing else. + * + * Search is the reason this exists. A text track's `content` is the whole document, so indexing + * it raw puts `WEBVTT`, every `00:01:02.500 --> 00:01:04.000` timing line, cue settings + * (`align:start`), cue ids, `NOTE` and `STYLE` blocks, and inline tags (``, ``, + * `<00:00:01.000>`) into the index beside the dialogue. A query for "00" or "align" then + * matches every caption ever published, and the terms that matter are diluted by markup. + * + * ## What counts as a cue line + * + * One pass over the lines, no model built. A line containing `-->` opens a cue (it is the + * timing line; the optional identifier line before it is skipped because nothing is open yet), + * and every non-blank line after it until the next blank line is payload. Everything outside a + * cue — the `WEBVTT` header, `NOTE`, `STYLE`, `REGION` — is skipped by the same rule, since none + * of it may contain `-->`. SRT happens to fit the same shape (a numeric id, a timing line, the + * text), so a publisher that wraps SRT instead of WebVTT is read correctly too. + * + * A document with no timing line at all is not a caption file, so it is indexed as written + * rather than discarded: [hasCues] is the switch, and callers pass such content through + * unchanged. + * + * ## Markup + * + * Inline tags are removed, not their contents: `really` is `really`. Voice spans lose the + * speaker name with the tag (`Hello` is `Hello`): the name is an annotation, not + * something said. The HTML character references WebVTT allows are decoded — the named ones the + * spec lists, plus numeric ones — so `Tom & Jerry` is indexed as `Tom & Jerry`. An unknown + * reference is kept as written. + */ +object WebVttText { + private const val TIMING_ARROW = "-->" + + /** Whether [document] has at least one cue. Without one it is not a caption file. */ + fun hasCues(document: String) = document.contains(TIMING_ARROW) + + /** + * The cue text of [document], one payload line per line. Callers should check [hasCues] + * first; a document without cues yields an empty string here. + */ + fun cueText(document: String): String { + val out = StringBuilder(document.length) + forEachCueLine(document) { line -> + if (out.isNotEmpty()) out.append('\n') + out.append(line) + true + } + return out.toString() + } + + /** + * Hands each cleaned, non-empty payload line of [document] to [visitor] in order. Joining + * them with `"\n"` gives exactly [cueText]. Returns false when the visitor stopped the walk. + * + * Lines without markup are passed as substrings; only a line carrying a tag or a character + * reference is rebuilt. + */ + fun forEachCueLine( + document: String, + visitor: IndexableFieldVisitor, + ): Boolean { + var inCue = false + var start = 0 + val length = document.length + while (start <= length) { + var end = start + while (end < length && document[end] != '\n' && document[end] != '\r') end++ + + if (isBlank(document, start, end)) { + inCue = false + } else if (!inCue) { + // Header, NOTE / STYLE / REGION blocks and cue identifiers all end up here and + // are skipped; only a timing line opens a cue. + if (hasArrow(document, start, end)) inCue = true + } else { + val line = clean(document, start, end) + if (line != null && !visitor.visit(line)) return false + } + + if (end >= length) break + // A CRLF pair is one line break. + start = if (document[end] == '\r' && end + 1 < length && document[end + 1] == '\n') end + 2 else end + 1 + } + return true + } + + /** Bounded to the line: an unbounded indexOf would rescan up to the next cue from every skipped line. */ + private fun hasArrow( + s: String, + start: Int, + end: Int, + ): Boolean { + for (i in start until end - 2) { + if (s[i] == '-' && s[i + 1] == '-' && s[i + 2] == '>') return true + } + return false + } + + private fun isBlank( + s: String, + start: Int, + end: Int, + ): Boolean { + for (i in start until end) if (!s[i].isWhitespace()) return false + return true + } + + /** The payload line in `[start, end)` without tags, references decoded and trimmed; null when nothing is left. */ + private fun clean( + s: String, + start: Int, + end: Int, + ): String? { + var markup = false + for (i in start until end) { + val c = s[i] + if (c == '<' || c == '&') { + markup = true + break + } + } + if (!markup) return s.substring(start, end).trim().ifEmpty { null } + + val out = StringBuilder(end - start) + var i = start + while (i < end) { + val c = s[i] + when (c) { + '<' -> { + val close = s.indexOf('>', i + 1) + // An unterminated tag runs to the end of the line, as the WebVTT parser reads it. + i = if (close < 0 || close >= end) end else close + 1 + } + + '&' -> { + val semi = s.indexOf(';', i + 1) + val decoded = if (semi in (i + 1) until end) decodeReference(s, i + 1, semi) else null + if (decoded == null) { + out.append(c) + i++ + } else { + appendCodePoint(out, decoded) + i = semi + 1 + } + } + + else -> { + out.append(c) + i++ + } + } + } + return out.toString().trim().ifEmpty { null } + } + + /** The code point named by the reference body in `[start, end)` (between `&` and `;`), or null. */ + private fun decodeReference( + s: String, + start: Int, + end: Int, + ): Int? { + if (end - start > 10) return null + if (s[start] == '#') { + val hex = start + 1 < end && (s[start + 1] == 'x' || s[start + 1] == 'X') + val digits = s.substring(if (hex) start + 2 else start + 1, end) + val code = digits.toIntOrNull(if (hex) 16 else 10) ?: return null + return if (code in 1..0x10FFFF && code !in 0xD800..0xDFFF) code else null + } + return when (s.substring(start, end)) { + "amp" -> '&'.code + "lt" -> '<'.code + "gt" -> '>'.code + "quot" -> '"'.code + "apos" -> '\''.code + "nbsp" -> 0x00A0 + "lrm" -> 0x200E + "rlm" -> 0x200F + else -> null + } + } + + private fun appendCodePoint( + out: StringBuilder, + code: Int, + ) { + if (code <= 0xFFFF) { + out.append(code.toChar()) + } else { + val v = code - 0x10000 + out.append((0xD800 + (v shr 10)).toChar()) + out.append((0xDC00 + (v and 0x3FF)).toChar()) + } + } +} diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractorTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractorTest.kt index 51b77ca13f..617a4fe607 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractorTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip50Search/SearchFieldExtractorTest.kt @@ -23,6 +23,8 @@ package com.vitorpamplona.quartz.nip50Search import com.vitorpamplona.quartz.buzz.agentProfiles.AgentProfileEvent import com.vitorpamplona.quartz.experimental.birdstar.BirdDetectionEvent import com.vitorpamplona.quartz.experimental.birdstar.BirdexEvent +import com.vitorpamplona.quartz.experimental.decentralizedLists.header.ListHeaderEvent +import com.vitorpamplona.quartz.experimental.decentralizedLists.item.AddressableListItemEvent import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent import com.vitorpamplona.quartz.experimental.ps1saves.Ps1SaveEvent import com.vitorpamplona.quartz.experimental.trustedLists.users.UserTrustedListEvent @@ -38,9 +40,11 @@ import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupEditMetadataEve import com.vitorpamplona.quartz.nip32Labeling.LabelEvent import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent +import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip5aStaticWebsites.NamedSiteEvent import com.vitorpamplona.quartz.nip69P2pOrderEvents.P2POrderEvent +import com.vitorpamplona.quartz.nip71Video.textTrack.TextTrackEvent import com.vitorpamplona.quartz.nip85TrustedAssertions.users.UserAssertionEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent @@ -412,6 +416,73 @@ class SearchFieldExtractorTest { assertEquals(IndexableFields.Tiered(secondary = listOf("spam"), text = "obvious bot"), fields) } + @Test + fun nip82ReleaseNotesAreTheBodyOfARelease() { + // indexableContent() already carried the notes; the branch used to drop them, so an + // engine reading this extractor could not find a release by what changed in it. + val tags = arrayOf(arrayOf("d", "com.example.app@1.2.0"), arrayOf("i", "com.example.app"), arrayOf("version", "1.2.0"), arrayOf("title", "Example 1.2")) + val fields = SearchFieldExtractor.extract(ReleaseArtifactSetEvent("2e".repeat(32), alice, 1L, tags, "fixes the offline sync crash", "")) + assertEquals(IndexableFields.Tiered(primary = listOf("Example 1.2"), text = "fixes the offline sync crash"), fields) + } + + @Test + fun nip51ReleaseSetContentIsNeverTheBody() { + // A NIP-51 set (no `i` + `version`) may keep encrypted private items in content. + val tags = arrayOf(arrayOf("d", "release"), arrayOf("title", "Example"), arrayOf("description", "all builds")) + val fields = SearchFieldExtractor.extract(ReleaseArtifactSetEvent("2f".repeat(32), alice, 1L, tags, "AgJ0b3BzZWNyZXQ=", "")) + assertEquals(IndexableFields.Tiered(primary = listOf("Example"), secondary = listOf("all builds")), fields) + } + + @Test + fun decentralizedListHeadersSplitWhatTheyAreCalledFromWhatTheyAreAbout() { + val tags = + arrayOf( + arrayOf("names", "podcast", "podcasts"), + arrayOf("titles", "Podcast", "Podcasts"), + arrayOf("description", "shows worth a listen"), + arrayOf("t", "audio"), + ) + val fields = SearchFieldExtractor.extract(ListHeaderEvent("3a".repeat(32), alice, 1L, tags, "", "")) + assertEquals( + IndexableFields.Tiered( + primary = listOf("podcast", "podcasts", "Podcast", "Podcasts"), + secondary = listOf("shows worth a listen"), + hashtags = listOf("audio"), + ), + fields, + ) + } + + @Test + fun decentralizedListItemHashtagsAreNotIndexedTwice() { + // indexableContent() appends the `t` values; the funnel already carries them as + // hashtags, so the branch leaves them out of every tier (the 1111 reasoning). + val tags = + arrayOf( + arrayOf("z", "podcasts"), + arrayOf("title", "Bitcoin Audible"), + arrayOf("description", "long reads, read aloud"), + arrayOf("comments", "start with the classics"), + arrayOf("t", "bitcoin"), + ) + val fields = SearchFieldExtractor.extract(AddressableListItemEvent("3b".repeat(32), alice, 1L, tags + arrayOf(arrayOf("d", "ba")), "", "")) + assertEquals( + IndexableFields.Tiered( + primary = listOf("Bitcoin Audible"), + secondary = listOf("long reads, read aloud", "start with the classics"), + hashtags = listOf("bitcoin"), + ), + fields, + ) + } + + @Test + fun textTracksIndexWhatIsSaidNotTheTimings() { + val vtt = "WEBVTT\n\n1\n00:00:00.000 --> 00:00:02.000 align:start\nHello nostr\n" + val fields = SearchFieldExtractor.extract(TextTrackEvent("3c".repeat(32), alice, 1L, arrayOf(arrayOf("d", "subtitles:v1")), vtt, "")) + assertEquals(IndexableFields.Tiered(text = "Hello nostr"), fields) + } + @Test fun commentHashtagsAreNotIndexedTwice() { // indexableContent() concatenates the `t` tags INTO the body for kind diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttTextTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttTextTest.kt new file mode 100644 index 0000000000..e435eef505 --- /dev/null +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip71Video/textTrack/WebVttTextTest.kt @@ -0,0 +1,111 @@ +/* + * 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.quartz.nip71Video.textTrack + +import com.vitorpamplona.quartz.nip50Search.IndexableFieldVisitor +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class WebVttTextTest { + private val document = + """ + WEBVTT - the whole header line is skipped + Kind: captions + + STYLE + ::cue { color: yellow } + + NOTE this block is a comment + and so is this line + + intro + 00:00:00.000 --> 00:00:02.000 align:start position:10% + We are in New York City + + 00:00:02.500 --> 00:00:04.000 + Really? Tom & Jerry! + on <00:00:03.000>two lines + + 00:00:05.000 --> 00:00:06.000 + + """.trimIndent() + + @Test + fun keepsOnlyTheWordsOfEachCue() { + assertEquals("We are in New York City\nReally? Tom & Jerry!\non two lines", WebVttText.cueText(document)) + } + + @Test + fun theVisitedLinesRejoinToTheCueText() { + val lines = mutableListOf() + assertTrue(WebVttText.forEachCueLine(document, IndexableFieldVisitor { lines.add(it!!) })) + assertEquals(WebVttText.cueText(document), lines.joinToString("\n")) + } + + @Test + fun aVisitorCanStopTheWalk() { + val lines = mutableListOf() + assertFalse( + WebVttText.forEachCueLine(document, IndexableFieldVisitor { lines.add(it!!) && false }), + ) + assertEquals(listOf("We are in New York City"), lines) + } + + @Test + fun crlfAndSrtAreReadTheSameWay() { + val srt = "1\r\n00:00:01,000 --> 00:00:02,000\r\nHello\r\n\r\n2\r\n00:00:03,000 --> 00:00:04,000\r\nworld\r\n" + assertEquals("Hello\nworld", WebVttText.cueText(srt)) + } + + @Test + fun unknownReferencesAndStrayAmpersandsStayAsWritten() { + val vtt = "WEBVTT\n\n00:00.000 --> 00:01.000\nfish & chips &unknown; &#xZZ;" + assertEquals("fish & chips &unknown; &#xZZ;", WebVttText.cueText(vtt)) + } + + @Test + fun anUnterminatedTagRunsToTheEndOfTheLine() { + val vtt = "WEBVTT\n\n00:00.000 --> 00:01.000\nkept () + track.forEachIndexableField { visited.add(it) } + assertEquals(track.indexableContent(), visited.joinToString(track.indexableSeparator())) + + val plain = TextTrackEvent("2".repeat(64), "a1".repeat(32), 1L, tags, "no cues here", "") + assertEquals("no cues here", plain.indexableContent()) + } +} From d7ca63008a051f0fa577397618996c581a7c446f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 03:27:57 +0000 Subject: [PATCH 04/19] refactor(chess): one shared ChessViewModel for Android and Desktop ChessViewModelNew (Android) and DesktopChessViewModelNew forwarded the same ~40 members to ChessLobbyLogic; only the adapters and the scope differed. commons/nip64Chess/ChessViewModel now takes the adapters, the polling config, the dismissed-games store and an optional scope. - Android: ChessViewModelFactory builds it with the Android adapters and no scope, so it runs on viewModelScope and stops polling in onCleared, as before. The factory's unused Application parameter is gone. - Desktop: ChessScreen builds it with the Desktop adapters and its rememberCoroutineScope(); the UserMetadataCache the old class owned is remembered beside it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BkG4KTqmnprUK9BAcwAyc8 --- .../amethyst/ui/note/types/Chess.kt | 8 +- .../screen/loggedIn/chess/ChessGameScreen.kt | 11 +- .../screen/loggedIn/chess/ChessLobbyScreen.kt | 17 +- .../loggedIn/chess/ChessRelaySettingsSheet.kt | 5 +- .../loggedIn/chess/ChessViewModelFactory.kt | 18 +- .../chess/datasource/ChessSubscription.kt | 4 +- .../loggedIn/home/NewChessGameButton.kt | 8 +- .../commons/nip64Chess/ChessViewModel.kt | 59 +++-- .../amethyst/desktop/chess/ChessScreen.kt | 32 ++- .../desktop/chess/DesktopChessViewModelNew.kt | 203 ------------------ 10 files changed, 86 insertions(+), 279 deletions(-) rename amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip64Chess/ChessViewModel.kt (77%) delete mode 100644 desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt index 7f7c919335..a9907ff6f7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt @@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.routeFor import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessGameViewer import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.chess_accept @@ -61,7 +62,6 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew import com.vitorpamplona.quartz.nip64Chess.challenge.offer.LiveChessGameChallengeEvent import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent @@ -124,10 +124,10 @@ fun RenderLiveChessChallenge( val gameId = event.gameId() val activity = LocalActivity.current as androidx.fragment.app.FragmentActivity - val chessViewModel: ChessViewModelNew = + val chessViewModel: ChessViewModel = viewModel( - key = "ChessViewModelNew-${accountViewModel.account.userProfile().pubkeyHex}", - factory = ChessViewModelFactory(accountViewModel.account, activity.application), + key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}", + factory = ChessViewModelFactory(accountViewModel.account), ) val isOpenChallenge = event.opponentPubkey() == null diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt index d013cb4f1c..2e15512276 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt @@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastStatus +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessBroadcastBanner import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessSyncBanner import com.vitorpamplona.amethyst.commons.nip64Chess.ui.LiveChessGameScreen @@ -99,15 +100,11 @@ fun ChessGameScreen( ) { // Scope ViewModel to Activity so it's shared between lobby and game screens val activity = LocalActivity.current as FragmentActivity - val chessViewModel: ChessViewModelNew = + val chessViewModel: ChessViewModel = viewModel( viewModelStoreOwner = activity, - key = "ChessViewModelNew-${accountViewModel.account.userProfile().pubkeyHex}", - factory = - ChessViewModelFactory( - accountViewModel.account, - activity.application, - ), + key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}", + factory = ChessViewModelFactory(accountViewModel.account), ) val activeGames by chessViewModel.activeGames.collectAsState() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessLobbyScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessLobbyScreen.kt index 811cbb1c66..ca57f5d7c3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessLobbyScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessLobbyScreen.kt @@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ActiveGameCard import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChallengeCard import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessSyncBanner @@ -97,11 +98,11 @@ fun ChessLobbyScreen( ) { // Scope ViewModel to Activity so it's shared between lobby and game screens val activity = LocalActivity.current as FragmentActivity - val chessViewModel: ChessViewModelNew = + val chessViewModel: ChessViewModel = viewModel( viewModelStoreOwner = activity, - key = "ChessViewModelNew-${accountViewModel.account.userProfile().pubkeyHex}", - factory = ChessViewModelFactory(accountViewModel.account, activity.application), + key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}", + factory = ChessViewModelFactory(accountViewModel.account), ) // Subscribe to chess events when screen is visible @@ -133,7 +134,7 @@ fun ChessLobbyScreen( @Composable fun NavigateIfInAGame( - chessViewModel: ChessViewModelNew, + chessViewModel: ChessViewModel, nav: INav, ) { val selectedGameId by chessViewModel.selectedGameId.collectAsState() @@ -156,7 +157,7 @@ fun NavigateIfInAGame( @OptIn(ExperimentalMaterial3Api::class) @Composable fun ChessLobbyScreen( - chessViewModel: ChessViewModelNew, + chessViewModel: ChessViewModel, accountViewModel: AccountViewModel, nav: INav, ) { @@ -256,7 +257,7 @@ fun ChessLobbyScreen( } @Composable -fun ChessSyncBanner(chessViewModel: ChessViewModelNew) { +fun ChessSyncBanner(chessViewModel: ChessViewModel) { val syncStatus by chessViewModel.syncStatus.collectAsState() ChessSyncBanner( @@ -267,7 +268,7 @@ fun ChessSyncBanner(chessViewModel: ChessViewModelNew) { } @Composable -fun ErrorDisplay(chessViewModel: ChessViewModelNew) { +fun ErrorDisplay(chessViewModel: ChessViewModel) { // Error display val error by chessViewModel.error.collectAsState() @@ -292,7 +293,7 @@ fun ErrorDisplay(chessViewModel: ChessViewModelNew) { @Composable fun ChessLobbyContent( - chessViewModel: ChessViewModelNew, + chessViewModel: ChessViewModel, accountViewModel: AccountViewModel, onAcceptChallenge: (ChessChallenge) -> Unit, onOpenOwnChallenge: (ChessChallenge) -> Unit, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessRelaySettingsSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessRelaySettingsSheet.kt index d1069687de..faa4bff105 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessRelaySettingsSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessRelaySettingsSheet.kt @@ -46,6 +46,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.nip64Chess.ChessConfig +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.connected import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -59,7 +60,7 @@ import kotlinx.coroutines.flow.map */ @Composable fun ChessRelaySettingsSheet( - chessViewModel: ChessViewModelNew, + chessViewModel: ChessViewModel, accountViewModel: AccountViewModel, ) { // Get relay information for settings display @@ -226,7 +227,7 @@ fun ChessRelaySettingsSheet( @SuppressLint("StateFlowValueCalledInComposition") @Composable -fun StatRow(chessViewModel: ChessViewModelNew) { +fun StatRow(chessViewModel: ChessViewModel) { Column(horizontalAlignment = Alignment.CenterHorizontally) { val challengeCount by remember { chessViewModel.challenges.map { it.size } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelFactory.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelFactory.kt index ea1e191647..3d69227fd1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelFactory.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelFactory.kt @@ -22,20 +22,28 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessDismissedGamesStore +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.model.Account /** - * Factory for creating ChessViewModelNew instances. - * Uses the slim ViewModel that delegates to shared ChessLobbyLogic. + * Factory for the shared [ChessViewModel], wired with the Android adapters. */ class ChessViewModelFactory( private val account: Account, - private val application: android.app.Application, ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { - if (modelClass.isAssignableFrom(ChessViewModelNew::class.java)) { - return ChessViewModelNew(account, application) as T + if (modelClass.isAssignableFrom(ChessViewModel::class.java)) { + return ChessViewModel( + userPubkey = account.userProfile().pubkeyHex, + publisher = AndroidChessPublisher(account), + fetcher = AndroidRelayFetcher(account), + metadataProvider = AndroidMetadataProvider(), + pollingConfig = ChessPollingDefaults.android, + dismissedStorage = ChessDismissedGamesStore(chessDismissedGamesData), + ) as T } throw IllegalArgumentException("Unknown ViewModel class") } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt index d8d788c180..a9522ae2e9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt @@ -25,10 +25,10 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.relayClient.chess.ChessQueryState import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew /** * Subscribe to chess events when the Chess screen is active. @@ -38,7 +38,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew */ @Composable fun ChessSubscription( - chessViewModel: ChessViewModelNew, + chessViewModel: ChessViewModel, accountViewModel: AccountViewModel, ) { // Get active game IDs from the view model for game-specific subscriptions diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/NewChessGameButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/NewChessGameButton.kt index 7c8c77eb6f..df0959e00b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/NewChessGameButton.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/NewChessGameButton.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.nip64Chess.ui.NewChessGameDialog import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.new_chess_game @@ -40,7 +41,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew /** * Floating action button for creating new chess game challenges @@ -53,10 +53,10 @@ fun NewChessGameButton( var showDialog by remember { mutableStateOf(false) } val activity = LocalActivity.current as androidx.fragment.app.FragmentActivity - val chessViewModel: ChessViewModelNew = + val chessViewModel: ChessViewModel = viewModel( - key = "ChessViewModelNew-${accountViewModel.account.userProfile().pubkeyHex}", - factory = ChessViewModelFactory(accountViewModel.account, activity.application), + key = "ChessViewModel-${accountViewModel.account.userProfile().pubkeyHex}", + factory = ChessViewModelFactory(accountViewModel.account), ) FloatingActionButton( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip64Chess/ChessViewModel.kt similarity index 77% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip64Chess/ChessViewModel.kt index 9bd3819de4..bbd7695555 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip64Chess/ChessViewModel.kt @@ -18,60 +18,51 @@ * 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.screen.loggedIn.chess +package com.vitorpamplona.amethyst.commons.nip64Chess import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastStatus -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessDismissedGamesStore -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessLobbyLogic -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessSyncStatus -import com.vitorpamplona.amethyst.commons.nip64Chess.CompletedGame -import com.vitorpamplona.amethyst.commons.nip64Chess.PublicGame -import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip64Chess.Color import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent import com.vitorpamplona.quartz.utils.Log +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow /** - * Slim Android ViewModel for chess (~120 lines). + * The chess screen's state holder, shared by Android and Desktop. * - * Delegates all business logic to ChessLobbyLogic. - * Only handles Android-specific concerns: - * - ViewModel lifecycle (viewModelScope) - * - Platform adapter creation - * - State exposure to Compose UI + * All business logic lives in [ChessLobbyLogic]; this class only exposes its state and forwards + * the user's actions. Each platform supplies its own adapters (how to publish, fetch, resolve + * metadata and remember dismissed games) and its polling cadence. + * + * On Android it is a lifecycle-scoped `ViewModel`: [scope] defaults to `viewModelScope` and + * polling stops in [onCleared]. Desktop, which has no `ViewModelStore`, passes the scope of the + * composition that owns it; polling then ends when that scope is cancelled. */ @Stable -class ChessViewModelNew( - private val account: Account, - application: android.app.Application, +class ChessViewModel( + private val userPubkey: HexKey, + publisher: ChessEventPublisher, + fetcher: ChessRelayFetcher, + metadataProvider: IUserMetadataProvider, + pollingConfig: ChessPollingConfig, + dismissedStorage: ChessDismissedGamesStore?, + scope: CoroutineScope? = null, ) : ViewModel() { - // Instance ID for debugging ViewModel sharing - val instanceId = System.identityHashCode(this) - - // Platform adapters - private val publisher = AndroidChessPublisher(account) - private val fetcher = AndroidRelayFetcher(account) - private val metadataProvider = AndroidMetadataProvider() - private val dismissedStorage = ChessDismissedGamesStore(chessDismissedGamesData) - // Shared business logic (creates its own ChessLobbyState internally) private val logic = ChessLobbyLogic( - userPubkey = account.userProfile().pubkeyHex, + userPubkey = userPubkey, publisher = publisher, fetcher = fetcher, metadataProvider = metadataProvider, - scope = viewModelScope, - pollingConfig = ChessPollingDefaults.android, + scope = scope ?: viewModelScope, + pollingConfig = pollingConfig, dismissedStorage = dismissedStorage, ) @@ -99,7 +90,7 @@ class ChessViewModelNew( // ============================================ init { - Log.d("chessdebug") { "[AndroidVM] init: instanceId=$instanceId, userPubkey=${account.userProfile().pubkeyHex.take(8)}" } + Log.d("chessdebug") { "[ChessVM] init: userPubkey=${userPubkey.take(8)}" } logic.startPolling() } @@ -143,10 +134,10 @@ class ChessViewModelNew( if (event.kind != JesterProtocol.KIND) return val jesterEvent = event.toJesterEvent() ?: run { - Log.d("chessdebug") { "[AndroidVM] handleIncomingEvent: failed to parse kind ${event.kind} event ${event.id.take(8)} as JesterEvent" } + Log.d("chessdebug") { "[ChessVM] handleIncomingEvent: failed to parse kind ${event.kind} event ${event.id.take(8)} as JesterEvent" } return } - Log.d("chessdebug") { "[AndroidVM] handleIncomingEvent: id=${event.id.take(8)}, pubkey=${event.pubKey.take(8)}, isStart=${jesterEvent.isStartEvent()}, isMove=${jesterEvent.isMoveEvent()}" } + Log.d("chessdebug") { "[ChessVM] handleIncomingEvent: id=${event.id.take(8)}, pubkey=${event.pubKey.take(8)}, isStart=${jesterEvent.isStartEvent()}, isMove=${jesterEvent.isMoveEvent()}" } logic.handleIncomingEvent(jesterEvent) } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt index 348b47a8a6..271312a224 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt @@ -65,8 +65,11 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.UserMetadataCache import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge import com.vitorpamplona.amethyst.commons.nip64Chess.ChessConfig +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults +import com.vitorpamplona.amethyst.commons.nip64Chess.ChessViewModel import com.vitorpamplona.amethyst.commons.nip64Chess.CompletedGame import com.vitorpamplona.amethyst.commons.nip64Chess.PublicGame +import com.vitorpamplona.amethyst.commons.nip64Chess.desktopChessDismissedGamesStore import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ActiveGameCard import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChallengeCard import com.vitorpamplona.amethyst.commons.nip64Chess.ui.ChessBroadcastBanner @@ -100,9 +103,18 @@ fun ChessScreen( compactMode: Boolean = false, ) { val scope = rememberCoroutineScope() + val userMetadataCache = remember(account.pubKeyHex) { UserMetadataCache() } val viewModel = remember(account.pubKeyHex) { - DesktopChessViewModelNew(account, relayManager, scope) + ChessViewModel( + userPubkey = account.pubKeyHex, + publisher = DesktopChessPublisher(account, relayManager), + fetcher = DesktopRelayFetcher(relayManager, account.pubKeyHex), + metadataProvider = DesktopMetadataProvider(userMetadataCache), + pollingConfig = ChessPollingDefaults.desktop, + dismissedStorage = desktopChessDismissedGamesStore(), + scope = scope, + ) } val connectedRelays by relayManager.connectedRelays.collectAsState() val broadcastStatus by viewModel.broadcastStatus.collectAsState() @@ -151,7 +163,7 @@ fun ChessScreen( } // Subscribe to user metadata for pubkeys that need it - val pubkeysNeeded by viewModel.userMetadataCache.pubkeysNeeded.collectAsState() + val pubkeysNeeded by userMetadataCache.pubkeysNeeded.collectAsState() rememberSubscription(connectedRelays, pubkeysNeeded, relayManager = relayManager) { if (connectedRelays.isNotEmpty() && pubkeysNeeded.isNotEmpty()) { createMetadataListSubscription( @@ -171,7 +183,7 @@ fun ChessScreen( val publicGames by viewModel.publicGames.collectAsState() val completedGames by viewModel.completedGames.collectAsState() // Observe metadata changes to trigger recomposition - val userMetadata by viewModel.userMetadataCache.metadata.collectAsState() + val userMetadata by userMetadataCache.metadata.collectAsState() val selectedGameId by viewModel.selectedGameId.collectAsState() val error by viewModel.error.collectAsState() val isRefreshing by viewModel.isRefreshing.collectAsState() @@ -289,8 +301,8 @@ fun ChessScreen( DesktopChessGameLayout( gameState = gameState, - opponentName = viewModel.userMetadataCache.getDisplayName(gameState.opponentPubkey), - opponentPicture = viewModel.userMetadataCache.getPictureUrl(gameState.opponentPubkey), + opponentName = userMetadataCache.getDisplayName(gameState.opponentPubkey), + opponentPicture = userMetadataCache.getPictureUrl(gameState.opponentPubkey), onMoveMade = { from, to, _ -> viewModel.publishMove(gameState.startEventId, from, to) }, @@ -306,12 +318,12 @@ fun ChessScreen( null }, compactMode = compactMode, - whiteName = viewModel.userMetadataCache.getDisplayName(whitePubkey), + whiteName = userMetadataCache.getDisplayName(whitePubkey), whiteHex = whitePubkey, - whiteAvatarUrl = viewModel.userMetadataCache.getPictureUrl(whitePubkey), - blackName = viewModel.userMetadataCache.getDisplayName(blackPubkey), + whiteAvatarUrl = userMetadataCache.getPictureUrl(whitePubkey), + blackName = userMetadataCache.getDisplayName(blackPubkey), blackHex = blackPubkey, - blackAvatarUrl = viewModel.userMetadataCache.getPictureUrl(blackPubkey), + blackAvatarUrl = userMetadataCache.getPictureUrl(blackPubkey), ) } } else { @@ -338,7 +350,7 @@ fun ChessScreen( publicGames = publicGames, completedGames = completedGames, userPubkey = account.pubKeyHex, - metadataCache = viewModel.userMetadataCache, + metadataCache = userMetadataCache, onAcceptChallenge = { viewModel.acceptChallenge(it) }, onOpenOwnChallenge = { viewModel.openOwnChallenge(it) }, onWatchGame = { viewModel.loadGameAsSpectator(it) }, diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt deleted file mode 100644 index 3a5123f8b7..0000000000 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.desktop.chess - -import com.vitorpamplona.amethyst.commons.model.cache.UserMetadataCache -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastStatus -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessLobbyLogic -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults -import com.vitorpamplona.amethyst.commons.nip64Chess.ChessSyncStatus -import com.vitorpamplona.amethyst.commons.nip64Chess.CompletedGame -import com.vitorpamplona.amethyst.commons.nip64Chess.PublicGame -import com.vitorpamplona.amethyst.commons.nip64Chess.desktopChessDismissedGamesStore -import com.vitorpamplona.amethyst.desktop.account.AccountState -import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager -import com.vitorpamplona.quartz.nip01Core.core.Event -import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState -import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol -import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.StateFlow - -/** - * Slim Desktop ViewModel for chess (~120 lines). - * - * Delegates all business logic to ChessLobbyLogic. - * Only handles Desktop-specific concerns: - * - Platform adapter creation - * - State exposure to Compose Desktop UI - * - UserMetadataCache for profile display - */ -class DesktopChessViewModelNew( - private val account: AccountState.LoggedIn, - private val relayManager: DesktopRelayConnectionManager, - private val scope: CoroutineScope, -) { - // Desktop-specific metadata cache - val userMetadataCache = UserMetadataCache() - - // Platform adapters - private val publisher = DesktopChessPublisher(account, relayManager) - private val fetcher = DesktopRelayFetcher(relayManager, account.pubKeyHex) - private val metadataProvider = DesktopMetadataProvider(userMetadataCache) - private val dismissedStorage = desktopChessDismissedGamesStore() - - // Shared business logic (creates its own ChessLobbyState internally) - private val logic = - ChessLobbyLogic( - userPubkey = account.pubKeyHex, - publisher = publisher, - fetcher = fetcher, - metadataProvider = metadataProvider, - scope = scope, - pollingConfig = ChessPollingDefaults.desktop, - dismissedStorage = dismissedStorage, - ) - - // ============================================ - // State exposure (delegated from ChessLobbyLogic.state) - // ============================================ - - val activeGames: StateFlow> = logic.state.activeGames - val spectatingGames: StateFlow> = logic.state.spectatingGames - val challenges: StateFlow> = logic.state.challenges - val publicGames: StateFlow> = logic.state.publicGames - val completedGames: StateFlow> = logic.state.completedGames - val broadcastStatus: StateFlow = logic.state.broadcastStatus - val error: StateFlow = logic.state.error - val selectedGameId: StateFlow = logic.state.selectedGameId - val isRefreshing: StateFlow = logic.state.isRefreshing - val syncStatus: StateFlow = logic.state.syncStatus - val stateVersion: StateFlow = logic.state.stateVersion - - /** Badge count (incoming challenges + your turn games) - computed property */ - val badgeCount: Int get() = logic.state.badgeCount - - // ============================================ - // Lifecycle - // ============================================ - - init { - logic.startPolling() - } - - fun startPolling() = logic.startPolling() - - fun stopPolling() = logic.stopPolling() - - fun forceRefresh() = logic.forceRefresh() - - /** - * Ensure a game ID is being polled for updates. - * Call this when viewing a game. - */ - fun ensureGamePolling(gameId: String) = logic.ensureGamePolling(gameId) - - /** - * Set focused game mode - only poll this specific game. - * Call this when viewing a game to avoid refreshing unrelated games. - */ - fun setFocusedGame(gameId: String) = logic.setFocusedGame(gameId) - - /** - * Clear focused game mode - return to lobby mode (poll all games). - * Call this when returning to the lobby view. - */ - fun clearFocusedGame() = logic.clearFocusedGame() - - // ============================================ - // Incoming event routing (from relay subscriptions) - // ============================================ - - fun handleIncomingEvent(event: Event) { - if (event.kind != JesterProtocol.KIND) return - val jesterEvent = event.toJesterEvent() ?: return - logic.handleIncomingEvent(jesterEvent) - } - - // ============================================ - // Challenge operations - // ============================================ - - fun createChallenge( - opponentPubkey: String? = null, - playerColor: Color = Color.WHITE, - timeControl: String? = null, - ) = logic.createChallenge(opponentPubkey, playerColor, timeControl) - - fun acceptChallenge(challenge: ChessChallenge) = logic.acceptChallenge(challenge) - - fun openOwnChallenge(challenge: ChessChallenge) = logic.openOwnChallenge(challenge) - - // ============================================ - // Game operations - // ============================================ - - fun selectGame(gameId: String?) = logic.selectGame(gameId) - - fun publishMove( - gameId: String, - from: String, - to: String, - ) = logic.publishMove(gameId, from, to) - - fun resign(gameId: String) = logic.resign(gameId) - - fun claimAbandonmentVictory(gameId: String) = logic.claimAbandonmentVictory(gameId) - - fun dismissGame(gameId: String) = logic.dismissGame(gameId) - - fun dismissCompletedGame(gameId: String) = logic.dismissCompletedGame(gameId) - - fun dismissAllCompletedGames() = logic.dismissAllCompletedGames() - - // ============================================ - // Spectator operations - // ============================================ - - fun loadGame(gameId: String) = logic.loadGame(gameId) - - fun loadGameAsSpectator(gameId: String) = logic.loadGameAsSpectator(gameId) - - fun stopSpectating(gameId: String) = logic.stopSpectating(gameId) - - fun removeGame(gameId: String) = logic.removeGame(gameId) - - // ============================================ - // Utility - // ============================================ - - fun clearError() = logic.clearError() - - fun getGameState(gameId: String): LiveChessGameState? = logic.state.getGameState(gameId) - - /** Check if a game was accepted (prevents loading as spectator during race) */ - fun wasAccepted(gameId: String): Boolean = logic.state.wasAccepted(gameId) - - /** Helper for derived challenge lists */ - fun incomingChallenges(): List = logic.state.incomingChallenges() - - fun outgoingChallenges(): List = logic.state.outgoingChallenges() - - fun openChallenges(): List = logic.state.openChallenges() -} From 9512fd886f1d61c019e535db8ccd124e2b772f49 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 03:28:08 +0000 Subject: [PATCH 05/19] refactor: move the unblocked amethyst/ui files to commons and commonsUI A transitive-blocker sweep (imports plus same-package references) found 96 files under amethyst/ui that no longer depend on Android or on app-only code. This moves 68 of them, deletes 3 app copies of code that commons already had, and leaves 25 that are Android-only by nature. - To commons: the chatroom-list and relay-group filters, the *LastRead routes, composer state (NewMessageTagger, DraftTagState, IExpiration, PreviewState, SplitConversor), FilterByListParams and the small feed states, RelayFeedViewModel, UserExternalIdentitiesViewModel, TorDialogViewModel, the QR structured-append accumulator, PitchShifter and others. - To commonsUI: InformationDialog, UrlPreviewCard, the mention input transformation, the nav wrappers (ObservableNav, TwoPaneNav, ShareToDMNav), SettingsCatalog, KeyboardState and feature composables under their feature ui/ packages. - Deleted app copies: ui/note/PubKeyFormatter, ui/note/ZapFormatter and the EqualImmutableLists shim; callers use the commons originals. - commonMain fixes the moves needed: String.format, codePointAt, Integer.parseInt, javaClass and a Class ViewModel factory replaced by their KMP equivalents. - Eleven pure unit tests moved with their code. The per-file table, the files kept in the app and why, and what the sweep found outside ui/ are in commons/plans/2026-08-30-commons-migration-sweep.md. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BkG4KTqmnprUK9BAcwAyc8 --- .claude/skills/feed-patterns/SKILL.md | 4 +- .../references/feed-filter-composition.md | 2 +- .../references/abstraction-examples.md | 4 +- ...entionPreservingInputTransformationTest.kt | 2 +- .../loggedIn/settings/LegalSettingsSection.kt | 1 + .../baselineProfiles/baseline-prof.txt | 26 ++-- .../vitorpamplona/amethyst/model/Account.kt | 2 +- .../amethyst/model/AccountConcordActions.kt | 2 +- .../amethyst/model/AccountSyncedSettings.kt | 2 +- .../NotificationReplyReceiver.kt | 2 +- .../notifications/RelayPurposeSummary.kt | 2 +- .../renderers/NwcPaymentNotifier.kt | 2 +- .../renderers/ZapNotification.kt | 2 +- .../RelaySubscriptionsCoordinator.kt | 2 +- .../amethyst/ui/actions/EditPostView.kt | 2 + .../amethyst/ui/actions/EditPostViewModel.kt | 4 +- .../amethyst/ui/actions/NewMediaModel.kt | 2 +- .../amethyst/ui/actions/NewMediaView.kt | 1 + .../actions/UrlUserTagOutputTransformation.kt | 1 + .../mediaServers/AllMediaServersLIstView.kt | 1 + .../mediaServers/BlossomServersViewModel.kt | 2 + .../ui/actions/uploads/RecordVoiceButton.kt | 2 + .../uploads/VoiceAnonymizationController.kt | 1 + .../uploads/VoiceAnonymizationSection.kt | 1 + .../ui/actions/uploads/VoiceAnonymizer.kt | 2 + .../ui/actions/uploads/VoiceMessagePreview.kt | 1 + .../ui/actions/uploads/VoicePresetSelector.kt | 1 + .../amethyst/ui/components/LoadUrlPreview.kt | 1 + .../amethyst/ui/components/RichTextViewer.kt | 2 +- .../ui/components/ZoomableContentView.kt | 2 +- .../components/toasts/DisplayErrorMessages.kt | 2 +- .../amethyst/ui/navigation/AppNavigation.kt | 6 +- .../ui/navigation/bottombars/AppBottomBar.kt | 3 + .../bottombars/AppNavigationRail.kt | 1 + .../drawer/AccountSwitchBottomSheet.kt | 2 +- .../ui/navigation/navs/RememberNavs.kt | 1 + .../ui/navigation/routes/RouteMaker.kt | 1 + .../amethyst/ui/note/Bolt12ZapGallery.kt | 1 + .../amethyst/ui/note/MultiSetCompose.kt | 1 + .../amethyst/ui/note/NutzapGallery.kt | 1 + .../amethyst/ui/note/NutzapUserSetCompose.kt | 1 + .../amethyst/ui/note/OnchainZapGallery.kt | 1 + .../amethyst/ui/note/PubKeyFormatter.kt | 33 ----- .../amethyst/ui/note/ReactionsRow.kt | 3 +- .../amethyst/ui/note/RelayCompose.kt | 2 +- .../amethyst/ui/note/UpdateZapAmountDialog.kt | 1 + .../amethyst/ui/note/ZapCustomDialog.kt | 1 + .../amethyst/ui/note/ZapFormatter.kt | 83 ----------- .../amethyst/ui/note/ZapPollNote.kt | 1 + .../expiration/ExpirationDatePicker.kt | 1 + .../creators/messagefield/MessageField.kt | 2 +- .../note/creators/previews/DisplayPreviews.kt | 1 + .../ui/note/creators/previews/PreviewUrl.kt | 2 +- .../note/creators/zapsplits/ForwardZapTo.kt | 2 +- .../ui/note/elements/DisplayReward.kt | 2 +- .../nip22Comments/CommentPostViewModel.kt | 14 +- .../note/nip22Comments/DisplayExternalId.kt | 2 +- .../nip22Comments/GenericCommentPostScreen.kt | 3 +- .../amethyst/ui/note/types/Chat.kt | 1 + .../amethyst/ui/note/types/Classifieds.kt | 2 +- .../amethyst/ui/note/types/CommunityHeader.kt | 2 +- .../amethyst/ui/note/types/Goal.kt | 2 +- .../amethyst/ui/note/types/LiveActivity.kt | 6 +- .../amethyst/ui/note/types/MeetingSpace.kt | 6 +- .../amethyst/ui/note/types/MusicPlaylist.kt | 1 + .../amethyst/ui/note/types/MusicTrack.kt | 1 + .../amethyst/ui/note/types/Nutzap.kt | 2 +- .../amethyst/ui/screen/UserFeedView.kt | 1 + .../amethyst/ui/screen/UserFeedViewModel.kt | 3 +- .../ui/screen/loggedIn/AccountViewModel.kt | 7 +- .../articles/dal/ArticlesFeedFilter.kt | 2 +- .../loggedIn/badges/dal/BadgesFeedFilter.kt | 2 +- .../loggedIn/badges/post/NewBadgeDialog.kt | 2 +- .../loggedIn/badges/post/NewBadgeModel.kt | 2 +- .../loggedIn/buzz/AgentWorkBoardScreen.kt | 3 + .../loggedIn/buzz/AgentWorkBoardViewModel.kt | 2 + .../loggedIn/buzz/BuzzForumPostScreen.kt | 2 +- .../buzz/ForumReplyNewMessageViewModel.kt | 2 +- .../loggedIn/calendars/CalendarsScreen.kt | 1 + .../loggedIn/calendars/CalendarsTopBar.kt | 1 + .../loggedIn/calendars/CalendarsViewModel.kt | 1 + .../dal/CalendarAppointmentsFeedFilter.kt | 2 +- .../dal/CalendarCollectionsFeedFilter.kt | 2 +- .../chats/cordnGroup/CordnComposer.kt | 2 +- .../loggedIn/chats/feed/ChatFeedView.kt | 2 +- .../loggedIn/chats/feed/LoadingReplyNote.kt | 1 + .../chats/feed/types/RenderChatZap.kt | 2 +- .../chats/feed/types/RenderRegularTextNote.kt | 2 +- .../chats/marmotGroup/MarmotGroupChatView.kt | 3 +- .../marmotGroup/MarmotGroupListScreen.kt | 1 + .../loggedIn/chats/privateDM/ChatroomView.kt | 2 +- .../privateDM/send/ChatNewMessageViewModel.kt | 12 +- .../chats/privateDM/send/NewGroupDMScreen.kt | 2 +- .../send/PrivateMessageEditFieldRow.kt | 4 +- .../concord/ConcordChannelScreen.kt | 5 +- .../publicChannels/concord/ConcordUnread.kt | 1 + .../header/LiveStreamTopZappers.kt | 2 +- .../header/LongLiveActivityChannelHeader.kt | 2 +- .../header/ShortLiveActivityChannelHeader.kt | 4 +- .../relayGroup/RelayGroupChannelView.kt | 3 +- .../relayGroup/RelayGroupDiscoveryScreen.kt | 2 +- .../relayGroup/RelayGroupUnread.kt | 1 + .../dal/RelayGroupDiscoveryFeedFilter.kt | 1 + .../RelayGroupOpenThreadsSubscription.kt | 2 + .../RelayGroupsDiscoverySubAssembler.kt | 1 + .../send/ChannelNewMessageViewModel.kt | 8 +- .../chats/publicChannels/send/EditFieldRow.kt | 4 +- .../chats/rooms/ChatroomHeaderCompose.kt | 10 +- .../chats/rooms/MarkChatRoomsAsRead.kt | 10 +- .../screen/loggedIn/chats/rooms/RowUnread.kt | 10 +- .../rooms/dal/ChatroomListKnownFeedFilter.kt | 2 + .../ChatroomListNip04HistorySubAssembler.kt | 2 + .../ChatroomListNip04SubAssembler.kt | 2 + .../FollowingEphemeralChatSubAssembler.kt | 1 + .../FollowingPublicChatSubAssembler.kt | 2 + .../chats/rooms/feed/ChatroomListFeedView.kt | 2 +- .../chats/rooms/twopane/MessagesTwoPane.kt | 1 + .../loggedIn/chats/share/ShareToDMScreen.kt | 1 + .../chats/utils/ChatFileUploadState.kt | 2 +- .../loggedIn/chess/dal/ChessFeedFilter.kt | 2 +- .../communities/dal/CommunityFeedFilter.kt | 2 + .../dal/CommunityModerationFeedFilter.kt | 2 + .../list/dal/CommunitiesFeedFilter.kt | 2 +- .../newCommunity/NewCommunityModel.kt | 2 +- .../newCommunity/NewCommunityScreen.kt | 2 +- .../DiscoverLongFormFeedFilter.kt | 2 +- .../nip23LongForm/LongFormPostScreen.kt | 4 +- .../nip23LongForm/LongFormPostViewModel.kt | 12 +- .../nip28Chats/DiscoverChatFeedFilter.kt | 2 +- .../RenderPublicChatChannelThumb.kt | 2 +- .../DiscoverFollowSetsFeedFilter.kt | 2 +- .../DiscoverLiveFeedFilter.kt | 2 +- .../nip53LiveActivities/LiveActivityCard.kt | 10 +- .../nip72Communities/CommunityCard.kt | 2 +- .../DiscoverCommunityFeedFilter.kt | 2 +- .../nip90DVMs/DiscoverNIP89FeedFilter.kt | 2 +- .../nip99Classifieds/ClassifiedsThumb.kt | 2 +- .../DiscoverMarketplaceFeedFilter.kt | 2 +- .../nip99Classifieds/NewProductScreen.kt | 2 +- .../nip99Classifieds/NewProductViewModel.kt | 14 +- .../discover/nip99Classifieds/SellProduct.kt | 2 +- .../dal/DvmContentDiscoveryResponseFilter.kt | 2 +- .../browse/dal/BrowseEmojiSetsFeedFilter.kt | 2 +- .../FollowPackFeedConversationsFeedFilter.kt | 2 +- .../dal/FollowPackFeedNewThreadFeedFilter.kt | 2 +- .../list/dal/FollowPacksFeedFilter.kt | 2 +- .../geocaches/dal/GeocacheHuntsFeedFilter.kt | 2 +- .../geocaches/dal/GeocacheMineFeedFilter.kt | 1 + .../geocaches/dal/GeocachesFeedFilter.kt | 3 +- .../geocaches/dal/GeocachesHereFeedFilter.kt | 1 + .../dal/GitRepositoriesFeedFilter.kt | 2 +- .../HashtagFeedFilterSubAssembler.kt | 1 + .../highlights/NewHighlightPostViewModel.kt | 2 +- .../highlights/dal/HighlightsFeedFilter.kt | 2 +- .../loggedIn/home/ShortNotePostScreen.kt | 2 +- .../loggedIn/home/ShortNotePostViewModel.kt | 16 +-- .../loggedIn/home/VoiceReplyViewModel.kt | 2 +- .../home/dal/HomeConversationsFeedFilter.kt | 2 +- .../loggedIn/home/dal/HomeLiveFilter.kt | 2 +- .../home/dal/HomeNewThreadFeedFilter.kt | 2 +- .../livestreams/dal/LiveStreamsFeedFilter.kt | 2 +- .../loggedIn/longs/dal/LongsFeedFilter.kt | 4 +- .../loggedIn/music/NewMusicPlaylistScreen.kt | 2 +- .../music/NewMusicPlaylistViewModel.kt | 2 +- .../loggedIn/music/NewMusicTrackScreen.kt | 2 +- .../loggedIn/music/NewMusicTrackViewModel.kt | 2 +- .../music/dal/MusicPlaylistsFeedFilter.kt | 2 +- .../music/dal/MusicTracksFeedFilter.kt | 2 +- .../napplets/ConnectedAppDetailScreen.kt | 2 +- .../screen/loggedIn/nests/NestsFeedLoaded.kt | 8 +- .../loggedIn/nests/dal/NestsFeedFilter.kt | 2 +- .../nests/room/chat/NestEditFieldRow.kt | 4 +- .../room/chat/NestNewMessageViewModel.kt | 8 +- .../nests/room/stage/SpeakerZapOverlay.kt | 2 +- .../notifications/CardFeedContentState.kt | 1 + .../loggedIn/notifications/CardFeedView.kt | 2 +- .../notifications/EqualImmutableLists.kt | 31 ---- .../notifications/NotificationSummaryState.kt | 2 +- .../chart/AmountValueFormatter.kt | 4 +- .../dal/NotificationFeedFilter.kt | 2 +- .../publicMessages/NewPublicMessageScreen.kt | 4 +- .../NewPublicMessageViewModel.kt | 14 +- .../pictures/dal/PictureFeedFilter.kt | 2 +- .../loggedIn/podcasts/PodcastTopSupporters.kt | 2 +- .../authoring/EditPodcastShowScreen.kt | 2 +- .../authoring/EditPodcastShowViewModel.kt | 2 +- .../authoring/NewPodcastEpisodeScreen.kt | 2 +- .../authoring/NewPodcastEpisodeViewModel.kt | 2 +- .../authoring/NewPodcastTrailerScreen.kt | 2 +- .../authoring/NewPodcastTrailerViewModel.kt | 2 +- .../podcasts/dal/PodcastEpisodesFeedFilter.kt | 2 +- .../podcasts/dal/PodcastsFeedFilter.kt | 2 +- .../polls/dal/ClosedPollsFeedFilter.kt | 2 +- .../loggedIn/polls/dal/OpenPollsFeedFilter.kt | 2 +- .../loggedIn/polls/dal/PollsFeedFilter.kt | 2 +- .../polls/results/PollResultsScreen.kt | 1 + .../products/dal/ProductsFeedFilter.kt | 2 +- .../screen/loggedIn/profile/ProfileScreen.kt | 2 +- .../apps/dal/UserProfileAppsFeedFilter.kt | 2 +- .../dal/UserProfileGalleryFeedFilter.kt | 2 +- .../profile/header/DrawAdditionalInfo.kt | 2 +- .../loggedIn/profile/header/ProfileHeader.kt | 2 +- .../profile/payment/SendPaymentContent.kt | 2 +- .../profile/payment/SendPaymentScreen.kt | 2 +- .../loggedIn/profile/relays/RelayFeedView.kt | 2 + .../loggedIn/profile/relays/TabRelays.kt | 1 + .../loggedIn/profile/zaps/ZapNoteCompose.kt | 2 +- .../loggedIn/profile/zaps/ZapTabHeader.kt | 2 +- .../publicChats/dal/PublicChatsFeedFilter.kt | 2 +- .../loggedIn/qrcode/scanner/BarcodeDecoder.kt | 3 + .../qrcode/scanner/QrFrameAnalyzer.kt | 2 + .../loggedIn/qrcode/scanner/QrImageImport.kt | 1 + .../qrcode/scanner/QrScannerDialog.kt | 1 + .../qrcode/scanner/QrScannerOverlay.kt | 3 + .../loggedIn/qrcode/scanner/QrScannerState.kt | 3 + .../ActiveSubscriptionsScreen.kt | 2 +- .../loggedIn/settings/AllSettingsScreen.kt | 4 + .../loggedIn/settings/HiddenWordsScreen.kt | 1 + .../loggedIn/settings/NIP47SetupScreen.kt | 1 + .../settings/NotificationSettingsScreen.kt | 2 +- .../loggedIn/settings/SecurityListsCommon.kt | 2 +- .../settings/SettingsCatalogBuilder.kt | 3 + .../loggedIn/settings/StringFeedView.kt | 1 + .../loggedIn/settings/StringFeedViewModel.kt | 3 +- .../settings/cordn/CordnBackupScreen.kt | 2 + .../settings/cordn/CordnCoordinatorsScreen.kt | 1 + .../settings/cordn/CordnKeyPackagesScreen.kt | 1 + .../settings/cordn/CordnLinkScreen.kt | 1 + .../settings/cordn/CordnMigrateScreen.kt | 2 + .../settings/nip46/Nip46SignerScreen.kt | 1 + .../loggedIn/shorts/dal/ShortsFeedFilter.kt | 4 +- .../dal/SoftwareAppsFeedFilter.kt | 2 +- .../loggedIn/threadview/ThreadFeedView.kt | 2 +- .../ui/screen/loggedIn/url/UrlScreen.kt | 2 +- .../loggedIn/video/dal/VideoFeedFilter.kt | 3 +- .../video/hls/HlsPublishOrchestrator.kt | 1 + .../hls/HlsPublishOrchestratorFactory.kt | 1 + .../loggedIn/video/hls/NewHlsVideoScreen.kt | 1 + .../video/hls/NewHlsVideoViewModel.kt | 1 + .../loggedIn/wallet/OnchainZapSendDialog.kt | 2 +- .../loggedIn/wallet/ReloadMintScreen.kt | 2 +- .../ui/screen/loggedIn/wallet/WalletScreen.kt | 1 + .../wallet/WalletTransactionsScreen.kt | 1 + .../wizard/CashuWalletWizardViewModel.kt | 1 + .../workouts/dal/WorkoutFeedFilter.kt | 2 +- .../ui/screen/loggedOff/login/LoginScreen.kt | 1 + .../screen/loggedOff/login/LoginViewModel.kt | 1 + .../screen/loggedOff/signup/SignUpScreen.kt | 2 +- .../loggedOff/signup/SignUpViewModel.kt | 2 +- .../amethyst/ui/tor/TorSettingsDialog.kt | 1 + .../loggedIn/settings/LegalSettingsSection.kt | 3 + .../calendar/CalendarsViewModelTest.kt | 2 +- .../uploads/hls/HlsPublishOrchestratorTest.kt | 2 +- .../qrcode/scanner/QrScannerStateTest.kt | 3 + .../login/LoginViewModelKeyKindTest.kt | 1 + commons/ARCHITECTURE.md | 4 +- .../2026-08-30-commons-migration-sweep.md | 133 ++++++++++++++++++ commons/plans/README.md | 5 +- .../amethyst/commons/audio}/PitchShifter.kt | 3 +- .../commons/feeds}/FilterByListParams.kt | 2 +- .../commons/feeds}/StringFeedState.kt | 2 +- .../commons/feeds}/SupportedContent.kt | 2 +- .../amethyst/commons/feeds}/UserFeedState.kt | 2 +- .../commons/model}/ConcordLastRead.kt | 2 +- .../commons/model}/MarmotGroupLastRead.kt | 2 +- .../commons/model}/RelayGroupLastRead.kt | 2 +- .../amethyst/commons/model}/buzz/AgentWork.kt | 6 +- .../model/chats}/ConcordServerRoomNote.kt | 2 +- .../model/chats}/RelayGroupServerRoomNote.kt | 2 +- .../commons/model/composer}/DraftTagState.kt | 2 +- .../commons/model/composer}/IExpiration.kt | 2 +- .../model/composer}/NewMessageTagger.kt | 2 +- .../commons/model/composer}/SplitConversor.kt | 3 +- .../model/navigation}/RouteTextArgs.kt | 13 +- .../navigation}/ShareToDMRouteRewriter.kt | 4 +- .../GroupDiscoveryConstraintResolver.kt | 3 +- .../TransactionRowLabels.kt | 2 +- .../nip72Communities}/CommunityRulesLookup.kt | 2 +- .../commons/model/nip92IMeta}/ImetaContent.kt | 2 +- .../commons/music}/MusicFormatting.kt | 6 +- .../amethyst/commons/qrcode}/ScanResult.kt | 2 +- .../qrcode}/StructuredAppendAccumulator.kt | 2 +- .../RelayGroupOpenThreadsFilterAssembler.kt | 3 +- .../relayGroup}/RelayGroupsDiscoveryFilter.kt | 11 +- .../FilterFollowingEphemeralChats.kt | 2 +- .../chatrooms}/FilterFollowingPublicChats.kt | 2 +- .../FilterLastMessageFollowingPublicChats.kt | 2 +- .../chatrooms}/FilterNip04DMsFromMe.kt | 2 +- .../chatrooms}/FilterNip04DMsToMe.kt | 2 +- .../geocaches}/GeocacheListingKinds.kt | 2 +- .../hashtag}/FilterPostsByHashtags.kt | 2 +- .../polls/results/RelayPollResponseLoader.kt | 2 +- .../service/upload}/HlsPublishState.kt | 2 +- .../service/upload}/MediaUploadTracker.kt | 2 +- .../upload}/SuspendableConfirmation.kt | 2 +- .../commons}/tor/TorDialogViewModel.kt | 12 +- .../commons/util}/DebouncedPublisher.kt | 2 +- .../commons/viewmodels}/RelayFeedViewModel.kt | 5 +- .../UserExternalIdentitiesViewModel.kt | 10 +- .../commons/cashu}/CashuWalletDiscovery.kt | 2 +- .../mediaServers/MediaServerHealth.kt | 2 +- .../commons/model/chats}/HistoryDateFormat.kt | 2 +- .../commons/model/composer}/PreviewState.kt | 2 +- .../amethyst/commons/util}/TimeFormatUtils.kt | 2 +- .../amethyst/commons/util/ZapFormatter.kt | 12 +- .../commons/util}/ZapFormatterNoDecimals.kt | 2 +- .../commons/audio}/PitchShifterTest.kt | 2 +- .../commons/feeds}/SupportedContentTest.kt | 2 +- .../composer}/NewMessageTaggerKeyParseTest.kt | 3 +- .../model}/navigation/RouteTextArgsTest.kt | 6 +- .../navigation/ShareToDMRouteRewriterTest.kt | 4 +- .../RelayGroupDiscoveryConstraintTest.kt | 4 +- .../TransactionRowLabelsTest.kt | 3 +- .../CommunityRulesLookupTest.kt | 2 +- .../StructuredAppendAccumulatorTest.kt | 2 +- .../commons/util}/DebouncedPublisherTest.kt | 2 +- .../account/ui}/login/LoginErrorManager.kt | 2 +- .../amethyst/commons/audio}/VoicePreset.kt | 2 +- .../commons/clink/ui}/ClinkBudgetDialog.kt | 2 +- .../amethyst/commons/cordn/ui}/BusyLabel.kt | 2 +- .../commons/cordn/ui}/SettingsFormBlock.kt | 2 +- .../nip46RemoteSigner/ui}/Nip46ActivityUi.kt | 2 +- .../nip52Calendar/ui}/CalendarsViewMode.kt | 2 +- .../ui}/CommunityRulesViolationBanner.kt | 2 +- .../commons/relays/ui}/SubPurposeLabels.kt | 2 +- .../upload/ui}/StrippingFailureDialog.kt | 6 +- .../commons/ui/insets}/KeyboardState.kt | 3 +- .../bottombars/TabReselectCoordinator.kt | 2 +- .../ui/navigation/navs/ObservableNav.kt | 3 +- .../ui/navigation/navs}/ShareToDMNav.kt | 4 +- .../commons/ui/navigation/navs}/TwoPaneNav.kt | 3 +- .../commons/ui}/settings/SettingsCatalog.kt | 2 +- .../MentionPreservingInputTransformation.kt | 6 +- .../commons/audio}/RecordingIndicators.kt | 3 +- .../ui}/StreamingStatusFlags.kt | 2 +- .../ui/components}/InformationDialog.kt | 2 +- .../commons}/ui/components/UrlPreviewCard.kt | 5 +- .../commons}/ui/theme/ColorSchemePreview.kt | 3 +- .../ui}/settings/SettingsCatalogFilterTest.kt | 2 +- 339 files changed, 639 insertions(+), 551 deletions(-) delete mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PubKeyFormatter.kt delete mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt delete mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/EqualImmutableLists.kt rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio}/PitchShifter.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds}/FilterByListParams.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds}/StringFeedState.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds}/SupportedContent.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds}/UserFeedState.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model}/ConcordLastRead.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model}/MarmotGroupLastRead.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model}/RelayGroupLastRead.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model}/buzz/AgentWork.kt (94%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats}/ConcordServerRoomNote.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats}/RelayGroupServerRoomNote.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/draftTags => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/DraftTagState.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/IExpiration.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/NewMessageTagger.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/SplitConversor.kt (92%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation}/RouteTextArgs.kt (93%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation}/ShareToDMRouteRewriter.kt (92%) rename amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/GroupDiscoveryConstraint.kt => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/GroupDiscoveryConstraintResolver.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect}/TransactionRowLabels.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities}/CommunityRulesLookup.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip92IMeta}/ImetaContent.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/music}/MusicFormatting.kt (89%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode}/ScanResult.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode}/StructuredAppendAccumulator.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup}/RelayGroupOpenThreadsFilterAssembler.kt (94%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup}/RelayGroupsDiscoveryFilter.kt (78%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms}/FilterFollowingEphemeralChats.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms}/FilterFollowingPublicChats.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms}/FilterLastMessageFollowingPublicChats.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms}/FilterNip04DMsFromMe.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms}/FilterNip04DMsToMe.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/geocaches}/GeocacheListingKinds.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/hashtag}/FilterPostsByHashtags.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient}/polls/results/RelayPollResponseLoader.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload}/HlsPublishState.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload}/MediaUploadTracker.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload}/SuspendableConfirmation.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons}/tor/TorDialogViewModel.kt (90%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util}/DebouncedPublisher.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels}/RelayFeedViewModel.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/identity => commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels}/UserExternalIdentitiesViewModel.kt (88%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu}/CashuWalletDiscovery.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons}/mediaServers/MediaServerHealth.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/chats}/HistoryDateFormat.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/PreviewState.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util}/TimeFormatUtils.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util}/ZapFormatterNoDecimals.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/actions/uploads => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/audio}/PitchShifterTest.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/feeds}/SupportedContentTest.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/composer}/NewMessageTaggerKeyParseTest.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model}/navigation/RouteTextArgsTest.kt (91%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model}/navigation/ShareToDMRouteRewriterTest.kt (91%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups}/RelayGroupDiscoveryConstraintTest.kt (96%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/wallet => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect}/TransactionRowLabelsTest.kt (97%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities}/CommunityRulesLookupTest.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/qrcode}/StructuredAppendAccumulatorTest.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util}/DebouncedPublisherTest.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/account/ui}/login/LoginErrorManager.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio}/VoicePreset.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/clink/ui}/ClinkBudgetDialog.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui}/BusyLabel.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui}/SettingsFormBlock.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46 => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip46RemoteSigner/ui}/Nip46ActivityUi.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip52Calendar/ui}/CalendarsViewMode.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip72ModCommunities/ui}/CommunityRulesViolationBanner.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/ui}/SubPurposeLabels.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/ui}/StrippingFailureDialog.kt (91%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/insets}/KeyboardState.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons}/ui/navigation/bottombars/TabReselectCoordinator.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons}/ui/navigation/navs/ObservableNav.kt (95%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs}/ShareToDMNav.kt (93%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs}/TwoPaneNav.kt (93%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui}/settings/SettingsCatalog.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/text}/MentionPreservingInputTransformation.kt (94%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio}/RecordingIndicators.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui}/StreamingStatusFlags.kt (97%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components}/InformationDialog.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons}/ui/components/UrlPreviewCard.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons}/ui/theme/ColorSchemePreview.kt (98%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn => commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui}/settings/SettingsCatalogFilterTest.kt (98%) diff --git a/.claude/skills/feed-patterns/SKILL.md b/.claude/skills/feed-patterns/SKILL.md index ab8846db84..34acbe1ed5 100644 --- a/.claude/skills/feed-patterns/SKILL.md +++ b/.claude/skills/feed-patterns/SKILL.md @@ -1,6 +1,6 @@ --- name: feed-patterns -description: Feed composition and data-access layer patterns in Amethyst. Use when adding or modifying a feed (home, profile, hashtag, bookmarks, notifications, DMs, communities), working with the shared `FeedFilter` / `AdditiveFeedFilter` / `ChangesFlowFilter` / `FeedContentState` in `commons/.../feeds/`, the Android-only `AdditiveComplexFeedFilter` / `FilterByListParams` in `amethyst/.../ui/dal/`, or extending the `FeedViewModel` family in `commons/.../viewmodels/`. Covers how feeds scan `LocalCache`, react to changes, apply ordering, and render through Compose. +description: Feed composition and data-access layer patterns in Amethyst. Use when adding or modifying a feed (home, profile, hashtag, bookmarks, notifications, DMs, communities), working with the shared `FeedFilter` / `AdditiveFeedFilter` / `ChangesFlowFilter` / `FeedContentState` in `commons/.../feeds/`, `FilterByListParams` in `commons/.../feeds/`, the Android-only `AdditiveComplexFeedFilter` in `amethyst/.../ui/dal/`, or extending the `FeedViewModel` family in `commons/.../viewmodels/`. Covers how feeds scan `LocalCache`, react to changes, apply ordering, and render through Compose. --- # Feed Patterns @@ -101,7 +101,7 @@ Concrete filters (Home, Hashtag, Profile, Bookmark, Notifications, Communities, ## Filter Sharing (Android vs Desktop) - The filter **base classes** (`FeedFilter`, `AdditiveFeedFilter`, `ChangesFlowFilter`) and feed state (`FeedContentState`) are in `commons/.../feeds/` — **shared**. ViewModels are in `commons/.../viewmodels/` — **shared**. -- The **concrete** filters are platform-local: Android's in `amethyst/.../ui/screen/loggedIn/*/dal/`, Desktop's in `desktopApp/.../feeds/`. `amethyst/.../ui/dal/` keeps Android-only helpers (`AdditiveComplexFeedFilter`, `FilterByListParams`, `DefaultFeedOrder`) plus back-compat typealiases. +- The **concrete** filters are platform-local: Android's in `amethyst/.../ui/screen/loggedIn/*/dal/`, Desktop's in `desktopApp/.../feeds/`. `amethyst/.../ui/dal/` keeps Android-only helpers (`AdditiveComplexFeedFilter`, `DefaultFeedOrder`) plus back-compat typealiases; `FilterByListParams` is shared, in `commons/.../feeds/`. - When porting a feed, share the concrete filter only if both platforms need identical inclusion rules. ## Gotchas diff --git a/.claude/skills/feed-patterns/references/feed-filter-composition.md b/.claude/skills/feed-patterns/references/feed-filter-composition.md index 4c00329919..8946e9a105 100644 --- a/.claude/skills/feed-patterns/references/feed-filter-composition.md +++ b/.claude/skills/feed-patterns/references/feed-filter-composition.md @@ -12,7 +12,7 @@ Step-by-step recipe for composing a new feed. Assume the feed shows `Note`s filt | The feed is a simple list that changes frequently (e.g. bookmarks, lists) | `FeedFilter` + `ListChangeFeedViewModel` | | The feed is a DM thread | `ChatroomFeedViewModel` (already provides filter machinery) | -The bases live in `commons/src/commonMain/.../commons/ui/feeds/`; `AdditiveComplexFeedFilter` and the `FilterByListParams` / `DefaultFeedOrder` helpers in `amethyst/src/main/java/.../ui/dal/`. +The bases live in `commons/src/commonMain/.../commons/ui/feeds/`; `AdditiveComplexFeedFilter` and the `DefaultFeedOrder` helper in `amethyst/src/main/java/.../ui/dal/`; `FilterByListParams` in `commons/src/commonMain/.../commons/feeds/`. ## 2. Write the Filter diff --git a/.claude/skills/kotlin-multiplatform/references/abstraction-examples.md b/.claude/skills/kotlin-multiplatform/references/abstraction-examples.md index e1277dc75c..bbdc2c9bae 100644 --- a/.claude/skills/kotlin-multiplatform/references/abstraction-examples.md +++ b/.claude/skills/kotlin-multiplatform/references/abstraction-examples.md @@ -253,13 +253,13 @@ fun String.toDisplayHexKey(): String { **After:** ```kotlin -// commons/commonMain/formatters/PubKeyFormatter.kt +// commons/src/commonMain/.../commons/util/PubKeyFormatter.kt fun String.toDisplayHexKey(): String { return "${take(8)}:${takeLast(8)}" } // Both apps use it -import com.vitorpamplona.amethyst.commons.formatters.toDisplayHexKey +import com.vitorpamplona.amethyst.commons.util.toDisplayHexKey ``` **Why successful:** diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/MentionPreservingInputTransformationTest.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/MentionPreservingInputTransformationTest.kt index 227812f324..155ce6a0c4 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/MentionPreservingInputTransformationTest.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/MentionPreservingInputTransformationTest.kt @@ -26,7 +26,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.text.input.TextFieldBuffer import androidx.compose.foundation.text.input.TextFieldState import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith diff --git a/amethyst/src/fdroid/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt b/amethyst/src/fdroid/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt index a4ee5ec78a..5ab3d60652 100644 --- a/amethyst/src/fdroid/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt +++ b/amethyst/src/fdroid/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings import androidx.compose.ui.platform.UriHandler +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory // F-Droid distributes Amethyst as MIT-licensed free software; the build must // not surface links to external (e.g. GitHub-hosted) policy documents. diff --git a/amethyst/src/main/generated/baselineProfiles/baseline-prof.txt b/amethyst/src/main/generated/baselineProfiles/baseline-prof.txt index 016fe51292..90f8c4c78e 100644 --- a/amethyst/src/main/generated/baselineProfiles/baseline-prof.txt +++ b/amethyst/src/main/generated/baselineProfiles/baseline-prof.txt @@ -25005,11 +25005,11 @@ SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup Lcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenChatTailSubAssembler; SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenChatTailSubAssembler;->()V SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenChatTailSubAssembler;->(Lcom/vitorpamplona/quartz/nip01Core/relay/client/INostrClient;Lkotlin/jvm/functions/Function0;)V -Lcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler; -SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler;->()V -SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler;->(Lcom/vitorpamplona/quartz/nip01Core/relay/client/INostrClient;)V -Lcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler$group$1; -SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler$group$1;->(Ljava/lang/Object;)V +Lcom/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler; +SPLcom/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler;->()V +SPLcom/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler;->(Lcom/vitorpamplona/quartz/nip01Core/relay/client/INostrClient;)V +Lcom/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler$group$1; +SPLcom/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler$group$1;->(Ljava/lang/Object;)V Lcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsHistoryFilterAssembler; SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsHistoryFilterAssembler;->()V SPLcom/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsHistoryFilterAssembler;->(Lcom/vitorpamplona/quartz/nip01Core/relay/client/INostrClient;)V @@ -25535,13 +25535,13 @@ SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginButtonKt;->LoginButt Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginButtonKt$$ExternalSyntheticLambda0; SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginButtonKt$$ExternalSyntheticLambda0;->(Z)V SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginButtonKt$$ExternalSyntheticLambda0;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager; -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager;->()V -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager;->()V -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager;->clearErrors()V -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager;->getError()Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager$IErrorMsg; -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager;->setError(Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager$IErrorMsg;)V -Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager$IErrorMsg; +Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager; +SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager;->()V +SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager;->()V +SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager;->clearErrors()V +SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager;->getError()Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager$IErrorMsg; +SPLcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager;->setError(Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager$IErrorMsg;)V +Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager$IErrorMsg; Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreenKt; SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreenKt;->LoginPage$lambda$4(Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;Lkotlin/jvm/functions/Function0;ILandroidx/compose/runtime/Composer;I)Lkotlin/Unit; SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreenKt;->LoginPage(Lcom/vitorpamplona/amethyst/ui/screen/AccountSessionManager;ZLjava/lang/String;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;II)V @@ -25575,7 +25575,7 @@ SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->()V SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->clear()V SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getAcceptedTerms()Z -SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getErrorManager()Lcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager; +SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getErrorManager()Lcom/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager; SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getKey()Landroidx/compose/ui/text/input/TextFieldValue; SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getNeedsPassword()Z SPLcom/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel;->getOfferTemporaryLogin()Z 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 fe03751d12..e2a51f8237 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.commons.model.buzz.BuzzRelayDialect import com.vitorpamplona.amethyst.commons.model.buzz.BuzzWorkspaces import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filter +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannelListState import com.vitorpamplona.amethyst.commons.model.concord.ConcordSessionManager @@ -175,7 +176,6 @@ import com.vitorpamplona.amethyst.model.topNavFeeds.FeedTopNavFilterState import com.vitorpamplona.amethyst.model.trustedAssertions.TrustProviderListState import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.FileHeader -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.screen.loggedIn.EventProcessor import com.vitorpamplona.marmotquic.QuicAgentTextStreamTransport import com.vitorpamplona.quartz.buzz.threading.buzzThread diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountConcordActions.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountConcordActions.kt index 31c41e236d..198509f959 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountConcordActions.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountConcordActions.kt @@ -29,8 +29,8 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.filter import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.concord.ConcordCommunitySession +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.commons.viewmodels.ReplyMode -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordChannelLastReadRoute import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityList.withControlRoot import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEntry import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt index 3acb2ed75a..3e6978ed94 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt @@ -29,7 +29,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.navBarItemsFromNames import com.vitorpamplona.amethyst.commons.model.navigation.toNames import com.vitorpamplona.amethyst.commons.service.pow.PoWCategory import com.vitorpamplona.amethyst.commons.service.pow.PoWPolicy -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent import kotlinx.collections.immutable.ImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationReplyReceiver.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationReplyReceiver.kt index da7c86e2bc..4b69bfd809 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationReplyReceiver.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationReplyReceiver.kt @@ -30,10 +30,10 @@ import androidx.core.content.ContextCompat import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.model.accountsCache.AccountCacheState import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.cancelAndPrune import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.cancelChildlessGroupSummaries -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/RelayPurposeSummary.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/RelayPurposeSummary.kt index 1f7ac582a4..69d9e65b95 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/RelayPurposeSummary.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/RelayPurposeSummary.kt @@ -23,12 +23,12 @@ package com.vitorpamplona.amethyst.service.notifications import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.purposes +import com.vitorpamplona.amethyst.commons.relays.ui.SubPurposeLabels import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.relay_purpose_browsing import com.vitorpamplona.amethyst.commons.resources.relay_purpose_line import com.vitorpamplona.amethyst.commons.ui.loadPluralStringRes import com.vitorpamplona.amethyst.commons.ui.loadStringRes -import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.SubPurposeLabels import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/NwcPaymentNotifier.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/NwcPaymentNotifier.kt index d0c217ecf8..16c5fc2538 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/NwcPaymentNotifier.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/NwcPaymentNotifier.kt @@ -24,12 +24,12 @@ import android.content.Context import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.app_notification_payments_channel_message import com.vitorpamplona.amethyst.commons.ui.loadStringRes +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.notifications.NotificationCategory import com.vitorpamplona.amethyst.service.notifications.NotificationRoutes import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postStandard import com.vitorpamplona.amethyst.service.notifications.notificationManager -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/ZapNotification.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/ZapNotification.kt index 6f13908502..67c9c9e6cb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/ZapNotification.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/renderers/ZapNotification.kt @@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.commons.resources.app_notification_zaps_channe import com.vitorpamplona.amethyst.commons.resources.app_notification_zaps_channel_message_for import com.vitorpamplona.amethyst.commons.resources.app_notification_zaps_channel_message_from import com.vitorpamplona.amethyst.commons.ui.loadStringRes +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.notifications.NotificationCategory import com.vitorpamplona.amethyst.service.notifications.NotificationContent @@ -38,7 +39,6 @@ import com.vitorpamplona.amethyst.service.notifications.NotificationEnricher import com.vitorpamplona.amethyst.service.notifications.NotificationRoutes import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postStandard import com.vitorpamplona.amethyst.service.notifications.notificationManager -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt index 12900a0d28..7291d8e943 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt @@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.badges.BadgesFilterAssembl import com.vitorpamplona.amethyst.commons.relayClient.badges.profile.ProfileBadgesFilterAssembler import com.vitorpamplona.amethyst.commons.relayClient.calendars.CalendarsFilterAssembler import com.vitorpamplona.amethyst.commons.relayClient.channel.ChannelFilterAssembler +import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RelayGroupOpenThreadsFilterAssembler import com.vitorpamplona.amethyst.commons.relayClient.chess.ChessFilterAssembler import com.vitorpamplona.amethyst.commons.relayClient.communities.CommunityFilterAssembler import com.vitorpamplona.amethyst.commons.relayClient.communities.list.CommunitiesListFilterAssembler @@ -85,7 +86,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupJoinedStateFilterAssembler import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenChatHistoryFilterAssembler import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenChatTailFilterAssembler -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenThreadsFilterAssembler import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenThreadsHistoryFilterAssembler import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsDiscoveryFilterAssembler import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsOnRelayFilterAssembler diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt index f7f574dbd6..b9d4cbe885 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt @@ -78,12 +78,14 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_create_and_add_inv import com.vitorpamplona.amethyst.commons.resources.lightning_invoice import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind import com.vitorpamplona.amethyst.commons.richtext.RichTextParser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextField import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.invoice.AddLnInvoiceButton import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt index 3c16bf0fb7..cb9aedbd85 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt @@ -38,6 +38,8 @@ import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_de import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.richtext.RichTextParser +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.text.currentWord import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor @@ -45,9 +47,7 @@ import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt index d217a6f5e9..6c58b3d639 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt @@ -36,11 +36,11 @@ import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_de import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.richtext.RichTextParser +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index 1522eb0928..905d049668 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -71,6 +71,7 @@ import com.vitorpamplona.amethyst.commons.resources.strip_metadata_description import com.vitorpamplona.amethyst.commons.resources.strip_metadata_label import com.vitorpamplona.amethyst.commons.resources.video_codec_h265_description import com.vitorpamplona.amethyst.commons.resources.video_codec_h265_label +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UrlUserTagOutputTransformation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UrlUserTagOutputTransformation.kt index f6c87b44b9..8306f7eba2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UrlUserTagOutputTransformation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UrlUserTagOutputTransformation.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.style.TextDecoration import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.ui.text.MENTION_REGEX import com.vitorpamplona.quartz.nip19Bech32.decodePublicKeyAsHexOrNull import kotlin.coroutines.cancellation.CancellationException diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt index f9c6ce849e..e8c09e4a3d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt @@ -61,6 +61,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.mediaServers.ServerHealth import com.vitorpamplona.amethyst.commons.mediaServers.ui.MediaServerEditField import com.vitorpamplona.amethyst.commons.model.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt index d5be72fc12..757b5c7023 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt @@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.actions.mediaServers import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.vitorpamplona.amethyst.commons.mediaServers.MediaServerHealthProbe +import com.vitorpamplona.amethyst.commons.mediaServers.ServerHealth import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType import com.vitorpamplona.amethyst.commons.service.http.IRoleBasedHttpClientBuilder diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordVoiceButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordVoiceButton.kt index b05ef98a56..fe3f79176a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordVoiceButton.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordVoiceButton.kt @@ -37,6 +37,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.audio.ExpandingCirclesAnimation +import com.vitorpamplona.amethyst.commons.audio.FloatingRecordingIndicator import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.resources.Res diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationController.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationController.kt index 9883511d35..3f3a70fb4c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationController.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationController.kt @@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.actions.uploads import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationSection.kt index 5eb5947e91..28b2cfe15a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizationSection.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.voice_anonymize_description import com.vitorpamplona.amethyst.commons.resources.voice_anonymize_title diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizer.kt index 0a53b6412e..0ea2b1c747 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceAnonymizer.kt @@ -25,6 +25,8 @@ import android.media.MediaCodecInfo import android.media.MediaExtractor import android.media.MediaFormat import android.media.MediaMuxer +import com.vitorpamplona.amethyst.commons.audio.PitchShifter +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessagePreview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessagePreview.kt index 6db110e268..f43c6b5a19 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessagePreview.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessagePreview.kt @@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.commons.resources.recording_indicator_descript import com.vitorpamplona.amethyst.commons.resources.remove import com.vitorpamplona.amethyst.commons.ui.components.AudioWaveformReadOnly import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatSecondsToTime import com.vitorpamplona.quartz.nipA0VoiceMessages.AudioMeta import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.delay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePresetSelector.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePresetSelector.kt index c0b98e8f9b..3e0ca3fbd4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePresetSelector.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePresetSelector.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.amethyst.commons.ui.stringRes @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/LoadUrlPreview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/LoadUrlPreview.kt index c852bc1208..5087df059e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/LoadUrlPreview.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/LoadUrlPreview.kt @@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlPdf import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled +import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index bfa2899272..981807d040 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -122,6 +122,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.CashuCardBorders import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.inlinePlaceholder +import com.vitorpamplona.amethyst.commons.util.toShortDisplay import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserNickname @@ -129,7 +130,6 @@ import com.vitorpamplona.amethyst.service.uploads.blossom.bud10.openBlossomUriAs import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.creators.invoice.ClinkOfferPreview import com.vitorpamplona.amethyst.ui.note.creators.invoice.MayBeInvoicePreview -import com.vitorpamplona.amethyst.ui.note.toShortDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 820a1ae3b2..9c064731ca 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -107,6 +107,7 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.isAnimatedMediaUrl import com.vitorpamplona.amethyst.commons.service.image.placeholderModel import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled +import com.vitorpamplona.amethyst.commons.ui.components.InformationDialog import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation import com.vitorpamplona.amethyst.commons.ui.components.M3ActionDialog import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow @@ -127,7 +128,6 @@ import com.vitorpamplona.amethyst.model.MediaAspectRatioCache import com.vitorpamplona.amethyst.service.images.BlossomFetcher import com.vitorpamplona.amethyst.service.playback.composable.VideoView import com.vitorpamplona.amethyst.service.uploads.blossom.bud10.openBlossomUriAsIntent -import com.vitorpamplona.amethyst.ui.actions.InformationDialog import com.vitorpamplona.amethyst.ui.components.pdf.PdfPreviewCard import com.vitorpamplona.amethyst.ui.components.pdf.PdfViewerDialog import com.vitorpamplona.amethyst.ui.note.BlankNote diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt index 399c6687e1..c17b98d42c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.components.toasts import androidx.compose.runtime.Composable import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.ui.components.InformationDialog import com.vitorpamplona.amethyst.commons.ui.components.toasts.ActionableStringToastMsg import com.vitorpamplona.amethyst.commons.ui.components.toasts.ResourceToastMsg import com.vitorpamplona.amethyst.commons.ui.components.toasts.StringToastMsg @@ -31,7 +32,6 @@ import com.vitorpamplona.amethyst.commons.ui.components.toasts.ToastManager import com.vitorpamplona.amethyst.commons.ui.components.toasts.multiline.MultiErrorToastMsg import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.actions.InformationDialog import com.vitorpamplona.amethyst.ui.components.toasts.multiline.MultiUserErrorMessageDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel 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 cb32a317ff..79c3116611 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 @@ -51,6 +51,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.MediaFeedRoute import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.favoriteIds import com.vitorpamplona.amethyst.commons.model.navigation.isSameRoute +import com.vitorpamplona.amethyst.commons.model.navigation.limitToRouteTextArg import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState import com.vitorpamplona.amethyst.commons.relayClient.event.LocalEventFinder import com.vitorpamplona.amethyst.commons.relayClient.user.LocalUserFinder @@ -59,6 +60,8 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.invalid_nip19_uri import com.vitorpamplona.amethyst.commons.resources.invalid_nip19_uri_description import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout +import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.LocalTabReselectCoordinator +import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.TabReselectCoordinator import com.vitorpamplona.amethyst.service.crashreports.DisplayCrashMessages import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayAuthPromptHost import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages @@ -76,14 +79,11 @@ import com.vitorpamplona.amethyst.ui.call.CallActivity import com.vitorpamplona.amethyst.ui.components.getActivity import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages import com.vitorpamplona.amethyst.ui.layouts.rememberScreenLayoutSpec -import com.vitorpamplona.amethyst.ui.navigation.bottombars.LocalTabReselectCoordinator -import com.vitorpamplona.amethyst.ui.navigation.bottombars.TabReselectCoordinator import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.navigation.navs.rememberNav import com.vitorpamplona.amethyst.ui.navigation.routes.consumesSharesInPlace import com.vitorpamplona.amethyst.ui.navigation.routes.getRouteWithArguments import com.vitorpamplona.amethyst.ui.navigation.routes.isBaseRoute -import com.vitorpamplona.amethyst.ui.navigation.routes.limitToRouteTextArg import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeScreen import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt index c91ee5a633..ec0ca2e514 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt @@ -50,7 +50,10 @@ import com.vitorpamplona.amethyst.commons.favorites.FavoriteAppIcon import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.ui.insets.KeyboardState +import com.vitorpamplona.amethyst.commons.ui.insets.keyboardAsState import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout +import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.LocalTabReselectCoordinator import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppNavigationRail.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppNavigationRail.kt index 30dddb074a..a4397757b0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppNavigationRail.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppNavigationRail.kt @@ -39,6 +39,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.LocalTabReselectCoordinator import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.navigation.routes.getRouteWithArguments import com.vitorpamplona.amethyst.ui.navigation.topbars.LoggedInUserPictureDrawer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt index 1df6a6cfc4..f3fe6c163e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt @@ -80,8 +80,8 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.AccountPictureModifier import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp +import com.vitorpamplona.amethyst.commons.util.toShortDisplay import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo -import com.vitorpamplona.amethyst.ui.note.toShortDisplay import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedOff.AddAccountDialog diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt index a5fb6dbef6..a9133a7c43 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/RememberNavs.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.navigation.compose.rememberNavController import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.ObservableNav @Composable fun rememberNav(): Nav { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteMaker.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteMaker.kt index 198ff3adac..9cfc9b9ab6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteMaker.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteMaker.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.model.navigation.limitToRouteTextArg import com.vitorpamplona.amethyst.commons.model.navigation.minichatRouteFor import com.vitorpamplona.amethyst.commons.model.navigation.routeFor import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Bolt12ZapGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Bolt12ZapGallery.kt index a5d895987b..ae70fc9d85 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Bolt12ZapGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Bolt12ZapGallery.kt @@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp import com.vitorpamplona.amethyst.commons.ui.theme.Size35Modifier import com.vitorpamplona.amethyst.commons.ui.theme.StdStartPadding import com.vitorpamplona.amethyst.commons.ui.theme.WidthAuthorPictureModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt index a227d40fcb..651a79a79b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt @@ -105,6 +105,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.WidthAuthorPictureModifierWit import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor import com.vitorpamplona.amethyst.commons.ui.theme.overPictureBackground import com.vitorpamplona.amethyst.commons.ui.theme.profile35dpModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsScore import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapGallery.kt index d0198e2beb..f97e5b0712 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapGallery.kt @@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size20dp import com.vitorpamplona.amethyst.commons.ui.theme.Size35Modifier import com.vitorpamplona.amethyst.commons.ui.theme.StdStartPadding import com.vitorpamplona.amethyst.commons.ui.theme.WidthAuthorPictureModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapUserSetCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapUserSetCompose.kt index 1417cc6c83..1665f2cc2f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapUserSetCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NutzapUserSetCompose.kt @@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NutzapUserSetCard diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/OnchainZapGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/OnchainZapGallery.kt index 2c0760b419..e7db7e7b22 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/OnchainZapGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/OnchainZapGallery.kt @@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp import com.vitorpamplona.amethyst.commons.ui.theme.Size35Modifier import com.vitorpamplona.amethyst.commons.ui.theme.StdStartPadding import com.vitorpamplona.amethyst.commons.ui.theme.WidthAuthorPictureModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PubKeyFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PubKeyFormatter.kt deleted file mode 100644 index f31b1448f7..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PubKeyFormatter.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.note - -import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.core.toHexKey - -fun ByteArray.toHexShortDisplay(): String = toHexKey().toShortDisplay() - -fun String.toShortDisplay(): String { - if (length <= 16) return this - return replaceRange(8, length - 8, "…") -} - -fun HexKey.toDisplayHexKey(): String = this.toShortDisplay() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index a74451ba8f..bc90957b86 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -114,6 +114,7 @@ import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.vitorpamplona.amethyst.commons.audio.FloatingRecordingIndicator import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder import com.vitorpamplona.amethyst.commons.hashtags.Cashu import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons @@ -206,6 +207,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.reactionBox import com.vitorpamplona.amethyst.commons.ui.theme.ripple24dp import com.vitorpamplona.amethyst.commons.ui.theme.selectedReactionBoxModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.MIN_ONCHAIN_ZAP_SATS import com.vitorpamplona.amethyst.model.ReactionRowAction import com.vitorpamplona.amethyst.model.ReactionRowItem @@ -226,7 +228,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo -import com.vitorpamplona.amethyst.ui.actions.uploads.FloatingRecordingIndicator import com.vitorpamplona.amethyst.ui.actions.uploads.MAX_VOICE_RECORD_SECONDS import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox import com.vitorpamplona.amethyst.ui.navigation.routes.routeReplyTo diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt index c964eb47d6..3e683cb40e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt @@ -49,8 +49,8 @@ import com.vitorpamplona.amethyst.commons.ui.theme.ButtonPadding import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.StdPadding import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.viewmodels.MyRelayInfo import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.MyRelayInfo import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt index af65ce3dbb..82ca56da87 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt @@ -118,6 +118,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SettingsCategorySpacingModifi import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonRow import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt index ac414ad2b2..06b87aaaa0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt @@ -112,6 +112,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.ZeroPadding import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.ZapPaymentHandler import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt deleted file mode 100644 index 0e9f839b46..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.note - -import java.math.BigDecimal -import java.math.RoundingMode -import java.text.DecimalFormat - -val TenGiga = BigDecimal(10000000000) -val OneGiga = BigDecimal(1000000000) -val TenMega = BigDecimal(10000000) -val OneMega = BigDecimal(1000000) -val TenKilo = BigDecimal(10000) -val OneKilo = BigDecimal(1000) - -private val dfGBig = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#.#G") - } - -private val dfGSmall = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#.0G") - } - -private val dfMBig = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#.#M") - } - -private val dfMSmall = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#.0M") - } - -private val dfK = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#.#k") - } - -private val dfN = - object : ThreadLocal() { - override fun initialValue() = DecimalFormat("#") - } - -fun showAmount(amount: BigDecimal?): String { - if (amount == null) return "" - if (amount.abs() < BigDecimal(0.01)) return "" - - return when { - amount >= TenGiga -> dfGBig.get()!!.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) - amount >= OneGiga -> dfGSmall.get()!!.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) - amount >= TenMega -> dfMBig.get()!!.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) - amount >= OneMega -> dfMSmall.get()!!.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) - amount >= TenKilo -> dfK.get()!!.format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP)) - else -> dfN.get()!!.format(amount) - } -} - -fun showAmountWithZero(amount: BigDecimal?): String { - if (amount == null) return "0" - if (amount.abs() < BigDecimal(0.01)) return "0" - - return showAmount(amount) -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapPollNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapPollNote.kt index 60899cf0e4..e476f5e5df 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapPollNote.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapPollNote.kt @@ -105,6 +105,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.grayText import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.ripple24dp import com.vitorpamplona.amethyst.commons.ui.theme.subtleBorder +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.ZapPaymentHandler import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/ExpirationDatePicker.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/ExpirationDatePicker.kt index d1775e6f0f..2e6e2421d0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/ExpirationDatePicker.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/ExpirationDatePicker.kt @@ -55,6 +55,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.confirm import com.vitorpamplona.amethyst.commons.resources.expiration_date_explainer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/messagefield/MessageField.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/messagefield/MessageField.kt index 763cdd05b7..bb58f52b24 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/messagefield/MessageField.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/messagefield/MessageField.kt @@ -42,8 +42,8 @@ import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField import kotlinx.coroutines.delay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/DisplayPreviews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/DisplayPreviews.kt index 502274a919..119970dbcd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/DisplayPreviews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/DisplayPreviews.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.FillWidthQuoteBorderModifier import com.vitorpamplona.amethyst.commons.ui.theme.HalfHorzPadding diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewUrl.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewUrl.kt index 4e7aa7b8f3..2ac9b1ee4e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewUrl.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewUrl.kt @@ -43,6 +43,7 @@ import coil3.compose.AsyncImage import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled +import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState @@ -50,7 +51,6 @@ import com.vitorpamplona.amethyst.model.UrlCachedPreviewer import com.vitorpamplona.amethyst.service.playback.composable.VideoView import com.vitorpamplona.amethyst.ui.components.ClickableUrl import com.vitorpamplona.amethyst.ui.components.DisplayUrlWithLoadingSymbol -import com.vitorpamplona.amethyst.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.ui.components.WaitAndDisplay import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/ForwardZapTo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/ForwardZapTo.kt index ad2b1c3ea5..fc59070ecb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/ForwardZapTo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/ForwardZapTo.kt @@ -49,12 +49,12 @@ import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextF import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.ZapSplitIcon import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.note.BaseUserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt index 91ff84a092..50e02948d5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt @@ -64,9 +64,9 @@ import com.vitorpamplona.amethyst.commons.ui.note.buttons.PostButton import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteReplies -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt index f6550f6e3a..71616b6476 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt @@ -36,8 +36,13 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState @@ -46,6 +51,8 @@ import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_de import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.service.pow.PoWReplay +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.notify.IAudience @@ -60,18 +67,11 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber -import com.vitorpamplona.amethyst.ui.note.creators.previews.PreviewState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/DisplayExternalId.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/DisplayExternalId.kt index 8bc7198144..5f1e9b9626 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/DisplayExternalId.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/DisplayExternalId.kt @@ -45,12 +45,12 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.external_id_scope import com.vitorpamplona.amethyst.commons.resources.external_url_scope +import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier -import com.vitorpamplona.amethyst.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.ui.components.rememberUrlPreviewState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt index 208535d4b4..572558dc41 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt @@ -68,6 +68,7 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.composer.AudienceSelection import com.vitorpamplona.amethyst.commons.nip30CustomEmojis.ui.ShowEmojiSuggestionList +import com.vitorpamplona.amethyst.commons.nip72ModCommunities.ui.CommunityRulesViolationBanner import com.vitorpamplona.amethyst.commons.relayClient.communities.CommunityFilterAssemblerSubscription import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.geo_post_change_place @@ -77,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_invoice import com.vitorpamplona.amethyst.commons.resources.post_anonymously import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind import com.vitorpamplona.amethyst.commons.resources.zapraiser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.ContentSensitivityExplainer @@ -98,7 +100,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chat.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chat.kt index 3f81c7b6ce..55e2bcc6a9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chat.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chat.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import com.vitorpamplona.amethyst.commons.model.EmptyTagList import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.nip92IMeta.appendMissingImetaUrls import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Classifieds.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Classifieds.kt index 4bf3991117..6c4e230095 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Classifieds.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Classifieds.kt @@ -45,10 +45,10 @@ import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder import com.vitorpamplona.amethyst.commons.ui.theme.subtleBorder +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent import kotlinx.collections.immutable.ImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt index 57712a02a7..95c20d0123 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt @@ -93,6 +93,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.components.RichTextViewer @@ -109,7 +110,6 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.externalLinkForNote import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.nip72ModCommunities.definition.tags.ModeratorTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Goal.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Goal.kt index 0c314f5017..7d6c699e66 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Goal.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Goal.kt @@ -53,12 +53,12 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP import com.vitorpamplona.amethyst.commons.ui.theme.fundraiserProgressColor import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip75ZapGoals.ZapGoalEvent import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivity.kt index cb90389db7..599b5b6438 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/LiveActivity.kt @@ -49,6 +49,8 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.routeFor +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.LiveFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.ScheduledFlag import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.live_stream_has_ended import com.vitorpamplona.amethyst.commons.resources.live_stream_is_offline @@ -61,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.playback.composable.VideoView import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote import com.vitorpamplona.amethyst.ui.components.MyAsyncImage @@ -71,12 +74,9 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CrossfadeCheckIfVideoIsOnline import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MeetingSpace.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MeetingSpace.kt index bae3eb41e3..163b3bbc5d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MeetingSpace.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MeetingSpace.kt @@ -49,6 +49,8 @@ import androidx.core.net.toUri import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.navigation.routeFor +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.LiveFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.ScheduledFlag import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.meeting_room_in_space import com.vitorpamplona.amethyst.commons.resources.meeting_room_in_space_unknown @@ -72,15 +74,13 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy5dp import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CrossfadeCheckIfVideoIsOnline -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt index d3518bc2f1..cb9ddb2e41 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt @@ -62,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists +import com.vitorpamplona.amethyst.commons.music.formatTrackDuration import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.music_playlist_collaborative import com.vitorpamplona.amethyst.commons.resources.music_playlist_edit_action diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt index ec896a8963..026f8c118c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt @@ -58,6 +58,7 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists +import com.vitorpamplona.amethyst.commons.music.formatTrackDuration import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Nutzap.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Nutzap.kt index a6a0843de2..071875487c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Nutzap.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Nutzap.kt @@ -38,10 +38,10 @@ import com.vitorpamplona.amethyst.commons.ui.note.ActivityCardFrame import com.vitorpamplona.amethyst.commons.ui.note.ActivityHeaderRow import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.ui.note.CrossfadeToDisplayComment import com.vitorpamplona.amethyst.ui.note.DisplayBlankAuthor import com.vitorpamplona.amethyst.ui.note.UserPicture -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.claimedSatsTotal diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt index 4ed610bed1..d4ffca2d73 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.feeds.UserFeedState import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt index d0ee9ac934..953e7058cf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt @@ -27,11 +27,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.commons.feeds.FeedFilter import com.vitorpamplona.amethyst.commons.feeds.InvalidatableContent +import com.vitorpamplona.amethyst.commons.feeds.UserFeedState import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.BundledUpdate +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.checkNotInMainThread -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.utils.Log import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index faa2f7b8af..8f49870418 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -50,6 +50,7 @@ import com.vitorpamplona.amethyst.commons.model.UiSettingsFlow import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.backups.ReplaceableBackupConflict import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry @@ -110,6 +111,9 @@ import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState import com.vitorpamplona.amethyst.commons.ui.state.GenericBaseCache import com.vitorpamplona.amethyst.commons.ui.state.GenericBaseCacheAsync +import com.vitorpamplona.amethyst.commons.util.DebouncedPublisher +import com.vitorpamplona.amethyst.commons.util.showAmount +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.logTime import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AccountSettings @@ -127,12 +131,9 @@ import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.dismis import com.vitorpamplona.amethyst.service.pow.powKindLabelRes import com.vitorpamplona.amethyst.service.relayClient.reqCommand.RelaySubscriptionsCoordinator import com.vitorpamplona.amethyst.ui.actions.MediaSaverToDisk -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus import com.vitorpamplona.amethyst.ui.note.payViaIntent -import com.vitorpamplona.amethyst.ui.note.showAmount -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.send.MarmotGroupIconChange import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.send.MarmotGroupIconUpload import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.send.MarmotGroupIconUploader diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/dal/ArticlesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/dal/ArticlesFeedFilter.kt index 7bea4bb080..56bdbdcce6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/dal/ArticlesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/dal/ArticlesFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/dal/BadgesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/dal/BadgesFeedFilter.kt index 9f650a9a2a..3425254468 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/dal/BadgesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/dal/BadgesFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeDialog.kt index e3d2855357..b417ccb3d4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeDialog.kt @@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.commons.resources.media_compression_quality_un import com.vitorpamplona.amethyst.commons.resources.new_badge import com.vitorpamplona.amethyst.commons.resources.strip_metadata_description import com.vitorpamplona.amethyst.commons.resources.strip_metadata_label +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe @@ -91,7 +92,6 @@ import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.Settin import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelect import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeModel.kt index 5a96572326..b59ed9928f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/post/NewBadgeModel.kt @@ -34,11 +34,11 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardScreen.kt index 580444c5c0..93bda944b7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardScreen.kt @@ -88,6 +88,9 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.model.buzz.AgentWorkItem +import com.vitorpamplona.amethyst.commons.model.buzz.AgentWorkKind +import com.vitorpamplona.amethyst.commons.model.buzz.AgentWorkState import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.note.elements.TimeAgo diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardViewModel.kt index fe0d4e9ebf..49ab04252a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWorkBoardViewModel.kt @@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.buzz import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.vitorpamplona.amethyst.commons.model.buzz.AgentWorkBoard +import com.vitorpamplona.amethyst.commons.model.buzz.AgentWorkItem import com.vitorpamplona.amethyst.commons.model.buzz.BuzzJobAggregator import com.vitorpamplona.amethyst.commons.model.buzz.WorkflowRunAggregator import com.vitorpamplona.amethyst.model.Account diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzForumPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzForumPostScreen.kt index d3379eb592..de6e21895d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzForumPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzForumPostScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.buzz_forum_body_label import com.vitorpamplona.amethyst.commons.resources.buzz_forum_new_title @@ -49,7 +50,6 @@ import com.vitorpamplona.amethyst.commons.resources.buzz_forum_post_action import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.buzz.forum.ForumPostEvent import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/ForumReplyNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/ForumReplyNewMessageViewModel.kt index 55f0a6984d..d7845566f9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/ForumReplyNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/ForumReplyNewMessageViewModel.kt @@ -20,8 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.buzz +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.send.ChannelNewMessageViewModel import com.vitorpamplona.quartz.buzz.forum.ForumCommentEvent import com.vitorpamplona.quartz.nip01Core.core.Event diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsScreen.kt index e498d73b81..5b019c2ac3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsScreen.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.nip52Calendar.ui.CalendarsViewMode import com.vitorpamplona.amethyst.commons.nip52Calendar.ui.NewCalendarButton import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.FabBottomBarPadded diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsTopBar.kt index 433f7224b8..3ad0fc5182 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsTopBar.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter +import com.vitorpamplona.amethyst.commons.nip52Calendar.ui.CalendarsViewMode import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.select_list_to_filter import com.vitorpamplona.amethyst.commons.search.SearchSeed diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewModel.kt index 5b41ea50f8..fe401d2b98 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewModel.kt @@ -36,6 +36,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.nip52Calendar.MonthGridBarSegment import com.vitorpamplona.amethyst.commons.model.nip52Calendar.computeMonthGridBars import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpanded +import com.vitorpamplona.amethyst.commons.nip52Calendar.ui.CalendarsViewMode import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt index 65ef31262e..50b42f8a00 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -28,7 +29,6 @@ import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.nip52Calendar.upcomingFirstCalendarOrder import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarCollectionsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarCollectionsFeedFilter.kt index 7396b10d5a..ef43b1e29b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarCollectionsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarCollectionsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnComposer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnComposer.kt index c892f3d020..a09a212e37 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnComposer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnComposer.kt @@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestio import com.vitorpamplona.amethyst.commons.nip30CustomEmojis.ui.ShowEmojiSuggestionList import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cordn_composer_hint +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.currentWord import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder @@ -56,7 +57,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.RecordingResult import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatFeedView.kt index 25519ffbb2..69b60bc6ff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatFeedView.kt @@ -42,13 +42,13 @@ import com.vitorpamplona.amethyst.commons.chats.ui.watchChatGroupPosition import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.feeds.FeedState import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/LoadingReplyNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/LoadingReplyNote.kt index 850f26e70b..bcf67f43bc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/LoadingReplyNote.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/LoadingReplyNote.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.relayClient.paging.PagingStatus import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.chats_reply_not_found diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChatZap.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChatZap.kt index c52b22a690..b3d0c85a11 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChatZap.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChatZap.kt @@ -48,12 +48,12 @@ import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier import com.vitorpamplona.amethyst.commons.ui.theme.Size20dp import com.vitorpamplona.amethyst.commons.ui.theme.Size24Modifier +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.CrossfadeToDisplayComment import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderRegularTextNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderRegularTextNote.kt index ba2c0c0dfb..b28fd4501d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderRegularTextNote.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderRegularTextNote.kt @@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.commons.chats.ui.jumboEmojiCount import com.vitorpamplona.amethyst.commons.chats.ui.jumboEmojiFontSize import com.vitorpamplona.amethyst.commons.model.EmptyTagList import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.nip92IMeta.appendMissingImetaUrls import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.could_not_decrypt_the_message @@ -39,7 +40,6 @@ import com.vitorpamplona.amethyst.ui.components.SensitivityWarning import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContentOrNull import com.vitorpamplona.amethyst.ui.note.types.RenderAudioFromIMeta -import com.vitorpamplona.amethyst.ui.note.types.appendMissingImetaUrls import com.vitorpamplona.amethyst.ui.note.types.getAudioMetaWithWaveform import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt index 1a480a9adc..4269133bc2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupChatView.kt @@ -50,6 +50,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.chats.ui.ThinSendButton import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.marmot_group_composer_disbanding import com.vitorpamplona.amethyst.commons.resources.marmot_group_composer_leaving @@ -61,13 +62,13 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt index 7894287c53..d94e18af51 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupListScreen.kt @@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.commons.chats.ui.ChatUnreadBadge import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.resources.Res diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt index 14f7c0528b..611bf714b2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt @@ -41,6 +41,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.feeds.FeedState import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.model.privateChatLastReadRoute import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.chats_history_proto_nip04 @@ -61,7 +62,6 @@ import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.elements.ObserveRelayListForDMsAndDisplayIfNotFound import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.RefreshingChatroomFeedView -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.formatHistoryReachDate import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.dal.ChatroomFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.datasource.ChatroomFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.DmReportWarningCard diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt index ca797a2b12..889e7ded8a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt @@ -36,10 +36,16 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField import com.vitorpamplona.amethyst.commons.ui.text.currentWord @@ -48,15 +54,9 @@ import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber -import com.vitorpamplona.amethyst.ui.note.creators.previews.PreviewState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.ChatFileSender import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.ChatFileUploader diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt index 5b32232905..38c55eadf4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt @@ -104,6 +104,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.AddZapraise import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.ZapRaiserRequest import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP @@ -113,7 +114,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt index bb752cb228..11b011c810 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt @@ -62,10 +62,12 @@ import com.vitorpamplona.amethyst.commons.resources.reply_here import com.vitorpamplona.amethyst.commons.resources.retry_without_encryption import com.vitorpamplona.amethyst.commons.resources.this_message_will_disappear_in import com.vitorpamplona.amethyst.commons.resources.upload_without_encryption_warning +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.timeAheadNoDot import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier @@ -77,8 +79,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy10dp import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordChannelScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordChannelScreen.kt index fc1c1f025b..81f5853241 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordChannelScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordChannelScreen.kt @@ -59,7 +59,9 @@ import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.feeds.FeedState import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.model.concord.ConcordCommunitySession +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.commons.nip30CustomEmojis.ui.ShowEmojiSuggestionList import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.back @@ -80,6 +82,7 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier @@ -87,7 +90,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia @@ -96,7 +98,6 @@ import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.WatchAndLoad import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.RefreshingChatroomFeedView -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.formatHistoryReachDate import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.ChatFileUploader import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.SuccessfulUploads import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.datasource.ConcordChannelHistorySubAssembler diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordUnread.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordUnread.kt index 93bca99702..82b63aa77e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordUnread.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordUnread.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.model.Channel import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.quartz.concord.cord03Channels.ConcordChannelId import com.vitorpamplona.quartz.nip22Comments.CommentEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamTopZappers.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamTopZappers.kt index 9744b58ed0..7d31a80b08 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamTopZappers.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamTopZappers.kt @@ -60,9 +60,9 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier import com.vitorpamplona.amethyst.commons.ui.theme.Size24dp +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.commons.viewmodels.LiveStreamTopZappersViewModel import com.vitorpamplona.amethyst.ui.note.UserPicture -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import java.math.BigDecimal diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt index fc50788692..99a26c721d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LongLiveActivityChannelHeader.kt @@ -54,6 +54,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.elements.NormalTimeAgo import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.Size25dp +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor @@ -65,7 +66,6 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt index 7cf7e4a9ce..7279559775 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/ShortLiveActivityChannelHeader.kt @@ -39,6 +39,8 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.LiveFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.OfflineFlag import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing import com.vitorpamplona.amethyst.commons.ui.theme.Size34dp @@ -49,8 +51,6 @@ import com.vitorpamplona.amethyst.ui.note.LikeReaction import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.OfflineFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelView.kt index 9a6aec581c..18f185b258 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelView.kt @@ -43,7 +43,9 @@ import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.feeds.FeedState import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.buzz.BuzzRelayDialect +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.model.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.relay_group_invite_only_to_post import com.vitorpamplona.amethyst.commons.resources.relay_group_join_to_post @@ -58,7 +60,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.RefreshingChatroomFeedView -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.formatHistoryReachDate import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.dal.ChannelFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.ChannelFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupOpenChatHistorySubAssembler diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt index b2125dac37..f2c36607d5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt @@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.routeFor import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.toGroupConstraints import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.relay_group_badge_invite_only import com.vitorpamplona.amethyst.commons.resources.relay_group_badge_live @@ -99,7 +100,6 @@ import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.dal.relayGroupDiscoveryChannelFor -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.dal.toGroupConstraints import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupCardWarmupSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsDiscoveryFilterAssemblerSubscription import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupUnread.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupUnread.kt index 3937effabe..1a8ef9e314 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupUnread.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupUnread.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.chats.isMinichatReply import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.model.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryFeedFilter.kt index afcb987b7e..7aec5941ff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryFeedFilter.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.GroupDiscoveryConstraint import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.toGroupConstraints import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.nip11RelayInfo.isRelaySignedRelayGroup diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsSubscription.kt index 154db5d648..f94b833729 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsSubscription.kt @@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relay import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RelayGroupOpenThreadsFilterAssembler +import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RelayGroupOpenThreadsQueryState import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip29RelayGroups.GroupId diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoverySubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoverySubAssembler.kt index 23715d84f2..d4d8353db7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoverySubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoverySubAssembler.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAuthors +import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsDiscovery import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.relayGroupChannelsByRelay import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.scopedTo diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt index 993ee2c827..cd4edf2318 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt @@ -38,6 +38,9 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.buzz.BuzzRelayDialect import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel @@ -53,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_ import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.richtext.UrlParser import com.vitorpamplona.amethyst.commons.service.pow.PoWReplay +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.text.currentWord import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor @@ -62,12 +66,8 @@ import com.vitorpamplona.amethyst.commons.viewmodels.ReplyMode import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt index 803b0ef134..a1093b684d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt @@ -53,16 +53,16 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.buzz_editing_banner import com.vitorpamplona.amethyst.commons.resources.cancel import com.vitorpamplona.amethyst.commons.resources.reply_here +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt index 5556468cd8..514b461e3f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt @@ -54,10 +54,14 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.chatMessageMarksRoomAsRead +import com.vitorpamplona.amethyst.commons.model.chats.ConcordServerRoomNote +import com.vitorpamplona.amethyst.commons.model.chats.RelayGroupServerRoomNote import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.commons.model.cordnGroups.CordnGroupChatroom import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel +import com.vitorpamplona.amethyst.commons.model.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.routeFor @@ -66,6 +70,7 @@ import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChann import com.vitorpamplona.amethyst.commons.model.privateChatLastReadRoute import com.vitorpamplona.amethyst.commons.model.privateChats.ChatPreview import com.vitorpamplona.amethyst.commons.model.privateChats.chatPreviewOf +import com.vitorpamplona.amethyst.commons.model.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.commons.nip29RelayGroups.ui.RelayNameChip import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.add_to_messages @@ -139,19 +144,14 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types.buzzTimelinePreviewSummary import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types.observeUserNameByHex import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.loadMarmotRelayIcon -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.rememberMarmotGroupAvatarUrl import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.RoomNameDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.reportWarningContentDescription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.ConcordLeaveDialog -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordChannelLastReadRoute import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordCommunityHasUnreadFlow import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.rememberConcordImageModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.LoadEphemeralChatChannel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.relayGroupServerHasUnreadFlow -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.ConcordServerRoomNote -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.RelayGroupServerRoomNote import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.cordn.coordinatorDisplayName import com.vitorpamplona.quartz.buzz.notifications.MemberAddedNotificationEvent import com.vitorpamplona.quartz.cordn.appEncryptedMedia.CordnMediaTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/MarkChatRoomsAsRead.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/MarkChatRoomsAsRead.kt index 488860dee0..48914bf464 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/MarkChatRoomsAsRead.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/MarkChatRoomsAsRead.kt @@ -21,17 +21,17 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.chats.ConcordServerRoomNote +import com.vitorpamplona.amethyst.commons.model.chats.RelayGroupServerRoomNote import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel +import com.vitorpamplona.amethyst.commons.model.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.model.privateChatLastReadRoute +import com.vitorpamplona.amethyst.commons.model.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.marmotGroupLastReadRoute -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordChannelLastReadRoute -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.relayGroupChannelLastReadRoute -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.ConcordServerRoomNote -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.RelayGroupServerRoomNote import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/RowUnread.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/RowUnread.kt index 64a741db99..7b2a052e2e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/RowUnread.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/RowUnread.kt @@ -21,20 +21,20 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.chats.ConcordServerRoomNote +import com.vitorpamplona.amethyst.commons.model.chats.RelayGroupServerRoomNote import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel +import com.vitorpamplona.amethyst.commons.model.concordChannelLastReadRoute import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel +import com.vitorpamplona.amethyst.commons.model.marmotGroupLastReadRoute import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.model.publicChatChannelIdOf +import com.vitorpamplona.amethyst.commons.model.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.commons.model.unreadPrivateChatRoute import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.marmotGroupLastReadRoute -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordChannelLastReadRoute import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.concordCommunityHasUnreadFlow -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.relayGroupChannelLastReadRoute import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.relayGroupServerHasUnreadFlow -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.ConcordServerRoomNote -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.dal.RelayGroupServerRoomNote import com.vitorpamplona.quartz.experimental.bitchat.geohash.GeohashChatEvent import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt index 1ba516916f..f3e9c55adf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt @@ -24,6 +24,8 @@ import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType +import com.vitorpamplona.amethyst.commons.model.chats.ConcordServerRoomNote +import com.vitorpamplona.amethyst.commons.model.chats.RelayGroupServerRoomNote import com.vitorpamplona.amethyst.commons.model.concord.ConcordChannel import com.vitorpamplona.amethyst.commons.model.concord.ConcordViewMode import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt index 02af582f7a..976c38b64f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt @@ -21,6 +21,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterNip04DMsFromMe +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterNip04DMsToMe import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager import com.vitorpamplona.amethyst.commons.relayClient.paging.BackwardRelayPager import com.vitorpamplona.amethyst.commons.relayClient.paging.PagingStatus diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04SubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04SubAssembler.kt index f4349abb05..6eb9f65478 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04SubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04SubAssembler.kt @@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType import com.vitorpamplona.amethyst.commons.model.privateChats.DmHistoryTuning +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterNip04DMsFromMe +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterNip04DMsToMe import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager import com.vitorpamplona.amethyst.commons.relayClient.paging.WindowLoadTracker import com.vitorpamplona.amethyst.commons.relayClient.paging.trackingListener diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingEphemeralChatSubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingEphemeralChatSubAssembler.kt index b4d5033d8b..c13664a974 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingEphemeralChatSubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingEphemeralChatSubAssembler.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterFollowingEphemeralChats import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.launchChatFeedToggleObserver diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingPublicChatSubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingPublicChatSubAssembler.kt index 0cc193ea4e..d4ad165145 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingPublicChatSubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FollowingPublicChatSubAssembler.kt @@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterFollowingPublicChatsCreationEvent +import com.vitorpamplona.amethyst.commons.relayClient.chatrooms.filterLastMessageFollowingPublicChats import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.launchChatFeedToggleObserver diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt index 502d424d8c..f17261abee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt @@ -41,6 +41,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.feeds.FeedState import com.vitorpamplona.amethyst.commons.model.Note +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.relayClient.paging.PagingStatus import com.vitorpamplona.amethyst.commons.resources.Res @@ -64,7 +65,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.formatHistoryReachDate import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt index 5e37b9a13f..21776a66c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt @@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.commons.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.TwoPaneNav import com.vitorpamplona.amethyst.commons.ui.theme.Size20dp import com.vitorpamplona.amethyst.ui.components.getActivity import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMScreen.kt index 9c0e0a9566..6be8af6f4d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMScreen.kt @@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.resources.share_to_dm_start_new import com.vitorpamplona.amethyst.commons.resources.share_to_dm_title import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.navigation.navs.ShareToDMNav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadState.kt index ac0c7a21c3..2c6327533a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadState.kt @@ -27,8 +27,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.richtext.RichTextParser +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import kotlinx.collections.immutable.ImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt index 1afe8bcf7e..8380fc7400 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt @@ -21,10 +21,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityFeedFilter.kt index cca44e07e7..9970d5352d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityFeedFilter.kt @@ -24,6 +24,8 @@ import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.nip72Communities.latestCommunityRules +import com.vitorpamplona.amethyst.commons.model.nip72Communities.violatesCommunityRules import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityModerationFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityModerationFeedFilter.kt index ea5403ca0b..044eb8f25a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityModerationFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityModerationFeedFilter.kt @@ -24,6 +24,8 @@ import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.nip72Communities.latestCommunityRules +import com.vitorpamplona.amethyst.commons.model.nip72Communities.violatesCommunityRules import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/dal/CommunitiesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/dal/CommunitiesFeedFilter.kt index 7469843e02..d60215b38a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/dal/CommunitiesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/dal/CommunitiesFeedFilter.kt @@ -20,12 +20,12 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.dal +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.mapNotNullIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip72Communities.DiscoverCommunityFeedFilter import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityModel.kt index 576c4cd584..981d691248 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityModel.kt @@ -37,11 +37,11 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityScreen.kt index 0f9b946033..eac78f2eb4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/newCommunity/NewCommunityScreen.kt @@ -93,6 +93,7 @@ import com.vitorpamplona.amethyst.commons.resources.new_community_rules import com.vitorpamplona.amethyst.commons.resources.new_community_rules_section import com.vitorpamplona.amethyst.commons.resources.remove import com.vitorpamplona.amethyst.commons.resources.save +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.components.Nip05OrPubkeyLine import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -101,7 +102,6 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.CreatingTopBar import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelect import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery import com.vitorpamplona.amethyst.ui.note.UserPicture diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/DiscoverLongFormFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/DiscoverLongFormFeedFilter.kt index 33fdf104ce..49df00f59d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/DiscoverLongFormFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/DiscoverLongFormFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip23LongForm import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt index 2ece98c9c1..bc4b838b8e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt @@ -101,6 +101,7 @@ import com.vitorpamplona.amethyst.commons.resources.url_slug import com.vitorpamplona.amethyst.commons.resources.url_slug_placeholder import com.vitorpamplona.amethyst.commons.resources.your_article_title import com.vitorpamplona.amethyst.commons.resources.zapraiser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.ContentSensitivityExplainer @@ -114,13 +115,12 @@ import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.AddZapraise import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.ZapRaiserRequest import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt index e076080373..4b10bc1c3c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostViewModel.kt @@ -37,8 +37,12 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState.EmojiMedia @@ -49,6 +53,8 @@ import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_ import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.resources.server_did_not_provide_a_url_after_uploading import com.vitorpamplona.amethyst.commons.service.pow.PoWReplay +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField @@ -62,19 +68,13 @@ import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.CompressorQuality import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.blossom.BlossomUploader import com.vitorpamplona.amethyst.service.uploads.nip96.Nip96Uploader -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt index 3bb50c255f..3c2f9df44d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/DiscoverChatFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip28Chats import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/RenderPublicChatChannelThumb.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/RenderPublicChatChannelThumb.kt index fda82b5723..481db912e5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/RenderPublicChatChannelThumb.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip28Chats/RenderPublicChatChannelThumb.kt @@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.grayText +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner import com.vitorpamplona.amethyst.ui.note.Gallery @@ -68,7 +69,6 @@ import com.vitorpamplona.amethyst.ui.note.LikeReaction import com.vitorpamplona.amethyst.ui.note.LoadPublicChatChannel import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip51FollowSets/DiscoverFollowSetsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip51FollowSets/DiscoverFollowSetsFeedFilter.kt index 1e0fdcad4d..a1a3eb2311 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip51FollowSets/DiscoverFollowSetsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip51FollowSets/DiscoverFollowSetsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/DiscoverLiveFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/DiscoverLiveFeedFilter.kt index 8b7e5cb5e5..6189aa50e5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/DiscoverLiveFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/DiscoverLiveFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.ParticipantListBuilder import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -34,7 +35,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.Mute import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.commons.service.OnlineChecker import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt index 246fadcb6d..d66acadf03 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip53LiveActivities/LiveActivityCard.kt @@ -52,22 +52,22 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.Aut import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community.SingleCommunityTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.EndedFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.LiveFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.OfflineFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.ScheduledFlag import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner import com.vitorpamplona.amethyst.ui.note.Gallery import com.vitorpamplona.amethyst.ui.note.LoadLiveActivityChannel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.EndedFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.OfflineFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.header.LiveActivitiesChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent import com.vitorpamplona.quartz.nip53LiveActivities.LiveStreamLike diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/CommunityCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/CommunityCard.kt index 43dfdebcbe..f99364c6a9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/CommunityCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/CommunityCard.kt @@ -63,13 +63,13 @@ import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.grayText +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner import com.vitorpamplona.amethyst.ui.note.Gallery import com.vitorpamplona.amethyst.ui.note.LikeReaction import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import kotlinx.collections.immutable.ImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt index d817affb56..baa0d09d50 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip72Communities/DiscoverCommunityFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip72Communities import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.mapNotNullIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip90DVMs/DiscoverNIP89FeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip90DVMs/DiscoverNIP89FeedFilter.kt index 12d37d180c..e337baf17c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip90DVMs/DiscoverNIP89FeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip90DVMs/DiscoverNIP89FeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.ParticipantListBuilder import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -35,7 +36,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community. import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/ClassifiedsThumb.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/ClassifiedsThumb.kt index 396cb8b7d3..8b71b86826 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/ClassifiedsThumb.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/ClassifiedsThumb.kt @@ -47,9 +47,9 @@ import coil3.compose.AsyncImage import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/DiscoverMarketplaceFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/DiscoverMarketplaceFeedFilter.kt index 79d9ebed30..c84934f8c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/DiscoverMarketplaceFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/DiscoverMarketplaceFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent open class DiscoverMarketplaceFeedFilter( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt index d92f87629f..8814d25bbb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt @@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_create_and_add_inv import com.vitorpamplona.amethyst.commons.resources.lightning_invoice import com.vitorpamplona.amethyst.commons.resources.new_product import com.vitorpamplona.amethyst.commons.resources.zapraiser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -71,7 +72,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt index eaa34fa87d..9b588113cd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt @@ -36,13 +36,20 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField @@ -54,18 +61,11 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber -import com.vitorpamplona.amethyst.ui.note.creators.previews.PreviewState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/SellProduct.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/SellProduct.kt index 9284dfbe4f..065426107c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/SellProduct.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/SellProduct.kt @@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.commons.resources.classifieds_title_placeholde import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP import com.vitorpamplona.amethyst.commons.ui.theme.Height100Modifier @@ -90,7 +91,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.SquaredQuoteBorderModifier import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/DvmContentDiscoveryResponseFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/DvmContentDiscoveryResponseFilter.kt index b9c8653a49..5ba64e8d5f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/DvmContentDiscoveryResponseFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/DvmContentDiscoveryResponseFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.observables.CreatedAtComparator import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/dal/BrowseEmojiSetsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/dal/BrowseEmojiSetsFeedFilter.kt index 327d5e81d6..ded1c94b30 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/dal/BrowseEmojiSetsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/dal/BrowseEmojiSetsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedConversationsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedConversationsFeedFilter.kt index 2a56243e86..c56e60de18 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedConversationsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedConversationsFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -29,7 +30,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allUserFollows.AllUs import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip01Core.core.Event diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedNewThreadFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedNewThreadFeedFilter.kt index 26a3f6bd6f..5de1c944b7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedNewThreadFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/dal/FollowPackFeedNewThreadFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note @@ -29,7 +30,6 @@ import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allUserFollows.AllUserFollowsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allUserFollows.AllUserFollowsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.agora.FundraiserEvent import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/dal/FollowPacksFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/dal/FollowPacksFeedFilter.kt index b4f0a54144..d42d44d8ac 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/dal/FollowPacksFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/dal/FollowPacksFeedFilter.kt @@ -20,9 +20,9 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.dal +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets.DiscoverFollowSetsFeedFilter class FollowPacksFeedFilter( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheHuntsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheHuntsFeedFilter.kt index a836bbd6b1..c052a0fa78 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheHuntsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheHuntsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.geocaches.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nipCCGeocaching.curation.GeocacheCurationListEvent /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheMineFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheMineFeedFilter.kt index e38fd625c9..6bb4e8a551 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheMineFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheMineFeedFilter.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet +import com.vitorpamplona.amethyst.commons.relayClient.geocaches.GeocacheListingKinds import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.quartz.nipCCGeocaching.listing.GeocacheListingEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesFeedFilter.kt index 7604b35a7a..b1e60aaaad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesFeedFilter.kt @@ -21,12 +21,13 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.geocaches.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter +import com.vitorpamplona.amethyst.commons.relayClient.geocaches.GeocacheListingKinds import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nipCCGeocaching.listing.GeocacheListingEvent /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesHereFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesHereFeedFilter.kt index 6ce924b5fd..0713842aca 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesHereFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocachesHereFeedFilter.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet +import com.vitorpamplona.amethyst.commons.relayClient.geocaches.GeocacheListingKinds import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.quartz.nipCCGeocaching.listing.GeocacheListingEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt index e9a34e4ad9..298bea2bfb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFeedFilterSubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFeedFilterSubAssembler.kt index aa7297b134..9a7425b921 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFeedFilterSubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFeedFilterSubAssembler.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager +import com.vitorpamplona.amethyst.commons.relayClient.hashtag.filterPostsByHashtags import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightPostViewModel.kt index 573cbbe5da..14f8152590 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/NewHighlightPostViewModel.kt @@ -30,13 +30,13 @@ import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState.EmojiMedia import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.text.currentWord import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Address diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/dal/HighlightsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/dal/HighlightsFeedFilter.kt index 6fa403c631..d0eeb79514 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/dal/HighlightsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/highlights/dal/HighlightsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.highlights.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent 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 142a83683e..031fc4226f 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 @@ -110,6 +110,7 @@ import com.vitorpamplona.amethyst.commons.resources.schedule_post_always_on_prom import com.vitorpamplona.amethyst.commons.resources.schedule_post_always_on_prompt_title import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind import com.vitorpamplona.amethyst.commons.resources.zapraiser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.aihelp.AiWritingHelpPanel @@ -137,7 +138,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPa import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog 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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt index 625d16fd37..e4733e4694 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt @@ -34,13 +34,19 @@ import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.BooleanType import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState.EmojiMedia import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState @@ -59,6 +65,8 @@ import com.vitorpamplona.amethyst.commons.service.ai.WritingAssistantStatus import com.vitorpamplona.amethyst.commons.service.ai.WritingResult import com.vitorpamplona.amethyst.commons.service.ai.WritingTone import com.vitorpamplona.amethyst.commons.service.pow.PoWReplay +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.notify.IAudience @@ -76,22 +84,14 @@ import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.CompressorQuality import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadingState -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.RecordingResult import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationController -import com.vitorpamplona.amethyst.ui.actions.uploads.VoicePreset -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber -import com.vitorpamplona.amethyst.ui.note.creators.previews.PreviewState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyViewModel.kt index c1b223f5fb..2ae2f26bf4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyViewModel.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.amethyst.commons.audio.VoicePreset import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName @@ -44,7 +45,6 @@ import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadingState import com.vitorpamplona.amethyst.ui.actions.uploads.RecordingResult import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationController -import com.vitorpamplona.amethyst.ui.actions.uploads.VoicePreset import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.tags.people.toPTag import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeConversationsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeConversationsFeedFilter.kt index 146ceae389..3479a85771 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeConversationsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeConversationsFeedFilter.kt @@ -21,13 +21,13 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.HomeFeedType import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip01Core.core.Event diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt index 886dd9671a..2448f89a4e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeLiveFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveComplexFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Channel import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -34,7 +35,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community. import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.experimental.bitchat.geohash.GeohashChatEvent import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt index 1cf99188dd..288ea25da9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.feeds.isRenderableRepost import com.vitorpamplona.amethyst.commons.model.HomeFeedType import com.vitorpamplona.amethyst.commons.model.Note @@ -29,7 +30,6 @@ import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.agora.FundraiserEvent import com.vitorpamplona.quartz.experimental.attestations.attestation.AttestationEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/dal/LiveStreamsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/dal/LiveStreamsFeedFilter.kt index 5fe9419106..4c70fbfb1a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/dal/LiveStreamsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/dal/LiveStreamsFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.ParticipantListBuilder import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -34,7 +35,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.Mute import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.commons.service.OnlineChecker import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/dal/LongsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/dal/LongsFeedFilter.kt index 138d6b8e34..a8b24ae923 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/dal/LongsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/dal/LongsFeedFilter.kt @@ -21,6 +21,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams +import com.vitorpamplona.amethyst.commons.feeds.SupportedContent import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -29,9 +31,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder -import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.dal.SupportedContent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip71Video.AddressableNormalVideoEvent import com.vitorpamplona.quartz.nip71Video.VideoMeta diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistScreen.kt index 8ef8fa212d..c185009a62 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistScreen.kt @@ -92,6 +92,7 @@ import com.vitorpamplona.amethyst.commons.resources.music_playlist_track_count import com.vitorpamplona.amethyst.commons.resources.music_playlist_unknown_track import com.vitorpamplona.amethyst.commons.resources.music_playlist_uploading_banner import com.vitorpamplona.amethyst.commons.resources.new_music_playlist +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -100,7 +101,6 @@ import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistViewModel.kt index 5c7c115a8d..9b1d06e1fa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicPlaylistViewModel.kt @@ -25,12 +25,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.CompressorQuality import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadingState import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackScreen.kt index 354b50506b..53f6016435 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackScreen.kt @@ -83,13 +83,13 @@ import com.vitorpamplona.amethyst.commons.resources.music_track_title_label import com.vitorpamplona.amethyst.commons.resources.music_track_title_placeholder import com.vitorpamplona.amethyst.commons.resources.music_track_uploading_banner import com.vitorpamplona.amethyst.commons.resources.new_music_track +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SendingTopBar import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackViewModel.kt index a0878ce79a..936a63a29a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/NewMusicTrackViewModel.kt @@ -28,12 +28,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.CompressorQuality import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicPlaylistsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicPlaylistsFeedFilter.kt index 42746dd5c3..818d11805d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicPlaylistsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicPlaylistsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.music.playlist.MusicPlaylistEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicTracksFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicTracksFeedFilter.kt index 35bed93b10..f0de10a463 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicTracksFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/music/dal/MusicTracksFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.music.track.MusicTrackEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/napplets/ConnectedAppDetailScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/napplets/ConnectedAppDetailScreen.kt index 5386f09d5a..bf71ccce1c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/napplets/ConnectedAppDetailScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/napplets/ConnectedAppDetailScreen.kt @@ -82,6 +82,7 @@ import com.vitorpamplona.amethyst.commons.napplet.permissions.GrantState import com.vitorpamplona.amethyst.commons.napplet.permissions.NappletPermissionLedger import com.vitorpamplona.amethyst.commons.napplet.ui.PolicyCard import com.vitorpamplona.amethyst.commons.napplet.ui.symbol +import com.vitorpamplona.amethyst.commons.nip46RemoteSigner.ui.Nip46ActivityCard import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cancel import com.vitorpamplona.amethyst.commons.resources.napplet_connected_app_capabilities @@ -122,7 +123,6 @@ import com.vitorpamplona.amethyst.napplet.descriptionRes import com.vitorpamplona.amethyst.napplet.labelRes import com.vitorpamplona.amethyst.napplet.resolveNappletMeta import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46ActivityCard import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46AppIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46LiveStatus import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.nip46.Nip46ReconnectPill diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/NestsFeedLoaded.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/NestsFeedLoaded.kt index cddb8b18e9..f3bd69a5bd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/NestsFeedLoaded.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/NestsFeedLoaded.kt @@ -64,6 +64,10 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.routeFor +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.EndedFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.LiveFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.PrivateFlag +import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.ScheduledFlag import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.live_stream_ended_tag import com.vitorpamplona.amethyst.commons.resources.nests_ended_ago @@ -99,10 +103,6 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.EndedFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.PrivateFlag -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.LoadParticipants import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.dal.NestsFeedFilter import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.dal.NestsFeedFilter.NestBucket diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/dal/NestsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/dal/NestsFeedFilter.kt index 1b963d40fe..85a35951f6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/dal/NestsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/dal/NestsFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.ParticipantListBuilder import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -34,7 +35,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community. import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestEditFieldRow.kt index 4297f1e73a..72a702b616 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestEditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestEditFieldRow.kt @@ -39,15 +39,15 @@ import com.vitorpamplona.amethyst.commons.chats.ui.ThinSendButton import com.vitorpamplona.amethyst.commons.nip30CustomEmojis.ui.ShowEmojiSuggestionList import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.reply_here +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldBorder import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.commons.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestNewMessageViewModel.kt index b72f99f4b1..1dded94988 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/chat/NestNewMessageViewModel.kt @@ -36,6 +36,9 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState @@ -44,6 +47,7 @@ import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_de import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user import com.vitorpamplona.amethyst.commons.richtext.UrlParser +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.text.currentWord import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor @@ -52,12 +56,8 @@ import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/SpeakerZapOverlay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/SpeakerZapOverlay.kt index 8e62c90521..0a2a12a512 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/SpeakerZapOverlay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/SpeakerZapOverlay.kt @@ -46,9 +46,9 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.commons.viewmodels.REACTION_WINDOW_SEC import com.vitorpamplona.amethyst.commons.viewmodels.RoomZap -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import kotlinx.coroutines.delay /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt index a219448a67..09e15458df 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt @@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.commons.service.BundledInsert import com.vitorpamplona.amethyst.commons.service.BundledUpdate import com.vitorpamplona.amethyst.commons.ui.notifications.Card import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.logTime import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.checkNotInMainThread diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedView.kt index bec6b7c1fa..9a64c999a6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedView.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.model.chats.formatHistoryReachDate import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.notification_feed_is_empty import com.vitorpamplona.amethyst.commons.resources.refresh @@ -82,7 +83,6 @@ import com.vitorpamplona.amethyst.ui.note.NutzapUserSetCompose import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose import com.vitorpamplona.amethyst.ui.note.types.ReplyRenderType import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.formatHistoryReachDate import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.donations.ShowDonationCard import kotlinx.coroutines.delay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/EqualImmutableLists.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/EqualImmutableLists.kt deleted file mode 100644 index e582bbe707..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/EqualImmutableLists.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications - -import kotlinx.collections.immutable.ImmutableList - -// Re-export from commons for backwards compatibility -fun equalImmutableLists( - list1: ImmutableList, - list2: ImmutableList, -): Boolean = - com.vitorpamplona.amethyst.commons.util - .equalImmutableLists(list1, list2) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt index e1cc54cc4a..3eb9bfd2ae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt @@ -29,9 +29,9 @@ import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.BundledInsert +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.checkNotInMainThread -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.note.showCount import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt index 70a2444ea8..71359a8851 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt @@ -24,8 +24,8 @@ import androidx.compose.runtime.Stable import com.patrykandpatrick.vico.compose.cartesian.CartesianMeasuringContext import com.patrykandpatrick.vico.compose.cartesian.axis.Axis import com.patrykandpatrick.vico.compose.cartesian.data.CartesianValueFormatter -import com.vitorpamplona.amethyst.ui.note.showAmountIntegerWithZero -import com.vitorpamplona.amethyst.ui.note.showAmountWithZero +import com.vitorpamplona.amethyst.commons.util.showAmountIntegerWithZero +import com.vitorpamplona.amethyst.commons.util.showAmountWithZero import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.ShowDecimals @Stable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt index 433d8d8183..0a79bfd44a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -31,7 +32,6 @@ import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.buzz.jobs.JobErrorEvent import com.vitorpamplona.quartz.buzz.jobs.JobResultEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt index febe2b7ffe..f48d6ef95e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt @@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.commons.resources.messages_new_message_to import com.vitorpamplona.amethyst.commons.resources.messages_new_message_to_caption import com.vitorpamplona.amethyst.commons.resources.public_message import com.vitorpamplona.amethyst.commons.resources.zapraiser +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -74,6 +75,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.AddZapraise import com.vitorpamplona.amethyst.commons.ui.note.creators.zapraiser.ZapRaiserRequest import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.text.MentionPreservingInputTransformation import com.vitorpamplona.amethyst.commons.ui.text.onUiThread import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP @@ -82,8 +84,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt index 653df37778..32ae7d4e20 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt @@ -36,8 +36,13 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache +import com.vitorpamplona.amethyst.commons.model.composer.DraftTagState +import com.vitorpamplona.amethyst.commons.model.composer.IExpiration import com.vitorpamplona.amethyst.commons.model.composer.IZapRaiser +import com.vitorpamplona.amethyst.commons.model.composer.NewMessageTagger +import com.vitorpamplona.amethyst.commons.model.composer.PreviewState import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder +import com.vitorpamplona.amethyst.commons.model.composer.toZapSplitSetup import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiSuggestionState @@ -45,6 +50,8 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_sign_events import com.vitorpamplona.amethyst.commons.resources.read_only_user +import com.vitorpamplona.amethyst.commons.service.upload.MediaUploadTracker +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField @@ -56,18 +63,11 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger -import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing -import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState -import com.vitorpamplona.amethyst.ui.note.creators.expiration.IExpiration import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber -import com.vitorpamplona.amethyst.ui.note.creators.previews.PreviewState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/dal/PictureFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/dal/PictureFeedFilter.kt index 49aea905f5..39280f161a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/dal/PictureFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/dal/PictureFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip68Picture.PictureEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastTopSupporters.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastTopSupporters.kt index 5b8fdcda06..88abf9fe0a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastTopSupporters.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastTopSupporters.kt @@ -54,11 +54,11 @@ import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.grayText +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowScreen.kt index dd7d2eaa9b..ee7b75f2ff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowScreen.kt @@ -73,10 +73,10 @@ import com.vitorpamplona.amethyst.commons.resources.podcast_show_type_episodic import com.vitorpamplona.amethyst.commons.resources.podcast_show_type_label import com.vitorpamplona.amethyst.commons.resources.podcast_show_type_serial import com.vitorpamplona.amethyst.commons.resources.podcast_show_website_label +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SendingTopBar -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.music.CoverImagePicker diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowViewModel.kt index d6cfec4f68..ad55093d4a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/EditPodcastShowViewModel.kt @@ -26,10 +26,10 @@ import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.podcasts.V4VSplitEditorState +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Address diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeScreen.kt index 07d1d263f8..3022b3717c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeScreen.kt @@ -89,11 +89,11 @@ import com.vitorpamplona.amethyst.commons.resources.podcast_episode_transcript_l import com.vitorpamplona.amethyst.commons.resources.podcast_episode_video_label import com.vitorpamplona.amethyst.commons.resources.podcast_new_episode import com.vitorpamplona.amethyst.commons.resources.podcast_publishing_banner +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SendingTopBar import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeViewModel.kt index 9f095c0afc..35090b62f9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastEpisodeViewModel.kt @@ -27,10 +27,10 @@ import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.podcasts.V4VSplitEditorState +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Address diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerScreen.kt index f33b87c771..8492d0c385 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerScreen.kt @@ -70,11 +70,11 @@ import com.vitorpamplona.amethyst.commons.resources.podcast_trailer_title_placeh import com.vitorpamplona.amethyst.commons.resources.podcast_trailer_upload_cta import com.vitorpamplona.amethyst.commons.resources.podcast_trailer_upload_hint import com.vitorpamplona.amethyst.commons.resources.podcast_trailer_url_label +import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SendingTopBar import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.music.UploadInProgressBanner diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerViewModel.kt index af67cec9e6..304b497cb3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/authoring/NewPodcastTrailerViewModel.kt @@ -24,10 +24,10 @@ import android.content.Context import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nipXXPodcasting20.trailer.Podcasting20TrailerEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastEpisodesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastEpisodesFeedFilter.kt index b98f416758..4c816f5ece 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastEpisodesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastEpisodesFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nipXXPodcasting20.episode.Podcasting20EpisodeEvent import com.vitorpamplona.quartz.podcasts.PodcastEpisode diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastsFeedFilter.kt index 3026c56aa7..2092b73e6f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/dal/PodcastsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent import com.vitorpamplona.quartz.nipF4Podcasts.metadata.PodcastMetadataEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/ClosedPollsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/ClosedPollsFeedFilter.kt index f076b195d0..f1a3215b9f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/ClosedPollsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/ClosedPollsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/OpenPollsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/OpenPollsFeedFilter.kt index 9869ae301f..c4ff649bfe 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/OpenPollsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/OpenPollsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/PollsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/PollsFeedFilter.kt index 43294a5591..97af35782c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/PollsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/dal/PollsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt index 884d0d827a..1ccf04c10c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt @@ -72,6 +72,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.navigation.routeFor +import com.vitorpamplona.amethyst.commons.relayClient.polls.results.RelayPollResponseLoader import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.poll_multiple_choice import com.vitorpamplona.amethyst.commons.resources.poll_results_all_options diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/dal/ProductsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/dal/ProductsFeedFilter.kt index bb061fb74b..70c485d6c3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/dal/ProductsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/dal/ProductsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.products.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent class ProductsFeedFilter( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt index 0bc9808c07..861d97c490 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt @@ -81,6 +81,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.commons.ui.theme.Size8dp +import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenUser import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -104,7 +105,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.hashtags.TabFollowe import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.ProfileTopBar import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.identity.UserExternalIdentitiesViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.TabMutualConversations import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.TabNotesNewThreads diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/apps/dal/UserProfileAppsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/apps/dal/UserProfileAppsFeedFilter.kt index 0c285398e1..3264316f1b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/apps/dal/UserProfileAppsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/apps/dal/UserProfileAppsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.apps.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip5aStaticWebsites.NamedSiteEvent import com.vitorpamplona.quartz.nip5aStaticWebsites.RootSiteEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/dal/UserProfileGalleryFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/dal/UserProfileGalleryFeedFilter.kt index aef3178ea3..05cf74f316 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/dal/UserProfileGalleryFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/dal/UserProfileGalleryFeedFilter.kt @@ -21,13 +21,13 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt index 731c811b4d..ee6838f0a2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt @@ -74,6 +74,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SpacedBy5dp import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.util.toShortDisplay +import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.components.util.LongPressCopyText @@ -84,7 +85,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.DisplayAppRecommendations import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges.DisplayBadges -import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.identity.UserExternalIdentitiesViewModel import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/ProfileHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/ProfileHeader.kt index 19e9a71e11..01cc8fc5b6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/ProfileHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/ProfileHeader.kt @@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size100dp import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.userProfileBorderModifier +import com.vitorpamplona.amethyst.commons.viewmodels.UserExternalIdentitiesViewModel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataViewModel import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelectSingle @@ -72,7 +73,6 @@ import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.identity.UserExternalIdentitiesViewModel import kotlinx.coroutines.launch @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentContent.kt index 1bd3ac0ebb..8b98de44c5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentContent.kt @@ -98,7 +98,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText -import com.vitorpamplona.amethyst.ui.note.showAmount +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentScreen.kt index 32bfc5a77a..0d2c6bdd90 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/payment/SendPaymentScreen.kt @@ -96,6 +96,7 @@ import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.DEFAULT_ONCHAIN_ZAP_SATS import com.vitorpamplona.amethyst.model.MIN_ONCHAIN_ZAP_SATS import com.vitorpamplona.amethyst.service.ClinkOfferPayer @@ -105,7 +106,6 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.routeToMessage import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.payViaIntent -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.nwc.nwcFailureDetail import com.vitorpamplona.amethyst.ui.nwc.nwcTimeoutMessage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedView.kt index 2b3399da6f..88214cac18 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedView.kt @@ -44,6 +44,8 @@ import com.vitorpamplona.amethyst.commons.resources.public_notif_section_explain import com.vitorpamplona.amethyst.commons.ui.components.util.setText import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness +import com.vitorpamplona.amethyst.commons.viewmodels.MyRelayInfo +import com.vitorpamplona.amethyst.commons.viewmodels.RelayFeedViewModel import com.vitorpamplona.amethyst.ui.note.RelayCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/TabRelays.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/TabRelays.kt index 4fea0bc053..94a32f6e90 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/TabRelays.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/TabRelays.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.viewmodels.RelayFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapNoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapNoteCompose.kt index f3d45d8565..fa36cb56dd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapNoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapNoteCompose.kt @@ -41,11 +41,11 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.UserActionOptions import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay -import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.ZapAmount diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapTabHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapTabHeader.kt index 3e16e0f612..186ea85888 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapTabHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ZapTabHeader.kt @@ -27,7 +27,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.zaps import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.note.showAmountInteger +import com.vitorpamplona.amethyst.commons.util.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps.dal.UserProfileZapsViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt index d30cb73aa2..ac88e54778 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.publicChats.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/BarcodeDecoder.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/BarcodeDecoder.kt index 67f6a03ee5..5a3b037399 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/BarcodeDecoder.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/BarcodeDecoder.kt @@ -23,6 +23,9 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.scanner import android.graphics.Bitmap import android.graphics.Rect import androidx.camera.core.ImageProxy +import com.vitorpamplona.amethyst.commons.qrcode.ScanBounds +import com.vitorpamplona.amethyst.commons.qrcode.ScanPoint +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult import com.vitorpamplona.quartz.utils.Log import zxingcpp.BarcodeReader diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrFrameAnalyzer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrFrameAnalyzer.kt index d526f5e914..f883f31aaf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrFrameAnalyzer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrFrameAnalyzer.kt @@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.scanner import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy +import com.vitorpamplona.amethyst.commons.qrcode.ScanFrame +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult import com.vitorpamplona.quartz.utils.Log import kotlin.math.max diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrImageImport.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrImageImport.kt index 83141ac20c..d2a1051a7e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrImageImport.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrImageImport.kt @@ -26,6 +26,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import androidx.core.graphics.scale +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt index f6efc9f418..e05e6246cd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt @@ -90,6 +90,7 @@ import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult import com.vitorpamplona.amethyst.commons.qrcode.classifyScannedPayload import com.vitorpamplona.amethyst.commons.qrcode.ui.QrImageCodeChooser import com.vitorpamplona.amethyst.commons.qrcode.ui.ScanOutcomeSheet diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt index 5de6999b6b..18b8bccd6e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt @@ -53,6 +53,9 @@ import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.qrcode.ScanBounds +import com.vitorpamplona.amethyst.commons.qrcode.ScanFrame +import com.vitorpamplona.amethyst.commons.qrcode.ScanPoint import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.close import com.vitorpamplona.amethyst.commons.resources.qr_scanner_dark_hint diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt index 4a3de54979..01c204e033 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt @@ -25,7 +25,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import com.vitorpamplona.amethyst.commons.qrcode.ScanFrame +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult import com.vitorpamplona.amethyst.commons.qrcode.ScannedPayload +import com.vitorpamplona.amethyst.commons.qrcode.StructuredAppendAccumulator /** * Everything the scanner UI draws, and the decision of what a frame means. diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt index d2e0a0496f..67146fe09a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt @@ -71,6 +71,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.Aut import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilter import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurposeGroup +import com.vitorpamplona.amethyst.commons.relays.ui.SubPurposeLabels import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.active_subs_filters import com.vitorpamplona.amethyst.commons.resources.active_subs_groups @@ -102,7 +103,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.loadMarmotRelayIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.marmotGroup.rememberMarmotGroupAvatarUrl import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.rememberConcordImageModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.SubPurposeLabels import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt index 6f4c0a13ad..bb68210b82 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt @@ -66,6 +66,10 @@ import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SettingsSearchTopBar +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsEntry +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsIcon +import com.vitorpamplona.amethyst.commons.ui.settings.filterSettings import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/HiddenWordsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/HiddenWordsScreen.kt index 81edb6d1d7..3a2207444f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/HiddenWordsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/HiddenWordsScreen.kt @@ -60,6 +60,7 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.commons.feeds.StringFeedState import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.hidden_words import com.vitorpamplona.amethyst.commons.resources.hide_new_word_label diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt index 8597f78173..2e091a4cf1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt @@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.commons.resources.payment_targets_section_expl import com.vitorpamplona.amethyst.commons.resources.wallet_connect import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SavingTopBar +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.SettingsCategorySpacingModifier import com.vitorpamplona.amethyst.commons.ui.theme.grayText diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NotificationSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NotificationSettingsScreen.kt index 02f18f92e9..3c4e4be86f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NotificationSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NotificationSettingsScreen.kt @@ -88,6 +88,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackBu import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn +import com.vitorpamplona.amethyst.commons.util.toShortDisplay import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.service.notifications.BatteryOptimizationHelper import com.vitorpamplona.amethyst.service.notifications.NotificationChannels @@ -95,7 +96,6 @@ import com.vitorpamplona.amethyst.ui.components.PushNotificationProviderTile import com.vitorpamplona.amethyst.ui.components.hasPushNotificationProvider import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay -import com.vitorpamplona.amethyst.ui.note.toShortDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityListsCommon.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityListsCommon.kt index 790a12547b..1e5368f749 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityListsCommon.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityListsCommon.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.feeds.UserFeedState import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.navigation.routeFor @@ -75,7 +76,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay -import com.vitorpamplona.amethyst.ui.screen.UserFeedState import com.vitorpamplona.amethyst.ui.screen.UserFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import org.jetbrains.compose.resources.StringResource diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogBuilder.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogBuilder.kt index a180a43368..ff9348aea5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogBuilder.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogBuilder.kt @@ -106,6 +106,9 @@ import com.vitorpamplona.amethyst.commons.resources.video_player_settings import com.vitorpamplona.amethyst.commons.resources.zaps import com.vitorpamplona.amethyst.commons.resources.zaps_search_keywords import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsEntry +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsIcon import org.jetbrains.compose.resources.StringResource /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedView.kt index 99db311d0f..96b549e38d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedView.kt @@ -35,6 +35,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.vitorpamplona.amethyst.commons.feeds.StringFeedState import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.feed_is_empty import com.vitorpamplona.amethyst.commons.resources.refresh diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt index a4e4693d5e..b4a28274ee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt @@ -27,10 +27,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.commons.feeds.FeedFilter import com.vitorpamplona.amethyst.commons.feeds.InvalidatableContent +import com.vitorpamplona.amethyst.commons.feeds.StringFeedState import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.service.BundledUpdate +import com.vitorpamplona.amethyst.commons.util.equalImmutableLists import com.vitorpamplona.amethyst.service.checkNotInMainThread -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.quartz.utils.Log import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt index 0876d4cf5d..762afc71a6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnBackupScreen.kt @@ -52,6 +52,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.cordn.ui.BusyLabel +import com.vitorpamplona.amethyst.commons.cordn.ui.SettingsFormBlock import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cancel import com.vitorpamplona.amethyst.commons.resources.cordn_backup_contents_body diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt index cdae46aa2a..2dc743c32a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt @@ -62,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.cordn.CoordinatorConfig import com.vitorpamplona.amethyst.commons.cordn.CoordinatorHealth import com.vitorpamplona.amethyst.commons.cordn.CordnCoordinatorDiscovery import com.vitorpamplona.amethyst.commons.cordn.DiscoveredCoordinator +import com.vitorpamplona.amethyst.commons.cordn.ui.SettingsFormBlock import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.cache.LocalCache diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt index b3ebf82863..7d01ae4159 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnKeyPackagesScreen.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.cordn.CoordinatorConfig +import com.vitorpamplona.amethyst.commons.cordn.ui.SettingsFormBlock import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cordn_coordinators_none import com.vitorpamplona.amethyst.commons.resources.cordn_group_unavailable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt index f4ac911967..2f793891ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnLinkScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.cordn.CordnLinkInspection import com.vitorpamplona.amethyst.commons.cordn.ui.CordnExposureCard +import com.vitorpamplona.amethyst.commons.cordn.ui.SettingsFormBlock import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cordn_link_clear import com.vitorpamplona.amethyst.commons.resources.cordn_link_coordinator diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt index 6c55c2bbb0..7f5ca299c9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt @@ -48,6 +48,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.cordn.CordnMigration +import com.vitorpamplona.amethyst.commons.cordn.ui.BusyLabel +import com.vitorpamplona.amethyst.commons.cordn.ui.SettingsFormBlock import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.cordn_group_unavailable import com.vitorpamplona.amethyst.commons.resources.cordn_group_unavailable_detail diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46SignerScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46SignerScreen.kt index 1ce01265b3..df18f48a12 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46SignerScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46SignerScreen.kt @@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.nip46Signer.Nip46ActivityEntry +import com.vitorpamplona.amethyst.commons.nip46RemoteSigner.ui.Nip46ActivityCard import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.nip46_signer_activity_title import com.vitorpamplona.amethyst.commons.resources.nip46_signer_background_hint diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/dal/ShortsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/dal/ShortsFeedFilter.kt index dfbaf15534..02aacf30d7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/dal/ShortsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/dal/ShortsFeedFilter.kt @@ -21,6 +21,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams +import com.vitorpamplona.amethyst.commons.feeds.SupportedContent import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -29,9 +31,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder -import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.dal.SupportedContent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip71Video.AddressableShortVideoEvent import com.vitorpamplona.quartz.nip71Video.VideoMeta diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/softwareapps/dal/SoftwareAppsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/softwareapps/dal/SoftwareAppsFeedFilter.kt index 0f3bb8cdf1..a2cdbb0f7c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/softwareapps/dal/SoftwareAppsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/softwareapps/dal/SoftwareAppsFeedFilter.kt @@ -21,12 +21,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.softwareapps.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.SoftwareApplicationEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt index 510b334ad9..5bbc3dfe06 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt @@ -125,6 +125,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.lessImportantLink import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.selectedNote import com.vitorpamplona.amethyst.commons.ui.thread.drawReplyLevel +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.commons.viewmodels.thread.LevelFeedViewModel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeCommunityApprovalNeedStatus import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid @@ -168,7 +169,6 @@ import com.vitorpamplona.amethyst.ui.note.elements.Reward import com.vitorpamplona.amethyst.ui.note.elements.ShowForkInformation import com.vitorpamplona.amethyst.ui.note.nip22Comments.DisplayCommentScope import com.vitorpamplona.amethyst.ui.note.observeEdits -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.types.AudioHeader import com.vitorpamplona.amethyst.ui.note.types.AudioTrackHeader import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlScreen.kt index 47784e4175..e8946326b6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/url/UrlScreen.kt @@ -30,6 +30,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.external_content_title +import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.commons.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.FabBottomBarPadded @@ -37,7 +38,6 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.MaxWidthWithHorzPadding -import com.vitorpamplona.amethyst.ui.components.UrlPreviewCard import com.vitorpamplona.amethyst.ui.components.rememberUrlPreviewState import com.vitorpamplona.amethyst.ui.feeds.FeedLoaded import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/VideoFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/VideoFeedFilter.kt index 7fad0d489e..42dd92faf4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/VideoFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/VideoFeedFilter.kt @@ -21,6 +21,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.video.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams +import com.vitorpamplona.amethyst.commons.feeds.SupportedContent import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache @@ -29,7 +31,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestrator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestrator.kt index 7b3419010c..7a42bf22e3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestrator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestrator.kt @@ -30,6 +30,7 @@ import com.davotoula.lightcompressor.hls.HlsUploaded import com.davotoula.lightcompressor.hls.Rendition import com.davotoula.lightcompressor.hls.SimpleHlsListener import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName +import com.vitorpamplona.amethyst.commons.service.upload.HlsPublishState import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult import com.vitorpamplona.amethyst.service.uploads.hls.HlsBlobUploader import com.vitorpamplona.amethyst.service.uploads.hls.HlsKind1SiblingBuilder diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestratorFactory.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestratorFactory.kt index d84708ff46..610ce5be46 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestratorFactory.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishOrchestratorFactory.kt @@ -26,6 +26,7 @@ import android.media.MediaMetadataRetriever import android.net.Uri import com.davotoula.lightcompressor.hls.HlsContentTypes import com.davotoula.lightcompressor.hls.HlsUploadHelper +import com.vitorpamplona.amethyst.commons.service.upload.HlsPublishState import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult import com.vitorpamplona.amethyst.service.uploads.PreviewMetadataCalculator diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt index fe04b878af..2c4cb16da7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt @@ -110,6 +110,7 @@ import com.vitorpamplona.amethyst.commons.resources.hls_title_label import com.vitorpamplona.amethyst.commons.resources.hls_title_placeholder import com.vitorpamplona.amethyst.commons.resources.hls_try_again import com.vitorpamplona.amethyst.commons.resources.share_hls_video +import com.vitorpamplona.amethyst.commons.service.upload.HlsPublishState import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoViewModel.kt index 1bf8029983..4a324037c2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoViewModel.kt @@ -33,6 +33,7 @@ import com.davotoula.lightcompressor.hls.HlsLadder import com.davotoula.lightcompressor.utils.CompressorUtils import com.vitorpamplona.amethyst.commons.model.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName +import com.vitorpamplona.amethyst.commons.service.upload.HlsPublishState import com.vitorpamplona.amethyst.model.Account import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainZapSendDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainZapSendDialog.kt index 131305e7bb..a2ea4d577a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainZapSendDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainZapSendDialog.kt @@ -120,13 +120,13 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.model.DEFAULT_ONCHAIN_ZAP_SATS import com.vitorpamplona.amethyst.model.MIN_ONCHAIN_ZAP_SATS import com.vitorpamplona.amethyst.ui.components.namecoin.NamecoinResolutionRow import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ReloadMintScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ReloadMintScreen.kt index cef6a7c1bd..d6a85fe7e3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ReloadMintScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ReloadMintScreen.kt @@ -98,8 +98,8 @@ import com.vitorpamplona.amethyst.commons.ui.components.util.setText import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText +import com.vitorpamplona.amethyst.commons.util.showAmount import com.vitorpamplona.amethyst.ui.note.UserPicture -import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import kotlinx.coroutines.launch import java.util.UUID diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt index e6d0cc127a..452a991874 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt @@ -73,6 +73,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.commons.clink.ui.ClinkBudgetDialog import com.vitorpamplona.amethyst.commons.hashtags.Cashu import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons import com.vitorpamplona.amethyst.commons.icons.symbols.Icon diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt index b01f4380c0..1934a77dcc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt @@ -59,6 +59,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.model.nip47WalletConnect.TransactionRowLabels import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.back import com.vitorpamplona.amethyst.commons.resources.wallet_filter_all diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardViewModel.kt index 9051c9a277..f8c14aae0e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardViewModel.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.wizard import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.vitorpamplona.amethyst.commons.cashu.CashuWalletDiscovery import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/dal/WorkoutFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/dal/WorkoutFeedFilter.kt index 48397e0c3f..127553191b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/dal/WorkoutFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/dal/WorkoutFeedFilter.kt @@ -21,11 +21,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.dal import com.vitorpamplona.amethyst.commons.feeds.AdditiveFeedFilter +import com.vitorpamplona.amethyst.commons.feeds.FilterByListParams import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter import com.vitorpamplona.amethyst.model.Account -import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.quartz.experimental.fitness.workout.WorkoutRecordEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreen.kt index fcc9a24726..b35647c0e2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginScreen.kt @@ -67,6 +67,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.account.ui.login.LoginButton +import com.vitorpamplona.amethyst.commons.account.ui.login.LoginErrorManager import com.vitorpamplona.amethyst.commons.account.ui.login.SignUpButton import com.vitorpamplona.amethyst.commons.hashtags.Amethyst import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel.kt index 38e6c7ea99..b1062bd9c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModel.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.BuildConfig +import com.vitorpamplona.amethyst.commons.account.ui.login.LoginErrorManager import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.invalid_key import com.vitorpamplona.amethyst.commons.resources.invalid_key_with_message diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpScreen.kt index 4425f0a89b..eb80fa22ce 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpScreen.kt @@ -49,6 +49,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.amethyst.commons.account.ui.login.LoginErrorManager import com.vitorpamplona.amethyst.commons.account.ui.signup.LoginButton import com.vitorpamplona.amethyst.commons.account.ui.signup.SignUpButton import com.vitorpamplona.amethyst.commons.hashtags.Amethyst @@ -70,7 +71,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager import com.vitorpamplona.amethyst.ui.screen.loggedOff.TorSettingsSetup import com.vitorpamplona.amethyst.ui.screen.loggedOff.legal.TermsGate -import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager import kotlinx.coroutines.launch @Preview(device = "spec:width=2160px,height=2340px,dpi=440") diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpViewModel.kt index 42307cb17b..5e2b8b3c09 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/signup/SignUpViewModel.kt @@ -27,11 +27,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.BuildConfig +import com.vitorpamplona.amethyst.commons.account.ui.login.LoginErrorManager import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.name_is_required import com.vitorpamplona.amethyst.commons.tor.TorSettingsFlow import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager -import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager @Stable class SignUpViewModel : ViewModel() { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt index 415d54252d..01ef24d512 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt @@ -77,6 +77,7 @@ import com.vitorpamplona.amethyst.commons.resources.tor_use_videos import com.vitorpamplona.amethyst.commons.resources.tor_use_videos_explainer import com.vitorpamplona.amethyst.commons.resources.use_internal_tor import com.vitorpamplona.amethyst.commons.resources.use_internal_tor_explainer +import com.vitorpamplona.amethyst.commons.tor.TorDialogViewModel import com.vitorpamplona.amethyst.commons.tor.TorPresetType import com.vitorpamplona.amethyst.commons.tor.TorSettings import com.vitorpamplona.amethyst.commons.tor.TorType diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt index 03ffe47da7..68ff8804ca 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/LegalSettingsSection.kt @@ -28,6 +28,9 @@ import com.vitorpamplona.amethyst.commons.resources.child_safety_search_keywords import com.vitorpamplona.amethyst.commons.resources.child_safety_standards import com.vitorpamplona.amethyst.commons.resources.privacy_policy import com.vitorpamplona.amethyst.commons.resources.privacy_policy_search_keywords +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsCategory +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsEntry +import com.vitorpamplona.amethyst.commons.ui.settings.SettingsIcon /** Play build surfaces the GitHub-hosted legal policy links. */ fun legalSettingsCategory(uriHandler: UriHandler): SettingsCategory? = diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarsViewModelTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarsViewModelTest.kt index 1358a9f67d..f70a77734c 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarsViewModelTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarsViewModelTest.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.calendar import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStore import androidx.lifecycle.viewModelScope -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.CalendarsViewMode +import com.vitorpamplona.amethyst.commons.nip52Calendar.ui.CalendarsViewMode import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.CalendarsViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.startOfWeek import kotlinx.coroutines.Dispatchers diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/uploads/hls/HlsPublishOrchestratorTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/uploads/hls/HlsPublishOrchestratorTest.kt index 778232794a..2d02dd8886 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/uploads/hls/HlsPublishOrchestratorTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/uploads/hls/HlsPublishOrchestratorTest.kt @@ -29,11 +29,11 @@ import com.davotoula.lightcompressor.hls.HlsUploaded import com.davotoula.lightcompressor.hls.Rendition import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerName import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType +import com.vitorpamplona.amethyst.commons.service.upload.HlsPublishState import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.hls.HlsPosterUpload import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.hls.HlsPublishOrchestrator import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.hls.HlsPublishRequest -import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.hls.HlsPublishState import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import kotlinx.coroutines.flow.MutableStateFlow diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerStateTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerStateTest.kt index df79de8085..c2af5a01dc 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerStateTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerStateTest.kt @@ -20,6 +20,9 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.scanner +import com.vitorpamplona.amethyst.commons.qrcode.ScanFrame +import com.vitorpamplona.amethyst.commons.qrcode.ScanResult +import com.vitorpamplona.amethyst.commons.qrcode.StructuredAppendAccumulator import com.vitorpamplona.amethyst.commons.qrcode.classifyScannedPayload import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModelKeyKindTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModelKeyKindTest.kt index 814a5b1ba2..5f83740441 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModelKeyKindTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginViewModelKeyKindTest.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedOff.login import androidx.compose.ui.text.input.TextFieldValue +import com.vitorpamplona.amethyst.commons.account.ui.login.LoginErrorManager import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.login_bunker_not_supported import com.vitorpamplona.amethyst.commons.resources.login_nostrconnect_not_supported diff --git a/commons/ARCHITECTURE.md b/commons/ARCHITECTURE.md index 8f44e28665..7fd1105b31 100644 --- a/commons/ARCHITECTURE.md +++ b/commons/ARCHITECTURE.md @@ -132,7 +132,7 @@ type (`LazyListState`, `TextFieldValue`, `TextFieldState`) goes to `commonsUI`. ### UI (Compose — lives in **`commonsUI`**) | Package | UI? | Purpose | |----------------|-----|---------| -| `ui` | yes | **Cross-cutting** shared composables only, organized by area: `ui/components`, `ui/theme`, `ui/signing`, `ui/thread`, `ui/note`, `ui/richtext`, `ui/search`, `ui/notifications`, `ui/screens`, `ui/layouts`, `ui/feeds` (feed shell: empty/error/loading states, refresh box, remembered scroll states), `ui/navigation` (`INav`, `EmptyNav`, top bars, drawer swipe), `ui/markdown`, `ui/privacylock`, plus Compose helpers in `ui/state` (cached-state) and `ui/text` (TextField extensions). Feature-specific UI lives in `/ui`, **not** here. Nothing under `ui.*` lives in *this* module any more: the feed DAL that used to sit in `ui/feeds` is now `feeds/`, and the reply-context logic that sat in `ui/note` (`replyingDirectlyTo`, `ReplyContext`) is now in `model/` next to `ThreadAssembler`. | +| `ui` | yes | **Cross-cutting** shared composables only, organized by area: `ui/components`, `ui/theme`, `ui/signing`, `ui/thread`, `ui/note`, `ui/richtext`, `ui/search`, `ui/notifications`, `ui/screens`, `ui/layouts`, `ui/feeds` (feed shell: empty/error/loading states, refresh box, remembered scroll states), `ui/navigation` (`INav`, `EmptyNav`, `ObservableNav`/`TwoPaneNav`/`ShareToDMNav`, top bars, drawer swipe), `ui/settings` (the searchable settings catalog), `ui/markdown`, `ui/privacylock`, plus Compose helpers in `ui/state` (cached-state) and `ui/text` (TextField extensions). Feature-specific UI lives in `/ui`, **not** here. Nothing under `ui.*` lives in *this* module any more: the feed DAL that used to sit in `ui/feeds` is now `feeds/`, and the reply-context logic that sat in `ui/note` (`replyingDirectlyTo`, `ReplyContext`) is now in `model/` next to `ThreadAssembler`. | | `nip23LongContent` | yes | Long-form (NIP-23) article UI: `nip23LongContent/ui/article` (reader) + `…/ui/editor` (authoring). The model lives in `model/nip23LongContent` (here). | | `icons` | yes | `ImageVector` icon definitions + builders, Material Symbols codepoints, the icon-font glyph tables. | | `hashtags` | yes | Custom hashtag `ImageVector`s. | @@ -142,7 +142,7 @@ type (`LazyListState`, `TextFieldValue`, `TextFieldState`) goes to `commonsUI`. | `napplet` | mixed | Protocol/permission logic here; `NappletWebContract` (serves the shell/shim from `composeResources`) in `commonsUI`. | | `favorites`, `nip30CustomEmojis`, `nip34Git`, `nip85TrustedAssertions`, `nip53LiveActivities` | mixed | Logic here; each feature's `ui/` (or the flat `FavoriteAppIcon`, `EmojiSuggestionState`) in `commonsUI`. | | `chats` | yes | Composables shared by every chat kind (DMs, public chats, relay groups, concord): unread badge, divisors, system messages, author line, send button, reply toggle, new-conversation screen. Chat *models* are in `model/chats`. | -| `nip17Dm`, `nip23LongContent`, `nip28PublicChat`, `nip29RelayGroups`, `nip32Labeling`, `nip43RelayMembers`, `nip51Lists`, `nip52Calendar`, `nip56Reports`, `nip72ModCommunities`, `nip73ExternalIds`, `nip99Classifieds`, `nipC0CodeSnippets`, `nipCCGeocaching`, `birdstar`, `nipsOnNostr`, `buzz`, `cashu`, `concord`, `music`, `mediaServers`, `browser`, `profile`, `napplet` | yes / mixed | Single-feature UI under `/ui` (named after the `quartz` package, or its concern name when `quartz` has none). Where the package also has logic (`cashu`, `browser`, `napplet`, `profile`) that part stays here; the `buzz` and `concord` models are in `model/buzz` and `model/concord`. | +| `nip17Dm`, `nip23LongContent`, `nip28PublicChat`, `nip29RelayGroups`, `nip32Labeling`, `nip43RelayMembers`, `nip51Lists`, `nip52Calendar`, `nip56Reports`, `nip72ModCommunities`, `nip73ExternalIds`, `nip99Classifieds`, `nipC0CodeSnippets`, `nipCCGeocaching`, `birdstar`, `nipsOnNostr`, `buzz`, `cashu`, `clink`, `concord`, `music`, `mediaServers`, `nip46RemoteSigner`, `browser`, `profile`, `napplet` | yes / mixed | Single-feature UI under `/ui` (named after the `quartz` package, or its concern name when `quartz` has none). Where the package also has logic (`cashu`, `browser`, `napplet`, `profile`) that part stays here; the `buzz` and `concord` models are in `model/buzz` and `model/concord`. | ### Mixed (documented debt — see §4) | Package | UI? | Purpose | diff --git a/commons/plans/2026-08-30-commons-migration-sweep.md b/commons/plans/2026-08-30-commons-migration-sweep.md index 5e0e2d40ef..c67ba3f177 100644 --- a/commons/plans/2026-08-30-commons-migration-sweep.md +++ b/commons/plans/2026-08-30-commons-migration-sweep.md @@ -1553,3 +1553,136 @@ A transitive-blocker sweep of the 1,385 files under `amethyst/…/ui/`, after th same gap. - Adding the module costs about +28 MB to the unpacked runtime. That is a packaging call, left for the Desktop phase. + +## 2026-09-27 (later) — the unblocked `ui/` files, and one chess view model + +### How the list was rebuilt + +The morning's "80 files have no blocker" count was not saved, so it was recomputed +with a script (not committed; it is a one-off) that: + +- indexes every public top-level declaration in `amethyst/src/{main,play,fdroid}`; +- follows each file's imports **and** its same-package references (the + under-count this plan's header warns about); +- marks a file blocked by `android.*`, a non-KMP `androidx.*`, `R.`/`BuildConfig`, + or a library the shared modules lack; +- takes the fixpoint, then assigns each survivor a module and source set from what it + (transitively) uses: Compose UI, `Res` or a `commonsUI` symbol means `commonsUI`; + `java.*`/JVM tokens or a jvmAndroid-only `commons` symbol means `jvmAndroid`. + +It found **96** such files under `ui/` (142 app-wide). 68 of the `ui/` ones moved +(plus `service/uploads/SuspendableConfirmation`, which one of them needs), 3 were app +copies of existing `commons` code and were deleted, and 25 stay, for the reasons below. + +### Moved + +**To `commons`** + +| Package | Files | +|---|---| +| `model` | `ConcordLastRead`, `MarmotGroupLastRead`, `RelayGroupLastRead` (beside `privateChatLastReadRoute`) | +| `model/chats` | `ConcordServerRoomNote`, `RelayGroupServerRoomNote`, `HistoryDateFormat` (jvmAndroid) | +| `model/composer` | `NewMessageTagger`, `DraftTagState`, `IExpiration`, `SplitConversor`, `PreviewState` (jvmAndroid) | +| `model/navigation` | `RouteTextArgs`, `ShareToDMRouteRewriter` | +| `model/buzz` | `AgentWork` | +| `model/nip29RelayGroups` | `GroupDiscoveryConstraintResolver` (was `dal/GroupDiscoveryConstraint.kt`; renamed because the matcher it resolves to already owns that file name here) | +| `model/nip47WalletConnect` | `TransactionRowLabels` | +| `model/nip72Communities` | `CommunityRulesLookup` | +| `model/nip92IMeta` | `ImetaContent` | +| `relayClient/chatrooms` (new) | the five `Filter*` functions of the chatroom-list assembler | +| `relayClient/channel/relayGroup` | `RelayGroupOpenThreadsFilterAssembler`, `RelayGroupsDiscoveryFilter` | +| `relayClient/hashtag` (new), `relayClient/geocaches`, `relayClient/polls/results` | `FilterPostsByHashtags`, `GeocacheListingKinds`, `RelayPollResponseLoader` | +| `feeds` | `FilterByListParams`, `UserFeedState`, `StringFeedState`, `SupportedContent` | +| `viewmodels` | `RelayFeedViewModel`, `UserExternalIdentitiesViewModel` | +| `service/upload` | `MediaUploadTracker`, `HlsPublishState`, `SuspendableConfirmation` | +| `audio`, `music`, `qrcode`, `cashu`, `mediaServers`, `tor` | `PitchShifter`; `MusicFormatting`; `ScanResult` + `StructuredAppendAccumulator`; `CashuWalletDiscovery` (jvmAndroid); `MediaServerHealth` (jvmAndroid); `TorDialogViewModel` | +| `util` | `DebouncedPublisher`, `TimeFormatUtils` + `ZapFormatterNoDecimals` (jvmAndroid) | + +**To `commonsUI`** + +| Package | Files | +|---|---| +| `ui/components` | `InformationDialog`, `UrlPreviewCard` (both jvmAndroid) | +| `ui/text` | `MentionPreservingInputTransformation` (its `MENTION_REGEX` is now public; the app's `UrlUserTagOutputTransformation` shares it) | +| `ui/navigation/navs` | `ObservableNav`, `TwoPaneNav`, `ShareToDMNav` | +| `ui/navigation/bottombars`, `ui/insets`, `ui/theme`, `ui/settings` (new) | `TabReselectCoordinator`; `KeyboardState`; `ColorSchemePreview` (jvmAndroid: it formats with `String.format`); `SettingsCatalog` | +| feature `ui/` packages | `account/ui/login/LoginErrorManager`, `clink/ui/ClinkBudgetDialog`, `cordn/ui/{BusyLabel,SettingsFormBlock}`, `nip46RemoteSigner/ui/Nip46ActivityUi`, `nip52Calendar/ui/CalendarsViewMode`, `nip53LiveActivities/ui/StreamingStatusFlags`, `nip72ModCommunities/ui/CommunityRulesViolationBanner`, `relays/ui/SubPurposeLabels`, `service/upload/ui/StrippingFailureDialog` | +| `audio` | `VoicePreset`, `RecordingIndicators` (jvmAndroid) | + +Most files moved unchanged apart from the package line. The exceptions, all for +`commonMain`: + +- `formatTrackDuration` went from `internal` to public so the app's music renderers can + still call it. Its `"%d:%02d".format(...)` is now a `padStart`, because `String.format` + is JVM-only. +- `RouteTextArgs`: `codePointAt` / `Character.charCount` → the commons + `codePointAtKmp` / `codePointCharCount`, which mirror the JDK ones. +- `TorDialogViewModel`: `Integer.parseInt` → `toInt()`. Both throw + `NumberFormatException` on the JVM. +- `UserExternalIdentitiesViewModel.Factory`: `create(Class)` → the multiplatform + `create(KClass, CreationExtras)`, as `PollResultsViewModel` already does. +- `Dispatchers.IO` needs `import kotlinx.coroutines.IO` in common code, and + `RelayFeedViewModel`'s `javaClass.simpleName` log is now `this::class.simpleName`. + +A tooling note for the next sweep: stripping comments with a regex before scanning +identifiers breaks on a `/*` inside a string (`"image/*"`), which swallows code up to the +next `*/`. Tokenize strings and comments together. And scan bodies, not only imports, +for inline `android.`/`java.` names and `javaClass`. + +Eleven pure unit tests moved with their code to `commons` jvmTest (NewMessageTagger key +parsing, PitchShifter, RouteTextArgs, ShareToDMRouteRewriter, DebouncedPublisher, +RelayGroupDiscoveryConstraint, StructuredAppendAccumulator, SupportedContent, +TransactionRowLabels, CommunityRulesLookup) and `commonsUI` jvmTest +(SettingsCatalogFilter). + +**Deleted app copies** (callers repointed at the `commons` original): +`ui/note/PubKeyFormatter.kt` (the commons `toShortDisplay` is the same with an optional +`prefixSize`), `ui/note/ZapFormatter.kt` (same functions; the commons `TenGiga`…`OneKilo` +constants became public for `ZapFormatterNoDecimals`), and the +`EqualImmutableLists.kt` re-export shim. + +### `ChessViewModelNew` and `DesktopChessViewModelNew` → one `ChessViewModel` + +The two classes forwarded the same ~40 members to `ChessLobbyLogic`; only the +adapters and the scope differed. `commons/nip64Chess/ChessViewModel` now takes the +adapters, the polling config, the dismissed-games store and an optional scope: + +- **Android:** `ChessViewModelFactory` builds it with the Android adapters and no + scope, so it runs on `viewModelScope` and stops polling in `onCleared`, as before. + The factory lost its unused `Application` parameter. The `viewModel(key = …)` key + changed from `ChessViewModelNew-` to `ChessViewModel-`, in all four + call sites together, so the lobby, game screen, note card and home button still share + one instance. +- **Desktop:** `ChessScreen` builds it with the Desktop adapters and its + `rememberCoroutineScope()`, which is what `DesktopChessViewModelNew` did. The + `UserMetadataCache` that class owned is now remembered next to it in `ChessScreen`. + The debug `instanceId` (`System.identityHashCode`) went, since it isn't KMP. + +### Left in the app on purpose (unblocked, but not shared code) + +- `ui/screen/AndroidFeedViewModel`, `threadview/dal/AndroidLevelFeedViewModel`: they + exist to bind Android's process-wide `LocalCache`. Desktop still runs + `DesktopLocalCache` (Wave 2 part B). +- `ui/navigation/ShareIntentRouting`: names the manifest's ``es. +- `ui/screen/loggedIn/embed/*` (9 files) and `browser/EmbeddedPageRequests`: bridges + for the Android WebView host. No other front end embeds a WebView. +- `workouts/health/HealthConnectRationaleScreen`: Health Connect is Android-only. +- `ui/tor/{TorManager,TorBackend,ArtiNative,ArtiGuardState,TorConnectionFailureDialog}`: + the in-process Arti client. Desktop drives an external Tor. +- `ui/tor/TorServiceStatus`: `commons/tor` has its own copy that differs on purpose + (Android has `Bootstrapping`, Desktop has `Error`). Merging them changes every `when` + over it on both platforms; that's a design step, not a move. +- `ui/actions/UrlUserTagOutputTransformation`: calls `android.util.Patterns.WEB_URL` + by its fully-qualified name, which an import-based scan cannot see. It needs a KMP URL + matcher (commons `richtext` has one) before it can move. +- `nests/room/{lifecycle/NestRoomEventCollectors, participants/RoomParticipantActions, + stage/SpeakerReactionOverlay, stage/SpeakerZapOverlay}`: `internal` pieces of the + Android audio-room screen, which Desktop doesn't have. + +### Unblocked outside `ui/` (not moved in this round) + +The same sweep finds 46 more movable files outside `ui/`. The notable groups are +`service/resourceusage` (9, the resource-usage ledger), `service/uploads/blossom` +(`InFlightInvoices`, `PaymentPromptLedger`, the `bud10` resolver trio), +`service/playback` leaves (`HlsLivenessCache`, `SessionRegistry`, +`WssDataStreamCollector`, the player control buttons), and `model/MediaAspectRatioCache`. diff --git a/commons/plans/README.md b/commons/plans/README.md index 01bc81b0cf..faf2fdd620 100644 --- a/commons/plans/README.md +++ b/commons/plans/README.md @@ -1,18 +1,19 @@ # commons plans -_Audited 2026-06-30 (+ 2026-09-12 split entry). 7 plans: 3 shipped, 2 in-progress, 2 queued, 0 abandoned._ +_Audited 2026-06-30 (+ 2026-09-12 split entry, 2026-09-27 migration entries)._ ## In progress | Plan | Summary | | ---- | ------- | | [2026-05-04-custom-feeds-plan.md](2026-05-04-custom-feeds-plan.md) | Custom feed creation/discovery/management for Desktop; core model + builder + kind 31890 + desktop UI shipped, but relay-filter layer, DVM marketplace, kind 10090 sync, and list resolution still pending. | | [2026-05-06-nest-subscription-manager-extraction.md](2026-05-06-nest-subscription-manager-extraction.md) | Split the per-speaker subscription state machine out of `NestViewModel`; only the `ActiveSubscription` stepping-stone is extracted so far. | +| [2026-08-30-commons-migration-sweep.md](2026-08-30-commons-migration-sweep.md) | The running tracker for moving `amethyst/` code into `commons`/`commonsUI`: waves, what moved each round, what stays and why. `LocalCache` has moved; retiring `DesktopLocalCache` (Wave 2 part B) and the `AccountViewModel` hub composables are next. | +| [2026-05-30-amethyst-to-commons-migration.md](2026-05-30-amethyst-to-commons-migration.md) | The original roadmap for the same move; superseded in practice by the sweep tracker above. | ## Queued | Plan | Summary | | ---- | ------- | | [2026-04-21-event-renderer.md](2026-04-21-event-renderer.md) | Cross-platform UI-agnostic `RenderedEvent` subsystem shared by Amy, Desktop, and Android; not started. | -| [2026-05-30-amethyst-to-commons-migration.md](2026-05-30-amethyst-to-commons-migration.md) | Roadmap to move shared `amethyst` Android code into `commons`; keystone `Account`/`LocalCache` extraction not begun. | | [2026-08-03-poll-results-page.md](2026-08-03-poll-results-page.md) | Extended NIP-88 poll results page (per-option counts + who voted for what) for Android and Desktop; also specifies four tally-correctness fixes and the missing poll-relay subscription. Proposed, not started. | ## Shipped diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifter.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifter.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifter.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifter.kt index cb620f8294..d022792b57 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifter.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifter.kt @@ -18,9 +18,8 @@ * 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 +package com.vitorpamplona.amethyst.commons.audio -import com.vitorpamplona.amethyst.commons.audio.AudioWindow import kotlin.math.roundToInt /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/FilterByListParams.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/FilterByListParams.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/FilterByListParams.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/FilterByListParams.kt index 9715816619..71ea95206f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/FilterByListParams.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/FilterByListParams.kt @@ -18,7 +18,7 @@ * 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.dal +package com.vitorpamplona.amethyst.commons.feeds import com.vitorpamplona.amethyst.commons.model.LiveHiddenUsers import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/StringFeedState.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedState.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/StringFeedState.kt index f63f6830af..011df2b7d0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/StringFeedState.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings +package com.vitorpamplona.amethyst.commons.feeds import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.MutableStateFlow diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContent.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContent.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContent.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContent.kt index fee279e1f1..fc9fe90180 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContent.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContent.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.video.dal +package com.vitorpamplona.amethyst.commons.feeds import com.vitorpamplona.amethyst.commons.richtext.RichTextParser diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/UserFeedState.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/UserFeedState.kt index 1659f47cef..4cc72b76eb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/feeds/UserFeedState.kt @@ -18,7 +18,7 @@ * 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.screen +package com.vitorpamplona.amethyst.commons.feeds import androidx.compose.runtime.Stable import com.vitorpamplona.amethyst.commons.model.User diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordLastRead.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordLastRead.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordLastRead.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordLastRead.kt index 65b819fd52..3fffed0ce4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordLastRead.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ConcordLastRead.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.publicChannels.concord +package com.vitorpamplona.amethyst.commons.model /** * The per-account last-read route key for one Concord channel — the string diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupLastRead.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/MarmotGroupLastRead.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupLastRead.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/MarmotGroupLastRead.kt index 252fb79e8a..b5fc79c21f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupLastRead.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/MarmotGroupLastRead.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.marmotGroup +package com.vitorpamplona.amethyst.commons.model import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupLastRead.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/RelayGroupLastRead.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupLastRead.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/RelayGroupLastRead.kt index f5f563f050..639bd5017d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupLastRead.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/RelayGroupLastRead.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.publicChannels.relayGroup +package com.vitorpamplona.amethyst.commons.model import com.vitorpamplona.quartz.nip29RelayGroups.GroupId diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWork.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt similarity index 94% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWork.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt index fa7b3412a7..98d2de1c56 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/AgentWork.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt @@ -18,13 +18,9 @@ * 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.screen.loggedIn.buzz +package com.vitorpamplona.amethyst.commons.model.buzz import androidx.compose.runtime.Immutable -import com.vitorpamplona.amethyst.commons.model.buzz.JobState -import com.vitorpamplona.amethyst.commons.model.buzz.JobView -import com.vitorpamplona.amethyst.commons.model.buzz.WorkflowRun -import com.vitorpamplona.amethyst.commons.model.buzz.WorkflowRunState import com.vitorpamplona.quartz.nip01Core.core.HexKey /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ConcordServerRoomNote.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/ConcordServerRoomNote.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ConcordServerRoomNote.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/ConcordServerRoomNote.kt index e591edb75a..7e3d987f7f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ConcordServerRoomNote.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/ConcordServerRoomNote.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.dal +package com.vitorpamplona.amethyst.commons.model.chats import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/RelayGroupServerRoomNote.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/RelayGroupServerRoomNote.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/RelayGroupServerRoomNote.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/RelayGroupServerRoomNote.kt index 4aab0fb043..bb5b4dc8d2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/RelayGroupServerRoomNote.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/chats/RelayGroupServerRoomNote.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.dal +package com.vitorpamplona.amethyst.commons.model.chats import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/draftTags/DraftTagState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/DraftTagState.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/draftTags/DraftTagState.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/DraftTagState.kt index a014a1627e..404291aa9f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/draftTags/DraftTagState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/DraftTagState.kt @@ -18,7 +18,7 @@ * 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.note.creators.draftTags +package com.vitorpamplona.amethyst.commons.model.composer import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/IExpiration.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/IExpiration.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/IExpiration.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/IExpiration.kt index 6d3a77fdbf..10c359151c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/expiration/IExpiration.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/IExpiration.kt @@ -18,7 +18,7 @@ * 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.note.creators.expiration +package com.vitorpamplona.amethyst.commons.model.composer import androidx.compose.runtime.Stable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTagger.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTagger.kt index 3d2e20ad2e..368c2f6d1f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTagger.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.model.composer import androidx.compose.runtime.Immutable import com.vitorpamplona.amethyst.commons.model.Dao diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/SplitConversor.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/SplitConversor.kt similarity index 92% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/SplitConversor.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/SplitConversor.kt index c492243bba..e51b41c4de 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/SplitConversor.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/composer/SplitConversor.kt @@ -18,10 +18,9 @@ * 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.note.creators.zapsplits +package com.vitorpamplona.amethyst.commons.model.composer import com.vitorpamplona.amethyst.commons.model.User -import com.vitorpamplona.amethyst.commons.model.composer.SplitBuilder import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup import kotlin.math.round diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteTextArgs.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgs.kt similarity index 93% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteTextArgs.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgs.kt index 0c54d0e01b..ced966ada8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/RouteTextArgs.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgs.kt @@ -18,7 +18,10 @@ * 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.navigation.routes +package com.vitorpamplona.amethyst.commons.model.navigation + +import com.vitorpamplona.amethyst.commons.util.codePointAtKmp +import com.vitorpamplona.amethyst.commons.util.codePointCharCount /** * Navigation routes travel as a URI string: `…Route.Room/{id}?message={message}&…`, with every @@ -77,14 +80,14 @@ fun String.limitToRouteTextArg(maxEncodedLength: Int = MAX_ROUTE_TEXT_ARG_ENCODE var cutAt = 0 while (index < length) { // Steps over whole code points so a surrogate pair is never cut in half. - val codePoint = codePointAt(index) + val codePoint = codePointAtKmp(index) spent += encodedCodePointLength(codePoint) if (spent > maxEncodedLength) { // Only here is truncation certain. [cutAt] is the last position whose prefix still // leaves room for the marker; a budget too small to hold even that yields nothing. return if (budget >= 0) substring(0, cutAt) + ROUTE_TEXT_ARG_TRUNCATION_MARKER else "" } - index += Character.charCount(codePoint) + index += codePointCharCount(codePoint) if (spent <= budget) cutAt = index } return this @@ -95,9 +98,9 @@ fun encodedRouteArgLength(text: String): Int { var total = 0 var index = 0 while (index < text.length) { - val codePoint = text.codePointAt(index) + val codePoint = text.codePointAtKmp(index) total += encodedCodePointLength(codePoint) - index += Character.charCount(codePoint) + index += codePointCharCount(codePoint) } return total } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMRouteRewriter.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriter.kt similarity index 92% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMRouteRewriter.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriter.kt index a17a3b5805..d2d49c61a2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMRouteRewriter.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriter.kt @@ -18,9 +18,7 @@ * 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.screen.loggedIn.chats.share - -import com.vitorpamplona.amethyst.commons.model.navigation.Route +package com.vitorpamplona.amethyst.commons.model.navigation /** * Injects shared content into a chatroom navigation so that tapping a recent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/GroupDiscoveryConstraint.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/GroupDiscoveryConstraintResolver.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/GroupDiscoveryConstraint.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/GroupDiscoveryConstraintResolver.kt index 69f7969579..6ba7b1c6b2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/GroupDiscoveryConstraint.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/GroupDiscoveryConstraintResolver.kt @@ -18,9 +18,8 @@ * 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.screen.loggedIn.chats.publicChannels.relayGroup.dal +package com.vitorpamplona.amethyst.commons.model.nip29RelayGroups -import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.GroupDiscoveryConstraint import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/TransactionRowLabels.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabels.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/TransactionRowLabels.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabels.kt index 6f157fd4c8..1bbeac7d5b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/TransactionRowLabels.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabels.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.wallet +package com.vitorpamplona.amethyst.commons.model.nip47WalletConnect import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransactionType diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookup.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookup.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookup.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookup.kt index 5393ac47f4..ce3cdcc2ff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookup.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookup.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.communities.dal +package com.vitorpamplona.amethyst.commons.model.nip72Communities import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.cache.LocalCache diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/ImetaContent.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip92IMeta/ImetaContent.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/ImetaContent.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip92IMeta/ImetaContent.kt index 8ddd7facc2..7e886ec19d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/ImetaContent.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/nip92IMeta/ImetaContent.kt @@ -18,7 +18,7 @@ * 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.note.types +package com.vitorpamplona.amethyst.commons.model.nip92IMeta import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip92IMeta.imetas diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicFormatting.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/music/MusicFormatting.kt similarity index 89% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicFormatting.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/music/MusicFormatting.kt index 70c9814545..3a03cc1f9c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicFormatting.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/music/MusicFormatting.kt @@ -18,11 +18,11 @@ * 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.note.types +package com.vitorpamplona.amethyst.commons.music /** `m:ss` formatting for a track length in seconds, shared by both MusicTrack and MusicPlaylist UI. */ -internal fun formatTrackDuration(seconds: Int): String { +fun formatTrackDuration(seconds: Int): String { val minutes = seconds / 60 val secs = seconds % 60 - return "%d:%02d".format(minutes, secs) + return "$minutes:${secs.toString().padStart(2, '0')}" } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/ScanResult.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/ScanResult.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/ScanResult.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/ScanResult.kt index ab0c14b126..383262c3ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/ScanResult.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/ScanResult.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.qrcode.scanner +package com.vitorpamplona.amethyst.commons.qrcode /** A point in the decoder's image space (origin top-left, after crop and rotation). */ data class ScanPoint( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulator.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulator.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulator.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulator.kt index 26a0c8cc77..94b5112521 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulator.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulator.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.qrcode.scanner +package com.vitorpamplona.amethyst.commons.qrcode /** * Reassembles a Structured Append payload — one logical string split across several physical QR diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler.kt similarity index 94% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler.kt index 72de0c6434..4345dd6eca 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupOpenThreadsFilterAssembler.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupOpenThreadsFilterAssembler.kt @@ -18,10 +18,9 @@ * 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.screen.loggedIn.chats.publicChannels.relayGroup.datasource +package com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupThreadsFilter import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoveryFilter.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupsDiscoveryFilter.kt similarity index 78% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoveryFilter.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupsDiscoveryFilter.kt index 96a00d8091..ef81e16e47 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupsDiscoveryFilter.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/channel/relayGroup/RelayGroupsDiscoveryFilter.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.publicChannels.relayGroup.datasource +package com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilterSet @@ -31,15 +31,6 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.Aut import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.relay.RelayTopNavPerRelayFilterSet -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAllCommunities -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAuthors -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByCommunity -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByFollows -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByGeohashes -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByHashtag -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByMutedAuthors -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByRelay -import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsGlobal import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.scopedTo import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingEphemeralChats.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingEphemeralChats.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingEphemeralChats.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingEphemeralChats.kt index 9279e7b853..e1b3655e1b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingEphemeralChats.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingEphemeralChats.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.datasource +package com.vitorpamplona.amethyst.commons.relayClient.chatrooms import com.vitorpamplona.amethyst.commons.defaults.Constants import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingPublicChats.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingPublicChats.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingPublicChats.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingPublicChats.kt index 07ff49216d..3dcb6fc825 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterFollowingPublicChats.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterFollowingPublicChats.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.datasource +package com.vitorpamplona.amethyst.commons.relayClient.chatrooms import com.vitorpamplona.amethyst.commons.defaults.Constants import com.vitorpamplona.amethyst.commons.model.cache.LocalCache diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterLastMessageFollowingPublicChats.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterLastMessageFollowingPublicChats.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterLastMessageFollowingPublicChats.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterLastMessageFollowingPublicChats.kt index 5d17ed04d6..a96c62d3f8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterLastMessageFollowingPublicChats.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterLastMessageFollowingPublicChats.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.datasource +package com.vitorpamplona.amethyst.commons.relayClient.chatrooms import com.vitorpamplona.amethyst.commons.defaults.Constants import com.vitorpamplona.amethyst.commons.model.cache.LocalCache diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsFromMe.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsFromMe.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsFromMe.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsFromMe.kt index 516c98b011..898bf515f4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsFromMe.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsFromMe.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.datasource +package com.vitorpamplona.amethyst.commons.relayClient.chatrooms import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsToMe.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsToMe.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsToMe.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsToMe.kt index d7b68ba331..ab1c19e77d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/FilterNip04DMsToMe.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/chatrooms/FilterNip04DMsToMe.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.rooms.datasource +package com.vitorpamplona.amethyst.commons.relayClient.chatrooms import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheListingKinds.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/geocaches/GeocacheListingKinds.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheListingKinds.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/geocaches/GeocacheListingKinds.kt index 0444eb635e..9be06768db 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/dal/GeocacheListingKinds.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/geocaches/GeocacheListingKinds.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.geocaches.dal +package com.vitorpamplona.amethyst.commons.relayClient.geocaches import com.vitorpamplona.quartz.nipCCGeocaching.listing.GeocacheListingEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/FilterPostsByHashtags.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/hashtag/FilterPostsByHashtags.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/FilterPostsByHashtags.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/hashtag/FilterPostsByHashtags.kt index 2586985599..4a47e35c1e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/FilterPostsByHashtags.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/hashtag/FilterPostsByHashtags.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.hashtag.datasource +package com.vitorpamplona.amethyst.commons.relayClient.hashtag import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.CommentKinds import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/RelayPollResponseLoader.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/polls/results/RelayPollResponseLoader.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/RelayPollResponseLoader.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/polls/results/RelayPollResponseLoader.kt index b1bf7a931e..c3ebf26beb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/RelayPollResponseLoader.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/polls/results/RelayPollResponseLoader.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.polls.results +package com.vitorpamplona.amethyst.commons.relayClient.polls.results import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.viewmodels.nip88Polls.PollLoadReport diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/HlsPublishState.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishState.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/HlsPublishState.kt index 6513aeb45f..6f5669bce3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/HlsPublishState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/HlsPublishState.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.video.hls +package com.vitorpamplona.amethyst.commons.service.upload sealed class HlsPublishState { data object Idle : HlsPublishState() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/MediaUploadTracker.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/MediaUploadTracker.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/MediaUploadTracker.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/MediaUploadTracker.kt index 6ff5e72ec8..4d344e8514 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/MediaUploadTracker.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/MediaUploadTracker.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.service.upload import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/SuspendableConfirmation.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/SuspendableConfirmation.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt index ca94a7728e..44a457a83c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/SuspendableConfirmation.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt @@ -18,7 +18,7 @@ * 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.service.uploads +package com.vitorpamplona.amethyst.commons.service.upload import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt similarity index 90% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt index ffae7081c7..8158665f84 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt @@ -18,20 +18,12 @@ * 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.tor +package com.vitorpamplona.amethyst.commons.tor import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel -import com.vitorpamplona.amethyst.commons.tor.TorPresetType -import com.vitorpamplona.amethyst.commons.tor.TorSettings -import com.vitorpamplona.amethyst.commons.tor.TorType -import com.vitorpamplona.amethyst.commons.tor.torDefaultPreset -import com.vitorpamplona.amethyst.commons.tor.torFullyPrivate -import com.vitorpamplona.amethyst.commons.tor.torOnlyWhenNeededPreset -import com.vitorpamplona.amethyst.commons.tor.torSmallPayloadsPreset -import com.vitorpamplona.amethyst.commons.tor.whichPreset @Stable class TorDialogViewModel : ViewModel() { @@ -92,7 +84,7 @@ class TorDialogViewModel : ViewModel() { fun save(): TorSettings = TorSettings( torType = torType.value, - externalSocksPort = Integer.parseInt(socksPortStr.value), + externalSocksPort = socksPortStr.value.toInt(), onionRelaysViaTor = onionRelaysViaTor.value, dmRelaysViaTor = dmRelaysViaTor.value, newRelaysViaTor = newRelaysViaTor.value, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisher.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisher.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt index 71e72118c0..eff052fcb0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisher.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn +package com.vitorpamplona.amethyst.commons.util import kotlinx.coroutines.Job import kotlinx.coroutines.delay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedViewModel.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedViewModel.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt index 3c10216804..be771d2bf8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/relays/RelayFeedViewModel.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.profile.relays +package com.vitorpamplona.amethyst.commons.viewmodels import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable @@ -36,6 +36,7 @@ import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.IO import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -195,6 +196,6 @@ class RelayFeedViewModel : } override fun onCleared() { - Log.d("Init") { "OnCleared: ${this.javaClass.simpleName}" } + Log.d("Init") { "OnCleared: ${this::class.simpleName}" } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/identity/UserExternalIdentitiesViewModel.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/UserExternalIdentitiesViewModel.kt similarity index 88% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/identity/UserExternalIdentitiesViewModel.kt rename to commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/UserExternalIdentitiesViewModel.kt index 9a5c175da6..217b87ec7d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/identity/UserExternalIdentitiesViewModel.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/UserExternalIdentitiesViewModel.kt @@ -18,21 +18,24 @@ * 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.screen.loggedIn.profile.header.identity +package com.vitorpamplona.amethyst.commons.viewmodels import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.CreationExtras import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.quartz.nip39ExtIdentities.ExternalIdentitiesEvent import com.vitorpamplona.quartz.nip39ExtIdentities.identityClaims import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlin.reflect.KClass @Stable class UserExternalIdentitiesViewModel( @@ -61,6 +64,9 @@ class UserExternalIdentitiesViewModel( val user: User, ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T = UserExternalIdentitiesViewModel(user) as T + override fun create( + modelClass: KClass, + extras: CreationExtras, + ): T = UserExternalIdentitiesViewModel(user) as T } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletDiscovery.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletDiscovery.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt index 41bd6b695a..54aae743e5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletDiscovery.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.wallet.wizard +package com.vitorpamplona.amethyst.commons.cashu import androidx.compose.runtime.Stable import com.vitorpamplona.quartz.nip01Core.core.Event diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/MediaServerHealth.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/mediaServers/MediaServerHealth.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/MediaServerHealth.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/mediaServers/MediaServerHealth.kt index 23e8c85326..7e338ac74c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/MediaServerHealth.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/mediaServers/MediaServerHealth.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.actions.mediaServers +package com.vitorpamplona.amethyst.commons.mediaServers import com.vitorpamplona.quartz.nipB7Blossom.BlossomServerUrl import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/HistoryDateFormat.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/chats/HistoryDateFormat.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/HistoryDateFormat.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/chats/HistoryDateFormat.kt index 7b6ab94391..cf3fd25fb0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/HistoryDateFormat.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/chats/HistoryDateFormat.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.feed +package com.vitorpamplona.amethyst.commons.model.chats import java.text.SimpleDateFormat import java.util.Date diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewState.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/composer/PreviewState.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewState.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/composer/PreviewState.kt index ad97ee17c2..b5c748ecd5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/previews/PreviewState.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/composer/PreviewState.kt @@ -18,7 +18,7 @@ * 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.note.creators.previews +package com.vitorpamplona.amethyst.commons.model.composer import androidx.compose.runtime.Stable import com.vitorpamplona.amethyst.commons.richtext.CachedUrlParser diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/TimeFormatUtils.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeFormatUtils.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/TimeFormatUtils.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeFormatUtils.kt index 3ff304ae67..ce09af14e6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/TimeFormatUtils.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeFormatUtils.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.util import java.util.Locale diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt index 6879dc34d5..a0ad25666c 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt @@ -24,12 +24,12 @@ import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat -private val TenGiga = BigDecimal(10_000_000_000) -private val OneGiga = BigDecimal(1_000_000_000) -private val TenMega = BigDecimal(10_000_000) -private val OneMega = BigDecimal(1_000_000) -private val TenKilo = BigDecimal(10_000) -private val OneKilo = BigDecimal(1_000) +val TenGiga = BigDecimal(10_000_000_000) +val OneGiga = BigDecimal(1_000_000_000) +val TenMega = BigDecimal(10_000_000) +val OneMega = BigDecimal(1_000_000) +val TenKilo = BigDecimal(10_000) +val OneKilo = BigDecimal(1_000) private val dfGBig = ThreadLocal.withInitial { DecimalFormat("#.#G") } private val dfGSmall = ThreadLocal.withInitial { DecimalFormat("#.0G") } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt index ab8aa20159..5207240aef 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt @@ -18,7 +18,7 @@ * 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.note +package com.vitorpamplona.amethyst.commons.util import java.math.BigDecimal import java.math.RoundingMode diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifterTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifterTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifterTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifterTest.kt index 318f7690b5..de3b04e36e 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/actions/uploads/PitchShifterTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/audio/PitchShifterTest.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.audio import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContentTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContentTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContentTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContentTest.kt index e6e0029e17..8cffb4db36 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/dal/SupportedContentTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/feeds/SupportedContentTest.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.video.dal +package com.vitorpamplona.amethyst.commons.feeds import junit.framework.TestCase.assertTrue import org.junit.Test diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/NewMessageTaggerKeyParseTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTaggerKeyParseTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/NewMessageTaggerKeyParseTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTaggerKeyParseTest.kt index f126658623..3cc2675f47 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/NewMessageTaggerKeyParseTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/composer/NewMessageTaggerKeyParseTest.kt @@ -18,14 +18,13 @@ * 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 +package com.vitorpamplona.amethyst.commons.model.composer import com.vitorpamplona.amethyst.commons.model.AddressableNote import com.vitorpamplona.amethyst.commons.model.Dao import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.LocalCache.getOrCreateAddressableNoteInternal -import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip19Bech32.entities.NNote import com.vitorpamplona.quartz.nip19Bech32.entities.NPub diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/RouteTextArgsTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgsTest.kt similarity index 91% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/RouteTextArgsTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgsTest.kt index 00fe91a596..74f33677d2 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/RouteTextArgsTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/RouteTextArgsTest.kt @@ -18,12 +18,8 @@ * 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.navigation +package com.vitorpamplona.amethyst.commons.model.navigation -import com.vitorpamplona.amethyst.ui.navigation.routes.MAX_ROUTE_TEXT_ARG_ENCODED_LENGTH -import com.vitorpamplona.amethyst.ui.navigation.routes.ROUTE_TEXT_ARG_TRUNCATION_MARKER -import com.vitorpamplona.amethyst.ui.navigation.routes.encodedRouteArgLength -import com.vitorpamplona.amethyst.ui.navigation.routes.limitToRouteTextArg import org.junit.Assert.assertEquals import org.junit.Assert.assertSame import org.junit.Assert.assertTrue diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/ShareToDMRouteRewriterTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriterTest.kt similarity index 91% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/ShareToDMRouteRewriterTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriterTest.kt index 1758af9418..8765dca0f9 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/navigation/ShareToDMRouteRewriterTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/navigation/ShareToDMRouteRewriterTest.kt @@ -18,10 +18,8 @@ * 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.navigation +package com.vitorpamplona.amethyst.commons.model.navigation -import com.vitorpamplona.amethyst.commons.model.navigation.Route -import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.share.ShareToDMRouteRewriter import org.junit.Assert.assertEquals import org.junit.Assert.assertSame import org.junit.Test diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryConstraintTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/RelayGroupDiscoveryConstraintTest.kt similarity index 96% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryConstraintTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/RelayGroupDiscoveryConstraintTest.kt index efaf124679..45275e84bc 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/dal/RelayGroupDiscoveryConstraintTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip29RelayGroups/RelayGroupDiscoveryConstraintTest.kt @@ -18,10 +18,8 @@ * 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.screen.loggedIn.chats.publicChannels.relayGroup.dal +package com.vitorpamplona.amethyst.commons.model.nip29RelayGroups -import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.GroupDiscoveryConstraint -import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.model.topNavFeeds.global.GlobalTopNavPerRelayFilter import com.vitorpamplona.amethyst.commons.model.topNavFeeds.global.GlobalTopNavPerRelayFilterSet import com.vitorpamplona.amethyst.commons.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilter diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/wallet/TransactionRowLabelsTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabelsTest.kt similarity index 97% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/wallet/TransactionRowLabelsTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabelsTest.kt index 6e34dfe90e..fd378e4035 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/wallet/TransactionRowLabelsTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip47WalletConnect/TransactionRowLabelsTest.kt @@ -18,9 +18,8 @@ * 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.wallet +package com.vitorpamplona.amethyst.commons.model.nip47WalletConnect -import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.TransactionRowLabels import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction import org.junit.Assert.assertEquals import org.junit.Assert.assertNull diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookupTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookupTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookupTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookupTest.kt index 8d73566da6..8d6a3bf7b2 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/dal/CommunityRulesLookupTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip72Communities/CommunityRulesLookupTest.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.communities.dal +package com.vitorpamplona.amethyst.commons.model.nip72Communities import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulatorTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulatorTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulatorTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulatorTest.kt index e20b9106b4..c679704a4e 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/StructuredAppendAccumulatorTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/qrcode/StructuredAppendAccumulatorTest.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.qrcode.scanner +package com.vitorpamplona.amethyst.commons.qrcode import org.junit.Assert.assertEquals import org.junit.Assert.assertNull diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisherTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisherTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt index b7b26673cf..d90d6a823a 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DebouncedPublisherTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn +package com.vitorpamplona.amethyst.commons.util import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager.kt index b07884c2ac..50a91b33bd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/LoginErrorManager.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/account/ui/login/LoginErrorManager.kt @@ -18,7 +18,7 @@ * 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.screen.loggedOff.login +package com.vitorpamplona.amethyst.commons.account.ui.login import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePreset.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/VoicePreset.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePreset.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/VoicePreset.kt index 9b87c11b77..1ac2fb77ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoicePreset.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/VoicePreset.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.audio import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.voice_preset_deep diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ClinkBudgetDialog.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/clink/ui/ClinkBudgetDialog.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ClinkBudgetDialog.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/clink/ui/ClinkBudgetDialog.kt index 020b4ad7c0..59ddc9f118 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/ClinkBudgetDialog.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/clink/ui/ClinkBudgetDialog.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.wallet +package com.vitorpamplona.amethyst.commons.clink.ui import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/BusyLabel.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/BusyLabel.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/BusyLabel.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/BusyLabel.kt index c916830d4c..ae3c0b5c99 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/BusyLabel.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/BusyLabel.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings.cordn +package com.vitorpamplona.amethyst.commons.cordn.ui import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/SettingsFormBlock.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/SettingsFormBlock.kt index ca8825fec0..e66b6b51e9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/SettingsFormBlock.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/SettingsFormBlock.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings.cordn +package com.vitorpamplona.amethyst.commons.cordn.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46ActivityUi.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip46RemoteSigner/ui/Nip46ActivityUi.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46ActivityUi.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip46RemoteSigner/ui/Nip46ActivityUi.kt index e4180fef22..5ed65fb057 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/nip46/Nip46ActivityUi.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip46RemoteSigner/ui/Nip46ActivityUi.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings.nip46 +package com.vitorpamplona.amethyst.commons.nip46RemoteSigner.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewMode.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip52Calendar/ui/CalendarsViewMode.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewMode.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip52Calendar/ui/CalendarsViewMode.kt index 9dd2439014..ac135fc16d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarsViewMode.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip52Calendar/ui/CalendarsViewMode.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.calendars +package com.vitorpamplona.amethyst.commons.nip52Calendar.ui import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.calendar_view_day diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommunityRulesViolationBanner.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip72ModCommunities/ui/CommunityRulesViolationBanner.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommunityRulesViolationBanner.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip72ModCommunities/ui/CommunityRulesViolationBanner.kt index 064a3e2748..48f278e49d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommunityRulesViolationBanner.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip72ModCommunities/ui/CommunityRulesViolationBanner.kt @@ -18,7 +18,7 @@ * 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.note.nip22Comments +package com.vitorpamplona.amethyst.commons.nip72ModCommunities.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/SubPurposeLabels.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/ui/SubPurposeLabels.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/SubPurposeLabels.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/ui/SubPurposeLabels.kt index 8cde025250..50717b3f36 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/SubPurposeLabels.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/ui/SubPurposeLabels.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.relays.common +package com.vitorpamplona.amethyst.commons.relays.ui import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurposeGroup diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/StrippingFailureDialog.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/ui/StrippingFailureDialog.kt similarity index 91% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/StrippingFailureDialog.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/ui/StrippingFailureDialog.kt index 2649999976..2f528636bc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/StrippingFailureDialog.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/ui/StrippingFailureDialog.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.service.upload.ui import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button @@ -29,9 +29,9 @@ import com.vitorpamplona.amethyst.commons.resources.cancel import com.vitorpamplona.amethyst.commons.resources.metadata_strip_failed_body import com.vitorpamplona.amethyst.commons.resources.metadata_strip_failed_title import com.vitorpamplona.amethyst.commons.resources.metadata_strip_failed_upload +import com.vitorpamplona.amethyst.commons.service.upload.ConfirmationCallbacks +import com.vitorpamplona.amethyst.commons.service.upload.SuspendableConfirmation import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.service.uploads.ConfirmationCallbacks -import com.vitorpamplona.amethyst.service.uploads.SuspendableConfirmation @Composable fun StrippingFailureDialog(confirmation: SuspendableConfirmation) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/KeyboardState.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/insets/KeyboardState.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/KeyboardState.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/insets/KeyboardState.kt index 4d9a7fde2d..109b8e4260 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/KeyboardState.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/insets/KeyboardState.kt @@ -18,14 +18,13 @@ * 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.navigation.bottombars +package com.vitorpamplona.amethyst.commons.ui.insets import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalDensity -import com.vitorpamplona.amethyst.commons.ui.insets.rememberSafeImeInsets enum class KeyboardState { Opened, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/TabReselectCoordinator.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/bottombars/TabReselectCoordinator.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/TabReselectCoordinator.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/bottombars/TabReselectCoordinator.kt index 936bbc16fc..dee5d92392 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/TabReselectCoordinator.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/bottombars/TabReselectCoordinator.kt @@ -18,7 +18,7 @@ * 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.navigation.bottombars +package com.vitorpamplona.amethyst.commons.ui.navigation.bottombars import androidx.compose.runtime.Stable import androidx.compose.runtime.staticCompositionLocalOf diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/ObservableNav.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ObservableNav.kt similarity index 95% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/ObservableNav.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ObservableNav.kt index a21f8fd611..63b60e2bb9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/navs/ObservableNav.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ObservableNav.kt @@ -18,10 +18,9 @@ * 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.navigation.navs +package com.vitorpamplona.amethyst.commons.ui.navigation.navs import com.vitorpamplona.amethyst.commons.model.navigation.Route -import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.reflect.KClass diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMNav.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ShareToDMNav.kt similarity index 93% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMNav.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ShareToDMNav.kt index 0ac8cadba6..daf4eb5422 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/share/ShareToDMNav.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/ShareToDMNav.kt @@ -18,11 +18,11 @@ * 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.screen.loggedIn.chats.share +package com.vitorpamplona.amethyst.commons.ui.navigation.navs import androidx.compose.runtime.Stable import com.vitorpamplona.amethyst.commons.model.navigation.Route -import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.commons.model.navigation.ShareToDMRouteRewriter /** * Wraps an [INav] so that navigating to a chatroom ([Route.Room]) from the diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/TwoPaneNav.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/TwoPaneNav.kt similarity index 93% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/TwoPaneNav.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/TwoPaneNav.kt index fbc5eeae1c..add3728387 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/TwoPaneNav.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/navigation/navs/TwoPaneNav.kt @@ -18,11 +18,10 @@ * 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.screen.loggedIn.chats.rooms.twopane +package com.vitorpamplona.amethyst.commons.ui.navigation.navs import androidx.compose.runtime.mutableStateOf import com.vitorpamplona.amethyst.commons.model.navigation.Route -import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalog.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalog.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalog.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalog.kt index 41a2de97c9..5260295216 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalog.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalog.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings +package com.vitorpamplona.amethyst.commons.ui.settings import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol import org.jetbrains.compose.resources.StringResource diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/MentionPreservingInputTransformation.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/text/MentionPreservingInputTransformation.kt similarity index 94% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/MentionPreservingInputTransformation.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/text/MentionPreservingInputTransformation.kt index 608099835a..3a35dd38ef 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/MentionPreservingInputTransformation.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/text/MentionPreservingInputTransformation.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.ui.text import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.text.input.InputTransformation @@ -26,11 +26,11 @@ import androidx.compose.foundation.text.input.TextFieldBuffer /** * Matches a complete Nostr mention token: `@npub1…`, `nostr:npub1…`, - * `@nprofile1…`, `nostr:nprofile1…`. Shared with [UrlUserTagOutputTransformation] + * `@nprofile1…`, `nostr:nprofile1…`. Shared with the app's `UrlUserTagOutputTransformation` * so the wedge it produces and the input-side guard below agree on what * counts as a mention. */ -internal val MENTION_REGEX = Regex("(?:@|nostr:)(?:npub1[a-z0-9]{58}|nprofile1[a-z0-9]+)") +val MENTION_REGEX = Regex("(?:@|nostr:)(?:npub1[a-z0-9]{58}|nprofile1[a-z0-9]+)") /** * Keeps Nostr mentions atomic against IME edits that would only modify part of diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt index 2926375967..be2eb5eced 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.audio import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode @@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.recording_indicator_description import com.vitorpamplona.amethyst.commons.resources.recording_indicator_with_time import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatSecondsToTime /** * Animated expanding circles that pulse outward from the recording button diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/StreamingStatusFlags.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/StreamingStatusFlags.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt index 802856fe13..7528de89ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/StreamingStatusFlags.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.chats.publicChannels.nip53LiveActivities +package com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/InformationDialog.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InformationDialog.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/InformationDialog.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InformationDialog.kt index 32e9e00590..1c7509449e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/InformationDialog.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InformationDialog.kt @@ -18,7 +18,7 @@ * 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 +package com.vitorpamplona.amethyst.commons.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/UrlPreviewCard.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/UrlPreviewCard.kt index d3e60a8a4d..95ff1c5d00 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/UrlPreviewCard.kt @@ -18,7 +18,7 @@ * 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.components +package com.vitorpamplona.amethyst.commons.ui.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable @@ -48,9 +48,6 @@ import com.vitorpamplona.amethyst.commons.resources.copy_url_to_clipboard import com.vitorpamplona.amethyst.commons.resources.kind_comments import com.vitorpamplona.amethyst.commons.resources.link_actions_dialog_title import com.vitorpamplona.amethyst.commons.resources.url_preview_open_in_browser -import com.vitorpamplona.amethyst.commons.ui.components.M3ActionDialog -import com.vitorpamplona.amethyst.commons.ui.components.M3ActionRow -import com.vitorpamplona.amethyst.commons.ui.components.M3ActionSection import com.vitorpamplona.amethyst.commons.ui.components.util.setText import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/ColorSchemePreview.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/theme/ColorSchemePreview.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/ColorSchemePreview.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/theme/ColorSchemePreview.kt index a0bff9549d..f964c23093 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/ColorSchemePreview.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/theme/ColorSchemePreview.kt @@ -18,7 +18,7 @@ * 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.theme +package com.vitorpamplona.amethyst.commons.ui.theme import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -48,7 +48,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonRow import kotlin.math.max import kotlin.math.min diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogFilterTest.kt b/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalogFilterTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogFilterTest.kt rename to commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalogFilterTest.kt index 42b0e69dd7..10b234ad70 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SettingsCatalogFilterTest.kt +++ b/commonsUI/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/ui/settings/SettingsCatalogFilterTest.kt @@ -18,7 +18,7 @@ * 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.screen.loggedIn.settings +package com.vitorpamplona.amethyst.commons.ui.settings import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.account_settings From 0af3395b582507bdc5ccffe2a3b6726b3b0eb382 Mon Sep 17 00:00:00 2001 From: davotoula Date: Thu, 24 Sep 2026 12:04:13 +0200 Subject: [PATCH 06/19] fix(audio): stop dropping all but 2 spectrum frames per decoder buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pcm tap emits every fft frame of a decoder buffer synchronously from the audio thread, with no suspension point in between, while the UI collector sits on the main dispatcher and cannot interleave. The per-media flow was built with `replay = 1, extraBufferCapacity = 1` and published via `tryEmit`, which on a SUSPEND-overflow flow discards rather than blocks. The buffer therefore filled at 2 and the rest of every burst was thrown away — a hard cap of two frames per decoder buffer no matter how much audio it carried. The visualizer updated at the decoder-buffer rate (~5 Hz) instead of the ~43 Hz the fft actually produces, which is why every live style (bars, waves, radial, aurora) felt equally sluggish. Hold a whole burst instead, and drop the stalest frame rather than the newest if the UI does fall behind. The test pins delivery through the real PcmTapRegistry wiring; before this change it reported 2 frames delivered for bursts of 8, 20 and 50 alike. --- .../playback/playerPool/PcmTapRegistry.kt | 18 +++- .../playerPool/SpectrumBurstDeliveryTest.kt | 86 +++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt index 62129db719..5348d81d99 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt @@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.commons.audio.Spectrum import com.vitorpamplona.amethyst.commons.audio.normalizeToPeakInPlace import com.vitorpamplona.amethyst.commons.audio.toLogBins import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import java.nio.ByteBuffer @@ -112,6 +113,11 @@ class SpectrumAudioBufferSink( object PcmTapRegistry { private const val MAX_TRACKED_FLOWS = 64 + // Frames buffered per media flow beyond the 1-frame replay. One decoder buffer is typically a + // handful of 1024-sample hops; 63 leaves room for an unusually large one without letting a + // stalled UI bank more than ~1.5 s of stale spectrum. + private const val SPECTRUM_BUFFER_FRAMES = 63 + private val lock = Any() // access-order LinkedHashMap → eldest (least-recently-used) entries iterate first for eviction. @@ -167,7 +173,17 @@ object PcmTapRegistry { if (!fedByLiveSink && !stillCollected) iter.remove() } } - MutableSharedFlow(replay = 1, extraBufferCapacity = 1) + // The audio thread emits every fft frame of a decoder buffer synchronously, with no + // suspension point, while the UI collector sits on the main dispatcher and cannot + // interleave. A 2-slot buffer therefore capped the visualizer at two frames per + // decoder buffer however much audio it carried — the update rate tracked the decoder + // buffer rate (~5 Hz), not the ~43 Hz the fft produces. Hold a whole burst instead, + // and drop the STALEST frame rather than the newest when the UI does fall behind. + MutableSharedFlow( + replay = 1, + extraBufferCapacity = SPECTRUM_BUFFER_FRAMES, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) } } } diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt new file mode 100644 index 0000000000..d9c9a48c63 --- /dev/null +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt @@ -0,0 +1,86 @@ +/* + * 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.service.playback.playerPool + +import androidx.annotation.OptIn +import androidx.media3.common.C +import androidx.media3.common.util.UnstableApi +import com.vitorpamplona.amethyst.commons.audio.Spectrum +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Test +import java.nio.ByteBuffer +import java.nio.ByteOrder + +/** + * The audio thread runs [SpectrumAudioBufferSink.handleBuffer] to completion, emitting EVERY fft + * frame of one decoder buffer synchronously with no suspension point in between. The UI collector + * lives on the main dispatcher and cannot interleave with that burst, so anything that does not fit + * in the flow's buffer at that instant is silently dropped by `tryEmit`. + * + * With a 2-slot buffer that capped the visualizer at two frames per decoder buffer no matter how + * much audio it carried, decoupling the update rate from the ~43 Hz the fft actually produces. + */ +@kotlin.OptIn(ExperimentalCoroutinesApi::class) +@OptIn(UnstableApi::class) +class SpectrumBurstDeliveryTest { + private val fftSize = 64 + + private fun monoPcm(totalSamples: Int): ByteBuffer { + val bb = ByteBuffer.allocate(totalSamples * 2).order(ByteOrder.LITTLE_ENDIAN) + for (i in 0 until totalSamples) bb.putShort(((i % 100) * 300).toShort()) + bb.flip() + return bb + } + + /** Frames the UI receives from ONE decoder buffer holding [framesInBurst] fft frames. */ + private fun deliveredFromOneBurst(framesInBurst: Int): Int { + var count = -1 + runTest { + val mediaId = "test://burst-$framesInBurst" + val sink = SpectrumAudioBufferSink(fftSize = fftSize, binCount = 16) + PcmTapRegistry.bind(mediaId, sink) + sink.flush(48000, 1, C.ENCODING_PCM_16BIT) + + val received = mutableListOf() + val collector = launch { PcmTapRegistry.spectrumFor(mediaId).collect { received.add(it) } } + advanceUntilIdle() // let the collector subscribe + + sink.handleBuffer(monoPcm(fftSize * framesInBurst)) + + advanceUntilIdle() // now let the UI collector run + collector.cancel() + PcmTapRegistry.bind(null, sink) + count = received.size + } + return count + } + + @Test + fun everyFrameOfADecoderBurstReachesTheVisualizer() { + assertEquals("burst of 8", 8, deliveredFromOneBurst(8)) + assertEquals("burst of 20", 20, deliveredFromOneBurst(20)) + assertEquals("burst of 50", 50, deliveredFromOneBurst(50)) + } +} From 65df16bbc3a8abf9a77d847aaa366b0e52b542c6 Mon Sep 17 00:00:00 2001 From: davotoula Date: Thu, 24 Sep 2026 12:04:23 +0200 Subject: [PATCH 07/19] fix(audio): pace spectrum frames to the display instead of drawing a burst once Buffering the tap fixes delivery but not motion. Frames still arrive in decoder-sized bursts that run ahead of the audio, and writing a whole burst into Compose state collapses it: every write lands before the next vsync, so the burst draws ONCE and shows only its newest frame. The visual would keep stepping at the decoder-buffer rate despite every frame now arriving. Queue the frames and take exactly one per displayed frame. Production (~43 Hz) is slower than the display, so the queue drains and sits near empty; when it is starved the caller simply holds the frame already drawn rather than redrawing identical bins, so this adds no redundant draws over the previous behaviour. SpectrumPacer is a plain class in commons rather than a flow operator so the queue and eviction rules are unit-testable without a Compose or coroutine harness. It is bounded and evicts the stalest frame so a stalled UI cannot bank a growing backlog and drift permanently behind the audio. --- .../amethyst/commons/audio/SpectrumPacer.kt | 59 ++++++++++++++++ .../commons/audio/SpectrumPacerTest.kt | 67 +++++++++++++++++++ .../amethyst/commons/audio/SpectrumCanvas.kt | 15 ++++- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt create mode 100644 commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt new file mode 100644 index 0000000000..bc8fc2c913 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt @@ -0,0 +1,59 @@ +/* + * 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.commons.audio + +/** + * Spreads a bursty spectrum stream over the frames that draw it. + * + * The decoder hands the pcm tap a whole buffer at once, so spectrum frames arrive in bursts that run + * ahead of what is audible (the tap sits upstream of the audio output — see `delayedByFrames`). + * Delivering that burst straight into Compose state collapses it: every write lands before the next + * vsync, so the burst draws ONCE, showing only its newest frame. The visual then steps at the + * decoder-buffer rate instead of the ~43 Hz the fft produces. + * + * Buffering here and taking exactly one frame per drawn frame turns the burst back into motion. + * Production (~43 Hz) is slower than the display (60 Hz+), so the queue drains and sits near empty; + * [next] then returns null and the caller simply holds the frame it already has. + * + * Not thread-safe by design: both ends run on the UI dispatcher. + */ +class SpectrumPacer( + private val capacity: Int = DEFAULT_CAPACITY, +) { + private val queue = ArrayDeque(capacity) + + /** Queues a freshly decoded frame, evicting the stalest if the UI has fallen behind. */ + fun offer(frame: Spectrum) { + // Bound the queue so a stalled UI cannot bank an ever-growing backlog of stale spectrum and + // drift permanently behind the audio. The newest frame is always the one worth keeping. + while (queue.size >= capacity) queue.removeFirst() + queue.addLast(frame) + } + + /** The next frame to draw, or null when the queue is empty and the last frame should persist. */ + fun next(): Spectrum? = queue.removeFirstOrNull() + + companion object { + // ~1.5 s of 1024-sample hops at 44.1 kHz: room for any realistic decoder buffer without + // letting a stalled UI accumulate an unbounded backlog. + const val DEFAULT_CAPACITY = 64 + } +} diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt new file mode 100644 index 0000000000..5dbaf18950 --- /dev/null +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt @@ -0,0 +1,67 @@ +/* + * 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.commons.audio + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class SpectrumPacerTest { + private fun frame(id: Float) = Spectrum(floatArrayOf(id)) + + private fun idOf(spectrum: Spectrum?) = spectrum?.bins?.first() + + @Test + fun drainsOneFramePerTickInArrivalOrder() { + val pacer = SpectrumPacer(capacity = 8) + pacer.offer(frame(1f)) + pacer.offer(frame(2f)) + pacer.offer(frame(3f)) + + assertEquals(1f, idOf(pacer.next())) + assertEquals(2f, idOf(pacer.next())) + assertEquals(3f, idOf(pacer.next())) + } + + @Test + fun returnsNullWhenStarvedSoTheCallerCanHoldTheLastFrame() { + val pacer = SpectrumPacer(capacity = 8) + assertNull(pacer.next()) + + pacer.offer(frame(1f)) + assertEquals(1f, idOf(pacer.next())) + assertNull(pacer.next()) + } + + @Test + fun overCapacityDropsTheStalestFrameNotTheNewest() { + val pacer = SpectrumPacer(capacity = 3) + pacer.offer(frame(1f)) + pacer.offer(frame(2f)) + pacer.offer(frame(3f)) + pacer.offer(frame(4f)) // evicts 1 + + assertEquals(2f, idOf(pacer.next())) + assertEquals(3f, idOf(pacer.next())) + assertEquals(4f, idOf(pacer.next())) + assertNull(pacer.next()) + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt index a3c0b51eca..ae54e3b19f 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt @@ -46,9 +46,22 @@ fun SpectrumCanvas( draw: DrawScope.(bins: FloatArray, timeSec: Float, palette: VisualizerPalette) -> Unit, ) { val smoothed = remember { mutableStateOf(FloatArray(0)) } + + // Frames arrive in decoder-sized bursts that run ahead of the audio, so they are queued here and + // drawn one per displayed frame. Writing a whole burst straight into `smoothed` would collapse it + // into a single draw at the next vsync and the visual would step at the decoder-buffer rate. + val pacer = remember(spectrum) { SpectrumPacer() } + LaunchedEffect(spectrum) { + spectrum.collect { pacer.offer(it) } + } + LaunchedEffect(spectrum, decay) { var prev = FloatArray(0) - spectrum.collect { frame -> + while (true) { + withFrameMillis { } + // Starved: production (~43 Hz) is slower than the display, so this is the common case + // between frames. Hold what is already drawn rather than redrawing the same bins. + val frame = pacer.next() ?: continue // A fresh array each frame is intentional: mutableStateOf compares by reference, so a new // instance is what signals Compose to redraw. Do NOT switch to in-place mutation. val next = From 22051c546d7c1d94bdcb2a9e60b1007ff586fc65 Mon Sep 17 00:00:00 2001 From: davotoula Date: Thu, 24 Sep 2026 12:23:33 +0200 Subject: [PATCH 08/19] refactor(audio): extract the per-frame visualizer step into a testable SpectrumTrail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pacing commit left the queue drain, the decay trail and the fresh-array rule inline in SpectrumCanvas, where none of it could be covered — commonsUI has no Compose test harness, and adding one to a KMP module for three lines of glue is not worth the dependency. Move the whole per-displayed-frame step into SpectrumTrail in commons instead: take one queued frame, decay against what is drawn, return a new array, or null when starved. The composable keeps only the plumbing (collect into offer, drain on withFrameMillis), so what is left untested is code with no branches. Covered: starvation holds the drawn frame, the first frame has nothing to decay from, a rising bin attacks instantly, a falling bin releases gradually, each draw gets a distinct array (mutableStateOf compares by reference, so reuse would silently stop redrawing), and a backlog evicts the stalest frame. Also corrects the SpectrumCanvas KDoc: there is now a per-frame loop whatever `animated` is, so the note about it existing to avoid 60fps redraws no longer described the code. It only governs the monotonic clock. --- .../amethyst/commons/audio/SpectrumTrail.kt | 63 ++++++++++++ .../commons/audio/SpectrumTrailTest.kt | 96 +++++++++++++++++++ .../amethyst/commons/audio/SpectrumCanvas.kt | 40 ++++---- 3 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt create mode 100644 commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt new file mode 100644 index 0000000000..e4bb934759 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt @@ -0,0 +1,63 @@ +/* + * 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.commons.audio + +/** + * The whole per-displayed-frame step of the spectrum visualizer: take one queued frame, apply the + * decay trail against what is already on screen, and return a fresh array to draw. + * + * Bursty spectrum frames are paced through a [SpectrumPacer] (see its docs for why), and the decay + * gives each bin an instant attack and a gradual release, so bars snap up to a transient and fall + * back smoothly instead of flickering. + * + * This lives here, rather than inline in the Compose collector, so the queueing, starvation and + * decay rules are unit-testable without a Compose harness; the composable is left as plumbing. + * + * Not thread-safe by design: both ends run on the UI dispatcher. + */ +class SpectrumTrail( + private val decay: Float, + capacity: Int = SpectrumPacer.DEFAULT_CAPACITY, +) { + private val pacer = SpectrumPacer(capacity) + private var drawn = FloatArray(0) + + /** Queues a freshly decoded frame, evicting the stalest if the UI has fallen behind. */ + fun offer(frame: Spectrum) = pacer.offer(frame) + + /** + * The next array to draw, or null when no frame is queued and the current one should persist. + * + * A fresh array each time is intentional: `mutableStateOf` compares by reference, so a new + * instance is what signals Compose to redraw. Do NOT switch to in-place mutation. + */ + fun nextOrNull(): FloatArray? { + val frame = pacer.next() ?: return null + val prev = drawn + val next = + FloatArray(frame.bins.size) { i -> + val prior = if (i < prev.size) prev[i] * decay else 0f + if (frame.bins[i] > prior) frame.bins[i] else prior + } + drawn = next + return next + } +} diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt new file mode 100644 index 0000000000..9708e33105 --- /dev/null +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt @@ -0,0 +1,96 @@ +/* + * 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.commons.audio + +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertNotSame +import kotlin.test.assertNull +import kotlin.test.assertTrue + +/** + * The whole per-displayed-frame step the visualizer runs: take one queued frame, apply the decay + * trail against what is already drawn, and hand back a fresh array. Extracted from SpectrumCanvas + * so it is testable without a Compose harness — the composable is left as plumbing around this. + */ +class SpectrumTrailTest { + private fun frame(vararg bins: Float) = Spectrum(bins) + + @Test + fun yieldsNothingWhenStarvedSoTheDrawnFrameIsHeld() { + val trail = SpectrumTrail(decay = 0.5f) + assertNull(trail.nextOrNull()) + } + + @Test + fun theFirstFrameIsDrawnAsIsWithNothingToDecayFrom() { + val trail = SpectrumTrail(decay = 0.5f) + trail.offer(frame(1f, 0.5f, 0f)) + + assertContentEquals(floatArrayOf(1f, 0.5f, 0f), trail.nextOrNull()) + } + + @Test + fun aRisingBinTakesItsNewValueImmediately() { + val trail = SpectrumTrail(decay = 0.5f) + trail.offer(frame(0.2f)) + trail.nextOrNull() + trail.offer(frame(0.9f)) + + assertContentEquals(floatArrayOf(0.9f), trail.nextOrNull()) + } + + @Test + fun aFallingBinDecaysFromWhatWasDrawnRatherThanSnapping() { + val trail = SpectrumTrail(decay = 0.5f) + trail.offer(frame(1f)) + trail.nextOrNull() + trail.offer(frame(0f)) + + // 1f * 0.5 decay beats the new 0f, so the bar falls gradually. + assertContentEquals(floatArrayOf(0.5f), trail.nextOrNull()) + } + + @Test + fun eachDrawGetsAFreshArraySoComposeSeesTheChange() { + val trail = SpectrumTrail(decay = 0.5f) + trail.offer(frame(1f)) + trail.offer(frame(1f)) + + val first = trail.nextOrNull() + val second = trail.nextOrNull() + + // mutableStateOf compares by reference; reusing one array would never trigger a redraw. + assertNotSame(first, second) + } + + @Test + fun aBacklogIsBoundedByDroppingTheStalestFrame() { + val trail = SpectrumTrail(decay = 0f, capacity = 2) + trail.offer(frame(1f)) + trail.offer(frame(2f)) + trail.offer(frame(3f)) // evicts the 1f frame + + assertContentEquals(floatArrayOf(2f), trail.nextOrNull()) + assertContentEquals(floatArrayOf(3f), trail.nextOrNull()) + assertTrue(trail.nextOrNull() == null) + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt index ae54e3b19f..1326ffac57 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt @@ -34,7 +34,14 @@ import kotlinx.coroutines.flow.Flow * Collects [spectrum] into a decayed [FloatArray] and optionally runs a monotonic time clock, * then calls [draw] inside the Canvas draw lambda. The fast-changing state is read * ONLY in the draw lambda, so new frames trigger the draw phase, never recomposition. - * Pass [animated] = false for non-time-varying styles (bars, radial) to avoid 60fps redraws. + * + * Frames are paced one per displayed frame through [SpectrumTrail] — they arrive from the decoder in + * bursts, and writing a burst straight into state would collapse it into a single draw. The pacing + * loop runs every frame but only writes state when a spectrum frame is actually queued, so the redraw + * rate still tracks the ~43 Hz the fft produces rather than the display. + * + * Pass [animated] = false for non-time-varying styles (bars, radial): that drops the monotonic clock, + * whose whole purpose is to redraw every frame even when the spectrum has not moved. */ @Composable fun SpectrumCanvas( @@ -47,30 +54,21 @@ fun SpectrumCanvas( ) { val smoothed = remember { mutableStateOf(FloatArray(0)) } - // Frames arrive in decoder-sized bursts that run ahead of the audio, so they are queued here and - // drawn one per displayed frame. Writing a whole burst straight into `smoothed` would collapse it - // into a single draw at the next vsync and the visual would step at the decoder-buffer rate. - val pacer = remember(spectrum) { SpectrumPacer() } - LaunchedEffect(spectrum) { - spectrum.collect { pacer.offer(it) } + // Frames arrive in decoder-sized bursts that run ahead of the audio, so they are queued and drawn + // one per displayed frame. Writing a whole burst straight into `smoothed` would collapse it into a + // single draw at the next vsync and the visual would step at the decoder-buffer rate. Queueing and + // decay live in SpectrumTrail so they are testable without a Compose harness. + val trail = remember(spectrum, decay) { SpectrumTrail(decay) } + LaunchedEffect(spectrum, trail) { + spectrum.collect { trail.offer(it) } } - LaunchedEffect(spectrum, decay) { - var prev = FloatArray(0) + LaunchedEffect(trail) { while (true) { withFrameMillis { } - // Starved: production (~43 Hz) is slower than the display, so this is the common case - // between frames. Hold what is already drawn rather than redrawing the same bins. - val frame = pacer.next() ?: continue - // A fresh array each frame is intentional: mutableStateOf compares by reference, so a new - // instance is what signals Compose to redraw. Do NOT switch to in-place mutation. - val next = - FloatArray(frame.bins.size) { i -> - val prior = if (i < prev.size) prev[i] * decay else 0f - if (frame.bins[i] > prior) frame.bins[i] else prior - } - smoothed.value = next - prev = next + // Null means starved. Production (~43 Hz) is slower than the display, so this is the common + // case between frames: hold what is drawn rather than redrawing identical bins. + smoothed.value = trail.nextOrNull() ?: continue } } From 991687a33e563c579a24c19c729fb695593611de Mon Sep 17 00:00:00 2001 From: davotoula Date: Thu, 24 Sep 2026 14:47:34 +0200 Subject: [PATCH 09/19] docs(playback): correct the log-level claims guarding the diagnostic traces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both comments described levels no variant actually uses. DEFAULT_LOG_LEVEL gives debug builds INFO (not DEBUG) and release WARN (not ERROR), and the `benchmark` build type counts as `isDebug`, so it gets INFO too rather than being the silent release-like variant the text implied. The practical consequence was the misleading part: both said a debug build is enough to capture the trace. It is not — these are `Log.d`, which needs `minLevel <= DEBUG`, so PlaybackDiag and VideoQuality are silent in a stock debug build and only appear once Amethyst.VERBOSE_LOGS is flipped to true. RelayUsageListener already documents this correctly ("a debug build defaults to LogLevel.INFO"); these two had drifted from it. --- .../amethyst/service/playback/PlaybackDiag.kt | 10 +++++++--- .../composable/controls/VideoQualityControls.kt | 8 +++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackDiag.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackDiag.kt index c6cd9c9085..ac5c018225 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackDiag.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackDiag.kt @@ -22,9 +22,13 @@ package com.vitorpamplona.amethyst.service.playback /** * Shared logcat tag for the playback diagnostic trace (source routing, player lifecycle, error - * recovery, HLS liveness learning). Emitted with `Log.d`, so it appears only in a debug build - * (`Log.minLevel = DEBUG`) and is silent in benchmark/release (`ERROR`). To capture a playback - * investigation, install a debug build and run: + * recovery, HLS liveness learning). + * + * Emitted with `Log.d`, which fires only while `Log.minLevel <= DEBUG`. No shipped variant is + * there by default: [com.vitorpamplona.amethyst.Amethyst.DEFAULT_LOG_LEVEL] gives debug AND + * benchmark builds `INFO` (the `benchmark` build type counts as `isDebug`) and release `WARN`, so + * this trace is silent everywhere until `Amethyst.VERBOSE_LOGS` is flipped to true. To capture a + * playback investigation, set that flag, install a debug build and run: * * ``` * adb logcat -s PlaybackDiag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/VideoQualityControls.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/VideoQualityControls.kt index dbc3491464..8acee68d0a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/VideoQualityControls.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/VideoQualityControls.kt @@ -148,9 +148,11 @@ const val VIDEO_QUALITY_TAG = "VideoQuality" * Traces which rendition adaptive selection landed on, against the full ladder the manifest * offered. * - * The listener is registered only when the trace can actually be emitted — debug builds set - * `Log.minLevel = DEBUG` while benchmark/release set `ERROR` (see [PLAYBACK_DIAG_TAG]) — so the - * release path keeps the "no listener per player" property that dropping the old selector bought. + * The listener is registered only when the trace can actually be emitted. `Log.minLevel` is above + * `DEBUG` in every variant by default — `INFO` for debug and benchmark builds, `WARN` for release + * (see [PLAYBACK_DIAG_TAG]) — so this costs nothing until `Amethyst.VERBOSE_LOGS` is turned on, + * and every build keeps the "no listener per player" property that dropping the old selector + * bought. */ @Composable fun LogVideoQualitySelection(player: Player) { From 33f684773b86c439cc0bd8c845d0467540f2da14 Mon Sep 17 00:00:00 2001 From: davotoula Date: Sun, 27 Sep 2026 08:37:37 +0200 Subject: [PATCH 10/19] fix(audio): pace visualizer frames by the audio time they cover, not the display refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Releasing one spectrum frame per display refresh still looked jerky on device: bursts of fast motion with regular freezes two or three times a second. Measured on a Pixel 9a at 120 Hz with temporary logging: frames reach the UI in clusters of ~15 roughly every 330 ms, because the audio pipeline fills its output buffer in chunks. Released one per 8.3 ms refresh, a cluster played out in ~110 ms (3x too fast) and the screen then froze for ~220 ms until the next one — 161 stalls over 52.8 s, 3.05 a second, averaging 211 ms. Each Spectrum now carries the audio time it describes (fft size / sample rate, ~21 ms at 48 kHz) and SpectrumTrail releases a frame only once the previous one has covered its time, so a cluster spreads across the gap to the next. Time lost while starved is not owed back: after a gap, frames resume from the current frame time rather than being dumped at once to catch up. The backlog is capped at 24 frames (~0.5 s) so 2x playback speed cannot leave the picture drifting behind the sound; ordinary clusters peak at ~15 and are never trimmed. The duration defaults to zero, meaning "show on arrival", so producers that do not pace to audio — the synthetic preview emits one frame per display frame — keep their existing behaviour. Same device afterwards: 46.5 frames/s (48 kHz / 1024 = 46.9), median gap 24 ms, p95 26 ms, 2 gaps over 150 ms in 49.3 s, peak backlog 15. Also corrects the PcmTapRegistry comment from the buffering fix, which blamed a single large decoder buffer. The logs show decoder calls mostly carry one fft frame; the burst is many calls landing within a few ms, which the main-thread collector cannot interleave with either way. --- .../playback/playerPool/PcmTapRegistry.kt | 24 ++-- .../playerPool/SpectrumAudioBufferSinkTest.kt | 25 ++++ .../amethyst/commons/audio/AudioSpectrum.kt | 9 +- .../amethyst/commons/audio/SpectrumPacer.kt | 18 ++- .../amethyst/commons/audio/SpectrumTrail.kt | 63 ++++++-- .../commons/audio/SpectrumTrailTest.kt | 134 ++++++++++++++---- .../amethyst/commons/audio/SpectrumCanvas.kt | 25 ++-- 7 files changed, 226 insertions(+), 72 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt index 5348d81d99..70b32c1a03 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt @@ -66,6 +66,10 @@ class SpectrumAudioBufferSink( private var channels = 1 private var encoding = C.ENCODING_PCM_16BIT + // Audio time one fft frame covers: fftSize samples per channel at the stream's sample rate. + // Zero until the first flush reports a rate, which leaves the frame unpaced rather than wrong. + private var frameDurationNanos = 0L + @kotlin.OptIn(ExperimentalCoroutinesApi::class) override fun flush( sampleRateHz: Int, @@ -74,6 +78,7 @@ class SpectrumAudioBufferSink( ) { this.channels = channelCount.coerceAtLeast(1) this.encoding = encoding + this.frameDurationNanos = if (sampleRateHz > 0) fftSize * 1_000_000_000L / sampleRateHz else 0L filled = 0 output?.resetReplayCache() } @@ -99,7 +104,7 @@ class SpectrumAudioBufferSink( // Skip the DC bin (index 0): toLogBins ignores it, so letting a DC/offset component be the // peak would scale every audible bin toward zero and wash the spectrum out. mags.normalizeToPeakInPlace(fromIndex = 1) - output?.tryEmit(Spectrum(mags.toLogBins(binCount))) + output?.tryEmit(Spectrum(mags.toLogBins(binCount), frameDurationNanos)) } } @@ -113,8 +118,8 @@ class SpectrumAudioBufferSink( object PcmTapRegistry { private const val MAX_TRACKED_FLOWS = 64 - // Frames buffered per media flow beyond the 1-frame replay. One decoder buffer is typically a - // handful of 1024-sample hops; 63 leaves room for an unusually large one without letting a + // Frames buffered per media flow beyond the 1-frame replay. A cluster is ~15 fft frames (~0.33 s + // of audio, measured on a Pixel 9a); 63 leaves room for an unusually large one without letting a // stalled UI bank more than ~1.5 s of stale spectrum. private const val SPECTRUM_BUFFER_FRAMES = 63 @@ -173,12 +178,13 @@ object PcmTapRegistry { if (!fedByLiveSink && !stillCollected) iter.remove() } } - // The audio thread emits every fft frame of a decoder buffer synchronously, with no - // suspension point, while the UI collector sits on the main dispatcher and cannot - // interleave. A 2-slot buffer therefore capped the visualizer at two frames per - // decoder buffer however much audio it carried — the update rate tracked the decoder - // buffer rate (~5 Hz), not the ~43 Hz the fft produces. Hold a whole burst instead, - // and drop the STALEST frame rather than the newest when the UI does fall behind. + // The audio thread emits in clusters — the pipeline fills its output buffer ~3 times a + // second, so ~15 fft frames land within a few ms of each other (one per decoder call, + // or several from one large call) — while the UI collector sits on the main dispatcher + // and cannot run in between. A 2-slot buffer therefore kept only ~2 frames of each + // cluster and dropped the rest. Hold a whole cluster instead, and drop the STALEST + // frame rather than the newest if the UI does fall behind. (Spreading the cluster + // back over time is SpectrumTrail's job, not this flow's.) MutableSharedFlow( replay = 1, extraBufferCapacity = SPECTRUM_BUFFER_FRAMES, diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt index 6e9ce2ea35..012ddf6e72 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt @@ -136,4 +136,29 @@ class SpectrumAudioBufferSinkTest { assertTrue("non-16-bit PCM must not emit a spectrum", out.replayCache.isEmpty()) } + + // Pacing draws each frame for the audio time it covers, so the sink has to say how long that is. + // It is fftSize samples PER CHANNEL: a stereo stream must not report half (or double) the time. + @Test + fun eachFrameCarriesTheAudioTimeItCovers() { + val sink = SpectrumAudioBufferSink(fftSize = fftSize, binCount = binCount) + val out = MutableSharedFlow(replay = 1, extraBufferCapacity = 1) + sink.output = out + sink.flush(48000, 1, C.ENCODING_PCM_16BIT) + sink.handleBuffer(monoPcm(sineShorts(k = 2, n = fftSize))) + + assertEquals(fftSize * 1_000_000_000L / 48000, out.replayCache.last().durationNanos) + } + + @Test + fun stereoFramesCoverTheSameTimeAsMono() { + val sink = SpectrumAudioBufferSink(fftSize = fftSize, binCount = binCount) + val out = MutableSharedFlow(replay = 1, extraBufferCapacity = 1) + sink.output = out + sink.flush(44100, 2, C.ENCODING_PCM_16BIT) + val tone = sineShorts(k = 2, n = fftSize) + sink.handleBuffer(interleavedStereoPcm(tone, tone)) + + assertEquals(fftSize * 1_000_000_000L / 44100, out.replayCache.last().durationNanos) + } } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/AudioSpectrum.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/AudioSpectrum.kt index 86eb82822c..c074eecc49 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/AudioSpectrum.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/AudioSpectrum.kt @@ -26,9 +26,16 @@ import kotlin.math.exp import kotlin.math.ln import kotlin.math.log10 -/** One frame of frequency-domain magnitudes, ordered low→high Hz and normalized 0f..1f. */ +/** + * One frame of frequency-domain magnitudes, ordered low→high Hz and normalized 0f..1f. + * + * [durationNanos] is how much audio the frame describes (fft size / sample rate), which is how long + * it should stay on screen. Zero — the default — means the producer is not pacing to audio (the + * synthetic preview emits one per display frame), so the frame is shown as soon as it arrives. + */ class Spectrum( val bins: FloatArray, + val durationNanos: Long = 0L, ) /** diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt index bc8fc2c913..e6f8cc2e1d 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt @@ -21,17 +21,13 @@ package com.vitorpamplona.amethyst.commons.audio /** - * Spreads a bursty spectrum stream over the frames that draw it. + * The bounded queue between the audio tap and the visualizer. * - * The decoder hands the pcm tap a whole buffer at once, so spectrum frames arrive in bursts that run - * ahead of what is audible (the tap sits upstream of the audio output — see `delayedByFrames`). - * Delivering that burst straight into Compose state collapses it: every write lands before the next - * vsync, so the burst draws ONCE, showing only its newest frame. The visual then steps at the - * decoder-buffer rate instead of the ~43 Hz the fft produces. - * - * Buffering here and taking exactly one frame per drawn frame turns the burst back into motion. - * Production (~43 Hz) is slower than the display (60 Hz+), so the queue drains and sits near empty; - * [next] then returns null and the caller simply holds the frame it already has. + * Frames reach the UI in clusters, because the audio pipeline fills its output buffer in chunks + * (~15 frames about three times a second, measured on a Pixel 9a). This holds them until + * [SpectrumTrail] releases each one when its audio time comes due. It is bounded and evicts the + * stalest frame, so faster-than-real-time playback or a stalled UI cannot bank an ever-growing + * backlog and leave the picture permanently behind the sound. * * Not thread-safe by design: both ends run on the UI dispatcher. */ @@ -48,6 +44,8 @@ class SpectrumPacer( queue.addLast(frame) } + fun isEmpty(): Boolean = queue.isEmpty() + /** The next frame to draw, or null when the queue is empty and the last frame should persist. */ fun next(): Spectrum? = queue.removeFirstOrNull() diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt index e4bb934759..98e40f7770 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt @@ -21,36 +21,68 @@ package com.vitorpamplona.amethyst.commons.audio /** - * The whole per-displayed-frame step of the spectrum visualizer: take one queued frame, apply the - * decay trail against what is already on screen, and return a fresh array to draw. + * The whole per-displayed-frame step of the spectrum visualizer: release queued frames as their + * audio time comes due, apply the decay trail against what is already on screen, and return a + * fresh array to draw. * - * Bursty spectrum frames are paced through a [SpectrumPacer] (see its docs for why), and the decay - * gives each bin an instant attack and a gradual release, so bars snap up to a transient and fall - * back smoothly instead of flickering. + * Frames arrive in clusters (see [SpectrumPacer]). Releasing one per display refresh drained a + * ~15-frame cluster in ~110 ms at 120 Hz and then froze until the next one — measured as ~3 stalls + * of ~210 ms every second. Each frame instead stays up for the [Spectrum.durationNanos] of audio it + * describes, so a cluster spreads across the gap to the next. Time lost while starved is not owed + * back: when frames return after a gap they resume from the current frame time rather than being + * dumped at once to catch up. * - * This lives here, rather than inline in the Compose collector, so the queueing, starvation and - * decay rules are unit-testable without a Compose harness; the composable is left as plumbing. + * The decay gives each bin an instant attack and a gradual release, so bars snap up to a transient + * and fall back smoothly instead of flickering. + * + * This lives here, rather than inline in the Compose collector, so the pacing, starvation and decay + * rules are unit-testable without a Compose harness; the composable is left as plumbing. * * Not thread-safe by design: both ends run on the UI dispatcher. */ class SpectrumTrail( private val decay: Float, - capacity: Int = SpectrumPacer.DEFAULT_CAPACITY, + capacity: Int = MAX_BACKLOG_FRAMES, ) { private val pacer = SpectrumPacer(capacity) private var drawn = FloatArray(0) - /** Queues a freshly decoded frame, evicting the stalest if the UI has fallen behind. */ + // When the frame at the head of the queue may be drawn, in the caller's frame-clock nanos. + private var dueNanos = Long.MIN_VALUE + private var starved = true + + /** Queues a freshly decoded frame, evicting the stalest if the backlog is at capacity. */ fun offer(frame: Spectrum) = pacer.offer(frame) /** - * The next array to draw, or null when no frame is queued and the current one should persist. + * The array to draw at [frameTimeNanos], or null when nothing new is due and the current one + * should persist. * * A fresh array each time is intentional: `mutableStateOf` compares by reference, so a new * instance is what signals Compose to redraw. Do NOT switch to in-place mutation. */ - fun nextOrNull(): FloatArray? { - val frame = pacer.next() ?: return null + fun nextOrNull(frameTimeNanos: Long): FloatArray? { + if (pacer.isEmpty()) { + starved = true + return null + } + if (starved) { + // Resume from now, but never earlier than the last drawn frame's audio time runs out. + dueNanos = maxOf(dueNanos, frameTimeNanos) + starved = false + } + + var result: FloatArray? = null + while (dueNanos <= frameTimeNanos) { + val frame = pacer.next() ?: break + result = decayedFrom(frame) + dueNanos += frame.durationNanos + } + + return result + } + + private fun decayedFrom(frame: Spectrum): FloatArray { val prev = drawn val next = FloatArray(frame.bins.size) { i -> @@ -60,4 +92,11 @@ class SpectrumTrail( drawn = next return next } + + companion object { + // A cluster is ~15 frames, so 24 (~0.5 s of audio) never trims ordinary playback, while + // capping how far the picture can lag the sound when frames come faster than real time + // (2x playback speed). + const val MAX_BACKLOG_FRAMES = 24 + } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt index 9708e33105..39a73cb261 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt @@ -24,49 +24,139 @@ import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertNotSame import kotlin.test.assertNull -import kotlin.test.assertTrue /** - * The whole per-displayed-frame step the visualizer runs: take one queued frame, apply the decay - * trail against what is already drawn, and hand back a fresh array. Extracted from SpectrumCanvas - * so it is testable without a Compose harness — the composable is left as plumbing around this. + * The per-displayed-frame step of the visualizer: release queued frames as their audio time comes + * due, apply the decay trail against what is drawn, and hand back a fresh array. + * + * Frames reach the UI in clusters (~15 at a time, ~3 times a second, measured on a Pixel 9a) because + * the audio pipeline fills its output buffer in chunks. Releasing one per display refresh drained a + * cluster in ~110 ms at 120 Hz and then froze for ~220 ms. Frames must instead be released at the + * rate of the audio they describe, so a cluster spreads across the gap to the next one. */ class SpectrumTrailTest { - private fun frame(vararg bins: Float) = Spectrum(bins) + private val ms = 1_000_000L + + private fun frame( + value: Float, + durationMs: Long = 20, + ) = Spectrum(floatArrayOf(value), durationNanos = durationMs * ms) @Test fun yieldsNothingWhenStarvedSoTheDrawnFrameIsHeld() { val trail = SpectrumTrail(decay = 0.5f) - assertNull(trail.nextOrNull()) + assertNull(trail.nextOrNull(0)) } @Test - fun theFirstFrameIsDrawnAsIsWithNothingToDecayFrom() { + fun theFirstFrameIsDrawnOnArrivalWithNothingToDecayFrom() { val trail = SpectrumTrail(decay = 0.5f) - trail.offer(frame(1f, 0.5f, 0f)) + trail.offer(frame(1f)) - assertContentEquals(floatArrayOf(1f, 0.5f, 0f), trail.nextOrNull()) + assertContentEquals(floatArrayOf(1f), trail.nextOrNull(0)) + } + + @Test + fun aClusterIsSpreadOverTheAudioTimeItCoversNotTheScreenRefresh() { + val trail = SpectrumTrail(decay = 0f) + trail.offer(frame(1f)) + trail.offer(frame(2f)) + trail.offer(frame(3f)) + + // 120 Hz refreshes (~8 ms) must not drain a frame that covers 20 ms of audio. + assertContentEquals(floatArrayOf(1f), trail.nextOrNull(0)) + assertNull(trail.nextOrNull(8 * ms)) + assertNull(trail.nextOrNull(16 * ms)) + assertContentEquals(floatArrayOf(2f), trail.nextOrNull(20 * ms)) + assertNull(trail.nextOrNull(33 * ms)) + assertContentEquals(floatArrayOf(3f), trail.nextOrNull(40 * ms)) + assertNull(trail.nextOrNull(48 * ms)) + } + + @Test + fun afterStarvingTheNextClusterStartsWhenItArrivesInsteadOfBeingDumpedToCatchUp() { + val trail = SpectrumTrail(decay = 0f) + trail.offer(frame(1f)) + assertContentEquals(floatArrayOf(1f), trail.nextOrNull(0)) + assertNull(trail.nextOrNull(100 * ms)) // starved for a while + + trail.offer(frame(2f)) + trail.offer(frame(3f)) + + // The idle time is not owed: frame 2 shows now and frame 3 a full frame later. + assertContentEquals(floatArrayOf(2f), trail.nextOrNull(300 * ms)) + assertNull(trail.nextOrNull(308 * ms)) + assertContentEquals(floatArrayOf(3f), trail.nextOrNull(320 * ms)) + } + + @Test + fun aFrameArrivingBeforeThePreviousOneElapsedStillWaitsItsTurn() { + val trail = SpectrumTrail(decay = 0f) + trail.offer(frame(1f)) + assertContentEquals(floatArrayOf(1f), trail.nextOrNull(0)) + assertNull(trail.nextOrNull(8 * ms)) // queue momentarily empty + + trail.offer(frame(2f)) + + assertNull(trail.nextOrNull(16 * ms)) // frame 1 still covers until 20 ms + assertContentEquals(floatArrayOf(2f), trail.nextOrNull(24 * ms)) + } + + @Test + fun aSlowFrameClockJumpsToTheLatestDueFrame() { + val trail = SpectrumTrail(decay = 0f) + trail.offer(frame(1f)) + trail.offer(frame(2f)) + trail.offer(frame(3f)) + + assertContentEquals(floatArrayOf(1f), trail.nextOrNull(0)) + // A janky 45 ms frame: frames 2 (due 20) and 3 (due 40) are both due; show the newest. + assertContentEquals(floatArrayOf(3f), trail.nextOrNull(45 * ms)) + } + + @Test + fun aFrameWithNoDurationIsShownOnArrival() { + // Producers that do not declare a duration (the synthetic preview emits one per display + // frame) keep the old behaviour: whatever is queued is current. + val trail = SpectrumTrail(decay = 0f) + trail.offer(Spectrum(floatArrayOf(1f))) + trail.offer(Spectrum(floatArrayOf(2f))) + + assertContentEquals(floatArrayOf(2f), trail.nextOrNull(0)) + } + + @Test + fun aBacklogIsCappedByDroppingTheStalestFrame() { + // Faster-than-real-time playback (2x speed) produces frames faster than they come due; + // the cap bounds how far the picture can lag the sound. + val trail = SpectrumTrail(decay = 0f, capacity = 2) + trail.offer(frame(1f)) + trail.offer(frame(2f)) + trail.offer(frame(3f)) // evicts frame 1 + + assertContentEquals(floatArrayOf(2f), trail.nextOrNull(0)) + assertContentEquals(floatArrayOf(3f), trail.nextOrNull(20 * ms)) + assertNull(trail.nextOrNull(40 * ms)) } @Test fun aRisingBinTakesItsNewValueImmediately() { val trail = SpectrumTrail(decay = 0.5f) trail.offer(frame(0.2f)) - trail.nextOrNull() + trail.nextOrNull(0) trail.offer(frame(0.9f)) - assertContentEquals(floatArrayOf(0.9f), trail.nextOrNull()) + assertContentEquals(floatArrayOf(0.9f), trail.nextOrNull(20 * ms)) } @Test fun aFallingBinDecaysFromWhatWasDrawnRatherThanSnapping() { val trail = SpectrumTrail(decay = 0.5f) trail.offer(frame(1f)) - trail.nextOrNull() + trail.nextOrNull(0) trail.offer(frame(0f)) - // 1f * 0.5 decay beats the new 0f, so the bar falls gradually. - assertContentEquals(floatArrayOf(0.5f), trail.nextOrNull()) + assertContentEquals(floatArrayOf(0.5f), trail.nextOrNull(20 * ms)) } @Test @@ -75,22 +165,10 @@ class SpectrumTrailTest { trail.offer(frame(1f)) trail.offer(frame(1f)) - val first = trail.nextOrNull() - val second = trail.nextOrNull() + val first = trail.nextOrNull(0) + val second = trail.nextOrNull(20 * ms) // mutableStateOf compares by reference; reusing one array would never trigger a redraw. assertNotSame(first, second) } - - @Test - fun aBacklogIsBoundedByDroppingTheStalestFrame() { - val trail = SpectrumTrail(decay = 0f, capacity = 2) - trail.offer(frame(1f)) - trail.offer(frame(2f)) - trail.offer(frame(3f)) // evicts the 1f frame - - assertContentEquals(floatArrayOf(2f), trail.nextOrNull()) - assertContentEquals(floatArrayOf(3f), trail.nextOrNull()) - assertTrue(trail.nextOrNull() == null) - } } diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt index 1326ffac57..56a2ec66d9 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.withFrameMillis +import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.drawscope.DrawScope import kotlinx.coroutines.flow.Flow @@ -35,10 +36,10 @@ import kotlinx.coroutines.flow.Flow * then calls [draw] inside the Canvas draw lambda. The fast-changing state is read * ONLY in the draw lambda, so new frames trigger the draw phase, never recomposition. * - * Frames are paced one per displayed frame through [SpectrumTrail] — they arrive from the decoder in - * bursts, and writing a burst straight into state would collapse it into a single draw. The pacing - * loop runs every frame but only writes state when a spectrum frame is actually queued, so the redraw - * rate still tracks the ~43 Hz the fft produces rather than the display. + * Frames are released through [SpectrumTrail] as the audio they describe comes due — they arrive + * in clusters, and neither dumping a cluster into state nor draining it one per vsync looks live. + * The pacing loop runs every frame but only writes state when a frame is due, so the redraw rate + * tracks the ~43-47 Hz the fft produces rather than the display. * * Pass [animated] = false for non-time-varying styles (bars, radial): that drops the monotonic clock, * whose whole purpose is to redraw every frame even when the spectrum has not moved. @@ -54,10 +55,10 @@ fun SpectrumCanvas( ) { val smoothed = remember { mutableStateOf(FloatArray(0)) } - // Frames arrive in decoder-sized bursts that run ahead of the audio, so they are queued and drawn - // one per displayed frame. Writing a whole burst straight into `smoothed` would collapse it into a - // single draw at the next vsync and the visual would step at the decoder-buffer rate. Queueing and - // decay live in SpectrumTrail so they are testable without a Compose harness. + // Frames arrive in clusters, so they are queued and released as their audio time comes due. + // Writing a cluster straight into `smoothed` collapses it into one draw; releasing one per vsync + // races through it and then freezes. Pacing and decay live in SpectrumTrail so they are testable + // without a Compose harness. val trail = remember(spectrum, decay) { SpectrumTrail(decay) } LaunchedEffect(spectrum, trail) { spectrum.collect { trail.offer(it) } @@ -65,10 +66,10 @@ fun SpectrumCanvas( LaunchedEffect(trail) { while (true) { - withFrameMillis { } - // Null means starved. Production (~43 Hz) is slower than the display, so this is the common - // case between frames: hold what is drawn rather than redrawing identical bins. - smoothed.value = trail.nextOrNull() ?: continue + val frameTimeNanos = withFrameNanos { it } + // Null means nothing new is due. Frames cover ~21 ms of audio against an 8-16 ms refresh, + // so this is the common case: hold what is drawn rather than redrawing identical bins. + smoothed.value = trail.nextOrNull(frameTimeNanos) ?: continue } } From 5b8e6e5b0a55c0c97f9f3b72f7edb2fe7ec918a8 Mon Sep 17 00:00:00 2001 From: davotoula Date: Sun, 27 Sep 2026 08:49:09 +0200 Subject: [PATCH 11/19] refactor(audio): simplify the visualizer pacing and stop it polling while idle Cleanup pass over the visualizer fix: - Fold SpectrumPacer into SpectrumTrail. It had one caller and only wrapped an ArrayDeque; its unused 64-frame default contradicted the real 24-frame cap. Its three tests were already covered by SpectrumTrailTest. - Size the PcmTapRegistry flow buffer from SpectrumTrail.MAX_BACKLOG_FRAMES instead of a separate 63. Both drop the stalest frame, so the visualizer keeps the same newest frames either way; the trail's cap is the one that actually bounds how far the picture can lag the sound. - Move the cluster-delivery test into SpectrumAudioBufferSinkTest on its existing monoPcm/sineShorts helpers instead of a second PCM encoder. Re-checked against the old 2-slot buffer: it still fails with 2 of 8. - Park the pacing loop off the frame clock while nothing is queued. It woke the main thread every vsync (up to 120 Hz) even when paused, which was new work for the non-animated bars and radial styles. It parks only after a nextOrNull has seen the empty queue, so the starvation reset still runs and the next cluster is not dumped at once to catch up. - Keep the cluster-timing explanation in the SpectrumTrail KDoc only. It had been restated in six places and the copies had already drifted (210 vs 220 ms). Also corrects the SpectrumCanvas KDoc's redraw-rate claim, which only held for bars and radial. --- .../playback/playerPool/PcmTapRegistry.kt | 19 ++-- .../playerPool/SpectrumAudioBufferSinkTest.kt | 38 ++++++++ .../playerPool/SpectrumBurstDeliveryTest.kt | 86 ------------------- .../amethyst/commons/audio/SpectrumPacer.kt | 57 ------------ .../amethyst/commons/audio/SpectrumTrail.kt | 42 +++++---- .../commons/audio/SpectrumPacerTest.kt | 67 --------------- .../commons/audio/SpectrumTrailTest.kt | 27 ++++-- .../amethyst/commons/audio/SpectrumCanvas.kt | 31 ++++--- 8 files changed, 105 insertions(+), 262 deletions(-) delete mode 100644 amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt delete mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt delete mode 100644 commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt index 70b32c1a03..2fd9ce4a6d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/PcmTapRegistry.kt @@ -27,6 +27,7 @@ import androidx.media3.exoplayer.audio.TeeAudioProcessor import com.vitorpamplona.amethyst.commons.audio.AudioWindow import com.vitorpamplona.amethyst.commons.audio.Fft import com.vitorpamplona.amethyst.commons.audio.Spectrum +import com.vitorpamplona.amethyst.commons.audio.SpectrumTrail import com.vitorpamplona.amethyst.commons.audio.normalizeToPeakInPlace import com.vitorpamplona.amethyst.commons.audio.toLogBins import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -118,11 +119,6 @@ class SpectrumAudioBufferSink( object PcmTapRegistry { private const val MAX_TRACKED_FLOWS = 64 - // Frames buffered per media flow beyond the 1-frame replay. A cluster is ~15 fft frames (~0.33 s - // of audio, measured on a Pixel 9a); 63 leaves room for an unusually large one without letting a - // stalled UI bank more than ~1.5 s of stale spectrum. - private const val SPECTRUM_BUFFER_FRAMES = 63 - private val lock = Any() // access-order LinkedHashMap → eldest (least-recently-used) entries iterate first for eviction. @@ -178,16 +174,13 @@ object PcmTapRegistry { if (!fedByLiveSink && !stillCollected) iter.remove() } } - // The audio thread emits in clusters — the pipeline fills its output buffer ~3 times a - // second, so ~15 fft frames land within a few ms of each other (one per decoder call, - // or several from one large call) — while the UI collector sits on the main dispatcher - // and cannot run in between. A 2-slot buffer therefore kept only ~2 frames of each - // cluster and dropped the rest. Hold a whole cluster instead, and drop the STALEST - // frame rather than the newest if the UI does fall behind. (Spreading the cluster - // back over time is SpectrumTrail's job, not this flow's.) + // Frames arrive in clusters (see SpectrumTrail) emitted within a few ms on the audio + // thread, while the collector sits on the main dispatcher and cannot run in between; + // a 2-slot buffer kept ~2 frames of each cluster and dropped the rest. Hold as much as + // the visualizer's own backlog, which is the real lag bound, dropping the stalest. MutableSharedFlow( replay = 1, - extraBufferCapacity = SPECTRUM_BUFFER_FRAMES, + extraBufferCapacity = SpectrumTrail.MAX_BACKLOG_FRAMES, onBufferOverflow = BufferOverflow.DROP_OLDEST, ) } diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt index 012ddf6e72..7cf470a967 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumAudioBufferSinkTest.kt @@ -24,7 +24,11 @@ import androidx.annotation.OptIn import androidx.media3.common.C import androidx.media3.common.util.UnstableApi import com.vitorpamplona.amethyst.commons.audio.Spectrum +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -33,6 +37,7 @@ import java.nio.ByteOrder import kotlin.math.PI import kotlin.math.sin +@kotlin.OptIn(ExperimentalCoroutinesApi::class) @OptIn(UnstableApi::class) class SpectrumAudioBufferSinkTest { private val fftSize = 64 @@ -161,4 +166,37 @@ class SpectrumAudioBufferSinkTest { assertEquals(fftSize * 1_000_000_000L / 44100, out.replayCache.last().durationNanos) } + + /** + * The audio thread emits a cluster of fft frames within a few ms, with no suspension point in + * between, while the UI collector sits on the main dispatcher and cannot interleave. Anything the + * registry's flow cannot hold at that instant is dropped; a 2-slot buffer kept 2 of every cluster. + */ + private fun deliveredFromOneCluster(framesInCluster: Int): Int { + var count = -1 + runTest { + val mediaId = "test://cluster-$framesInCluster" + val sink = SpectrumAudioBufferSink(fftSize = fftSize, binCount = binCount) + PcmTapRegistry.bind(mediaId, sink) + sink.flush(48000, 1, C.ENCODING_PCM_16BIT) + + val received = mutableListOf() + val collector = launch { PcmTapRegistry.spectrumFor(mediaId).collect { received.add(it) } } + advanceUntilIdle() // let the collector subscribe + + sink.handleBuffer(monoPcm(sineShorts(k = 2, n = fftSize * framesInCluster))) + + advanceUntilIdle() // only now does the UI collector get to run + collector.cancel() + PcmTapRegistry.bind(null, sink) + count = received.size + } + return count + } + + @Test + fun everyFrameOfAClusterReachesTheVisualizer() { + assertEquals("cluster of 8", 8, deliveredFromOneCluster(8)) + assertEquals("cluster of 20", 20, deliveredFromOneCluster(20)) + } } diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt deleted file mode 100644 index d9c9a48c63..0000000000 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/service/playback/playerPool/SpectrumBurstDeliveryTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.service.playback.playerPool - -import androidx.annotation.OptIn -import androidx.media3.common.C -import androidx.media3.common.util.UnstableApi -import com.vitorpamplona.amethyst.commons.audio.Spectrum -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Test -import java.nio.ByteBuffer -import java.nio.ByteOrder - -/** - * The audio thread runs [SpectrumAudioBufferSink.handleBuffer] to completion, emitting EVERY fft - * frame of one decoder buffer synchronously with no suspension point in between. The UI collector - * lives on the main dispatcher and cannot interleave with that burst, so anything that does not fit - * in the flow's buffer at that instant is silently dropped by `tryEmit`. - * - * With a 2-slot buffer that capped the visualizer at two frames per decoder buffer no matter how - * much audio it carried, decoupling the update rate from the ~43 Hz the fft actually produces. - */ -@kotlin.OptIn(ExperimentalCoroutinesApi::class) -@OptIn(UnstableApi::class) -class SpectrumBurstDeliveryTest { - private val fftSize = 64 - - private fun monoPcm(totalSamples: Int): ByteBuffer { - val bb = ByteBuffer.allocate(totalSamples * 2).order(ByteOrder.LITTLE_ENDIAN) - for (i in 0 until totalSamples) bb.putShort(((i % 100) * 300).toShort()) - bb.flip() - return bb - } - - /** Frames the UI receives from ONE decoder buffer holding [framesInBurst] fft frames. */ - private fun deliveredFromOneBurst(framesInBurst: Int): Int { - var count = -1 - runTest { - val mediaId = "test://burst-$framesInBurst" - val sink = SpectrumAudioBufferSink(fftSize = fftSize, binCount = 16) - PcmTapRegistry.bind(mediaId, sink) - sink.flush(48000, 1, C.ENCODING_PCM_16BIT) - - val received = mutableListOf() - val collector = launch { PcmTapRegistry.spectrumFor(mediaId).collect { received.add(it) } } - advanceUntilIdle() // let the collector subscribe - - sink.handleBuffer(monoPcm(fftSize * framesInBurst)) - - advanceUntilIdle() // now let the UI collector run - collector.cancel() - PcmTapRegistry.bind(null, sink) - count = received.size - } - return count - } - - @Test - fun everyFrameOfADecoderBurstReachesTheVisualizer() { - assertEquals("burst of 8", 8, deliveredFromOneBurst(8)) - assertEquals("burst of 20", 20, deliveredFromOneBurst(20)) - assertEquals("burst of 50", 50, deliveredFromOneBurst(50)) - } -} diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt deleted file mode 100644 index e6f8cc2e1d..0000000000 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacer.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.commons.audio - -/** - * The bounded queue between the audio tap and the visualizer. - * - * Frames reach the UI in clusters, because the audio pipeline fills its output buffer in chunks - * (~15 frames about three times a second, measured on a Pixel 9a). This holds them until - * [SpectrumTrail] releases each one when its audio time comes due. It is bounded and evicts the - * stalest frame, so faster-than-real-time playback or a stalled UI cannot bank an ever-growing - * backlog and leave the picture permanently behind the sound. - * - * Not thread-safe by design: both ends run on the UI dispatcher. - */ -class SpectrumPacer( - private val capacity: Int = DEFAULT_CAPACITY, -) { - private val queue = ArrayDeque(capacity) - - /** Queues a freshly decoded frame, evicting the stalest if the UI has fallen behind. */ - fun offer(frame: Spectrum) { - // Bound the queue so a stalled UI cannot bank an ever-growing backlog of stale spectrum and - // drift permanently behind the audio. The newest frame is always the one worth keeping. - while (queue.size >= capacity) queue.removeFirst() - queue.addLast(frame) - } - - fun isEmpty(): Boolean = queue.isEmpty() - - /** The next frame to draw, or null when the queue is empty and the last frame should persist. */ - fun next(): Spectrum? = queue.removeFirstOrNull() - - companion object { - // ~1.5 s of 1024-sample hops at 44.1 kHz: room for any realistic decoder buffer without - // letting a stalled UI accumulate an unbounded backlog. - const val DEFAULT_CAPACITY = 64 - } -} diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt index 98e40f7770..08f2afd6be 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrail.kt @@ -21,16 +21,20 @@ package com.vitorpamplona.amethyst.commons.audio /** - * The whole per-displayed-frame step of the spectrum visualizer: release queued frames as their - * audio time comes due, apply the decay trail against what is already on screen, and return a + * The per-displayed-frame step of the spectrum visualizer: queue incoming frames, release each as + * its audio time comes due, apply the decay trail against what is already on screen, and return a * fresh array to draw. * - * Frames arrive in clusters (see [SpectrumPacer]). Releasing one per display refresh drained a - * ~15-frame cluster in ~110 ms at 120 Hz and then froze until the next one — measured as ~3 stalls - * of ~210 ms every second. Each frame instead stays up for the [Spectrum.durationNanos] of audio it - * describes, so a cluster spreads across the gap to the next. Time lost while starved is not owed - * back: when frames return after a gap they resume from the current frame time rather than being - * dumped at once to catch up. + * Why pacing is needed at all: the audio pipeline fills its output buffer in chunks, so frames reach + * the UI in clusters — ~15 frames roughly every 330 ms, measured on a Pixel 9a. Drawn on arrival a + * cluster collapses into one frame; released one per display refresh it plays out in ~110 ms at + * 120 Hz and then freezes (161 stalls averaging 211 ms over 52.8 s). Each frame instead stays up for + * the [Spectrum.durationNanos] of audio it describes, so a cluster spreads across the gap to the + * next. Time lost while starved is not owed back: after a gap, frames resume from the current frame + * time rather than being dumped at once to catch up. + * + * The backlog is capped at [capacity], evicting the stalest frame, so faster-than-real-time playback + * or a stalled UI cannot leave the picture drifting ever further behind the sound. * * The decay gives each bin an instant attack and a gradual release, so bars snap up to a transient * and fall back smoothly instead of flickering. @@ -42,17 +46,23 @@ package com.vitorpamplona.amethyst.commons.audio */ class SpectrumTrail( private val decay: Float, - capacity: Int = MAX_BACKLOG_FRAMES, + private val capacity: Int = MAX_BACKLOG_FRAMES, ) { - private val pacer = SpectrumPacer(capacity) + private val queue = ArrayDeque(capacity) private var drawn = FloatArray(0) // When the frame at the head of the queue may be drawn, in the caller's frame-clock nanos. private var dueNanos = Long.MIN_VALUE private var starved = true - /** Queues a freshly decoded frame, evicting the stalest if the backlog is at capacity. */ - fun offer(frame: Spectrum) = pacer.offer(frame) + /** Queues a freshly decoded frame, evicting the stalest if the backlog is at [capacity]. */ + fun offer(frame: Spectrum) { + while (queue.size >= capacity) queue.removeFirst() + queue.addLast(frame) + } + + /** True when nothing is queued, so the caller can stop polling until the next [offer]. */ + fun isEmpty(): Boolean = queue.isEmpty() /** * The array to draw at [frameTimeNanos], or null when nothing new is due and the current one @@ -62,7 +72,7 @@ class SpectrumTrail( * instance is what signals Compose to redraw. Do NOT switch to in-place mutation. */ fun nextOrNull(frameTimeNanos: Long): FloatArray? { - if (pacer.isEmpty()) { + if (queue.isEmpty()) { starved = true return null } @@ -74,11 +84,10 @@ class SpectrumTrail( var result: FloatArray? = null while (dueNanos <= frameTimeNanos) { - val frame = pacer.next() ?: break + val frame = queue.removeFirstOrNull() ?: break result = decayedFrom(frame) dueNanos += frame.durationNanos } - return result } @@ -95,8 +104,7 @@ class SpectrumTrail( companion object { // A cluster is ~15 frames, so 24 (~0.5 s of audio) never trims ordinary playback, while - // capping how far the picture can lag the sound when frames come faster than real time - // (2x playback speed). + // bounding how far the picture can lag the sound (e.g. at 2x playback speed). const val MAX_BACKLOG_FRAMES = 24 } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt deleted file mode 100644 index 5dbaf18950..0000000000 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumPacerTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.commons.audio - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class SpectrumPacerTest { - private fun frame(id: Float) = Spectrum(floatArrayOf(id)) - - private fun idOf(spectrum: Spectrum?) = spectrum?.bins?.first() - - @Test - fun drainsOneFramePerTickInArrivalOrder() { - val pacer = SpectrumPacer(capacity = 8) - pacer.offer(frame(1f)) - pacer.offer(frame(2f)) - pacer.offer(frame(3f)) - - assertEquals(1f, idOf(pacer.next())) - assertEquals(2f, idOf(pacer.next())) - assertEquals(3f, idOf(pacer.next())) - } - - @Test - fun returnsNullWhenStarvedSoTheCallerCanHoldTheLastFrame() { - val pacer = SpectrumPacer(capacity = 8) - assertNull(pacer.next()) - - pacer.offer(frame(1f)) - assertEquals(1f, idOf(pacer.next())) - assertNull(pacer.next()) - } - - @Test - fun overCapacityDropsTheStalestFrameNotTheNewest() { - val pacer = SpectrumPacer(capacity = 3) - pacer.offer(frame(1f)) - pacer.offer(frame(2f)) - pacer.offer(frame(3f)) - pacer.offer(frame(4f)) // evicts 1 - - assertEquals(2f, idOf(pacer.next())) - assertEquals(3f, idOf(pacer.next())) - assertEquals(4f, idOf(pacer.next())) - assertNull(pacer.next()) - } -} diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt index 39a73cb261..8a6ffab71a 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumTrailTest.kt @@ -22,18 +22,12 @@ package com.vitorpamplona.amethyst.commons.audio import kotlin.test.Test import kotlin.test.assertContentEquals +import kotlin.test.assertFalse import kotlin.test.assertNotSame import kotlin.test.assertNull +import kotlin.test.assertTrue -/** - * The per-displayed-frame step of the visualizer: release queued frames as their audio time comes - * due, apply the decay trail against what is drawn, and hand back a fresh array. - * - * Frames reach the UI in clusters (~15 at a time, ~3 times a second, measured on a Pixel 9a) because - * the audio pipeline fills its output buffer in chunks. Releasing one per display refresh drained a - * cluster in ~110 ms at 120 Hz and then froze for ~220 ms. Frames must instead be released at the - * rate of the audio they describe, so a cluster spreads across the gap to the next one. - */ +/** Pacing, starvation, backlog and decay rules of [SpectrumTrail]; see its KDoc for why each exists. */ class SpectrumTrailTest { private val ms = 1_000_000L @@ -171,4 +165,19 @@ class SpectrumTrailTest { // mutableStateOf compares by reference; reusing one array would never trigger a redraw. assertNotSame(first, second) } + + @Test + fun reportsEmptyOnlyOnceEveryQueuedFrameIsReleasedSoTheCallerKnowsWhenToPark() { + val trail = SpectrumTrail(decay = 0f) + assertTrue(trail.isEmpty()) + + trail.offer(frame(1f)) + trail.offer(frame(2f)) + assertFalse(trail.isEmpty()) + + trail.nextOrNull(0) + assertFalse(trail.isEmpty()) // frame 2 is queued but not yet due + trail.nextOrNull(20 * ms) + assertTrue(trail.isEmpty()) + } } diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt index 56a2ec66d9..a89eade52f 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/audio/SpectrumCanvas.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.withFrameMillis import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.drawscope.DrawScope +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow /** @@ -36,10 +37,9 @@ import kotlinx.coroutines.flow.Flow * then calls [draw] inside the Canvas draw lambda. The fast-changing state is read * ONLY in the draw lambda, so new frames trigger the draw phase, never recomposition. * - * Frames are released through [SpectrumTrail] as the audio they describe comes due — they arrive - * in clusters, and neither dumping a cluster into state nor draining it one per vsync looks live. - * The pacing loop runs every frame but only writes state when a frame is due, so the redraw rate - * tracks the ~43-47 Hz the fft produces rather than the display. + * Frames are released through [SpectrumTrail] as the audio they describe comes due. The pacing loop + * only writes state when a frame is due, and parks entirely while nothing is queued (paused, idle), + * so on its own it redraws at the ~43-47 Hz the fft produces and costs nothing when silent. * * Pass [animated] = false for non-time-varying styles (bars, radial): that drops the monotonic clock, * whose whole purpose is to redraw every frame even when the spectrum has not moved. @@ -55,21 +55,26 @@ fun SpectrumCanvas( ) { val smoothed = remember { mutableStateOf(FloatArray(0)) } - // Frames arrive in clusters, so they are queued and released as their audio time comes due. - // Writing a cluster straight into `smoothed` collapses it into one draw; releasing one per vsync - // races through it and then freezes. Pacing and decay live in SpectrumTrail so they are testable - // without a Compose harness. val trail = remember(spectrum, decay) { SpectrumTrail(decay) } + val arrivals = remember(trail) { Channel(Channel.CONFLATED) } LaunchedEffect(spectrum, trail) { - spectrum.collect { trail.offer(it) } + spectrum.collect { + trail.offer(it) + arrivals.trySend(Unit) + } } LaunchedEffect(trail) { while (true) { - val frameTimeNanos = withFrameNanos { it } - // Null means nothing new is due. Frames cover ~21 ms of audio against an 8-16 ms refresh, - // so this is the common case: hold what is drawn rather than redrawing identical bins. - smoothed.value = trail.nextOrNull(frameTimeNanos) ?: continue + val next = trail.nextOrNull(withFrameNanos { it }) + if (next != null) { + smoothed.value = next + } else if (trail.isEmpty()) { + // Park off the frame clock until the next offer. This must follow a nextOrNull that + // saw the empty queue: that call records the starvation which stops the next cluster + // from being dumped at once to "catch up". + arrivals.receive() + } } } From 9af317786fda45fc29d7cb5c62a525437de3e37c Mon Sep 17 00:00:00 2001 From: davotoula Date: Sun, 27 Sep 2026 10:13:16 +0200 Subject: [PATCH 12/19] fix(strings): turn count-bearing strings into plurals Fifteen commonsUI strings put a count next to a noun or verb that agrees with it ("%1$d groups moved", "%1$d caches here", "Search %1$s people"). As a they only have one form, which Czech, Polish and similar languages cannot express. cyberspace_bag_dropped worked around it with a hand-rolled _many sibling chosen by `== 1`, which is wrong for any language whose `one` category covers more than 1. Convert them to and resolve them with the plural helpers. The existing hu, hi and pl translations are carried across with their full category sets (pl gets one/few/many/other). geocache_map_pin_count had no call site and is removed. --- .../amethyst/ui/note/types/CyberspaceBag.kt | 17 ++-- .../backups/BackupConflictEventViews.kt | 3 +- .../backups/BackupConflictListViews.kt | 2 +- .../cordnGroup/CordnCreateGroupScreen.kt | 6 +- .../loggedIn/geocaches/GeocacheHuntRow.kt | 3 +- .../detail/GeocacheAddToHuntSheet.kt | 3 +- .../loggedIn/geohash/GeocachesHereChip.kt | 4 +- .../qrcode/scanner/QrScannerOverlay.kt | 3 +- .../settings/cordn/CordnCoordinatorsScreen.kt | 5 +- .../settings/cordn/CordnMigrateScreen.kt | 3 +- .../values-hi-rIN/strings.xml | 47 ++++++++--- .../values-hu-rHU/strings.xml | 47 ++++++++--- .../values-pl-rPL/strings.xml | 65 +++++++++++++--- .../composeResources/values/strings.xml | 77 +++++++++++++++---- .../commons/cordn/ui/CoordinatorHealthRow.kt | 3 +- 15 files changed, 213 insertions(+), 75 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CyberspaceBag.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CyberspaceBag.kt index 6249d38fed..1490ae1cde 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CyberspaceBag.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CyberspaceBag.kt @@ -50,7 +50,6 @@ import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_cancel import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_damaged import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_destination import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_dropped -import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_dropped_many import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_empty import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_measuring import com.vitorpamplona.amethyst.commons.resources.cyberspace_bag_no_hint @@ -80,6 +79,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource /** @@ -242,7 +242,7 @@ private fun Contents( null -> Footnote(stringResource(Res.string.cyberspace_bag_damaged)) is CyberspaceBagContents.Opaque -> - Footnote(asText(contents.bytes) ?: stringResource(Res.string.cyberspace_bag_opaque, contents.bytes.size)) + Footnote(asText(contents.bytes) ?: pluralStringResource(Res.plurals.cyberspace_bag_opaque, contents.bytes.size, contents.bytes.size)) is CyberspaceBagContents.Items -> { if (contents.items.isEmpty() && contents.dropped == 0) { @@ -256,13 +256,7 @@ private fun Contents( // still worth saying that something was thrown away. if (contents.dropped > 0) { Spacer(Modifier.height(6.dp)) - Footnote( - if (contents.dropped == 1) { - stringResource(Res.string.cyberspace_bag_dropped, contents.dropped) - } else { - stringResource(Res.string.cyberspace_bag_dropped_many, contents.dropped) - }, - ) + Footnote(pluralStringResource(Res.plurals.cyberspace_bag_dropped, contents.dropped, contents.dropped)) } } } @@ -322,9 +316,10 @@ private fun Footnote(text: String) { private fun outOfReach(estimateMillis: Long): String { val minutes = estimateMillis / 60_000L return if (minutes >= MINUTES_PER_HOUR) { - stringResource(Res.string.cyberspace_bag_too_slow_hours, (minutes / MINUTES_PER_HOUR).toInt()) + val hours = (minutes / MINUTES_PER_HOUR).toInt() + pluralStringResource(Res.plurals.cyberspace_bag_too_slow_hours, hours, hours) } else { - stringResource(Res.string.cyberspace_bag_too_slow_minutes, minutes.toInt()) + pluralStringResource(Res.plurals.cyberspace_bag_too_slow_minutes, minutes.toInt(), minutes.toInt()) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt index 1916f09286..b348bb27a9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt @@ -144,6 +144,7 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.elements.BannerImage +import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.LoadRelayGroupChannel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser @@ -420,7 +421,7 @@ private fun PeopleSearch( singleLine = true, shape = RoundedCornerShape(14.dp), leadingIcon = { Icon(symbol = MaterialSymbols.Search, contentDescription = null) }, - placeholder = { Text(stringRes(Res.string.backup_review_search_people, count.toString())) }, + placeholder = { Text(pluralStringRes(Res.plurals.backup_review_search_people, count, count.toString())) }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt index 19d694dd09..9be726a715 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt @@ -754,7 +754,7 @@ private fun LazyListScope.trustProviderItems( Icon(symbol = MaterialSymbols.Group, contentDescription = null, tint = tones.changed) } Column { - Text(stringRes(Res.string.backup_review_services_changed, affected.toString(), services.size.toString()), fontSize = 24.sp, lineHeight = 28.sp, fontWeight = FontWeight.Bold) + Text(pluralStringResource(Res.plurals.backup_review_services_changed, services.size, affected.toString(), services.size.toString()), fontSize = 24.sp, lineHeight = 28.sp, fontWeight = FontWeight.Bold) Text(stringRes(Res.string.backup_review_services_changed_explainer), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.placeholderText) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnCreateGroupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnCreateGroupScreen.kt index 6479e51eb5..d1d0f28e89 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnCreateGroupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/cordnGroup/CordnCreateGroupScreen.kt @@ -387,7 +387,7 @@ fun CordnCreateGroupScreen( if (draft.showStale) { stringRes(Res.string.cordn_coordinators_hide_older) } else { - stringRes(Res.string.cordn_coordinators_show_older, stale.size) + pluralStringRes(Res.plurals.cordn_coordinators_show_older, stale.size, stale.size) }, ) } @@ -462,7 +462,7 @@ fun CordnCreateGroupScreen( // different answers and only one of them is final. // Reporting the first for the second sends someone // off to paste a pubkey by hand over a timeout. - stringRes(Res.string.cordn_coordinators_discover_unheard, result.unreachable.size) + pluralStringRes(Res.plurals.cordn_coordinators_discover_unheard, result.unreachable.size, result.unreachable.size) }, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, @@ -927,7 +927,7 @@ private fun CoordinatorSummary( text = when { coverage == null || !coverage.answered -> stringRes(Res.string.cordn_create_coverage_unknown) - reached == rosterSize -> stringRes(Res.string.cordn_create_coverage_all, rosterSize) + reached == rosterSize -> pluralStringRes(Res.plurals.cordn_create_coverage_all, rosterSize, rosterSize) else -> stringRes(Res.string.cordn_create_coverage_partial, reached ?: 0, rosterSize) }, style = MaterialTheme.typography.labelSmall, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/GeocacheHuntRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/GeocacheHuntRow.kt index 0f1ff285f3..6bb8654ded 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/GeocacheHuntRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/GeocacheHuntRow.kt @@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_caches import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_progress import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.rememberGeocachePalette +import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -119,7 +120,7 @@ fun GeocacheHuntRow( Text( text = stringRes(Res.string.geocache_hunt_progress, doneCount, caches.size) + - " · " + stringRes(Res.string.geocache_hunt_caches, caches.size), + " · " + pluralStringRes(Res.plurals.geocache_hunt_caches, caches.size, caches.size), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt index fa89098694..48fb28b80e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt @@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_already_on import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_caches import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_new import com.vitorpamplona.amethyst.commons.resources.geocache_hunt_none_yet +import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Address @@ -178,7 +179,7 @@ fun GeocacheAddToHuntSheet( if (already) { stringRes(Res.string.geocache_hunt_already_on) } else { - stringRes(Res.string.geocache_hunt_caches, hunt.geocaches().size) + hunt.geocaches().size.let { pluralStringRes(Res.plurals.geocache_hunt_caches, it, it) } }, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeocachesHereChip.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeocachesHereChip.kt index 744cc2c59d..a279748574 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeocachesHereChip.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeocachesHereChip.kt @@ -48,7 +48,7 @@ import com.vitorpamplona.amethyst.commons.resources.geocache_caches_here import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.rememberGeocachePalette -import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.geocaches.dal.GeocachesHereFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.geocaches.datasource.GeocachesFilterAssemblerSubscription @@ -121,7 +121,7 @@ private fun GeocachesHereCount( tint = palette.live, ) Text( - text = stringRes(Res.string.geocache_caches_here, count), + text = pluralStringRes(Res.plurals.geocache_caches_here, count, count), style = MaterialTheme.typography.labelMedium, fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSurface, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt index 5de6999b6b..3a04b0a27e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt @@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.resources.qr_scanner_sequence_progress import com.vitorpamplona.amethyst.commons.resources.qr_scanner_torch_off import com.vitorpamplona.amethyst.commons.resources.qr_scanner_torch_on import com.vitorpamplona.amethyst.commons.resources.qr_scanner_zoom_reset +import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import kotlin.math.max @@ -255,7 +256,7 @@ private fun HintStack( val message = when { state.notice != null -> state.notice - progress != null -> stringRes(Res.string.qr_scanner_sequence_progress, progress.first, progress.second) + progress != null -> pluralStringRes(Res.plurals.qr_scanner_sequence_progress, progress.second, progress.first, progress.second) state.candidates.size > 1 -> stringRes(Res.string.qr_scanner_pick_one) state.isDark && !state.torchOn -> stringRes(Res.string.qr_scanner_dark_hint) else -> null diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt index cdae46aa2a..d59da9ba7e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnCoordinatorsScreen.kt @@ -114,6 +114,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.timeAgoNoDot import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness import com.vitorpamplona.amethyst.model.cordn.CordnRuntime import com.vitorpamplona.amethyst.ui.note.UserPicture +import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.cordnGroup.CopyableKeyRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsSection @@ -444,7 +445,7 @@ private fun HealthLine(state: CoordinatorHealth.State) { val text = when { state.isUnknown -> stringRes(Res.string.cordn_coordinators_health_unknown) - state.isDown -> stringRes(Res.string.cordn_coordinators_health_down, state.consecutiveFailures) + state.isDown -> pluralStringRes(Res.plurals.cordn_coordinators_health_down, state.consecutiveFailures, state.consecutiveFailures) state.consecutiveFailures > 0 -> stringRes(Res.string.cordn_coordinators_health_retrying) else -> stringRes(Res.string.cordn_coordinators_health_ok) } @@ -535,7 +536,7 @@ private fun DiscoverCoordinators( } else { // "Nobody is announcing" and "we were not told" are // different answers and only one of them is final. - stringRes(Res.string.cordn_coordinators_discover_unheard, found.unreachable.size) + pluralStringRes(Res.plurals.cordn_coordinators_discover_unheard, found.unreachable.size, found.unreachable.size) }, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt index 6c55c2bbb0..299e0c9c97 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/cordn/CordnMigrateScreen.kt @@ -80,6 +80,7 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.model.cordn.AndroidCordnBlobStore import com.vitorpamplona.amethyst.model.cordn.CordnRuntime +import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.QrCodeDrawer import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner @@ -351,7 +352,7 @@ private fun ReceiveSide( modifier = Modifier.fillMaxWidth(), ) - done?.let { Text(stringRes(Res.string.cordn_migrate_done, it), style = MaterialTheme.typography.bodyMedium) } + done?.let { Text(pluralStringRes(Res.plurals.cordn_migrate_done, it, it), style = MaterialTheme.typography.bodyMedium) } error?.let { Text(it, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.error) } Button( diff --git a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml index ab38725872..d4f890da43 100644 --- a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml @@ -206,7 +206,10 @@ नई खोज… आपने कोई खोज नहीं की अब तक। पहले से इस खोज में लगे - %1$d कोष यहाँ + + %1$d कोष यहाँ + %1$d कोष यहाँ + इस चित्र को हटाएँ इस कोष को संगृहीत कर दिया गया। यह अब खेल में नहीं है। सर्वप्रथम खोज का कथन %1$s द्वारा @@ -265,7 +268,6 @@ मुझ पर मध्यीकरण मानचित्र पर दीर्घ दबाएँ वहाँ एक कोष छिपाने के लिए खोलें - %1$d कोष कहाँ क्या अतिरिक्त @@ -293,7 +295,10 @@ कोष कहाँ छिपाया जाए चयन करें। %2$d में से %1$d प्राप्त खोज आरम्भ करें - %1$d कोष + + %1$d कोष + %1$d कोष + शीर्षक विवरण ध्वज चित्र @@ -342,7 +347,10 @@ उस चित्र में से कोई क्यूआर॰ चित्र अक्षरराशि प्राप्त नहीं टाँकाफलक में कुछ भी नहीं परखने के लिए अक्षरराशि का चयन करें - %2$s में से %1$s भाग प्राप्त। आगे बढें + + %2$s में से %1$s भाग प्राप्त। आगे बढें + %2$s में से %1$s भाग प्राप्त। आगे बढें + विस्तारण पुनःस्थापना अमेथिस्ट इस अक्षरराशि को नहीं खोल सकता इस अक्षरराशि में एक निजी कुंचिका अथवा जोडनेवाला रहस्य है। इसलिए इसकी विषयवस्तु यहाँ दिखाया नहीं जाता। अनुकूल पटल का उपयोग करें। @@ -5253,16 +5261,27 @@ रोकें खोज का मूल्यांकन इस यन्त्र पर… खोज सम्पन्न %2$s में से %1$s क्षेत्रों में - अप्राप्य। लगभग %1$d मिनुट का खोज इस यन्त्र पर - अप्राप्य। लगभग %1$d घण्टों का खोज इस यन्त्र पर + + अप्राप्य। लगभग %1$d मिनुट का खोज इस यन्त्र पर + अप्राप्य। लगभग %1$d मिनुट का खोज इस यन्त्र पर + + + अप्राप्य। लगभग %1$d घण्टों का खोज इस यन्त्र पर + अप्राप्य। लगभग %1$d घण्टों का खोज इस यन्त्र पर + सहायक संकेत में नामित पेटिका में नहीं क्षेत्र प्राप्त। पर विषयवस्तु पढने में असफल - %1$d वस्तु जो यहाँ छिपाया गया था उसके हस्ताक्षर से मेल नहीं तथा परित्यक्त - %1$d वस्तु जो यहाँ छिपाए गए थे उनके हस्ताक्षरों से मेल नहीं तथा परित्यक्त + + %1$d वस्तु जो यहाँ छिपाया गया था उसके हस्ताक्षर से मेल नहीं तथा परित्यक्त + %1$d वस्तु जो यहाँ छिपाए गए थे उनके हस्ताक्षरों से मेल नहीं तथा परित्यक्त + खोला। पर भीतर कुछ नहीं था हस्ताक्षररहित। इसलिए यह कर्ता केवल एक कथन है प्रकार %1$d का वस्तु। जिसे यह ग्राहक चित्रित नहीं करता - %1$d अष्टकों का अमुक जिसे यह ग्राहक पढता नहीं + + %1$d अष्टकों का अमुक जिसे यह ग्राहक पढता नहीं + %1$d अष्टकों का अमुक जिसे यह ग्राहक पढता नहीं + पुनःबाधित करें %1$s को रखें %1$s @@ -5501,13 +5520,19 @@ पुनःनामकृत पुनःनामकृत इस यन्त्र पर - %1$s लोग खोजें + + %1$s लोग खोजें + %1$s लोग खोजें + खोज मध्य सेवा अनुचर श्रेणी विषयसूची - %2$s में से %1$s सेवाएँ परिवर्तित + + %2$s में से %1$s सेवाएँ परिवर्तित + %2$s में से %1$s सेवाएँ परिवर्तित + वे अब अन्य व्यक्ति से आते है। अथवा किसी से नहीं अभी भी बाधित अभिलेखन मध्य diff --git a/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml b/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml index bc0ba670c6..04d81c7de6 100644 --- a/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-hu-rHU/strings.xml @@ -206,7 +206,10 @@ Új vadászat… Még nem hozott létre vadászatot. Már szerepel ebben a vadászatban - %1$d láda van itt + + %1$d láda van itt + %1$d láda van itt + Fénykép eltávolítása Ezt a ládát archiválták. Már nem játszik. Az első megtalálást %1$s szerezte meg @@ -265,7 +268,6 @@ Középre Egy láda elrejtéséhez nyomja meg hossza a térképet Megnyitás - %1$d láda Hol Mi Extrák @@ -293,7 +295,10 @@ Válassza ki, hogy hol van elrejtve a láda. %2$d / %1$d megtalálva Vadászat indítása - %1$d láda + + %1$d láda + %1$d láda + Cím Leírás Borítókép @@ -342,7 +347,10 @@ Nem található QR-kód a képen A vágólapon nincs semmi a beolvasáshoz Érintse meg a beolvasandó kódot - %1$s darabot sikerült beolvasni a(z) %2$s darab közül, folytatás + + %1$s darabot sikerült beolvasni a(z) %2$s darab közül, folytatás + %1$s darabot sikerült beolvasni a(z) %2$s darab közül, folytatás + Nagyítás visszaállítása Az Amethyst nem tudja megnyitni ezt a kódot Ez a kód egy titkos kulcsot vagy párosítási titkot tartalmaz, ezért a tartalma itt nem jelenik meg. Használja azt a képernyőt, ahol meg kell adni. @@ -5253,16 +5261,27 @@ Leállítás A keresés árazása ezen az eszközön… %2$s régióból %1$s átkutatva - Elérhetetlen: körülbelül %1$d perc keresés ezen az eszközön - Elérhetetlen: körülbelül %1$d óra keresés ezen az eszközön + + Elérhetetlen: körülbelül %1$d perc keresés ezen az eszközön + Elérhetetlen: körülbelül %1$d perc keresés ezen az eszközön + + + Elérhetetlen: körülbelül %1$d óra keresés ezen az eszközön + Elérhetetlen: körülbelül %1$d óra keresés ezen az eszközön + Nincs abban a dobozban, amelyet a nyom megnevezett A régió megvan, de a tartalmát nem sikerült beolvasni - Az itt elrejtett %1$d elem nem egyezett az aláírásával, ezért el lett vetve - Az itt elrejtett %1$d elem nem egyezett az aláírásával, ezért el lettek vetve + + Az itt elrejtett %1$d elem nem egyezett az aláírásával, ezért el lett vetve + Az itt elrejtett %1$d elem nem egyezett az aláírásával, ezért el lettek vetve + Kinyitva, és nem volt benne semmi Aláírás nélküli, így ez a szerző csak állítás Egy %1$d típusú elem, amelyet ez a kliens nem jelenít meg - %1$d bájt valamiből, amit ez a kliens nem olvas + + %1$d bájt valamiből, amit ez a kliens nem olvas + %1$d bájt valamiből, amit ez a kliens nem olvas + %1$s újbóli letiltása %1$s megtartása @@ -5501,13 +5520,19 @@ átnevezte Átnevezve Ezen az eszközön - %1$s személy keresése + + %1$s személy keresése + %1$s személy keresése + Keresés Szolgáltatás Követők Rang Témák - %2$s szolgáltatásból %1$s módosult + + %2$s szolgáltatásból %1$s módosult + %2$s szolgáltatásból %1$s módosult + Most már valaki mástól vagy senkitől sem származnak Továbbra is le van tiltva Tárolás diff --git a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml index ccd2e12d38..1166642559 100644 --- a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml @@ -214,7 +214,12 @@ Nowe polowanie… Nie dokonałeś jeszcze polowania. Już podczas tego polowania - %1$d skrzynek tutaj + + %1$d skrzynka tutaj + %1$d skrzynki tutaj + %1$d skrzynek tutaj + %1$d skrzynki tutaj + Usuń to zdjęcie Ta skrzynka została zarchiwizowana. Nie jest już w grze. Pierwszy, który znalazł – zgłoszone przez %1$s @@ -273,7 +278,6 @@ Wyśrodkuj na mnie Naciśnij i przytrzymaj mapę, aby ukryć skrzynkę Otwórz - %1$d skrzynek Gdzie Co Dodatki @@ -301,7 +305,12 @@ Wybierz gdzie skrzynka jest ukryta. Znaleziono %1$d z %2$d Rozpocznij polowanie - %1$d skrzynek + + %1$d skrzynka + %1$d skrzynki + %1$d skrzynek + %1$d skrzynki + Tytuł Opis Obraz banera @@ -352,7 +361,12 @@ Nie znaleziono kodu QR na tym obrazku Nic w schowku do skanowania Wybierz żądany kod - Zdobyto %1$s z %2$s części — kontynuuj + + Zdobyto %1$s z %2$s części — kontynuuj + Zdobyto %1$s z %2$s części — kontynuuj + Zdobyto %1$s z %2$s części — kontynuuj + Zdobyto %1$s z %2$s części — kontynuuj + Zresetuj zoom Ametyst nie może otworzyć tego kodu Ten kod zawiera klucz prywatny lub hasło parowania, dlatego jego treść nie jest tutaj wyświetlana. Należy skorzystać z odpowiedniego ekranu, na którym należy go wprowadzić. @@ -5475,16 +5489,35 @@ Stop Wycena wyszukiwania na tym urządzeniu… Przeszukano %1$s z %2$s regionów - Poza zasięgiem: około %1$d minut wyszukiwania na tym urządzeniu - Poza zasięgiem: około %1$d godzin wyszukiwania na tym urządzeniu + + Poza zasięgiem: około %1$d minuty wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d minut wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d minut wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d minuty wyszukiwania na tym urządzeniu + + + Poza zasięgiem: około %1$d godziny wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d godzin wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d godzin wyszukiwania na tym urządzeniu + Poza zasięgiem: około %1$d godziny wyszukiwania na tym urządzeniu + W pudełku nie ma podpowiedzi o nazwie Znaleziono region, ale nie udało się odczytać jego zawartości - %1$d element ukryty w tym miejscu nie pasował do swojej sygnatury podpisu i został usunięty - %1$d ukrytych tutaj elementów nie pasowało do swoich sygnatur i zostało usuniętych + + %1$d element ukryty w tym miejscu nie pasował do swojej sygnatury podpisu i został usunięty + %1$d elementy ukryte tutaj nie pasowały do swoich sygnatur i zostały usunięte + %1$d ukrytych tutaj elementów nie pasowało do swoich sygnatur i zostało usuniętych + %1$d ukrytego tutaj elementu nie pasowało do swoich sygnatur i zostało usuniętych + Otworzyłem, a w środku nic nie było Brak podpisu, więc autor to tylko domysł Element typu %1$d, którego ten klient nie wyświetla - %1$d bajtów danych, których ten klient nie odczytuje + + %1$d bajt danych, których ten klient nie odczytuje + %1$d bajty danych, których ten klient nie odczytuje + %1$d bajtów danych, których ten klient nie odczytuje + %1$d bajta danych, których ten klient nie odczytuje + Zablokuj %1$s ponownie Zachowaj %1$s @@ -5783,13 +5816,23 @@ zmieniono nazwę Zmieniono nazwę Na tym urządzeniu - Szukaj %1$s osób + + Szukaj %1$s osoby + Szukaj %1$s osób + Szukaj %1$s osób + Szukaj %1$s osoby + Szukam Serwis Obserwujący Ranking Tematy - Zmieniono %1$s z %2$s serwisów + + Zmieniono %1$s z %2$s serwisu + Zmieniono %1$s z %2$s serwisów + Zmieniono %1$s z %2$s serwisów + Zmieniono %1$s z %2$s serwisu + Teraz pochodzą od kogoś innego albo od nikogo Nadal zablokowana Przechowywanie diff --git a/commonsUI/src/commonMain/composeResources/values/strings.xml b/commonsUI/src/commonMain/composeResources/values/strings.xml index e1a0e9cd1e..3782cc7eb2 100644 --- a/commonsUI/src/commonMain/composeResources/values/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values/strings.xml @@ -219,7 +219,10 @@ New hunt… You have not made a hunt yet. Already on this hunt - %1$d caches here + + %1$d cache here + %1$d caches here + Remove this photo This cache has been archived. It is no longer in play. First to find claimed by %1$s @@ -278,7 +281,6 @@ Centre on me Long-press the map to hide a cache there Open - %1$d caches Where What Extras @@ -306,7 +308,10 @@ Pick where the cache is hidden. %1$d of %2$d found Start the hunt - %1$d caches + + %1$d cache + %1$d caches + Title Description Banner image @@ -355,7 +360,10 @@ No QR code found in that picture Nothing on the clipboard to scan Tap the code you want - Captured %1$s of %2$s parts — keep going + + Captured %1$s of %2$s part — keep going + Captured %1$s of %2$s parts — keep going + Reset zoom Amethyst can't open this code This code carries a private key or a pairing secret, so its contents are not shown here. Use the screen that expects it. @@ -5324,7 +5332,10 @@ Not contacted yet Responding Not responding - %1$d failed attempts in a row + + %1$d failed attempt in a row + %1$d failed attempts in a row + Paste a cordn1… link someone shared with you to see which coordinator carries that group, and what that operator would learn about you if you joined. cordn1… @@ -5420,7 +5431,10 @@ Bring the groups here Fetching… This replaces any cordn groups already on this phone. They cannot be merged. - %1$d groups moved + + %1$d group moved + %1$d groups moved + There are no cordn groups on this device to move. Set a media server first — the encrypted documents need somewhere to sit while the other phone fetches them. What leaves this device @@ -5446,16 +5460,27 @@ Stop Pricing the search on this device… Searched %1$s of %2$s regions - Out of reach: about %1$d minutes of searching on this device - Out of reach: about %1$d hours of searching on this device + + Out of reach: about %1$d minute of searching on this device + Out of reach: about %1$d minutes of searching on this device + + + Out of reach: about %1$d hour of searching on this device + Out of reach: about %1$d hours of searching on this device + Not in the box the hint named Found the region, but the contents could not be read - %1$d item hidden here did not match its signature and was dropped - %1$d items hidden here did not match their signatures and were dropped + + %1$d item hidden here did not match its signature and was dropped + %1$d items hidden here did not match their signatures and were dropped + Opened, and there was nothing inside Unsigned, so this author is a claim An item of kind %1$d, which this client does not draw - %1$d bytes of something this client does not read + + %1$d byte of something this client does not read + %1$d bytes of something this client does not read + Close Copy link @@ -5800,13 +5825,19 @@ renamed Renamed On this device - Search %1$s people + + Search %1$s person + Search %1$s people + Searching Service Followers Rank Topics - %1$s of %2$s services changed + + %1$s of %2$s service changed + %1$s of %2$s services changed + They now come from someone else, or no one Still blocked Storing @@ -5924,9 +5955,15 @@ Nobody is announcing on your relays. Announced %1$s ago Last announced %1$s - Nothing found, and %1$d of your relays did not answer. + + Nothing found, and %1$d of your relays did not answer. + Nothing found, and %1$d of your relays did not answer. + A coordinator orders one group's messages and is the only thing that can. Losing it loses the ordering, and a second one does not mirror the first — so these are named things you choose, not interchangeable relays. - Not answering — %1$d calls in a row have failed. Groups on it are not syncing. + + Not answering — %1$d call in a row has failed. Groups on it are not syncing. + Not answering — %1$d calls in a row have failed. Groups on it are not syncing. + Answering. A call failed; retrying. Nothing asked of it yet. @@ -5950,7 +5987,10 @@ Answered, but named nothing. Show all %1$d Show fewer - Show %1$d that stopped announcing + + Show %1$d that stopped announcing + Show %1$d that stopped announcing + These have not announced in over a month. Creating a group on one that has gone away will fail. Create group @@ -5972,7 +6012,10 @@ Another coordinator Coordinator public key (hex or npub) Relays it answers on, one per line - Reaches all %1$d of your people + + Reaches your %1$d person + Reaches all %1$d of your people + Reaches %1$d of %2$d — the rest would be left out Not checked — picking it will ask Description (optional) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/CoordinatorHealthRow.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/CoordinatorHealthRow.kt index a6770124d3..bcd12f79c7 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/CoordinatorHealthRow.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/cordn/ui/CoordinatorHealthRow.kt @@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.commons.resources.cordn_health_down import com.vitorpamplona.amethyst.commons.resources.cordn_health_failures import com.vitorpamplona.amethyst.commons.resources.cordn_health_ok import com.vitorpamplona.amethyst.commons.resources.cordn_health_unknown +import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource /** @@ -97,7 +98,7 @@ fun CoordinatorHealthRow( // deserves no words at all. if (state.isDown) { Text( - text = stringResource(Res.string.cordn_health_failures, state.consecutiveFailures), + text = pluralStringResource(Res.plurals.cordn_health_failures, state.consecutiveFailures, state.consecutiveFailures), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) From 6a3300be3541c1b26f87d94bfc4219e4706fb518 Mon Sep 17 00:00:00 2001 From: davotoula Date: Sun, 27 Sep 2026 10:28:15 +0200 Subject: [PATCH 13/19] chore(i18n): translate missing strings into cs, de, sv and pt-BR Fills every key that was absent from the four target locales: - commonsUI: 996 entries per locale (938 strings, 58 plurals), mostly the cordn, backup, geocache and browser features. - amethyst: the 4 platform-tier keys (QR share target and launcher shortcut labels, PoW "sending" notification). Plurals carry each locale's full category set (cs one/few/many/other). Pure format values (D%1$d, %1$02d:%2$02d, ...) are copied verbatim so the Crowdin upload seeds them for approval. --- amethyst/src/main/res/values-cs/strings.xml | 4 + .../src/main/res/values-de-rDE/strings.xml | 4 + .../src/main/res/values-pt-rBR/strings.xml | 4 + .../src/main/res/values-sv-rSE/strings.xml | 4 + .../composeResources/values-cs/strings.xml | 1286 +++++++++++++++++ .../values-de-rDE/strings.xml | 1170 +++++++++++++++ .../values-pt-rBR/strings.xml | 1170 +++++++++++++++ .../values-sv-rSE/strings.xml | 1170 +++++++++++++++ 8 files changed, 4812 insertions(+) diff --git a/amethyst/src/main/res/values-cs/strings.xml b/amethyst/src/main/res/values-cs/strings.xml index c56adcaead..7130fe54f3 100644 --- a/amethyst/src/main/res/values-cs/strings.xml +++ b/amethyst/src/main/res/values-cs/strings.xml @@ -377,4 +377,8 @@ Zvuk zapnutý. Klikněte pro ztlumení Hrát Pozastavit + Skenovat QR kód + Skenovat QR + Naskenovat QR kód + Odesílání na relé… diff --git a/amethyst/src/main/res/values-de-rDE/strings.xml b/amethyst/src/main/res/values-de-rDE/strings.xml index c46ed3312d..2e4d479622 100644 --- a/amethyst/src/main/res/values-de-rDE/strings.xml +++ b/amethyst/src/main/res/values-de-rDE/strings.xml @@ -349,4 +349,8 @@ Ton ein. Zur Stummschaltung drücken Abspielen Pausen + QR-Code scannen + QR scannen + Einen QR-Code scannen + Wird an Relays gesendet… diff --git a/amethyst/src/main/res/values-pt-rBR/strings.xml b/amethyst/src/main/res/values-pt-rBR/strings.xml index 53f568feea..af84b30244 100644 --- a/amethyst/src/main/res/values-pt-rBR/strings.xml +++ b/amethyst/src/main/res/values-pt-rBR/strings.xml @@ -349,4 +349,8 @@ Som ligado. Clique para silenciar Reproduzir Pausar + Escanear código QR + Ler QR + Escanear um código QR + Enviando para relays… diff --git a/amethyst/src/main/res/values-sv-rSE/strings.xml b/amethyst/src/main/res/values-sv-rSE/strings.xml index 310c1657a6..294ee30c6f 100644 --- a/amethyst/src/main/res/values-sv-rSE/strings.xml +++ b/amethyst/src/main/res/values-sv-rSE/strings.xml @@ -349,4 +349,8 @@ Ljud på. Klicka för att stänga av ljudet Spela Pausa + Skanna QR-kod + Skanna QR + Skanna en QR-kod + Skickar till reläer… diff --git a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml index 3f818c83b8..eb86dff2d2 100644 --- a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml @@ -5140,4 +5140,1290 @@ one-shot handoff rather than continuous sync. The exposure strings say what leaves the device; the fork strings say why the old phone stops. --> + Keš + Tradiční + Multi-keš + Mystery + Mikro + Malá + Normální + Velká + Jiná velikost + D%1$d + T%1$d + První nálezce + Umělecké dílo + Key Quest + Ověřené nálezy + Obsazeno + Archivováno + Nevíte si rady? Klepnutím zobrazíte nápovědu + Nečitelné? Klepnutím zobrazíte druhé čtení + Poblíž + Nalezeno! + Ověřený nález + Důkaz nesouhlasí + Načítání keše… + Ukryl %1$s + Navigovat + Kopírovat souřadnice + Souřadnice zkopírovány + Fotky + Mise Key Quest + Logy + Tuto keš zatím nikdo nezalogoval. Buďte první. + Nalezeno + Nenalezeno + Přidat poznámku + Nahlásit potřebu údržby + Přidat do výpravy + Nová výprava… + Zatím jste nevytvořili žádnou výpravu. + Už je v této výpravě + + %1$d keš zde + %1$d keše zde + %1$d keše zde + %1$d keší zde + + Odebrat tuto fotku + Tato keš byla archivována. Už není ve hře. + Prvním nálezcem se stal %1$s + Místo prvního nálezce je stále volné + Vlastník + Upravit tuto keš + Archivovat tuto keš + Archivovat. Nálezci uvidí, že už není ve hře, a listing zůstane čitelný. + Potvrdit prvního nálezce + Zveřejní tag F se jménem %1$s. Je to trvalé. + Zatím žádné ověřené nálezy, takže není koho potvrdit. + Zobrazit ověřovací kód + Vytiskněte ho a vložte do keše. Amethyst ho neukládá — pokud ho ztratíte, nálezci už nebudou moci prokázat, že tam byli. + Nenalezeno + Poznámka + Potřebuje údržbu + Zapsat nález + Keš + Váš log + Jak to šlo? + Přidat fotku + Vaše odpověď Key Quest + Důkaz přítomnosti + Naskenujte kód v keši + Naskenovat jiný kód + Ověřeno — byli jste u keše + Jednou podepsáno kódem z krabičky. Klíč se nikdy neukládá a nikdy neopustí tuto obrazovku. + To není ověřovací kód. + Tento kód patří jiné keši. + Tato keš nevyžaduje důkaz, takže ji můžete rovnou zalogovat. + K tomuto logu není připojena vaše vlastní poloha. + Odeslat + Přidat log + Poblíž + Mapa + Výpravy + Nálezy + Moje + Nenalezené + D ≤ %1$d + T ≤ %1$d + S cenou + Vyžaduje důkaz + Použít mou polohu + Seřazeno podle vzdálenosti + Nejnovější první + Ukrýt keš + Zatím tu nejsou žádné keše. Zkuste širší seznam sledovaných, nebo ukryjte první. + Zatím jste nezapsali žádný nález. + Zatím jste neukryli žádnou keš. + Zatím žádné výpravy. + Nálezy + První nálezy + Ověřené + Ukryté + Vycentrovat na mě + Dlouhým stiskem na mapě tam ukryjete keš + Otevřít + Kde + Co + Doplňky + Důkaz + Relaye + Tímto zveřejníte veřejnou polohu s přesností asi na 5 metrů. Uvidí ji kdokoli na Nostru a zveřejněnou událost nelze vzít zpět. + Vyberte místo + Zveřejní %1$s a k tomu hrubší buňky až po 3 znaky pro vyhledávání v okolí. + Název + Popis + Typ + Velikost + Obtížnost + Terén + Nápověda + Nálezci uvidí + Zašifrovat nápovědu (ROT13) + Mise Key Quest + Modifikátory + Vyžadovat důkaz přítomnosti + Pro tuto keš byl vygenerován ověřovací klíč. + Kam mají nálezci logovat + Zveřejnit + Pojmenujte keš. + Vyberte, kde je keš ukrytá. + Nalezeno %1$d z %2$d + Zahájit výpravu + + %1$d keš + %1$d keše + %1$d keše + %1$d keší + + Název + Popis + Obrázek banneru + Keše v pořadí + Přidat keš + Posunout nahoru + Posunout dolů + Odebrat + Motiv stránky + Styl mapy + Pojmenujte výpravu. + Přidejte alespoň jednu keš. + Zveřejnění se nezdařilo. Zkontrolujte připojení k relayům a zkuste to znovu. + Skupiny Cordn + Skupina s koordinátorem + Doručováno koordinátorem, ne relayi + Co tento koordinátor vidí + Koordinátor je jediný server, který přenáší všechny zprávy této skupiny, a to v pořadí. Je to jiný kompromis než u relayů — dobré to vědět, než s tím začnete zacházet jako s jakýmkoli jiným chatem. + Obsah zpráv + Kdo je ve skupině + Kdo co posílá + Nečitelné + Pod jednorázovým klíčem + Spojeno s vaším skutečným účtem + Podrobnosti + Koordinátor %1$s + Nejde o stejnou míru odhalení jako u skupiny Marmot, kde žádný jednotlivý provozovatel nedrží celou konverzaci. + Připojení uvádí skutečné účty na obou stranách, takže koordinátor ví, kdo je v této skupině. + Jeden jednorázový klíč odesílá a stahuje za všechny vaše skupiny zde, takže koordinátor pozná, že patří vám. + Jediný provozovatel drží úplnou, seřazenou historii každé skupiny, kterou přenáší. + Balíček klíčů, který jste zveřejnili, je podepsaný záznam o tom, že tento účet používá cordn, a koordinátor ho může předat komukoli. + Zprávy nejsou doplňovány na jednotnou délku, takže koordinátor vidí, jak dlouhá je každá z nich. + Tento klient nešifruje komunikaci s koordinátorem. Je to chyba — nahlaste ji prosím. + Zatím nekontaktován + Odpovídá + Neodpovídá + + %1$d neúspěšný pokus za sebou + %1$d neúspěšné pokusy za sebou + %1$d neúspěšného pokusu za sebou + %1$d neúspěšných pokusů za sebou + + Vložte odkaz cordn1…, který vám někdo poslal, a zjistěte, který koordinátor tuto skupinu přenáší a co by se o vás jeho provozovatel dozvěděl, kdybyste se připojili. + cordn1… + Prohlédnout + Vložit + Vymazat + To není odkaz na skupinu cordn: %1$s + ID skupiny + Koordinátor + Dostupné přes + Tento odkaz neuvádí žádného koordinátora, takže ho samotný nelze použít. Zeptejte se odesílatele, který koordinátor skupinu přenáší. + Prohlédnutím odkazu se k ničemu nepřipojíte a to, že ho máte, z vás nedělá člena. + Odkaz na skupinu Cordn + cordn koordinátor skupina odkaz pozvánka mls chat metadata soukromí odhalení coordinator group link invite privacy exposure + Koordinátoři Cordn + cordn koordinátor přidat odebrat vymazat stav server mls chat pořadí coordinator add remove purge health ordering + Balíčky klíčů Cordn + cordn balíček klíčů zveřejnit pozvánka záložní koordinátor mls key package publish invite last resort coordinator + Záloha Cordn + cordn záloha obnovení export import heslo skupiny obnova backup restore passphrase groups recovery + Skupina %1$s + Zatím žádné zprávy + Skupina Cordn přenášená přes %1$s + Zpráva smazána + Hlasová zpráva + Fotka + Video + Soubor + Máte pozvánku do skupiny cordn? + Zobrazit pozvánky + Cordn + Skupiny doručované koordinátorem, kterého si sami zvolíte, místo relayů. + Prohlédnout odkaz na skupinu + Zjistěte, kam odkaz cordn1… vede, dřív než s ním něco uděláte + Přesunout z tohoto telefonu + Převzít z jiného telefonu + Zveřejněné balíčky klíčů + Odkaz + Koordinátoři, které používáte + Ohlašující se koordinátoři + Přidat ručně + Heslo + Vytvořit zálohu + Obnovit ze zálohy + Koordinátor + Toto zařízení + Koordinátoři + Servery, které přenášejí vaše skupiny + Balíčky klíčů + Díky nim vás lidé mohou přidat do skupiny + Záloha a obnovení + Soubor zašifrovaný heslem, který si uschováte + Přesun na nový telefon + Předejte své skupiny zařízení, na které přecházíte + Přesun na nový telefon + Vaše skupiny cordn žijí na tomto zařízení. K jejich přesunu stačí jeden kód: tento telefon je zveřejní, nový telefon je načte a tento telefon s nimi přestane pracovat. + Nejprve se na novém telefonu přihlaste stejným účtem. Přesouvají se jen vaše skupiny, nikdy váš klíč. + Zahájit předání + Zveřejňování… + Naskenujte tento kód na novém telefonu + Kód je jen ukazatel. Kdo ho vyfotí, nic se nedozví — obsah, na který ukazuje, je zašifrovaný pro vás. + Toto zařízení předalo své skupiny + Přestalo odesílat a přijímat, protože dva telefony odesílající do jedné skupiny by ji rozbily pro všechny její členy. Vaše skupiny na tomto zařízení zůstávají a nic nebylo smazáno. + Vzít toto zařízení zpět + Použijte, pokud se přesun nedokončil. Udělejte to jen tehdy, když nový telefon ještě nezačal odesílat. + Přijmout z jiného telefonu + Naskenovat kód + nebo ho vložte + Přenést skupiny sem + Stahování… + Tímto nahradíte všechny skupiny cordn, které už na tomto telefonu jsou. Sloučit je nelze. + + Přesunuta %1$d skupina + Přesunuty %1$d skupiny + Přesunuto %1$d skupiny + Přesunuto %1$d skupin + + Na tomto zařízení nejsou žádné skupiny cordn k přesunu. + Nejprve nastavte mediální server — zašifrované dokumenty musí někde počkat, než si je druhý telefon stáhne. + Co opouští toto zařízení + Stav vašich skupin se zašifruje a nahraje na váš mediální server, aby si ho druhý telefon mohl stáhnout. Server ho nedokáže přečíst, vidí jen velikost a čas. Pokud nechcete nic zanechat, soubory potom smažte. + Smazat + Upravit + Úprava vaší zprávy + Přejít na zprávu + Připnout + Odpovědět + Odepnout + Tuto změnu nebylo možné provést. + Koordinátor neodpověděl, takže se nic nezměnilo. Zkuste to za chvíli znovu. + %1$s na tomto koordinátoru nezveřejnil balíček klíčů, takže ho zatím nelze přidat. Požádejte ho, aby na tomto koordinátoru nejprve otevřel cordn. + Ze skupiny cordn nemůžete odebrat sami sebe. + Pro %1$s se nepodařilo zajistit přístup do skupiny, takže přidání bylo zrušeno. + %1$s není v této skupině. + Tato osoba + Tento koordinátor poskytl balíček klíčů, který patří někomu jinému, takže %1$s nebyl přidán. Nic se nezměnilo. + Šifrovací klíče vašich skupin, vaše balíčky klíčů a všechny zprávy v těchto skupinách. Kdokoli soubor otevře, může konverzace číst. Jakmile soubor opustí Amethyst, chrání ho jen heslo. + Co soubor obsahuje + Váš nsec skupiny cordn neobnoví. Skupina cordn existuje jako šifrovací stav na tomto zařízení a jako seřazený proud zpráv na koordinátoru, který ho nedokáže přečíst — přijdete-li o zařízení bez zálohy, skupina je pryč, včetně všeho, co v ní už zaznělo. + Exportovat + Záloha uložena. + Nepovedlo se. Zkontrolujte heslo a soubor. + Heslo + Obnovit + Obnovení nahradí skupiny cordn na tomto zařízení těmi ze souboru. Je určeno pro telefon, který převzal roli jiného — ne k používání stejných skupin na dvou zařízeních zároveň. + Všechny skupiny cordn, které jsou nyní na tomto zařízení, budou odebrány a nahrazeny těmi ze souboru.\n\nNeobnovujte zálohu na druhý telefon, dokud se ten první stále používá. Oba by měly stejné klíče skupin, a jakmile oba odešlou zprávu, skupina se rozbije pro všechny — potichu a nevratně. + Nahradit skupiny cordn na tomto zařízení? + Obnovit + Obnoveno. + Vše, co sem pošlete, je zašifrováno pro tuto skupinu. Koordinátor to předává dál, aniž by to mohl přečíst. + Zatím žádné zprávy + Nové zprávy + Včera + Zpráva + Přidat + Tohoto koordinátora se nepodařilo otevřít. + Kopírovat npub koordinátora + Hledat koordinátory + Přidat + Čte oznámení, která už vaše relaye přenášejí. Žádný koordinátor není kontaktován, takže se žádný z nich nedozví, že jste hledali. + Oznámení se nepodařilo načíst + Na vašich relayích se nikdo neohlašuje. + Ohlášeno před %1$s + Naposledy ohlášeno %1$s + + Nic nenalezeno a %1$d z vašich relayů neodpověděl. + Nic nenalezeno a %1$d z vašich relayů neodpověděly. + Nic nenalezeno a %1$d z vašich relayů neodpovědělo. + Nic nenalezeno a %1$d z vašich relayů neodpovědělo. + + Koordinátor určuje pořadí zpráv jedné skupiny a nic jiného to dělat nemůže. Jeho ztrátou se ztratí i pořadí a druhý koordinátor ten první nezrcadlí — proto jde o konkrétní servery, které si zvolíte, ne o zaměnitelné relaye. + + Neodpovídá — %1$d požadavek za sebou selhal. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavky za sebou selhaly. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavku za sebou selhalo. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavků za sebou selhalo. Skupiny na něm se nesynchronizují. + + Odpovídá. + Požadavek selhal, zkouší se znovu. + Zatím nebyl o nic požádán. + Skrýt ty, které se přestaly ohlašovat + Zjistit, kdo to je + Klíč koordinátora + Název (váš vlastní, volitelný) + Tento název je váš a zůstává na tomto zařízení. Koordinátor nemůže název prokázat, proto se ho na něj nikdy neptáme. + Další akce + Zatím žádní koordinátoři. + Vymazat + Odebráním zůstanou skupiny na tomto zařízení, takže po opětovném přidání koordinátora se vrátí s ním. Vymazáním se smažou: klíče skupin, pozice čtení i vaše balíčky klíčů zmizí a do těchto skupin se lze vrátit jen s novou pozvánkou.\n\nKoordinátor se o tom nedozví a ponechá si vše, co už měl. + Vymazat tohoto koordinátora? + %1$s + Odpovídá na + %1$s +%2$d další + Odebrat + Přejmenovat + Tvrdí, že je: %1$s + Název a verzi si koordinátor zvolil sám. Identifikuje ho jen jeho veřejný klíč. + Odpověděl, ale nic neuvedl. + Zobrazit vše (%1$d) + Zobrazit méně + + Zobrazit %1$d, který se přestal ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + + Tito koordinátoři se neohlásili déle než měsíc. Vytvoření skupiny na takovém, který už zanikl, selže. + Vytvořit skupinu + + Vytvořit a pozvat %1$d osobu + Vytvořit a pozvat %1$d osoby + Vytvořit a pozvat %1$d osoby + Vytvořit a pozvat %1$d osob + + Přidat někoho + Administrátor + + %1$d osoba může přidávat a odebírat + %1$d osoby mohou přidávat a odebírat + %1$d osoby může přidávat a odebírat + %1$d osob může přidávat a odebírat + + Tuto skupinu spravuji jen já + Jen vy můžete zvát, odebírat členy nebo měnit údaje skupiny. Později to můžete předat někomu jinému. + Koordinátor + Změnit + Najít koordinátory + Hotovo + Jiný koordinátor + Veřejný klíč koordinátora (hex nebo npub) + Relaye, na kterých odpovídá, jeden na řádek + + Zastihne vaši %1$d osobu + Zastihne všechny %1$d vaše osoby + Zastihne %1$d vaší osoby + Zastihne všech %1$d vašich osob + + Zastihne %1$d z %2$d — ostatní by zůstali mimo + Neověřeno — výběrem se to zjistí + Popis (volitelné) + Všichni mohou přidávat a odebírat + Každý ve skupině může zvát, odebírat členy a měnit její údaje. Kterýkoli člen může později jmenovat administrátory, čímž tento režim skončí. + Pořadí zpráv ve skupině cordn určuje jeden koordinátor, kterého si zvolíte. Skupina se vytváří na tomto zařízení — koordinátor se o její existenci dozví, až někoho pozvete nebo pošlete zprávu. + Skupinu se nepodařilo vytvořit. + Název skupiny + Není zvolen žádný koordinátor + Každá pozvánka je zpráva Welcome ponechaná na koordinátoru, takže mohou selhat i jednotlivě. Nikdo nedostal oznámení: pozvánku uvidí, až příště otevřou svou aplikaci. + Koordinátor pozvánku odmítl + Poslat odkaz + Přidán do skupiny, ale pozvánka ho nezastihla — zkuste to znovu z obrazovky skupiny + Otevřít chat + Zde není žádný klíč, takže nešlo ponechat zprávu Welcome + Skupina existuje + Čeká se na ně + Hotovo + Zatím nikdo nepřidán. Skupinu lze vytvořit prázdnou a pozvánky poslat později, ale právě uvedením lidí teď zjistíte, který koordinátor je zastihne. + Zatím nikdo — klepnutím přidáte lidi + Uvedením lidí teď zjistíme, který koordinátor je zastihne. Každý z nich dostane zprávu Welcome ponechanou na tomto koordinátoru a vyzvedne si ji, až příště otevře svou aplikaci. + + Dostupný na %1$d koordinátoru + Dostupný na %1$d koordinátorech + Dostupný na %1$d koordinátoru + Dostupný na %1$d koordinátorech + + Žádný klíč na žádném z vašich koordinátorů + Zatím neověřeno + Kdo může přidávat a odebírat + Pojmenování + Kdo v ní bude + Kde poběží + Nová skupina Cordn + + %1$d osobu nezastihne žádný z vašich koordinátorů + %1$d osoby nezastihne žádný z vašich koordinátorů + %1$d osoby nezastihne žádný z vašich koordinátorů + %1$d osob nezastihne žádný z vašich koordinátorů + + Vy, vždy administrátor + Všichni ve skupině uvidí tuto zprávu jako staženou a Amethyst přestane zobrazovat její text. Koordinátor si stále uchovává zašifrovanou kopii, v jaké byla odeslána, a kdo ji už četl, ten ji už četl. + Stáhnout zprávu + Přijato koordinátorem + Rozumím + Co tento koordinátor vidí + Informace o skupině + Tato skupina není dostupná. Její koordinátor mohl být odebrán. + Přidejte koordinátora v Nastavení, abyste mohli začít používat skupiny cordn. + + %1$d skupina + %1$d skupiny + %1$d skupiny + %1$d skupin + + Z těchto skupin nic nepřichází. Amethyst to stále zkouší znovu. + Koordinátoři + Zatím žádné skupiny cordn + Skupina cordn žije na jednom koordinátoru, který spravuje její členství a předává zprávy, aniž by kdy viděl, co kdo píše. + Založit skupinu + Přidat někoho + Pod tímto jménem nikdo nenalezen. + Přidat lze jen ty, kdo na tomto koordinátoru zveřejnili balíček klíčů. + Jméno, npub nebo jmeno@domena + Admin + + %1$d administrátor skupiny zůstává beze změny. + %1$d administrátoři skupiny zůstávají beze změny. + %1$d administrátora skupiny zůstává beze změny. + %1$d administrátorů skupiny zůstává beze změny. + + Koordinátor + Klíč koordinátora + Odkaz na koordinátora + Kopírovat nprofile koordinátora + Upravit údaje + Tato skupina nemá administrátory: každý může přidávat, odebírat a přejmenovávat, a to natrvalo. + Epocha + ID skupiny + Lze přidat na tomto koordinátoru + Členové + %1$s odsud přestane dostávat zprávy. Co už přečetl, mu zůstane, a nelze to vrátit zpět — musel by být pozván znovu. + Odebrat ze skupiny? + Odebrat + Uložit + Technické podrobnosti + Připojit se + Odmítnout + Odmítnutí je trvalé. Pro návrat budete potřebovat novou pozvánku. + Kontroluje se jen při otevření této obrazovky a nikdy jindy — dotaz na koordinátora, zda vás někdo pozval, mu prozradí, že jste tady, a proto to Amethyst nedělá na pozadí. + Pozvánky se nepodařilo načíst. + + %1$d osoba už je v této skupině + %1$d osoby už jsou v této skupině + %1$d osoby už je v této skupině + %1$d osob už je v této skupině + + +%1$d další + Nemáte otevřeného žádného koordinátora. Pozvánky cordn přicházejí přes koordinátora, takže zatím není kde hledat. + Žádné čekající pozvánky. + Zkontrolovat znovu + Pozvánka byla ponechána pro jiné zařízení + Pozvánky Cordn + %1$s neodpověděl + Přes %1$s + Zveřejněním podepíšete záznam vlastním klíčem svého účtu. Daný koordinátor se pak dozví, že tento účet existuje a lze ho pozvat — natrvalo, a bez ohledu na to, zda vás někdo pozve. + Ostatní vás mohou přidat do skupiny cordn jen pomocí balíčku klíčů, který jste zveřejnili na daném koordinátoru. Cordn ho nemá kde jinde uchovat, takže bez zveřejněného balíčku vás nikdo nemůže pozvat — a nic mu neřekne proč. + Balíčky klíčů se nepodařilo načíst. + Žádný záložní balíček. Jakmile dojdou jednorázové, pozvánky budou selhávat. + Záložní balíček je zveřejněn, takže pozvánku lze vytvořit i poté, co dojdou jednorázové. + + %1$d balíček, který toto zařízení nedokáže otevřít + %1$d balíčky, které toto zařízení nedokáže otevřít + %1$d balíčku, které toto zařízení nedokáže otevřít + %1$d balíčků, které toto zařízení nedokáže otevřít + + Koordinátor je předá každému, kdo vás zve, ale soukromá část není na tomto zařízení — patří jiné instalaci. Pokud tato instalace už neexistuje, pozvánka s jedním z nich vytvoří zprávu Welcome, kterou nikdo nikdy neotevře. + Zveřejnit jeden + Zveřejnit záložní + + K dispozici je %1$d jednorázový balíček. + K dispozici jsou %1$d jednorázové balíčky. + K dispozici je %1$d jednorázového balíčku. + K dispozici je %1$d jednorázových balíčků. + + Jakmile zde poprvé něco zveřejníte, Amethyst bude spotřebované balíčky potichu nahrazovat. Ten první ale nikdy nezveřejní za vás. + Stáhnout vše + Stáhnout tyto + Požádat o připojení k této skupině + Tímto zveřejníte balíček klíčů pod vlastním klíčem svého účtu a sdělíte koordinátorovi, že se chcete připojit k této skupině — bez ohledu na to, zda někdo odpoví. + O připojení se nepodařilo požádat. + Požádáno. Některý člen skupiny vás musí přidat; pozvánka se pak objeví mezi pozvánkami cordn. + Skenovat + Tento soubor se nepodařilo otevřít. + Pro tento účet není nastaven žádný mediální server, takže soubor nemá kam uložit. Vyberte ho v Nastavení. + Tento soubor nelze přečíst. + Tento soubor se nepodařilo odeslat. + + %1$d člen + %1$d členové + %1$d člena + %1$d členů + + Zpráva smazána + Koordinátor + Kurzor + Odesláno + Podrobnosti zprávy + upraveno + Připnul %1$s + + %1$d připnutá zpráva v této skupině + %1$d připnuté zprávy v této skupině + %1$d připnuté zprávy v této skupině + %1$d připnutých zpráv v této skupině + + Další připnutá zpráva + Předchozí připnutá zpráva + Zobrazit všechny připnuté zprávy + Připnuté zprávy + Reakce + Přidat + Přijmout může jen administrátor. Ve skupině bez administrátorů to je kdokoli. + Zkontrolovat žádosti + Zamítnout + Žádosti se nepodařilo načíst. + Nikdo nečeká na připojení. + Žádosti o připojení + Odeslat + Odeslání se nezdařilo. + Koordinátor této skupiny není otevřen, takže zatím nelze nic odeslat. + Zkopírováno + Kopírovat odkaz + Kdokoli s tímto odkazem může požádat o připojení. Pustit ho dovnitř ale musí administrátor. + Sdílet tuto skupinu + Přehrát hlasovou zprávu + Nahrát hlasovou zprávu + Zastavit a odeslat + Amethyst potřebuje fotoaparát ke skenování QR kódů. Nic se nenahrává ani neodesílá — snímky se dekódují v zařízení a poté se zahodí. + Povolit fotoaparát + Přístup k fotoaparátu je pro Amethyst vypnutý. Znovu ho můžete zapnout v nastavení Androidu. + Otevřít nastavení + Dekodér QR kódů se v tomto zařízení nepodařilo spustit. Kód můžete stále vložit jako text. + Zapnout světlo + Vypnout světlo + Je tma — zkuste světlo + Naskenovat obrázek + Vložit + V obrázku nebyl nalezen žádný QR kód + Ve schránce není nic ke skenování + Klepněte na požadovaný kód + + Načteno %1$s z %2$s části — pokračujte + Načteno %1$s z %2$s částí — pokračujte + Načteno %1$s z %2$s části — pokračujte + Načteno %1$s z %2$s částí — pokračujte + + Obnovit přiblížení + Amethyst tento kód nedokáže otevřít + Tento kód obsahuje soukromý klíč nebo tajemství pro spárování, proto se jeho obsah zde nezobrazuje. Použijte obrazovku, která ho očekává. + Toto je soukromý klíč. Vložte ho raději na přihlašovací obrazovce. + Toto je připojení peněženky. Přidejte ho v nastavení peněženky. + Toto je adresa vzdáleného podepisovatele. Amethyst je rozdává, aby ho ostatní aplikace mohly žádat o podpis — přihlásit se s ní nedokáže. + Toto je aplikace, která se chce připojit k vašemu podepisovateli. Otevřete ji z obrazovky podepisovatele NIP-46. + Toto je Lightning faktura. + Toto je Cashu token. + Toto je webový odkaz. + Toto je prostý text, ne kód Nostr. + Toto je kód Nostr, ale tato obrazovka ho nedokáže otevřít. + Profil + Poznámka + Kód Nostr + Relé + Soukromý klíč + Připojení peněženky + Adresa vzdáleného podepisovatele + Žádost o připojení k podepisovateli + Lightning faktura + Cashu token + Webový odkaz + Prostý text + Otevřít odkaz + Kopírovat + Zkopírováno + Skenovat znovu + Využijte tréninky, které už máte zaznamenané + Připojte Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) — získáte Moji kondici, vlastní přehled tréninků, a tento trénink vyplníte podle záznamu z hodinek místo ručního psaní každého čísla. + Amethyst používá Health Connect pro funkci Moje kondice — váš vlastní přehled tréninků přímo v telefonu. + Moje kondice (menu → Vy → Moje kondice) ukazuje, jak trénujete: kolik jste toho udělali tento týden oproti minulému, jak se váš čas dělí mezi běh, cyklistiku, chůzi, plavání a posilovnu, vaše nejlepší výkony, kolik dní jste trénovali a vaši aktuální sérii. Amethyst tento obraz sestaví z tréninků, které vaše hodinky nebo fitness aplikace už uložily do Health Connect, spolu s tréninky, které jste zaznamenali přímo v Amethystu. Vše se vypočítá ve vašem telefonu a zobrazí jen vám. + Cvičení · jakou aktivitu jste dělali, kdy začala a jak dlouho trvala. To je samotný trénink: určuje počet tréninků, dobu tréninku, rozpis podle aktivit, aktivní dny a vaši sérii. + Vzdálenost · jak daleko jste se dostali; zobrazuje se jako vzdálenost tento týden oproti minulému, týdenní průměr, vzdálenost podle aktivity a nejdelší vzdálenost. + Aktivní a celkové kalorie · energie spálená při tréninku; zobrazuje se jako energie tento týden a její srovnání s minulým týdnem. Aktivní kalorie se použijí, pokud je váš zdroj zaznamenává; celkové kalorie slouží jako náhrada u zdrojů, které zaznamenávají jen celkovou energii. + Tepová frekvence · průměr a maximum během každého tréninku, sloučené do průměrné a nejvyšší tepové frekvence za období. Podle ní poznáte lehký týden od náročného při stejné vzdálenosti. + Kroky · počet kroků při běhu, chůzi nebo túře; zobrazuje se jako týdenní průměr kroků a nejvyšší počet kroků. + Nastoupané převýšení · kolik jste nastoupali; zobrazuje se jako týdenní průměr stoupání a největší stoupání. Právě to odlišuje rovinatý týden od kopcovitého. + Čte jen tréninky z posledních 4 týdnů, a to jen když je otevřená Moje kondice nebo editor tréninku. Nikdy nečte na pozadí. + Vaše zdravotní údaje nikdy nikam neposílá. Moje kondice se počítá ve vašem telefonu a zůstává v něm. Pokud později budete chtít zveřejnit jeden trénink, klepnete u něj na Sdílet, přečtete si příspěvek, který Amethyst vyplnil, a zveřejníte ho sami — vždy jen jeden trénink, nikdy automaticky. + Celá funkce je volitelná. Oprávnění můžete kdykoli odvolat v Health Connect — Moje kondice se pak vrátí k úvodní výzvě — nebo vypnout návrhy v editoru v Nastavení → Nastavení editoru. Zbytek Amethystu funguje dál. + Moje kondice + Poslední 4 týdny + Načítám vzdálenost, kalorie a tepovou frekvenci… + Poslední čtyři celé týdny vašeho tréninku: tréninky, které jste zveřejnili, a vše, co Health Connect zaznamenává v tomto telefonu. Amethyst přehled počítá v zařízení a nic z něj nezveřejňuje. + Tento týden + oproti minulému týdnu + Týdenní průměr + Tréninky + Aktivní dny + Série dní + Čas + %1$d h %2$d min + %1$d min + %1$d s + Vzdálenost + Kalorie + Kroky + Stoupání + Prům. TF + Max. TF + Podle aktivity + Nejlepší výkony + Nejdelší vzdálenost + Nejdelší trénink + Největší stoupání + Nejvíce kroků + Nejvyšší tepová frekvence + Nedávné tréninky + Za poslední 4 týdny nic zaznamenáno. Zobrazí se zde tréninky, které zveřejníte, i ty, které zaznamenají vaše hodinky. + Sledujte svůj trénink + Připojte Health Connect a uvidíte týdenní součty, vývoj oproti minulému týdnu, nejlepší výkony a tréninkovou sérii. Vše zůstává ve vašem telefonu. + Připojte Health Connect a přidejte tréninky zaznamenané hodinkami, včetně tepové frekvence, kroků a stoupání. + Připojit + Sdílet tento trénink + tep/min + kcal + km + mi + m + ft + Načítání + Čekám na načtení vašich soukromých zpráv… + %1$02d:%2$02d + %1$d:%2$02d:%3$02d + Skupina aktualizována + Příloha + Zpráva + %1$s: %2$s + Vytvořit skupinu + Pozvánky Marmot chodí na jiné zařízení a zde je nelze otevřít. + Zavřít + Zveřejnit z tohoto zařízení + Otevřít poznámku se zapem + Titulky + Zapnout nebo vypnout titulky (skryto, pokud je video nemá) + Neodesláno + Zkusit znovu + Odesílání… + Stále se odesílá… + Tento účet byl z Amethystu odebrán. + Vypnuto + Vypnout titulky + Zapnout titulky + Titulky + Seznam aplikací + Seznam článků + Sada záložek + Oblíbené kanály + Git autoři + Git repozitáře + Důvěryhodní autoři wiki + Důvěryhodná wiki relé + Zájmy + Sledování médií + Startovní balíček médií + Ztlumeno pro druh %1$s + Seznam obrázků + Skupiny + Spolupráce na videu + Seznam videí + Titulky videa + Amethyst pro Android se nemůže přihlásit pomocí adresy bunker://. Tyto adresy rozdává, aby ho ostatní aplikace mohly žádat o podpis, ale přihlásit se s nimi nedokáže. Použijte externí podepisovací aplikaci nebo Amethyst Desktop. + Toto je aplikace, která se chce připojit k vašemu podepisovateli, ne způsob přihlášení. Nejprve se přihlaste a pak ji otevřete z obrazovky podepisovatele NIP-46. + Moje kondice + Tento seznam má soukromé členy, které vidí jen jeho autor + Upravit keš + Upravit výpravu + Keš + Výprava + Výpravy + Keše + Zapsat nález + Ukrýt keš + Nová výprava + Přijal uvedení jako spolupracovník + Odmítl uvedení jako spolupracovník + Uvedení přijato + Zvuk od + Účinkuje + Inspirováno + Odkazuje na + Soukromý seznam + + %1$d video + %1$d videa + %1$d videa + %1$d videí + + Toto zařízení nyní přijímá vaše pozvánky Marmot. + Zveřejnění KeyPackage selhalo: %1$s + Žádné relé KeyPackage nepřijalo. Zkontrolujte nastavení relé a zkuste to znovu. + Odesílání… + Nepodařilo se odeslat. Klepnutím to zkusíte znovu. + + %1$d nová zpráva + %1$d nové zprávy + %1$d nové zprávy + %1$d nových zpráv + + Skupiny Cordn + End-to-end šifrované skupiny, jejichž pořadí zpráv určuje vámi zvolený koordinátor, nikoli relé. + Skupina Cordn + Šifrovaný skupinový chat řazený koordinátorem, kterého si vyberete. + Koordinovaná + Nejlepší pro týmy, které chtějí jedno spolehlivé pořadí konverzace. + Vytvořit skupinu Cordn + End-to-end šifrování pomocí MLS — koordinátor nikdy nepřečte žádnou zprávu. + Jedno dohodnuté pořadí pro všechny, takže historii nemohou přeházet špatně nastavené hodiny. + Koordinátor zjistí, kdo je ve skupině a kdy spolu mluvíte, i když nemůže číst, co píšete. + 3D objekt + %1$d vrcholů · %2$d stěn + Tento 3D objekt nelze přečíst (pravidlo %1$s) + Avatar Cyberspace + Tento avatar nezaplatil za svou velikost, proto se nevykresluje + Osvětleno; klepnutím vykreslíte objekt plošně + Plošně; klepnutím objekt osvětlíte + 1 jednotka = %1$s + Ukryto v dataspace · %1$s + Ukryto v ideaspace · %1$s + Výchozí avatar + Není zveřejněn žádný tvar, takže všichni vidí tento + Ukryto na místě + Ukryto v bloku %1$s regionů + Ukryto v jednom regionu, který nápověda přesně určuje + Bez nápovědy, takže to může být kdekoli v kyberprostoru + Mimo dosah: nápověda určuje 2^%1$d regionů k prohledání + Hledat + Zastavit + Odhaduji náročnost hledání v tomto zařízení… + Prohledáno %1$s z %2$s regionů + + Mimo dosah: zhruba %1$d minuta hledání v tomto zařízení + Mimo dosah: zhruba %1$d minuty hledání v tomto zařízení + Mimo dosah: zhruba %1$d minuty hledání v tomto zařízení + Mimo dosah: zhruba %1$d minut hledání v tomto zařízení + + + Mimo dosah: zhruba %1$d hodina hledání v tomto zařízení + Mimo dosah: zhruba %1$d hodiny hledání v tomto zařízení + Mimo dosah: zhruba %1$d hodiny hledání v tomto zařízení + Mimo dosah: zhruba %1$d hodin hledání v tomto zařízení + + Není v bloku, který nápověda určila + Region nalezen, ale obsah nelze přečíst + + %1$d položka ukrytá zde neodpovídala svému podpisu a byla zahozena + %1$d položky ukryté zde neodpovídaly svým podpisům a byly zahozeny + %1$d položky ukryté zde neodpovídalo svým podpisům a bylo zahozeno + %1$d položek ukrytých zde neodpovídalo svým podpisům a bylo zahozeno + + Otevřeno, ale uvnitř nic nebylo + Nepodepsáno, takže autorství je jen tvrzení + Položka druhu %1$d, kterou tento klient nevykresluje + + %1$d bajt něčeho, co tento klient neumí přečíst + %1$d bajty něčeho, co tento klient neumí přečíst + %1$d bajtu něčeho, co tento klient neumí přečíst + %1$d bajtů něčeho, co tento klient neumí přečíst + + Zavřít + Kopírovat odkaz + Najít + Velikost textu + Počítačová verze + Na plochu + Jiný prohlížeč + Celá obrazovka + Upravit adresu + Zpět + Vpřed + Obnovit + Zastavit načítání + Přidat do oblíbených + Odebrat z oblíbených + Sdílet + Kopírovat odkaz + Najít na stránce + Velikost textu + Přidat na plochu + Verze pro počítač + Otevřít v prohlížeči + Otevřít v aplikaci %1$s + Celá obrazovka + Opustili jste %1$s + Zpět do aplikace + Menší text + Větší text + Obnovit + %1$d% + Soukromí + Onion routing + Web nevidí vaši IP adresu + Web vidí vaši IP adresu + Nastavení webu + Zatím nic nepovoleno + K čemu má přístup + Vaše klíče nikdy neopustí Amethyst + Každé podepsání, zveřejnění, nahrání nebo platba vyžaduje vaše schválení + Žádný zvláštní přístup + Běží v izolovaném prostředí a nemůže jednat za váš účet + Spravovat oprávnění + nSite · izolovaný + nApplet · izolovaný + Konzole + Zabezpečené připojení + Nezabezpečeno + Přes onion routing + Izolovaná aplikace + Fotoaparát + Mikrofon + Poloha + Zeptat se + Povolit + Blokovat + %1$s: %2$s + povoleno + blokováno + Hledejte nebo zadejte adresu + Přejít + Vymazat + Vložit a přejít + Použít tuto adresu + Najít na stránce + %1$d / %2$d + Žádné shody + Předchozí shoda + Další shoda + Zavřít hledání na stránce + Vše + Chyby + Varování + Kopírovat + Vymazat + Zatím nic nezaznamenáno + Tento web chce použít + Vidět, co vidí váš fotoaparát + Slyšet, co slyší váš mikrofon + Znát vaši přibližnou polohu + Hovory mohou odhalit vaši IP adresu i přes Tor. + Povolit během návštěvy + Jen tentokrát + Nepovolit + %1$s sděluje + Tato stránka sděluje + Nedovolit této stránce zobrazovat další dialogy + Vaše odpověď + Opustit web? + Provedené změny se nemusí uložit. + Opustit + OK + Zrušit + Připojení + Připojení je šifrované + Informace, které odešlete, nelze cestou přečíst. + Připojení není šifrované + Nezadávejte hesla ani platební údaje. + Přes onion routing sítí Tor + Otevřený web + Certifikát + Vydáno pro %1$s, vydavatel %2$s · platí do %3$s + Oprávnění + Soubory cookie a data webu + Přihlášení a předvolby, které tento web uložil pro tento účet + Vymazat data webu + Odhlásit se z tohoto webu a smazat jeho data? + Znovu blokovat %1$s + Ponechat %1$s + Znovu vstoupit (%1$s) + Znovu ztlumit %1$s + Obnovit %1$s + Obnovit kanály + Obnovit místa + Obnovit profil + Obnovit poskytovatele + Obnovit relé + Obnovit témata + Obnovit peněženku + Přijmout + + %1$s a přidali %2$d + %1$s a přidali %2$d + %1$s a přidali %2$d + %1$s a přidali %2$d + + + %1$s a zablokovali %2$d + %1$s a zablokovali %2$d + %1$s a zablokovali %2$d + %1$s a zablokovali %2$d + + + %1$s a začali sledovat %2$d + %1$s a začali sledovat %2$d + %1$s a začali sledovat %2$d + %1$s a začali sledovat %2$d + + + %1$s a připojili se k %2$d + %1$s a připojili se k %2$d + %1$s a připojili se k %2$d + %1$s a připojili se k %2$d + + + %1$s a ztlumili %2$d + %1$s a ztlumili %2$d + %1$s a ztlumili %2$d + %1$s a ztlumili %2$d + + Vaše synchronizovaná nastavení Amethystu byla změněna v jiné aplikaci, která není Amethyst. To může způsobit problémy. + Vyprázdnili jste svůj seznam Concord + Změnili jste svůj seznam Concord + + Odebrali jste %1$d DM relé + Odebrali jste %1$d DM relé + Odebrali jste %1$d DM relé + Odebrali jste %1$d DM relé + + + Odebrali jste %1$d oblíbený kanál + Odebrali jste %1$d oblíbené kanály + Odebrali jste %1$d oblíbeného kanálu + Odebrali jste %1$d oblíbených kanálů + + + Přestali jste sledovat %1$d osobu + Přestali jste sledovat %1$d osoby + Přestali jste sledovat %1$d osoby + Přestali jste sledovat %1$d osob + + + Přestali jste sledovat %1$d hashtag + Přestali jste sledovat %1$d hashtagy + Přestali jste sledovat %1$d hashtagu + Přestali jste sledovat %1$d hashtagů + + + Odebrali jste %1$d indexační relé + Odebrali jste %1$d indexační relé + Odebrali jste %1$d indexačního relé + Odebrali jste %1$d indexačních relé + + + Odebrali jste %1$d relé pro klíče skupinových chatů + Odebrali jste %1$d relé pro klíče skupinových chatů + Odebrali jste %1$d relé pro klíče skupinových chatů + Odebrali jste %1$d relé pro klíče skupinových chatů + + + Opustili jste %1$d veřejný chat + Opustili jste %1$d veřejné chaty + Opustili jste %1$d veřejného chatu + Opustili jste %1$d veřejných chatů + + + Opustili jste %1$d komunitu + Opustili jste %1$d komunity + Opustili jste %1$d komunity + Opustili jste %1$d komunit + + + Opustili jste %1$d skupinu + Opustili jste %1$d skupiny + Opustili jste %1$d skupiny + Opustili jste %1$d skupin + + + Opustili jste %1$d chatovací místnost + Opustili jste %1$d chatovací místnosti + Opustili jste %1$d chatovací místnosti + Opustili jste %1$d chatovacích místností + + + Zrušili jste ztlumení %1$d položky + Zrušili jste ztlumení %1$d položek + Zrušili jste ztlumení %1$d položky + Zrušili jste ztlumení %1$d položek + + + Odebrali jste %1$d veřejné relé + Odebrali jste %1$d veřejná relé + Odebrali jste %1$d veřejného relé + Odebrali jste %1$d veřejných relé + + Odebrali jste svůj klíč pro nutzapy + Nahradili jste svůj klíč pro nutzapy + + Odebrali jste %1$d mint pro nutzapy + Odebrali jste %1$d minty pro nutzapy + Odebrali jste %1$d mintu pro nutzapy + Odebrali jste %1$d mintů pro nutzapy + + + Odebrali jste %1$d nabídku BOLT12 + Odebrali jste %1$d nabídky BOLT12 + Odebrali jste %1$d nabídky BOLT12 + Odebrali jste %1$d nabídek BOLT12 + + + Odebrali jste %1$d způsob, jak vám zaplatit + Odebrali jste %1$d způsoby, jak vám zaplatit + Odebrali jste %1$d způsobu, jak vám zaplatit + Odebrali jste %1$d způsobů, jak vám zaplatit + + + Přestali jste sledovat %1$d místo + Přestali jste sledovat %1$d místa + Přestali jste sledovat %1$d místa + Přestali jste sledovat %1$d míst + + + Odebrali jste %1$d soukromé relé + Odebrali jste %1$d soukromá relé + Odebrali jste %1$d soukromého relé + Odebrali jste %1$d soukromých relé + + + Odebrali jste %1$d pole profilu + Odebrali jste %1$d pole profilu + Odebrali jste %1$d pole profilu + Odebrali jste %1$d polí profilu + + + Odebrali jste %1$d kanál relé + Odebrali jste %1$d kanály relé + Odebrali jste %1$d kanálu relé + Odebrali jste %1$d kanálů relé + + + Odebrali jste %1$d vyhledávací relé + Odebrali jste %1$d vyhledávací relé + Odebrali jste %1$d vyhledávacího relé + Odebrali jste %1$d vyhledávacích relé + + + Změnili jste poskytovatele u %1$d služby důvěry + Změnili jste poskytovatele u %1$d služeb důvěry + Změnili jste poskytovatele u %1$d služby důvěry + Změnili jste poskytovatele u %1$d služeb důvěry + + + Přestali jste důvěřovat %1$d relé + Přestali jste důvěřovat %1$d relé + Přestali jste důvěřovat %1$d relé + Přestali jste důvěřovat %1$d relé + + + Odblokovali jste %1$d relé + Odblokovali jste %1$d relé + Odblokovali jste %1$d relé + Odblokovali jste %1$d relé + + Vyprázdnili jste svou peněženku Cashu + Změnili jste svou peněženku Cashu + Přidáno (%1$s) + Změněno (%1$s) + %1$s přidáno + %1$s změněno + %1$s odebráno + Použít novou verzi + Přidali jste soukromé (šifrované) položky. + Změnili jste také soukromé (šifrované) položky. Zde je nelze porovnat. + Odebrali jste všechny soukromé (šifrované) položky. + jen čtení + čtení i zápis + jen zápis + Odebráno (%1$s) + Obnovit uloženou verzi + V jiné aplikaci jste změnili: %1$s + Kanály + Chatovací místnosti + Komunity + Hashtagy + Propojené identity + Místa + Minty + Klíče pro nutzapy + Nabídky + Ostatní položky + Platební adresy + Lidé + Pole profilu + Veřejné chaty + Relé + Skupiny + Vlákna + Poskytovatelé důvěry + Slova + O mně + Banner + Narozeniny + Příznak bota + Nabídka CLINK + Zobrazované jméno + LNURL + Lightning adresa + Uživatelské jméno + Nostr adresa + Obrázek + Zájmena + Webová stránka + Povoleno + Změněno v jiné aplikaci · %1$s + Vaše komunity Concord jsou šifrované pro vás, takže je zde nelze porovnat. Obnovení vrátí přesně to, co uložilo toto zařízení. + Přesunuto + Vypuštěno: %1$s + Vypuštěno + Vytvořeno vámi + Změněno vámi + Vyprázdněno vámi + Kanál + Odstraněno + Hashtagy, které sledujete + Inbox + kde vás ostatní zastihnou + připojeno + zachováno + Zachováno: %1$s + Přijímací klíč odebrán + Přijímací klíč nahrazen + Nutzapy budou uzamčeny tímto novým klíčem. Pokud ho toto zařízení nemá, možná je zde nebudete moci uplatnit. + opuštěno + Opuštěno + Uvedeno + Přesunuto + %1$s a další (%2$s) + Nová verze + Nové: %1$s + nové + Nové + Nově blokováno + Nově ztlumeno: %1$s + Již neblokováno + Nenastaveno + Nic tu není + Nabídka BOLT12 + + %1$d nabídka odebrána + %1$d nabídky odebrány + %1$d nabídky odebráno + %1$d nabídek odebráno + + Outbox + kam publikujete + + Odebrán %1$d způsob, jak vám zaplatit + Odebrány %1$d způsoby, jak vám zaplatit + Odebráno %1$d způsobu, jak vám zaplatit + Odebráno %1$d způsobů, jak vám zaplatit + + Lidé, kteří vám platí, vidí jen to, co je v tomto seznamu. + odebraná místa + Místa, která sledujete + Nyní jsou šifrované — nejspíš právě sem se přesunuly veřejné položky výše. + Převedli jste položky na soukromé + Jsou šifrované, takže je zde nelze porovnat. + Změnili jste soukromé položky + Jsou šifrované, takže jen vy můžete vidět, co obsahovaly. Obnovení je všechny vrátí. + Odebrali jste všechny soukromé položky + Přijímá + přejmenováno + Přejmenováno + V tomto zařízení + + Prohledat %1$s osobu + Prohledat %1$s osoby + Prohledat %1$s osoby + Prohledat %1$s osob + + Vyhledávání + Služba + Sledující + Hodnocení + Témata + + Změněno %1$s z %2$s služby + Změněno %1$s z %2$s služeb + Změněno %1$s z %2$s služby + Změněno %1$s z %2$s služeb + + Nyní je poskytuje někdo jiný, nebo nikdo + Stále blokováno + Ukládá + Vypuštěno · %1$s + Upraveno · %1$s + Nové · %1$s + %1$s – vymazáno + %1$s – aktualizováno + odebraná témata + Hodnocení, počty sledujících a filtrování spamu, které vidíte, pocházejí od těchto poskytovatelů. + Důvěryhodné + %1$s odblokováno + Amethyst se k nim znovu připojí + Jednotky + %1$s odztlumeno + Tyto položky se opět mohou objevit ve vašich kanálech + Nedůvěryhodné + Minty a klíče vaší peněženky jsou šifrované pro vás, takže je zde nelze porovnat položku po položce. Obnovení vrátí přesně to, co uložilo toto zařízení. + dříve „%1$s“ + Slova a hashtagy + synchronizovaná nastavení aplikace + Nastavení Amethystu synchronizovaná mezi vašimi zařízeními. Jsou šifrovaná, takže lze porovnat jen to, zda existují. + seznam blokovaných relé + Relé, ke kterým se tato aplikace nikdy nepřipojí. + nabídky BOLT12 + Lightning nabídky, pomocí kterých vám ostatní platí. + peněženka Cashu + Nastavení a minty vaší ecash peněženky. Jsou šifrované, takže lze porovnat jen to, zda existují. + seznam komunit + Komunity, ke kterým jste se připojili. + seznam komunit Concord + Komunity Concord, ke kterým jste se připojili. Jsou šifrované, takže lze porovnat jen to, zda existují. + seznam DM relé + Kam vám ostatní doručují soukromé zprávy. + seznam dočasných chatů + Dočasné chatovací místnosti, ke kterým jste se připojili. + seznam oblíbených kanálů + Algoritmické kanály, které jste si uložili. + seznam sledovaných + Určuje, čí příspěvky se zobrazí ve vašem domovském kanálu. + sledovaná místa + Místa, jejichž příspěvky se zobrazují ve vašich kanálech. + sledované hashtagy + Hashtagy, jejichž příspěvky se zobrazují ve vašich kanálech. + seznam indexačních relé + Relé používaná k hledání profilů a seznamů relé ostatních lidí. + relé pro klíče skupinových chatů + Kde ostatní najdou klíče, aby vás mohli přidat do šifrovaných skupinových chatů. + seznam ztlumených + Skrývá lidi, slova, hashtagy a vlákna ze všeho, co vidíte. + nastavení příjmu nutzapů + Minty a klíč, které ostatní používají k posílání nutzapů vám. + seznam + Seznam uložený v tomto zařízení. + seznam veřejných relé + Říká všem, kde najdou vaše příspěvky a kam vám posílat odpovědi a zmínky. + platební adresy + Způsoby, jak vám ostatní mohou zaplatit. + seznam soukromých relé + Kde jsou uloženy vaše koncepty a soukromá data. + profil + Vaše jméno, obrázek, bio a platební adresy tak, jak je vidí všichni. + seznam veřejných chatů + Veřejné chaty, ke kterým jste se připojili. + oblíbené kanály relé + Relé, která procházíte jako kanály. + seznam skupin + Skupiny na relé, ke kterým jste se připojili. + seznam vyhledávacích relé + Relé používaná při vyhledávání. + seznam poskytovatelů důvěry + Služby, kterým důvěřujete, že pro vás hodnotí a ověřují lidi. + seznam důvěryhodných relé + Relé, ke kterým se připojujete bez dotazu. diff --git a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml index 94482ed9f7..64ad7f8fd8 100644 --- a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml @@ -4924,4 +4924,1174 @@ one-shot handoff rather than continuous sync. The exposure strings say what leaves the device; the fork strings say why the old phone stops. --> + Geocache + Traditional + Multi-Cache + Mystery + Mikro + Klein + Normal + Groß + Andere Größe + D%1$d + T%1$d + FTF + Kunstwerk + Key Quest + Verifizierte Funde + FTF vergeben + Archiviert + Nicht weiter? Tippen, um den Hinweis anzuzeigen + Nicht lesbar? Für die andere Lesart tippen + In der Nähe + Gefunden! + Verifizierter Fund + Nachweis ist ungültig + Cache wird geladen… + Versteckt von %1$s + Navigieren + Koordinaten kopieren + Koordinaten kopiert + Fotos + Key-Quest-Mission + Logs + Diesen Cache hat noch niemand geloggt. Sei der Erste. + Ich habe ihn gefunden + Nicht gefunden + Notiz hinzufügen + Wartungsbedarf melden + Zu einer Tour hinzufügen + Neue Tour… + Du hast noch keine Tour erstellt. + Bereits in dieser Tour + + %1$d Cache hier + %1$d Caches hier + + Dieses Foto entfernen + Dieser Cache wurde archiviert. Er ist nicht mehr im Spiel. + FTF geht an %1$s + Der FTF ist noch zu haben + Owner + Diesen Cache bearbeiten + Diesen Cache archivieren + Archivieren. Finder sehen, dass er nicht mehr im Spiel ist, und das Listing bleibt lesbar. + Erstfinder festschreiben + Veröffentlicht einen F-Tag mit %1$s. Das ist endgültig. + Noch keine verifizierten Funde, also gibt es niemanden zum Festschreiben. + Verifizierungscode anzeigen + Drucke ihn aus und lege ihn in den Cache. Amethyst speichert ihn nicht — wenn du ihn verlierst, können Finder nicht mehr beweisen, dass sie dort waren. + Nicht gefunden + Notiz + Wartung erforderlich + Fund loggen + Cache + Dein Log + Wie lief es? + Foto hinzufügen + Deine Key-Quest-Antwort + Anwesenheitsnachweis + Code im Cache scannen + Anderen Code scannen + Verifiziert — du warst am Cache + Einmalig mit dem Code aus der Dose signiert. Der Schlüssel wird nie gespeichert und verlässt diesen Bildschirm nie. + Das ist kein Verifizierungscode. + Dieser Code gehört zu einem anderen Cache. + Dieser Cache verlangt keinen Nachweis, du kannst ihn also einfach loggen. + Dein eigener Standort wird diesem Log nicht angehängt. + Senden + Log hinzufügen + In der Nähe + Karte + Touren + Funde + Meine + Nicht gefunden + D ≤ %1$d + T ≤ %1$d + Mit Preis + Nachweis erforderlich + Meinen Standort verwenden + Nach Entfernung sortiert + Neueste zuerst + Cache verstecken + Hier gibt es noch keine Caches. Probiere eine größere Folgeliste oder verstecke den ersten. + Du hast noch keinen Fund geloggt. + Du hast noch keinen Cache versteckt. + Noch keine Touren. + Funde + FTFs + Verifiziert + Versteckt + Auf mich zentrieren + Lange auf die Karte drücken, um dort einen Cache zu verstecken + Öffnen + Wo + Was + Extras + Nachweis + Relays + Dies veröffentlicht einen öffentlichen Standort mit einer Genauigkeit von etwa 5 Metern. Jeder auf Nostr kann ihn sehen, und ein veröffentlichtes Event kann nicht zurückgeholt werden. + Ort auswählen + Veröffentlicht %1$s sowie gröbere Zellen bis hinunter zu 3 Zeichen für die Umkreissuche. + Name + Beschreibung + Typ + Größe + Schwierigkeit + Gelände + Hinweis + Finder sehen + Hinweis verschlüsseln (ROT13) + Key-Quest-Mission + Zusätze + Anwesenheitsnachweis verlangen + Für diesen Cache wurde ein Verifizierungsschlüssel erzeugt. + Wo Finder loggen sollen + Veröffentlichen + Gib dem Cache einen Namen. + Wähle aus, wo der Cache versteckt ist. + %1$d von %2$d gefunden + Tour starten + + %1$d Cache + %1$d Caches + + Titel + Beschreibung + Bannerbild + Caches, in Reihenfolge + Cache hinzufügen + Nach oben + Nach unten + Entfernen + Seitendesign + Kartenstil + Gib der Tour einen Titel. + Füge mindestens einen Cache hinzu. + Veröffentlichen fehlgeschlagen. Prüfe deine Relay-Verbindung und versuche es erneut. + Cordn-Gruppen + Koordinator-Gruppe + Zugestellt von einem Koordinator, nicht von Relays + Was dieser Koordinator sehen kann + Ein Koordinator ist ein einzelner Server, der jede Nachricht dieser Gruppe in Reihenfolge überträgt. Das ist ein anderer Kompromiss als bei Relays — gut zu wissen, bevor du dies wie jeden anderen Chat behandelst. + Nachrichteninhalte + Wer in der Gruppe ist + Wer was sendet + Nicht lesbar + Unter einem Wegwerfschlüssel + Mit deinem echten Konto verknüpft + Details + Koordinator %1$s + Das ist nicht dieselbe Offenlegung wie bei einer Marmot-Gruppe, bei der kein einzelner Betreiber die gesamte Unterhaltung besitzt. + Beim Beitritt werden auf beiden Seiten echte Konten genannt, daher weiß der Koordinator, wer in dieser Gruppe ist. + Ein einziger Wegwerfschlüssel sendet und ruft für jede deiner Gruppen hier ab, daher kann der Koordinator erkennen, dass sie alle zu dir gehören. + Ein einzelner Betreiber besitzt den vollständigen, geordneten Verlauf jeder Gruppe, die er überträgt. + Das von dir veröffentlichte KeyPackage ist ein signierter Nachweis, dass dieses Konto cordn nutzt, und der Koordinator kann es an jeden weitergeben. + Nachrichten werden nicht aufgefüllt, daher sieht der Koordinator, wie lang jede einzelne ist. + Dieser Client verschlüsselt nicht für den Koordinator. Das ist ein Fehler — bitte melde ihn. + Noch nicht kontaktiert + Antwortet + Antwortet nicht + + %1$d fehlgeschlagener Versuch in Folge + %1$d fehlgeschlagene Versuche in Folge + + Füge einen cordn1…-Link ein, den dir jemand geschickt hat, um zu sehen, welcher Koordinator diese Gruppe überträgt und was dieser Betreiber über dich erfahren würde, wenn du beitrittst. + cordn1… + Prüfen + Einfügen + Leeren + Das ist kein cordn-Gruppenlink: %1$s + Gruppen-ID + Koordinator + Erreichbar über + Dieser Link nennt keinen Koordinator und kann daher nicht allein verwendet werden. Frag die Person, die ihn geschickt hat, welcher Koordinator die Gruppe überträgt. + Durch das Prüfen eines Links trittst du nichts bei, und ein Link allein macht dich nicht zum Mitglied. + Cordn-Gruppenlink + cordn koordinator coordinator gruppe link einladung invite mls chat metadaten privatsphäre offenlegung + Cordn-Koordinatoren + cordn koordinator coordinator hinzufügen entfernen löschen purge status server mls chat reihenfolge + Cordn-KeyPackages + cordn keypackage schlüsselpaket veröffentlichen einladen einladung last resort koordinator mls + Cordn-Backup + cordn backup sicherung wiederherstellen export import passphrase gruppen wiederherstellung + Gruppe %1$s + Noch keine Nachrichten + Cordn-Gruppe, übertragen von %1$s + Nachricht gelöscht + Sprachnachricht + Foto + Video + Datei + In eine cordn-Gruppe eingeladen? + Einladungen ansehen + Cordn + Gruppen, die von einem Koordinator deiner Wahl statt von Relays zugestellt werden. + Gruppenlink prüfen + Sieh nach, worauf ein cordn1…-Link verweist, bevor du darauf reagierst + Von diesem Smartphone umziehen + Von einem anderen Smartphone übernehmen + Veröffentlichte KeyPackages + Der Link + Deine Koordinatoren + Selbst angekündigt + Manuell hinzufügen + Passphrase + Backup erstellen + Aus Backup wiederherstellen + Der Koordinator + Dieses Gerät + Koordinatoren + Die Server, die deine Gruppen übertragen + KeyPackages + Damit können andere dich zu einer Gruppe hinzufügen + Backup und Wiederherstellung + Eine mit Passphrase verschlüsselte Datei, die du aufbewahrst + Auf ein neues Smartphone umziehen + Übergib deine Gruppen an ein Gerät, auf das du wechselst + Auf ein neues Smartphone umziehen + Deine cordn-Gruppen liegen auf diesem Gerät. Für den Umzug braucht es einen Code: Dieses Smartphone veröffentlicht sie, das neue Smartphone liest sie ein, und dieses Smartphone hört auf. + Melde dich zuerst auf dem neuen Smartphone mit demselben Konto an. Umgezogen werden deine Gruppen, niemals dein Schlüssel. + Übergabe starten + Wird veröffentlicht… + Scanne dies auf dem neuen Smartphone + Der Code ist nur ein Verweis. Wer ihn fotografiert, erfährt nichts — das, worauf er verweist, ist für dich verschlüsselt. + Dieses Gerät hat seine Gruppen übergeben + Es sendet und empfängt nicht mehr, denn wenn zwei Smartphones aus derselben Gruppe senden, geht sie für alle darin kaputt. Deine Gruppen sind noch auf diesem Gerät, und nichts wurde gelöscht. + Dieses Gerät zurückholen + Verwende dies, wenn der Umzug nicht abgeschlossen wurde. Nur, wenn das neue Smartphone noch nicht mit dem Senden begonnen hat. + Von einem anderen Smartphone empfangen + Code scannen + oder einfügen + Gruppen hierher holen + Wird abgerufen… + Dies ersetzt alle cordn-Gruppen, die bereits auf diesem Smartphone sind. Sie können nicht zusammengeführt werden. + + %1$d Gruppe umgezogen + %1$d Gruppen umgezogen + + Auf diesem Gerät gibt es keine cordn-Gruppen zum Umziehen. + Lege zuerst einen Medienserver fest — die verschlüsselten Dokumente brauchen einen Ablageort, während das andere Smartphone sie abruft. + Was dieses Gerät verlässt + Dein Gruppenzustand wird verschlüsselt und auf deinen Medienserver hochgeladen, damit das andere Smartphone ihn abrufen kann. Für diesen Server ist er nicht lesbar, er sieht nur eine Größe und einen Zeitpunkt. Lösche die Blobs danach, wenn nichts zurückbleiben soll. + Löschen + Bearbeiten + Deine Nachricht wird bearbeitet + Zur Nachricht + Anheften + Antworten + Loslösen + Diese Änderung konnte nicht vorgenommen werden. + Der Koordinator hat nicht geantwortet, daher wurde nichts geändert. Versuche es gleich noch einmal. + %1$s hat kein KeyPackage bei diesem Koordinator veröffentlicht und kann daher noch nicht hinzugefügt werden. Bitte die Person, cordn zuerst auf diesem Koordinator zu öffnen. + Du kannst dich nicht selbst aus einer cordn-Gruppe entfernen. + %1$s konnte kein Zugang zur Gruppe gegeben werden, daher wurde das Hinzufügen abgebrochen. + %1$s ist nicht in dieser Gruppe. + Diese Person + Dieser Koordinator hat ein KeyPackage geliefert, das jemand anderem gehört, daher wurde %1$s nicht hinzugefügt. Es wurde nichts geändert. + Deine Gruppen-Verschlüsselungsschlüssel, deine KeyPackages und jede Nachricht in diesen Gruppen. Wer die Datei öffnet, kann die Unterhaltungen lesen. Sobald sie Amethyst verlässt, schützt sie nur noch die Passphrase. + Was in der Datei steckt + Dein nsec kann cordn-Gruppen nicht zurückholen. Eine cordn-Gruppe existiert als Verschlüsselungszustand auf diesem Gerät und als geordneter Datenstrom auf einem Koordinator, der ihn nicht lesen kann — verlierst du das Gerät ohne Backup, ist die Gruppe weg, samt allem, was darin schon gesagt wurde. + Exportieren + Backup gespeichert. + Das hat nicht funktioniert. Prüfe die Passphrase und die Datei. + Passphrase + Wiederherstellen + Beim Wiederherstellen werden die cordn-Gruppen auf diesem Gerät durch die aus der Datei ersetzt. Das ist für ein Smartphone gedacht, das ein anderes abgelöst hat — nicht, um dieselben Gruppen gleichzeitig auf zwei Geräten zu nutzen. + Jede cordn-Gruppe, die jetzt auf diesem Gerät ist, wird entfernt und durch die aus der Datei ersetzt.\n\nStelle nicht auf einem zweiten Smartphone wieder her, während das erste noch genutzt wird. Beide hätten dieselben Gruppenschlüssel, und sobald beide eine Nachricht gesendet haben, geht die Gruppe für alle kaputt — unbemerkt und ohne Weg zurück. + Die cordn-Gruppen dieses Geräts ersetzen? + Wiederherstellen + Wiederhergestellt. + Alles, was hier gesendet wird, ist für diese Gruppe verschlüsselt. Der Koordinator leitet es weiter, ohne es lesen zu können. + Noch keine Nachrichten + Neue Nachrichten + Gestern + Nachricht + Hinzufügen + Dieser Koordinator konnte nicht geöffnet werden. + npub des Koordinators kopieren + Nach Koordinatoren suchen + Hinzufügen + Liest Ankündigungen, die deine Relays ohnehin führen. Kein Koordinator wird kontaktiert, daher erfährt keiner, dass du gesucht hast. + Ankündigungen konnten nicht gelesen werden + Auf deinen Relays kündigt sich niemand an. + Vor %1$s angekündigt + Zuletzt angekündigt %1$s + + Nichts gefunden, und %1$d deiner Relays hat nicht geantwortet. + Nichts gefunden, und %1$d deiner Relays haben nicht geantwortet. + + Ein Koordinator legt die Reihenfolge der Nachrichten einer Gruppe fest und ist der Einzige, der das kann. Geht er verloren, ist die Reihenfolge verloren, und ein zweiter spiegelt den ersten nicht — daher sind das benannte Dienste, die du auswählst, keine austauschbaren Relays. + + Antwortet nicht — %1$d Anfrage in Folge ist fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. + Antwortet nicht — %1$d Anfragen in Folge sind fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. + + Antwortet. + Eine Anfrage ist fehlgeschlagen; neuer Versuch läuft. + Noch nichts angefragt. + Die ausblenden, die sich nicht mehr ankündigen + Fragen, wer er ist + Koordinator-Schlüssel + Ein Name dafür (deiner, optional) + Dieser Name gehört dir und bleibt auf diesem Gerät. Ein Koordinator kann keinen Namen beweisen, daher wird er nie nach einem gefragt. + Weitere Aktionen + Noch keine Koordinatoren. + Löschen + Beim Entfernen bleiben die Gruppen auf diesem Gerät, sodass sie zurückkommen, wenn du den Koordinator wieder hinzufügst. Beim Löschen werden sie vernichtet: Die Gruppenschlüssel, die Lesepositionen und deine KeyPackages verschwinden, und diese Gruppen können nur über eine neue Einladung wieder betreten werden.\n\nDer Koordinator wird nicht benachrichtigt und behält alles, was er bereits hatte. + Diesen Koordinator löschen? + %1$s + Antwortet auf + %1$s +%2$d weitere + Entfernen + Umbenennen + Gibt an: %1$s + Name und Version eines Koordinators sind selbst gewählte Angaben. Nur sein öffentlicher Schlüssel identifiziert ihn. + Hat geantwortet, aber nichts angegeben. + Alle %1$d anzeigen + Weniger anzeigen + + %1$d anzeigen, der sich nicht mehr ankündigt + %1$d anzeigen, die sich nicht mehr ankündigen + + Diese haben sich seit über einem Monat nicht angekündigt. Eine Gruppe auf einem verschwundenen Koordinator zu erstellen, schlägt fehl. + Gruppe erstellen + + Erstellen und %1$d Person einladen + Erstellen und %1$d Personen einladen + + Jemanden hinzufügen + Admin + + %1$d Person kann hinzufügen und entfernen + %1$d Personen können hinzufügen und entfernen + + Nur ich verwalte diese Gruppe + Nur du kannst einladen, Mitglieder entfernen oder die Gruppendetails ändern. Du kannst das später an jemand anderen übergeben. + Koordinator + Ändern + Koordinatoren finden + Fertig + Anderer Koordinator + Öffentlicher Schlüssel des Koordinators (hex oder npub) + Relays, auf denen er antwortet, eines pro Zeile + + Erreicht deine %1$d Person + Erreicht alle %1$d deiner Personen + + Erreicht %1$d von %2$d — die übrigen blieben außen vor + Nicht geprüft — bei Auswahl wird nachgefragt + Beschreibung (optional) + Alle können hinzufügen und entfernen + Alle in der Gruppe können einladen, Mitglieder entfernen und die Details ändern. Jedes Mitglied kann später Admins ernennen, womit das endet. + Eine cordn-Gruppe wird von einem Koordinator deiner Wahl geordnet. Die Gruppe wird auf diesem Gerät erstellt — der Koordinator erfährt erst von ihr, wenn du jemanden einlädst oder eine Nachricht sendest. + Die Gruppe konnte nicht erstellt werden. + Gruppenname + Kein Koordinator ausgewählt + Jede Einladung ist eine Welcome-Nachricht, die beim Koordinator hinterlegt wird, daher können sie einzeln fehlschlagen. Niemand wurde benachrichtigt: Die Eingeladenen sehen sie, wenn sie ihre App das nächste Mal öffnen. + Der Koordinator hat die Einladung abgelehnt + Link senden + Zur Gruppe hinzugefügt, aber die Einladung hat die Person nicht erreicht — versuche es auf dem Gruppenbildschirm erneut + Chat öffnen + Kein Schlüssel hier, daher konnte keine Welcome-Nachricht hinterlegt werden + Die Gruppe existiert + Warte auf die Person + Fertig + Noch niemand hinzugefügt. Eine Gruppe kann leer erstellt und Einladungen später gesendet werden, aber wenn du jetzt Personen nennst, zeigt sich, welcher Koordinator sie erreichen kann. + Noch niemand — tippen, um Personen hinzuzufügen + Wenn du jetzt Personen nennst, erfahren wir, welcher Koordinator sie erreichen kann. Jede erhält eine Welcome-Nachricht auf diesem Koordinator, die sie abholt, wenn sie ihre App das nächste Mal öffnet. + + Erreichbar über %1$d Koordinator + Erreichbar über %1$d Koordinatoren + + Kein Schlüssel auf einem deiner Koordinatoren + Noch nicht geprüft + Wer hinzufügen und entfernen kann + Name + Wer dabei ist + Wo sie liegt + Neue Cordn-Gruppe + + %1$d Person ist über keinen deiner Koordinatoren erreichbar + %1$d Personen sind über keinen deiner Koordinatoren erreichbar + + Du, immer Admin + Alle in der Gruppe sehen diese Nachricht als zurückgezogen, und Amethyst zeigt ihren Text nicht mehr an. Der Koordinator behält die verschlüsselte Kopie, als die sie gesendet wurde, und wer sie schon gelesen hat, hat sie gelesen. + Nachricht zurückziehen + Vom Koordinator angenommen + Verstanden + Was dieser Koordinator sehen kann + Gruppeninfo + Diese Gruppe ist nicht verfügbar. Ihr Koordinator wurde möglicherweise entfernt. + Füge in den Einstellungen einen Koordinator hinzu, um cordn-Gruppen zu nutzen. + + %1$d Gruppe + %1$d Gruppen + + Aus diesen Gruppen kommt nichts an. Amethyst versucht es weiter. + Koordinatoren + Noch keine cordn-Gruppen + Eine cordn-Gruppe liegt auf einem Koordinator, der ihre Mitgliedschaft verwaltet und Nachrichten weitergibt, ohne je zu sehen, was jemand schreibt. + Eine starten + Jemanden hinzufügen + Niemand mit diesem Namen gefunden. + Personen können nur hinzugefügt werden, wenn sie ein KeyPackage bei diesem Koordinator veröffentlicht haben. + Name, npub oder name@domain + Admin + + Der %1$d Admin der Gruppe bleibt unverändert. + Die %1$d Admins der Gruppe bleiben unverändert. + + Koordinator + Koordinator-Schlüssel + Koordinator-Link + nprofile des Koordinators kopieren + Details bearbeiten + Diese Gruppe hat keine Admins: Alle können dauerhaft hinzufügen, entfernen und umbenennen. + Epoche + Gruppen-ID + Kann über diesen Koordinator hinzugefügt werden + Mitglieder + %1$s erhält von hier keine Nachrichten mehr. Bereits Gelesenes bleibt erhalten, und das lässt sich nicht rückgängig machen — die Person müsste erneut eingeladen werden. + Aus der Gruppe entfernen? + Entfernen + Speichern + Technische Details + Beitreten + Ablehnen + Ablehnen ist endgültig. Um wieder hineinzukommen, musst du erneut eingeladen werden. + Wird nur geprüft, wenn du diesen Bildschirm öffnest, und sonst nie — einen Koordinator zu fragen, ob dich jemand eingeladen hat, verrät ihm, dass du da bist, daher tut Amethyst das nicht im Hintergrund. + Einladungen konnten nicht gelesen werden. + + %1$d Person ist bereits in dieser Gruppe + %1$d Personen sind bereits in dieser Gruppe + + +%1$d weitere + Du hast keinen Koordinator geöffnet. cordn-Einladungen kommen über einen Koordinator an, daher gibt es noch nirgends etwas nachzusehen. + Keine wartenden Einladungen. + Erneut prüfen + Eine Einladung wurde für ein anderes Gerät hinterlegt + Cordn-Einladungen + %1$s hat nicht geantwortet + Über %1$s + Beim Veröffentlichen wird ein Eintrag mit deinem eigenen Kontoschlüssel signiert. Dieser Koordinator weiß dann, dass dieses Konto existiert und eingeladen werden kann — dauerhaft, und unabhängig davon, ob dich jemand einlädt. + Andere können dich nur zu einer cordn-Gruppe hinzufügen, indem sie ein KeyPackage verwenden, das du bei diesem Koordinator veröffentlicht hast. cordn hat keinen anderen Ort dafür, also kann dich ohne veröffentlichtes KeyPackage niemand einladen — und nichts sagt ihnen, warum. + Die KeyPackages konnten nicht gelesen werden. + Kein Last-Resort-Paket. Sobald die Einmal-Pakete aufgebraucht sind, schlagen Einladungen fehl. + Ein Last-Resort-Paket ist veröffentlicht, daher sind Einladungen auch dann noch möglich, wenn die Einmal-Pakete aufgebraucht sind. + + %1$d Paket, das dieses Gerät nicht öffnen kann + %1$d Pakete, die dieses Gerät nicht öffnen kann + + Der Koordinator gibt diese an jeden weiter, der dich einlädt, aber die private Hälfte ist nicht auf diesem Gerät — sie gehört zu einer anderen Installation. Gibt es diese Installation nicht mehr, erzeugt eine Einladung mit einem dieser Pakete eine Welcome-Nachricht, die niemand je öffnen kann. + Eines veröffentlichen + Last-Resort veröffentlichen + + %1$d Einmal-Paket verfügbar. + %1$d Einmal-Pakete verfügbar. + + Sobald du hier veröffentlicht hast, ersetzt Amethyst verbrauchte Pakete unauffällig. Das erste veröffentlicht es nie für dich. + Alle zurückziehen + Diese zurückziehen + Beitritt zu dieser Gruppe anfragen + Dies veröffentlicht ein KeyPackage unter deinem eigenen Kontoschlüssel und teilt dem Koordinator mit, dass du in diese Gruppe möchtest — unabhängig davon, ob jemand antwortet. + Beitrittsanfrage fehlgeschlagen. + Angefragt. Ein Mitglied der Gruppe muss dich hinzufügen; die Einladung erscheint unter cordn-Einladungen. + Scannen + Diese Datei konnte nicht geöffnet werden. + Für dieses Konto ist kein Medienserver festgelegt, daher gibt es keinen Ablageort für die Datei. Wähle einen in den Einstellungen. + Diese Datei konnte nicht gelesen werden. + Diese Datei konnte nicht gesendet werden. + + %1$d Mitglied + %1$d Mitglieder + + Nachricht gelöscht + Koordinator + Cursor + Gesendet + Nachrichtendetails + bearbeitet + Angeheftet von %1$s + + %1$d angeheftete Nachricht in dieser Gruppe + %1$d angeheftete Nachrichten in dieser Gruppe + + Nächste angeheftete Nachricht + Vorherige angeheftete Nachricht + Alle angehefteten Nachrichten anzeigen + Angeheftete Nachrichten + Reaktionen + Hinzufügen + Nur ein Admin kann annehmen. In einer Gruppe ohne Admins sind das alle. + Nach Anfragen suchen + Verwerfen + Die Anfragen konnten nicht gelesen werden. + Niemand wartet auf den Beitritt. + Beitrittsanfragen + Senden + Das wurde nicht gesendet. + Der Koordinator dieser Gruppe ist nicht geöffnet, daher kann noch nichts gesendet werden. + Kopiert + Link kopieren + Jeder mit diesem Link kann den Beitritt anfragen. Ein Admin muss ihn trotzdem noch hereinlassen. + Diese Gruppe teilen + Sprachnachricht abspielen + Sprachnachricht aufnehmen + Stoppen und senden + Amethyst braucht die Kamera, um QR-Codes zu scannen. Es wird nichts aufgezeichnet oder hochgeladen — die Bilder werden auf dem Gerät ausgewertet und verworfen. + Kamera erlauben + Der Kamerazugriff ist für Amethyst deaktiviert. Du kannst ihn in den Android-Einstellungen wieder aktivieren. + Einstellungen öffnen + Der QR-Decoder konnte auf diesem Gerät nicht starten. Du kannst den Code trotzdem als Text einfügen. + Licht einschalten + Licht ausschalten + Es ist dunkel — probier das Licht + Bild scannen + Einfügen + Kein QR-Code in diesem Bild gefunden + Nichts zum Scannen in der Zwischenablage + Tippe auf den gewünschten Code + + %1$s von %2$s Teil erfasst — weiter so + %1$s von %2$s Teilen erfasst — weiter so + + Zoom zurücksetzen + Amethyst kann diesen Code nicht öffnen + Dieser Code enthält einen privaten Schlüssel oder ein Kopplungsgeheimnis, daher wird sein Inhalt hier nicht angezeigt. Verwende den Bildschirm, der ihn erwartet. + Das ist ein privater Schlüssel. Füge ihn stattdessen auf dem Anmeldebildschirm ein. + Das ist eine Wallet-Verbindung. Füge sie in den Wallet-Einstellungen hinzu. + Das ist eine Remote-Signer-Adresse. Amethyst gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können — anmelden kann es sich damit nicht. + Das ist eine App, die sich mit deinem Signierer verbinden möchte. Öffne sie über den NIP-46-Signierer-Bildschirm. + Das ist eine Lightning-Rechnung. + Das ist ein Cashu-Token. + Das ist ein Weblink. + Das ist einfacher Text, kein Nostr-Code. + Das ist ein Nostr-Code, aber keiner, den dieser Bildschirm öffnen kann. + Profil + Beitrag + Nostr-Code + Relay + Privater Schlüssel + Wallet-Verbindung + Remote-Signer-Adresse + Verbindungsanfrage an Signierer + Lightning-Rechnung + Cashu-Token + Weblink + Einfacher Text + Link öffnen + Kopieren + Kopiert + Erneut scannen + Nutze die Trainings, die du schon aufgezeichnet hast + Verbinde Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…), um „Meine Fitness“ aufzubauen, deine persönliche Trainingsübersicht, und um dieses Training aus einem zu übernehmen, das deine Uhr bereits aufgezeichnet hat, statt jede Zahl von Hand einzutippen. + Amethyst nutzt Health Connect, um „Meine Fitness“ aufzubauen — deine persönliche Trainingsübersicht, auf deinem Telefon. + „Meine Fitness“ (Menü → Du → Meine Fitness) zeigt dir, wie du trainiert hast: wie viel du diese Woche im Vergleich zur letzten gemacht hast, wie sich deine Zeit auf Laufen, Radfahren, Gehen, Schwimmen und Fitnessstudio verteilt, deine Bestleistungen, an wie vielen Tagen du trainiert hast und deine aktuelle Serie. Amethyst erstellt dieses Bild aus den Trainings, die deine Uhr oder Fitness-App bereits in Health Connect gespeichert hat, zusammen mit allen Trainings, die du in Amethyst selbst erfasst hast. Es wird auf deinem Telefon berechnet und nur dir angezeigt. + Training · welche Aktivität du gemacht hast, wann sie begann und wie lange sie dauerte. Das ist das Training selbst: Daraus ergeben sich deine Anzahl an Trainings, deine Trainingszeit, die Aufschlüsselung nach Aktivität, deine aktiven Tage und deine Serie. + Distanz · wie weit du gekommen bist, angezeigt als deine Distanz dieser Woche im Vergleich zur letzten, dein Wochendurchschnitt, deine Distanz pro Aktivität und deine längste Distanz. + Aktive und Gesamtkalorien · die Energie, die ein Training verbrannt hat, angezeigt als deine Energie dieser Woche und im Vergleich zur letzten Woche. Aktive Kalorien werden verwendet, wenn deine Quelle sie erfasst; Gesamtkalorien dienen als Ersatz für Quellen, die nur den Gesamtenergieverbrauch erfassen. + Herzfrequenz · Durchschnitt und Maximum jedes Trainings, zusammengefasst zu deiner durchschnittlichen und höchsten Herzfrequenz im Zeitraum. So unterscheidest du eine lockere Woche von einer harten bei gleicher Distanz. + Schritte · die Schrittzahl eines Laufs, Spaziergangs oder einer Wanderung, angezeigt als dein wöchentlicher Schrittdurchschnitt und deine höchste Schrittzahl. + Höhenmeter · wie viel du aufgestiegen bist, angezeigt als dein wöchentlicher Durchschnitt an Höhenmetern und dein größter Anstieg. Daran erkennst du eine flache Woche von einer hügeligen. + Liest nur Trainings der letzten 4 Wochen, und nur solange „Meine Fitness“ oder der Trainings-Editor geöffnet ist. Liest nie im Hintergrund. + Sendet deine Gesundheitsdaten nirgendwohin. „Meine Fitness“ wird auf deinem Telefon berechnet und bleibt dort. Wenn du später ein Training posten möchtest, tippst du bei diesem Training auf Teilen, liest den Beitrag, den Amethyst ausgefüllt hat, und veröffentlichst ihn selbst — ein Training nach dem anderen, nie automatisch. + Die gesamte Funktion ist optional. Widerrufe die Berechtigungen jederzeit in Health Connect — „Meine Fitness“ zeigt dann wieder die Aufforderung — oder schalte die Editor-Vorschläge unter Einstellungen → Editor-Einstellungen aus. Der Rest von Amethyst funktioniert weiter. + Meine Fitness + Letzte 4 Wochen + Distanz, Kalorien und Herzfrequenz werden geladen… + Die letzten vier vollen Wochen deines Trainings: die Trainings, die du veröffentlicht hast, plus alles, was Health Connect auf diesem Telefon erfasst. Amethyst berechnet die Übersicht auf dem Gerät und veröffentlicht nichts davon. + Diese Woche + ggü. letzter Woche + Wochendurchschnitt + Trainings + Aktive Tage + Tage in Folge + Zeit + %1$d Std. %2$d Min. + %1$d Min. + %1$d Sek. + Distanz + Kalorien + Schritte + Höhenmeter + Ø HF + Max. HF + Nach Aktivität + Bestleistungen + Längste Distanz + Längstes Training + Größter Anstieg + Meiste Schritte + Höchste Herzfrequenz + Letzte Trainings + In den letzten 4 Wochen wurde nichts erfasst. Trainings, die du postest, und alle, die deine Uhr aufzeichnet, erscheinen hier. + Verfolge dein Training + Verbinde Health Connect, um deine Wochensummen, den Trend im Wochenvergleich, deine Bestleistungen und deine Trainingsserie zu sehen. Alles bleibt auf deinem Telefon. + Verbinde Health Connect, um die Trainings deiner Uhr mit Herzfrequenz, Schritten und Höhenmetern hinzuzufügen. + Verbinden + Dieses Training teilen + S/min + kcal + km + mi + m + ft + Wird geladen + Warte, bis deine privaten Nachrichten geladen sind… + %1$02d:%2$02d + %1$d:%2$02d:%3$02d + Gruppe aktualisiert + Anhang + Nachricht + %1$s: %2$s + Gruppe erstellen + Marmot-Einladungen gehen an ein anderes Gerät und können hier nicht geöffnet werden. + Schließen + Von diesem Gerät veröffentlichen + Gezappten Beitrag öffnen + Untertitel + Untertitel ein- oder ausschalten (ausgeblendet, wenn das Video keine hat) + Nicht gesendet + Erneut versuchen + Wird gesendet… + Wird noch gesendet… + Dieses Konto wurde aus Amethyst entfernt. + Aus + Untertitel ausschalten + Untertitel einschalten + Untertitel + App-Liste + Artikelliste + Lesezeichen-Set + Lieblings-Feeds + Git-Autoren + Git-Repositories + Vertrauenswürdige Wiki-Autoren + Vertrauenswürdige Wiki-Relays + Interessen + Medien-Follows + Medien-Starterpaket + Stummgeschaltet für Typ %1$s + Bilderliste + Gruppen + Video-Zusammenarbeit + Videoliste + Video-Untertitel + Amethyst für Android kann sich nicht mit einer bunker://-Adresse anmelden. Es gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können, aber anmelden kann es sich damit nicht. Verwende eine externe Signierer-App oder Amethyst Desktop. + Das ist eine App, die sich mit deinem Signierer verbinden möchte, keine Anmeldemöglichkeit. Melde dich zuerst an und öffne sie dann über den NIP-46-Signierer-Bildschirm. + Meine Fitness + Diese Liste hat private Mitglieder, die nur ihr Autor sehen kann + Cache bearbeiten + Tour bearbeiten + Cache + Tour + Cache-Touren + Geocaches + Fund loggen + Cache verstecken + Neue Tour + Hat eine Mitwirkungsnennung angenommen + Hat eine Mitwirkungsnennung abgelehnt + Nennung angenommen + Audio von + Mit + Inspiriert von + Verweise + Private Liste + + %1$d Video + %1$d Videos + + Dieses Gerät empfängt jetzt deine Marmot-Einladungen. + KeyPackage konnte nicht veröffentlicht werden: %1$s + Kein Relay hat das KeyPackage angenommen. Prüfe deine Relay-Einstellungen und versuche es erneut. + Wird gesendet… + Konnte nicht gesendet werden. Tippe, um es erneut zu versuchen. + + %1$d neue Nachricht + %1$d neue Nachrichten + + Cordn-Gruppen + Ende-zu-Ende-verschlüsselte Gruppen, die von einem Koordinator deiner Wahl statt von Relays geordnet werden. + Cordn-Gruppe + Verschlüsselter Gruppenchat, geordnet von einem Koordinator deiner Wahl. + Koordiniert + Ideal für Teams, die eine verlässliche Reihenfolge der Unterhaltung wollen. + Cordn-Gruppe erstellen + Ende-zu-Ende-verschlüsselt mit MLS — der Koordinator kann nie eine Nachricht lesen. + Eine gemeinsame Reihenfolge für alle, sodass der Verlauf nicht durch eine falsch gehende Uhr durcheinandergeraten kann. + Der Koordinator erfährt, wer in der Gruppe ist und wann ihr schreibt, auch wenn er nicht lesen kann, was ihr sagt. + 3D-Objekt + %1$d Eckpunkte · %2$d Flächen + Dieses 3D-Objekt konnte nicht gelesen werden (Regel %1$s) + Cyberspace-Avatar + Dieser Avatar hat nicht für seine Größe bezahlt und wird daher nicht gezeichnet + Beleuchtet; tippen, um das Objekt flach zu zeichnen + Flach; tippen, um das Objekt zu beleuchten + 1 Einheit = %1$s + Versteckt im Dataspace · %1$s + Versteckt im Ideaspace · %1$s + Standard-Avatar + Keine Form veröffentlicht, daher sehen alle diesen hier + An einem Ort versteckt + Versteckt in einem Bereich von %1$s Regionen + Versteckt in einer Region, die der Hinweis genau benennt + Kein Hinweis, das könnte also überall im Cyberspace sein + Außer Reichweite: Der Hinweis nennt 2^%1$d zu durchsuchende Regionen + Danach suchen + Stopp + Suchaufwand auf diesem Gerät wird ermittelt… + %1$s von %2$s Regionen durchsucht + + Außer Reichweite: etwa %1$d Minute Suche auf diesem Gerät + Außer Reichweite: etwa %1$d Minuten Suche auf diesem Gerät + + + Außer Reichweite: etwa %1$d Stunde Suche auf diesem Gerät + Außer Reichweite: etwa %1$d Stunden Suche auf diesem Gerät + + Nicht in dem Bereich, den der Hinweis nennt + Region gefunden, aber der Inhalt konnte nicht gelesen werden + + %1$d hier verstecktes Objekt passte nicht zu seiner Signatur und wurde verworfen + %1$d hier versteckte Objekte passten nicht zu ihren Signaturen und wurden verworfen + + Geöffnet, aber es war nichts drin + Unsigniert, dieser Autor ist also nur eine Behauptung + Ein Objekt vom Typ %1$d, den dieser Client nicht darstellt + + %1$d Byte an Daten, die dieser Client nicht lesen kann + %1$d Bytes an Daten, die dieser Client nicht lesen kann + + Schließen + Link kopieren + Suchen + Textgröße + Desktop + Zum Startbildschirm + Anderer Browser + Vollbild + Adresse bearbeiten + Zurück + Vorwärts + Neu laden + Laden stoppen + Zu Favoriten hinzufügen + Aus Favoriten entfernen + Teilen + Link kopieren + Auf Seite suchen + Textgröße + Zum Startbildschirm hinzufügen + Desktop-Version + Im Browser öffnen + In %1$s öffnen + Vollbild + Du hast %1$s verlassen + Zurück zur App + Kleinerer Text + Größerer Text + Zurücksetzen + %1$d% + Privatsphäre + Onion-Routing + Die Website kann deine IP-Adresse nicht sehen + Die Website kann deine IP-Adresse sehen + Website-Einstellungen + Noch nichts erlaubt + Worauf sie zugreifen kann + Deine Schlüssel verlassen Amethyst nie + Jedes Signieren, Veröffentlichen, Hochladen oder Bezahlen braucht deine Zustimmung + Kein besonderer Zugriff + Sie läuft in einer Sandbox und kann nicht in deinem Konto handeln + Berechtigungen verwalten + nSite · Sandbox + nApplet · Sandbox + Konsole + Sichere Verbindung + Nicht sicher + Über Onion-Routing + App in Sandbox + Kamera + Mikrofon + Standort + Fragen + Erlauben + Blockieren + %1$s %2$s + erlaubt + blockiert + Suchen oder Adresse eingeben + Öffnen + Löschen + Einfügen und öffnen + Diese Adresse verwenden + Auf Seite suchen + %1$d / %2$d + Keine Treffer + Vorheriger Treffer + Nächster Treffer + Suche auf Seite schließen + Alle + Fehler + Warnungen + Kopieren + Löschen + Noch nichts protokolliert + Diese Website möchte Folgendes verwenden + Sehen, was deine Kamera sieht + Hören, was dein Mikrofon hört + Ungefähr wissen, wo du bist + Anrufe können deine IP-Adresse auch über Tor preisgeben. + Während des Besuchs erlauben + Nur dieses Mal + Nicht erlauben + %1$s meldet + Diese Seite meldet + Diese Seite keine weiteren Dialoge anzeigen lassen + Deine Antwort + Website verlassen? + Vorgenommene Änderungen werden möglicherweise nicht gespeichert. + Verlassen + OK + Abbrechen + Verbindung + Verbindung ist verschlüsselt + Was du sendest, kann unterwegs nicht mitgelesen werden. + Verbindung ist nicht verschlüsselt + Gib keine Passwörter oder Zahlungsdaten ein. + Über Tor per Onion-Routing + Offenes Web + Zertifikat + Ausgestellt für %1$s von %2$s · gültig bis %3$s + Berechtigungen + Cookies und Websitedaten + Anmeldungen und Einstellungen, die diese Website für dieses Konto gespeichert hat + Websitedaten löschen + Von dieser Website abmelden und ihre Daten löschen? + %1$s erneut blockieren + %1$s behalten + %1$s erneut beitreten + %1$s erneut stummschalten + %1$s wiederherstellen + Feeds wiederherstellen + Orte wiederherstellen + Profil wiederherstellen + Anbieter wiederherstellen + Relays wiederherstellen + Themen wiederherstellen + Wallet wiederherstellen + Übernehmen + + %1$s und %2$d hinzugefügt + %1$s und %2$d hinzugefügt + + + %1$s und %2$d neu blockiert + %1$s und %2$d neu blockiert + + + %1$s und folgst %2$d neuen Person + %1$s und folgst %2$d neuen Personen + + + %1$s und bist %2$d neuen beigetreten + %1$s und bist %2$d neuen beigetreten + + + %1$s und %2$d neu stummgeschaltet + %1$s und %2$d neu stummgeschaltet + + Deine synchronisierten Amethyst-Einstellungen wurden in einer anderen App als Amethyst geändert. Das kann Probleme verursachen. + Du hast deine Concord-Liste geleert + Du hast deine Concord-Liste geändert + + Du hast %1$d DM-Relay entfernt + Du hast %1$d DM-Relays entfernt + + + Du hast %1$d Lieblings-Feed entfernt + Du hast %1$d Lieblings-Feeds entfernt + + + Du folgst %1$d Person nicht mehr + Du folgst %1$d Personen nicht mehr + + + Du hast %1$d gefolgten Hashtag entfernt + Du hast %1$d gefolgte Hashtags entfernt + + + Du hast %1$d Indexer-Relay entfernt + Du hast %1$d Indexer-Relays entfernt + + + Du hast %1$d Relay für Gruppenchat-Schlüssel entfernt + Du hast %1$d Relays für Gruppenchat-Schlüssel entfernt + + + Du hast %1$d öffentlichen Chat verlassen + Du hast %1$d öffentliche Chats verlassen + + + Du hast %1$d Community verlassen + Du hast %1$d Communities verlassen + + + Du hast %1$d Gruppe verlassen + Du hast %1$d Gruppen verlassen + + + Du hast %1$d Chatraum verlassen + Du hast %1$d Chaträume verlassen + + + Du hast die Stummschaltung für %1$d Eintrag aufgehoben + Du hast die Stummschaltung für %1$d Einträge aufgehoben + + + Du hast %1$d öffentliches Relay entfernt + Du hast %1$d öffentliche Relays entfernt + + Du hast deinen Nutzap-Schlüssel entfernt + Du hast deinen Nutzap-Schlüssel ersetzt + + Du hast %1$d Nutzap-Mint entfernt + Du hast %1$d Nutzap-Mints entfernt + + + Du hast %1$d BOLT12-Angebot entfernt + Du hast %1$d BOLT12-Angebote entfernt + + + Du hast %1$d Zahlungsweg entfernt + Du hast %1$d Zahlungswege entfernt + + + Du hast %1$d gefolgten Ort entfernt + Du hast %1$d gefolgte Orte entfernt + + + Du hast %1$d privates Relay entfernt + Du hast %1$d private Relays entfernt + + + Du hast %1$d Profilfeld entfernt + Du hast %1$d Profilfelder entfernt + + + Du hast %1$d Relay-Feed entfernt + Du hast %1$d Relay-Feeds entfernt + + + Du hast %1$d Such-Relay entfernt + Du hast %1$d Such-Relays entfernt + + + Du hast den Anbieter von %1$d Vertrauensdienst geändert + Du hast den Anbieter von %1$d Vertrauensdiensten geändert + + + Du hast %1$d vertrauenswürdiges Relay entfernt + Du hast %1$d vertrauenswürdige Relays entfernt + + + Du hast %1$d Relay entsperrt + Du hast %1$d Relays entsperrt + + Du hast deine Cashu-Wallet geleert + Du hast deine Cashu-Wallet geändert + Hinzugefügt (%1$s) + Geändert (%1$s) + %1$s hinzugefügt + %1$s geändert + %1$s entfernt + Neue Version verwenden + Du hast private (verschlüsselte) Einträge hinzugefügt. + Du hast auch private (verschlüsselte) Einträge geändert. Sie können hier nicht verglichen werden. + Du hast alle privaten (verschlüsselten) Einträge entfernt. + nur lesen + lesen und schreiben + nur schreiben + Entfernt (%1$s) + Gespeicherte Version wiederherstellen + %1$s in einer anderen App geändert + Feeds + Chaträume + Communities + Hashtags + Verknüpfte Identitäten + Orte + Mints + Nutzap-Schlüssel + Angebote + Andere Einträge + Zahlungsadressen + Personen + Profilfelder + Öffentliche Chats + Relays + Gruppen + Threads + Vertrauensanbieter + Wörter + Über mich + Banner + Geburtstag + Bot-Kennzeichnung + CLINK-Angebot + Anzeigename + LNURL + Lightning-Adresse + Benutzername + Nostr-Adresse + Bild + Pronomen + Website + Erlaubt + In einer anderen App geändert · %1$s + Deine Concord-Communities sind für dich verschlüsselt und können daher hier nicht verglichen werden. Beim Wiederherstellen wird genau das zurückgesetzt, was dieses Gerät gespeichert hat. + Verschoben + %1$s entfernt + Entfernt + Von dir erstellt + Von dir geändert + Von dir geleert + Feed + Weg + Hashtags, denen du folgst + Inbox + wo andere dich erreichen + beigetreten + behalten + %1$s behalten + Empfangsschlüssel entfernt + Empfangsschlüssel ersetzt + Nutzaps werden an diesen neuen Schlüssel gebunden. Wenn dieses Gerät ihn nicht hat, kannst du sie hier möglicherweise nicht einlösen. + verlassen + Verlassen + Gelistet + Verschoben + %1$s und %2$s weitere + Neue Version + %1$s neu + neu + Neu + Neu blockiert + %1$s neu stummgeschaltet + Nicht mehr blockiert + Nicht festgelegt + Hier ist nichts + BOLT12-Angebot + + %1$d Angebot entfernt + %1$d Angebote entfernt + + Outbox + wo du veröffentlichst + + %1$d Weg, dich zu bezahlen, entfernt + %1$d Wege, dich zu bezahlen, entfernt + + Wer dich bezahlt, sieht nur, was in dieser Liste steht. + Orte entfernt + Orte, denen du folgst + Sie sind jetzt verschlüsselt — vermutlich sind die öffentlichen Einträge oben dorthin gewandert. + Du hast Einträge privat gemacht + Sie sind verschlüsselt und können daher hier nicht verglichen werden. + Du hast private Einträge geändert + Sie sind verschlüsselt, daher kannst nur du sehen, was sie waren. Beim Wiederherstellen kommen alle zurück. + Du hast alle privaten Einträge entfernt + Empfang + umbenannt + Umbenannt + Auf diesem Gerät + + %1$s Person durchsuchen + %1$s Personen durchsuchen + + Suche + Dienst + Follower + Rang + Themen + + %1$s von %2$s Dienst geändert + %1$s von %2$s Diensten geändert + + Sie kommen jetzt von jemand anderem oder von niemandem + Weiterhin blockiert + Speicherung + Entfernt · %1$s + Bearbeitet · %1$s + Neu · %1$s + %1$s geleert + %1$s aktualisiert + Themen entfernt + Ränge, Follower-Zahlen und Spamfilter, die du siehst, stammen von diesen Anbietern. + Vertrauenswürdig + %1$s entsperrt + Amethyst verbindet sich wieder mit ihnen + Einheiten + %1$s nicht mehr stummgeschaltet + Diese können wieder in deinen Feeds erscheinen + Nicht vertrauenswürdig + Die Mints und Schlüssel deiner Wallet sind für dich verschlüsselt und können daher hier nicht einzeln verglichen werden. Beim Wiederherstellen wird genau das zurückgesetzt, was dieses Gerät gespeichert hat. + hieß „%1$s“ + Wörter & Hashtags + Synchronisierte App-Einstellungen + Amethyst-Einstellungen, die zwischen deinen Geräten synchronisiert werden. Sie sind verschlüsselt, daher kann nur ihr Vorhandensein verglichen werden. + Liste blockierter Relays + Relays, mit denen sich diese App nie verbindet. + BOLT12-Angebote + Die Lightning-Angebote, mit denen andere dich bezahlen. + Cashu-Wallet + Deine Ecash-Wallet-Einstellungen und Mints. Sie sind verschlüsselt, daher kann nur ihr Vorhandensein verglichen werden. + Community-Liste + Die Communities, denen du beigetreten bist. + Concord-Community-Liste + Die Concord-Communities, denen du beigetreten bist. Sie sind verschlüsselt, daher kann nur ihr Vorhandensein verglichen werden. + DM-Relay-Liste + Wohin andere deine privaten Nachrichten zustellen. + Liste flüchtiger Chats + Die flüchtigen Chaträume, denen du beigetreten bist. + Liste der Lieblings-Feeds + Die algorithmischen Feeds, die du gespeichert hast. + Folgeliste + Bestimmt, wessen Beiträge in deinem Home-Feed erscheinen. + Gefolgte Orte + Orte, deren Beiträge in deinen Feeds erscheinen. + Gefolgte Hashtags + Hashtags, deren Beiträge in deinen Feeds erscheinen. + Indexer-Relay-Liste + Die Relays, über die Profile und Relay-Listen anderer Personen gefunden werden. + Relays für Gruppenchat-Schlüssel + Wo andere die Schlüssel finden, um dich zu verschlüsselten Gruppenchats hinzuzufügen. + Stummschaltliste + Blendet Personen, Wörter, Hashtags und Threads überall aus, wo du etwas siehst. + Nutzap-Empfangseinstellungen + Die Mints und der Schlüssel, mit denen andere dir Nutzaps senden. + Liste + Eine auf diesem Gerät gespeicherte Liste. + Öffentliche Relay-Liste + Teilt allen mit, wo deine Beiträge zu finden sind und wohin Antworten und Erwähnungen an dich gesendet werden sollen. + Zahlungsadressen + Die Wege, auf denen andere dich bezahlen können. + Private Relay-Liste + Wo deine Entwürfe und privaten Daten gespeichert werden. + Profil + Dein Name, Bild, Bio und deine Zahlungsadressen, so wie alle sie sehen. + Liste öffentlicher Chats + Die öffentlichen Chats, denen du beigetreten bist. + Lieblings-Relay-Feeds + Die Relays, die du als Feeds durchstöberst. + Gruppenliste + Die Relay-basierten Gruppen, denen du beigetreten bist. + Such-Relay-Liste + Die Relays, die bei deiner Suche verwendet werden. + Liste der Vertrauensanbieter + Die Dienste, denen du vertraust, Personen für dich zu bewerten und zu verifizieren. + Liste vertrauenswürdiger Relays + Relays, denen du vertraust, sodass ohne Nachfrage eine Verbindung aufgebaut wird. diff --git a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml index fdc35e9309..33a24e3cbd 100644 --- a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml @@ -4922,4 +4922,1174 @@ one-shot handoff rather than continuous sync. The exposure strings say what leaves the device; the fork strings say why the old phone stops. --> + Geocache + Tradicional + Multi-cache + Mistério + Micro + Pequeno + Regular + Grande + Outro tamanho + D%1$d + T%1$d + Primeiro a encontrar + Arte + Key Quest + Encontros verificados + Reivindicado + Arquivado + Travou? Toque para revelar a dica + Ilegível? Toque para ver a outra leitura + Por perto + Encontrei! + Encontro verificado + A prova não confere + Carregando cache… + Escondido por %1$s + Navegar + Copiar coordenadas + Coordenadas copiadas + Fotos + Missão Key Quest + Logs + Ninguém registrou este cache ainda. Seja o primeiro. + Encontrei + Não encontrei + Adicionar uma nota + Informar que precisa de manutenção + Adicionar a uma caçada + Nova caçada… + Você ainda não criou uma caçada. + Já está nesta caçada + + %1$d cache aqui + %1$d caches aqui + + Remover esta foto + Este cache foi arquivado. Ele não está mais em jogo. + Primeiro a encontrar: %1$s + O primeiro a encontrar ainda está em aberto + Dono + Editar este cache + Arquivar este cache + Arquivar. Quem procurar verá que ele está fora de jogo, e a listagem continua legível. + Confirmar o primeiro a encontrar + Publica uma tag F indicando %1$s. Isso é permanente. + Ainda não há encontros verificados, então não há ninguém para confirmar. + Mostrar o código de verificação + Imprima isto e coloque dentro do cache. O Amethyst não o armazena — se você perdê-lo, quem encontrar o cache não poderá mais provar que esteve lá. + Não encontrei + Nota + Precisa de manutenção + Registrar um encontro + Cache + Seu log + Como foi? + Adicionar uma foto + Sua resposta da Key Quest + Prova de presença + Escaneie o código dentro do cache + Escanear outro código + Verificado — você esteve no cache + Assinado uma única vez com o código da caixa. A chave nunca é salva e nunca sai desta tela. + Isso não é um código de verificação. + Esse código pertence a outro cache. + Este cache não exige prova, então você pode simplesmente registrá-lo. + Sua própria localização não é anexada a este log. + Publicar + Adicionar um log + Por perto + Mapa + Caçadas + Encontrados + Meus + Não encontrados + D ≤ %1$d + T ≤ %1$d + Tem prêmio + Exige prova + Usar minha localização + Ordenado por distância + Mais recentes primeiro + Esconder um cache + Ainda não há caches aqui. Tente uma lista de seguidos mais ampla ou esconda o primeiro. + Você ainda não registrou nenhum encontro. + Você ainda não escondeu nenhum cache. + Nenhuma caçada ainda. + Encontros + Primeiros a encontrar + Verificados + Escondidos + Centralizar em mim + Toque e segure no mapa para esconder um cache ali + Abrir + Onde + O quê + Extras + Prova + Relays + Isto publica uma localização pública com precisão de cerca de 5 metros. Qualquer pessoa no Nostr pode vê-la, e um evento publicado não pode ser retirado. + Escolha o local + Publica %1$s, além de células mais amplas de até 3 caracteres para a busca por proximidade. + Nome + Descrição + Tipo + Tamanho + Dificuldade + Terreno + Dica + Quem procurar verá + Embaralhar a dica (ROT13) + Missão Key Quest + Modificadores + Exigir prova de presença + Uma chave de verificação foi gerada para este cache. + Onde os encontros devem ser registrados + Publicar + Dê um nome ao cache. + Escolha onde o cache está escondido. + %1$d de %2$d encontrados + Começar a caçada + + %1$d cache + %1$d caches + + Título + Descrição + Imagem de capa + Caches, em ordem + Adicionar um cache + Mover para cima + Mover para baixo + Remover + Tema da página + Estilo do mapa + Dê um título à caçada. + Adicione pelo menos um cache. + Não foi possível publicar. Verifique sua conexão com os relays e tente novamente. + Grupos Cordn + Grupo com coordenador + Entregue por um coordenador, não por relays + O que este coordenador pode ver + Um coordenador é um único servidor que transporta todas as mensagens deste grupo, em ordem. É uma troca diferente da dos relays — vale saber disso antes de tratar isto como qualquer outro chat. + Conteúdo das mensagens + Quem está no grupo + Quem envia o quê + Ilegível + Sob uma chave descartável + Vinculado à sua conta real + Detalhes + Coordenador %1$s + Esta não é a mesma exposição de um grupo Marmot, em que nenhum operador sozinho detém a conversa inteira. + Entrar identifica contas reais nas duas pontas, então o coordenador sabe quem está neste grupo. + Uma única chave descartável envia e busca para todos os seus grupos aqui, então o coordenador consegue perceber que todos são seus. + Um único operador detém o histórico completo e ordenado de todos os grupos que transporta. + O pacote de chaves que você publicou é um registro assinado de que esta conta usa cordn, e o coordenador pode entregá-lo a qualquer pessoa. + As mensagens não recebem preenchimento, então o coordenador vê o tamanho de cada uma. + Este cliente não está criptografando para o coordenador. Isso é um bug — por favor, reporte. + Ainda não contatado + Respondendo + Sem resposta + + %1$d tentativa seguida com falha + %1$d tentativas seguidas com falha + + Cole um link cordn1… que alguém compartilhou com você para ver qual coordenador transporta esse grupo e o que esse operador saberia sobre você se você entrasse. + cordn1… + Inspecionar + Colar + Limpar + Isso não é um link de grupo cordn: %1$s + ID do grupo + Coordenador + Acessível por + Este link não indica nenhum coordenador, então não pode ser seguido sozinho. Pergunte a quem o enviou qual coordenador transporta o grupo. + Inspecionar um link não entra em nada, e ter um link não torna você membro. + Link de grupo Cordn + cordn coordenador grupo link convite mls chat metadados privacidade exposição + Coordenadores Cordn + cordn coordenador adicionar remover expurgar saúde servidor mls chat ordenação + Pacotes de chaves Cordn + cordn pacote de chaves publicar convite último recurso coordenador mls + Backup do Cordn + cordn backup restaurar exportar importar senha grupos recuperação + Grupo %1$s + Nenhuma mensagem ainda + Grupo Cordn, transportado por %1$s + Mensagem excluída + Mensagem de voz + Foto + Vídeo + Arquivo + Convidado para um grupo cordn? + Ver convites + Cordn + Grupos entregues por um coordenador que você escolhe, em vez de por relays. + Inspecionar um link de grupo + Veja para onde um link cordn1… aponta antes de agir + Sair deste celular + Assumir a partir de outro celular + Pacotes de chaves publicados + O link + Coordenadores que você usa + Anunciando-se + Adicionar manualmente + Senha + Fazer um backup + Restaurar de um backup + O coordenador + Este dispositivo + Coordenadores + Os servidores que transportam seus grupos + Pacotes de chaves + O que permite que as pessoas adicionem você a um grupo + Backup e restauração + Um arquivo criptografado com senha que fica com você + Mudar para um novo celular + Passe seus grupos para o dispositivo que vai substituir este + Mudar para um novo celular + Seus grupos cordn ficam neste dispositivo. Movê-los exige um código: este celular os publica, o novo celular os lê e este celular para. + Primeiro entre com a mesma conta no novo celular. Isto move seus grupos, nunca sua chave. + Iniciar a transferência + Publicando… + Escaneie isto no novo celular + O código é apenas um ponteiro. Quem fotografá-lo não descobre nada — aquilo para que ele aponta está criptografado para você. + Este dispositivo transferiu seus grupos + Ele parou de enviar e receber, porque dois celulares enviando no mesmo grupo o quebrariam para todos nele. Seus grupos continuam neste dispositivo e nada foi excluído. + Retomar este dispositivo + Use isto se a mudança não terminou. Só faça isso se o novo celular ainda não começou a enviar. + Receber de outro celular + Escanear o código + ou cole-o + Trazer os grupos para cá + Buscando… + Isto substitui todos os grupos cordn que já estão neste celular. Eles não podem ser mesclados. + + %1$d grupo movido + %1$d grupos movidos + + Não há grupos cordn neste dispositivo para mover. + Defina primeiro um servidor de mídia — os documentos criptografados precisam de um lugar para ficar enquanto o outro celular os busca. + O que sai deste dispositivo + O estado dos seus grupos é criptografado e enviado ao seu servidor de mídia para que o outro celular possa buscá-lo. Ele é ilegível para esse servidor, que vê apenas um tamanho e um horário. Exclua os blobs depois se não quiser deixar nada para trás. + Excluir + Editar + Editando sua mensagem + Ir para a mensagem + Fixar + Responder + Desafixar + Não foi possível fazer essa alteração. + O coordenador não respondeu, então nada foi alterado. Tente novamente em instantes. + %1$s não publicou um pacote de chaves neste coordenador, então ainda não pode ser adicionado. Peça que abra o cordn neste coordenador primeiro. + Você não pode remover a si mesmo de um grupo cordn. + Não foi possível dar a %1$s um meio de entrar no grupo, então a adição foi cancelada. + %1$s não está neste grupo. + Essa pessoa + Este coordenador forneceu um pacote de chaves que pertence a outra pessoa, então %1$s não foi adicionado. Nada foi alterado. + As chaves de criptografia dos seus grupos, seus pacotes de chaves e todas as mensagens desses grupos. Qualquer pessoa que abrir o arquivo pode ler as conversas. A senha é a única coisa que o protege depois que ele sai do Amethyst. + O que há no arquivo + Sua nsec não recupera grupos cordn. Um grupo cordn existe como estado de criptografia neste dispositivo e como um fluxo ordenado em um coordenador que não consegue lê-lo — perca o dispositivo sem um backup e o grupo se vai, incluindo tudo o que já foi dito nele. + Exportar + Backup salvo. + Não funcionou. Verifique a senha e o arquivo. + Senha + Restaurar + Restaurar substitui os grupos cordn deste dispositivo pelos do arquivo. Serve para um celular que substituiu outro — não é uma forma de usar os mesmos grupos em dois dispositivos ao mesmo tempo. + Todos os grupos cordn atualmente neste dispositivo serão removidos e substituídos pelos do arquivo.\n\nNão restaure em um segundo celular enquanto o primeiro ainda estiver em uso. Ambos teriam as mesmas chaves de grupo e, assim que os dois enviarem uma mensagem, o grupo quebra para todos — silenciosamente e sem volta. + Substituir os grupos cordn deste dispositivo? + Restaurar + Restaurado. + Tudo o que for enviado aqui é criptografado para este grupo. O coordenador repassa as mensagens sem conseguir lê-las. + Nenhuma mensagem ainda + Novas mensagens + Ontem + Mensagem + Adicionar + Não foi possível abrir esse coordenador. + Copiar a npub do coordenador + Procurar coordenadores + Adicionar + Lê anúncios que seus relays já transportam. Nenhum coordenador é contatado, então nenhum deles fica sabendo que você procurou. + Não foi possível ler os anúncios + Ninguém está se anunciando nos seus relays. + Anunciado há %1$s + Último anúncio: %1$s + + Nada encontrado, e %1$d dos seus relays não respondeu. + Nada encontrado, e %1$d dos seus relays não responderam. + + Um coordenador ordena as mensagens de um grupo e é o único que pode fazer isso. Perdê-lo significa perder a ordenação, e um segundo coordenador não espelha o primeiro — por isso são escolhas nomeadas por você, não relays intercambiáveis. + + Sem resposta — %1$d chamada seguida falhou. Os grupos nele não estão sincronizando. + Sem resposta — %1$d chamadas seguidas falharam. Os grupos nele não estão sincronizando. + + Respondendo. + Uma chamada falhou; tentando novamente. + Nada foi solicitado a ele ainda. + Ocultar os que pararam de se anunciar + Perguntar quem é + Chave do coordenador + Um nome para ele (seu, opcional) + Este nome é seu e fica neste dispositivo. Um coordenador não pode comprovar um nome, então nunca é solicitado um. + Mais ações + Nenhum coordenador ainda. + Expurgar + Remover mantém os grupos neste dispositivo, então adicionar o coordenador de volta os traz junto. Expurgar os exclui: as chaves dos grupos, as posições de leitura e seus pacotes de chaves são apagados, e esses grupos só podem ser reabertos com um novo convite.\n\nO coordenador não é avisado e mantém tudo o que já tinha. + Expurgar este coordenador? + %1$s + Responde em + %1$s +%2$d mais + Remover + Renomear + Diz ser: %1$s + O nome e a versão de um coordenador são textos escolhidos por ele. A chave pública é a única coisa que o identifica. + Respondeu, mas não informou nenhum nome. + Mostrar todos os %1$d + Mostrar menos + + Mostrar %1$d que parou de se anunciar + Mostrar %1$d que pararam de se anunciar + + Estes não se anunciam há mais de um mês. Criar um grupo em um que tenha sumido vai falhar. + Criar grupo + + Criar e convidar %1$d pessoa + Criar e convidar %1$d pessoas + + Adicionar alguém + Admin + + %1$d pessoa pode adicionar e remover + %1$d pessoas podem adicionar e remover + + Só eu gerencio este grupo + Só você pode convidar, remover membros ou alterar os detalhes do grupo. Você pode passar isso para outra pessoa depois. + Coordenador + Alterar + Encontrar coordenadores + Concluído + Outro coordenador + Chave pública do coordenador (hex ou npub) + Relays em que ele responde, um por linha + + Alcança a %1$d pessoa escolhida + Alcança todas as %1$d pessoas escolhidas + + Alcança %1$d de %2$d — as demais ficariam de fora + Não verificado — escolhê-lo fará a consulta + Descrição (opcional) + Todos podem adicionar e remover + Todos no grupo podem convidar, remover membros e alterar os detalhes. Qualquer membro pode nomear admins depois, o que encerra esse modo. + Um grupo cordn é ordenado por um coordenador que você escolhe. A criação do grupo acontece neste dispositivo — o coordenador só fica sabendo que o grupo existe quando você convida alguém ou envia uma mensagem. + Não foi possível criar o grupo. + Nome do grupo + Nenhum coordenador escolhido + Cada convite é um Welcome deixado no coordenador, então eles podem falhar um a um. Ninguém foi notificado: as pessoas verão o convite na próxima vez que abrirem o app. + O coordenador recusou o convite + Enviar um link + Adicionado ao grupo, mas o convite não chegou — tente novamente pela tela do grupo + Abrir o chat + Sem chave aqui, então nenhum Welcome pôde ser deixado + O grupo foi criado + Aguardando a pessoa + Concluído + Ninguém adicionado ainda. Um grupo pode ser criado vazio e os convites enviados depois, mas indicar as pessoas agora é o que mostra qual coordenador consegue alcançá-las. + Ninguém ainda — toque para adicionar pessoas + Indicar as pessoas agora é o que nos diz qual coordenador consegue alcançá-las. Cada uma recebe um Welcome deixado nesse coordenador, que ela recebe na próxima vez que abrir o app. + + Alcançável em %1$d coordenador + Alcançável em %1$d coordenadores + + Sem chave em nenhum coordenador que você usa + Ainda não verificado + Quem pode adicionar e remover + Dê um nome + Quem participa + Onde ele fica + Novo grupo Cordn + + %1$d pessoa não pode ser alcançada por nenhum coordenador que você usa + %1$d pessoas não podem ser alcançadas por nenhum coordenador que você usa + + Você, sempre admin + Todos no grupo verão esta mensagem como retirada, e o Amethyst deixará de mostrar o texto. O coordenador ainda guarda a cópia criptografada que foi enviada, e quem já a leu, já leu. + Retirar mensagem + Aceita pelo coordenador + Entendi + O que este coordenador pode ver + Informações do grupo + Este grupo não está disponível. O coordenador dele pode ter sido removido. + Adicione um coordenador nas Configurações para começar a usar grupos cordn. + + %1$d grupo + %1$d grupos + + Nada está chegando destes grupos. O Amethyst continua tentando. + Coordenadores + Nenhum grupo cordn ainda + Um grupo cordn fica em um coordenador que guarda a lista de membros e repassa as mensagens, sem nunca ver o que alguém escreve. + Criar um + Adicionar alguém + Ninguém encontrado com esse nome. + Só é possível adicionar quem publicou um pacote de chaves neste coordenador. + Nome, npub ou nome@domínio + Admin + + O %1$d admin do grupo continua como está. + Os %1$d admins do grupo continuam como estão. + + Coordenador + Chave do coordenador + Link do coordenador + Copiar o nprofile do coordenador + Editar detalhes + Este grupo não tem admins: todos podem adicionar, remover e renomear, permanentemente. + Época + ID do grupo + Pode ser adicionado neste coordenador + Membros + %1$s deixa de receber as mensagens daqui. A pessoa mantém o que já leu, e isso não pode ser desfeito — ela teria de ser convidada novamente. + Remover do grupo? + Remover + Salvar + Detalhes técnicos + Entrar + Recusar + Recusar é permanente. Para voltar, será preciso ser convidado novamente. + Verificado quando você abre esta tela, e em nenhum outro momento — perguntar a um coordenador se alguém convidou você revela a ele que você está aqui, por isso o Amethyst não faz isso em segundo plano. + Não foi possível ler os convites. + + %1$d pessoa já está neste grupo + %1$d pessoas já estão neste grupo + + +%1$d mais + Você não tem nenhum coordenador aberto. Os convites do cordn chegam por um coordenador, então ainda não há onde procurar. + Nenhum convite pendente. + Verificar novamente + Um convite foi deixado para outro dispositivo + Convites Cordn + %1$s não respondeu + Por %1$s + Publicar assina um registro com a chave da sua própria conta. Esse coordenador passa a saber que esta conta existe e pode ser convidada — permanentemente, e quer alguém convide você ou não. + Outras pessoas só podem adicionar você a um grupo cordn usando um pacote de chaves que você publicou nesse coordenador. O cordn não tem outro lugar para guardá-lo, então sem nenhum publicado ninguém pode convidar você — e nada diz a elas o porquê. + Não foi possível ler os pacotes de chaves. + Nenhum pacote de último recurso. Quando os de uso único acabarem, os convites falharão. + Um pacote de último recurso está publicado, então ainda é possível convidar você quando os de uso único acabarem. + + %1$d pacote que este dispositivo não consegue abrir + %1$d pacotes que este dispositivo não consegue abrir + + O coordenador entregará estes pacotes a quem for convidar você, mas a metade privada não está neste dispositivo — ela pertence a outra instalação. Se essa instalação não existir mais, um convite que use um deles gera um Welcome que ninguém jamais conseguirá abrir. + Publicar um + Publicar de último recurso + + %1$d pacote de uso único disponível. + %1$d pacotes de uso único disponíveis. + + Depois que você publicar aqui, o Amethyst repõe discretamente os pacotes conforme forem usados. Ele nunca publica o primeiro por você. + Retirar todos + Retirar esses + Pedir para entrar neste grupo + Isto publica um pacote de chaves com a chave da sua própria conta e informa ao coordenador que você quer entrar neste grupo — quer alguém responda ou não. + Não foi possível pedir para entrar. + Pedido enviado. Um membro do grupo precisa adicionar você; o convite aparecerá em Convites Cordn. + Escanear + Não foi possível abrir esse arquivo. + Nenhum servidor de mídia está definido para esta conta, então não há onde colocar o arquivo. Escolha um nas Configurações. + Não foi possível ler esse arquivo. + Não foi possível enviar esse arquivo. + + %1$d membro + %1$d membros + + Mensagem excluída + Coordenador + Cursor + Enviada + Detalhes da mensagem + editada + Fixada por %1$s + + %1$d mensagem fixada neste grupo + %1$d mensagens fixadas neste grupo + + Próxima mensagem fixada + Mensagem fixada anterior + Mostrar todas as mensagens fixadas + Mensagens fixadas + Reações + Adicionar + Só um admin pode aceitar. Em um grupo sem admins, isso vale para todos. + Verificar pedidos + Dispensar + Não foi possível ler os pedidos. + Ninguém está esperando para entrar. + Pedidos para entrar + Enviar + Não foi enviada. + O coordenador deste grupo não está aberto, então ainda não é possível enviar nada. + Copiado + Copiar link + Qualquer pessoa com este link pode pedir para entrar. Um admin ainda precisa aceitá-la. + Compartilhar este grupo + Reproduzir mensagem de voz + Gravar uma mensagem de voz + Parar e enviar + O Amethyst precisa da câmera para ler códigos QR. Nada é gravado nem enviado — os quadros são decodificados no dispositivo e descartados. + Permitir câmera + O acesso à câmera está desativado para o Amethyst. Você pode reativá-lo nas configurações do Android. + Abrir configurações + O leitor de QR não pôde ser iniciado neste dispositivo. Você ainda pode colar o código como texto. + Acender a lanterna + Apagar a lanterna + Está escuro — experimente a lanterna + Ler de uma imagem + Colar + Nenhum código QR encontrado nessa imagem + Nada na área de transferência para ler + Toque no código que você quer + + %1$s de %2$s parte capturada — continue + %1$s de %2$s partes capturadas — continue + + Redefinir zoom + O Amethyst não consegue abrir este código + Este código contém uma chave privada ou um segredo de pareamento, por isso o conteúdo não é exibido aqui. Use a tela que espera por ele. + Isto é uma chave privada. Cole-a na tela de login. + Isto é uma conexão de carteira. Adicione-a nas configurações da Carteira. + Isto é um endereço de assinador remoto. O Amethyst fornece esses endereços para que outros apps possam pedir que ele assine — não é possível entrar com um deles. + Isto é um app pedindo para se conectar ao seu assinador. Abra-o pela tela do assinador NIP-46. + Isto é uma fatura Lightning. + Isto é um token Cashu. + Isto é um link da web. + Isto é texto simples, não um código Nostr. + Isto é um código Nostr, mas não um que esta tela consiga abrir. + Perfil + Nota + Código Nostr + Relay + Chave privada + Conexão de carteira + Endereço de assinador remoto + Pedido de conexão ao assinador + Fatura Lightning + Token Cashu + Link da web + Texto simples + Abrir link + Copiar + Copiado + Ler novamente + Use os treinos que você já registrou + Conecte o Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) para montar a Minha Forma Física, seu próprio resumo de treinos, e para preencher este treino a partir de um que seu relógio já registrou, em vez de digitar cada número à mão. + O Amethyst usa o Health Connect para montar a Minha Forma Física — seu próprio resumo de treinos, no seu celular. + A Minha Forma Física (menu → Você → Minha Forma Física) mostra como você tem treinado: quanto fez nesta semana em comparação com a anterior, como seu tempo se divide entre corrida, ciclismo, caminhada, natação e academia, seus melhores desempenhos, quantos dias você treinou e sua sequência atual. O Amethyst monta esse panorama a partir dos treinos que seu relógio ou app de fitness já salvou no Health Connect, junto com os treinos que você registrou no próprio Amethyst. Tudo é calculado no seu celular e mostrado a você. + Exercício · qual atividade você fez, quando começou e quanto durou. É o treino em si: define sua contagem de treinos, seu tempo de treino, a divisão por atividade, seus dias ativos e sua sequência. + Distância · até onde você foi, mostrada como sua distância nesta semana em comparação com a anterior, sua média semanal, sua distância por atividade e sua maior distância. + Calorias ativas e totais · a energia gasta em um treino, mostrada como sua energia nesta semana e como ela se compara com a semana anterior. As calorias ativas são usadas quando sua fonte as registra; as calorias totais são a alternativa para fontes que só registram a energia total. + Frequência cardíaca · a média e o máximo de cada treino, combinados na sua frequência cardíaca média e mais alta do período. É o que diferencia uma semana leve de uma pesada com a mesma distância. + Passos · a contagem de passos de uma corrida, caminhada ou trilha, mostrada como sua média semanal de passos e sua maior contagem de passos. + Ganho de elevação · quanto você subiu, mostrado como sua média semanal de subida e sua maior subida. É o que separa uma semana plana de uma com morros. + Só lê treinos das últimas 4 semanas, e só enquanto a Minha Forma Física ou o editor de treinos estiver aberto. Nunca lê em segundo plano. + Nunca envia seus dados de saúde para lugar nenhum. A Minha Forma Física é calculada no seu celular e fica nele. Se depois você quiser publicar um treino, toque em Compartilhar nesse treino, leia a publicação que o Amethyst preencheu e publique você mesmo — um treino por vez, nunca automaticamente. + O recurso inteiro é opcional. Revogue as permissões no Health Connect a qualquer momento — a Minha Forma Física volta a exibir o convite para conectar — ou desative as sugestões do editor em Configurações → Configurações de composição. O resto do Amethyst continua funcionando. + Minha Forma Física + Últimas 4 semanas + Carregando distância, calorias e frequência cardíaca… + As últimas quatro semanas completas do seu treino: os treinos que você publicou, mais tudo o que o Health Connect registra neste celular. O Amethyst calcula o resumo no dispositivo e não publica nada dele. + Esta semana + vs. semana passada + Média semanal + Treinos + Dias ativos + Dias seguidos + Tempo + %1$d h %2$d min + %1$d min + %1$d s + Distância + Calorias + Passos + Subida + FC média + FC máx. + Por atividade + Melhores desempenhos + Maior distância + Treino mais longo + Maior subida + Mais passos + Maior frequência cardíaca + Treinos recentes + Nada registrado nas últimas 4 semanas. Os treinos que você publicar, e os que seu relógio registrar, aparecem aqui. + Acompanhe seu treino + Conecte o Health Connect para ver seus totais semanais, a tendência semana a semana, seus melhores desempenhos e sua sequência de treinos. Tudo fica no seu celular. + Conecte o Health Connect para adicionar os treinos que seu relógio registra, com frequência cardíaca, passos e subida. + Conectar + Compartilhar este treino + bpm + kcal + km + mi + m + pés + Carregando + Aguardando suas mensagens privadas carregarem… + %1$02d:%2$02d + %1$d:%2$02d:%3$02d + Grupo atualizado + Anexo + Mensagem + %1$s: %2$s + Criar um grupo + Os convites do Marmot estão indo para outro dispositivo e não podem ser abertos aqui. + Dispensar + Publicar a partir deste dispositivo + Abrir nota que recebeu o zap + Legendas + Ativar ou desativar as legendas (oculto quando o vídeo não tem nenhuma) + Não enviada + Tentar novamente + Enviando… + Ainda enviando… + Esta conta foi removida do Amethyst. + Desativadas + Desativar legendas + Ativar legendas + Legendas + Lista de apps + Lista de artigos + Conjunto de favoritos + Feeds favoritos + Autores Git + Repositórios Git + Autores de wiki confiáveis + Relays de wiki confiáveis + Interesses + Seguidos de mídia + Pacote inicial de mídia + Silenciado para o kind %1$s + Lista de imagens + Grupos + Colaboração em vídeo + Lista de vídeos + Legendas de vídeo + O Amethyst para Android não consegue entrar com um endereço bunker://. Ele fornece esses endereços para que outros apps possam pedir que ele assine, mas não é possível entrar com um deles. Use um app assinador externo ou o Amethyst Desktop. + Isso é um app pedindo para se conectar ao seu assinador, não uma forma de entrar. Entre primeiro e depois abra-o pela tela do assinador NIP-46. + Minha Forma Física + Esta lista tem membros privados que só o autor pode ver + Editar cache + Editar caçada + Cache + Caçada + Caçadas de caches + Geocaches + Registrar um achado + Esconder um cache + Nova caçada + Aceitou um crédito de colaboração + Recusou um crédito de colaboração + Crédito aceito + Áudio de + Participação de + Inspirado em + Referências + Lista privada + + %1$d vídeo + %1$d vídeos + + Este dispositivo agora recebe seus convites do Marmot. + Falha ao publicar o KeyPackage: %1$s + Nenhum relay aceitou o KeyPackage. Verifique suas configurações de relays e tente novamente. + Enviando… + Não foi possível enviar. Toque para tentar novamente. + + %1$d nova mensagem + %1$d novas mensagens + + Grupos Cordn + Grupos com criptografia de ponta a ponta, ordenados por um coordenador que você escolhe, em vez de por relays. + Grupo Cordn + Chat em grupo criptografado, ordenado por um coordenador que você escolhe. + Coordenado + Ideal para equipes que querem uma ordem confiável única para a conversa. + Criar grupo Cordn + Criptografia de ponta a ponta com MLS — o coordenador nunca consegue ler uma mensagem. + Uma única ordem acordada para todos, então o histórico não pode ser embaralhado por um relógio errado. + O coordenador fica sabendo quem está no grupo e quando vocês conversam, mesmo sem conseguir ler o que é dito. + Objeto 3D + %1$d vértices · %2$d faces + Não foi possível ler este objeto 3D (regra %1$s) + Avatar do Cyberspace + Este avatar não pagou pelo seu tamanho, por isso não é desenhado + Iluminado; toque para desenhar o objeto plano + Plano; toque para iluminar o objeto + 1 unidade = %1$s + Escondido no dataspace · %1$s + Escondido no ideaspace · %1$s + Avatar padrão + Nenhuma forma publicada, então todos veem este + Escondido em um lugar + Escondido em uma caixa de %1$s regiões + Escondido em uma região, que a dica indica exatamente + Sem dica, então pode estar em qualquer lugar do ciberespaço + Fora de alcance: a dica indica 2^%1$d regiões para buscar + Procurar + Parar + Estimando o custo da busca neste dispositivo… + %1$s de %2$s regiões verificadas + + Fora de alcance: cerca de %1$d minuto de busca neste dispositivo + Fora de alcance: cerca de %1$d minutos de busca neste dispositivo + + + Fora de alcance: cerca de %1$d hora de busca neste dispositivo + Fora de alcance: cerca de %1$d horas de busca neste dispositivo + + Não está na caixa que a dica indicou + A região foi encontrada, mas o conteúdo não pôde ser lido + + %1$d item escondido aqui não correspondia à sua assinatura e foi descartado + %1$d itens escondidos aqui não correspondiam às suas assinaturas e foram descartados + + Aberto, e não havia nada dentro + Sem assinatura, então este autor é apenas uma alegação + Um item do kind %1$d, que este cliente não desenha + + %1$d byte de algo que este cliente não lê + %1$d bytes de algo que este cliente não lê + + Fechar + Copiar link + Localizar + Tamanho do texto + Desktop + Tela inicial + Outro navegador + Tela cheia + Editar endereço + Voltar + Avançar + Recarregar + Parar de carregar + Adicionar aos favoritos + Remover dos favoritos + Compartilhar + Copiar link + Localizar na página + Tamanho do texto + Adicionar à tela inicial + Versão para desktop + Abrir no navegador + Abrir no %1$s + Tela cheia + Você saiu de %1$s + Voltar ao app + Texto menor + Texto maior + Redefinir + %1$d% + Privacidade + Roteamento onion + O site não consegue ver seu endereço IP + O site consegue ver seu endereço IP + Configurações do site + Nada permitido ainda + O que ele pode acessar + Suas chaves nunca saem do Amethyst + Toda assinatura, publicação, envio de arquivo ou pagamento passa pela sua aprovação + Nenhum acesso especial + Ele roda isolado e não pode agir na sua conta + Gerenciar permissões + nSite · isolado + nApplet · isolado + Console + Conexão segura + Não seguro + Roteado via onion + App isolado + Câmera + Microfone + Localização + Perguntar + Permitir + Bloquear + %1$s: %2$s + permitido + bloqueado + Pesquise ou digite um endereço + Ir + Limpar + Colar e ir + Usar este endereço + Localizar na página + %1$d / %2$d + Nenhuma correspondência + Correspondência anterior + Próxima correspondência + Fechar localizar na página + Todos + Erros + Avisos + Copiar + Limpar + Nada registrado ainda + Este site quer usar + Ver o que sua câmera vê + Ouvir o que seu microfone ouve + Saber aproximadamente onde você está + Chamadas podem revelar seu endereço IP mesmo pelo Tor. + Permitir durante a visita + Só desta vez + Não permitir + %1$s diz + Esta página diz + Impedir que esta página mostre mais diálogos + Sua resposta + Sair do site? + As alterações que você fez podem não ser salvas. + Sair + OK + Cancelar + Conexão + A conexão é criptografada + As informações que você envia não podem ser lidas no caminho. + A conexão não é criptografada + Não digite senhas nem dados de pagamento. + Roteado via onion pelo Tor + Web aberta + Certificado + Emitido para %1$s por %2$s · válido até %3$s + Permissões + Cookies e dados do site + Logins e preferências que este site salvou para esta conta + Limpar dados do site + Sair deste site e excluir os dados dele? + Bloquear %1$s de novo + Manter %1$s + Entrar de novo em %1$s + Silenciar %1$s de novo + Restaurar %1$s + Restaurar feeds + Restaurar lugares + Restaurar perfil + Restaurar provedores + Restaurar relays + Restaurar tópicos + Restaurar carteira + Aceitar + + %1$s e adicionou %2$d + %1$s e adicionou %2$d + + + %1$s e bloqueou %2$d + %1$s e bloqueou %2$d + + + %1$s e seguiu %2$d + %1$s e seguiu %2$d + + + %1$s e entrou em %2$d + %1$s e entrou em %2$d + + + %1$s e silenciou %2$d + %1$s e silenciou %2$d + + Suas configurações sincronizadas do Amethyst foram alteradas em outro app que não é o Amethyst. Isso pode causar problemas. + Você esvaziou sua lista do Concord + Você alterou sua lista do Concord + + Você removeu %1$d relay de DM + Você removeu %1$d relays de DM + + + Você removeu %1$d feed favorito + Você removeu %1$d feeds favoritos + + + Você deixou de seguir %1$d pessoa + Você deixou de seguir %1$d pessoas + + + Você deixou de seguir %1$d hashtag + Você deixou de seguir %1$d hashtags + + + Você removeu %1$d relay de indexação + Você removeu %1$d relays de indexação + + + Você removeu %1$d relay de chaves de chat em grupo + Você removeu %1$d relays de chaves de chat em grupo + + + Você saiu de %1$d chat público + Você saiu de %1$d chats públicos + + + Você saiu de %1$d comunidade + Você saiu de %1$d comunidades + + + Você saiu de %1$d grupo + Você saiu de %1$d grupos + + + Você saiu de %1$d sala de chat + Você saiu de %1$d salas de chat + + + Você deixou de silenciar %1$d item + Você deixou de silenciar %1$d itens + + + Você removeu %1$d relay público + Você removeu %1$d relays públicos + + Você removeu sua chave de nutzap + Você substituiu sua chave de nutzap + + Você removeu %1$d mint de nutzap + Você removeu %1$d mints de nutzap + + + Você removeu %1$d oferta BOLT12 + Você removeu %1$d ofertas BOLT12 + + + Você removeu %1$d forma de receber pagamentos + Você removeu %1$d formas de receber pagamentos + + + Você deixou de seguir %1$d lugar + Você deixou de seguir %1$d lugares + + + Você removeu %1$d relay privado + Você removeu %1$d relays privados + + + Você removeu %1$d campo do perfil + Você removeu %1$d campos do perfil + + + Você removeu %1$d feed de relay + Você removeu %1$d feeds de relay + + + Você removeu %1$d relay de busca + Você removeu %1$d relays de busca + + + Você trocou o provedor de %1$d serviço de confiança + Você trocou o provedor de %1$d serviços de confiança + + + Você deixou de confiar em %1$d relay + Você deixou de confiar em %1$d relays + + + Você desbloqueou %1$d relay + Você desbloqueou %1$d relays + + Você esvaziou sua carteira Cashu + Você alterou sua carteira Cashu + Adicionados (%1$s) + Alterados (%1$s) + Adicionados: %1$s + Alterados: %1$s + Removidos: %1$s + Usar a nova versão + Você adicionou itens privados (criptografados). + Você também alterou itens privados (criptografados). Eles não podem ser comparados aqui. + Você removeu todos os itens privados (criptografados). + somente leitura + leitura e escrita + somente escrita + Removidos (%1$s) + Restaurar a versão salva + Alterado em outro app: %1$s + Feeds + Salas de chat + Comunidades + Hashtags + Identidades vinculadas + Localizações + Mints + Chaves de nutzap + Ofertas + Outras entradas + Endereços de pagamento + Pessoas + Campos do perfil + Chats públicos + Relays + Grupos + Threads + Provedores de confiança + Palavras + Sobre + Banner + Aniversário + Marcação de bot + Oferta CLINK + Nome de exibição + LNURL + Endereço Lightning + Nome de usuário + Endereço Nostr + Foto + Pronomes + Site + Permitido + Alterado em outro app · %1$s + Suas comunidades Concord são criptografadas para você, por isso não podem ser comparadas aqui. A restauração devolve exatamente o que este dispositivo salvou. + Movido + Removidos: %1$s + Removido + Você criou + Você alterou + Você esvaziou + Feed + Removido + Hashtags que você segue + Caixa de entrada + onde os outros encontram você + entradas + mantidos + Mantidos: %1$s + Chave de recebimento removida + Chave de recebimento substituída + Os nutzaps ficarão bloqueados para esta nova chave. Se este dispositivo não a tiver, talvez você não consiga resgatá-los aqui. + saídas + Saiu + Listado + Movido + %1$s e mais %2$s + Nova versão + Novos: %1$s + novos + Novo + Bloqueados agora + Silenciados agora: %1$s + Não mais bloqueados + Não definido + Nada aqui + Oferta BOLT12 + + %1$d oferta removida + %1$d ofertas removidas + + Caixa de saída + onde você publica + + %1$d forma de pagar você removida + %1$d formas de pagar você removidas + + Quem paga você só vê o que está nesta lista. + lugares removidos + Lugares que você segue + Agora eles estão criptografados, e provavelmente foi para lá que os itens públicos acima foram. + Você tornou itens privados + Eles são criptografados, por isso não podem ser comparados aqui. + Você alterou itens privados + Eles são criptografados, então só você pode ver o que eram. A restauração traz todos de volta. + Você removeu todos os itens privados + Recebimento + renomeados + Renomeado + Neste dispositivo + + Pesquisar %1$s pessoa + Pesquisar entre %1$s pessoas + + Busca + Serviço + Seguidores + Classificação + Tópicos + + %1$s de %2$s serviço alterado + %1$s de %2$s serviços alterados + + Agora eles vêm de outra pessoa, ou de ninguém + Ainda bloqueados + Armazenamento + Removidos · %1$s + Editados · %1$s + Novos · %1$s + %1$s: tudo removido + %1$s: atualização + tópicos removidos + As classificações, as contagens de seguidores e o filtro de spam que você vê vêm desses provedores. + Confiável + Desbloqueados: %1$s + O Amethyst vai se conectar a eles novamente + Unidades + Não silenciados: %1$s + Eles podem voltar a aparecer nos seus feeds + Não confiável + Os mints e as chaves da sua carteira são criptografados para você, por isso não podem ser comparados item a item aqui. A restauração devolve exatamente o que este dispositivo salvou. + antes: “%1$s” + Palavras e hashtags + configurações sincronizadas do app + Configurações do Amethyst sincronizadas entre seus dispositivos. Elas são criptografadas, então só é possível comparar se existem. + lista de relays bloqueados + Relays aos quais este app nunca se conecta. + ofertas BOLT12 + As ofertas Lightning que os outros usam para pagar você. + carteira Cashu + As configurações e os mints da sua carteira de ecash. Eles são criptografados, então só é possível comparar se existem. + lista de comunidades + As comunidades em que você entrou. + lista de comunidades Concord + As comunidades Concord em que você entrou. Elas são criptografadas, então só é possível comparar se existem. + lista de relays de DM + Onde os outros entregam suas mensagens privadas. + lista de chats efêmeros + As salas de chat efêmeras em que você entrou. + lista de feeds favoritos + Os feeds algorítmicos que você salvou. + lista de seguidos + Define de quem são as publicações que aparecem no seu feed inicial. + localizações seguidas + Localizações cujas publicações aparecem nos seus feeds. + hashtags seguidas + Hashtags cujas publicações aparecem nos seus feeds. + lista de relays de indexação + Os relays usados para encontrar perfis e listas de relays de outras pessoas. + relays de chaves de chat em grupo + Onde os outros encontram as chaves para adicionar você a chats em grupo criptografados. + lista de silenciados + Oculta pessoas, palavras, hashtags e threads de tudo o que você vê. + configurações de recebimento de nutzap + Os mints e a chave que os outros usam para enviar nutzaps para você. + lista + Uma lista salva neste dispositivo. + lista de relays públicos + Informa a todos onde encontrar suas publicações e para onde enviar respostas e menções a você. + endereços de pagamento + As formas pelas quais outras pessoas podem pagar você. + lista de relays privados + Onde seus rascunhos e dados privados são armazenados. + perfil + Seu nome, foto, bio e endereços de pagamento, como todos os veem. + lista de chats públicos + Os chats públicos em que você entrou. + feeds de relays favoritos + Os relays que você navega como feeds. + lista de grupos + Os grupos baseados em relays em que você entrou. + lista de relays de busca + Os relays usados quando você pesquisa. + lista de provedores de confiança + Os serviços em que você confia para classificar e verificar pessoas para você. + lista de relays confiáveis + Relays em que você confia para se conectar sem perguntar. diff --git a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml index 64cf4922d4..6a44e20832 100644 --- a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -4922,4 +4922,1174 @@ one-shot handoff rather than continuous sync. The exposure strings say what leaves the device; the fork strings say why the old phone stops. --> + Geocache + Traditionell + Multicache + Mystery + Mikro + Liten + Normal + Stor + Annan storlek + D%1$d + T%1$d + Först att hitta (FTF) + Konstverk + Key Quest + Verifierade fynd + Tagen + Arkiverad + Kört fast? Tryck för att visa ledtråden + Går den inte att läsa? Tryck för den andra versionen + I närheten + Hittad! + Verifierat fynd + Beviset stämmer inte + Läser in cache… + Gömd av %1$s + Navigera + Kopiera koordinater + Koordinater kopierade + Foton + Key Quest-uppdrag + Loggar + Ingen har loggat den här cachen ännu. Bli först. + Jag hittade den + Hittade den inte + Lägg till en anteckning + Rapportera behov av underhåll + Lägg till i en jakt + Ny jakt… + Du har inte skapat någon jakt ännu. + Redan med i den här jakten + + %1$d cache här + %1$d cacher här + + Ta bort det här fotot + Den här cachen har arkiverats. Den är inte längre i spel. + FTF tagen av %1$s + FTF är fortfarande ledig + Ägare + Redigera den här cachen + Arkivera den här cachen + Arkivera den. Hittare ser att den är ur spel, och beskrivningen går fortfarande att läsa. + Lås den första hittaren + Publicerar en F-tagg som anger %1$s. Detta är permanent. + Inga verifierade fynd ännu, så det finns ingen att låsa. + Visa verifieringskoden + Skriv ut den och lägg den i cachen. Amethyst sparar den inte — om du tappar bort den kan hittare inte längre bevisa att de var där. + Hittade inte (DNF) + Anteckning + Behöver underhåll + Logga ett fynd + Cache + Din logg + Hur gick det? + Lägg till ett foto + Ditt Key Quest-svar + Bevis på närvaro + Skanna koden i cachen + Skanna en annan kod + Verifierat — du var vid cachen + Signeras en gång med koden i lådan. Nyckeln sparas aldrig och lämnar aldrig den här skärmen. + Det där är ingen verifieringskod. + Den koden hör till en annan cache. + Den här cachen kräver inget bevis, så du kan logga den direkt. + Din egen plats bifogas inte till den här loggen. + Publicera + Lägg till en logg + I närheten + Karta + Jakter + Fynd + Mina + Ej hittade + D ≤ %1$d + T ≤ %1$d + Har ett pris + Bevis krävs + Använd min plats + Sorterat efter avstånd + Nyaste först + Göm en cache + Inga cacher här ännu. Prova en bredare följlista, eller göm den första. + Du har inte loggat något fynd ännu. + Du har inte gömt någon cache ännu. + Inga jakter ännu. + Fynd + FTF + Verifierade + Gömda + Centrera på mig + Tryck länge på kartan för att gömma en cache där + Öppna + Var + Vad + Extra + Bevis + Reläer + Detta publicerar en offentlig plats med cirka 5 meters noggrannhet. Alla på Nostr kan se den, och en publicerad händelse kan inte återkallas. + Välj platsen + Publicerar %1$s, plus grövre celler ned till 3 tecken för närhetssökning. + Namn + Beskrivning + Typ + Storlek + Svårighetsgrad + Terräng + Ledtråd + Hittare kommer att se + Kryptera ledtråden (ROT13) + Key Quest-uppdrag + Tillägg + Kräv bevis på närvaro + En verifieringsnyckel har skapats för den här cachen. + Var hittare ska logga + Publicera + Ge cachen ett namn. + Välj var cachen är gömd. + %1$d av %2$d hittade + Starta jakten + + %1$d cache + %1$d cacher + + Titel + Beskrivning + Bannerbild + Cacher, i ordning + Lägg till en cache + Flytta upp + Flytta ned + Ta bort + Sidtema + Kartstil + Ge jakten en titel. + Lägg till minst en cache. + Kunde inte publicera. Kontrollera reläanslutningen och försök igen. + Cordn-grupper + Koordinatorgrupp + Levereras av en koordinator, inte av reläer + Vad den här koordinatorn kan se + En koordinator är en enda server som för vidare varje meddelande i den här gruppen, i ordning. Det är en annan avvägning än med reläer — värt att känna till innan du behandlar det här som vilken chatt som helst. + Meddelandeinnehåll + Vilka som är med i gruppen + Vem som skickar vad + Oläsligt + Under en engångsnyckel + Kopplat till ditt riktiga konto + Detaljer + Koordinator %1$s + Detta är inte samma exponering som i en Marmot-grupp, där ingen enskild operatör har hela konversationen. + När du går med namnges riktiga konton i båda ändar, så koordinatorn vet vilka som är med i gruppen. + En och samma engångsnyckel skickar och hämtar för alla dina grupper här, så koordinatorn kan se att allihop är du. + En enda operatör har den fullständiga, ordnade historiken för varje grupp den för vidare. + Nyckelpaketet du publicerade är ett signerat belägg för att det här kontot använder cordn, och koordinatorn kan lämna ut det till vem som helst. + Meddelanden utfylls inte, så koordinatorn ser hur långt varje meddelande är. + Den här klienten krypterar inte till koordinatorn. Det är en bugg — rapportera den gärna. + Inte kontaktad ännu + Svarar + Svarar inte + + %1$d misslyckat försök i rad + %1$d misslyckade försök i rad + + Klistra in en cordn1…-länk som någon delat med dig för att se vilken koordinator som för vidare gruppen, och vad den operatören skulle få veta om dig om du gick med. + cordn1… + Granska + Klistra in + Rensa + Det där är ingen cordn-grupplänk: %1$s + Grupp-id + Koordinator + Nås via + Den här länken anger ingen koordinator, så den kan inte följas på egen hand. Fråga den som skickade den vilken koordinator som för vidare gruppen. + Att granska en länk går inte med i något, och att ha en gör dig inte till medlem. + Cordn-grupplänk + cordn koordinator grupp länk inbjudan mls chatt metadata integritet exponering coordinator group link invite + Cordn-koordinatorer + cordn koordinator lägg till ta bort rensa hälsa server mls chatt ordning coordinator add remove purge health + Cordn-nyckelpaket + cordn nyckelpaket publicera inbjudan reserv koordinator mls key package last resort + Cordn-säkerhetskopia + cordn säkerhetskopia återställ exportera importera lösenfras grupper återställning backup restore + Grupp %1$s + Inga meddelanden ännu + Cordn-grupp, förs vidare av %1$s + Meddelandet raderat + Röstmeddelande + Foto + Video + Fil + Inbjuden till en cordn-grupp? + Granska inbjudningar + Cordn + Grupper som levereras av en koordinator du väljer, i stället för av reläer. + Granska en grupplänk + Se vad en cordn1…-länk pekar på innan du agerar på den + Flytta från den här telefonen + Ta över från en annan telefon + Publicerade nyckelpaket + Länken + Koordinatorer du använder + Annonserar sig själva + Lägg till en manuellt + Lösenfras + Skapa en säkerhetskopia + Återställ från en säkerhetskopia + Koordinatorn + Den här enheten + Koordinatorer + Servrarna som för vidare dina grupper + Nyckelpaket + Det som gör att andra kan lägga till dig i en grupp + Säkerhetskopiera och återställ + En lösenfraskrypterad fil som du behåller + Flytta till en ny telefon + Lämna över dina grupper till en enhet du byter till + Flytta till en ny telefon + Dina cordn-grupper finns på den här enheten. Att flytta dem kräver en kod: den här telefonen publicerar dem, den nya telefonen läser dem, och den här telefonen slutar. + Logga först in med samma konto på den nya telefonen. Detta flyttar dina grupper, aldrig din nyckel. + Starta överlämningen + Publicerar… + Skanna den här på den nya telefonen + Koden är bara en pekare. Den som fotograferar den får inte veta något — det den pekar på är krypterat till dig. + Den här enheten har lämnat över sina grupper + Den har slutat skicka och ta emot, eftersom två telefoner som skickar från samma grupp skulle förstöra den för alla i den. Dina grupper finns kvar på den här enheten och inget har raderats. + Ta tillbaka den här enheten + Använd detta om flytten inte blev klar. Gör det bara om den nya telefonen inte har börjat skicka. + Ta emot från en annan telefon + Skanna koden + eller klistra in den + Hämta grupperna hit + Hämtar… + Detta ersätter alla cordn-grupper som redan finns på den här telefonen. De kan inte slås samman. + + %1$d grupp flyttad + %1$d grupper flyttade + + Det finns inga cordn-grupper på den här enheten att flytta. + Ställ in en mediaserver först — de krypterade dokumenten behöver någonstans att ligga medan den andra telefonen hämtar dem. + Vad som lämnar den här enheten + Ditt grupptillstånd krypteras och laddas upp till din mediaserver så att den andra telefonen kan hämta det. Det är oläsligt för servern, som bara ser en storlek och en tidpunkt. Radera blobbarna efteråt om du inte vill lämna något kvar. + Radera + Redigera + Redigerar ditt meddelande + Gå till meddelandet + Fäst + Svara + Lossa + Ändringen kunde inte genomföras. + Koordinatorn svarade inte, så inget ändrades. Försök igen om en stund. + %1$s har inte publicerat något nyckelpaket till den här koordinatorn, så de kan inte läggas till ännu. Be dem att först öppna cordn på den här koordinatorn. + Du kan inte ta bort dig själv från en cordn-grupp. + %1$s kunde inte ges någon väg in i gruppen, så tillägget avbröts. + %1$s är inte med i den här gruppen. + Den personen + Den här koordinatorn lämnade ut ett nyckelpaket som tillhör någon annan, så %1$s lades inte till. Inget ändrades. + Dina gruppkrypteringsnycklar, dina nyckelpaket och varje meddelande i de grupperna. Den som öppnar filen kan läsa konversationerna. Lösenfrasen är det enda som skyddar den när den väl lämnat Amethyst. + Vad filen innehåller + Din nsec kan inte återskapa cordn-grupper. En cordn-grupp lever som krypteringstillstånd på den här enheten och som en ordnad ström hos en koordinator som inte kan läsa den — förlorar du enheten utan säkerhetskopia är gruppen borta, inklusive allt som redan sagts i den. + Exportera + Säkerhetskopian sparad. + Det fungerade inte. Kontrollera lösenfrasen och filen. + Lösenfras + Återställ + Återställning ersätter cordn-grupperna på den här enheten med dem i filen. Den är till för en telefon som tagit över från en annan — inte ett sätt att använda samma grupper på två enheter samtidigt. + Alla cordn-grupper som nu finns på den här enheten tas bort och ersätts med dem i filen.\n\nÅterställ inte till en andra telefon medan den första fortfarande används. Båda skulle ha samma gruppnycklar, och så snart båda har skickat ett meddelande går gruppen sönder för alla — tyst, och utan väg tillbaka. + Ersätta den här enhetens cordn-grupper? + Återställ + Återställd. + Allt som skickas här krypteras för den här gruppen. Koordinatorn förmedlar det utan att kunna läsa det. + Inga meddelanden ännu + Nya meddelanden + I går + Meddelande + Lägg till + Kunde inte öppna den koordinatorn. + Kopiera koordinatorns npub + Leta efter koordinatorer + Lägg till + Läser annonser som dina reläer redan har. Ingen koordinator kontaktas, så ingen av dem får veta att du letade. + Kunde inte läsa annonser + Ingen annonserar på dina reläer. + Annonserade för %1$s sedan + Senast annonserad %1$s + + Inget hittades, och %1$d av dina reläer svarade inte. + Inget hittades, och %1$d av dina reläer svarade inte. + + En koordinator ordnar en grupps meddelanden och är det enda som kan göra det. Förlorar man den förloras ordningen, och en andra koordinator speglar inte den första — så det här är namngivna saker du väljer, inte utbytbara reläer. + + Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. + Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. + + Svarar. + Ett anrop misslyckades; försöker igen. + Inget har efterfrågats ännu. + Dölj dem som slutat annonsera + Fråga vem den är + Koordinatornyckel + Ett namn för den (ditt eget, valfritt) + Det här namnet är ditt och stannar på den här enheten. En koordinator kan inte bevisa ett namn, så den ombeds aldrig uppge något. + Fler åtgärder + Inga koordinatorer ännu. + Rensa bort + Att ta bort behåller grupperna på den här enheten, så om du lägger till koordinatorn igen kommer de tillbaka. Att rensa bort raderar dem: gruppnycklarna, lässtatusen och dina nyckelpaket försvinner, och de grupperna kan bara nås igen via en ny inbjudan.\n\nKoordinatorn meddelas inte och behåller allt den redan hade. + Rensa bort den här koordinatorn? + %1$s + Svarar på + %1$s +%2$d till + Ta bort + Byt namn + Säger sig vara: %1$s + En koordinators namn och version är strängar den själv valt. Dess publika nyckel är det enda som identifierar den. + Svarade, men angav inget namn. + Visa alla %1$d + Visa färre + + Visa %1$d som slutat annonsera + Visa %1$d som slutat annonsera + + Dessa har inte annonserat på över en månad. Att skapa en grupp på en som försvunnit kommer att misslyckas. + Skapa grupp + + Skapa och bjud in %1$d person + Skapa och bjud in %1$d personer + + Lägg till någon + Admin + + %1$d person kan lägga till och ta bort + %1$d personer kan lägga till och ta bort + + Bara jag hanterar den här gruppen + Bara du kan bjuda in, ta bort medlemmar eller ändra gruppens uppgifter. Du kan lämna över det till någon annan senare. + Koordinator + Ändra + Hitta koordinatorer + Klar + En annan koordinator + Koordinatorns publika nyckel (hex eller npub) + Reläer den svarar på, ett per rad + + Når %1$d person — alla du valt + Når alla dina %1$d personer + + Når %1$d av %2$d — resten skulle bli utanför + Inte kontrollerad — den tillfrågas om du väljer den + Beskrivning (valfritt) + Alla kan lägga till och ta bort + Alla i gruppen kan bjuda in, ta bort medlemmar och ändra gruppens uppgifter. Vilken medlem som helst kan utse admins senare, vilket upphäver detta. + En cordn-grupp ordnas av en koordinator du väljer. Gruppen skapas på den här enheten — koordinatorn får veta att gruppen finns först när du bjuder in någon eller skickar ett meddelande. + Gruppen kunde inte skapas. + Gruppnamn + Ingen koordinator vald + Varje inbjudan är ett Welcome-meddelande som lämnas hos koordinatorn, så de kan misslyckas en i taget. Ingen har meddelats: de ser det nästa gång de öppnar sin app. + Koordinatorn avvisade inbjudan + Skicka en länk + Tillagd i gruppen, men inbjudan nådde inte fram — försök igen från gruppskärmen + Öppna chatten + Ingen nyckel här, så inget Welcome-meddelande kunde lämnas + Gruppen är skapad + Väntar på dem + Klar + Ingen tillagd ännu. En grupp kan skapas tom och inbjudningar skickas senare, men att ange personer nu är det som visar vilken koordinator som kan nå dem. + Ingen ännu — tryck för att lägga till personer + Att ange personer nu är det som talar om för oss vilken koordinator som kan nå dem. Var och en får ett Welcome-meddelande som lämnas hos den koordinatorn, och som de hämtar nästa gång de öppnar sin app. + + Nåbar via %1$d koordinator + Nåbar via %1$d koordinatorer + + Ingen nyckel hos någon koordinator du använder + Inte kontrollerad ännu + Vem som kan lägga till och ta bort + Namnge den + Vilka som är med + Var den finns + Ny Cordn-grupp + + %1$d person kan inte nås av någon koordinator du använder + %1$d personer kan inte nås av någon koordinator du använder + + Du, alltid admin + Alla i gruppen kommer att se meddelandet som återkallat, och Amethyst slutar visa texten. Koordinatorn har fortfarande den krypterade kopia som skickades, och den som redan har läst det har redan läst det. + Återkalla meddelande + Accepterat av koordinatorn + Uppfattat + Vad den här koordinatorn kan se + Gruppinfo + Den här gruppen är inte tillgänglig. Dess koordinator kan ha tagits bort. + Lägg till en koordinator i Inställningar för att börja använda cordn-grupper. + + %1$d grupp + %1$d grupper + + Inget kommer fram från de här grupperna. Amethyst fortsätter att försöka. + Koordinatorer + Inga cordn-grupper ännu + En cordn-grupp finns hos en koordinator som håller reda på medlemskapet och skickar meddelanden vidare, utan att någonsin se vad någon skriver. + Starta en + Lägg till någon + Ingen hittades med det namnet. + De kan bara läggas till om de har publicerat ett nyckelpaket till den här koordinatorn. + Namn, npub eller namn@domän + Admin + + Gruppens %1$d admin förblir oförändrad. + Gruppens %1$d admins förblir oförändrade. + + Koordinator + Koordinatornyckel + Koordinatorlänk + Kopiera koordinatorns nprofile + Redigera uppgifter + Den här gruppen har inga admins: alla kan lägga till, ta bort och byta namn, permanent. + Epok + Grupp-id + Kan läggas till via den här koordinatorn + Medlemmar + %1$s slutar ta emot meddelanden härifrån. De behåller det de redan har läst, och detta kan inte ångras — de skulle behöva bjudas in igen. + Ta bort från gruppen? + Ta bort + Spara + Tekniska detaljer + Gå med + Avböj + Att avböja är permanent. För att komma in igen måste du bjudas in på nytt. + Kontrolleras när du öppnar den här skärmen, och aldrig annars — att fråga en koordinator om någon har bjudit in dig avslöjar att du finns här, så Amethyst gör det inte i bakgrunden. + Kunde inte läsa inbjudningar. + + %1$d person är redan med i den här gruppen + %1$d personer är redan med i den här gruppen + + +%1$d till + Du har ingen koordinator öppen. cordn-inbjudningar kommer via en koordinator, så det finns ingenstans att leta ännu. + Inga väntande inbjudningar. + Kontrollera igen + En inbjudan lämnades till en annan enhet + Cordn-inbjudningar + %1$s svarade inte + Via %1$s + Publicering signerar ett belägg med din egen kontonyckel. Den koordinatorn vet då att kontot finns och går att bjuda in — permanent, och oavsett om någon bjuder in dig. + Andra kan bara lägga till dig i en cordn-grupp genom att ta ett nyckelpaket du publicerat till den koordinatorn. cordn har ingen annan plats att förvara ett, så utan något publicerat kan ingen bjuda in dig — och inget talar om för dem varför. + Kunde inte läsa nyckelpaketen. + Inget reservpaket. När engångspaketen tar slut misslyckas inbjudningar. + Ett reservpaket är publicerat, så en inbjudan kan fortfarande göras när engångspaketen tagit slut. + + %1$d paket som den här enheten inte kan öppna + %1$d paket som den här enheten inte kan öppna + + Koordinatorn lämnar ut dessa till den som bjuder in dig, men den privata halvan finns inte på den här enheten — den tillhör en annan installation. Om den installationen är borta ger en inbjudan med något av dessa ett Welcome-meddelande som ingen någonsin kan öppna. + Publicera ett + Publicera reservpaket + + %1$d engångspaket tillgängligt. + %1$d engångspaket tillgängliga. + + När du väl har publicerat här ersätter Amethyst tyst paketen allteftersom de används. Det första publicerar den aldrig åt dig. + Återkalla alla + Återkalla dem + Be att få gå med i gruppen + Detta publicerar ett nyckelpaket med din egen kontonyckel och talar om för koordinatorn att du vill gå med i gruppen — oavsett om någon svarar. + Kunde inte be att få gå med. + Förfrågan skickad. En medlem i gruppen måste lägga till dig; inbjudan dyker upp under cordn-inbjudningar. + Skanna + Kunde inte öppna den filen. + Ingen mediaserver är inställd för det här kontot, så filen har ingenstans att ta vägen. Välj en i Inställningar. + Filen kunde inte läsas. + Kunde inte skicka den filen. + + %1$d medlem + %1$d medlemmar + + Meddelandet raderat + Koordinator + Markör + Skickat + Meddelandedetaljer + redigerat + Fäst av %1$s + + %1$d fäst meddelande i den här gruppen + %1$d fästa meddelanden i den här gruppen + + Nästa fästa meddelande + Föregående fästa meddelande + Visa alla fästa meddelanden + Fästa meddelanden + Reaktioner + Lägg till + Bara en admin kan godkänna. I en grupp utan admins är det alla. + Sök efter förfrågningar + Avfärda + Kunde inte läsa förfrågningarna. + Ingen väntar på att få gå med. + Förfrågningar om att gå med + Skicka + Det gick inte att skicka. + Den här gruppens koordinator är inte öppen, så inget kan skickas ännu. + Kopierad + Kopiera länk + Alla som har den här länken kan be att få gå med. En admin måste fortfarande släppa in dem. + Dela den här gruppen + Spela upp röstmeddelande + Spela in ett röstmeddelande + Stoppa och skicka + Amethyst behöver kameran för att skanna QR-koder. Inget spelas in eller laddas upp — bilderna avkodas på enheten och kastas sedan. + Tillåt kamera + Kameraåtkomst är avstängd för Amethyst. Du kan slå på den igen i Androids inställningar. + Öppna inställningar + QR-avkodaren kunde inte starta på den här enheten. Du kan fortfarande klistra in koden som text. + Tänd lampan + Släck lampan + Det är mörkt — prova lampan + Skanna en bild + Klistra in + Ingen QR-kod hittades i bilden + Det finns inget i urklipp att skanna + Tryck på koden du vill ha + + Fångade %1$s av %2$s del — fortsätt + Fångade %1$s av %2$s delar — fortsätt + + Återställ zoom + Amethyst kan inte öppna den här koden + Den här koden innehåller en privat nyckel eller en parningshemlighet, så innehållet visas inte här. Använd skärmen som förväntar sig den. + Det här är en privat nyckel. Klistra in den på inloggningsskärmen i stället. + Det här är en plånboksanslutning. Lägg till den under Plånboksinställningar. + Det här är en fjärrsignatäradress. Amethyst delar ut sådana så att andra appar kan be den signera — den kan inte logga in med en. + Det här är en app som vill ansluta till din signatär. Öppna den från NIP-46-signatärskärmen. + Det här är en Lightning-faktura. + Det här är en Cashu-token. + Det här är en webblänk. + Det här är vanlig text, inte en Nostr-kod. + Det här är en Nostr-kod, men inte en som den här skärmen kan öppna. + Profil + Anteckning + Nostr-kod + Relä + Privat nyckel + Plånboksanslutning + Fjärrsignatäradress + Anslutningsbegäran till signatär + Lightning-faktura + Cashu-token + Webblänk + Vanlig text + Öppna länk + Kopiera + Kopierat + Skanna igen + Använd träningspassen du redan har registrerat + Anslut Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) för att bygga Min träning, din egen träningssammanfattning, och för att fylla i det här passet från ett som din klocka redan har registrerat i stället för att skriva in varje siffra för hand. + Amethyst använder Health Connect för att bygga Min träning — din egen träningssammanfattning, på din telefon. + Min träning (meny → Du → Min träning) visar hur du har tränat: hur mycket du gjorde den här veckan jämfört med förra, hur din tid fördelas mellan löpning, cykling, promenader, simning och gym, dina bästa prestationer, hur många dagar du tränade och din nuvarande svit. Amethyst bygger den bilden från träningspassen som din klocka eller träningsapp redan har sparat i Health Connect, tillsammans med pass du har loggat i Amethyst. Allt räknas ut på din telefon och visas för dig. + Träning · vilken aktivitet du utförde, när den började och hur länge den varade. Det här är själva passet: det styr antalet pass, din träningstid, uppdelningen per aktivitet, dina aktiva dagar och din svit. + Distans · hur långt du tog dig, visat som din distans den här veckan jämfört med förra, ditt veckosnitt, din distans per aktivitet och din längsta distans. + Aktiva och totala kalorier · energin ett pass förbrände, visat som din energi den här veckan och hur den står sig mot förra veckan. Aktiva kalorier används när din källa registrerar dem; totala kalorier används som reserv för källor som bara registrerar total energi. + Puls · genomsnitt och maxvärde för varje pass, sammanslaget till din genomsnittliga och högsta puls för perioden. Det är så du skiljer en lätt vecka från en tuff med samma distans. + Steg · antalet steg under en löptur, promenad eller vandring, visat som ditt veckosnitt för steg och ditt högsta antal steg. + Höjdmeter · hur mycket du klättrade, visat som ditt veckosnitt för stigning och din största stigning. Det är det som skiljer en platt vecka från en kuperad. + Läser bara träningspass från de senaste 4 veckorna, och bara medan Min träning eller passredigeraren är öppen. Läser aldrig i bakgrunden. + Skickar aldrig dina hälsodata någonstans. Min träning räknas ut på din telefon och stannar där. Om du senare vill publicera ett pass trycker du på Dela för det passet, läser inlägget som Amethyst fyllt i och publicerar det själv — ett pass i taget, aldrig automatiskt. + Hela funktionen är valfri. Återkalla behörigheterna i Health Connect när som helst — Min träning går då tillbaka till sin uppmaning — eller stäng av förslagen i redigeraren under Inställningar → Skrivinställningar. Resten av Amethyst fortsätter att fungera. + Min träning + Senaste 4 veckorna + Läser in distans, kalorier och puls… + De senaste fyra hela veckorna av din träning: passen du har publicerat, plus allt Health Connect registrerar på den här telefonen. Amethyst räknar ut sammanfattningen på enheten och publicerar ingenting av den. + Den här veckan + jämfört med förra veckan + Veckosnitt + Pass + Aktiva dagar + Dagar i rad + Tid + %1$d h %2$d min + %1$d min + %1$d s + Distans + Kalorier + Steg + Stigning + Snittpuls + Maxpuls + Per aktivitet + Bästa prestationer + Längsta distans + Längsta pass + Största stigning + Flest steg + Högsta puls + Senaste passen + Inget loggat de senaste 4 veckorna. Pass du publicerar, och de som din klocka registrerar, visas här. + Följ din träning + Anslut Health Connect för att se dina veckosummor, utvecklingen vecka för vecka, bästa prestationer och träningssvit. Allt stannar på din telefon. + Anslut Health Connect för att lägga till passen din klocka registrerar, med puls, steg och stigning. + Anslut + Dela det här passet + bpm + kcal + km + mi + m + ft + Läser in + Väntar på att dina privata meddelanden ska läsas in… + %1$02d:%2$02d + %1$d:%2$02d:%3$02d + Gruppen uppdaterad + Bilaga + Meddelande + %1$s: %2$s + Skapa en grupp + Marmot-inbjudningar går till en annan enhet och kan inte öppnas här. + Avfärda + Publicera från den här enheten + Öppna zappad anteckning + Undertexter + Slå på eller av undertexter (dolt när videon saknar sådana) + Inte skickat + Försök igen + Skickar… + Skickar fortfarande… + Det här kontot har tagits bort från Amethyst. + Av + Stäng av undertexter + Slå på undertexter + Undertexter + Applista + Artikellista + Bokmärkessamling + Favoritflöden + Git-författare + Git-förråd + Betrodda wikiförfattare + Betrodda wikireläer + Intressen + Mediaföljningar + Startpaket för media + Tystat för typ %1$s + Bildlista + Grupper + Videosamarbete + Videolista + Videoundertexter + Amethyst för Android kan inte logga in med en bunker://-adress. Den delar ut sådana så att andra appar kan be den signera, men den kan inte logga in med en. Använd en extern signatärapp eller Amethyst Desktop. + Det där är en app som vill ansluta till din signatär, inte ett sätt att logga in. Logga in först och öppna den sedan från NIP-46-signatärskärmen. + Min träning + Den här listan har privata medlemmar som bara dess skapare kan se + Redigera cache + Redigera jakt + Cache + Jakt + Cachejakter + Geocacher + Logga ett fynd + Göm en cache + Ny jakt + Accepterade ett samarbetsomnämnande + Avböjde ett samarbetsomnämnande + Omnämnande accepterat + Ljud från + Med + Inspirerad av + Referenser + Privat lista + + %1$d video + %1$d videor + + Den här enheten tar nu emot dina Marmot-inbjudningar. + Det gick inte att publicera KeyPackage: %1$s + Inget relä accepterade KeyPackage. Kontrollera dina reläinställningar och försök igen. + Skickar… + Kunde inte skickas. Tryck för att försöka igen. + + %1$d nytt meddelande + %1$d nya meddelanden + + Cordn-grupper + End-to-end-krypterade grupper som ordnas av en koordinator du väljer, i stället för av reläer. + Cordn-grupp + Krypterad gruppchatt som ordnas av en koordinator du väljer. + Koordinerad + Bäst för team som vill ha en pålitlig ordning i konversationen. + Skapa Cordn-grupp + End-to-end-krypterad med MLS — koordinatorn kan aldrig läsa ett meddelande. + En gemensam ordning för alla, så att historiken inte kan kastas om av en felaktig klocka. + Koordinatorn får veta vilka som är med i gruppen och när ni pratar, även om den inte kan läsa vad ni säger. + 3D-objekt + %1$d hörn · %2$d ytor + Det här 3D-objektet kunde inte läsas (regel %1$s) + Cyberspace-avatar + Den här avataren har inte betalat för sin storlek, så den ritas inte + Belyst; tryck för att rita objektet platt + Platt; tryck för att belysa objektet + 1 enhet = %1$s + Gömd i dataspace · %1$s + Gömd i ideaspace · %1$s + Standardavatar + Ingen form publicerad, så alla ser den här + Gömd på en plats + Gömd i en låda med %1$s regioner + Gömd i en enda region, som ledtråden anger exakt + Ingen ledtråd, så den kan vara var som helst i cyberspace + Utom räckhåll: ledtråden anger 2^%1$d regioner att söka i + Sök efter den + Stoppa + Beräknar sökningens kostnad på den här enheten… + Sökt i %1$s av %2$s regioner + + Utom räckhåll: cirka %1$d minuts sökning på den här enheten + Utom räckhåll: cirka %1$d minuters sökning på den här enheten + + + Utom räckhåll: cirka %1$d timmes sökning på den här enheten + Utom räckhåll: cirka %1$d timmars sökning på den här enheten + + Inte i lådan som ledtråden angav + Regionen hittades, men innehållet kunde inte läsas + + %1$d föremål som gömts här matchade inte sin signatur och kastades + %1$d föremål som gömts här matchade inte sina signaturer och kastades + + Öppnad, och det fanns ingenting i den + Osignerad, så den här författaren är bara ett påstående + Ett föremål av typ %1$d, som den här klienten inte ritar + + %1$d byte av något som den här klienten inte läser + %1$d byte av något som den här klienten inte läser + + Stäng + Kopiera länk + Sök + Textstorlek + Datorversion + Till startskärm + Annan webbläsare + Helskärm + Redigera adress + Bakåt + Framåt + Ladda om + Sluta läsa in + Lägg till i favoriter + Ta bort från favoriter + Dela + Kopiera länk + Sök på sidan + Textstorlek + Lägg till på startskärmen + Datorversion av webbplatsen + Öppna i webbläsare + Öppna i %1$s + Helskärm + Du lämnade %1$s + Tillbaka till appen + Mindre text + Större text + Återställ + %1$d% + Integritet + Onion-routing + Webbplatsen kan inte se din IP-adress + Webbplatsen kan se din IP-adress + Webbplatsinställningar + Inget tillåtet ännu + Vad den kan komma åt + Dina nycklar lämnar aldrig Amethyst + Varje signering, publicering, uppladdning eller betalning kräver ditt godkännande + Ingen särskild åtkomst + Den körs i sandlåda och kan inte agera på ditt konto + Hantera behörigheter + nSite · i sandlåda + nApplet · i sandlåda + Konsol + Säker anslutning + Inte säker + Onion-routad + App i sandlåda + Kamera + Mikrofon + Plats + Fråga + Tillåt + Blockera + %1$s %2$s + tillåten + blockerad + Sök eller ange adress + Gå + Rensa + Klistra in och gå + Använd den här adressen + Sök på sidan + %1$d / %2$d + Inga träffar + Föregående träff + Nästa träff + Stäng sökning på sidan + Alla + Fel + Varningar + Kopiera + Rensa + Inget loggat ännu + Den här webbplatsen vill använda + Se det din kamera ser + Höra det din mikrofon hör + Veta ungefär var du är + Samtal kan avslöja din IP-adress även via Tor. + Tillåt under besöket + Bara den här gången + Tillåt inte + %1$s säger + Den här sidan säger + Låt inte den här sidan visa fler dialogrutor + Ditt svar + Lämna webbplatsen? + Ändringar du har gjort kanske inte sparas. + Lämna + OK + Avbryt + Anslutning + Anslutningen är krypterad + Information du skickar kan inte läsas på vägen. + Anslutningen är inte krypterad + Ange inte lösenord eller betalningsuppgifter. + Onion-routad via Tor + Öppen webb + Certifikat + Utfärdat till %1$s av %2$s · giltigt till %3$s + Behörigheter + Cookies och webbplatsdata + Inloggningar och inställningar som den här webbplatsen sparat för det här kontot + Rensa webbplatsdata + Logga ut från den här webbplatsen och radera dess data? + Blockera %1$s igen + Behåll %1$s + Gå med i %1$s igen + Tysta %1$s igen + Återställ %1$s + Återställ flöden + Återställ platser + Återställ profil + Återställ leverantörer + Återställ reläer + Återställ ämnen + Återställ plånbok + Acceptera + + %1$s och lade till %2$d + %1$s och lade till %2$d + + + %1$s och blockerade %2$d + %1$s och blockerade %2$d + + + %1$s och följde %2$d + %1$s och följde %2$d + + + %1$s och gick med i %2$d + %1$s och gick med i %2$d + + + %1$s och tystade %2$d + %1$s och tystade %2$d + + Dina synkade Amethyst-appinställningar ändrades i en annan app som inte är Amethyst. Det kan orsaka problem. + Du tömde din Concord-lista + Du ändrade din Concord-lista + + Du tog bort %1$d DM-relä + Du tog bort %1$d DM-reläer + + + Du tog bort %1$d favoritflöde + Du tog bort %1$d favoritflöden + + + Du slutade följa %1$d person + Du slutade följa %1$d personer + + + Du slutade följa %1$d hashtagg + Du slutade följa %1$d hashtaggar + + + Du tog bort %1$d indexerande relä + Du tog bort %1$d indexerande reläer + + + Du tog bort %1$d nyckelrelä för gruppchattar + Du tog bort %1$d nyckelreläer för gruppchattar + + + Du lämnade %1$d offentlig chatt + Du lämnade %1$d offentliga chattar + + + Du lämnade %1$d gemenskap + Du lämnade %1$d gemenskaper + + + Du lämnade %1$d grupp + Du lämnade %1$d grupper + + + Du lämnade %1$d chattrum + Du lämnade %1$d chattrum + + + Du slutade tysta %1$d sak + Du slutade tysta %1$d saker + + + Du tog bort %1$d offentligt relä + Du tog bort %1$d offentliga reläer + + Du tog bort din nutzap-nyckel + Du bytte ut din nutzap-nyckel + + Du tog bort %1$d nutzap-mint + Du tog bort %1$d nutzap-mints + + + Du tog bort %1$d BOLT12-erbjudande + Du tog bort %1$d BOLT12-erbjudanden + + + Du tog bort %1$d sätt att få betalt + Du tog bort %1$d sätt att få betalt + + + Du slutade följa %1$d plats + Du slutade följa %1$d platser + + + Du tog bort %1$d privat relä + Du tog bort %1$d privata reläer + + + Du tog bort %1$d profilfält + Du tog bort %1$d profilfält + + + Du tog bort %1$d reläflöde + Du tog bort %1$d reläflöden + + + Du tog bort %1$d sökrelä + Du tog bort %1$d sökreläer + + + Du bytte leverantör för %1$d förtroendetjänst + Du bytte leverantör för %1$d förtroendetjänster + + + Du slutade lita på %1$d relä + Du slutade lita på %1$d reläer + + + Du slutade blockera %1$d relä + Du slutade blockera %1$d reläer + + Du tömde din Cashu-plånbok + Du ändrade din Cashu-plånbok + Tillagt (%1$s) + Ändrat (%1$s) + Tillagda: %1$s + Ändrade: %1$s + Borttagna: %1$s + Använd ny version + Du lade till privata (krypterade) objekt. + Du ändrade även privata (krypterade) objekt. De kan inte jämföras här. + Du tog bort alla privata (krypterade) objekt. + endast läsning + läsning och skrivning + endast skrivning + Borttaget (%1$s) + Återställ sparad version + Ändring i en annan app: %1$s + Flöden + Chattrum + Gemenskaper + Hashtaggar + Länkade identiteter + Platser + Mints + Nutzap-nycklar + Erbjudanden + Övriga poster + Betalningsadresser + Personer + Profilfält + Offentliga chattar + Reläer + Grupper + Trådar + Förtroendeleverantörer + Ord + Om + Banner + Födelsedag + Bot-markering + CLINK-erbjudande + Visningsnamn + LNURL + Lightning-adress + Användarnamn + Nostr-adress + Bild + Pronomen + Webbplats + Tillåten + Ändrad i en annan app · %1$s + Dina Concord-gemenskaper är krypterade till dig, så de kan inte jämföras här. En återställning lägger tillbaka exakt det som den här enheten sparade. + Flyttad + Borttagna: %1$s + Borttagen + Du skapade den + Du ändrade den + Du tömde den + Flöde + Borta + Hashtaggar du följer + Inkorg + där andra når dig + gick med + behållna + Behållna: %1$s + Mottagarnyckel borttagen + Mottagarnyckel utbytt + Nutzaps kommer att låsas till den här nya nyckeln. Om den här enheten inte har den kanske du inte kan lösa in dem här. + lämnade + Lämnad + Listad + Flyttad + %1$s och %2$s till + Ny version + Nya: %1$s + nya + Ny + Nyligen blockerade + Nyligen tystade: %1$s + Inte längre blockerade + Inte angivet + Inget här + BOLT12-erbjudande + + %1$d erbjudande borttaget + %1$d erbjudanden borttagna + + Utkorg + där du publicerar + + %1$d sätt att betala dig borttaget + %1$d sätt att betala dig borttagna + + De som betalar dig ser bara det som finns i den här listan. + platser borta + Platser du följer + De är krypterade nu, vilket troligen är vart de offentliga objekten ovan tog vägen. + Du gjorde objekt privata + De är krypterade, så de kan inte jämföras här. + Du ändrade privata objekt + De är krypterade, så bara du kan se vad de var. En återställning tar tillbaka alla. + Du tog bort alla privata objekt + Tar emot + omdöpt + Omdöpt + På den här enheten + + Sök bland %1$s person + Sök bland %1$s personer + + Söker + Tjänst + Följare + Rang + Ämnen + + %1$s av %2$s tjänst ändrad + %1$s av %2$s tjänster ändrade + + De kommer nu från någon annan, eller från ingen + Fortfarande blockerade + Lagrar + Borttagna · %1$s + Redigerade · %1$s + Nya · %1$s + %1$s har tömts + %1$s har uppdaterats + ämnen borta + Rang, antal följare och skräppostfiltrering som du ser kommer från de här leverantörerna. + Betrodd + Blockering hävd: %1$s + Amethyst ansluter till dem igen + Enheter + Tystnad hävd: %1$s + De här kan nå dina flöden igen + Ej betrodd + Din plånboks mints och nycklar är krypterade till dig, så de kan inte jämföras objekt för objekt här. En återställning lägger tillbaka exakt det som den här enheten sparade. + hette ”%1$s” + Ord & hashtaggar + synkade appinställningar + Amethyst-inställningar som synkas mellan dina enheter. De är krypterade, så bara att de finns kan jämföras. + lista över blockerade reläer + Reläer som den här appen aldrig ansluter till. + BOLT12-erbjudanden + Lightning-erbjudandena som andra använder för att betala dig. + Cashu-plånbok + Dina inställningar och mints för ecash-plånboken. De är krypterade, så bara att de finns kan jämföras. + gemenskapslista + Gemenskaperna du gått med i. + Concord-gemenskapslista + Concord-gemenskaperna du gått med i. De är krypterade, så bara att de finns kan jämföras. + DM-relälista + Dit andra levererar dina privata meddelanden. + lista över tillfälliga chattar + De tillfälliga chattrummen du gått med i. + lista över favoritflöden + De algoritmiska flödena du har sparat. + följlista + Avgör vems inlägg som visas i ditt hemflöde. + följda platser + Platser vars inlägg visas i dina flöden. + följda hashtaggar + Hashtaggar vars inlägg visas i dina flöden. + lista över indexerande reläer + Reläerna som används för att hitta andras profiler och relälistor. + nyckelreläer för gruppchattar + Där andra hittar nycklarna för att lägga till dig i krypterade gruppchattar. + tystnadslista + Döljer personer, ord, hashtaggar och trådar från allt du ser. + inställningar för nutzap-mottagning + Mints och nyckel som andra använder för att skicka nutzaps till dig. + lista + En lista som sparats på den här enheten. + offentlig relälista + Talar om för alla var dina inlägg finns och vart svar och omnämnanden till dig ska skickas. + betalningsadresser + Sätten som andra kan betala dig på. + privat relälista + Där dina utkast och privata data lagras. + profil + Ditt namn, din bild, din bio och dina betalningsadresser, så som alla ser dem. + lista över offentliga chattar + De offentliga chattarna du gått med i. + favoritreläflöden + Reläerna du bläddrar i som flöden. + grupplista + De relä-baserade grupperna du gått med i. + lista över sökreläer + Reläerna som används när du söker. + lista över förtroendeleverantörer + Tjänsterna du litar på för att rangordna och verifiera personer åt dig. + lista över betrodda reläer + Reläer du litar på att ansluta till utan att fråga. From fbd710f9cffabe3d05247900357928f8247ee6fb Mon Sep 17 00:00:00 2001 From: davotoula Date: Sun, 27 Sep 2026 11:16:29 +0200 Subject: [PATCH 14/19] refactor(geocache): name the hunt's cache count instead of a let chain --- .../screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt index 48fb28b80e..7028c9f23f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geocaches/detail/GeocacheAddToHuntSheet.kt @@ -179,7 +179,8 @@ fun GeocacheAddToHuntSheet( if (already) { stringRes(Res.string.geocache_hunt_already_on) } else { - hunt.geocaches().size.let { pluralStringRes(Res.plurals.geocache_hunt_caches, it, it) } + val cacheCount = hunt.geocaches().size + pluralStringRes(Res.plurals.geocache_hunt_caches, cacheCount, cacheCount) }, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, From d993ee8abc972a1e3b1bc3ef1a3dcca1f7938a11 Mon Sep 17 00:00:00 2001 From: davotoula <1747287+davotoula@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:00:17 +0000 Subject: [PATCH 15/19] chore: sync Crowdin translations and seed translator npub placeholders --- amethyst/src/main/res/values-cs/strings.xml | 8 +- .../src/main/res/values-de-rDE/strings.xml | 8 +- .../src/main/res/values-pt-rBR/strings.xml | 8 +- .../src/main/res/values-sv-rSE/strings.xml | 8 +- .../composeResources/values-cs/strings.xml | 1286 ++++++++--------- .../values-de-rDE/strings.xml | 1206 ++++++++-------- .../values-hi-rIN/strings.xml | 40 +- .../values-hu-rHU/strings.xml | 519 ++++++- .../values-pl-rPL/strings.xml | 232 +++ .../values-pt-rBR/strings.xml | 1206 ++++++++-------- .../values-sv-rSE/strings.xml | 1206 ++++++++-------- docs/changelog/translators.json | 12 +- 12 files changed, 3230 insertions(+), 2509 deletions(-) diff --git a/amethyst/src/main/res/values-cs/strings.xml b/amethyst/src/main/res/values-cs/strings.xml index 7130fe54f3..0d68f74921 100644 --- a/amethyst/src/main/res/values-cs/strings.xml +++ b/amethyst/src/main/res/values-cs/strings.xml @@ -192,6 +192,9 @@ Nový obrázek Nové krátké video Nové video + Skenovat QR kód + Skenovat QR + Naskenovat QR kód Zvýrazněný text @@ -311,6 +314,7 @@ Těžení proof of work… (%1$d příspěvků ve frontě) Těžení proof of work + Odesílání na relé… Zrušit ≈ %1$s zbývá každou chvíli @@ -377,8 +381,4 @@ Zvuk zapnutý. Klikněte pro ztlumení Hrát Pozastavit - Skenovat QR kód - Skenovat QR - Naskenovat QR kód - Odesílání na relé… diff --git a/amethyst/src/main/res/values-de-rDE/strings.xml b/amethyst/src/main/res/values-de-rDE/strings.xml index 2e4d479622..5a488067cf 100644 --- a/amethyst/src/main/res/values-de-rDE/strings.xml +++ b/amethyst/src/main/res/values-de-rDE/strings.xml @@ -180,6 +180,9 @@ Neues Bild Neuer Kurzfilm Neues Video + QR-Code scannen + QR scannen + Einen QR-Code scannen Hervorgehobener Text @@ -291,6 +294,7 @@ Arbeitsnachweis wird geschürft… (%1$d Beiträge in der Warteschlange) Arbeitsnachweis wird geschürft + Wird an Relays gesendet… Abbrechen ≈ %1$s verbleibend jeden Moment @@ -349,8 +353,4 @@ Ton ein. Zur Stummschaltung drücken Abspielen Pausen - QR-Code scannen - QR scannen - Einen QR-Code scannen - Wird an Relays gesendet… diff --git a/amethyst/src/main/res/values-pt-rBR/strings.xml b/amethyst/src/main/res/values-pt-rBR/strings.xml index af84b30244..0cc858952f 100644 --- a/amethyst/src/main/res/values-pt-rBR/strings.xml +++ b/amethyst/src/main/res/values-pt-rBR/strings.xml @@ -180,6 +180,9 @@ Nova Foto Novo Vídeo Curto Novo Vídeo + Escanear código QR + Ler QR + Escanear um código QR Texto destacado @@ -291,6 +294,7 @@ Minerando prova de trabalho… (%1$d publicações na fila) Minerando prova de trabalho + Enviando para relays… Cancelar ≈ %1$s restante a qualquer momento @@ -349,8 +353,4 @@ Som ligado. Clique para silenciar Reproduzir Pausar - Escanear código QR - Ler QR - Escanear um código QR - Enviando para relays… diff --git a/amethyst/src/main/res/values-sv-rSE/strings.xml b/amethyst/src/main/res/values-sv-rSE/strings.xml index 294ee30c6f..4d6749fdb5 100644 --- a/amethyst/src/main/res/values-sv-rSE/strings.xml +++ b/amethyst/src/main/res/values-sv-rSE/strings.xml @@ -180,6 +180,9 @@ Ny bild Ny kortfilm Ny video + Skanna QR-kod + Skanna QR + Skanna en QR-kod Markerad text @@ -291,6 +294,7 @@ Minar bevis på arbete… (%1$d inlägg i kön) Minar bevis på arbete + Skickar till reläer… Avbryt ≈ %1$s kvar när som helst nu @@ -349,8 +353,4 @@ Ljud på. Klicka för att stänga av ljudet Spela Pausa - Skanna QR-kod - Skanna QR - Skanna en QR-kod - Skickar till reläer… diff --git a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml index eb86dff2d2..06b182ff77 100644 --- a/commonsUI/src/commonMain/composeResources/values-cs/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-cs/strings.xml @@ -174,6 +174,156 @@ Dopravní zácpa Silniční událost + Keš + Tradiční + Multi-keš + Mystery + Mikro + Malá + Normální + Velká + Jiná velikost + D%1$d + T%1$d + První nálezce + Umělecké dílo + Key Quest + Ověřené nálezy + Obsazeno + Archivováno + Nevíte si rady? Klepnutím zobrazíte nápovědu + Nečitelné? Klepnutím zobrazíte druhé čtení + Poblíž + Nalezeno! + Ověřený nález + Důkaz nesouhlasí + Načítání keše… + Ukryl %1$s + Navigovat + Kopírovat souřadnice + Souřadnice zkopírovány + Fotky + Mise Key Quest + Logy + Tuto keš zatím nikdo nezalogoval. Buďte první. + Nalezeno + Nenalezeno + Přidat poznámku + Nahlásit potřebu údržby + Přidat do výpravy + Nová výprava… + Zatím jste nevytvořili žádnou výpravu. + Už je v této výpravě + + %1$d keš zde + %1$d keše zde + %1$d keše zde + %1$d keší zde + + Odebrat tuto fotku + Tato keš byla archivována. Už není ve hře. + Prvním nálezcem se stal %1$s + Místo prvního nálezce je stále volné + Vlastník + Upravit tuto keš + Archivovat tuto keš + Archivovat. Nálezci uvidí, že už není ve hře, a listing zůstane čitelný. + Potvrdit prvního nálezce + Zveřejní tag F se jménem %1$s. Je to trvalé. + Zatím žádné ověřené nálezy, takže není koho potvrdit. + Zobrazit ověřovací kód + Vytiskněte ho a vložte do keše. Amethyst ho neukládá — pokud ho ztratíte, nálezci už nebudou moci prokázat, že tam byli. + Nenalezeno + Poznámka + Potřebuje údržbu + Zapsat nález + Keš + Váš log + Jak to šlo? + Přidat fotku + Vaše odpověď Key Quest + Důkaz přítomnosti + Naskenujte kód v keši + Naskenovat jiný kód + Ověřeno — byli jste u keše + Jednou podepsáno kódem z krabičky. Klíč se nikdy neukládá a nikdy neopustí tuto obrazovku. + To není ověřovací kód. + Tento kód patří jiné keši. + Tato keš nevyžaduje důkaz, takže ji můžete rovnou zalogovat. + K tomuto logu není připojena vaše vlastní poloha. + Odeslat + Přidat log + Poblíž + Mapa + Výpravy + Nálezy + Moje + Nenalezené + D ≤ %1$d + T ≤ %1$d + S cenou + Vyžaduje důkaz + Použít mou polohu + Seřazeno podle vzdálenosti + Nejnovější první + Ukrýt keš + Zatím tu nejsou žádné keše. Zkuste širší seznam sledovaných, nebo ukryjte první. + Zatím jste nezapsali žádný nález. + Zatím jste neukryli žádnou keš. + Zatím žádné výpravy. + Nálezy + První nálezy + Ověřené + Ukryté + Vycentrovat na mě + Dlouhým stiskem na mapě tam ukryjete keš + Otevřít + Kde + Co + Doplňky + Důkaz + Relaye + Tímto zveřejníte veřejnou polohu s přesností asi na 5 metrů. Uvidí ji kdokoli na Nostru a zveřejněnou událost nelze vzít zpět. + Vyberte místo + Zveřejní %1$s a k tomu hrubší buňky až po 3 znaky pro vyhledávání v okolí. + Název + Popis + Typ + Velikost + Obtížnost + Terén + Nápověda + Nálezci uvidí + Zašifrovat nápovědu (ROT13) + Mise Key Quest + Modifikátory + Vyžadovat důkaz přítomnosti + Pro tuto keš byl vygenerován ověřovací klíč. + Kam mají nálezci logovat + Zveřejnit + Pojmenujte keš. + Vyberte, kde je keš ukrytá. + Nalezeno %1$d z %2$d + Zahájit výpravu + + %1$d keš + %1$d keše + %1$d keše + %1$d keší + + Název + Popis + Obrázek banneru + Keše v pořadí + Přidat keš + Posunout nahoru + Posunout dolů + Odebrat + Motiv stránky + Styl mapy + Pojmenujte výpravu. + Přidejte alespoň jednu keš. + Zveřejnění se nezdařilo. Zkontrolujte připojení k relayům a zkuste to znovu. Zapy na toto se rozdělují mezi: %1$d%% Value-for-Value @@ -198,6 +348,53 @@ Banner profilu Nasměrujte na QR kód + Amethyst potřebuje fotoaparát ke skenování QR kódů. Nic se nenahrává ani neodesílá — snímky se dekódují v zařízení a poté se zahodí. + Povolit fotoaparát + Přístup k fotoaparátu je pro Amethyst vypnutý. Znovu ho můžete zapnout v nastavení Androidu. + Otevřít nastavení + Dekodér QR kódů se v tomto zařízení nepodařilo spustit. Kód můžete stále vložit jako text. + Zapnout světlo + Vypnout světlo + Je tma — zkuste světlo + Naskenovat obrázek + Vložit + V obrázku nebyl nalezen žádný QR kód + Ve schránce není nic ke skenování + Klepněte na požadovaný kód + + Načteno %1$s z %2$s části — pokračujte + Načteno %1$s z %2$s částí — pokračujte + Načteno %1$s z %2$s části — pokračujte + Načteno %1$s z %2$s částí — pokračujte + + Obnovit přiblížení + Amethyst tento kód nedokáže otevřít + Tento kód obsahuje soukromý klíč nebo tajemství pro spárování, proto se jeho obsah zde nezobrazuje. Použijte obrazovku, která ho očekává. + Toto je soukromý klíč. Vložte ho raději na přihlašovací obrazovce. + Toto je připojení peněženky. Přidejte ho v nastavení peněženky. + Toto je adresa vzdáleného podepisovatele. Amethyst je rozdává, aby ho ostatní aplikace mohly žádat o podpis — přihlásit se s ní nedokáže. + Toto je aplikace, která se chce připojit k vašemu podepisovateli. Otevřete ji z obrazovky podepisovatele NIP-46. + Toto je Lightning faktura. + Toto je Cashu token. + Toto je webový odkaz. + Toto je prostý text, ne kód Nostr. + Toto je kód Nostr, ale tato obrazovka ho nedokáže otevřít. + Profil + Poznámka + Kód Nostr + Relé + Soukromý klíč + Připojení peněženky + Adresa vzdáleného podepisovatele + Žádost o připojení k podepisovateli + Lightning faktura + Cashu token + Webový odkaz + Prostý text + Otevřít odkaz + Kopírovat + Zkopírováno + Skenovat znovu Zobrazit QR kód Profilový obrázek Váš profilový obrázek @@ -648,17 +845,70 @@ Hodiny Minuty Sekundy + Využijte tréninky, které už máte zaznamenané + Připojte Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) — získáte Moji kondici, vlastní přehled tréninků, a tento trénink vyplníte podle záznamu z hodinek místo ručního psaní každého čísla. Připojit %1$s km Z Health Connect Co Amethyst čte Health Connect a Amethyst + Amethyst používá Health Connect pro funkci Moje kondice — váš vlastní přehled tréninků přímo v telefonu. + Moje kondice (menu → Vy → Moje kondice) ukazuje, jak trénujete: kolik jste toho udělali tento týden oproti minulému, jak se váš čas dělí mezi běh, cyklistiku, chůzi, plavání a posilovnu, vaše nejlepší výkony, kolik dní jste trénovali a vaši aktuální sérii. Amethyst tento obraz sestaví z tréninků, které vaše hodinky nebo fitness aplikace už uložily do Health Connect, spolu s tréninky, které jste zaznamenali přímo v Amethystu. Vše se vypočítá ve vašem telefonu a zobrazí jen vám. Co Amethyst čte a proč + Cvičení · jakou aktivitu jste dělali, kdy začala a jak dlouho trvala. To je samotný trénink: určuje počet tréninků, dobu tréninku, rozpis podle aktivit, aktivní dny a vaši sérii. + Vzdálenost · jak daleko jste se dostali; zobrazuje se jako vzdálenost tento týden oproti minulému, týdenní průměr, vzdálenost podle aktivity a nejdelší vzdálenost. + Aktivní a celkové kalorie · energie spálená při tréninku; zobrazuje se jako energie tento týden a její srovnání s minulým týdnem. Aktivní kalorie se použijí, pokud je váš zdroj zaznamenává; celkové kalorie slouží jako náhrada u zdrojů, které zaznamenávají jen celkovou energii. + Tepová frekvence · průměr a maximum během každého tréninku, sloučené do průměrné a nejvyšší tepové frekvence za období. Podle ní poznáte lehký týden od náročného při stejné vzdálenosti. + Kroky · počet kroků při běhu, chůzi nebo túře; zobrazuje se jako týdenní průměr kroků a nejvyšší počet kroků. + Nastoupané převýšení · kolik jste nastoupali; zobrazuje se jako týdenní průměr stoupání a největší stoupání. Právě to odlišuje rovinatý týden od kopcovitého. Co Amethyst nedělá + Čte jen tréninky z posledních 4 týdnů, a to jen když je otevřená Moje kondice nebo editor tréninku. Nikdy nečte na pozadí. Nikdy nic nezapisuje do Health Connect a nikdy nežádá o trasu vašeho cvičení, polohu ani jiný typ zdravotních údajů. + Vaše zdravotní údaje nikdy nikam neposílá. Moje kondice se počítá ve vašem telefonu a zůstává v něm. Pokud později budete chtít zveřejnit jeden trénink, klepnete u něj na Sdílet, přečtete si příspěvek, který Amethyst vyplnil, a zveřejníte ho sami — vždy jen jeden trénink, nikdy automaticky. + Celá funkce je volitelná. Oprávnění můžete kdykoli odvolat v Health Connect — Moje kondice se pak vrátí k úvodní výzvě — nebo vypnout návrhy v editoru v Nastavení → Nastavení editoru. Zbytek Amethystu funguje dál. Přečíst si celé zásady ochrany soukromí + Moje kondice + Poslední 4 týdny + Načítám vzdálenost, kalorie a tepovou frekvenci… + Poslední čtyři celé týdny vašeho tréninku: tréninky, které jste zveřejnili, a vše, co Health Connect zaznamenává v tomto telefonu. Amethyst přehled počítá v zařízení a nic z něj nezveřejňuje. + Tento týden + oproti minulému týdnu + Týdenní průměr + Tréninky + Aktivní dny + Série dní + Čas + %1$d h %2$d min + %1$d min + %1$d s + Vzdálenost + Kalorie + Kroky + Stoupání + Prům. TF + Max. TF + Podle aktivity + Nejlepší výkony + Nejdelší vzdálenost + Nejdelší trénink + Největší stoupání + Nejvíce kroků + Nejvyšší tepová frekvence + Nedávné tréninky + Za poslední 4 týdny nic zaznamenáno. Zobrazí se zde tréninky, které zveřejníte, i ty, které zaznamenají vaše hodinky. + Sledujte svůj trénink + Připojte Health Connect a uvidíte týdenní součty, vývoj oproti minulému týdnu, nejlepší výkony a tréninkovou sérii. Vše zůstává ve vašem telefonu. + Připojte Health Connect a přidejte tréninky zaznamenané hodinkami, včetně tepové frekvence, kroků a stoupání. + Připojit + Sdílet tento trénink + tep/min + kcal + km + mi + m + ft Zatím nenalezeny žádné nSites. Obnovit Integrovaný prohlížeč vyžaduje Android 11 nebo novější. @@ -1094,6 +1344,8 @@ Datum a čas ukončení ankety Anketa končí za %1$s Hledá se událost %1$s + Načítání + Čekám na načtení vašich soukromých zpráv… Odeslat Zap Přidat veřejnou zprávu Přidat soukromou zprávu @@ -1233,6 +1485,8 @@ + %1$02d:%2$02d + %1$d:%2$02d:%3$02d Zavřít Přepnout kameru Hlasový hovor @@ -1367,6 +1621,14 @@ Uživatelské nastavení Hledat nastavení Pro „%1$s“ nebyla nalezena žádná nastavení + Skupina aktualizována + Příloha + Zpráva + %1$s: %2$s + Vytvořit skupinu + Pozvánky Marmot chodí na jiné zařízení a zde je nelze otevřít. + Zavřít + Zveřejnit z tohoto zařízení Resetovat stav Marmot? Tímto trvale odstraníte všechny skupinové chaty Marmot, historii zpráv a MLS klíče v tomto zařízení pro aktuální účet. Ostatní uživatelé nebudou upozorněni a stále vás mohou vidět ve skupinách až do jejich dalšího potvrzení. Tuto akci nelze vrátit zpět. Nový KeyPackage bude publikován při příští synchronizaci aplikace. Resetovat @@ -1641,6 +1903,7 @@ Žádné transakce neodpovídají tomuto filtru Načítání… Přijato + Otevřít poznámku se zapem Odesláno Obnovit Vše @@ -1957,6 +2220,8 @@ Přehrávat video v plovoucím okně (skryto, pokud není podporováno) Odeslat do zařízení Odeslat video do přijímače Chromecast ve vaší Wi-Fi (skryto pro místní soubory) + Titulky + Zapnout nebo vypnout titulky (skryto, pokud je video nemá) Vyberte animaci zobrazenou při přehrávání zvukových poznámek. Vypnuto Spektrální pruhy @@ -2644,6 +2909,7 @@ Nesplňuješ požadavky sítě důvěry této komunity. Nejnovější dokument s pravidly komunity byl odmítnut jako zastaralý. Skupiny Marmot + Skupiny Cordn Vytvořit skupinu Vytvořit skupinu Marmot Bude vytvořena nová MLS skupina. Členy můžete přidat poté. @@ -2943,13 +3209,18 @@ Upozorní vás, když někdo odpoví na váš příspěvek Odpovědi Nové odpovědi + Neodesláno Odpovědět + Zkusit znovu + Odesílání… + Stále se odesílá… Upozorní vás, když někdo přepošle váš příspěvek %1$s přeposlal váš příspěvek Přeposlání Nová přeposlání Oznámení, když je naplánovaný příspěvek publikován nebo selže při publikaci. Naplánované příspěvky + Tento účet byl z Amethystu odebrán. Upozorní vás, když vás někdo zapsne %1$s satů pro %1$s @@ -3180,6 +3451,10 @@ Zrušit ztlumení Adresa URL zpětného volání nebyla nalezena v odpovědi %1$s Zrušit + Vypnuto + Vypnout titulky + Zapnout titulky + Titulky Cashu Token Kontroluji mint… Nepodařilo se použít Cashu @@ -3693,10 +3968,12 @@ Prolog dobrodružství Čtení dobrodružství Scéna dobrodružství + Seznam aplikací Doporučení aplikací Schůzka Aplikace RSVP na schůzku + Seznam článků Audio hlavička Audio stopa Vytvořené podcasty @@ -3709,6 +3986,7 @@ Blossom ověření Blossom servery Seznam záložek + Sada záložek Broadcast relaye Kalendář Definice kanálu @@ -3746,6 +4024,7 @@ Balíčky emoji Dočasný chat Dočasné chatovací místnosti + Oblíbené kanály Oblíbené podcasty Oblíbené relaye Hlavičky souborů @@ -3754,28 +4033,36 @@ Balíčky sledování Sledování geohashe Dárkové balíčky + Git autoři Git problém Git záplata Pull request Aktualizace PR Git odpověď Git repozitář + Git repozitáře Git stav: Aplikováno Git stav: Uzavřeno Git stav: Koncept Git stav: Otevřeno + Důvěryhodní autoři wiki + Důvěryhodná wiki relé Sledované hashtagy Zvýraznění HTTP ověření Seznam indexovacích relayů + Zájmy Živé chaty Živé přenosy + Sledování médií + Startovní balíček médií Lékařská data Místnost schůzky Prostor schůzky Hudební playlist Hudební stopa Seznam ztlumených + Ztlumeno pro druh %1$s Pojmenované záložky Servery Nests NIP @@ -3790,6 +4077,7 @@ Outbox relaye Platba na Seznamy lidí + Seznam obrázků Obrázky Připnuté Epizoda podcastu @@ -3816,6 +4104,7 @@ Vyhledávací relaye Krátká videa Krátká videa (odpověď) + Skupiny Komentáře k torrentu Torrenty Důvěryhodní poskytovatelé @@ -3824,7 +4113,10 @@ Uživatelská nastavení Stav uživatele Video + Spolupráce na videu + Seznam videí Video (odpověď) + Titulky videa Hlasová zpráva Hlasová odpověď Probuzení @@ -3865,6 +4157,8 @@ Změnit místo na mapě Kanály podle polohy Vyberte místo na mapě + Amethyst pro Android se nemůže přihlásit pomocí adresy bunker://. Tyto adresy rozdává, aby ho ostatní aplikace mohly žádat o podpis, ale přihlásit se s nimi nedokáže. Použijte externí podepisovací aplikaci nebo Amethyst Desktop. + Toto je aplikace, která se chce připojit k vašemu podepisovateli, ne způsob přihlášení. Nejprve se přihlaste a pak ji otevřete z obrazovky podepisovatele NIP-46. Pro zvýšení příspěvků se přihlaste s privátním klíčem Přihlaste se pomocí soukromého klíče, abyste mohli následovat Používáte veřejný klíč a veřejné klíče jsou pouze pro čtení. Přihlaste se pomocí soukromého klíče, abyste mohli skrýt slovo nebo větu @@ -3965,6 +4259,7 @@ Ztlumit oznámení Ztlumené. Klikněte pro odztlumení Mé soubory Blossom + Moje kondice Moje seznamy Jméno je povinné %1$dms @@ -4258,6 +4553,7 @@ Aplikace vzdáleného podepisovatele Vzdálený podepisovatel + Tento seznam má soukromé členy, které vidí jen jeho autor %1$d majetek je zbalený %1$d majetky jsou zbalený @@ -4856,12 +5152,21 @@ Kalendáře Šachy Objevit + Upravit keš + Upravit výpravu + Keš + Výprava + Výpravy + Keše Domů Importovat sledované + Zapsat nález Média Zprávy Playlisty Hudba + Ukrýt keš + Nová výprava Upozornění Epizody Podcasty @@ -5063,6 +5368,20 @@ Žádost o zmizení odeslána Lepší kvalita při menší velikosti souboru, ale ne všechna zařízení podporují přehrávání H.265. Použít kodek H.265/HEVC + Přijal uvedení jako spolupracovník + Odmítl uvedení jako spolupracovník + Uvedení přijato + Zvuk od + Účinkuje + Inspirováno + Odkazuje na + Soukromý seznam + + %1$d video + %1$d videa + %1$d videa + %1$d videí + Stahování videa bylo zahájeno… Tlačítka přehrávače videa Video uloženo do video galerie telefonu @@ -5128,169 +5447,20 @@ Služba blesku příjemce v %1$s není k dispozici. Byla vypočtena z adresy blesku "%2$s". Chyba: %3$s. Zkontrolujte, zda je server aktivní a zda je adresa blesku správná Nelze vytvořit bleskovou fakturu z %1$s: Prvek pr nebyl nalezen ve výsledném JSON. Nelze vytvořit bleskovou fakturu. Zpráva od %1$s: %2$s + Toto zařízení nyní přijímá vaše pozvánky Marmot. + Zveřejnění KeyPackage selhalo: %1$s + Žádné relé KeyPackage nepřijalo. Zkontrolujte nastavení relé a zkuste to znovu. + Odesílání… + Nepodařilo se odeslat. Klepnutím to zkusíte znovu. + + %1$d nová zpráva + %1$d nové zprávy + %1$d nové zprávy + %1$d nových zpráv + - - - - - Keš - Tradiční - Multi-keš - Mystery - Mikro - Malá - Normální - Velká - Jiná velikost - D%1$d - T%1$d - První nálezce - Umělecké dílo - Key Quest - Ověřené nálezy - Obsazeno - Archivováno - Nevíte si rady? Klepnutím zobrazíte nápovědu - Nečitelné? Klepnutím zobrazíte druhé čtení - Poblíž - Nalezeno! - Ověřený nález - Důkaz nesouhlasí - Načítání keše… - Ukryl %1$s - Navigovat - Kopírovat souřadnice - Souřadnice zkopírovány - Fotky - Mise Key Quest - Logy - Tuto keš zatím nikdo nezalogoval. Buďte první. - Nalezeno - Nenalezeno - Přidat poznámku - Nahlásit potřebu údržby - Přidat do výpravy - Nová výprava… - Zatím jste nevytvořili žádnou výpravu. - Už je v této výpravě - - %1$d keš zde - %1$d keše zde - %1$d keše zde - %1$d keší zde - - Odebrat tuto fotku - Tato keš byla archivována. Už není ve hře. - Prvním nálezcem se stal %1$s - Místo prvního nálezce je stále volné - Vlastník - Upravit tuto keš - Archivovat tuto keš - Archivovat. Nálezci uvidí, že už není ve hře, a listing zůstane čitelný. - Potvrdit prvního nálezce - Zveřejní tag F se jménem %1$s. Je to trvalé. - Zatím žádné ověřené nálezy, takže není koho potvrdit. - Zobrazit ověřovací kód - Vytiskněte ho a vložte do keše. Amethyst ho neukládá — pokud ho ztratíte, nálezci už nebudou moci prokázat, že tam byli. - Nenalezeno - Poznámka - Potřebuje údržbu - Zapsat nález - Keš - Váš log - Jak to šlo? - Přidat fotku - Vaše odpověď Key Quest - Důkaz přítomnosti - Naskenujte kód v keši - Naskenovat jiný kód - Ověřeno — byli jste u keše - Jednou podepsáno kódem z krabičky. Klíč se nikdy neukládá a nikdy neopustí tuto obrazovku. - To není ověřovací kód. - Tento kód patří jiné keši. - Tato keš nevyžaduje důkaz, takže ji můžete rovnou zalogovat. - K tomuto logu není připojena vaše vlastní poloha. - Odeslat - Přidat log - Poblíž - Mapa - Výpravy - Nálezy - Moje - Nenalezené - D ≤ %1$d - T ≤ %1$d - S cenou - Vyžaduje důkaz - Použít mou polohu - Seřazeno podle vzdálenosti - Nejnovější první - Ukrýt keš - Zatím tu nejsou žádné keše. Zkuste širší seznam sledovaných, nebo ukryjte první. - Zatím jste nezapsali žádný nález. - Zatím jste neukryli žádnou keš. - Zatím žádné výpravy. - Nálezy - První nálezy - Ověřené - Ukryté - Vycentrovat na mě - Dlouhým stiskem na mapě tam ukryjete keš - Otevřít - Kde - Co - Doplňky - Důkaz - Relaye - Tímto zveřejníte veřejnou polohu s přesností asi na 5 metrů. Uvidí ji kdokoli na Nostru a zveřejněnou událost nelze vzít zpět. - Vyberte místo - Zveřejní %1$s a k tomu hrubší buňky až po 3 znaky pro vyhledávání v okolí. - Název - Popis - Typ - Velikost - Obtížnost - Terén - Nápověda - Nálezci uvidí - Zašifrovat nápovědu (ROT13) - Mise Key Quest - Modifikátory - Vyžadovat důkaz přítomnosti - Pro tuto keš byl vygenerován ověřovací klíč. - Kam mají nálezci logovat - Zveřejnit - Pojmenujte keš. - Vyberte, kde je keš ukrytá. - Nalezeno %1$d z %2$d - Zahájit výpravu - - %1$d keš - %1$d keše - %1$d keše - %1$d keší - - Název - Popis - Obrázek banneru - Keše v pořadí - Přidat keš - Posunout nahoru - Posunout dolů - Odebrat - Motiv stránky - Styl mapy - Pojmenujte výpravu. - Přidejte alespoň jednu keš. - Zveřejnění se nezdařilo. Zkontrolujte připojení k relayům a zkuste to znovu. - Skupiny Cordn Skupina s koordinátorem Doručováno koordinátorem, ne relayi Co tento koordinátor vidí @@ -5330,6 +5500,9 @@ Dostupné přes Tento odkaz neuvádí žádného koordinátora, takže ho samotný nelze použít. Zeptejte se odesílatele, který koordinátor skupinu přenáší. Prohlédnutím odkazu se k ničemu nepřipojíte a to, že ho máte, z vás nedělá člena. + Odkaz na skupinu Cordn cordn koordinátor skupina odkaz pozvánka mls chat metadata soukromí odhalení coordinator group link invite privacy exposure Koordinátoři Cordn @@ -5338,6 +5511,10 @@ cordn balíček klíčů zveřejnit pozvánka záložní koordinátor mls key package publish invite last resort coordinator Záloha Cordn cordn záloha obnovení export import heslo skupiny obnova backup restore passphrase groups recovery + Skupiny Cordn + End-to-end šifrované skupiny, jejichž pořadí zpráv určuje vámi zvolený koordinátor, nikoli relé. + Skupina %1$s Zatím žádné zprávy Skupina Cordn přenášená přes %1$s @@ -5346,8 +5523,19 @@ Fotka Video Soubor + Skupina Cordn + Šifrovaný skupinový chat řazený koordinátorem, kterého si vyberete. + Koordinovaná + Nejlepší pro týmy, které chtějí jedno spolehlivé pořadí konverzace. + Vytvořit skupinu Cordn + End-to-end šifrování pomocí MLS — koordinátor nikdy nepřečte žádnou zprávu. + Jedno dohodnuté pořadí pro všechny, takže historii nemohou přeházet špatně nastavené hodiny. + Koordinátor zjistí, kdo je ve skupině a kdy spolu mluvíte, i když nemůže číst, co píšete. Máte pozvánku do skupiny cordn? Zobrazit pozvánky + Cordn Skupiny doručované koordinátorem, kterého si sami zvolíte, místo relayů. Prohlédnout odkaz na skupinu @@ -5399,489 +5587,6 @@ Nejprve nastavte mediální server — zašifrované dokumenty musí někde počkat, než si je druhý telefon stáhne. Co opouští toto zařízení Stav vašich skupin se zašifruje a nahraje na váš mediální server, aby si ho druhý telefon mohl stáhnout. Server ho nedokáže přečíst, vidí jen velikost a čas. Pokud nechcete nic zanechat, soubory potom smažte. - Smazat - Upravit - Úprava vaší zprávy - Přejít na zprávu - Připnout - Odpovědět - Odepnout - Tuto změnu nebylo možné provést. - Koordinátor neodpověděl, takže se nic nezměnilo. Zkuste to za chvíli znovu. - %1$s na tomto koordinátoru nezveřejnil balíček klíčů, takže ho zatím nelze přidat. Požádejte ho, aby na tomto koordinátoru nejprve otevřel cordn. - Ze skupiny cordn nemůžete odebrat sami sebe. - Pro %1$s se nepodařilo zajistit přístup do skupiny, takže přidání bylo zrušeno. - %1$s není v této skupině. - Tato osoba - Tento koordinátor poskytl balíček klíčů, který patří někomu jinému, takže %1$s nebyl přidán. Nic se nezměnilo. - Šifrovací klíče vašich skupin, vaše balíčky klíčů a všechny zprávy v těchto skupinách. Kdokoli soubor otevře, může konverzace číst. Jakmile soubor opustí Amethyst, chrání ho jen heslo. - Co soubor obsahuje - Váš nsec skupiny cordn neobnoví. Skupina cordn existuje jako šifrovací stav na tomto zařízení a jako seřazený proud zpráv na koordinátoru, který ho nedokáže přečíst — přijdete-li o zařízení bez zálohy, skupina je pryč, včetně všeho, co v ní už zaznělo. - Exportovat - Záloha uložena. - Nepovedlo se. Zkontrolujte heslo a soubor. - Heslo - Obnovit - Obnovení nahradí skupiny cordn na tomto zařízení těmi ze souboru. Je určeno pro telefon, který převzal roli jiného — ne k používání stejných skupin na dvou zařízeních zároveň. - Všechny skupiny cordn, které jsou nyní na tomto zařízení, budou odebrány a nahrazeny těmi ze souboru.\n\nNeobnovujte zálohu na druhý telefon, dokud se ten první stále používá. Oba by měly stejné klíče skupin, a jakmile oba odešlou zprávu, skupina se rozbije pro všechny — potichu a nevratně. - Nahradit skupiny cordn na tomto zařízení? - Obnovit - Obnoveno. - Vše, co sem pošlete, je zašifrováno pro tuto skupinu. Koordinátor to předává dál, aniž by to mohl přečíst. - Zatím žádné zprávy - Nové zprávy - Včera - Zpráva - Přidat - Tohoto koordinátora se nepodařilo otevřít. - Kopírovat npub koordinátora - Hledat koordinátory - Přidat - Čte oznámení, která už vaše relaye přenášejí. Žádný koordinátor není kontaktován, takže se žádný z nich nedozví, že jste hledali. - Oznámení se nepodařilo načíst - Na vašich relayích se nikdo neohlašuje. - Ohlášeno před %1$s - Naposledy ohlášeno %1$s - - Nic nenalezeno a %1$d z vašich relayů neodpověděl. - Nic nenalezeno a %1$d z vašich relayů neodpověděly. - Nic nenalezeno a %1$d z vašich relayů neodpovědělo. - Nic nenalezeno a %1$d z vašich relayů neodpovědělo. - - Koordinátor určuje pořadí zpráv jedné skupiny a nic jiného to dělat nemůže. Jeho ztrátou se ztratí i pořadí a druhý koordinátor ten první nezrcadlí — proto jde o konkrétní servery, které si zvolíte, ne o zaměnitelné relaye. - - Neodpovídá — %1$d požadavek za sebou selhal. Skupiny na něm se nesynchronizují. - Neodpovídá — %1$d požadavky za sebou selhaly. Skupiny na něm se nesynchronizují. - Neodpovídá — %1$d požadavku za sebou selhalo. Skupiny na něm se nesynchronizují. - Neodpovídá — %1$d požadavků za sebou selhalo. Skupiny na něm se nesynchronizují. - - Odpovídá. - Požadavek selhal, zkouší se znovu. - Zatím nebyl o nic požádán. - Skrýt ty, které se přestaly ohlašovat - Zjistit, kdo to je - Klíč koordinátora - Název (váš vlastní, volitelný) - Tento název je váš a zůstává na tomto zařízení. Koordinátor nemůže název prokázat, proto se ho na něj nikdy neptáme. - Další akce - Zatím žádní koordinátoři. - Vymazat - Odebráním zůstanou skupiny na tomto zařízení, takže po opětovném přidání koordinátora se vrátí s ním. Vymazáním se smažou: klíče skupin, pozice čtení i vaše balíčky klíčů zmizí a do těchto skupin se lze vrátit jen s novou pozvánkou.\n\nKoordinátor se o tom nedozví a ponechá si vše, co už měl. - Vymazat tohoto koordinátora? - %1$s - Odpovídá na - %1$s +%2$d další - Odebrat - Přejmenovat - Tvrdí, že je: %1$s - Název a verzi si koordinátor zvolil sám. Identifikuje ho jen jeho veřejný klíč. - Odpověděl, ale nic neuvedl. - Zobrazit vše (%1$d) - Zobrazit méně - - Zobrazit %1$d, který se přestal ohlašovat - Zobrazit %1$d, které se přestaly ohlašovat - Zobrazit %1$d, které se přestaly ohlašovat - Zobrazit %1$d, které se přestaly ohlašovat - - Tito koordinátoři se neohlásili déle než měsíc. Vytvoření skupiny na takovém, který už zanikl, selže. - Vytvořit skupinu - - Vytvořit a pozvat %1$d osobu - Vytvořit a pozvat %1$d osoby - Vytvořit a pozvat %1$d osoby - Vytvořit a pozvat %1$d osob - - Přidat někoho - Administrátor - - %1$d osoba může přidávat a odebírat - %1$d osoby mohou přidávat a odebírat - %1$d osoby může přidávat a odebírat - %1$d osob může přidávat a odebírat - - Tuto skupinu spravuji jen já - Jen vy můžete zvát, odebírat členy nebo měnit údaje skupiny. Později to můžete předat někomu jinému. - Koordinátor - Změnit - Najít koordinátory - Hotovo - Jiný koordinátor - Veřejný klíč koordinátora (hex nebo npub) - Relaye, na kterých odpovídá, jeden na řádek - - Zastihne vaši %1$d osobu - Zastihne všechny %1$d vaše osoby - Zastihne %1$d vaší osoby - Zastihne všech %1$d vašich osob - - Zastihne %1$d z %2$d — ostatní by zůstali mimo - Neověřeno — výběrem se to zjistí - Popis (volitelné) - Všichni mohou přidávat a odebírat - Každý ve skupině může zvát, odebírat členy a měnit její údaje. Kterýkoli člen může později jmenovat administrátory, čímž tento režim skončí. - Pořadí zpráv ve skupině cordn určuje jeden koordinátor, kterého si zvolíte. Skupina se vytváří na tomto zařízení — koordinátor se o její existenci dozví, až někoho pozvete nebo pošlete zprávu. - Skupinu se nepodařilo vytvořit. - Název skupiny - Není zvolen žádný koordinátor - Každá pozvánka je zpráva Welcome ponechaná na koordinátoru, takže mohou selhat i jednotlivě. Nikdo nedostal oznámení: pozvánku uvidí, až příště otevřou svou aplikaci. - Koordinátor pozvánku odmítl - Poslat odkaz - Přidán do skupiny, ale pozvánka ho nezastihla — zkuste to znovu z obrazovky skupiny - Otevřít chat - Zde není žádný klíč, takže nešlo ponechat zprávu Welcome - Skupina existuje - Čeká se na ně - Hotovo - Zatím nikdo nepřidán. Skupinu lze vytvořit prázdnou a pozvánky poslat později, ale právě uvedením lidí teď zjistíte, který koordinátor je zastihne. - Zatím nikdo — klepnutím přidáte lidi - Uvedením lidí teď zjistíme, který koordinátor je zastihne. Každý z nich dostane zprávu Welcome ponechanou na tomto koordinátoru a vyzvedne si ji, až příště otevře svou aplikaci. - - Dostupný na %1$d koordinátoru - Dostupný na %1$d koordinátorech - Dostupný na %1$d koordinátoru - Dostupný na %1$d koordinátorech - - Žádný klíč na žádném z vašich koordinátorů - Zatím neověřeno - Kdo může přidávat a odebírat - Pojmenování - Kdo v ní bude - Kde poběží - Nová skupina Cordn - - %1$d osobu nezastihne žádný z vašich koordinátorů - %1$d osoby nezastihne žádný z vašich koordinátorů - %1$d osoby nezastihne žádný z vašich koordinátorů - %1$d osob nezastihne žádný z vašich koordinátorů - - Vy, vždy administrátor - Všichni ve skupině uvidí tuto zprávu jako staženou a Amethyst přestane zobrazovat její text. Koordinátor si stále uchovává zašifrovanou kopii, v jaké byla odeslána, a kdo ji už četl, ten ji už četl. - Stáhnout zprávu - Přijato koordinátorem - Rozumím - Co tento koordinátor vidí - Informace o skupině - Tato skupina není dostupná. Její koordinátor mohl být odebrán. - Přidejte koordinátora v Nastavení, abyste mohli začít používat skupiny cordn. - - %1$d skupina - %1$d skupiny - %1$d skupiny - %1$d skupin - - Z těchto skupin nic nepřichází. Amethyst to stále zkouší znovu. - Koordinátoři - Zatím žádné skupiny cordn - Skupina cordn žije na jednom koordinátoru, který spravuje její členství a předává zprávy, aniž by kdy viděl, co kdo píše. - Založit skupinu - Přidat někoho - Pod tímto jménem nikdo nenalezen. - Přidat lze jen ty, kdo na tomto koordinátoru zveřejnili balíček klíčů. - Jméno, npub nebo jmeno@domena - Admin - - %1$d administrátor skupiny zůstává beze změny. - %1$d administrátoři skupiny zůstávají beze změny. - %1$d administrátora skupiny zůstává beze změny. - %1$d administrátorů skupiny zůstává beze změny. - - Koordinátor - Klíč koordinátora - Odkaz na koordinátora - Kopírovat nprofile koordinátora - Upravit údaje - Tato skupina nemá administrátory: každý může přidávat, odebírat a přejmenovávat, a to natrvalo. - Epocha - ID skupiny - Lze přidat na tomto koordinátoru - Členové - %1$s odsud přestane dostávat zprávy. Co už přečetl, mu zůstane, a nelze to vrátit zpět — musel by být pozván znovu. - Odebrat ze skupiny? - Odebrat - Uložit - Technické podrobnosti - Připojit se - Odmítnout - Odmítnutí je trvalé. Pro návrat budete potřebovat novou pozvánku. - Kontroluje se jen při otevření této obrazovky a nikdy jindy — dotaz na koordinátora, zda vás někdo pozval, mu prozradí, že jste tady, a proto to Amethyst nedělá na pozadí. - Pozvánky se nepodařilo načíst. - - %1$d osoba už je v této skupině - %1$d osoby už jsou v této skupině - %1$d osoby už je v této skupině - %1$d osob už je v této skupině - - +%1$d další - Nemáte otevřeného žádného koordinátora. Pozvánky cordn přicházejí přes koordinátora, takže zatím není kde hledat. - Žádné čekající pozvánky. - Zkontrolovat znovu - Pozvánka byla ponechána pro jiné zařízení - Pozvánky Cordn - %1$s neodpověděl - Přes %1$s - Zveřejněním podepíšete záznam vlastním klíčem svého účtu. Daný koordinátor se pak dozví, že tento účet existuje a lze ho pozvat — natrvalo, a bez ohledu na to, zda vás někdo pozve. - Ostatní vás mohou přidat do skupiny cordn jen pomocí balíčku klíčů, který jste zveřejnili na daném koordinátoru. Cordn ho nemá kde jinde uchovat, takže bez zveřejněného balíčku vás nikdo nemůže pozvat — a nic mu neřekne proč. - Balíčky klíčů se nepodařilo načíst. - Žádný záložní balíček. Jakmile dojdou jednorázové, pozvánky budou selhávat. - Záložní balíček je zveřejněn, takže pozvánku lze vytvořit i poté, co dojdou jednorázové. - - %1$d balíček, který toto zařízení nedokáže otevřít - %1$d balíčky, které toto zařízení nedokáže otevřít - %1$d balíčku, které toto zařízení nedokáže otevřít - %1$d balíčků, které toto zařízení nedokáže otevřít - - Koordinátor je předá každému, kdo vás zve, ale soukromá část není na tomto zařízení — patří jiné instalaci. Pokud tato instalace už neexistuje, pozvánka s jedním z nich vytvoří zprávu Welcome, kterou nikdo nikdy neotevře. - Zveřejnit jeden - Zveřejnit záložní - - K dispozici je %1$d jednorázový balíček. - K dispozici jsou %1$d jednorázové balíčky. - K dispozici je %1$d jednorázového balíčku. - K dispozici je %1$d jednorázových balíčků. - - Jakmile zde poprvé něco zveřejníte, Amethyst bude spotřebované balíčky potichu nahrazovat. Ten první ale nikdy nezveřejní za vás. - Stáhnout vše - Stáhnout tyto - Požádat o připojení k této skupině - Tímto zveřejníte balíček klíčů pod vlastním klíčem svého účtu a sdělíte koordinátorovi, že se chcete připojit k této skupině — bez ohledu na to, zda někdo odpoví. - O připojení se nepodařilo požádat. - Požádáno. Některý člen skupiny vás musí přidat; pozvánka se pak objeví mezi pozvánkami cordn. - Skenovat - Tento soubor se nepodařilo otevřít. - Pro tento účet není nastaven žádný mediální server, takže soubor nemá kam uložit. Vyberte ho v Nastavení. - Tento soubor nelze přečíst. - Tento soubor se nepodařilo odeslat. - - %1$d člen - %1$d členové - %1$d člena - %1$d členů - - Zpráva smazána - Koordinátor - Kurzor - Odesláno - Podrobnosti zprávy - upraveno - Připnul %1$s - - %1$d připnutá zpráva v této skupině - %1$d připnuté zprávy v této skupině - %1$d připnuté zprávy v této skupině - %1$d připnutých zpráv v této skupině - - Další připnutá zpráva - Předchozí připnutá zpráva - Zobrazit všechny připnuté zprávy - Připnuté zprávy - Reakce - Přidat - Přijmout může jen administrátor. Ve skupině bez administrátorů to je kdokoli. - Zkontrolovat žádosti - Zamítnout - Žádosti se nepodařilo načíst. - Nikdo nečeká na připojení. - Žádosti o připojení - Odeslat - Odeslání se nezdařilo. - Koordinátor této skupiny není otevřen, takže zatím nelze nic odeslat. - Zkopírováno - Kopírovat odkaz - Kdokoli s tímto odkazem může požádat o připojení. Pustit ho dovnitř ale musí administrátor. - Sdílet tuto skupinu - Přehrát hlasovou zprávu - Nahrát hlasovou zprávu - Zastavit a odeslat - Amethyst potřebuje fotoaparát ke skenování QR kódů. Nic se nenahrává ani neodesílá — snímky se dekódují v zařízení a poté se zahodí. - Povolit fotoaparát - Přístup k fotoaparátu je pro Amethyst vypnutý. Znovu ho můžete zapnout v nastavení Androidu. - Otevřít nastavení - Dekodér QR kódů se v tomto zařízení nepodařilo spustit. Kód můžete stále vložit jako text. - Zapnout světlo - Vypnout světlo - Je tma — zkuste světlo - Naskenovat obrázek - Vložit - V obrázku nebyl nalezen žádný QR kód - Ve schránce není nic ke skenování - Klepněte na požadovaný kód - - Načteno %1$s z %2$s části — pokračujte - Načteno %1$s z %2$s částí — pokračujte - Načteno %1$s z %2$s části — pokračujte - Načteno %1$s z %2$s částí — pokračujte - - Obnovit přiblížení - Amethyst tento kód nedokáže otevřít - Tento kód obsahuje soukromý klíč nebo tajemství pro spárování, proto se jeho obsah zde nezobrazuje. Použijte obrazovku, která ho očekává. - Toto je soukromý klíč. Vložte ho raději na přihlašovací obrazovce. - Toto je připojení peněženky. Přidejte ho v nastavení peněženky. - Toto je adresa vzdáleného podepisovatele. Amethyst je rozdává, aby ho ostatní aplikace mohly žádat o podpis — přihlásit se s ní nedokáže. - Toto je aplikace, která se chce připojit k vašemu podepisovateli. Otevřete ji z obrazovky podepisovatele NIP-46. - Toto je Lightning faktura. - Toto je Cashu token. - Toto je webový odkaz. - Toto je prostý text, ne kód Nostr. - Toto je kód Nostr, ale tato obrazovka ho nedokáže otevřít. - Profil - Poznámka - Kód Nostr - Relé - Soukromý klíč - Připojení peněženky - Adresa vzdáleného podepisovatele - Žádost o připojení k podepisovateli - Lightning faktura - Cashu token - Webový odkaz - Prostý text - Otevřít odkaz - Kopírovat - Zkopírováno - Skenovat znovu - Využijte tréninky, které už máte zaznamenané - Připojte Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) — získáte Moji kondici, vlastní přehled tréninků, a tento trénink vyplníte podle záznamu z hodinek místo ručního psaní každého čísla. - Amethyst používá Health Connect pro funkci Moje kondice — váš vlastní přehled tréninků přímo v telefonu. - Moje kondice (menu → Vy → Moje kondice) ukazuje, jak trénujete: kolik jste toho udělali tento týden oproti minulému, jak se váš čas dělí mezi běh, cyklistiku, chůzi, plavání a posilovnu, vaše nejlepší výkony, kolik dní jste trénovali a vaši aktuální sérii. Amethyst tento obraz sestaví z tréninků, které vaše hodinky nebo fitness aplikace už uložily do Health Connect, spolu s tréninky, které jste zaznamenali přímo v Amethystu. Vše se vypočítá ve vašem telefonu a zobrazí jen vám. - Cvičení · jakou aktivitu jste dělali, kdy začala a jak dlouho trvala. To je samotný trénink: určuje počet tréninků, dobu tréninku, rozpis podle aktivit, aktivní dny a vaši sérii. - Vzdálenost · jak daleko jste se dostali; zobrazuje se jako vzdálenost tento týden oproti minulému, týdenní průměr, vzdálenost podle aktivity a nejdelší vzdálenost. - Aktivní a celkové kalorie · energie spálená při tréninku; zobrazuje se jako energie tento týden a její srovnání s minulým týdnem. Aktivní kalorie se použijí, pokud je váš zdroj zaznamenává; celkové kalorie slouží jako náhrada u zdrojů, které zaznamenávají jen celkovou energii. - Tepová frekvence · průměr a maximum během každého tréninku, sloučené do průměrné a nejvyšší tepové frekvence za období. Podle ní poznáte lehký týden od náročného při stejné vzdálenosti. - Kroky · počet kroků při běhu, chůzi nebo túře; zobrazuje se jako týdenní průměr kroků a nejvyšší počet kroků. - Nastoupané převýšení · kolik jste nastoupali; zobrazuje se jako týdenní průměr stoupání a největší stoupání. Právě to odlišuje rovinatý týden od kopcovitého. - Čte jen tréninky z posledních 4 týdnů, a to jen když je otevřená Moje kondice nebo editor tréninku. Nikdy nečte na pozadí. - Vaše zdravotní údaje nikdy nikam neposílá. Moje kondice se počítá ve vašem telefonu a zůstává v něm. Pokud později budete chtít zveřejnit jeden trénink, klepnete u něj na Sdílet, přečtete si příspěvek, který Amethyst vyplnil, a zveřejníte ho sami — vždy jen jeden trénink, nikdy automaticky. - Celá funkce je volitelná. Oprávnění můžete kdykoli odvolat v Health Connect — Moje kondice se pak vrátí k úvodní výzvě — nebo vypnout návrhy v editoru v Nastavení → Nastavení editoru. Zbytek Amethystu funguje dál. - Moje kondice - Poslední 4 týdny - Načítám vzdálenost, kalorie a tepovou frekvenci… - Poslední čtyři celé týdny vašeho tréninku: tréninky, které jste zveřejnili, a vše, co Health Connect zaznamenává v tomto telefonu. Amethyst přehled počítá v zařízení a nic z něj nezveřejňuje. - Tento týden - oproti minulému týdnu - Týdenní průměr - Tréninky - Aktivní dny - Série dní - Čas - %1$d h %2$d min - %1$d min - %1$d s - Vzdálenost - Kalorie - Kroky - Stoupání - Prům. TF - Max. TF - Podle aktivity - Nejlepší výkony - Nejdelší vzdálenost - Nejdelší trénink - Největší stoupání - Nejvíce kroků - Nejvyšší tepová frekvence - Nedávné tréninky - Za poslední 4 týdny nic zaznamenáno. Zobrazí se zde tréninky, které zveřejníte, i ty, které zaznamenají vaše hodinky. - Sledujte svůj trénink - Připojte Health Connect a uvidíte týdenní součty, vývoj oproti minulému týdnu, nejlepší výkony a tréninkovou sérii. Vše zůstává ve vašem telefonu. - Připojte Health Connect a přidejte tréninky zaznamenané hodinkami, včetně tepové frekvence, kroků a stoupání. - Připojit - Sdílet tento trénink - tep/min - kcal - km - mi - m - ft - Načítání - Čekám na načtení vašich soukromých zpráv… - %1$02d:%2$02d - %1$d:%2$02d:%3$02d - Skupina aktualizována - Příloha - Zpráva - %1$s: %2$s - Vytvořit skupinu - Pozvánky Marmot chodí na jiné zařízení a zde je nelze otevřít. - Zavřít - Zveřejnit z tohoto zařízení - Otevřít poznámku se zapem - Titulky - Zapnout nebo vypnout titulky (skryto, pokud je video nemá) - Neodesláno - Zkusit znovu - Odesílání… - Stále se odesílá… - Tento účet byl z Amethystu odebrán. - Vypnuto - Vypnout titulky - Zapnout titulky - Titulky - Seznam aplikací - Seznam článků - Sada záložek - Oblíbené kanály - Git autoři - Git repozitáře - Důvěryhodní autoři wiki - Důvěryhodná wiki relé - Zájmy - Sledování médií - Startovní balíček médií - Ztlumeno pro druh %1$s - Seznam obrázků - Skupiny - Spolupráce na videu - Seznam videí - Titulky videa - Amethyst pro Android se nemůže přihlásit pomocí adresy bunker://. Tyto adresy rozdává, aby ho ostatní aplikace mohly žádat o podpis, ale přihlásit se s nimi nedokáže. Použijte externí podepisovací aplikaci nebo Amethyst Desktop. - Toto je aplikace, která se chce připojit k vašemu podepisovateli, ne způsob přihlášení. Nejprve se přihlaste a pak ji otevřete z obrazovky podepisovatele NIP-46. - Moje kondice - Tento seznam má soukromé členy, které vidí jen jeho autor - Upravit keš - Upravit výpravu - Keš - Výprava - Výpravy - Keše - Zapsat nález - Ukrýt keš - Nová výprava - Přijal uvedení jako spolupracovník - Odmítl uvedení jako spolupracovník - Uvedení přijato - Zvuk od - Účinkuje - Inspirováno - Odkazuje na - Soukromý seznam - - %1$d video - %1$d videa - %1$d videa - %1$d videí - - Toto zařízení nyní přijímá vaše pozvánky Marmot. - Zveřejnění KeyPackage selhalo: %1$s - Žádné relé KeyPackage nepřijalo. Zkontrolujte nastavení relé a zkuste to znovu. - Odesílání… - Nepodařilo se odeslat. Klepnutím to zkusíte znovu. - - %1$d nová zpráva - %1$d nové zprávy - %1$d nové zprávy - %1$d nových zpráv - - Skupiny Cordn - End-to-end šifrované skupiny, jejichž pořadí zpráv určuje vámi zvolený koordinátor, nikoli relé. - Skupina Cordn - Šifrovaný skupinový chat řazený koordinátorem, kterého si vyberete. - Koordinovaná - Nejlepší pro týmy, které chtějí jedno spolehlivé pořadí konverzace. - Vytvořit skupinu Cordn - End-to-end šifrování pomocí MLS — koordinátor nikdy nepřečte žádnou zprávu. - Jedno dohodnuté pořadí pro všechny, takže historii nemohou přeházet špatně nastavené hodiny. - Koordinátor zjistí, kdo je ve skupině a kdy spolu mluvíte, i když nemůže číst, co píšete. 3D objekt %1$d vrcholů · %2$d stěn Tento 3D objekt nelze přečíst (pravidlo %1$s) @@ -5932,6 +5637,7 @@ %1$d bajtu něčeho, co tento klient neumí přečíst %1$d bajtů něčeho, co tento klient neumí přečíst + Zavřít Kopírovat odkaz Najít @@ -6426,4 +6132,298 @@ Služby, kterým důvěřujete, že pro vás hodnotí a ověřují lidi. seznam důvěryhodných relé Relé, ke kterým se připojujete bez dotazu. + Smazat + Upravit + Úprava vaší zprávy + Přejít na zprávu + Připnout + Odpovědět + Odepnout + Tuto změnu nebylo možné provést. + Koordinátor neodpověděl, takže se nic nezměnilo. Zkuste to za chvíli znovu. + %1$s na tomto koordinátoru nezveřejnil balíček klíčů, takže ho zatím nelze přidat. Požádejte ho, aby na tomto koordinátoru nejprve otevřel cordn. + Ze skupiny cordn nemůžete odebrat sami sebe. + Pro %1$s se nepodařilo zajistit přístup do skupiny, takže přidání bylo zrušeno. + %1$s není v této skupině. + Tato osoba + Tento koordinátor poskytl balíček klíčů, který patří někomu jinému, takže %1$s nebyl přidán. Nic se nezměnilo. + Šifrovací klíče vašich skupin, vaše balíčky klíčů a všechny zprávy v těchto skupinách. Kdokoli soubor otevře, může konverzace číst. Jakmile soubor opustí Amethyst, chrání ho jen heslo. + Co soubor obsahuje + Váš nsec skupiny cordn neobnoví. Skupina cordn existuje jako šifrovací stav na tomto zařízení a jako seřazený proud zpráv na koordinátoru, který ho nedokáže přečíst — přijdete-li o zařízení bez zálohy, skupina je pryč, včetně všeho, co v ní už zaznělo. + Exportovat + Záloha uložena. + Nepovedlo se. Zkontrolujte heslo a soubor. + Heslo + Obnovit + Obnovení nahradí skupiny cordn na tomto zařízení těmi ze souboru. Je určeno pro telefon, který převzal roli jiného — ne k používání stejných skupin na dvou zařízeních zároveň. + Všechny skupiny cordn, které jsou nyní na tomto zařízení, budou odebrány a nahrazeny těmi ze souboru.\n\nNeobnovujte zálohu na druhý telefon, dokud se ten první stále používá. Oba by měly stejné klíče skupin, a jakmile oba odešlou zprávu, skupina se rozbije pro všechny — potichu a nevratně. + Nahradit skupiny cordn na tomto zařízení? + Obnovit + Obnoveno. + Vše, co sem pošlete, je zašifrováno pro tuto skupinu. Koordinátor to předává dál, aniž by to mohl přečíst. + Zatím žádné zprávy + Nové zprávy + Včera + Zpráva + Přidat + Tohoto koordinátora se nepodařilo otevřít. + Kopírovat npub koordinátora + Hledat koordinátory + Přidat + Čte oznámení, která už vaše relaye přenášejí. Žádný koordinátor není kontaktován, takže se žádný z nich nedozví, že jste hledali. + Oznámení se nepodařilo načíst + Na vašich relayích se nikdo neohlašuje. + Ohlášeno před %1$s + Naposledy ohlášeno %1$s + + Nic nenalezeno a %1$d z vašich relayů neodpověděl. + Nic nenalezeno a %1$d z vašich relayů neodpověděly. + Nic nenalezeno a %1$d z vašich relayů neodpovědělo. + Nic nenalezeno a %1$d z vašich relayů neodpovědělo. + + Koordinátor určuje pořadí zpráv jedné skupiny a nic jiného to dělat nemůže. Jeho ztrátou se ztratí i pořadí a druhý koordinátor ten první nezrcadlí — proto jde o konkrétní servery, které si zvolíte, ne o zaměnitelné relaye. + + Neodpovídá — %1$d požadavek za sebou selhal. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavky za sebou selhaly. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavku za sebou selhalo. Skupiny na něm se nesynchronizují. + Neodpovídá — %1$d požadavků za sebou selhalo. Skupiny na něm se nesynchronizují. + + Odpovídá. + Požadavek selhal, zkouší se znovu. + Zatím nebyl o nic požádán. + Skrýt ty, které se přestaly ohlašovat + Zjistit, kdo to je + Klíč koordinátora + Název (váš vlastní, volitelný) + Tento název je váš a zůstává na tomto zařízení. Koordinátor nemůže název prokázat, proto se ho na něj nikdy neptáme. + Další akce + Zatím žádní koordinátoři. + Vymazat + Odebráním zůstanou skupiny na tomto zařízení, takže po opětovném přidání koordinátora se vrátí s ním. Vymazáním se smažou: klíče skupin, pozice čtení i vaše balíčky klíčů zmizí a do těchto skupin se lze vrátit jen s novou pozvánkou.\n\nKoordinátor se o tom nedozví a ponechá si vše, co už měl. + Vymazat tohoto koordinátora? + %1$s + Odpovídá na + %1$s +%2$d další + Odebrat + Přejmenovat + Tvrdí, že je: %1$s + Název a verzi si koordinátor zvolil sám. Identifikuje ho jen jeho veřejný klíč. + Odpověděl, ale nic neuvedl. + Zobrazit vše (%1$d) + Zobrazit méně + + Zobrazit %1$d, který se přestal ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + Zobrazit %1$d, které se přestaly ohlašovat + + Tito koordinátoři se neohlásili déle než měsíc. Vytvoření skupiny na takovém, který už zanikl, selže. + Vytvořit skupinu + + Vytvořit a pozvat %1$d osobu + Vytvořit a pozvat %1$d osoby + Vytvořit a pozvat %1$d osoby + Vytvořit a pozvat %1$d osob + + Přidat někoho + Administrátor + + %1$d osoba může přidávat a odebírat + %1$d osoby mohou přidávat a odebírat + %1$d osoby může přidávat a odebírat + %1$d osob může přidávat a odebírat + + Tuto skupinu spravuji jen já + Jen vy můžete zvát, odebírat členy nebo měnit údaje skupiny. Později to můžete předat někomu jinému. + Koordinátor + Změnit + Najít koordinátory + Hotovo + Jiný koordinátor + Veřejný klíč koordinátora (hex nebo npub) + Relaye, na kterých odpovídá, jeden na řádek + + Zastihne vaši %1$d osobu + Zastihne všechny %1$d vaše osoby + Zastihne %1$d vaší osoby + Zastihne všech %1$d vašich osob + + Zastihne %1$d z %2$d — ostatní by zůstali mimo + Neověřeno — výběrem se to zjistí + Popis (volitelné) + Všichni mohou přidávat a odebírat + Každý ve skupině může zvát, odebírat členy a měnit její údaje. Kterýkoli člen může později jmenovat administrátory, čímž tento režim skončí. + Pořadí zpráv ve skupině cordn určuje jeden koordinátor, kterého si zvolíte. Skupina se vytváří na tomto zařízení — koordinátor se o její existenci dozví, až někoho pozvete nebo pošlete zprávu. + Skupinu se nepodařilo vytvořit. + Název skupiny + Není zvolen žádný koordinátor + Každá pozvánka je zpráva Welcome ponechaná na koordinátoru, takže mohou selhat i jednotlivě. Nikdo nedostal oznámení: pozvánku uvidí, až příště otevřou svou aplikaci. + Koordinátor pozvánku odmítl + Poslat odkaz + Přidán do skupiny, ale pozvánka ho nezastihla — zkuste to znovu z obrazovky skupiny + Otevřít chat + Zde není žádný klíč, takže nešlo ponechat zprávu Welcome + Skupina existuje + Čeká se na ně + Hotovo + Zatím nikdo nepřidán. Skupinu lze vytvořit prázdnou a pozvánky poslat později, ale právě uvedením lidí teď zjistíte, který koordinátor je zastihne. + Zatím nikdo — klepnutím přidáte lidi + Uvedením lidí teď zjistíme, který koordinátor je zastihne. Každý z nich dostane zprávu Welcome ponechanou na tomto koordinátoru a vyzvedne si ji, až příště otevře svou aplikaci. + + Dostupný na %1$d koordinátoru + Dostupný na %1$d koordinátorech + Dostupný na %1$d koordinátoru + Dostupný na %1$d koordinátorech + + Žádný klíč na žádném z vašich koordinátorů + Zatím neověřeno + Kdo může přidávat a odebírat + Pojmenování + Kdo v ní bude + Kde poběží + Nová skupina Cordn + + %1$d osobu nezastihne žádný z vašich koordinátorů + %1$d osoby nezastihne žádný z vašich koordinátorů + %1$d osoby nezastihne žádný z vašich koordinátorů + %1$d osob nezastihne žádný z vašich koordinátorů + + Vy, vždy administrátor + Všichni ve skupině uvidí tuto zprávu jako staženou a Amethyst přestane zobrazovat její text. Koordinátor si stále uchovává zašifrovanou kopii, v jaké byla odeslána, a kdo ji už četl, ten ji už četl. + Stáhnout zprávu + Přijato koordinátorem + Rozumím + Co tento koordinátor vidí + Informace o skupině + Tato skupina není dostupná. Její koordinátor mohl být odebrán. + Přidejte koordinátora v Nastavení, abyste mohli začít používat skupiny cordn. + + %1$d skupina + %1$d skupiny + %1$d skupiny + %1$d skupin + + Z těchto skupin nic nepřichází. Amethyst to stále zkouší znovu. + Koordinátoři + Zatím žádné skupiny cordn + Skupina cordn žije na jednom koordinátoru, který spravuje její členství a předává zprávy, aniž by kdy viděl, co kdo píše. + Založit skupinu + Přidat někoho + Pod tímto jménem nikdo nenalezen. + Přidat lze jen ty, kdo na tomto koordinátoru zveřejnili balíček klíčů. + Jméno, npub nebo jmeno@domena + Admin + + %1$d administrátor skupiny zůstává beze změny. + %1$d administrátoři skupiny zůstávají beze změny. + %1$d administrátora skupiny zůstává beze změny. + %1$d administrátorů skupiny zůstává beze změny. + + Koordinátor + Klíč koordinátora + Odkaz na koordinátora + Kopírovat nprofile koordinátora + Upravit údaje + Tato skupina nemá administrátory: každý může přidávat, odebírat a přejmenovávat, a to natrvalo. + Epocha + ID skupiny + Lze přidat na tomto koordinátoru + Členové + %1$s odsud přestane dostávat zprávy. Co už přečetl, mu zůstane, a nelze to vrátit zpět — musel by být pozván znovu. + Odebrat ze skupiny? + Odebrat + Uložit + Technické podrobnosti + Připojit se + Odmítnout + Odmítnutí je trvalé. Pro návrat budete potřebovat novou pozvánku. + Kontroluje se jen při otevření této obrazovky a nikdy jindy — dotaz na koordinátora, zda vás někdo pozval, mu prozradí, že jste tady, a proto to Amethyst nedělá na pozadí. + Pozvánky se nepodařilo načíst. + + %1$d osoba už je v této skupině + %1$d osoby už jsou v této skupině + %1$d osoby už je v této skupině + %1$d osob už je v této skupině + + +%1$d další + Nemáte otevřeného žádného koordinátora. Pozvánky cordn přicházejí přes koordinátora, takže zatím není kde hledat. + Žádné čekající pozvánky. + Zkontrolovat znovu + Pozvánka byla ponechána pro jiné zařízení + Pozvánky Cordn + %1$s neodpověděl + Přes %1$s + Zveřejněním podepíšete záznam vlastním klíčem svého účtu. Daný koordinátor se pak dozví, že tento účet existuje a lze ho pozvat — natrvalo, a bez ohledu na to, zda vás někdo pozve. + Ostatní vás mohou přidat do skupiny cordn jen pomocí balíčku klíčů, který jste zveřejnili na daném koordinátoru. Cordn ho nemá kde jinde uchovat, takže bez zveřejněného balíčku vás nikdo nemůže pozvat — a nic mu neřekne proč. + Balíčky klíčů se nepodařilo načíst. + Žádný záložní balíček. Jakmile dojdou jednorázové, pozvánky budou selhávat. + Záložní balíček je zveřejněn, takže pozvánku lze vytvořit i poté, co dojdou jednorázové. + + %1$d balíček, který toto zařízení nedokáže otevřít + %1$d balíčky, které toto zařízení nedokáže otevřít + %1$d balíčku, které toto zařízení nedokáže otevřít + %1$d balíčků, které toto zařízení nedokáže otevřít + + Koordinátor je předá každému, kdo vás zve, ale soukromá část není na tomto zařízení — patří jiné instalaci. Pokud tato instalace už neexistuje, pozvánka s jedním z nich vytvoří zprávu Welcome, kterou nikdo nikdy neotevře. + Zveřejnit jeden + Zveřejnit záložní + + K dispozici je %1$d jednorázový balíček. + K dispozici jsou %1$d jednorázové balíčky. + K dispozici je %1$d jednorázového balíčku. + K dispozici je %1$d jednorázových balíčků. + + Jakmile zde poprvé něco zveřejníte, Amethyst bude spotřebované balíčky potichu nahrazovat. Ten první ale nikdy nezveřejní za vás. + Stáhnout vše + Stáhnout tyto + Požádat o připojení k této skupině + Tímto zveřejníte balíček klíčů pod vlastním klíčem svého účtu a sdělíte koordinátorovi, že se chcete připojit k této skupině — bez ohledu na to, zda někdo odpoví. + O připojení se nepodařilo požádat. + Požádáno. Některý člen skupiny vás musí přidat; pozvánka se pak objeví mezi pozvánkami cordn. + Skenovat + Tento soubor se nepodařilo otevřít. + Pro tento účet není nastaven žádný mediální server, takže soubor nemá kam uložit. Vyberte ho v Nastavení. + Tento soubor nelze přečíst. + Tento soubor se nepodařilo odeslat. + + %1$d člen + %1$d členové + %1$d člena + %1$d členů + + Zpráva smazána + Koordinátor + Kurzor + Odesláno + Podrobnosti zprávy + upraveno + Připnul %1$s + + %1$d připnutá zpráva v této skupině + %1$d připnuté zprávy v této skupině + %1$d připnuté zprávy v této skupině + %1$d připnutých zpráv v této skupině + + Další připnutá zpráva + Předchozí připnutá zpráva + Zobrazit všechny připnuté zprávy + Připnuté zprávy + Reakce + Přidat + Přijmout může jen administrátor. Ve skupině bez administrátorů to je kdokoli. + Zkontrolovat žádosti + Zamítnout + Žádosti se nepodařilo načíst. + Nikdo nečeká na připojení. + Žádosti o připojení + Odeslat + Odeslání se nezdařilo. + Koordinátor této skupiny není otevřen, takže zatím nelze nic odeslat. + Zkopírováno + Kopírovat odkaz + Kdokoli s tímto odkazem může požádat o připojení. Pustit ho dovnitř ale musí administrátor. + Sdílet tuto skupinu + Přehrát hlasovou zprávu + Nahrát hlasovou zprávu + Zastavit a odeslat diff --git a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml index 64ad7f8fd8..0d938c442c 100644 --- a/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-de-rDE/strings.xml @@ -166,6 +166,152 @@ Stau Straßenereignis + Geocache + Traditional + Multi-Cache + Mystery + Mikro + Klein + Normal + Groß + Andere Größe + D%1$d + T%1$d + FTF + Kunstwerk + Key Quest + Verifizierte Funde + FTF vergeben + Archiviert + Nicht weiter? Tippen, um den Hinweis anzuzeigen + Nicht lesbar? Für die andere Lesart tippen + In der Nähe + Gefunden! + Verifizierter Fund + Nachweis ist ungültig + Cache wird geladen… + Versteckt von %1$s + Navigieren + Koordinaten kopieren + Koordinaten kopiert + Fotos + Key-Quest-Mission + Logs + Diesen Cache hat noch niemand geloggt. Sei der Erste. + Ich habe ihn gefunden + Nicht gefunden + Notiz hinzufügen + Wartungsbedarf melden + Zu einer Tour hinzufügen + Neue Tour… + Du hast noch keine Tour erstellt. + Bereits in dieser Tour + + %1$d Cache hier + %1$d Caches hier + + Dieses Foto entfernen + Dieser Cache wurde archiviert. Er ist nicht mehr im Spiel. + FTF geht an %1$s + Der FTF ist noch zu haben + Owner + Diesen Cache bearbeiten + Diesen Cache archivieren + Archivieren. Finder sehen, dass er nicht mehr im Spiel ist, und das Listing bleibt lesbar. + Erstfinder festschreiben + Veröffentlicht einen F-Tag mit %1$s. Das ist endgültig. + Noch keine verifizierten Funde, also gibt es niemanden zum Festschreiben. + Verifizierungscode anzeigen + Drucke ihn aus und lege ihn in den Cache. Amethyst speichert ihn nicht — wenn du ihn verlierst, können Finder nicht mehr beweisen, dass sie dort waren. + Nicht gefunden + Notiz + Wartung erforderlich + Fund loggen + Cache + Dein Log + Wie lief es? + Foto hinzufügen + Deine Key-Quest-Antwort + Anwesenheitsnachweis + Code im Cache scannen + Anderen Code scannen + Verifiziert — du warst am Cache + Einmalig mit dem Code aus der Dose signiert. Der Schlüssel wird nie gespeichert und verlässt diesen Bildschirm nie. + Das ist kein Verifizierungscode. + Dieser Code gehört zu einem anderen Cache. + Dieser Cache verlangt keinen Nachweis, du kannst ihn also einfach loggen. + Dein eigener Standort wird diesem Log nicht angehängt. + Senden + Log hinzufügen + In der Nähe + Karte + Touren + Funde + Meine + Nicht gefunden + D ≤ %1$d + T ≤ %1$d + Mit Preis + Nachweis erforderlich + Meinen Standort verwenden + Nach Entfernung sortiert + Neueste zuerst + Cache verstecken + Hier gibt es noch keine Caches. Probiere eine größere Folgeliste oder verstecke den ersten. + Du hast noch keinen Fund geloggt. + Du hast noch keinen Cache versteckt. + Noch keine Touren. + Funde + FTFs + Verifiziert + Versteckt + Auf mich zentrieren + Lange auf die Karte drücken, um dort einen Cache zu verstecken + Öffnen + Wo + Was + Extras + Nachweis + Relays + Dies veröffentlicht einen öffentlichen Standort mit einer Genauigkeit von etwa 5 Metern. Jeder auf Nostr kann ihn sehen, und ein veröffentlichtes Event kann nicht zurückgeholt werden. + Ort auswählen + Veröffentlicht %1$s sowie gröbere Zellen bis hinunter zu 3 Zeichen für die Umkreissuche. + Name + Beschreibung + Typ + Größe + Schwierigkeit + Gelände + Hinweis + Finder sehen + Hinweis verschlüsseln (ROT13) + Key-Quest-Mission + Zusätze + Anwesenheitsnachweis verlangen + Für diesen Cache wurde ein Verifizierungsschlüssel erzeugt. + Wo Finder loggen sollen + Veröffentlichen + Gib dem Cache einen Namen. + Wähle aus, wo der Cache versteckt ist. + %1$d von %2$d gefunden + Tour starten + + %1$d Cache + %1$d Caches + + Titel + Beschreibung + Bannerbild + Caches, in Reihenfolge + Cache hinzufügen + Nach oben + Nach unten + Entfernen + Seitendesign + Kartenstil + Gib der Tour einen Titel. + Füge mindestens einen Cache hinzu. + Veröffentlichen fehlgeschlagen. Prüfe deine Relay-Verbindung und versuche es erneut. Zaps hierauf werden aufgeteilt zwischen: %1$d%% Value-for-Value @@ -188,6 +334,51 @@ Profilbanner Auf den QR-Code verweisen + Amethyst braucht die Kamera, um QR-Codes zu scannen. Es wird nichts aufgezeichnet oder hochgeladen — die Bilder werden auf dem Gerät ausgewertet und verworfen. + Kamera erlauben + Der Kamerazugriff ist für Amethyst deaktiviert. Du kannst ihn in den Android-Einstellungen wieder aktivieren. + Einstellungen öffnen + Der QR-Decoder konnte auf diesem Gerät nicht starten. Du kannst den Code trotzdem als Text einfügen. + Licht einschalten + Licht ausschalten + Es ist dunkel — probier das Licht + Bild scannen + Einfügen + Kein QR-Code in diesem Bild gefunden + Nichts zum Scannen in der Zwischenablage + Tippe auf den gewünschten Code + + %1$s von %2$s Teil erfasst — weiter so + %1$s von %2$s Teilen erfasst — weiter so + + Zoom zurücksetzen + Amethyst kann diesen Code nicht öffnen + Dieser Code enthält einen privaten Schlüssel oder ein Kopplungsgeheimnis, daher wird sein Inhalt hier nicht angezeigt. Verwende den Bildschirm, der ihn erwartet. + Das ist ein privater Schlüssel. Füge ihn stattdessen auf dem Anmeldebildschirm ein. + Das ist eine Wallet-Verbindung. Füge sie in den Wallet-Einstellungen hinzu. + Das ist eine Remote-Signer-Adresse. Amethyst gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können — anmelden kann es sich damit nicht. + Das ist eine App, die sich mit deinem Signierer verbinden möchte. Öffne sie über den NIP-46-Signierer-Bildschirm. + Das ist eine Lightning-Rechnung. + Das ist ein Cashu-Token. + Das ist ein Weblink. + Das ist einfacher Text, kein Nostr-Code. + Das ist ein Nostr-Code, aber keiner, den dieser Bildschirm öffnen kann. + Profil + Beitrag + Nostr-Code + Relay + Privater Schlüssel + Wallet-Verbindung + Remote-Signer-Adresse + Verbindungsanfrage an Signierer + Lightning-Rechnung + Cashu-Token + Weblink + Einfacher Text + Link öffnen + Kopieren + Kopiert + Erneut scannen QR-Code anzeigen Profilbild Dein Profilbild @@ -638,17 +829,70 @@ Stunden Minuten Sekunden + Nutze die Trainings, die du schon aufgezeichnet hast + Verbinde Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…), um „Meine Fitness“ aufzubauen, deine persönliche Trainingsübersicht, und um dieses Training aus einem zu übernehmen, das deine Uhr bereits aufgezeichnet hat, statt jede Zahl von Hand einzutippen. Verbinden %1$s km Aus Health Connect Was Amethyst liest Health Connect und Amethyst + Amethyst nutzt Health Connect, um „Meine Fitness“ aufzubauen — deine persönliche Trainingsübersicht, auf deinem Telefon. + „Meine Fitness“ (Menü → Du → Meine Fitness) zeigt dir, wie du trainiert hast: wie viel du diese Woche im Vergleich zur letzten gemacht hast, wie sich deine Zeit auf Laufen, Radfahren, Gehen, Schwimmen und Fitnessstudio verteilt, deine Bestleistungen, an wie vielen Tagen du trainiert hast und deine aktuelle Serie. Amethyst erstellt dieses Bild aus den Trainings, die deine Uhr oder Fitness-App bereits in Health Connect gespeichert hat, zusammen mit allen Trainings, die du in Amethyst selbst erfasst hast. Es wird auf deinem Telefon berechnet und nur dir angezeigt. Was Amethyst liest und warum + Training · welche Aktivität du gemacht hast, wann sie begann und wie lange sie dauerte. Das ist das Training selbst: Daraus ergeben sich deine Anzahl an Trainings, deine Trainingszeit, die Aufschlüsselung nach Aktivität, deine aktiven Tage und deine Serie. + Distanz · wie weit du gekommen bist, angezeigt als deine Distanz dieser Woche im Vergleich zur letzten, dein Wochendurchschnitt, deine Distanz pro Aktivität und deine längste Distanz. + Aktive und Gesamtkalorien · die Energie, die ein Training verbrannt hat, angezeigt als deine Energie dieser Woche und im Vergleich zur letzten Woche. Aktive Kalorien werden verwendet, wenn deine Quelle sie erfasst; Gesamtkalorien dienen als Ersatz für Quellen, die nur den Gesamtenergieverbrauch erfassen. + Herzfrequenz · Durchschnitt und Maximum jedes Trainings, zusammengefasst zu deiner durchschnittlichen und höchsten Herzfrequenz im Zeitraum. So unterscheidest du eine lockere Woche von einer harten bei gleicher Distanz. + Schritte · die Schrittzahl eines Laufs, Spaziergangs oder einer Wanderung, angezeigt als dein wöchentlicher Schrittdurchschnitt und deine höchste Schrittzahl. + Höhenmeter · wie viel du aufgestiegen bist, angezeigt als dein wöchentlicher Durchschnitt an Höhenmetern und dein größter Anstieg. Daran erkennst du eine flache Woche von einer hügeligen. Was Amethyst nicht tut + Liest nur Trainings der letzten 4 Wochen, und nur solange „Meine Fitness“ oder der Trainings-Editor geöffnet ist. Liest nie im Hintergrund. Schreibt nie etwas in Health Connect und fragt nie nach deiner Trainingsroute, deinem Standort oder anderen Gesundheitsdaten. + Sendet deine Gesundheitsdaten nirgendwohin. „Meine Fitness“ wird auf deinem Telefon berechnet und bleibt dort. Wenn du später ein Training posten möchtest, tippst du bei diesem Training auf Teilen, liest den Beitrag, den Amethyst ausgefüllt hat, und veröffentlichst ihn selbst — ein Training nach dem anderen, nie automatisch. + Die gesamte Funktion ist optional. Widerrufe die Berechtigungen jederzeit in Health Connect — „Meine Fitness“ zeigt dann wieder die Aufforderung — oder schalte die Editor-Vorschläge unter Einstellungen → Editor-Einstellungen aus. Der Rest von Amethyst funktioniert weiter. Vollständige Datenschutzerklärung lesen + Meine Fitness + Letzte 4 Wochen + Distanz, Kalorien und Herzfrequenz werden geladen… + Die letzten vier vollen Wochen deines Trainings: die Trainings, die du veröffentlicht hast, plus alles, was Health Connect auf diesem Telefon erfasst. Amethyst berechnet die Übersicht auf dem Gerät und veröffentlicht nichts davon. + Diese Woche + ggü. letzter Woche + Wochendurchschnitt + Trainings + Aktive Tage + Tage in Folge + Zeit + %1$d Std. %2$d Min. + %1$d Min. + %1$d Sek. + Distanz + Kalorien + Schritte + Höhenmeter + Ø HF + Max. HF + Nach Aktivität + Bestleistungen + Längste Distanz + Längstes Training + Größter Anstieg + Meiste Schritte + Höchste Herzfrequenz + Letzte Trainings + In den letzten 4 Wochen wurde nichts erfasst. Trainings, die du postest, und alle, die deine Uhr aufzeichnet, erscheinen hier. + Verfolge dein Training + Verbinde Health Connect, um deine Wochensummen, den Trend im Wochenvergleich, deine Bestleistungen und deine Trainingsserie zu sehen. Alles bleibt auf deinem Telefon. + Verbinde Health Connect, um die Trainings deiner Uhr mit Herzfrequenz, Schritten und Höhenmetern hinzuzufügen. + Verbinden + Dieses Training teilen + S/min + kcal + km + mi + m + ft Noch keine nSites gefunden. Neu laden Der In-App-Browser benötigt Android 11 oder neuer. @@ -1084,6 +1328,8 @@ Schließdatum und -zeit Umfrage endet in %1$s Veranstaltung suchen %1$s + Wird geladen + Warte, bis deine privaten Nachrichten geladen sind… Zap senden Füge eine öffentliche Nachricht hinzu Füge eine private Nachricht hinzu @@ -1223,6 +1469,8 @@ + %1$02d:%2$02d + %1$d:%2$02d:%3$02d Verwerfen Kamera wechseln Sprachanruf @@ -1357,6 +1605,14 @@ Benutzereinstellungen Einstellungen durchsuchen Keine Einstellungen für „%1$s“ gefunden + Gruppe aktualisiert + Anhang + Nachricht + %1$s: %2$s + Gruppe erstellen + Marmot-Einladungen gehen an ein anderes Gerät und können hier nicht geöffnet werden. + Schließen + Von diesem Gerät veröffentlichen Marmot-Status zurücksetzen? Dadurch werden alle Marmot-Gruppenchats, der Nachrichtenverlauf und alle MLS-Schlüssel auf diesem Gerät für das aktuelle Konto dauerhaft gelöscht. Andere Teilnehmer werden nicht benachrichtigt und können dich bis zu ihrem nächsten Commit weiterhin in Gruppen sehen. Dies kann nicht rückgängig gemacht werden. Beim nächsten Synchronisieren der App wird ein neues KeyPackage veröffentlicht. Zurücksetzen @@ -1627,6 +1883,7 @@ Keine Transaktionen entsprechen diesem Filter Wird geladen… Empfangen + Gezappten Beitrag öffnen Gesendet Aktualisieren Alle @@ -1943,6 +2200,8 @@ Video in einem schwebenden Fenster abspielen (ausgeblendet, wenn nicht unterstützt) An Gerät streamen Sendet das Video an einen Chromecast-Empfänger in deinem WLAN (für lokale Dateien ausgeblendet) + Untertitel + Untertitel ein- oder ausschalten (ausgeblendet, wenn das Video keine hat) Wähle die Animation, die beim Abspielen von Audios angezeigt wird. Aus Spektrum-Balken @@ -2630,6 +2889,7 @@ Du erfüllst die Web-of-Trust-Anforderungen dieser Community nicht. Das aktuelle Community-Regeldokument wurde als veraltet abgelehnt. Marmot-Gruppen + Cordn-Gruppen Gruppe erstellen Marmot-Gruppe erstellen Eine neue MLS-Gruppe wird erstellt. Sie können danach Mitglieder hinzufügen. @@ -2921,13 +3181,18 @@ Benachrichtigt dich, wenn jemand auf deinen Beitrag antwortet Antworten Neue Antworten + Nicht gesendet Antworten + Erneut versuchen + Wird gesendet… + Wird noch gesendet… Benachrichtigt dich, wenn jemand deinen Beitrag repostet %1$s hat deinen Beitrag repostet Reposts Neue Reposts Benachrichtigungen, wenn ein geplanter Beitrag veröffentlicht wird oder die Veröffentlichung fehlschlägt. Geplante Beiträge + Dieses Konto wurde aus Amethyst entfernt. Benachrichtigt dich, wenn dich jemand zappt %1$s Sats für %1$s @@ -3128,6 +3393,10 @@ Stummschaltung aufheben Callback-URL aus %1$s Antwort nicht gefunden Abbrechen + Aus + Untertitel ausschalten + Untertitel einschalten + Untertitel Cashu-Token Mint wird geprüft… Cashu konnte nicht eingelöst werden @@ -3605,10 +3874,12 @@ Abenteuer-Prolog Abenteuer-Lesung Abenteuer-Szene + App-Liste App-Empfehlungen Termin Apps RSVP für Termin + Artikelliste Audio-Header Audiospur Erstellte Podcasts @@ -3621,6 +3892,7 @@ Blossom-Authentifizierung Blossom-Server Lesezeichenliste + Lesezeichen-Set Broadcast-Relays Kalender Kanaldefinition @@ -3658,6 +3930,7 @@ Emoji-Pakete Ephemerer Chat Ephemere Chaträume + Lieblings-Feeds Lieblings-Podcasts Bevorzugte Relays Datei-Header @@ -3666,28 +3939,36 @@ Follow-Pakete Geohash-Follows Geschenkverpackungen + Git-Autoren Git-Issue Git-Patch Pull Request PR Update Git-Antwort Git-Repository + Git-Repositories Git-Status: Angewendet Git-Status: Geschlossen Git-Status: Entwurf Git-Status: Offen + Vertrauenswürdige Wiki-Autoren + Vertrauenswürdige Wiki-Relays Gefolgte Hashtags Highlights HTTP-Authentifizierung Index-Relay-Liste + Interessen Live-Chats Live-Streams + Medien-Follows + Medien-Starterpaket Medizinische Daten Besprechungsraum Besprechungsbereich Musik-Playlist Musiktitel Stummschaltliste + Stummgeschaltet für Typ %1$s Benannte Lesezeichen Nests-Server NIP @@ -3702,6 +3983,7 @@ Outbox-Relays Zahlen an Personenlisten + Bilderliste Bilder Angepinnt Podcast-Folge @@ -3728,6 +4010,7 @@ Such-Relays Shorts Shorts (Antwort) + Gruppen Torrent-Kommentare Torrents Vertrauenswürdige Anbieter @@ -3736,7 +4019,10 @@ Benutzereinstellungen Benutzerstatus Video + Video-Zusammenarbeit + Videoliste Video (Antwort) + Video-Untertitel Sprachnachricht Sprachantwort Aufwachen @@ -3773,6 +4059,8 @@ Ort auf der Karte ändern Standort-Kanäle Wähle einen Ort auf der Karte + Amethyst für Android kann sich nicht mit einer bunker://-Adresse anmelden. Es gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können, aber anmelden kann es sich damit nicht. Verwende eine externe Signierer-App oder Amethyst Desktop. + Das ist eine App, die sich mit deinem Signierer verbinden möchte, keine Anmeldemöglichkeit. Melde dich zuerst an und öffne sie dann über den NIP-46-Signierer-Bildschirm. Du verwendest einen öffentlichen Schlüssel und öffentliche Schlüssel sind schreibgeschützt. Mit einem privaten Schlüssel anmelden, um Beiträge boosten zu können Du verwendest einen öffentlichen Schlüssel und öffentliche Schlüssel sind schreibgeschützt. Mit einem privaten Schlüssel anmelden, um folgen zu können Du verwendest einen öffentlichen Schlüssel, und öffentliche Schlüssel sind nur lesbar. Melden Sie sich mit einem privaten Schlüssel an, um ein Wort oder einen Satz auszublenden @@ -3863,6 +4151,7 @@ Benachrichtigungen stummschalten Stummgeschaltet. Drücken, um Ton einzuschalten Meine Blossom-Dateien + Meine Fitness Meine Listen Ein Name wird benötigt %1$dms @@ -4126,6 +4415,7 @@ Remote-Signer-App Remote-Signer + Diese Liste hat private Mitglieder, die nur ihr Autor sehen kann %1$d Asset gebündelt %1$d Assets bündelt @@ -4658,12 +4948,21 @@ Kalender Schach Entdecken + Cache bearbeiten + Tour bearbeiten + Cache + Tour + Cache-Touren + Geocaches Startseite Folgeliste importieren + Fund loggen Medien Nachrichten Wiedergabelisten Musik + Cache verstecken + Neue Tour Benachrichtigungen Folgen Podcasts @@ -4853,6 +5152,18 @@ Löschanfrage gesendet Bessere Qualität bei kleinerer Dateigröße, aber nicht alle Geräte unterstützen die H.265-Wiedergabe. H.265/HEVC-Codec verwenden + Hat eine Mitwirkungsnennung angenommen + Hat eine Mitwirkungsnennung abgelehnt + Nennung angenommen + Audio von + Mit + Inspiriert von + Verweise + Private Liste + + %1$d Video + %1$d Videos + Der Videodownload wurde gestartet… Videoplayer-Schaltflächen Video in der Galerie des Smartphones gespeichert @@ -4912,165 +5223,18 @@ Der Lightning-Service des Empfängers unter %1$s ist nicht verfügbar. Er wurde aus der Lightning-Adresse "%2$s" berechnet. Fehler: %3$s. Überprüfen Sie, ob der Server online ist und ob die Lightning-Adresse korrekt ist Kann keine Blitzrechnung von %1$s erstellen: Element pr nicht im resultierenden JSON gefunden. Blitzrechnung konnte nicht erstellt werden. Nachricht von %1$s: %2$s + Dieses Gerät empfängt jetzt deine Marmot-Einladungen. + KeyPackage konnte nicht veröffentlicht werden: %1$s + Kein Relay hat das KeyPackage angenommen. Prüfe deine Relay-Einstellungen und versuche es erneut. + Wird gesendet… + Konnte nicht gesendet werden. Tippe, um es erneut zu versuchen. + + %1$d neue Nachricht + %1$d neue Nachrichten + - - - - - Geocache - Traditional - Multi-Cache - Mystery - Mikro - Klein - Normal - Groß - Andere Größe - D%1$d - T%1$d - FTF - Kunstwerk - Key Quest - Verifizierte Funde - FTF vergeben - Archiviert - Nicht weiter? Tippen, um den Hinweis anzuzeigen - Nicht lesbar? Für die andere Lesart tippen - In der Nähe - Gefunden! - Verifizierter Fund - Nachweis ist ungültig - Cache wird geladen… - Versteckt von %1$s - Navigieren - Koordinaten kopieren - Koordinaten kopiert - Fotos - Key-Quest-Mission - Logs - Diesen Cache hat noch niemand geloggt. Sei der Erste. - Ich habe ihn gefunden - Nicht gefunden - Notiz hinzufügen - Wartungsbedarf melden - Zu einer Tour hinzufügen - Neue Tour… - Du hast noch keine Tour erstellt. - Bereits in dieser Tour - - %1$d Cache hier - %1$d Caches hier - - Dieses Foto entfernen - Dieser Cache wurde archiviert. Er ist nicht mehr im Spiel. - FTF geht an %1$s - Der FTF ist noch zu haben - Owner - Diesen Cache bearbeiten - Diesen Cache archivieren - Archivieren. Finder sehen, dass er nicht mehr im Spiel ist, und das Listing bleibt lesbar. - Erstfinder festschreiben - Veröffentlicht einen F-Tag mit %1$s. Das ist endgültig. - Noch keine verifizierten Funde, also gibt es niemanden zum Festschreiben. - Verifizierungscode anzeigen - Drucke ihn aus und lege ihn in den Cache. Amethyst speichert ihn nicht — wenn du ihn verlierst, können Finder nicht mehr beweisen, dass sie dort waren. - Nicht gefunden - Notiz - Wartung erforderlich - Fund loggen - Cache - Dein Log - Wie lief es? - Foto hinzufügen - Deine Key-Quest-Antwort - Anwesenheitsnachweis - Code im Cache scannen - Anderen Code scannen - Verifiziert — du warst am Cache - Einmalig mit dem Code aus der Dose signiert. Der Schlüssel wird nie gespeichert und verlässt diesen Bildschirm nie. - Das ist kein Verifizierungscode. - Dieser Code gehört zu einem anderen Cache. - Dieser Cache verlangt keinen Nachweis, du kannst ihn also einfach loggen. - Dein eigener Standort wird diesem Log nicht angehängt. - Senden - Log hinzufügen - In der Nähe - Karte - Touren - Funde - Meine - Nicht gefunden - D ≤ %1$d - T ≤ %1$d - Mit Preis - Nachweis erforderlich - Meinen Standort verwenden - Nach Entfernung sortiert - Neueste zuerst - Cache verstecken - Hier gibt es noch keine Caches. Probiere eine größere Folgeliste oder verstecke den ersten. - Du hast noch keinen Fund geloggt. - Du hast noch keinen Cache versteckt. - Noch keine Touren. - Funde - FTFs - Verifiziert - Versteckt - Auf mich zentrieren - Lange auf die Karte drücken, um dort einen Cache zu verstecken - Öffnen - Wo - Was - Extras - Nachweis - Relays - Dies veröffentlicht einen öffentlichen Standort mit einer Genauigkeit von etwa 5 Metern. Jeder auf Nostr kann ihn sehen, und ein veröffentlichtes Event kann nicht zurückgeholt werden. - Ort auswählen - Veröffentlicht %1$s sowie gröbere Zellen bis hinunter zu 3 Zeichen für die Umkreissuche. - Name - Beschreibung - Typ - Größe - Schwierigkeit - Gelände - Hinweis - Finder sehen - Hinweis verschlüsseln (ROT13) - Key-Quest-Mission - Zusätze - Anwesenheitsnachweis verlangen - Für diesen Cache wurde ein Verifizierungsschlüssel erzeugt. - Wo Finder loggen sollen - Veröffentlichen - Gib dem Cache einen Namen. - Wähle aus, wo der Cache versteckt ist. - %1$d von %2$d gefunden - Tour starten - - %1$d Cache - %1$d Caches - - Titel - Beschreibung - Bannerbild - Caches, in Reihenfolge - Cache hinzufügen - Nach oben - Nach unten - Entfernen - Seitendesign - Kartenstil - Gib der Tour einen Titel. - Füge mindestens einen Cache hinzu. - Veröffentlichen fehlgeschlagen. Prüfe deine Relay-Verbindung und versuche es erneut. - Cordn-Gruppen Koordinator-Gruppe Zugestellt von einem Koordinator, nicht von Relays Was dieser Koordinator sehen kann @@ -5108,6 +5272,9 @@ Erreichbar über Dieser Link nennt keinen Koordinator und kann daher nicht allein verwendet werden. Frag die Person, die ihn geschickt hat, welcher Koordinator die Gruppe überträgt. Durch das Prüfen eines Links trittst du nichts bei, und ein Link allein macht dich nicht zum Mitglied. + Cordn-Gruppenlink cordn koordinator coordinator gruppe link einladung invite mls chat metadaten privatsphäre offenlegung Cordn-Koordinatoren @@ -5116,6 +5283,10 @@ cordn keypackage schlüsselpaket veröffentlichen einladen einladung last resort koordinator mls Cordn-Backup cordn backup sicherung wiederherstellen export import passphrase gruppen wiederherstellung + Cordn-Gruppen + Ende-zu-Ende-verschlüsselte Gruppen, die von einem Koordinator deiner Wahl statt von Relays geordnet werden. + Gruppe %1$s Noch keine Nachrichten Cordn-Gruppe, übertragen von %1$s @@ -5124,8 +5295,19 @@ Foto Video Datei + Cordn-Gruppe + Verschlüsselter Gruppenchat, geordnet von einem Koordinator deiner Wahl. + Koordiniert + Ideal für Teams, die eine verlässliche Reihenfolge der Unterhaltung wollen. + Cordn-Gruppe erstellen + Ende-zu-Ende-verschlüsselt mit MLS — der Koordinator kann nie eine Nachricht lesen. + Eine gemeinsame Reihenfolge für alle, sodass der Verlauf nicht durch eine falsch gehende Uhr durcheinandergeraten kann. + Der Koordinator erfährt, wer in der Gruppe ist und wann ihr schreibt, auch wenn er nicht lesen kann, was ihr sagt. In eine cordn-Gruppe eingeladen? Einladungen ansehen + Cordn Gruppen, die von einem Koordinator deiner Wahl statt von Relays zugestellt werden. Gruppenlink prüfen @@ -5175,453 +5357,6 @@ Lege zuerst einen Medienserver fest — die verschlüsselten Dokumente brauchen einen Ablageort, während das andere Smartphone sie abruft. Was dieses Gerät verlässt Dein Gruppenzustand wird verschlüsselt und auf deinen Medienserver hochgeladen, damit das andere Smartphone ihn abrufen kann. Für diesen Server ist er nicht lesbar, er sieht nur eine Größe und einen Zeitpunkt. Lösche die Blobs danach, wenn nichts zurückbleiben soll. - Löschen - Bearbeiten - Deine Nachricht wird bearbeitet - Zur Nachricht - Anheften - Antworten - Loslösen - Diese Änderung konnte nicht vorgenommen werden. - Der Koordinator hat nicht geantwortet, daher wurde nichts geändert. Versuche es gleich noch einmal. - %1$s hat kein KeyPackage bei diesem Koordinator veröffentlicht und kann daher noch nicht hinzugefügt werden. Bitte die Person, cordn zuerst auf diesem Koordinator zu öffnen. - Du kannst dich nicht selbst aus einer cordn-Gruppe entfernen. - %1$s konnte kein Zugang zur Gruppe gegeben werden, daher wurde das Hinzufügen abgebrochen. - %1$s ist nicht in dieser Gruppe. - Diese Person - Dieser Koordinator hat ein KeyPackage geliefert, das jemand anderem gehört, daher wurde %1$s nicht hinzugefügt. Es wurde nichts geändert. - Deine Gruppen-Verschlüsselungsschlüssel, deine KeyPackages und jede Nachricht in diesen Gruppen. Wer die Datei öffnet, kann die Unterhaltungen lesen. Sobald sie Amethyst verlässt, schützt sie nur noch die Passphrase. - Was in der Datei steckt - Dein nsec kann cordn-Gruppen nicht zurückholen. Eine cordn-Gruppe existiert als Verschlüsselungszustand auf diesem Gerät und als geordneter Datenstrom auf einem Koordinator, der ihn nicht lesen kann — verlierst du das Gerät ohne Backup, ist die Gruppe weg, samt allem, was darin schon gesagt wurde. - Exportieren - Backup gespeichert. - Das hat nicht funktioniert. Prüfe die Passphrase und die Datei. - Passphrase - Wiederherstellen - Beim Wiederherstellen werden die cordn-Gruppen auf diesem Gerät durch die aus der Datei ersetzt. Das ist für ein Smartphone gedacht, das ein anderes abgelöst hat — nicht, um dieselben Gruppen gleichzeitig auf zwei Geräten zu nutzen. - Jede cordn-Gruppe, die jetzt auf diesem Gerät ist, wird entfernt und durch die aus der Datei ersetzt.\n\nStelle nicht auf einem zweiten Smartphone wieder her, während das erste noch genutzt wird. Beide hätten dieselben Gruppenschlüssel, und sobald beide eine Nachricht gesendet haben, geht die Gruppe für alle kaputt — unbemerkt und ohne Weg zurück. - Die cordn-Gruppen dieses Geräts ersetzen? - Wiederherstellen - Wiederhergestellt. - Alles, was hier gesendet wird, ist für diese Gruppe verschlüsselt. Der Koordinator leitet es weiter, ohne es lesen zu können. - Noch keine Nachrichten - Neue Nachrichten - Gestern - Nachricht - Hinzufügen - Dieser Koordinator konnte nicht geöffnet werden. - npub des Koordinators kopieren - Nach Koordinatoren suchen - Hinzufügen - Liest Ankündigungen, die deine Relays ohnehin führen. Kein Koordinator wird kontaktiert, daher erfährt keiner, dass du gesucht hast. - Ankündigungen konnten nicht gelesen werden - Auf deinen Relays kündigt sich niemand an. - Vor %1$s angekündigt - Zuletzt angekündigt %1$s - - Nichts gefunden, und %1$d deiner Relays hat nicht geantwortet. - Nichts gefunden, und %1$d deiner Relays haben nicht geantwortet. - - Ein Koordinator legt die Reihenfolge der Nachrichten einer Gruppe fest und ist der Einzige, der das kann. Geht er verloren, ist die Reihenfolge verloren, und ein zweiter spiegelt den ersten nicht — daher sind das benannte Dienste, die du auswählst, keine austauschbaren Relays. - - Antwortet nicht — %1$d Anfrage in Folge ist fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. - Antwortet nicht — %1$d Anfragen in Folge sind fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. - - Antwortet. - Eine Anfrage ist fehlgeschlagen; neuer Versuch läuft. - Noch nichts angefragt. - Die ausblenden, die sich nicht mehr ankündigen - Fragen, wer er ist - Koordinator-Schlüssel - Ein Name dafür (deiner, optional) - Dieser Name gehört dir und bleibt auf diesem Gerät. Ein Koordinator kann keinen Namen beweisen, daher wird er nie nach einem gefragt. - Weitere Aktionen - Noch keine Koordinatoren. - Löschen - Beim Entfernen bleiben die Gruppen auf diesem Gerät, sodass sie zurückkommen, wenn du den Koordinator wieder hinzufügst. Beim Löschen werden sie vernichtet: Die Gruppenschlüssel, die Lesepositionen und deine KeyPackages verschwinden, und diese Gruppen können nur über eine neue Einladung wieder betreten werden.\n\nDer Koordinator wird nicht benachrichtigt und behält alles, was er bereits hatte. - Diesen Koordinator löschen? - %1$s - Antwortet auf - %1$s +%2$d weitere - Entfernen - Umbenennen - Gibt an: %1$s - Name und Version eines Koordinators sind selbst gewählte Angaben. Nur sein öffentlicher Schlüssel identifiziert ihn. - Hat geantwortet, aber nichts angegeben. - Alle %1$d anzeigen - Weniger anzeigen - - %1$d anzeigen, der sich nicht mehr ankündigt - %1$d anzeigen, die sich nicht mehr ankündigen - - Diese haben sich seit über einem Monat nicht angekündigt. Eine Gruppe auf einem verschwundenen Koordinator zu erstellen, schlägt fehl. - Gruppe erstellen - - Erstellen und %1$d Person einladen - Erstellen und %1$d Personen einladen - - Jemanden hinzufügen - Admin - - %1$d Person kann hinzufügen und entfernen - %1$d Personen können hinzufügen und entfernen - - Nur ich verwalte diese Gruppe - Nur du kannst einladen, Mitglieder entfernen oder die Gruppendetails ändern. Du kannst das später an jemand anderen übergeben. - Koordinator - Ändern - Koordinatoren finden - Fertig - Anderer Koordinator - Öffentlicher Schlüssel des Koordinators (hex oder npub) - Relays, auf denen er antwortet, eines pro Zeile - - Erreicht deine %1$d Person - Erreicht alle %1$d deiner Personen - - Erreicht %1$d von %2$d — die übrigen blieben außen vor - Nicht geprüft — bei Auswahl wird nachgefragt - Beschreibung (optional) - Alle können hinzufügen und entfernen - Alle in der Gruppe können einladen, Mitglieder entfernen und die Details ändern. Jedes Mitglied kann später Admins ernennen, womit das endet. - Eine cordn-Gruppe wird von einem Koordinator deiner Wahl geordnet. Die Gruppe wird auf diesem Gerät erstellt — der Koordinator erfährt erst von ihr, wenn du jemanden einlädst oder eine Nachricht sendest. - Die Gruppe konnte nicht erstellt werden. - Gruppenname - Kein Koordinator ausgewählt - Jede Einladung ist eine Welcome-Nachricht, die beim Koordinator hinterlegt wird, daher können sie einzeln fehlschlagen. Niemand wurde benachrichtigt: Die Eingeladenen sehen sie, wenn sie ihre App das nächste Mal öffnen. - Der Koordinator hat die Einladung abgelehnt - Link senden - Zur Gruppe hinzugefügt, aber die Einladung hat die Person nicht erreicht — versuche es auf dem Gruppenbildschirm erneut - Chat öffnen - Kein Schlüssel hier, daher konnte keine Welcome-Nachricht hinterlegt werden - Die Gruppe existiert - Warte auf die Person - Fertig - Noch niemand hinzugefügt. Eine Gruppe kann leer erstellt und Einladungen später gesendet werden, aber wenn du jetzt Personen nennst, zeigt sich, welcher Koordinator sie erreichen kann. - Noch niemand — tippen, um Personen hinzuzufügen - Wenn du jetzt Personen nennst, erfahren wir, welcher Koordinator sie erreichen kann. Jede erhält eine Welcome-Nachricht auf diesem Koordinator, die sie abholt, wenn sie ihre App das nächste Mal öffnet. - - Erreichbar über %1$d Koordinator - Erreichbar über %1$d Koordinatoren - - Kein Schlüssel auf einem deiner Koordinatoren - Noch nicht geprüft - Wer hinzufügen und entfernen kann - Name - Wer dabei ist - Wo sie liegt - Neue Cordn-Gruppe - - %1$d Person ist über keinen deiner Koordinatoren erreichbar - %1$d Personen sind über keinen deiner Koordinatoren erreichbar - - Du, immer Admin - Alle in der Gruppe sehen diese Nachricht als zurückgezogen, und Amethyst zeigt ihren Text nicht mehr an. Der Koordinator behält die verschlüsselte Kopie, als die sie gesendet wurde, und wer sie schon gelesen hat, hat sie gelesen. - Nachricht zurückziehen - Vom Koordinator angenommen - Verstanden - Was dieser Koordinator sehen kann - Gruppeninfo - Diese Gruppe ist nicht verfügbar. Ihr Koordinator wurde möglicherweise entfernt. - Füge in den Einstellungen einen Koordinator hinzu, um cordn-Gruppen zu nutzen. - - %1$d Gruppe - %1$d Gruppen - - Aus diesen Gruppen kommt nichts an. Amethyst versucht es weiter. - Koordinatoren - Noch keine cordn-Gruppen - Eine cordn-Gruppe liegt auf einem Koordinator, der ihre Mitgliedschaft verwaltet und Nachrichten weitergibt, ohne je zu sehen, was jemand schreibt. - Eine starten - Jemanden hinzufügen - Niemand mit diesem Namen gefunden. - Personen können nur hinzugefügt werden, wenn sie ein KeyPackage bei diesem Koordinator veröffentlicht haben. - Name, npub oder name@domain - Admin - - Der %1$d Admin der Gruppe bleibt unverändert. - Die %1$d Admins der Gruppe bleiben unverändert. - - Koordinator - Koordinator-Schlüssel - Koordinator-Link - nprofile des Koordinators kopieren - Details bearbeiten - Diese Gruppe hat keine Admins: Alle können dauerhaft hinzufügen, entfernen und umbenennen. - Epoche - Gruppen-ID - Kann über diesen Koordinator hinzugefügt werden - Mitglieder - %1$s erhält von hier keine Nachrichten mehr. Bereits Gelesenes bleibt erhalten, und das lässt sich nicht rückgängig machen — die Person müsste erneut eingeladen werden. - Aus der Gruppe entfernen? - Entfernen - Speichern - Technische Details - Beitreten - Ablehnen - Ablehnen ist endgültig. Um wieder hineinzukommen, musst du erneut eingeladen werden. - Wird nur geprüft, wenn du diesen Bildschirm öffnest, und sonst nie — einen Koordinator zu fragen, ob dich jemand eingeladen hat, verrät ihm, dass du da bist, daher tut Amethyst das nicht im Hintergrund. - Einladungen konnten nicht gelesen werden. - - %1$d Person ist bereits in dieser Gruppe - %1$d Personen sind bereits in dieser Gruppe - - +%1$d weitere - Du hast keinen Koordinator geöffnet. cordn-Einladungen kommen über einen Koordinator an, daher gibt es noch nirgends etwas nachzusehen. - Keine wartenden Einladungen. - Erneut prüfen - Eine Einladung wurde für ein anderes Gerät hinterlegt - Cordn-Einladungen - %1$s hat nicht geantwortet - Über %1$s - Beim Veröffentlichen wird ein Eintrag mit deinem eigenen Kontoschlüssel signiert. Dieser Koordinator weiß dann, dass dieses Konto existiert und eingeladen werden kann — dauerhaft, und unabhängig davon, ob dich jemand einlädt. - Andere können dich nur zu einer cordn-Gruppe hinzufügen, indem sie ein KeyPackage verwenden, das du bei diesem Koordinator veröffentlicht hast. cordn hat keinen anderen Ort dafür, also kann dich ohne veröffentlichtes KeyPackage niemand einladen — und nichts sagt ihnen, warum. - Die KeyPackages konnten nicht gelesen werden. - Kein Last-Resort-Paket. Sobald die Einmal-Pakete aufgebraucht sind, schlagen Einladungen fehl. - Ein Last-Resort-Paket ist veröffentlicht, daher sind Einladungen auch dann noch möglich, wenn die Einmal-Pakete aufgebraucht sind. - - %1$d Paket, das dieses Gerät nicht öffnen kann - %1$d Pakete, die dieses Gerät nicht öffnen kann - - Der Koordinator gibt diese an jeden weiter, der dich einlädt, aber die private Hälfte ist nicht auf diesem Gerät — sie gehört zu einer anderen Installation. Gibt es diese Installation nicht mehr, erzeugt eine Einladung mit einem dieser Pakete eine Welcome-Nachricht, die niemand je öffnen kann. - Eines veröffentlichen - Last-Resort veröffentlichen - - %1$d Einmal-Paket verfügbar. - %1$d Einmal-Pakete verfügbar. - - Sobald du hier veröffentlicht hast, ersetzt Amethyst verbrauchte Pakete unauffällig. Das erste veröffentlicht es nie für dich. - Alle zurückziehen - Diese zurückziehen - Beitritt zu dieser Gruppe anfragen - Dies veröffentlicht ein KeyPackage unter deinem eigenen Kontoschlüssel und teilt dem Koordinator mit, dass du in diese Gruppe möchtest — unabhängig davon, ob jemand antwortet. - Beitrittsanfrage fehlgeschlagen. - Angefragt. Ein Mitglied der Gruppe muss dich hinzufügen; die Einladung erscheint unter cordn-Einladungen. - Scannen - Diese Datei konnte nicht geöffnet werden. - Für dieses Konto ist kein Medienserver festgelegt, daher gibt es keinen Ablageort für die Datei. Wähle einen in den Einstellungen. - Diese Datei konnte nicht gelesen werden. - Diese Datei konnte nicht gesendet werden. - - %1$d Mitglied - %1$d Mitglieder - - Nachricht gelöscht - Koordinator - Cursor - Gesendet - Nachrichtendetails - bearbeitet - Angeheftet von %1$s - - %1$d angeheftete Nachricht in dieser Gruppe - %1$d angeheftete Nachrichten in dieser Gruppe - - Nächste angeheftete Nachricht - Vorherige angeheftete Nachricht - Alle angehefteten Nachrichten anzeigen - Angeheftete Nachrichten - Reaktionen - Hinzufügen - Nur ein Admin kann annehmen. In einer Gruppe ohne Admins sind das alle. - Nach Anfragen suchen - Verwerfen - Die Anfragen konnten nicht gelesen werden. - Niemand wartet auf den Beitritt. - Beitrittsanfragen - Senden - Das wurde nicht gesendet. - Der Koordinator dieser Gruppe ist nicht geöffnet, daher kann noch nichts gesendet werden. - Kopiert - Link kopieren - Jeder mit diesem Link kann den Beitritt anfragen. Ein Admin muss ihn trotzdem noch hereinlassen. - Diese Gruppe teilen - Sprachnachricht abspielen - Sprachnachricht aufnehmen - Stoppen und senden - Amethyst braucht die Kamera, um QR-Codes zu scannen. Es wird nichts aufgezeichnet oder hochgeladen — die Bilder werden auf dem Gerät ausgewertet und verworfen. - Kamera erlauben - Der Kamerazugriff ist für Amethyst deaktiviert. Du kannst ihn in den Android-Einstellungen wieder aktivieren. - Einstellungen öffnen - Der QR-Decoder konnte auf diesem Gerät nicht starten. Du kannst den Code trotzdem als Text einfügen. - Licht einschalten - Licht ausschalten - Es ist dunkel — probier das Licht - Bild scannen - Einfügen - Kein QR-Code in diesem Bild gefunden - Nichts zum Scannen in der Zwischenablage - Tippe auf den gewünschten Code - - %1$s von %2$s Teil erfasst — weiter so - %1$s von %2$s Teilen erfasst — weiter so - - Zoom zurücksetzen - Amethyst kann diesen Code nicht öffnen - Dieser Code enthält einen privaten Schlüssel oder ein Kopplungsgeheimnis, daher wird sein Inhalt hier nicht angezeigt. Verwende den Bildschirm, der ihn erwartet. - Das ist ein privater Schlüssel. Füge ihn stattdessen auf dem Anmeldebildschirm ein. - Das ist eine Wallet-Verbindung. Füge sie in den Wallet-Einstellungen hinzu. - Das ist eine Remote-Signer-Adresse. Amethyst gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können — anmelden kann es sich damit nicht. - Das ist eine App, die sich mit deinem Signierer verbinden möchte. Öffne sie über den NIP-46-Signierer-Bildschirm. - Das ist eine Lightning-Rechnung. - Das ist ein Cashu-Token. - Das ist ein Weblink. - Das ist einfacher Text, kein Nostr-Code. - Das ist ein Nostr-Code, aber keiner, den dieser Bildschirm öffnen kann. - Profil - Beitrag - Nostr-Code - Relay - Privater Schlüssel - Wallet-Verbindung - Remote-Signer-Adresse - Verbindungsanfrage an Signierer - Lightning-Rechnung - Cashu-Token - Weblink - Einfacher Text - Link öffnen - Kopieren - Kopiert - Erneut scannen - Nutze die Trainings, die du schon aufgezeichnet hast - Verbinde Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…), um „Meine Fitness“ aufzubauen, deine persönliche Trainingsübersicht, und um dieses Training aus einem zu übernehmen, das deine Uhr bereits aufgezeichnet hat, statt jede Zahl von Hand einzutippen. - Amethyst nutzt Health Connect, um „Meine Fitness“ aufzubauen — deine persönliche Trainingsübersicht, auf deinem Telefon. - „Meine Fitness“ (Menü → Du → Meine Fitness) zeigt dir, wie du trainiert hast: wie viel du diese Woche im Vergleich zur letzten gemacht hast, wie sich deine Zeit auf Laufen, Radfahren, Gehen, Schwimmen und Fitnessstudio verteilt, deine Bestleistungen, an wie vielen Tagen du trainiert hast und deine aktuelle Serie. Amethyst erstellt dieses Bild aus den Trainings, die deine Uhr oder Fitness-App bereits in Health Connect gespeichert hat, zusammen mit allen Trainings, die du in Amethyst selbst erfasst hast. Es wird auf deinem Telefon berechnet und nur dir angezeigt. - Training · welche Aktivität du gemacht hast, wann sie begann und wie lange sie dauerte. Das ist das Training selbst: Daraus ergeben sich deine Anzahl an Trainings, deine Trainingszeit, die Aufschlüsselung nach Aktivität, deine aktiven Tage und deine Serie. - Distanz · wie weit du gekommen bist, angezeigt als deine Distanz dieser Woche im Vergleich zur letzten, dein Wochendurchschnitt, deine Distanz pro Aktivität und deine längste Distanz. - Aktive und Gesamtkalorien · die Energie, die ein Training verbrannt hat, angezeigt als deine Energie dieser Woche und im Vergleich zur letzten Woche. Aktive Kalorien werden verwendet, wenn deine Quelle sie erfasst; Gesamtkalorien dienen als Ersatz für Quellen, die nur den Gesamtenergieverbrauch erfassen. - Herzfrequenz · Durchschnitt und Maximum jedes Trainings, zusammengefasst zu deiner durchschnittlichen und höchsten Herzfrequenz im Zeitraum. So unterscheidest du eine lockere Woche von einer harten bei gleicher Distanz. - Schritte · die Schrittzahl eines Laufs, Spaziergangs oder einer Wanderung, angezeigt als dein wöchentlicher Schrittdurchschnitt und deine höchste Schrittzahl. - Höhenmeter · wie viel du aufgestiegen bist, angezeigt als dein wöchentlicher Durchschnitt an Höhenmetern und dein größter Anstieg. Daran erkennst du eine flache Woche von einer hügeligen. - Liest nur Trainings der letzten 4 Wochen, und nur solange „Meine Fitness“ oder der Trainings-Editor geöffnet ist. Liest nie im Hintergrund. - Sendet deine Gesundheitsdaten nirgendwohin. „Meine Fitness“ wird auf deinem Telefon berechnet und bleibt dort. Wenn du später ein Training posten möchtest, tippst du bei diesem Training auf Teilen, liest den Beitrag, den Amethyst ausgefüllt hat, und veröffentlichst ihn selbst — ein Training nach dem anderen, nie automatisch. - Die gesamte Funktion ist optional. Widerrufe die Berechtigungen jederzeit in Health Connect — „Meine Fitness“ zeigt dann wieder die Aufforderung — oder schalte die Editor-Vorschläge unter Einstellungen → Editor-Einstellungen aus. Der Rest von Amethyst funktioniert weiter. - Meine Fitness - Letzte 4 Wochen - Distanz, Kalorien und Herzfrequenz werden geladen… - Die letzten vier vollen Wochen deines Trainings: die Trainings, die du veröffentlicht hast, plus alles, was Health Connect auf diesem Telefon erfasst. Amethyst berechnet die Übersicht auf dem Gerät und veröffentlicht nichts davon. - Diese Woche - ggü. letzter Woche - Wochendurchschnitt - Trainings - Aktive Tage - Tage in Folge - Zeit - %1$d Std. %2$d Min. - %1$d Min. - %1$d Sek. - Distanz - Kalorien - Schritte - Höhenmeter - Ø HF - Max. HF - Nach Aktivität - Bestleistungen - Längste Distanz - Längstes Training - Größter Anstieg - Meiste Schritte - Höchste Herzfrequenz - Letzte Trainings - In den letzten 4 Wochen wurde nichts erfasst. Trainings, die du postest, und alle, die deine Uhr aufzeichnet, erscheinen hier. - Verfolge dein Training - Verbinde Health Connect, um deine Wochensummen, den Trend im Wochenvergleich, deine Bestleistungen und deine Trainingsserie zu sehen. Alles bleibt auf deinem Telefon. - Verbinde Health Connect, um die Trainings deiner Uhr mit Herzfrequenz, Schritten und Höhenmetern hinzuzufügen. - Verbinden - Dieses Training teilen - S/min - kcal - km - mi - m - ft - Wird geladen - Warte, bis deine privaten Nachrichten geladen sind… - %1$02d:%2$02d - %1$d:%2$02d:%3$02d - Gruppe aktualisiert - Anhang - Nachricht - %1$s: %2$s - Gruppe erstellen - Marmot-Einladungen gehen an ein anderes Gerät und können hier nicht geöffnet werden. - Schließen - Von diesem Gerät veröffentlichen - Gezappten Beitrag öffnen - Untertitel - Untertitel ein- oder ausschalten (ausgeblendet, wenn das Video keine hat) - Nicht gesendet - Erneut versuchen - Wird gesendet… - Wird noch gesendet… - Dieses Konto wurde aus Amethyst entfernt. - Aus - Untertitel ausschalten - Untertitel einschalten - Untertitel - App-Liste - Artikelliste - Lesezeichen-Set - Lieblings-Feeds - Git-Autoren - Git-Repositories - Vertrauenswürdige Wiki-Autoren - Vertrauenswürdige Wiki-Relays - Interessen - Medien-Follows - Medien-Starterpaket - Stummgeschaltet für Typ %1$s - Bilderliste - Gruppen - Video-Zusammenarbeit - Videoliste - Video-Untertitel - Amethyst für Android kann sich nicht mit einer bunker://-Adresse anmelden. Es gibt solche Adressen heraus, damit andere Apps es um Signaturen bitten können, aber anmelden kann es sich damit nicht. Verwende eine externe Signierer-App oder Amethyst Desktop. - Das ist eine App, die sich mit deinem Signierer verbinden möchte, keine Anmeldemöglichkeit. Melde dich zuerst an und öffne sie dann über den NIP-46-Signierer-Bildschirm. - Meine Fitness - Diese Liste hat private Mitglieder, die nur ihr Autor sehen kann - Cache bearbeiten - Tour bearbeiten - Cache - Tour - Cache-Touren - Geocaches - Fund loggen - Cache verstecken - Neue Tour - Hat eine Mitwirkungsnennung angenommen - Hat eine Mitwirkungsnennung abgelehnt - Nennung angenommen - Audio von - Mit - Inspiriert von - Verweise - Private Liste - - %1$d Video - %1$d Videos - - Dieses Gerät empfängt jetzt deine Marmot-Einladungen. - KeyPackage konnte nicht veröffentlicht werden: %1$s - Kein Relay hat das KeyPackage angenommen. Prüfe deine Relay-Einstellungen und versuche es erneut. - Wird gesendet… - Konnte nicht gesendet werden. Tippe, um es erneut zu versuchen. - - %1$d neue Nachricht - %1$d neue Nachrichten - - Cordn-Gruppen - Ende-zu-Ende-verschlüsselte Gruppen, die von einem Koordinator deiner Wahl statt von Relays geordnet werden. - Cordn-Gruppe - Verschlüsselter Gruppenchat, geordnet von einem Koordinator deiner Wahl. - Koordiniert - Ideal für Teams, die eine verlässliche Reihenfolge der Unterhaltung wollen. - Cordn-Gruppe erstellen - Ende-zu-Ende-verschlüsselt mit MLS — der Koordinator kann nie eine Nachricht lesen. - Eine gemeinsame Reihenfolge für alle, sodass der Verlauf nicht durch eine falsch gehende Uhr durcheinandergeraten kann. - Der Koordinator erfährt, wer in der Gruppe ist und wann ihr schreibt, auch wenn er nicht lesen kann, was ihr sagt. 3D-Objekt %1$d Eckpunkte · %2$d Flächen Dieses 3D-Objekt konnte nicht gelesen werden (Regel %1$s) @@ -5664,6 +5399,7 @@ %1$d Byte an Daten, die dieser Client nicht lesen kann %1$d Bytes an Daten, die dieser Client nicht lesen kann + Schließen Link kopieren Suchen @@ -6094,4 +5830,268 @@ Die Dienste, denen du vertraust, Personen für dich zu bewerten und zu verifizieren. Liste vertrauenswürdiger Relays Relays, denen du vertraust, sodass ohne Nachfrage eine Verbindung aufgebaut wird. + Löschen + Bearbeiten + Deine Nachricht wird bearbeitet + Zur Nachricht + Anheften + Antworten + Loslösen + Diese Änderung konnte nicht vorgenommen werden. + Der Koordinator hat nicht geantwortet, daher wurde nichts geändert. Versuche es gleich noch einmal. + %1$s hat kein KeyPackage bei diesem Koordinator veröffentlicht und kann daher noch nicht hinzugefügt werden. Bitte die Person, cordn zuerst auf diesem Koordinator zu öffnen. + Du kannst dich nicht selbst aus einer cordn-Gruppe entfernen. + %1$s konnte kein Zugang zur Gruppe gegeben werden, daher wurde das Hinzufügen abgebrochen. + %1$s ist nicht in dieser Gruppe. + Diese Person + Dieser Koordinator hat ein KeyPackage geliefert, das jemand anderem gehört, daher wurde %1$s nicht hinzugefügt. Es wurde nichts geändert. + Deine Gruppen-Verschlüsselungsschlüssel, deine KeyPackages und jede Nachricht in diesen Gruppen. Wer die Datei öffnet, kann die Unterhaltungen lesen. Sobald sie Amethyst verlässt, schützt sie nur noch die Passphrase. + Was in der Datei steckt + Dein nsec kann cordn-Gruppen nicht zurückholen. Eine cordn-Gruppe existiert als Verschlüsselungszustand auf diesem Gerät und als geordneter Datenstrom auf einem Koordinator, der ihn nicht lesen kann — verlierst du das Gerät ohne Backup, ist die Gruppe weg, samt allem, was darin schon gesagt wurde. + Exportieren + Backup gespeichert. + Das hat nicht funktioniert. Prüfe die Passphrase und die Datei. + Passphrase + Wiederherstellen + Beim Wiederherstellen werden die cordn-Gruppen auf diesem Gerät durch die aus der Datei ersetzt. Das ist für ein Smartphone gedacht, das ein anderes abgelöst hat — nicht, um dieselben Gruppen gleichzeitig auf zwei Geräten zu nutzen. + Jede cordn-Gruppe, die jetzt auf diesem Gerät ist, wird entfernt und durch die aus der Datei ersetzt.\n\nStelle nicht auf einem zweiten Smartphone wieder her, während das erste noch genutzt wird. Beide hätten dieselben Gruppenschlüssel, und sobald beide eine Nachricht gesendet haben, geht die Gruppe für alle kaputt — unbemerkt und ohne Weg zurück. + Die cordn-Gruppen dieses Geräts ersetzen? + Wiederherstellen + Wiederhergestellt. + Alles, was hier gesendet wird, ist für diese Gruppe verschlüsselt. Der Koordinator leitet es weiter, ohne es lesen zu können. + Noch keine Nachrichten + Neue Nachrichten + Gestern + Nachricht + Hinzufügen + Dieser Koordinator konnte nicht geöffnet werden. + npub des Koordinators kopieren + Nach Koordinatoren suchen + Hinzufügen + Liest Ankündigungen, die deine Relays ohnehin führen. Kein Koordinator wird kontaktiert, daher erfährt keiner, dass du gesucht hast. + Ankündigungen konnten nicht gelesen werden + Auf deinen Relays kündigt sich niemand an. + Vor %1$s angekündigt + Zuletzt angekündigt %1$s + + Nichts gefunden, und %1$d deiner Relays hat nicht geantwortet. + Nichts gefunden, und %1$d deiner Relays haben nicht geantwortet. + + Ein Koordinator legt die Reihenfolge der Nachrichten einer Gruppe fest und ist der Einzige, der das kann. Geht er verloren, ist die Reihenfolge verloren, und ein zweiter spiegelt den ersten nicht — daher sind das benannte Dienste, die du auswählst, keine austauschbaren Relays. + + Antwortet nicht — %1$d Anfrage in Folge ist fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. + Antwortet nicht — %1$d Anfragen in Folge sind fehlgeschlagen. Gruppen darauf werden nicht synchronisiert. + + Antwortet. + Eine Anfrage ist fehlgeschlagen; neuer Versuch läuft. + Noch nichts angefragt. + Die ausblenden, die sich nicht mehr ankündigen + Fragen, wer er ist + Koordinator-Schlüssel + Ein Name dafür (deiner, optional) + Dieser Name gehört dir und bleibt auf diesem Gerät. Ein Koordinator kann keinen Namen beweisen, daher wird er nie nach einem gefragt. + Weitere Aktionen + Noch keine Koordinatoren. + Löschen + Beim Entfernen bleiben die Gruppen auf diesem Gerät, sodass sie zurückkommen, wenn du den Koordinator wieder hinzufügst. Beim Löschen werden sie vernichtet: Die Gruppenschlüssel, die Lesepositionen und deine KeyPackages verschwinden, und diese Gruppen können nur über eine neue Einladung wieder betreten werden.\n\nDer Koordinator wird nicht benachrichtigt und behält alles, was er bereits hatte. + Diesen Koordinator löschen? + %1$s + Antwortet auf + %1$s +%2$d weitere + Entfernen + Umbenennen + Gibt an: %1$s + Name und Version eines Koordinators sind selbst gewählte Angaben. Nur sein öffentlicher Schlüssel identifiziert ihn. + Hat geantwortet, aber nichts angegeben. + Alle %1$d anzeigen + Weniger anzeigen + + %1$d anzeigen, der sich nicht mehr ankündigt + %1$d anzeigen, die sich nicht mehr ankündigen + + Diese haben sich seit über einem Monat nicht angekündigt. Eine Gruppe auf einem verschwundenen Koordinator zu erstellen, schlägt fehl. + Gruppe erstellen + + Erstellen und %1$d Person einladen + Erstellen und %1$d Personen einladen + + Jemanden hinzufügen + Admin + + %1$d Person kann hinzufügen und entfernen + %1$d Personen können hinzufügen und entfernen + + Nur ich verwalte diese Gruppe + Nur du kannst einladen, Mitglieder entfernen oder die Gruppendetails ändern. Du kannst das später an jemand anderen übergeben. + Koordinator + Ändern + Koordinatoren finden + Fertig + Anderer Koordinator + Öffentlicher Schlüssel des Koordinators (hex oder npub) + Relays, auf denen er antwortet, eines pro Zeile + + Erreicht deine %1$d Person + Erreicht alle %1$d deiner Personen + + Erreicht %1$d von %2$d — die übrigen blieben außen vor + Nicht geprüft — bei Auswahl wird nachgefragt + Beschreibung (optional) + Alle können hinzufügen und entfernen + Alle in der Gruppe können einladen, Mitglieder entfernen und die Details ändern. Jedes Mitglied kann später Admins ernennen, womit das endet. + Eine cordn-Gruppe wird von einem Koordinator deiner Wahl geordnet. Die Gruppe wird auf diesem Gerät erstellt — der Koordinator erfährt erst von ihr, wenn du jemanden einlädst oder eine Nachricht sendest. + Die Gruppe konnte nicht erstellt werden. + Gruppenname + Kein Koordinator ausgewählt + Jede Einladung ist eine Welcome-Nachricht, die beim Koordinator hinterlegt wird, daher können sie einzeln fehlschlagen. Niemand wurde benachrichtigt: Die Eingeladenen sehen sie, wenn sie ihre App das nächste Mal öffnen. + Der Koordinator hat die Einladung abgelehnt + Link senden + Zur Gruppe hinzugefügt, aber die Einladung hat die Person nicht erreicht — versuche es auf dem Gruppenbildschirm erneut + Chat öffnen + Kein Schlüssel hier, daher konnte keine Welcome-Nachricht hinterlegt werden + Die Gruppe existiert + Warte auf die Person + Fertig + Noch niemand hinzugefügt. Eine Gruppe kann leer erstellt und Einladungen später gesendet werden, aber wenn du jetzt Personen nennst, zeigt sich, welcher Koordinator sie erreichen kann. + Noch niemand — tippen, um Personen hinzuzufügen + Wenn du jetzt Personen nennst, erfahren wir, welcher Koordinator sie erreichen kann. Jede erhält eine Welcome-Nachricht auf diesem Koordinator, die sie abholt, wenn sie ihre App das nächste Mal öffnet. + + Erreichbar über %1$d Koordinator + Erreichbar über %1$d Koordinatoren + + Kein Schlüssel auf einem deiner Koordinatoren + Noch nicht geprüft + Wer hinzufügen und entfernen kann + Name + Wer dabei ist + Wo sie liegt + Neue Cordn-Gruppe + + %1$d Person ist über keinen deiner Koordinatoren erreichbar + %1$d Personen sind über keinen deiner Koordinatoren erreichbar + + Du, immer Admin + Alle in der Gruppe sehen diese Nachricht als zurückgezogen, und Amethyst zeigt ihren Text nicht mehr an. Der Koordinator behält die verschlüsselte Kopie, als die sie gesendet wurde, und wer sie schon gelesen hat, hat sie gelesen. + Nachricht zurückziehen + Vom Koordinator angenommen + Verstanden + Was dieser Koordinator sehen kann + Gruppeninfo + Diese Gruppe ist nicht verfügbar. Ihr Koordinator wurde möglicherweise entfernt. + Füge in den Einstellungen einen Koordinator hinzu, um cordn-Gruppen zu nutzen. + + %1$d Gruppe + %1$d Gruppen + + Aus diesen Gruppen kommt nichts an. Amethyst versucht es weiter. + Koordinatoren + Noch keine cordn-Gruppen + Eine cordn-Gruppe liegt auf einem Koordinator, der ihre Mitgliedschaft verwaltet und Nachrichten weitergibt, ohne je zu sehen, was jemand schreibt. + Eine starten + Jemanden hinzufügen + Niemand mit diesem Namen gefunden. + Personen können nur hinzugefügt werden, wenn sie ein KeyPackage bei diesem Koordinator veröffentlicht haben. + Name, npub oder name@domain + Admin + + Der %1$d Admin der Gruppe bleibt unverändert. + Die %1$d Admins der Gruppe bleiben unverändert. + + Koordinator + Koordinator-Schlüssel + Koordinator-Link + nprofile des Koordinators kopieren + Details bearbeiten + Diese Gruppe hat keine Admins: Alle können dauerhaft hinzufügen, entfernen und umbenennen. + Epoche + Gruppen-ID + Kann über diesen Koordinator hinzugefügt werden + Mitglieder + %1$s erhält von hier keine Nachrichten mehr. Bereits Gelesenes bleibt erhalten, und das lässt sich nicht rückgängig machen — die Person müsste erneut eingeladen werden. + Aus der Gruppe entfernen? + Entfernen + Speichern + Technische Details + Beitreten + Ablehnen + Ablehnen ist endgültig. Um wieder hineinzukommen, musst du erneut eingeladen werden. + Wird nur geprüft, wenn du diesen Bildschirm öffnest, und sonst nie — einen Koordinator zu fragen, ob dich jemand eingeladen hat, verrät ihm, dass du da bist, daher tut Amethyst das nicht im Hintergrund. + Einladungen konnten nicht gelesen werden. + + %1$d Person ist bereits in dieser Gruppe + %1$d Personen sind bereits in dieser Gruppe + + +%1$d weitere + Du hast keinen Koordinator geöffnet. cordn-Einladungen kommen über einen Koordinator an, daher gibt es noch nirgends etwas nachzusehen. + Keine wartenden Einladungen. + Erneut prüfen + Eine Einladung wurde für ein anderes Gerät hinterlegt + Cordn-Einladungen + %1$s hat nicht geantwortet + Über %1$s + Beim Veröffentlichen wird ein Eintrag mit deinem eigenen Kontoschlüssel signiert. Dieser Koordinator weiß dann, dass dieses Konto existiert und eingeladen werden kann — dauerhaft, und unabhängig davon, ob dich jemand einlädt. + Andere können dich nur zu einer cordn-Gruppe hinzufügen, indem sie ein KeyPackage verwenden, das du bei diesem Koordinator veröffentlicht hast. cordn hat keinen anderen Ort dafür, also kann dich ohne veröffentlichtes KeyPackage niemand einladen — und nichts sagt ihnen, warum. + Die KeyPackages konnten nicht gelesen werden. + Kein Last-Resort-Paket. Sobald die Einmal-Pakete aufgebraucht sind, schlagen Einladungen fehl. + Ein Last-Resort-Paket ist veröffentlicht, daher sind Einladungen auch dann noch möglich, wenn die Einmal-Pakete aufgebraucht sind. + + %1$d Paket, das dieses Gerät nicht öffnen kann + %1$d Pakete, die dieses Gerät nicht öffnen kann + + Der Koordinator gibt diese an jeden weiter, der dich einlädt, aber die private Hälfte ist nicht auf diesem Gerät — sie gehört zu einer anderen Installation. Gibt es diese Installation nicht mehr, erzeugt eine Einladung mit einem dieser Pakete eine Welcome-Nachricht, die niemand je öffnen kann. + Eines veröffentlichen + Last-Resort veröffentlichen + + %1$d Einmal-Paket verfügbar. + %1$d Einmal-Pakete verfügbar. + + Sobald du hier veröffentlicht hast, ersetzt Amethyst verbrauchte Pakete unauffällig. Das erste veröffentlicht es nie für dich. + Alle zurückziehen + Diese zurückziehen + Beitritt zu dieser Gruppe anfragen + Dies veröffentlicht ein KeyPackage unter deinem eigenen Kontoschlüssel und teilt dem Koordinator mit, dass du in diese Gruppe möchtest — unabhängig davon, ob jemand antwortet. + Beitrittsanfrage fehlgeschlagen. + Angefragt. Ein Mitglied der Gruppe muss dich hinzufügen; die Einladung erscheint unter cordn-Einladungen. + Scannen + Diese Datei konnte nicht geöffnet werden. + Für dieses Konto ist kein Medienserver festgelegt, daher gibt es keinen Ablageort für die Datei. Wähle einen in den Einstellungen. + Diese Datei konnte nicht gelesen werden. + Diese Datei konnte nicht gesendet werden. + + %1$d Mitglied + %1$d Mitglieder + + Nachricht gelöscht + Koordinator + Cursor + Gesendet + Nachrichtendetails + bearbeitet + Angeheftet von %1$s + + %1$d angeheftete Nachricht in dieser Gruppe + %1$d angeheftete Nachrichten in dieser Gruppe + + Nächste angeheftete Nachricht + Vorherige angeheftete Nachricht + Alle angehefteten Nachrichten anzeigen + Angeheftete Nachrichten + Reaktionen + Hinzufügen + Nur ein Admin kann annehmen. In einer Gruppe ohne Admins sind das alle. + Nach Anfragen suchen + Verwerfen + Die Anfragen konnten nicht gelesen werden. + Niemand wartet auf den Beitritt. + Beitrittsanfragen + Senden + Das wurde nicht gesendet. + Der Koordinator dieser Gruppe ist nicht geöffnet, daher kann noch nichts gesendet werden. + Kopiert + Link kopieren + Jeder mit diesem Link kann den Beitritt anfragen. Ein Admin muss ihn trotzdem noch hereinlassen. + Diese Gruppe teilen + Sprachnachricht abspielen + Sprachnachricht aufnehmen + Stoppen und senden diff --git a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml index d4f890da43..9f928a700e 100644 --- a/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-hi-rIN/strings.xml @@ -1862,7 +1862,7 @@ मध्यम उच्च असंकुचित - यह अभिलेख प्रारूप परितथ्य हटाने का अवलम्बन नहीं करता। निजी जानकारी जैसे स्थान तथा यन्त्र विवरण समाविष्ट हो सकते हैं। क्या तो भी आरोहण करें। + यह अभिलेख प्रारूप उपतथ्य हटाने का अवलम्बन नहीं करता। निजी जानकारी जैसे स्थान तथा यन्त्र विवरण समाविष्ट हो सकते हैं। क्या तो भी आरोहण करें। तो भी आरोहण करें पाण्डुलिपि सम्पादन क्यूआर॰ चित्र अक्षरराशि के साथ प्रवेशांकन करें @@ -2889,6 +2889,7 @@ आप इस समुदाय के विश्वासजाल आवश्यकताओं को पार नहीं कर सके। समुदाय का नवीनतम नियम अभिलेख पुराना होने के कारण अस्वीकृत। मार्मोट॰ समूह + कोर्डन समूह समूह बनाएँ मार्मोट॰ समूह बनाएँ एक नया एमएलएस॰ समूह बनेगा। आप सदस्य जोड सकते हैं तत्पश्चात। @@ -3248,7 +3249,7 @@ जालपता के पूर्वीक्षण दिखाएँ स्वतःचालन चलचित्र स्वतः चालू करें चलचित्रों को जब पटल पर दिखाई दें - एविफ॰ से परितथ्य नहीं मिटा सके : %1$s + एविफ॰ से उपतथ्य नहीं मिटा सके। %1$s पदक पुरस्कार पीछे चलें कुंचिकाएँ सुरक्षित रखें @@ -4119,8 +4120,8 @@ आरोहण असफल विषय सन्देश - परितथ्य हटाने में असफल - श्रव्यदृश्याभिलेख से निजी परितथ्य हटाने में असफल। आरोहण निरस्त। + उपतथ्य हटाने में असफल + श्रव्यदृश्याभिलेख से निजी उपतथ्य हटाने में असफल। आरोहण निरस्त। स्मर्त्तव्यचिह्न सफलतापूर्वक स्थानान्तरित किए गए लैटनिंग स्थापना अनुपलब्ध अधिक विकल्प @@ -5056,8 +5057,8 @@ कचरालेख प्रेषक दो सूचना पृष्ठसूचक दिखाएँ। अनुचरित (लोग जिनका आप अनुचरण करते हैं) तथा सभी। अपठित संकेतक चमकता है केवल आपके द्वारा अनुचरित लोगों के गतिविधियों के लिए। सूचनाएँ विभाजित करें अनुचरित के अनुसार - निजी परितथ्य हटाने का प्रयास करता है आलम्बित श्रव्यदृश्याभिलेखों से आरोहण पूर्व - निजी परितथ्य हटाएँ + निजी उपतथ्य हटाने का प्रयास करता है आलम्बित श्रव्यदृश्याभिलेखों से आरोहण पूर्व + निजी उपतथ्य हटाएँ सम्पन्न व्यायाम पढता है स्वास्थ्य संयोजन से (सामसंग स्वास्थ्य तथा गूगिल फिट तथा फिटबिट तथा गार्मिन इत्यादि) तथा पत्र के रूप में बाँटने का सुझाव देता है। अनुमति अनुरोध केवल संयोजन करने पर। व्यायामों का सुझाव बाँटनें के लिए यन्त्रव्यवस्था @@ -5232,9 +5233,36 @@ + समायोजक समूह + समायोजक द्वारा भेजा गया। पुनःप्रसारकों द्वारा नहीं + यह समायोजक क्या देख सकता है + सन्देश विषयवस्तु + समूह में कौन हैं + कौन क्या भेजता है + पठन असाध्य + एक अस्थायी कुंचिका द्वारा + आपकी वास्तविक लेखा से सम्बद्ध + विवरण + समायोजक %1$s + सम्पर्क अब तक किया नहीं + प्रतिवचनमध्य + कोई प्रतिवचन नहीं + cordn1… + निरीक्षण + चिपकाएँ + रिक्त + वह एक कोर्डन समूह योजक नहीं। %1$s + समूह परिचायक + समायोजक + अभिगम्य इनके द्वारा + कोर्डन समूह योजक + कोर्डन समायोजक समूह योजक आमन्त्रण एमएलएस॰ चर्चा उपतथ्य गोपनीयता अनावरण + कोर्डन समायोजक + कोर्डन समायोजक जोडें हटाएँ मिटाएँ स्वास्थ्य सेवासंगणक एमएलएस॰ चर्चा अनुक्रमण + कोर्डन कुंचिका पोटलियाँ FONTOS: Mentse el a kulcsait! - A titkos kulcs (nsec) olyan, mint egy jelszó, amelyet SOHA nem lehet visszaállítani vagy helyreállítani. Ez az EGYETLEN módja a fiókhoz való hozzáférésnek, és aki birtokolja, az örökre irányítja ezt a fiókot. Ha elveszíti, a fiókja végleg elvész. Soha ne adja tovább és tárolja egy jelszókezelőben. + A titkos kulcs (nsec) olyan, mint egy jelszó, amelyet SOHA nem lehet visszaállítani vagy helyrehozni. Ez az EGYETLEN módja a fiókhoz való hozzáférésnek, és aki birtokolja, az örökre irányítja ezt a fiókot. Ha elveszíti, a fiókja végleg elvész. Soha ne adja tovább és tárolja egy jelszókezelőben. Nyilvános kulcs (megosztható): Titkos kulcs (SOHA ne ossza meg!): Elmentettem a kulcsaimat, folytatás @@ -33,7 +33,7 @@ A titkos kulcs (nsec) egy jelszó, amelyet SOHA nem lehet visszaállítani. Ez az egyetlen alkalom, amikor megjelenik — mentse el most, különben örökre elveszíti a hozzáférését ehhez a fiókhoz. Saját kulcsok Még valami - Tárolja a titkos kulcsát egy jelszókezelőben. Ha elveszíti, akkor senki sem tudja helyreállítani Önnek. + Tárolja a titkos kulcsát egy jelszókezelőben. Ha elveszíti, akkor senki sem tudja helyrehozni Önnek. Ez csak egy olvasásra jogosult fiók. Nem jött létre titkos kulcs, így nincs miről biztonsági mentést készíteni. Kulcsok biztonsági mentése @@ -42,7 +42,7 @@ QR-kód megjelenítése QR-kód elrejtése Titkos kulcs (nsec) - A titkos kulcs egy jelszó, amelyet SOHA nem lehet visszaállítani vagy helyreállítani. Aki ismeri, az örökre irányíthatja ezt a fiókot. Soha ne adja meg senkinek és tárolja egy biztonságos jelszókezelőben. + A titkos kulcs egy jelszó, amelyet SOHA nem lehet visszaállítani vagy helyrehozni. Aki ismeri, az örökre irányíthatja ezt a fiókot. Soha ne adja meg senkinek és tárolja egy biztonságos jelszókezelőben. A titkos kulcs rejtve van Titkos kulcs megjelenítése Titkos kulcs elrejtése @@ -654,10 +654,10 @@ Az összes ismert megjelölése olvasottként Az összes új üzenet megjelölése olvasottként Összes megjelölése olvasottként - ## Kulcs- és biztonsági mentési tippek \n\nFiókját egy titkos kulcs védi. A kulcs egy hosszú véletlenszerű karakterlánc, amely **nsec1**-vel kezdődik. Bárki, aki az Ön titkos kulcsához hozzáfér, az Ön személyazonosságának használatával bármilyen tartalmat közzétehet. \n\n- **Ne** helyezze el a titkos kulcsát olyan webhelyen vagy szoftverben, amelyben nem bízik. \n- Az Amethyst fejlesztői az Ön titkos kulcsát **soha** nem fogják Öntől elkérni. \n- A fiók-helyreállításának érdekében, a titkos kulcsáról **mindig** készítsen biztonsági másolatot. Javasoljuk egy jelszókezelő használatát. - A nagyobb biztonság érdekében titkosítsa egy jelszóval. A jelszóval védett kulcs **ncryptsec1**-vel kezdődik, és a jelszava nélkül nem használható. \n\nHa elveszíti a jelszavát, nem tudja visszaállítani a kulcsát. + ## Kulcs- és biztonsági mentési tippek \n\nFiókját egy titkos kulcs védi. A kulcs egy hosszú véletlenszerű karakterlánc, amely **nsec1**-vel kezdődik. Bárki, aki az Ön titkos kulcsához hozzáfér, az Ön személyazonosságának használatával bármilyen tartalmat közzétehet. \n\n- **Ne** helyezze el a titkos kulcsát olyan webhelyen vagy szoftverben, amelyben nem bízik. \n- Az Amethyst fejlesztői az Ön titkos kulcsát **soha** nem fogják Öntől elkérni. \n- A fiókja helyrehozása érdekében, a titkos kulcsáról **mindig** készítsen biztonsági másolatot. Javasoljuk egy jelszókezelő használatát. + A nagyobb biztonság érdekében titkosítsa egy jelszóval. A jelszóval védett kulcs **ncryptsec1**-vel kezdődik, és a jelszava nélkül nem használható. \n\nHa elveszíti a jelszavát, nem tudja helyrehozni a kulcsát. Biztonsági mentés készítése a kulcsokról - A titkos kulcs megadása az egyetlen módja a fiók elérésének. Ha elveszíti, akkor soha nem tudja visszaszerezni. Mentse el biztonságos helyre most. + A titkos kulcs megadása az egyetlen módja a fiók elérésének. Ha elveszíti, akkor soha nem tudja helyrehozni. Mentse el biztonságos helyre most. Biztonsági mentés most Elmentettem a kulcsokat Eltüntetés @@ -1021,7 +1021,7 @@ Bejelentkezve maradás az Amethyst újraindításáig Bejelentkezés elfelejtése Ha szükség lesz rá, a(z) %1$s ismét rákérdez. - Nincs visszaállítva. A „Soha ne jelentkezzen be” be van kapcsolva, ezért az Amethyst nem fog bejelentkezni ide: %1$s. + Nincs helyreállítva. A „Soha ne jelentkezzen be” be van kapcsolva, ezért az Amethyst nem fog bejelentkezni ide: %1$s. Letiltva az Ön tiltólistája alapján Az Amethyst soha nem jelentkezik be a letiltott átjátszókra. Semmi sincs letiltva. Az Ön által blokkolt átjátszókra soha nem fog bejelentkezni, függetlenül attól, hogy mit állít be itt. @@ -1927,8 +1927,8 @@ Beállítás fő pénztárcaként Összegek átmentése a fő pénztárcába %1$s satoshi menthető át - Sikeres átvitel: %1$s satoshi - Nincsenek átvihető összegek + Átmentve: %1$s satoshi + Nincsenek átmenthető összegek Nem sikerült beolvasni ezt a pénztárcát Először állítsa be a fő pénztárcáját, majd mentse át az összegeket a régebbiekből. Saját pénztárca beállítása… @@ -1971,10 +1971,10 @@ Ajánlás törlése Visszavonja az ajánlást? Egy pénzverde ajánlása - Visszaállítás seed-ből + Helyrehozás seed-ből Minden korábbi titok újralevezetése, és minden mint megkérdezése, hogy visszaadja-e a még nyilvántartásban lévő vak aláírásokat. Hasznos eszközvesztés vagy egy megbízhatatlan átjátszó általi token-esemény törlése esetén. Pénzverdék vizsgálata… - Nem található visszaállítható bizonyíték. + Nem található helyreállítható bizonyíték. Nutzapok fogadásának leállítása Vonja vissza a kind:10019 eseményét, hogy mások ne küldhessenek Önnek több nutzapot. Az Ön pénztárcája és egyenlege változatlan marad. Leállítja a nutzapok fogadását? @@ -1982,18 +1982,18 @@ Nutzap-kulcs újraelőállítása Egy teljesen új kulcs előállítása a nutzapok fogadásához. Ritkán van rá szükség - általában csak akkor, ha a jelenlegi kulcsa kiszivárgott. A régi kulcsára már elküldött, de még be nem váltott nutzapok elvesznek. Újraelőállítja a nutzap-kulcsot? - Egy új P2PK kulcs kerül előállításra és közzétételre a kind:10019 és kind:17375 eseményeiben, megtartva a jelenlegi pénzverdéit. A küldők elkezdenek nutzapokat zárolni az új kulcshoz. A régi kulcsára már elküldött, de még be nem váltott nutzapok helyreállíthatatlanná válnak. Erre ritkán van szükség. + Egy új P2PK kulcs kerül előállításra és közzétételre a kind:10019 és kind:17375 eseményeiben, megtartva a jelenlegi pénzverdéit. A küldők elkezdenek nutzapokat zárolni az új kulcshoz. A régi kulcsára már elküldött, de még be nem váltott nutzapok helyrehozhatatlanná válnak. Erre ritkán van szükség. Kulcs újraelőállítása Nutzap-kulcs importálása - Cserélje le a nutzap-kulcsát egy beillesztett kulcsra - például a pénztárca biztonsági mentésből történő visszaállításához. Ritkán van rá szükség. A régi kulcsához zárolt nutzapek a továbbiakban nem lesznek beválthatók. + Cserélje le a nutzap-kulcsát egy beillesztett kulcsra, például a pénztárca biztonsági mentésből történő helyreállításához. Ritkán van rá szükség. A régi kulcsához zárolt nutzapek a továbbiakban nem lesznek beválthatók. Importálja a nutzap-kulcsot? Illessze be a nutzapok fogadásához használandó P2PK privát kulcsot (hex). A kind:10019 és kind:17375 eseményei újra közzé lesznek téve ezzel a kulccsal, megtartva a jelenlegi pénzverdéit. A jelenlegi kulcsához zárolt nutzapok többé nem lesznek beválthatók, kivéve, ha a kulcs megegyezik. Erre ritkán van szükség. P2PK privát kulcs (hex) Kulcs importálása Pénztárca törlése - Távolítsa el a pénztárcát és állítsa le a nutzapokat. A fennmaradó egyenleg helyreállíthatatlanná válhat. + Távolítsa el a pénztárcát és állítsa le a nutzapokat. A fennmaradó egyenleg helyrehozhatatlanná válhat. Törli ezt a pénztárcát? - Ez a művelet a kind:17375 pénztárca és a kind:10019 nutzap-információ törlését kéri. Az ecash-igazolások nem törlődnek a pénzverdékből, de a pénztárca kulcsának eltűnésével a megmaradt egyenleg és a be nem váltott nutzapok helyreállíthatatlanná válhatnak. Győződjön meg arról, hogy először elköltötte vagy áthelyezte a pénzét. Ez a művelet nem vonható vissza. + Ez a művelet a kind:17375 pénztárca és a kind:10019 nutzap-információ törlését kéri. Az ecash-igazolások nem törlődnek a pénzverdékből, de a pénztárca kulcsának eltűnésével a megmaradt egyenleg és a be nem váltott nutzapok helyrehozhatatlanná válhatnak. Győződjön meg arról, hogy először elköltötte vagy áthelyezte a pénzét. Ez a művelet nem vonható vissza. Számla kifizetése Árajánlat kérése Árajánlat kérése a pénzverdétől… @@ -2159,7 +2159,7 @@ Beállítások megjelenítése Húzza a rendezéshez, koppintson az ✕ gombra az eltávolításhoz Eltávolítás - Alapértelmezés visszaállítása + Alapértelmezés helyreállítása Saját oldalsó menü Nyisson meg egy szakaszt, és kapcsolja ki azokat a sorokat, amelyeket soha nem használ. A beállítások mindig láthatók maradnak, így bármikor visszatérhet ide. A szakaszok sorrendje rögzített. Szakaszok @@ -2244,7 +2244,7 @@ %1$s beállította, hogy az üzenetek %2$s után tűnjenek el %1$s kikapcsolta az eltűnő üzeneteket %1$s archiválta ezt a csatornát - %1$s visszaállította ezt a csatornát + %1$s helyreállította ezt a csatornát %1$s létrehozta ezt a csatornát %1$s törölte ezt a csatornát %1$s törölt egy üzenetet @@ -2889,6 +2889,7 @@ Ön nem felel meg a közösség bizalmi hálóra (Web-of-Trust) vonatkozó követelményeinek. A legfrissebb közösségi szabálydokumentum elavultként el lett utasítva. Marmot csoportok + Cordn csoportok Csoport létrehozása Marmot csoport létrehozása Egy új MLS-csoport jön létre. A tagokat később adhatja hozzá. @@ -3420,7 +3421,7 @@ Kifizetsz %1$s satot + legfeljebb %2$s sat díjat? Törlési kérés közzététele a(z) %1$s pénzverdéről szóló kind:38000 ajánlásához. Azok az átjátszók, amelyek tiszteletben tartják a NIP-09-et, el fogják távolítani. - %1$s visszaállíttva %2$s bizonyítékon keresztül. + %1$s helyrehozva %2$s bizonyítékon keresztül. Cashu megkapva %1$s satoshi érkezett a pénztárcájába. (Költség: %2$s satoshi) Nem biztonságos Cashu pénzverde-cím @@ -3529,7 +3530,7 @@ Meghívási hivatkozás visszavonva. Meghívóhivatkozás Közösség elhagyása - Elhagyja a következőt: %1$s? Eltávolításra kerül ezen fiók listájáról, és leáll a szinkronizálása az Ön eszközein. A közösség nem kap értesítést, és Ön nem kerül le a tagok listájáról. Azok az üzenetek, amelyeket már nem tud visszafejteni, helyreállíthatatlanná válhatnak, és csak új meghívóval térhet vissza. + Elhagyja a következőt: %1$s? Eltávolításra kerül ezen fiók listájáról, és leáll a szinkronizálása az Ön eszközein. A közösség nem kap értesítést, és Ön nem kerül le a tagok listájáról. Azok az üzenetek, amelyeket már nem tud visszafejteni, helyrehozhatatlanná válhatnak, és csak új meghívóval térhet vissza. Ön hozta létre ezt a közösséget. A kilépés nem törli, és nem is adja át másnak, de eldobja a listáján tárolt tulajdonosi kulcsot — a jövőben nem fogja tudni kezelni. Elhagyja a közösséget? Kinevezés adminisztrátornak @@ -4250,7 +4251,7 @@ nKisalkalmazások Nem sikerült elküldeni az üzenetet Előadó elnémítása - Ezen előadó visszaállítása + Ezen előadó helyreállítása Elhagyás Nem sikerült a szobát zártként megjelölni. Távozás ettől függetlenül — a szoba automatikusan bezár. @@ -5232,14 +5233,120 @@ + Koordinátor-csoport + Koordinátor kézbesíti, nem az átjátszók + Mit lát ez a koordinátor + A koordinátor egyetlen kiszolgáló, amely ennek a csoportnak minden üzenetét sorrendben továbbítja. Ez másfajta kompromisszum, mint az átjátszóké; érdemes utánaolvasnia, mielőtt úgy kezelné ezt, mint bármely más csevegést. + Üzenetek tartalma + Ki van a csoportban + Ki mit küld + Olvashatatlan + Eldobható kulcs alatt + A valódi fiókjához van kötve + Részletek + %1$s koordinátor + Ez nem ugyanaz a kitettség, mint egy Marmot csoportnál, ahol egyetlen üzemeltető sem birtokolja a teljes beszélgetést. + A csatlakozás mindkét végén valódi fiókokat nevez meg, így a koordinátor tudja, ki van ebben a csoportban. + Egyetlen eldobható kulcs küld és kér le minden üzenetet az itteni csoportjához, így a koordinátor megállapíthatja, hogy mindegyik Ön. + Egyetlen üzemeltető birtokolja minden általa továbbított csoport teljes, sorrendbe rendezett előzményét. + A közzétett kulcscsomag egy aláírt nyilvántartás arról, hogy ez a fiók koordinátort használ, és a koordinátor bárkinek átadhatja. + Az üzenetek nincsenek kitöltve, így a koordinátor mindegyiket látja, hogy milyen hosszúak. + Ez a kliens nem titkosít a koordinátor felé. Ez egy hiba, jelentse minél hamarabb. + Még nem jött létra a kapcsolat + Válaszol + Nem válaszol + Illesszen be egy Önnel megosztott cordn1… hivatkozást, hogy lássa, melyik koordinátor továbbítja azt a csoportot, és mit tudna meg az az üzemeltető Önről, ha csatlakozna. + cordn1… + Vizsgálat + Beillesztés + Törlés + Ez nem egy Cordn csoporthivatkozás: %1$s + Csoportazonosító + Koordinátor + Elérhető ezeken keresztül + Ez a hivatkozás nem nevez meg koordinátort, így önmagában nem követhető. Kérdezze meg a küldőt, melyik koordinátor továbbítja a csoportot. + A hivatkozás vizsgálata nem csatlakozik semmihez, és a birtoklása nem tesz taggá. + Cordn csoporthivatkozás + cordn koordinátor csoporthivatkozás meghívó mls csevegés metaadat adatvédelem kitettség + Cordn koordinátorok + cordn koordinátor hozzáadás eltávolítás tisztítás állapot kiszolgáló mls csevegés sorbarendezés + Cordn kulcscsomagok + cordn kulcscsomag közzététel meghívó végsőmegoldás koordinátor mls + Cordn biztonsági mentés + cordn biztonsági mentés helyreállítás exportálás importálás jelmondat csoportok helyrehozás + Cordn csoportok + Végpontok között titkosított csoportok, amelyeket az Ön által választott koordinátor rendez, nem pedig átjátszók. + %1$s csoport + Még nincsenek üzenetek + Cordn csoport, továbbítja: %1$s + Üzenet törölve + Hangfelvétel + Fénykép + Videó + Fájl + Cordn csoport + Titkosított csoportos csevegés, amelyet az Ön által választott koordinátor rendez. + Koordinált + Azoknak a csapatoknak a legjobb, amelyek a beszélgetés egyetlen megbízható sorrendjét szeretnék. + Cordn csoport létrehozása + Végponttól végpontig titkosított MLS-sel – a koordinátor soha nem olvashat el egyetlen üzenetet sem. + Egy mindenki által elfogadott sorrend, így az előzményeket egy hibás óra sem keverheti össze. + A koordinátor tudja, ki van a csoportban és mikor beszélnek, még ha nem is tudja elolvasni, mit mondanak. + Meghívták egy cordn csoportba? + Meghívók áttekintése + Cordn + Csoportok, amelyeket az Ön által választott koordinátor kézbesít, nem pedig átjátszók. + Csoporthivatkozás vizsgálata + Nézze meg, mire mutat egy cordn1… hivatkozás, mielőtt bármit tenne + Átköltöztetés erről a telefonról + Átköltöztetés egy másik telefonról + Közzétett kulcscsomagok + A hivatkozás + Az Ön által használt koordinátorok + Bemutatkozottak + Hozzáadás kézzel + Jelmondat + Biztonsági mentés készítése + Helyreállítás biztonsági mentésből + A koordinátor + Ez az eszköz + Koordinátorok + A kiszolgálók, amelyek a csoportjait továbbítják + Kulcscsomagok + Ez teszi lehetővé, hogy mások csoportokhoz adhassák Önt + Biztonsági mentés és helyreállítás + Egy jelmondattal titkosított fájl, amelyet Ön őriz + Átköltöztetés másik telefonra + Költöztesse át a csoportjait arra az eszközre, amelyre vált + Átköltöztetés egy másik eszközre + A cordn csoportjai ezen az eszközön élnek. Az átköltöztetéshez egyetlen kód kell: ez az eszköz közzéteszi őket, és a másik eszközön beolvashatja őket, majd ez az eszköz nem tárolja tovább. + Először jelentkezzen be ugyanazzal a fiókkal a másik eszközön. Ez a csoportjait költözteti át, és nem a kulcsát. + Átköltöztetés indítása + Közzététel… + Olvassa be ezt a másik eszközön + A kód csak egy mutató. Aki lefényképezi, semmit sem tud meg, amire mutat, az az Ön fiókjához van titkosítva. + Ez az eszköz átköltöztette a csoportjait + Leállította az átköltöztetést, mert a két eszköz, amely ugyanabból a csoportból küld, mindenki számára tönkretenné az üzenetek fogadását. A csoportjai még mindig ezen az eszközön vannak, és semmi sem törlődött. + Eszköz visszavétele + Ezt akkor használja, ha az átköltöztetés nem fejeződött be. Csak akkor tegye, ha a másik eszközön még nem kezdett el üzeneteket küldeni. + Fogadás egy másik eszközről + Kód beolvasása + vagy illessze be + Csoportok átköltöztetése ide + Lekérés… + Ez lecseréli az eszközön már meglévő cordn csoportokat. Ezek nem olvaszthatók egybe. + Nincsenek átköltöztethető cordn csoportok ezen az eszközön. + Először állítson be médiakiszolgálót, a titkosított dokumentumoknak valahol lenniük kell, amíg a másik telefon le nem kéri őket. + Mi hagyja el ezt az eszközt + A csoport állapota titkosítva van, és fel lesz töltve a médiakiszolgálójára, hogy a másik eszköz le tudja kérni. Az a kiszolgáló számára olvashatatlan, csak egy méretet és egy időpontot lát. Ha nem szeretne semmit hátrahagyni, utána törölje a blobokat. 3D-objektum %1$d csúcs · %2$d lap Ezt a 3D-objektumot nem sikerült beolvasni (%1$s szabály) @@ -5283,18 +5390,124 @@ %1$d bájt valamiből, amit ez a kliens nem olvas + Bezárás + Hivatkozás másolása + Keresés + Szövegméret + Asztali + Hozzáadás a kezdőképernyőhöz + Másik böngésző + Teljes képernyő + Cím szerkesztése + Vissza + Előre + Újratöltés + Betöltés leállítása + Hozzáadás a kedvencekhez + Eltávolítás a kedvencekből + Megosztás + Hivatkozás másolása + Keresés az oldalon + Szövegméret + Hozzáadás a kezdőképernyőhöz + Asztali oldal + Megnyitás böngészőben + Megnyitás a(z) %1$s alkalmazásban + Teljes képernyő + Elhagyta a(z) %1$s oldalt + Vissza az alkalmazásba + Kisebb szöveg + Nagyobb szöveg + Visszaállítás + %1$d% + Adatvédelem + Onion-útválasztás + Az oldal nem látja az Ön IP-címét + Az oldal látja az Ön IP-címét + Oldalbeállítások + Még semmi sincs engedélyezve + Mihez férhet hozzá + A kulcsai soha nem hagyják el az Amethystet + Minden aláírás, közzététel, feltöltés vagy fizetés az Ön jóváhagyását követeli meg + Nincs különleges hozzáférés + Homokozóban fut, és nem tud cselekedni a fiókjában + Engedélyek kezelése + nOldal · homokozóban + nKisalkalmazás · homokozóban + Konzol + Biztonságos kapcsolat + Nem biztonságos + Onion-útvonalon + Homokozóban futó alkalmazás + Kamera + Mikrofon + Helyzet + Rákérdezés + Engedélyezés + Blokkolás + %1$s %2$s + engedélyezve + blokkolva + Keresés vagy cím megadása + Ugrás + Törlés + Beillesztés és ugrás + Cím használata + Keresés az oldalon + %1$d / %2$d + Nincs találat + Előző találat + Következő találat + Keresés bezárása az oldalon + Összes + Hibák + Figyelmeztetések + Másolás + Törlés + Még semmi sincs naplózva + Ez az oldal használni akarja + Nézze meg, amit a kamerája lát + Hallgassa meg, amit a mikrofonja hall + Nagyjából tudja, hol van + A hívások Tor-on keresztül is felfedhetik az IP-címét. + Engedélyezés a látogatás idejére + Csak most az egyszer + Ne engedélyezze + %1$s üzeni + Ez az oldal üzeni + Oldal megakadályozása a még több párbeszédablak megjelenítésében + Az Ön válasza + Elhagyja az oldalt? + Lehet, hogy a módosításai nem lettek mentve. + Elhagyás + OK + Mégse + Kapcsolódás + A kapcsolat titkosított + Az Ön által küldött információ útközben nem olvasható. + A kapcsolat nincs titkosítva + Ne adjon meg jelszavakat vagy fizetési adatokat. + Onion-útvonalon Tor-on keresztül + Nyílt web + Tanúsítvány + %2$s állította ki %1$s részére · érvényes eddig: %3$s + Engedélyek + Sütik és oldaladatok + Bejelentkezések és beállítások, amelyeket ez az oldal ehhez a fiókhoz mentett + Oldaladatok törlése + Kijelentkezik erről az oldalról és törli az adatait? %1$s újbóli letiltása %1$s megtartása Újbóli csatlakozás ehhez: %1$s %1$s újbóli némítása - %1$s visszaállítása - Hírfolyamok visszaállítása - Helyek visszaállítása - Profil visszaállítása - Szolgáltatók visszaállítása - Átjátszók visszaállítása - Témák visszaállítása - Pénztárca visszaállítása + %1$s helyreállítása + Hírfolyamok helyreállítása + Helyek helyreállítása + Profil helyreállítása + Szolgáltatók helyreállítása + Átjátszók helyreállítása + Témák helyreállítása + Pénztárca helyreállítása Elfogadás %1$s és %2$d hozzáadva @@ -5428,7 +5641,7 @@ olvasás és írás csak írható Eltávolítva (%1$s) - Mentett verzió visszaállítása + Mentett verzió helyreállítása Ön módosította a következőt: %1$s, egy másik alkalmazásban Hírfolyamok Csevegőszobák @@ -5607,4 +5820,252 @@ A szolgáltatások, amelyekben megbízik, hogy rangsorolják és ellenőrizzék Önnek az embereket. megbízható átjátszólista Átjátszók, amelyekhez kérdezés nélkül kapcsolódhat. + Törlés + Szerkesztés + Üzenet szerkesztése + Ugrás az üzenetre + Kitűzés + Válasz + Kitűzés megszüntetése + Nem sikerült végrehajtani a módosítás. + A koordinátor nem válaszolt, így semmi sem változott. Próbálja újra. + %1$s nem tett közzé kulcscsomagot ehhez a koordinátorhoz, így még nem adható hozzá. Kérje meg, hogy először nyissa meg a cordnt ezen a koordinátoron. + Nem távolíthatja el saját magát egy cordn csoportból. + %1$s számára nem sikerült utat biztosítani a csoportba, így a hozzáadása megszakadt. + %1$s nincs ebben a csoportban. + Az a személy + Ez a koordinátor egy máshoz tartozó kulcscsomagot szolgáltatott, így %1$s nem lett hozzáadva. Semmi sem változott. + A csoport titkosítási kulcsai, a kulcscsomagjai és azokban a csoportokban lévő minden üzenet. Aki megnyitja, elolvashatja a beszélgetéseket. A jelmondat az egyetlen dolog, ami védi, miután elhagyja az Amethystet. + Mi van a fájlban + Az nsec nem tudja visszahozni a cordn csoportokat. Egy cordn csoport titkosítási állapotként él ezen az eszközön, és sorrendbe rendezett adatfolyamként egy koordinátoron, amely nem tudja elolvasni; ha biztonsági mentés nélkül elveszíti az eszközt, a csoport eltűnik, beleértve mindazt, ami már elhangzott benne. + Exportálás + Biztonsági mentés kész. + Ez nem működött. Ellenőrizze a jelmondatot és a fájlt. + Jelmondat + Helyreállítás + A helyreállítás lecseréli az ezen az eszközön lévő cordn csoportokat a fájlban lévőkre. Ez egy olyan eszköznek szól, amely átvett egy másikat; nem arra való, hogy ugyanazokat a csoportokat egyszerre két eszközön használja. + Minden most ezen az eszközön lévő cordn csoport eltávolításra és a fájlban lévőkkel lesz helyettesítve.\n\nNe állítsa helyre egy második eszközre, amíg az elsőt még használja. Mindkettő ugyanazokat a csoportkulcsokat birtokolná, és miután mindkettő küldött egy üzenetet, a csoport mindenki számára végleg használhatatlanná válna. + Lecseréli az eszköz cordn csoportjait? + Helyreállítás + Helyreállítva. + Minden ide küldött üzenet titkosítva van ehhez a csoporthoz. A koordinátor továbbítja anélkül, hogy el tudná olvasni. + Még nincsenek üzenetek + Új üzenetek + Tegnap + Üzenet + Hozzáadás + Nem sikerült megnyitni azt a koordinátort. + A koordinátor npub-jának másolása + Koordinátorok keresése + Hozzáadás + Az átjátszói által már továbbított bejelentéseket olvassa. Egyetlen koordinátorral sem lép kapcsolatba, így egyik sem tudja meg, hogy Ön keresett. + Nem sikerült beolvasni a bejelentéseket + Senki sem jelentkezik be az átjátszóin. + Bejelentve ennyi ideje: %1$s + Utoljára bejelentve: %1$s + Egy koordinátor egy csoport üzeneteit rendezi sorba, és ez az egyetlen, ami ezt megteheti. Ha elvész, a sorrend is elvész, és egy második nem tükrözi az elsőt; így ezek névvel ellátott dolgok, amelyeket Ön választ, nem pedig felcserélhető átjátszók. + Válaszol. + Egy hívás sikertelen volt; újrapróbálkozás. + Még semmit sem kérdeztek tőle. + A bejelentést abbahagyók elrejtése + Kérdezze meg, ki az + Koordinátorkulcs + Egy név kell neki (az Öné, nem kötelező) + Ez a név az Öné, és ezen az eszközön marad. Egy koordinátor nem tudja bizonyítani a nevét, ezért soha nem kérdeznek tőle ilyet. + További műveletek + Még nincsenek koordinátorok. + Tisztítás + Az eltávolítás megtartja a csoportokat ezen az eszközön, így a koordinátor újbóli hozzáadása visszahozza őket. A tisztítás törli őket: a csoportkulcsok, az olvasási pozíciók és a kulcscsomagjai mind eltűnnek, és ezekbe a csoportokba csak friss meghívással lehet újra belépni.\n\nA koordinátort nem értesítik, és megtartja mindazt, amije már volt. + Tisztítja ezt a koordinátort? + %1$s + Válaszol ezeken + %1$s és további %2$d + Eltávolítás + Átnevezés + Azt mondja, ő: %1$s + Egy koordinátor neve és verziója az általa választott szöveg. Az egyetlen dolog, ami azonosítja, a nyilvános kulcsa. + Válaszolt, de semmit sem nevezett meg. + Mind a(z) %1$d megjelenítése + Kevesebb megjelenítése + Ezek több mint egy hónapja nem jelentkeztek be. A csoport létrehozása egy eltűnt koordinátoron sikertelen lesz. + Csoport létrehozása + + Létrehozás és %1$d személy meghívása + Létrehozás és %1$d személy meghívása + + Valaki hozzáadása + Adminisztrátor + + %1$d személy adhat hozzá és távolíthat el + %1$d személy adhat hozzá és távolíthat el + + Csak én kezelem ezt a csoportot + Csak Ön hívhat meg, távolíthat el tagokat vagy változtathatja meg a csoport adatait. Ezt később átadhatja valaki másnak. + Koordinátor + Módosítás + Koordinátorok keresése + Kész + Egy másik koordinátor + Koordinátor nyilvános kulcsa (hex vagy npub) + Átjátszók, amelyeken válaszol, soronként egy + %2$d / %1$d embert érne el, a többi kimaradna + Nincs ellenőrizve; a rendszer rá fog kérdezni a kiválasztására + Leírás (nem kötelező) + Mindenki hozzáadhat és eltávolíthat + A csoport minden tagja meghívhat, eltávolíthat tagokat és megváltoztathatja a csoport adatait. Bármely tag később kinevezhet adminisztrátorokat, ami ezt megszünteti. + Egy cordn csoportot egy Ön által választott koordinátor rendez. A csoport létrehozása ezen az eszközön történik – a koordinátor csak akkor szerez tudomást a csoport létezéséről, amikor meghív valakit vagy üzenetet küld. + Nem sikerült létrehozni a csoportot. + Csoportnév + Nincs koordinátor kiválasztva + Minden meghívó egy Üdvözlés is, amelyet a koordinátoron hagynak, így egyenként sikertelenek lehetnek. Senkit sincs értesítve: az emberek legközelebb akkor látják, amikor megnyitják az alkalmazást. + A koordinátor elutasította a meghívót + Hivatkozás küldése + Hozzáadva a csoporthoz, de a meghívó nem érte el, próbálja újra a csoport képernyőjéről + Csevegés megnyitása + Itt nincs kulcs, így nem lehetett Üdvözlést hagyni + A csoport létezik + Várakozás rájuk + Kész + Még senki sincs hozzáadva. Egy csoport létrehozható üresen, és a meghívók később küldhetők, de ha most megnevezi az embereket, az mutatja meg, melyik koordinátor tudja elérni őket. + Még senki – koppintson az emberek hozzáadásához + Ha most megnevezi az embereket, akkor mondja meg, melyik koordinátor tudja őket elérni. Mindegyikük kap egy Üdvözlést azon a koordinátoron, amelyet legközelebb akkor választanak ki, amikor megnyitják az alkalmazást. + + Elérhető %1$d koordinátoron + Elérhető %1$d koordinátoron + + Nincs kulcs egyetlen Ön által használt koordinátoron sem + Még nincs ellenőrizve + Ki adhat hozzá és távolíthat el + Nevezze el + Ki van benne + Hol él + Új Cordn csoport + + %1$d személyt egyetlen Ön által használt koordinátor sem tud elérni + %1$d személyt egyetlen Ön által használt koordinátor sem tud elérni + + Ön, mindig adminisztrátor + A csoport minden tagja visszavontként fogja látni ezt az üzenetet, és az Amethyst nem fogja többé megjeleníteni a szövegét. A koordinátor továbbra is birtokolja a titkosított másolatot, amelyként elküldték, és aki már elolvasta, az már elolvasta. + Üzenet visszavonása + A koordinátor elfogadta + Értem + Mit lát ez a koordinátor + Csoportadatok + Ez a csoport nem érhető el. Lehet, hogy a koordinátorát eltávolították. + Adjon hozzá egy koordinátort a Beállításokban, hogy elkezdje használni a cordn csoportokat. + + %1$d csoport + %1$d csoport + + Ezekből a csoportokból semmi sem érkezik. Az Amethyst folyamatosan újrapróbálkozik. + Koordinátorok + Még nincsenek cordn csoportok + Egy cordn csoport egyetlen koordinátoron él, amely nyilvántartja a tagságát és továbbítja az üzeneteket, anélkül, hogy valaha is látná, mit írnak. + Indítson egyet + Valaki hozzáadása + Senkit sem találtunk azon a néven. + Csak akkor adhatók hozzá, ha közzétettek egy kulcscsomagot ehhez a koordinátorhoz. + Név, npub vagy nev@domain + Adminisztrátor + + A csoport %1$d adminisztrátora marad, ahogy van. + A csoport %1$d adminisztrátora marad, ahogy van. + + Koordinátor + Koordinátorkulcs + Koordinátorhivatkozás + A koordinátor nprofile-jának másolása + Adatok szerkesztése + Ennek a csoportnak nincsenek adminisztrátorai: mindenki hozzáadhat, eltávolíthat és átnevezhet, véglegesen. + Korszak + Csoportazonosító + Hozzáadható ezen a koordinátoron + Tagok + %1$s nem fog többé üzeneteket fogadni innen. Megtartja, amit már elolvasott, és ez nem vonható vissza – újra meg kellene hívni. + Eltávolítja a csoportból? + Eltávolítás + Mentés + Technikai részletek + Csatlakozás + Elutasítás + Az elutasítás végleges. A visszakerüléshez újra meg kell hívni. + Akkor ellenőrzi, amikor megnyitja ezt a képernyőt, és máskor soha, ha megkérdez egy koordinátort, hogy meghívta-e valaki, az elárulja neki, hogy Ön itt van, ezért az Amethyst nem teszi ezt a háttérben. + Nem sikerült beolvasni a meghívókat. + + %1$d személy már tagja ennek a csoportnak + %1$d személy már tagja ennek a csoportnak + + +%1$d további + Nincs nyitott koordinátora. A cordn meghívók egy koordinátoron keresztül érkeznek, így még nincs hol keresni. + Nincs várakozó meghívó. + Újraellenőrzés + Egy meghívó egy másik eszköznek maradt + Cordn meghívók + %1$s nem válaszolt + %1$s által + A közzététel egy bejegyzést ír alá a saját fiókkulcsával. Az a koordinátor ezután tudja, hogy ez a fiók létezik és meghívható, véglegesen, függetlenül attól, hogy meghívja-e valaki. + Mások csak úgy adhatják hozzá egy cordn csoporthoz, ha elvesznek egy kulcscsomagot, amelyet Ön közzétett annál a koordinátornál. A cordnnak nincs más helye, ahol tárolhatna egyet, így ha egy sincs közzétéve, senki sem tudja meghívni – és semmi sem mondja meg nekik, miért. + Nem sikerült beolvasni a kulcscsomagokat. + Nincsenek tartalékcsomagok. Amint az egyszer használatosak elfogynak, a meghívók sikertelenek lesznek. + Egy végső megoldás csomag közzé van téve, így a meghívó akkor is elkészíthető, ha az egyszer használatosak elfogytak. + + %1$d csomag, amelyet ez az eszköz nem tud megnyitni + %1$d csomag, amelyet ez az eszköz nem tud megnyitni + + A koordinátor odaadja ezeket bárkinek, aki meghívja Önt, de a privát fele nincs ezen az eszközön – egy másik telepítéshez tartozik. Ha az a telepítés eltűnt, egy ilyen használatával készült meghívó olyan welcome-ot hoz létre, amelyet soha senki sem tud megnyitni. + Egy közzététele + Végső megoldás közzététele + + %1$d egyszer használatos csomag elérhető. + %1$d egyszer használatos csomag elérhető. + + Miután itt közzétett, az Amethyst csendben pótolja a csomagokat, ahogy elfogynak. Az elsőt soha nem teszi közzé Ön helyett. + Összes visszavonása + Azok visszavonása + Csatlakozási kérelem ehhez a csoporthoz + Ez közzétesz egy kulcscsomagot a saját fiókkulcsa alatt, és megmondja a koordinátornak, hogy be szeretne kerülni ebbe a csoportba – függetlenül attól, hogy válaszol-e valaki. + Nem sikerült csatlakozási kérelmet küldeni. + Elküldve. A csoport egyik tagjának hozzá kell adnia Önt; a meghívó a cordn meghívók alatt fog megjelenni. + Beolvasás + Nem sikerült megnyitni azt a fájlt. + Nincs médiakiszolgáló beállítva ehhez a fiókhoz, így nincs hová tenni a fájlt. Válasszon egyet a Beállításokban. + Azt a fájlt nem sikerült beolvasni. + Nem sikerült elküldeni azt a fájlt. + + %1$d tag + %1$d tag + + Üzenet törölve + Koordinátor + Kurzor + Elküldve + Üzenet részletei + szerkesztve + %1$s rögzítette + + %1$d rögzített üzenet ebben a csoportban + %1$d rögzített üzenet ebben a csoportban + + Következő rögzített üzenet + Előző rögzített üzenet + Összes rögzített üzenet megjelenítése + Rögzített üzenetek + Reakciók + Hozzáadás + Csak az adminisztrátor fogadhat el. Egy adminisztrátorok nélküli csoportban ez mindenki. + Kérelmek ellenőrzése + Elvetés + Nem sikerült beolvasni a kérelmeket. + Senki sem vár a csatlakozásra. + Csatlakozási kérelmek + Küldés + Ez nem került elküldésre. + Ennek a csoportnak a koordinátora nincs nyitva, így még semmit sem lehet küldeni. + Másolva + Hivatkozás másolása + Bárki, aki rendelkezik ezzel a hivatkozással, kérheti a csatlakozást. Egy adminisztrátornak még mindig be kell engednie. + Csoport megosztása + Hangfelvétel lejátszása + Hangfelvétel készítése + Leállítás és küldés diff --git a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml index 1166642559..906a63b569 100644 --- a/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pl-rPL/strings.xml @@ -2909,6 +2909,7 @@ Nie spełniasz wymogów dotyczących rankingu zaufania tej społeczności. Najnowszy dokument zawierający zasady społeczności został odrzucony jako nieaktualny. Grupy Marmot + Grupy Cordn Utwórz Grupę Utwórz Grupę Marmot Zostanie utworzona nowa grupa MLS. Następnie możesz dodać członków. @@ -5460,14 +5461,120 @@ + Grupa koordynatora + Dostarczone przez koordynatora, nie przez transmitery + Co ten koordynator może zobaczyć + Koordynator to serwer, który przekazuje wszystkie wiadomości w tej grupie w kolejności. Różni się to od działania transmiterów — warto o tym wiedzieć, zanim potraktujesz to, jak zwykły czat. + Treść wiadomości + Kto jest w grupie + Kto co wysyła + Nieczytelne + Z użyciem klucza jednorazowego + Powiązane z Twoim prawdziwym kontem + Szczegóły + Koordynator %1$s + Nie jest to taka sama forma interakcji jak w grupie Marmot, gdzie żadna z osób prowadzących nie dominuje całej rozmowy. + Łączy nazwy rzeczywistych kont po obu stronach, dzięki czemu koordynator wie, kto należy do tej grupy. + Pojedynczy klucz jednorazowego użytku służy do wysyłania i pobierania danych dla każdej z grup, które tu masz, dzięki czemu koordynator może stwierdzić, że wszystkie one należą do ciebie. + Każdy operator przechowuje kompletną, uporządkowaną historię wszystkich grup, które obsługuje. + Opublikowany przez Ciebie pakiet kluczy to podpisany zapis potwierdzający, że to konto korzysta z usługi cordn, a koordynator może go przekazać dowolnej osobie. + Komunikaty nie są uzupełniane, więc koordynator widzi, jak długi jest każdy z nich. + Ten klient nie szyfruje danych przesyłanych do koordynatora. To błąd — prosimy o zgłoszenie tego. + Jeszcze się nie skontaktowano + Odpowiedź + Brak odpowiedzi + Wklej link „cordn1…” udostępniony przez inną osobę, aby sprawdzić, który koordynator prowadzi tę grupę oraz jakie informacje o tobie uzyskałby ten operator, gdybyś do niej dołączył. + cordn1… + Zbadaj + Wklej + Wyczyść + To nie jest link do grupy Cordn: %1$s + ID grupy + Koordynator + Osiągalne przez + W tym linku nie podano nazwiska koordynatora, więc nie można z niego skorzystać samodzielnie. Zapytaj osobę, która go przesłała, który koordynator prowadzi tę grupę. + Sprawdzenie linku nie powoduje dołączenia do niczego, a samo kliknięcie go nie czyni cię członkiem. + Link do grupy Cordn + cordn koordynator grupa link zaproszenie mls czat metadane prywatność ujawnienie + Koordynatorzy Cordn + cordn koordynator dodawanie usuwanie czyszczenie stan serwera mls czat sortowanie + Pakiety kluczy Cordn + cordn klucz pakiet publikacja zaproszenie ostatni resort koordynator mls + Kopia zapasowa Cordn + cordn kopia zapasowa przywracanie eksport import hasło odzyskiwanie grup + Grupy Cordn + Grupy z szyfrowaniem typu end-to-end, zarządzane przez wybranego przez Ciebie koordynatora, a nie przez transmitery. + Grupa %1$s + Brak wiadomości + Grupa Cordn, obsługiwana przez %1$s + Wiadomość usunięta + Wiadomość głosowa + Zdjęcie + Wideo + Plik + Grupa Cordn + Szyfrowany czat grupowy zorganizowany przez wybranego przez Ciebie koordynatora. + Skoordynowane + Najlepsze rozwiązanie dla zespołów, które chcą mieć jeden spójny porządek dyskusji. + Utwórz grupę Cordn + Szyfrowanie typu „end-to-end” z wykorzystaniem MLS — koordynator nigdy nie ma możliwości odczytania wiadomości. + Jedno uzgodnione postanowienie dla wszystkich, aby historia nie mogła zostać zmieniona przez błędny zegar. + Koordynator wie, kto należy do grupy i kiedy ktoś zabiera głos, mimo że nie potrafi odczytać treści wypowiedzi. + Zostałeś zaproszony do grupy „cordn”? + Przejrzyj zaproszenia + Cordn + Grupy prowadzone przez wybranego przez Ciebie koordynatora, a nie przez transmitery. + Sprawdź link do grupy + Zanim klikniesz w link typu „cordn1…”, sprawdź, dokąd prowadzi + Zmień ten telefon + Przeniesienie danych z innego telefonu + Opublikowane pakiety kluczy + Link + Koordynatorzy, z których korzystasz + Przedstawianie się + Dodaj jedną ręcznie + Hasło + Utwórz kopię zapasową + Przywróć z kopii zapasowej + Koordynator + Urządzenie + Koordynatorzy + Serwery z Twoimi grupami + Pakiety kluczy + Co pozwala innym osobom dodać cię do grupy + Kopia zapasowa i przywracanie + Przechowywany plik zaszyfrowany hasłem + Przenieś na nowy telefon + Przekaż swoje grupy na urządzenie, na które się przełączasz + Przenieś na nowy telefon + Twoje grupy Cordn są przechowywane na tym urządzeniu. Przeniesienie ich wymaga jednego kodu: ten telefon je publikuje, nowy telefon je odczytuje, a ten telefon przestaje je wyświetlać. + Najpierw zaloguj się na nowym telefonie przy użyciu tego samego konta. Spowoduje to przeniesienie twoich grup, ale w żadnym wypadku nie twojego klucza. + Rozpocznij transfer + Publikowanie… + Zeskanuj to na nowym telefonie + Kod to tylko wskazówka. Ktokolwiek go sfotografuje, nic się nie dowie — to, na co wskazuje, jest dla ciebie zastrzeżone. + To urządzenie przeniosło swoje grupy + Aplikacja przestała wysyłać i odbierać wiadomości, ponieważ wysyłanie wiadomości przez dwa telefony z tej samej grupy spowodowałoby zakłócenia dla wszystkich jej członków. Twoje grupy nadal znajdują się na tym urządzeniu i nic nie zostało usunięte. + Przywróć to urządzenie + Skorzystaj z tej opcji, jeśli przenoszenie nie zostało zakończone. Zrób to tylko wtedy, gdy nowy telefon jeszcze nie rozpoczął wysyłania danych. + Odbierz z innego telefonu + Zeskanuj kod + lub wklej + Przynieś grupy tutaj + Pobieranie… + Powyższe dane zastępują wszelkie grupy „cordn” już istniejące w tym telefonie. Nie można ich połączyć. + Na tym urządzeniu nie ma żadnych grup cordn, które można by przenieść. + Najpierw skonfiguruj serwer multimediów — zaszyfrowane dokumenty muszą gdzieś się znaleźć, dopóki drugi telefon je pobierze. + Co opuszcza to urządzenie + Stan Twojej grupy jest szyfrowany i przesyłany na serwer multimediów, aby drugi telefon mógł go pobrać. Serwer ten nie jest w stanie odczytać tych danych – widzi jedynie rozmiar i czas. Jeśli chcesz, aby nic nie pozostało, usuń te pliki po zakończeniu. Obiekt 3D %1$d wierzchołków · %2$d ścian Nie udało się odczytać tego obiektu 3D (reguła %1$s) @@ -5519,6 +5626,111 @@ %1$d bajta danych, których ten klient nie odczytuje + Zamknij + Kopiuj link + Znajdź + Rozmiar tekstu + Pulpit + Dodaj do strony głównej + Inna przeglądarka + Pełny ekran + Edytuj adres + Wróć + Przekaż + Odśwież + Zatrzymaj wczytywanie + Dodaj do ulubionych + Usuń z ulubionych + Udostępnij + Kopiuj link + Znajdź na stronie + Rozmiar tekstu + Dodaj do strony głównej + Witryna stacjonarna + Otwórz w przeglądarce + Otwórz w %1$s + Pełny ekran + Opuściłeś %1$s + Powrót do aplikacji + Mniejszy tekst + Większy tekst + Zresetuj + %1$d% + Prywatność + Przekierowanie Onion + Witryna nie widzi Twojego adresu IP + Witryna może wykryć Twój adres IP + Ustawienia witryny + Na razie nic nie jest dozwolone + Do czego ma dostęp + Twoje klucze nigdy nie opuszczają Amethystu + Każdy wpis, publikacja, przesłanie treści lub płatność wymaga Twojej zgody + Brak specjalnego dostępu + Działa w środowisku izolowanym i nie ma dostępu do Twojego konta + Zarządzaj uprawnieniami + nSite · w środowisku izolowanym + nApplet · w środowisku izolowanym + Konsola + Bezpieczne połączenie + Niezabezpieczone + Przekierowanie przez Onion + Aplikacja w środowisku izolowanym + Aparat + Mikrofon + Lokalizacja + Zapytaj + Zezwól + Zablokuj + %1$s %2$s + dozwolone + zablokowane + Szukaj lub wprowadź adres + Przejdź + Wyczyść + Wklej i przejdź + Użyj tego adresu + Znajdź na stronie + %1$d x %2$d + Brak wyników + Poprzedni wynik + Następne wynik + Zamknij wyszukiwanie na stronie + Wszytko + Błędy + Ostrzeżenia + Kopiuj + Wyczyść + Nic nie zalogowano + Ta strona chce użyć + Zobacz, co widzi Twoja kamera + Usłysz, co słyszy Twój mikrofon + Wiedz mniej więcej, gdzie się znajdujesz + Połączenia mogą ujawnić Twój adres IP nawet przy korzystaniu z sieci Tor. + Zezwól podczas odwiedzin + Tylko tym razem + Nie zezwalaj + Na tej stronie napisano + Nie pozwól tej stronie wyświetlać więcej dialogów + Twoja odpowiedź + Opuścić witrynę? + Wprowadzone zmiany mogą nie zostać zapisane. + Wyjdź + OK + Anuluj + Połączenie + Połączenie jest zaszyfrowane + Przesyłane przez Ciebie informacje nie mogą zostać odczytane podczas przesyłania. + Połączenie nie jest zaszyfrowane + Nie wpisuj haseł ani danych płatności. + Przekierowanie typu Onion przez sieć Tor + Otwarta sieć + Certyfikat + Wydany dla %1$s przez %2$s · ważny do %3$s + Uprawnienia + Pliki cookie i dane witryny + Loginy i preferencje tej witryny zapisane dla tego konta + Wyczyść dane witryny + Wylogować się z tej witryny i usunąć dane? Zablokuj %1$s ponownie Zachowaj %1$s Ponów %1$s @@ -5907,4 +6119,24 @@ Serwisy, którym ufasz, a które zajmują się klasyfikowaniem i weryfikacją osób w Twoim imieniu. lista zaufanych transmiterów Zaufane transmitery, z którymi bez wahania nawiązujesz połączenie. + Usuń + Edytuj + Edycja wiadomości + Przejdź do wiadomości + Przypnij + Odpowiedz + Odepnij + Czy zamienić grupy cordn tego urządzenia? + Przywróć + Przywrócono. + Wszystkie wiadomości wysyłane tutaj są szyfrowane dla tej grupy. Koordynator przekazuje je dalej, nie mając możliwości ich odczytania. + Brak wiadomości + Nowe wiadomości + Wczoraj + Wiadomość + Dodaj + Nie można otworzyć tego koordynatora. + Skopiuj npub koordynatora + Szukaj koordynatorów + Dodaj diff --git a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml index 33a24e3cbd..a469cfbf88 100644 --- a/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-pt-rBR/strings.xml @@ -166,6 +166,152 @@ Congestionamento Evento na via + Geocache + Tradicional + Multi-cache + Mistério + Micro + Pequeno + Regular + Grande + Outro tamanho + D%1$d + T%1$d + Primeiro a encontrar + Arte + Key Quest + Encontros verificados + Reivindicado + Arquivado + Travou? Toque para revelar a dica + Ilegível? Toque para ver a outra leitura + Por perto + Encontrei! + Encontro verificado + A prova não confere + Carregando cache… + Escondido por %1$s + Navegar + Copiar coordenadas + Coordenadas copiadas + Fotos + Missão Key Quest + Logs + Ninguém registrou este cache ainda. Seja o primeiro. + Encontrei + Não encontrei + Adicionar uma nota + Informar que precisa de manutenção + Adicionar a uma caçada + Nova caçada… + Você ainda não criou uma caçada. + Já está nesta caçada + + %1$d cache aqui + %1$d caches aqui + + Remover esta foto + Este cache foi arquivado. Ele não está mais em jogo. + Primeiro a encontrar: %1$s + O primeiro a encontrar ainda está em aberto + Dono + Editar este cache + Arquivar este cache + Arquivar. Quem procurar verá que ele está fora de jogo, e a listagem continua legível. + Confirmar o primeiro a encontrar + Publica uma tag F indicando %1$s. Isso é permanente. + Ainda não há encontros verificados, então não há ninguém para confirmar. + Mostrar o código de verificação + Imprima isto e coloque dentro do cache. O Amethyst não o armazena — se você perdê-lo, quem encontrar o cache não poderá mais provar que esteve lá. + Não encontrei + Nota + Precisa de manutenção + Registrar um encontro + Cache + Seu log + Como foi? + Adicionar uma foto + Sua resposta da Key Quest + Prova de presença + Escaneie o código dentro do cache + Escanear outro código + Verificado — você esteve no cache + Assinado uma única vez com o código da caixa. A chave nunca é salva e nunca sai desta tela. + Isso não é um código de verificação. + Esse código pertence a outro cache. + Este cache não exige prova, então você pode simplesmente registrá-lo. + Sua própria localização não é anexada a este log. + Publicar + Adicionar um log + Por perto + Mapa + Caçadas + Encontrados + Meus + Não encontrados + D ≤ %1$d + T ≤ %1$d + Tem prêmio + Exige prova + Usar minha localização + Ordenado por distância + Mais recentes primeiro + Esconder um cache + Ainda não há caches aqui. Tente uma lista de seguidos mais ampla ou esconda o primeiro. + Você ainda não registrou nenhum encontro. + Você ainda não escondeu nenhum cache. + Nenhuma caçada ainda. + Encontros + Primeiros a encontrar + Verificados + Escondidos + Centralizar em mim + Toque e segure no mapa para esconder um cache ali + Abrir + Onde + O quê + Extras + Prova + Relays + Isto publica uma localização pública com precisão de cerca de 5 metros. Qualquer pessoa no Nostr pode vê-la, e um evento publicado não pode ser retirado. + Escolha o local + Publica %1$s, além de células mais amplas de até 3 caracteres para a busca por proximidade. + Nome + Descrição + Tipo + Tamanho + Dificuldade + Terreno + Dica + Quem procurar verá + Embaralhar a dica (ROT13) + Missão Key Quest + Modificadores + Exigir prova de presença + Uma chave de verificação foi gerada para este cache. + Onde os encontros devem ser registrados + Publicar + Dê um nome ao cache. + Escolha onde o cache está escondido. + %1$d de %2$d encontrados + Começar a caçada + + %1$d cache + %1$d caches + + Título + Descrição + Imagem de capa + Caches, em ordem + Adicionar um cache + Mover para cima + Mover para baixo + Remover + Tema da página + Estilo do mapa + Dê um título à caçada. + Adicione pelo menos um cache. + Não foi possível publicar. Verifique sua conexão com os relays e tente novamente. Os zaps para isto são divididos entre: %1$d%% Valor-para-Valor @@ -188,6 +334,51 @@ Banner do perfil Aponte para o código QR + O Amethyst precisa da câmera para ler códigos QR. Nada é gravado nem enviado — os quadros são decodificados no dispositivo e descartados. + Permitir câmera + O acesso à câmera está desativado para o Amethyst. Você pode reativá-lo nas configurações do Android. + Abrir configurações + O leitor de QR não pôde ser iniciado neste dispositivo. Você ainda pode colar o código como texto. + Acender a lanterna + Apagar a lanterna + Está escuro — experimente a lanterna + Ler de uma imagem + Colar + Nenhum código QR encontrado nessa imagem + Nada na área de transferência para ler + Toque no código que você quer + + %1$s de %2$s parte capturada — continue + %1$s de %2$s partes capturadas — continue + + Redefinir zoom + O Amethyst não consegue abrir este código + Este código contém uma chave privada ou um segredo de pareamento, por isso o conteúdo não é exibido aqui. Use a tela que espera por ele. + Isto é uma chave privada. Cole-a na tela de login. + Isto é uma conexão de carteira. Adicione-a nas configurações da Carteira. + Isto é um endereço de assinador remoto. O Amethyst fornece esses endereços para que outros apps possam pedir que ele assine — não é possível entrar com um deles. + Isto é um app pedindo para se conectar ao seu assinador. Abra-o pela tela do assinador NIP-46. + Isto é uma fatura Lightning. + Isto é um token Cashu. + Isto é um link da web. + Isto é texto simples, não um código Nostr. + Isto é um código Nostr, mas não um que esta tela consiga abrir. + Perfil + Nota + Código Nostr + Relay + Chave privada + Conexão de carteira + Endereço de assinador remoto + Pedido de conexão ao assinador + Fatura Lightning + Token Cashu + Link da web + Texto simples + Abrir link + Copiar + Copiado + Ler novamente Mostrar QR Imagem de perfil Sua Foto de Perfil @@ -638,17 +829,70 @@ Horas Minutos Segundos + Use os treinos que você já registrou + Conecte o Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) para montar a Minha Forma Física, seu próprio resumo de treinos, e para preencher este treino a partir de um que seu relógio já registrou, em vez de digitar cada número à mão. Conectar %1$s km Do Health Connect O que o Amethyst lê Health Connect e Amethyst + O Amethyst usa o Health Connect para montar a Minha Forma Física — seu próprio resumo de treinos, no seu celular. + A Minha Forma Física (menu → Você → Minha Forma Física) mostra como você tem treinado: quanto fez nesta semana em comparação com a anterior, como seu tempo se divide entre corrida, ciclismo, caminhada, natação e academia, seus melhores desempenhos, quantos dias você treinou e sua sequência atual. O Amethyst monta esse panorama a partir dos treinos que seu relógio ou app de fitness já salvou no Health Connect, junto com os treinos que você registrou no próprio Amethyst. Tudo é calculado no seu celular e mostrado a você. O que o Amethyst lê e por quê + Exercício · qual atividade você fez, quando começou e quanto durou. É o treino em si: define sua contagem de treinos, seu tempo de treino, a divisão por atividade, seus dias ativos e sua sequência. + Distância · até onde você foi, mostrada como sua distância nesta semana em comparação com a anterior, sua média semanal, sua distância por atividade e sua maior distância. + Calorias ativas e totais · a energia gasta em um treino, mostrada como sua energia nesta semana e como ela se compara com a semana anterior. As calorias ativas são usadas quando sua fonte as registra; as calorias totais são a alternativa para fontes que só registram a energia total. + Frequência cardíaca · a média e o máximo de cada treino, combinados na sua frequência cardíaca média e mais alta do período. É o que diferencia uma semana leve de uma pesada com a mesma distância. + Passos · a contagem de passos de uma corrida, caminhada ou trilha, mostrada como sua média semanal de passos e sua maior contagem de passos. + Ganho de elevação · quanto você subiu, mostrado como sua média semanal de subida e sua maior subida. É o que separa uma semana plana de uma com morros. O que o Amethyst não faz + Só lê treinos das últimas 4 semanas, e só enquanto a Minha Forma Física ou o editor de treinos estiver aberto. Nunca lê em segundo plano. Nunca grava nada no Health Connect e nunca solicita seu trajeto de exercício, localização ou qualquer outro tipo de dado de saúde. + Nunca envia seus dados de saúde para lugar nenhum. A Minha Forma Física é calculada no seu celular e fica nele. Se depois você quiser publicar um treino, toque em Compartilhar nesse treino, leia a publicação que o Amethyst preencheu e publique você mesmo — um treino por vez, nunca automaticamente. + O recurso inteiro é opcional. Revogue as permissões no Health Connect a qualquer momento — a Minha Forma Física volta a exibir o convite para conectar — ou desative as sugestões do editor em Configurações → Configurações de composição. O resto do Amethyst continua funcionando. Ler a política de privacidade completa + Minha Forma Física + Últimas 4 semanas + Carregando distância, calorias e frequência cardíaca… + As últimas quatro semanas completas do seu treino: os treinos que você publicou, mais tudo o que o Health Connect registra neste celular. O Amethyst calcula o resumo no dispositivo e não publica nada dele. + Esta semana + vs. semana passada + Média semanal + Treinos + Dias ativos + Dias seguidos + Tempo + %1$d h %2$d min + %1$d min + %1$d s + Distância + Calorias + Passos + Subida + FC média + FC máx. + Por atividade + Melhores desempenhos + Maior distância + Treino mais longo + Maior subida + Mais passos + Maior frequência cardíaca + Treinos recentes + Nada registrado nas últimas 4 semanas. Os treinos que você publicar, e os que seu relógio registrar, aparecem aqui. + Acompanhe seu treino + Conecte o Health Connect para ver seus totais semanais, a tendência semana a semana, seus melhores desempenhos e sua sequência de treinos. Tudo fica no seu celular. + Conecte o Health Connect para adicionar os treinos que seu relógio registra, com frequência cardíaca, passos e subida. + Conectar + Compartilhar este treino + bpm + kcal + km + mi + m + pés Nenhum nSite encontrado ainda. Recarregar O navegador integrado requer Android 11 ou superior. @@ -1084,6 +1328,8 @@ Data e hora de encerramento da enquete Enquete encerra em %1$s Procurando o evento %1$s + Carregando + Aguardando suas mensagens privadas carregarem… Enviar Zap Adicionar uma mensagem pública Adicionar uma mensagem privada @@ -1223,6 +1469,8 @@ + %1$02d:%2$02d + %1$d:%2$02d:%3$02d Dispensar Trocar câmera Chamada de voz @@ -1357,6 +1605,14 @@ Preferências do Usuário Pesquisar configurações Nenhuma configuração encontrada para "%1$s" + Grupo atualizado + Anexo + Mensagem + %1$s: %2$s + Criar um grupo + Os convites do Marmot estão indo para outro dispositivo e não podem ser abertos aqui. + Dispensar + Publicar a partir deste dispositivo Redefinir estado do Marmot? Isto excluirá permanentemente todos os chats em grupo Marmot, histórico de mensagens e chaves MLS deste dispositivo para a conta atual. Os participantes não serão notificados e ainda poderão ver você nos grupos até a próxima confirmação deles. Esta ação não pode ser desfeita. Um novo KeyPackage será publicado na próxima sincronização do aplicativo. Redefinir @@ -1627,6 +1883,7 @@ Nenhuma transação corresponde a este filtro Carregando… Recebido + Abrir nota que recebeu o zap Enviado Atualizar Todos @@ -1943,6 +2200,8 @@ Reproduzir o vídeo em uma janela flutuante (oculto se não suportado) Transmitir para dispositivo Envia o vídeo para um receptor Chromecast na sua Wi-Fi (oculto para arquivos locais) + Legendas + Ativar ou desativar as legendas (oculto quando o vídeo não tem nenhuma) Escolha a animação exibida enquanto as notas de áudio são reproduzidas. Desligado Barras de espectro @@ -2630,6 +2889,7 @@ Você não atende aos requisitos da rede de confiança desta comunidade. O documento mais recente de regras da comunidade foi rejeitado por estar desatualizado. Grupos Marmot + Grupos Cordn Criar grupo Criar grupo Marmot Um novo grupo MLS será criado. Você pode adicionar membros depois. @@ -2921,13 +3181,18 @@ Notifica você quando alguém responde à sua publicação Respostas Novas respostas + Não enviada Responder + Tentar novamente + Enviando… + Ainda enviando… Notifica você quando alguém reposta sua publicação %1$s repostou sua publicação Repostagens Novos reposts Notificações quando um post agendado é publicado ou falha ao publicar. Posts agendados + Esta conta foi removida do Amethyst. Notifica quando alguém te manda um zap %1$s sats por %1$s @@ -3128,6 +3393,10 @@ Ativar som URL de retorno de chamada não encontrado da resposta %1$s Cancelar + Desativadas + Desativar legendas + Ativar legendas + Legendas Token Cashu Verificando o mint… Não foi possível resgatar o Cashu @@ -3605,10 +3874,12 @@ Prólogo da aventura Leitura da aventura Cena da aventura + Lista de apps Recomendações de Apps Compromisso Aplicativos RSVP do Compromisso + Lista de artigos Cabeçalho de Áudio Faixa de Áudio Podcasts criados @@ -3621,6 +3892,7 @@ Autenticação Blossom Servidores Blossom Lista de Favoritos + Conjunto de favoritos Relays de Broadcast Calendário Definição de Canal @@ -3658,6 +3930,7 @@ Pacotes de Emoji Chat Efêmero Salas de Chat Efêmeras + Feeds favoritos Podcasts favoritos Relays Favoritos Cabeçalhos de Arquivo @@ -3666,28 +3939,36 @@ Pacotes de Seguir Seguindo Geohash Embrulhos de presente + Autores Git Issue do Git Patch do Git Pull request Atualização de PR Resposta do Git Repositório Git + Repositórios Git Status do Git: Aplicado Status do Git: Fechado Status do Git: Rascunho Status do Git: Aberto + Autores de wiki confiáveis + Relays de wiki confiáveis Hashtags seguidas Destaques Autenticação HTTP Lista de relays de índice + Interesses Chats ao vivo Transmissões ao vivo + Seguidos de mídia + Pacote inicial de mídia Dados Médicos Sala de reunião Espaço de reunião Playlist de música Faixa de música Lista de silenciados + Silenciado para o kind %1$s Favoritos nomeados Servidores Nests CNPJ @@ -3702,6 +3983,7 @@ Relays de Saída Pagar para Listas de pessoas + Lista de imagens Imagens Fixados Episódio de podcast @@ -3728,6 +4010,7 @@ Relays de busca Shorts Shorts (resposta) + Grupos Comentários de torrent Torrents Provedores confiáveis @@ -3736,7 +4019,10 @@ Configurações do Usuário Status do usuário Vídeo + Colaboração em vídeo + Lista de vídeos Vídeo (resposta) + Legendas de vídeo Mensagem de voz Resposta de voz Despertar @@ -3773,6 +4059,8 @@ Alterar lugar no mapa Canais de localização Escolher um lugar no mapa + O Amethyst para Android não consegue entrar com um endereço bunker://. Ele fornece esses endereços para que outros apps possam pedir que ele assine, mas não é possível entrar com um deles. Use um app assinador externo ou o Amethyst Desktop. + Isso é um app pedindo para se conectar ao seu assinador, não uma forma de entrar. Entre primeiro e depois abra-o pela tela do assinador NIP-46. Faça login com uma chave privada para poder impulsionar postagens Faça login com uma chave privada para poder seguir Você está usando uma chave pública e as chaves públicas são somente de leitura. Faça login com uma chave privada para poder ocultar uma palavra ou frase. @@ -3863,6 +4151,7 @@ Silenciar notificações Silenciado. Clique para ativar o som Meus arquivos Blossom + Minha Forma Física Minhas listas Um nome é necessário %1$dms @@ -4126,6 +4415,7 @@ Aplicativo de assinador remoto Assinador Remoto + Esta lista tem membros privados que só o autor pode ver %1$d recurso incluído %1$d recursos incluídos @@ -4656,12 +4946,21 @@ Calendários Xadrez Descobrir + Editar cache + Editar caçada + Cache + Caçada + Caçadas de caches + Geocaches Início Importar Seguidos + Registrar um achado Mídia Mensagens Listas de reprodução Música + Esconder um cache + Nova caçada Notificações Episódios Podcasts @@ -4851,6 +5150,18 @@ Pedido para desaparecer enviado Melhor qualidade em arquivos menores, mas nem todos os dispositivos suportam reprodução em H.265. Usar codec H.265/HEVC + Aceitou um crédito de colaboração + Recusou um crédito de colaboração + Crédito aceito + Áudio de + Participação de + Inspirado em + Referências + Lista privada + + %1$d vídeo + %1$d vídeos + O download do vídeo foi iniciado… Botões do reprodutor de vídeo Vídeo salvo na galeria de vídeos do telefone @@ -4910,165 +5221,18 @@ O serviço Lightning do destinatário em %1$s não está disponível. Foi calculado a partir do endereço Lightning "%2$s". Erro: %3$s. Verifique se o servidor está funcionando e se o endereço Lightning está correto Não foi possível criar uma fatura lightning a partir de %1$s: Elemento pr não encontrado no JSON resultante. Não foi possível criar uma fatura com raios. Mensagem do %1$s: %2$s + Este dispositivo agora recebe seus convites do Marmot. + Falha ao publicar o KeyPackage: %1$s + Nenhum relay aceitou o KeyPackage. Verifique suas configurações de relays e tente novamente. + Enviando… + Não foi possível enviar. Toque para tentar novamente. + + %1$d nova mensagem + %1$d novas mensagens + - - - - - Geocache - Tradicional - Multi-cache - Mistério - Micro - Pequeno - Regular - Grande - Outro tamanho - D%1$d - T%1$d - Primeiro a encontrar - Arte - Key Quest - Encontros verificados - Reivindicado - Arquivado - Travou? Toque para revelar a dica - Ilegível? Toque para ver a outra leitura - Por perto - Encontrei! - Encontro verificado - A prova não confere - Carregando cache… - Escondido por %1$s - Navegar - Copiar coordenadas - Coordenadas copiadas - Fotos - Missão Key Quest - Logs - Ninguém registrou este cache ainda. Seja o primeiro. - Encontrei - Não encontrei - Adicionar uma nota - Informar que precisa de manutenção - Adicionar a uma caçada - Nova caçada… - Você ainda não criou uma caçada. - Já está nesta caçada - - %1$d cache aqui - %1$d caches aqui - - Remover esta foto - Este cache foi arquivado. Ele não está mais em jogo. - Primeiro a encontrar: %1$s - O primeiro a encontrar ainda está em aberto - Dono - Editar este cache - Arquivar este cache - Arquivar. Quem procurar verá que ele está fora de jogo, e a listagem continua legível. - Confirmar o primeiro a encontrar - Publica uma tag F indicando %1$s. Isso é permanente. - Ainda não há encontros verificados, então não há ninguém para confirmar. - Mostrar o código de verificação - Imprima isto e coloque dentro do cache. O Amethyst não o armazena — se você perdê-lo, quem encontrar o cache não poderá mais provar que esteve lá. - Não encontrei - Nota - Precisa de manutenção - Registrar um encontro - Cache - Seu log - Como foi? - Adicionar uma foto - Sua resposta da Key Quest - Prova de presença - Escaneie o código dentro do cache - Escanear outro código - Verificado — você esteve no cache - Assinado uma única vez com o código da caixa. A chave nunca é salva e nunca sai desta tela. - Isso não é um código de verificação. - Esse código pertence a outro cache. - Este cache não exige prova, então você pode simplesmente registrá-lo. - Sua própria localização não é anexada a este log. - Publicar - Adicionar um log - Por perto - Mapa - Caçadas - Encontrados - Meus - Não encontrados - D ≤ %1$d - T ≤ %1$d - Tem prêmio - Exige prova - Usar minha localização - Ordenado por distância - Mais recentes primeiro - Esconder um cache - Ainda não há caches aqui. Tente uma lista de seguidos mais ampla ou esconda o primeiro. - Você ainda não registrou nenhum encontro. - Você ainda não escondeu nenhum cache. - Nenhuma caçada ainda. - Encontros - Primeiros a encontrar - Verificados - Escondidos - Centralizar em mim - Toque e segure no mapa para esconder um cache ali - Abrir - Onde - O quê - Extras - Prova - Relays - Isto publica uma localização pública com precisão de cerca de 5 metros. Qualquer pessoa no Nostr pode vê-la, e um evento publicado não pode ser retirado. - Escolha o local - Publica %1$s, além de células mais amplas de até 3 caracteres para a busca por proximidade. - Nome - Descrição - Tipo - Tamanho - Dificuldade - Terreno - Dica - Quem procurar verá - Embaralhar a dica (ROT13) - Missão Key Quest - Modificadores - Exigir prova de presença - Uma chave de verificação foi gerada para este cache. - Onde os encontros devem ser registrados - Publicar - Dê um nome ao cache. - Escolha onde o cache está escondido. - %1$d de %2$d encontrados - Começar a caçada - - %1$d cache - %1$d caches - - Título - Descrição - Imagem de capa - Caches, em ordem - Adicionar um cache - Mover para cima - Mover para baixo - Remover - Tema da página - Estilo do mapa - Dê um título à caçada. - Adicione pelo menos um cache. - Não foi possível publicar. Verifique sua conexão com os relays e tente novamente. - Grupos Cordn Grupo com coordenador Entregue por um coordenador, não por relays O que este coordenador pode ver @@ -5106,6 +5270,9 @@ Acessível por Este link não indica nenhum coordenador, então não pode ser seguido sozinho. Pergunte a quem o enviou qual coordenador transporta o grupo. Inspecionar um link não entra em nada, e ter um link não torna você membro. + Link de grupo Cordn cordn coordenador grupo link convite mls chat metadados privacidade exposição Coordenadores Cordn @@ -5114,6 +5281,10 @@ cordn pacote de chaves publicar convite último recurso coordenador mls Backup do Cordn cordn backup restaurar exportar importar senha grupos recuperação + Grupos Cordn + Grupos com criptografia de ponta a ponta, ordenados por um coordenador que você escolhe, em vez de por relays. + Grupo %1$s Nenhuma mensagem ainda Grupo Cordn, transportado por %1$s @@ -5122,8 +5293,19 @@ Foto Vídeo Arquivo + Grupo Cordn + Chat em grupo criptografado, ordenado por um coordenador que você escolhe. + Coordenado + Ideal para equipes que querem uma ordem confiável única para a conversa. + Criar grupo Cordn + Criptografia de ponta a ponta com MLS — o coordenador nunca consegue ler uma mensagem. + Uma única ordem acordada para todos, então o histórico não pode ser embaralhado por um relógio errado. + O coordenador fica sabendo quem está no grupo e quando vocês conversam, mesmo sem conseguir ler o que é dito. Convidado para um grupo cordn? Ver convites + Cordn Grupos entregues por um coordenador que você escolhe, em vez de por relays. Inspecionar um link de grupo @@ -5173,453 +5355,6 @@ Defina primeiro um servidor de mídia — os documentos criptografados precisam de um lugar para ficar enquanto o outro celular os busca. O que sai deste dispositivo O estado dos seus grupos é criptografado e enviado ao seu servidor de mídia para que o outro celular possa buscá-lo. Ele é ilegível para esse servidor, que vê apenas um tamanho e um horário. Exclua os blobs depois se não quiser deixar nada para trás. - Excluir - Editar - Editando sua mensagem - Ir para a mensagem - Fixar - Responder - Desafixar - Não foi possível fazer essa alteração. - O coordenador não respondeu, então nada foi alterado. Tente novamente em instantes. - %1$s não publicou um pacote de chaves neste coordenador, então ainda não pode ser adicionado. Peça que abra o cordn neste coordenador primeiro. - Você não pode remover a si mesmo de um grupo cordn. - Não foi possível dar a %1$s um meio de entrar no grupo, então a adição foi cancelada. - %1$s não está neste grupo. - Essa pessoa - Este coordenador forneceu um pacote de chaves que pertence a outra pessoa, então %1$s não foi adicionado. Nada foi alterado. - As chaves de criptografia dos seus grupos, seus pacotes de chaves e todas as mensagens desses grupos. Qualquer pessoa que abrir o arquivo pode ler as conversas. A senha é a única coisa que o protege depois que ele sai do Amethyst. - O que há no arquivo - Sua nsec não recupera grupos cordn. Um grupo cordn existe como estado de criptografia neste dispositivo e como um fluxo ordenado em um coordenador que não consegue lê-lo — perca o dispositivo sem um backup e o grupo se vai, incluindo tudo o que já foi dito nele. - Exportar - Backup salvo. - Não funcionou. Verifique a senha e o arquivo. - Senha - Restaurar - Restaurar substitui os grupos cordn deste dispositivo pelos do arquivo. Serve para um celular que substituiu outro — não é uma forma de usar os mesmos grupos em dois dispositivos ao mesmo tempo. - Todos os grupos cordn atualmente neste dispositivo serão removidos e substituídos pelos do arquivo.\n\nNão restaure em um segundo celular enquanto o primeiro ainda estiver em uso. Ambos teriam as mesmas chaves de grupo e, assim que os dois enviarem uma mensagem, o grupo quebra para todos — silenciosamente e sem volta. - Substituir os grupos cordn deste dispositivo? - Restaurar - Restaurado. - Tudo o que for enviado aqui é criptografado para este grupo. O coordenador repassa as mensagens sem conseguir lê-las. - Nenhuma mensagem ainda - Novas mensagens - Ontem - Mensagem - Adicionar - Não foi possível abrir esse coordenador. - Copiar a npub do coordenador - Procurar coordenadores - Adicionar - Lê anúncios que seus relays já transportam. Nenhum coordenador é contatado, então nenhum deles fica sabendo que você procurou. - Não foi possível ler os anúncios - Ninguém está se anunciando nos seus relays. - Anunciado há %1$s - Último anúncio: %1$s - - Nada encontrado, e %1$d dos seus relays não respondeu. - Nada encontrado, e %1$d dos seus relays não responderam. - - Um coordenador ordena as mensagens de um grupo e é o único que pode fazer isso. Perdê-lo significa perder a ordenação, e um segundo coordenador não espelha o primeiro — por isso são escolhas nomeadas por você, não relays intercambiáveis. - - Sem resposta — %1$d chamada seguida falhou. Os grupos nele não estão sincronizando. - Sem resposta — %1$d chamadas seguidas falharam. Os grupos nele não estão sincronizando. - - Respondendo. - Uma chamada falhou; tentando novamente. - Nada foi solicitado a ele ainda. - Ocultar os que pararam de se anunciar - Perguntar quem é - Chave do coordenador - Um nome para ele (seu, opcional) - Este nome é seu e fica neste dispositivo. Um coordenador não pode comprovar um nome, então nunca é solicitado um. - Mais ações - Nenhum coordenador ainda. - Expurgar - Remover mantém os grupos neste dispositivo, então adicionar o coordenador de volta os traz junto. Expurgar os exclui: as chaves dos grupos, as posições de leitura e seus pacotes de chaves são apagados, e esses grupos só podem ser reabertos com um novo convite.\n\nO coordenador não é avisado e mantém tudo o que já tinha. - Expurgar este coordenador? - %1$s - Responde em - %1$s +%2$d mais - Remover - Renomear - Diz ser: %1$s - O nome e a versão de um coordenador são textos escolhidos por ele. A chave pública é a única coisa que o identifica. - Respondeu, mas não informou nenhum nome. - Mostrar todos os %1$d - Mostrar menos - - Mostrar %1$d que parou de se anunciar - Mostrar %1$d que pararam de se anunciar - - Estes não se anunciam há mais de um mês. Criar um grupo em um que tenha sumido vai falhar. - Criar grupo - - Criar e convidar %1$d pessoa - Criar e convidar %1$d pessoas - - Adicionar alguém - Admin - - %1$d pessoa pode adicionar e remover - %1$d pessoas podem adicionar e remover - - Só eu gerencio este grupo - Só você pode convidar, remover membros ou alterar os detalhes do grupo. Você pode passar isso para outra pessoa depois. - Coordenador - Alterar - Encontrar coordenadores - Concluído - Outro coordenador - Chave pública do coordenador (hex ou npub) - Relays em que ele responde, um por linha - - Alcança a %1$d pessoa escolhida - Alcança todas as %1$d pessoas escolhidas - - Alcança %1$d de %2$d — as demais ficariam de fora - Não verificado — escolhê-lo fará a consulta - Descrição (opcional) - Todos podem adicionar e remover - Todos no grupo podem convidar, remover membros e alterar os detalhes. Qualquer membro pode nomear admins depois, o que encerra esse modo. - Um grupo cordn é ordenado por um coordenador que você escolhe. A criação do grupo acontece neste dispositivo — o coordenador só fica sabendo que o grupo existe quando você convida alguém ou envia uma mensagem. - Não foi possível criar o grupo. - Nome do grupo - Nenhum coordenador escolhido - Cada convite é um Welcome deixado no coordenador, então eles podem falhar um a um. Ninguém foi notificado: as pessoas verão o convite na próxima vez que abrirem o app. - O coordenador recusou o convite - Enviar um link - Adicionado ao grupo, mas o convite não chegou — tente novamente pela tela do grupo - Abrir o chat - Sem chave aqui, então nenhum Welcome pôde ser deixado - O grupo foi criado - Aguardando a pessoa - Concluído - Ninguém adicionado ainda. Um grupo pode ser criado vazio e os convites enviados depois, mas indicar as pessoas agora é o que mostra qual coordenador consegue alcançá-las. - Ninguém ainda — toque para adicionar pessoas - Indicar as pessoas agora é o que nos diz qual coordenador consegue alcançá-las. Cada uma recebe um Welcome deixado nesse coordenador, que ela recebe na próxima vez que abrir o app. - - Alcançável em %1$d coordenador - Alcançável em %1$d coordenadores - - Sem chave em nenhum coordenador que você usa - Ainda não verificado - Quem pode adicionar e remover - Dê um nome - Quem participa - Onde ele fica - Novo grupo Cordn - - %1$d pessoa não pode ser alcançada por nenhum coordenador que você usa - %1$d pessoas não podem ser alcançadas por nenhum coordenador que você usa - - Você, sempre admin - Todos no grupo verão esta mensagem como retirada, e o Amethyst deixará de mostrar o texto. O coordenador ainda guarda a cópia criptografada que foi enviada, e quem já a leu, já leu. - Retirar mensagem - Aceita pelo coordenador - Entendi - O que este coordenador pode ver - Informações do grupo - Este grupo não está disponível. O coordenador dele pode ter sido removido. - Adicione um coordenador nas Configurações para começar a usar grupos cordn. - - %1$d grupo - %1$d grupos - - Nada está chegando destes grupos. O Amethyst continua tentando. - Coordenadores - Nenhum grupo cordn ainda - Um grupo cordn fica em um coordenador que guarda a lista de membros e repassa as mensagens, sem nunca ver o que alguém escreve. - Criar um - Adicionar alguém - Ninguém encontrado com esse nome. - Só é possível adicionar quem publicou um pacote de chaves neste coordenador. - Nome, npub ou nome@domínio - Admin - - O %1$d admin do grupo continua como está. - Os %1$d admins do grupo continuam como estão. - - Coordenador - Chave do coordenador - Link do coordenador - Copiar o nprofile do coordenador - Editar detalhes - Este grupo não tem admins: todos podem adicionar, remover e renomear, permanentemente. - Época - ID do grupo - Pode ser adicionado neste coordenador - Membros - %1$s deixa de receber as mensagens daqui. A pessoa mantém o que já leu, e isso não pode ser desfeito — ela teria de ser convidada novamente. - Remover do grupo? - Remover - Salvar - Detalhes técnicos - Entrar - Recusar - Recusar é permanente. Para voltar, será preciso ser convidado novamente. - Verificado quando você abre esta tela, e em nenhum outro momento — perguntar a um coordenador se alguém convidou você revela a ele que você está aqui, por isso o Amethyst não faz isso em segundo plano. - Não foi possível ler os convites. - - %1$d pessoa já está neste grupo - %1$d pessoas já estão neste grupo - - +%1$d mais - Você não tem nenhum coordenador aberto. Os convites do cordn chegam por um coordenador, então ainda não há onde procurar. - Nenhum convite pendente. - Verificar novamente - Um convite foi deixado para outro dispositivo - Convites Cordn - %1$s não respondeu - Por %1$s - Publicar assina um registro com a chave da sua própria conta. Esse coordenador passa a saber que esta conta existe e pode ser convidada — permanentemente, e quer alguém convide você ou não. - Outras pessoas só podem adicionar você a um grupo cordn usando um pacote de chaves que você publicou nesse coordenador. O cordn não tem outro lugar para guardá-lo, então sem nenhum publicado ninguém pode convidar você — e nada diz a elas o porquê. - Não foi possível ler os pacotes de chaves. - Nenhum pacote de último recurso. Quando os de uso único acabarem, os convites falharão. - Um pacote de último recurso está publicado, então ainda é possível convidar você quando os de uso único acabarem. - - %1$d pacote que este dispositivo não consegue abrir - %1$d pacotes que este dispositivo não consegue abrir - - O coordenador entregará estes pacotes a quem for convidar você, mas a metade privada não está neste dispositivo — ela pertence a outra instalação. Se essa instalação não existir mais, um convite que use um deles gera um Welcome que ninguém jamais conseguirá abrir. - Publicar um - Publicar de último recurso - - %1$d pacote de uso único disponível. - %1$d pacotes de uso único disponíveis. - - Depois que você publicar aqui, o Amethyst repõe discretamente os pacotes conforme forem usados. Ele nunca publica o primeiro por você. - Retirar todos - Retirar esses - Pedir para entrar neste grupo - Isto publica um pacote de chaves com a chave da sua própria conta e informa ao coordenador que você quer entrar neste grupo — quer alguém responda ou não. - Não foi possível pedir para entrar. - Pedido enviado. Um membro do grupo precisa adicionar você; o convite aparecerá em Convites Cordn. - Escanear - Não foi possível abrir esse arquivo. - Nenhum servidor de mídia está definido para esta conta, então não há onde colocar o arquivo. Escolha um nas Configurações. - Não foi possível ler esse arquivo. - Não foi possível enviar esse arquivo. - - %1$d membro - %1$d membros - - Mensagem excluída - Coordenador - Cursor - Enviada - Detalhes da mensagem - editada - Fixada por %1$s - - %1$d mensagem fixada neste grupo - %1$d mensagens fixadas neste grupo - - Próxima mensagem fixada - Mensagem fixada anterior - Mostrar todas as mensagens fixadas - Mensagens fixadas - Reações - Adicionar - Só um admin pode aceitar. Em um grupo sem admins, isso vale para todos. - Verificar pedidos - Dispensar - Não foi possível ler os pedidos. - Ninguém está esperando para entrar. - Pedidos para entrar - Enviar - Não foi enviada. - O coordenador deste grupo não está aberto, então ainda não é possível enviar nada. - Copiado - Copiar link - Qualquer pessoa com este link pode pedir para entrar. Um admin ainda precisa aceitá-la. - Compartilhar este grupo - Reproduzir mensagem de voz - Gravar uma mensagem de voz - Parar e enviar - O Amethyst precisa da câmera para ler códigos QR. Nada é gravado nem enviado — os quadros são decodificados no dispositivo e descartados. - Permitir câmera - O acesso à câmera está desativado para o Amethyst. Você pode reativá-lo nas configurações do Android. - Abrir configurações - O leitor de QR não pôde ser iniciado neste dispositivo. Você ainda pode colar o código como texto. - Acender a lanterna - Apagar a lanterna - Está escuro — experimente a lanterna - Ler de uma imagem - Colar - Nenhum código QR encontrado nessa imagem - Nada na área de transferência para ler - Toque no código que você quer - - %1$s de %2$s parte capturada — continue - %1$s de %2$s partes capturadas — continue - - Redefinir zoom - O Amethyst não consegue abrir este código - Este código contém uma chave privada ou um segredo de pareamento, por isso o conteúdo não é exibido aqui. Use a tela que espera por ele. - Isto é uma chave privada. Cole-a na tela de login. - Isto é uma conexão de carteira. Adicione-a nas configurações da Carteira. - Isto é um endereço de assinador remoto. O Amethyst fornece esses endereços para que outros apps possam pedir que ele assine — não é possível entrar com um deles. - Isto é um app pedindo para se conectar ao seu assinador. Abra-o pela tela do assinador NIP-46. - Isto é uma fatura Lightning. - Isto é um token Cashu. - Isto é um link da web. - Isto é texto simples, não um código Nostr. - Isto é um código Nostr, mas não um que esta tela consiga abrir. - Perfil - Nota - Código Nostr - Relay - Chave privada - Conexão de carteira - Endereço de assinador remoto - Pedido de conexão ao assinador - Fatura Lightning - Token Cashu - Link da web - Texto simples - Abrir link - Copiar - Copiado - Ler novamente - Use os treinos que você já registrou - Conecte o Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) para montar a Minha Forma Física, seu próprio resumo de treinos, e para preencher este treino a partir de um que seu relógio já registrou, em vez de digitar cada número à mão. - O Amethyst usa o Health Connect para montar a Minha Forma Física — seu próprio resumo de treinos, no seu celular. - A Minha Forma Física (menu → Você → Minha Forma Física) mostra como você tem treinado: quanto fez nesta semana em comparação com a anterior, como seu tempo se divide entre corrida, ciclismo, caminhada, natação e academia, seus melhores desempenhos, quantos dias você treinou e sua sequência atual. O Amethyst monta esse panorama a partir dos treinos que seu relógio ou app de fitness já salvou no Health Connect, junto com os treinos que você registrou no próprio Amethyst. Tudo é calculado no seu celular e mostrado a você. - Exercício · qual atividade você fez, quando começou e quanto durou. É o treino em si: define sua contagem de treinos, seu tempo de treino, a divisão por atividade, seus dias ativos e sua sequência. - Distância · até onde você foi, mostrada como sua distância nesta semana em comparação com a anterior, sua média semanal, sua distância por atividade e sua maior distância. - Calorias ativas e totais · a energia gasta em um treino, mostrada como sua energia nesta semana e como ela se compara com a semana anterior. As calorias ativas são usadas quando sua fonte as registra; as calorias totais são a alternativa para fontes que só registram a energia total. - Frequência cardíaca · a média e o máximo de cada treino, combinados na sua frequência cardíaca média e mais alta do período. É o que diferencia uma semana leve de uma pesada com a mesma distância. - Passos · a contagem de passos de uma corrida, caminhada ou trilha, mostrada como sua média semanal de passos e sua maior contagem de passos. - Ganho de elevação · quanto você subiu, mostrado como sua média semanal de subida e sua maior subida. É o que separa uma semana plana de uma com morros. - Só lê treinos das últimas 4 semanas, e só enquanto a Minha Forma Física ou o editor de treinos estiver aberto. Nunca lê em segundo plano. - Nunca envia seus dados de saúde para lugar nenhum. A Minha Forma Física é calculada no seu celular e fica nele. Se depois você quiser publicar um treino, toque em Compartilhar nesse treino, leia a publicação que o Amethyst preencheu e publique você mesmo — um treino por vez, nunca automaticamente. - O recurso inteiro é opcional. Revogue as permissões no Health Connect a qualquer momento — a Minha Forma Física volta a exibir o convite para conectar — ou desative as sugestões do editor em Configurações → Configurações de composição. O resto do Amethyst continua funcionando. - Minha Forma Física - Últimas 4 semanas - Carregando distância, calorias e frequência cardíaca… - As últimas quatro semanas completas do seu treino: os treinos que você publicou, mais tudo o que o Health Connect registra neste celular. O Amethyst calcula o resumo no dispositivo e não publica nada dele. - Esta semana - vs. semana passada - Média semanal - Treinos - Dias ativos - Dias seguidos - Tempo - %1$d h %2$d min - %1$d min - %1$d s - Distância - Calorias - Passos - Subida - FC média - FC máx. - Por atividade - Melhores desempenhos - Maior distância - Treino mais longo - Maior subida - Mais passos - Maior frequência cardíaca - Treinos recentes - Nada registrado nas últimas 4 semanas. Os treinos que você publicar, e os que seu relógio registrar, aparecem aqui. - Acompanhe seu treino - Conecte o Health Connect para ver seus totais semanais, a tendência semana a semana, seus melhores desempenhos e sua sequência de treinos. Tudo fica no seu celular. - Conecte o Health Connect para adicionar os treinos que seu relógio registra, com frequência cardíaca, passos e subida. - Conectar - Compartilhar este treino - bpm - kcal - km - mi - m - pés - Carregando - Aguardando suas mensagens privadas carregarem… - %1$02d:%2$02d - %1$d:%2$02d:%3$02d - Grupo atualizado - Anexo - Mensagem - %1$s: %2$s - Criar um grupo - Os convites do Marmot estão indo para outro dispositivo e não podem ser abertos aqui. - Dispensar - Publicar a partir deste dispositivo - Abrir nota que recebeu o zap - Legendas - Ativar ou desativar as legendas (oculto quando o vídeo não tem nenhuma) - Não enviada - Tentar novamente - Enviando… - Ainda enviando… - Esta conta foi removida do Amethyst. - Desativadas - Desativar legendas - Ativar legendas - Legendas - Lista de apps - Lista de artigos - Conjunto de favoritos - Feeds favoritos - Autores Git - Repositórios Git - Autores de wiki confiáveis - Relays de wiki confiáveis - Interesses - Seguidos de mídia - Pacote inicial de mídia - Silenciado para o kind %1$s - Lista de imagens - Grupos - Colaboração em vídeo - Lista de vídeos - Legendas de vídeo - O Amethyst para Android não consegue entrar com um endereço bunker://. Ele fornece esses endereços para que outros apps possam pedir que ele assine, mas não é possível entrar com um deles. Use um app assinador externo ou o Amethyst Desktop. - Isso é um app pedindo para se conectar ao seu assinador, não uma forma de entrar. Entre primeiro e depois abra-o pela tela do assinador NIP-46. - Minha Forma Física - Esta lista tem membros privados que só o autor pode ver - Editar cache - Editar caçada - Cache - Caçada - Caçadas de caches - Geocaches - Registrar um achado - Esconder um cache - Nova caçada - Aceitou um crédito de colaboração - Recusou um crédito de colaboração - Crédito aceito - Áudio de - Participação de - Inspirado em - Referências - Lista privada - - %1$d vídeo - %1$d vídeos - - Este dispositivo agora recebe seus convites do Marmot. - Falha ao publicar o KeyPackage: %1$s - Nenhum relay aceitou o KeyPackage. Verifique suas configurações de relays e tente novamente. - Enviando… - Não foi possível enviar. Toque para tentar novamente. - - %1$d nova mensagem - %1$d novas mensagens - - Grupos Cordn - Grupos com criptografia de ponta a ponta, ordenados por um coordenador que você escolhe, em vez de por relays. - Grupo Cordn - Chat em grupo criptografado, ordenado por um coordenador que você escolhe. - Coordenado - Ideal para equipes que querem uma ordem confiável única para a conversa. - Criar grupo Cordn - Criptografia de ponta a ponta com MLS — o coordenador nunca consegue ler uma mensagem. - Uma única ordem acordada para todos, então o histórico não pode ser embaralhado por um relógio errado. - O coordenador fica sabendo quem está no grupo e quando vocês conversam, mesmo sem conseguir ler o que é dito. Objeto 3D %1$d vértices · %2$d faces Não foi possível ler este objeto 3D (regra %1$s) @@ -5662,6 +5397,7 @@ %1$d byte de algo que este cliente não lê %1$d bytes de algo que este cliente não lê + Fechar Copiar link Localizar @@ -6092,4 +5828,268 @@ Os serviços em que você confia para classificar e verificar pessoas para você. lista de relays confiáveis Relays em que você confia para se conectar sem perguntar. + Excluir + Editar + Editando sua mensagem + Ir para a mensagem + Fixar + Responder + Desafixar + Não foi possível fazer essa alteração. + O coordenador não respondeu, então nada foi alterado. Tente novamente em instantes. + %1$s não publicou um pacote de chaves neste coordenador, então ainda não pode ser adicionado. Peça que abra o cordn neste coordenador primeiro. + Você não pode remover a si mesmo de um grupo cordn. + Não foi possível dar a %1$s um meio de entrar no grupo, então a adição foi cancelada. + %1$s não está neste grupo. + Essa pessoa + Este coordenador forneceu um pacote de chaves que pertence a outra pessoa, então %1$s não foi adicionado. Nada foi alterado. + As chaves de criptografia dos seus grupos, seus pacotes de chaves e todas as mensagens desses grupos. Qualquer pessoa que abrir o arquivo pode ler as conversas. A senha é a única coisa que o protege depois que ele sai do Amethyst. + O que há no arquivo + Sua nsec não recupera grupos cordn. Um grupo cordn existe como estado de criptografia neste dispositivo e como um fluxo ordenado em um coordenador que não consegue lê-lo — perca o dispositivo sem um backup e o grupo se vai, incluindo tudo o que já foi dito nele. + Exportar + Backup salvo. + Não funcionou. Verifique a senha e o arquivo. + Senha + Restaurar + Restaurar substitui os grupos cordn deste dispositivo pelos do arquivo. Serve para um celular que substituiu outro — não é uma forma de usar os mesmos grupos em dois dispositivos ao mesmo tempo. + Todos os grupos cordn atualmente neste dispositivo serão removidos e substituídos pelos do arquivo.\n\nNão restaure em um segundo celular enquanto o primeiro ainda estiver em uso. Ambos teriam as mesmas chaves de grupo e, assim que os dois enviarem uma mensagem, o grupo quebra para todos — silenciosamente e sem volta. + Substituir os grupos cordn deste dispositivo? + Restaurar + Restaurado. + Tudo o que for enviado aqui é criptografado para este grupo. O coordenador repassa as mensagens sem conseguir lê-las. + Nenhuma mensagem ainda + Novas mensagens + Ontem + Mensagem + Adicionar + Não foi possível abrir esse coordenador. + Copiar a npub do coordenador + Procurar coordenadores + Adicionar + Lê anúncios que seus relays já transportam. Nenhum coordenador é contatado, então nenhum deles fica sabendo que você procurou. + Não foi possível ler os anúncios + Ninguém está se anunciando nos seus relays. + Anunciado há %1$s + Último anúncio: %1$s + + Nada encontrado, e %1$d dos seus relays não respondeu. + Nada encontrado, e %1$d dos seus relays não responderam. + + Um coordenador ordena as mensagens de um grupo e é o único que pode fazer isso. Perdê-lo significa perder a ordenação, e um segundo coordenador não espelha o primeiro — por isso são escolhas nomeadas por você, não relays intercambiáveis. + + Sem resposta — %1$d chamada seguida falhou. Os grupos nele não estão sincronizando. + Sem resposta — %1$d chamadas seguidas falharam. Os grupos nele não estão sincronizando. + + Respondendo. + Uma chamada falhou; tentando novamente. + Nada foi solicitado a ele ainda. + Ocultar os que pararam de se anunciar + Perguntar quem é + Chave do coordenador + Um nome para ele (seu, opcional) + Este nome é seu e fica neste dispositivo. Um coordenador não pode comprovar um nome, então nunca é solicitado um. + Mais ações + Nenhum coordenador ainda. + Expurgar + Remover mantém os grupos neste dispositivo, então adicionar o coordenador de volta os traz junto. Expurgar os exclui: as chaves dos grupos, as posições de leitura e seus pacotes de chaves são apagados, e esses grupos só podem ser reabertos com um novo convite.\n\nO coordenador não é avisado e mantém tudo o que já tinha. + Expurgar este coordenador? + %1$s + Responde em + %1$s +%2$d mais + Remover + Renomear + Diz ser: %1$s + O nome e a versão de um coordenador são textos escolhidos por ele. A chave pública é a única coisa que o identifica. + Respondeu, mas não informou nenhum nome. + Mostrar todos os %1$d + Mostrar menos + + Mostrar %1$d que parou de se anunciar + Mostrar %1$d que pararam de se anunciar + + Estes não se anunciam há mais de um mês. Criar um grupo em um que tenha sumido vai falhar. + Criar grupo + + Criar e convidar %1$d pessoa + Criar e convidar %1$d pessoas + + Adicionar alguém + Admin + + %1$d pessoa pode adicionar e remover + %1$d pessoas podem adicionar e remover + + Só eu gerencio este grupo + Só você pode convidar, remover membros ou alterar os detalhes do grupo. Você pode passar isso para outra pessoa depois. + Coordenador + Alterar + Encontrar coordenadores + Concluído + Outro coordenador + Chave pública do coordenador (hex ou npub) + Relays em que ele responde, um por linha + + Alcança a %1$d pessoa escolhida + Alcança todas as %1$d pessoas escolhidas + + Alcança %1$d de %2$d — as demais ficariam de fora + Não verificado — escolhê-lo fará a consulta + Descrição (opcional) + Todos podem adicionar e remover + Todos no grupo podem convidar, remover membros e alterar os detalhes. Qualquer membro pode nomear admins depois, o que encerra esse modo. + Um grupo cordn é ordenado por um coordenador que você escolhe. A criação do grupo acontece neste dispositivo — o coordenador só fica sabendo que o grupo existe quando você convida alguém ou envia uma mensagem. + Não foi possível criar o grupo. + Nome do grupo + Nenhum coordenador escolhido + Cada convite é um Welcome deixado no coordenador, então eles podem falhar um a um. Ninguém foi notificado: as pessoas verão o convite na próxima vez que abrirem o app. + O coordenador recusou o convite + Enviar um link + Adicionado ao grupo, mas o convite não chegou — tente novamente pela tela do grupo + Abrir o chat + Sem chave aqui, então nenhum Welcome pôde ser deixado + O grupo foi criado + Aguardando a pessoa + Concluído + Ninguém adicionado ainda. Um grupo pode ser criado vazio e os convites enviados depois, mas indicar as pessoas agora é o que mostra qual coordenador consegue alcançá-las. + Ninguém ainda — toque para adicionar pessoas + Indicar as pessoas agora é o que nos diz qual coordenador consegue alcançá-las. Cada uma recebe um Welcome deixado nesse coordenador, que ela recebe na próxima vez que abrir o app. + + Alcançável em %1$d coordenador + Alcançável em %1$d coordenadores + + Sem chave em nenhum coordenador que você usa + Ainda não verificado + Quem pode adicionar e remover + Dê um nome + Quem participa + Onde ele fica + Novo grupo Cordn + + %1$d pessoa não pode ser alcançada por nenhum coordenador que você usa + %1$d pessoas não podem ser alcançadas por nenhum coordenador que você usa + + Você, sempre admin + Todos no grupo verão esta mensagem como retirada, e o Amethyst deixará de mostrar o texto. O coordenador ainda guarda a cópia criptografada que foi enviada, e quem já a leu, já leu. + Retirar mensagem + Aceita pelo coordenador + Entendi + O que este coordenador pode ver + Informações do grupo + Este grupo não está disponível. O coordenador dele pode ter sido removido. + Adicione um coordenador nas Configurações para começar a usar grupos cordn. + + %1$d grupo + %1$d grupos + + Nada está chegando destes grupos. O Amethyst continua tentando. + Coordenadores + Nenhum grupo cordn ainda + Um grupo cordn fica em um coordenador que guarda a lista de membros e repassa as mensagens, sem nunca ver o que alguém escreve. + Criar um + Adicionar alguém + Ninguém encontrado com esse nome. + Só é possível adicionar quem publicou um pacote de chaves neste coordenador. + Nome, npub ou nome@domínio + Admin + + O %1$d admin do grupo continua como está. + Os %1$d admins do grupo continuam como estão. + + Coordenador + Chave do coordenador + Link do coordenador + Copiar o nprofile do coordenador + Editar detalhes + Este grupo não tem admins: todos podem adicionar, remover e renomear, permanentemente. + Época + ID do grupo + Pode ser adicionado neste coordenador + Membros + %1$s deixa de receber as mensagens daqui. A pessoa mantém o que já leu, e isso não pode ser desfeito — ela teria de ser convidada novamente. + Remover do grupo? + Remover + Salvar + Detalhes técnicos + Entrar + Recusar + Recusar é permanente. Para voltar, será preciso ser convidado novamente. + Verificado quando você abre esta tela, e em nenhum outro momento — perguntar a um coordenador se alguém convidou você revela a ele que você está aqui, por isso o Amethyst não faz isso em segundo plano. + Não foi possível ler os convites. + + %1$d pessoa já está neste grupo + %1$d pessoas já estão neste grupo + + +%1$d mais + Você não tem nenhum coordenador aberto. Os convites do cordn chegam por um coordenador, então ainda não há onde procurar. + Nenhum convite pendente. + Verificar novamente + Um convite foi deixado para outro dispositivo + Convites Cordn + %1$s não respondeu + Por %1$s + Publicar assina um registro com a chave da sua própria conta. Esse coordenador passa a saber que esta conta existe e pode ser convidada — permanentemente, e quer alguém convide você ou não. + Outras pessoas só podem adicionar você a um grupo cordn usando um pacote de chaves que você publicou nesse coordenador. O cordn não tem outro lugar para guardá-lo, então sem nenhum publicado ninguém pode convidar você — e nada diz a elas o porquê. + Não foi possível ler os pacotes de chaves. + Nenhum pacote de último recurso. Quando os de uso único acabarem, os convites falharão. + Um pacote de último recurso está publicado, então ainda é possível convidar você quando os de uso único acabarem. + + %1$d pacote que este dispositivo não consegue abrir + %1$d pacotes que este dispositivo não consegue abrir + + O coordenador entregará estes pacotes a quem for convidar você, mas a metade privada não está neste dispositivo — ela pertence a outra instalação. Se essa instalação não existir mais, um convite que use um deles gera um Welcome que ninguém jamais conseguirá abrir. + Publicar um + Publicar de último recurso + + %1$d pacote de uso único disponível. + %1$d pacotes de uso único disponíveis. + + Depois que você publicar aqui, o Amethyst repõe discretamente os pacotes conforme forem usados. Ele nunca publica o primeiro por você. + Retirar todos + Retirar esses + Pedir para entrar neste grupo + Isto publica um pacote de chaves com a chave da sua própria conta e informa ao coordenador que você quer entrar neste grupo — quer alguém responda ou não. + Não foi possível pedir para entrar. + Pedido enviado. Um membro do grupo precisa adicionar você; o convite aparecerá em Convites Cordn. + Escanear + Não foi possível abrir esse arquivo. + Nenhum servidor de mídia está definido para esta conta, então não há onde colocar o arquivo. Escolha um nas Configurações. + Não foi possível ler esse arquivo. + Não foi possível enviar esse arquivo. + + %1$d membro + %1$d membros + + Mensagem excluída + Coordenador + Cursor + Enviada + Detalhes da mensagem + editada + Fixada por %1$s + + %1$d mensagem fixada neste grupo + %1$d mensagens fixadas neste grupo + + Próxima mensagem fixada + Mensagem fixada anterior + Mostrar todas as mensagens fixadas + Mensagens fixadas + Reações + Adicionar + Só um admin pode aceitar. Em um grupo sem admins, isso vale para todos. + Verificar pedidos + Dispensar + Não foi possível ler os pedidos. + Ninguém está esperando para entrar. + Pedidos para entrar + Enviar + Não foi enviada. + O coordenador deste grupo não está aberto, então ainda não é possível enviar nada. + Copiado + Copiar link + Qualquer pessoa com este link pode pedir para entrar. Um admin ainda precisa aceitá-la. + Compartilhar este grupo + Reproduzir mensagem de voz + Gravar uma mensagem de voz + Parar e enviar diff --git a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml index 6a44e20832..c63d123853 100644 --- a/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/commonsUI/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -166,6 +166,152 @@ Trafikstockning Väghändelse + Geocache + Traditionell + Multicache + Mystery + Mikro + Liten + Normal + Stor + Annan storlek + D%1$d + T%1$d + Först att hitta (FTF) + Konstverk + Key Quest + Verifierade fynd + Tagen + Arkiverad + Kört fast? Tryck för att visa ledtråden + Går den inte att läsa? Tryck för den andra versionen + I närheten + Hittad! + Verifierat fynd + Beviset stämmer inte + Läser in cache… + Gömd av %1$s + Navigera + Kopiera koordinater + Koordinater kopierade + Foton + Key Quest-uppdrag + Loggar + Ingen har loggat den här cachen ännu. Bli först. + Jag hittade den + Hittade den inte + Lägg till en anteckning + Rapportera behov av underhåll + Lägg till i en jakt + Ny jakt… + Du har inte skapat någon jakt ännu. + Redan med i den här jakten + + %1$d cache här + %1$d cacher här + + Ta bort det här fotot + Den här cachen har arkiverats. Den är inte längre i spel. + FTF tagen av %1$s + FTF är fortfarande ledig + Ägare + Redigera den här cachen + Arkivera den här cachen + Arkivera den. Hittare ser att den är ur spel, och beskrivningen går fortfarande att läsa. + Lås den första hittaren + Publicerar en F-tagg som anger %1$s. Detta är permanent. + Inga verifierade fynd ännu, så det finns ingen att låsa. + Visa verifieringskoden + Skriv ut den och lägg den i cachen. Amethyst sparar den inte — om du tappar bort den kan hittare inte längre bevisa att de var där. + Hittade inte (DNF) + Anteckning + Behöver underhåll + Logga ett fynd + Cache + Din logg + Hur gick det? + Lägg till ett foto + Ditt Key Quest-svar + Bevis på närvaro + Skanna koden i cachen + Skanna en annan kod + Verifierat — du var vid cachen + Signeras en gång med koden i lådan. Nyckeln sparas aldrig och lämnar aldrig den här skärmen. + Det där är ingen verifieringskod. + Den koden hör till en annan cache. + Den här cachen kräver inget bevis, så du kan logga den direkt. + Din egen plats bifogas inte till den här loggen. + Publicera + Lägg till en logg + I närheten + Karta + Jakter + Fynd + Mina + Ej hittade + D ≤ %1$d + T ≤ %1$d + Har ett pris + Bevis krävs + Använd min plats + Sorterat efter avstånd + Nyaste först + Göm en cache + Inga cacher här ännu. Prova en bredare följlista, eller göm den första. + Du har inte loggat något fynd ännu. + Du har inte gömt någon cache ännu. + Inga jakter ännu. + Fynd + FTF + Verifierade + Gömda + Centrera på mig + Tryck länge på kartan för att gömma en cache där + Öppna + Var + Vad + Extra + Bevis + Reläer + Detta publicerar en offentlig plats med cirka 5 meters noggrannhet. Alla på Nostr kan se den, och en publicerad händelse kan inte återkallas. + Välj platsen + Publicerar %1$s, plus grövre celler ned till 3 tecken för närhetssökning. + Namn + Beskrivning + Typ + Storlek + Svårighetsgrad + Terräng + Ledtråd + Hittare kommer att se + Kryptera ledtråden (ROT13) + Key Quest-uppdrag + Tillägg + Kräv bevis på närvaro + En verifieringsnyckel har skapats för den här cachen. + Var hittare ska logga + Publicera + Ge cachen ett namn. + Välj var cachen är gömd. + %1$d av %2$d hittade + Starta jakten + + %1$d cache + %1$d cacher + + Titel + Beskrivning + Bannerbild + Cacher, i ordning + Lägg till en cache + Flytta upp + Flytta ned + Ta bort + Sidtema + Kartstil + Ge jakten en titel. + Lägg till minst en cache. + Kunde inte publicera. Kontrollera reläanslutningen och försök igen. Zaps till detta fördelas mellan: %1$d%% Värde -för-värde @@ -188,6 +334,51 @@ Profil Banner Peka mot QR-koden + Amethyst behöver kameran för att skanna QR-koder. Inget spelas in eller laddas upp — bilderna avkodas på enheten och kastas sedan. + Tillåt kamera + Kameraåtkomst är avstängd för Amethyst. Du kan slå på den igen i Androids inställningar. + Öppna inställningar + QR-avkodaren kunde inte starta på den här enheten. Du kan fortfarande klistra in koden som text. + Tänd lampan + Släck lampan + Det är mörkt — prova lampan + Skanna en bild + Klistra in + Ingen QR-kod hittades i bilden + Det finns inget i urklipp att skanna + Tryck på koden du vill ha + + Fångade %1$s av %2$s del — fortsätt + Fångade %1$s av %2$s delar — fortsätt + + Återställ zoom + Amethyst kan inte öppna den här koden + Den här koden innehåller en privat nyckel eller en parningshemlighet, så innehållet visas inte här. Använd skärmen som förväntar sig den. + Det här är en privat nyckel. Klistra in den på inloggningsskärmen i stället. + Det här är en plånboksanslutning. Lägg till den under Plånboksinställningar. + Det här är en fjärrsignatäradress. Amethyst delar ut sådana så att andra appar kan be den signera — den kan inte logga in med en. + Det här är en app som vill ansluta till din signatär. Öppna den från NIP-46-signatärskärmen. + Det här är en Lightning-faktura. + Det här är en Cashu-token. + Det här är en webblänk. + Det här är vanlig text, inte en Nostr-kod. + Det här är en Nostr-kod, men inte en som den här skärmen kan öppna. + Profil + Anteckning + Nostr-kod + Relä + Privat nyckel + Plånboksanslutning + Fjärrsignatäradress + Anslutningsbegäran till signatär + Lightning-faktura + Cashu-token + Webblänk + Vanlig text + Öppna länk + Kopiera + Kopierat + Skanna igen Visa QR Profilbild Din profilbild @@ -638,17 +829,70 @@ Timmar Minuter Sekunder + Använd träningspassen du redan har registrerat + Anslut Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) för att bygga Min träning, din egen träningssammanfattning, och för att fylla i det här passet från ett som din klocka redan har registrerat i stället för att skriva in varje siffra för hand. Anslut %1$s km Från Health Connect Vad Amethyst läser Health Connect och Amethyst + Amethyst använder Health Connect för att bygga Min träning — din egen träningssammanfattning, på din telefon. + Min träning (meny → Du → Min träning) visar hur du har tränat: hur mycket du gjorde den här veckan jämfört med förra, hur din tid fördelas mellan löpning, cykling, promenader, simning och gym, dina bästa prestationer, hur många dagar du tränade och din nuvarande svit. Amethyst bygger den bilden från träningspassen som din klocka eller träningsapp redan har sparat i Health Connect, tillsammans med pass du har loggat i Amethyst. Allt räknas ut på din telefon och visas för dig. Vad Amethyst läser och varför + Träning · vilken aktivitet du utförde, när den började och hur länge den varade. Det här är själva passet: det styr antalet pass, din träningstid, uppdelningen per aktivitet, dina aktiva dagar och din svit. + Distans · hur långt du tog dig, visat som din distans den här veckan jämfört med förra, ditt veckosnitt, din distans per aktivitet och din längsta distans. + Aktiva och totala kalorier · energin ett pass förbrände, visat som din energi den här veckan och hur den står sig mot förra veckan. Aktiva kalorier används när din källa registrerar dem; totala kalorier används som reserv för källor som bara registrerar total energi. + Puls · genomsnitt och maxvärde för varje pass, sammanslaget till din genomsnittliga och högsta puls för perioden. Det är så du skiljer en lätt vecka från en tuff med samma distans. + Steg · antalet steg under en löptur, promenad eller vandring, visat som ditt veckosnitt för steg och ditt högsta antal steg. + Höjdmeter · hur mycket du klättrade, visat som ditt veckosnitt för stigning och din största stigning. Det är det som skiljer en platt vecka från en kuperad. Vad Amethyst inte gör + Läser bara träningspass från de senaste 4 veckorna, och bara medan Min träning eller passredigeraren är öppen. Läser aldrig i bakgrunden. Skriver aldrig något till Health Connect och begär aldrig din träningsrutt, plats eller någon annan typ av hälsodata. + Skickar aldrig dina hälsodata någonstans. Min träning räknas ut på din telefon och stannar där. Om du senare vill publicera ett pass trycker du på Dela för det passet, läser inlägget som Amethyst fyllt i och publicerar det själv — ett pass i taget, aldrig automatiskt. + Hela funktionen är valfri. Återkalla behörigheterna i Health Connect när som helst — Min träning går då tillbaka till sin uppmaning — eller stäng av förslagen i redigeraren under Inställningar → Skrivinställningar. Resten av Amethyst fortsätter att fungera. Läs hela integritetspolicyn + Min träning + Senaste 4 veckorna + Läser in distans, kalorier och puls… + De senaste fyra hela veckorna av din träning: passen du har publicerat, plus allt Health Connect registrerar på den här telefonen. Amethyst räknar ut sammanfattningen på enheten och publicerar ingenting av den. + Den här veckan + jämfört med förra veckan + Veckosnitt + Pass + Aktiva dagar + Dagar i rad + Tid + %1$d h %2$d min + %1$d min + %1$d s + Distans + Kalorier + Steg + Stigning + Snittpuls + Maxpuls + Per aktivitet + Bästa prestationer + Längsta distans + Längsta pass + Största stigning + Flest steg + Högsta puls + Senaste passen + Inget loggat de senaste 4 veckorna. Pass du publicerar, och de som din klocka registrerar, visas här. + Följ din träning + Anslut Health Connect för att se dina veckosummor, utvecklingen vecka för vecka, bästa prestationer och träningssvit. Allt stannar på din telefon. + Anslut Health Connect för att lägga till passen din klocka registrerar, med puls, steg och stigning. + Anslut + Dela det här passet + bpm + kcal + km + mi + m + ft Inga nSites hittades ännu. Ladda om Den inbyggda webbläsaren kräver Android 11 eller senare. @@ -1084,6 +1328,8 @@ Stängningsdatum och tid Omröstningen stänger om %1$s Letar efter Event %1$s + Läser in + Väntar på att dina privata meddelanden ska läsas in… Skicka Zap Lägg till ett offentligt meddelande Lägg till ett privat meddelande @@ -1223,6 +1469,8 @@ + %1$02d:%2$02d + %1$d:%2$02d:%3$02d Avvisa Byt kamera Röstsamtal @@ -1357,6 +1605,14 @@ Användarinställningar Sök inställningar Inga inställningar hittades för ”%1$s” + Gruppen uppdaterad + Bilaga + Meddelande + %1$s: %2$s + Skapa en grupp + Marmot-inbjudningar går till en annan enhet och kan inte öppnas här. + Avfärda + Publicera från den här enheten Återställa Marmot-tillstånd? Detta tar permanent bort alla Marmot-gruppchattar, meddelandehistorik och MLS-nycklar på den här enheten för det aktuella kontot. Deltagare meddelas inte och kan fortfarande se dig i grupper tills de bekräftar nästa gång. Detta går inte att ångra. Ett nytt KeyPackage publiceras nästa gång appen synkroniseras. Återställ @@ -1627,6 +1883,7 @@ Inga transaktioner matchar detta filter Laddar… Mottagen + Öppna zappad anteckning Skickad Uppdatera Alla @@ -1943,6 +2200,8 @@ Spela upp videon i ett flytande fönster (dolt om det inte stöds) Casta till enhet Skicka videon till en Chromecast-mottagare på ditt Wi-Fi (dolt för lokala filer) + Undertexter + Slå på eller av undertexter (dolt när videon saknar sådana) Välj animeringen som visas när ljudanteckningar spelas upp. Av Spektrumstaplar @@ -2630,6 +2889,7 @@ Du uppfyller inte den här gemenskapens krav på förtroendenät. Det senaste dokumentet med gemenskapsregler avvisades som föråldrat. Marmot-grupper + Cordn-grupper Skapa grupp Skapa Marmot-grupp En ny MLS-grupp kommer att skapas. Du kan lägga till medlemmar efteråt. @@ -2921,13 +3181,18 @@ Meddelar dig när någon svarar på ditt inlägg Svar Nya svar + Inte skickat Svara + Försök igen + Skickar… + Skickar fortfarande… Meddelar dig när någon repostar ditt inlägg %1$s repostade ditt inlägg Reposter Nya reposter Aviseringar när ett schemalagt inlägg publiceras eller misslyckas att publicera. Schemalagda inlägg + Det här kontot har tagits bort från Amethyst. Meddela dig när någon zappar dig %1$s sats till %1$s @@ -3128,6 +3393,10 @@ Ljud på Callback URL hittades inte från %1$s's svar Avbryt + Av + Stäng av undertexter + Slå på undertexter + Undertexter Cashu Token Kontrollerar minten… Kunde inte lösa in Cashu @@ -3605,10 +3874,12 @@ Äventyrsprolog Äventyrsläsning Äventyrsscen + Applista Apprekommendationer Möte Appar OSA för möte + Artikellista Ljudhuvud Ljudspår Skapade poddar @@ -3621,6 +3892,7 @@ Blossom-autentisering Blossom-servrar Bokmärkeslista + Bokmärkessamling Broadcast-relayer Kalender Kanaldefinition @@ -3658,6 +3930,7 @@ Emoji-paket Tillfällig chatt Tillfälliga chattrum + Favoritflöden Favoritpoddar Favoritrelayer Filhuvuden @@ -3666,28 +3939,36 @@ Följ-paket Geohash-följer Presentinslagningar + Git-författare Git-ärende Git-patch Pull Request PR-uppdatering Git-svar Git-repository + Git-förråd Git-status: Tillämpad Git-status: Stängd Git-status: Utkast Git-status: Öppen + Betrodda wikiförfattare + Betrodda wikireläer Följda hashtaggar Höjdpunkter HTTP-autentisering Indexrelälista + Intressen Livechattar Livesändningar + Mediaföljningar + Startpaket för media Medicinska data Mötesrum Mötesutrymme Musikspellista Musikspår Tystnadslista + Tystat för typ %1$s Namngivna bokmärken Nests-servrar NIP @@ -3702,6 +3983,7 @@ Outbox relayer Betala till Personlistor + Bildlista Bilder Nålar Poddavsnitt @@ -3728,6 +4010,7 @@ Sökreläer Shorts Shorts (svar) + Grupper Torrentkommentarer Torrenter Betrodda leverantörer @@ -3736,7 +4019,10 @@ Användarinställningar Användarstatus Video + Videosamarbete + Videolista Video (svar) + Videoundertexter Röstmeddelande Röstsvar Väckning @@ -3773,6 +4059,8 @@ Ändra plats på kartan Platskanaler Välj en plats på kartan + Amethyst för Android kan inte logga in med en bunker://-adress. Den delar ut sådana så att andra appar kan be den signera, men den kan inte logga in med en. Använd en extern signatärapp eller Amethyst Desktop. + Det där är en app som vill ansluta till din signatär, inte ett sätt att logga in. Logga in först och öppna den sedan från NIP-46-signatärskärmen. Logga in med en privat nyckel för att kunna boosta inlägg Logga in med en privat nyckel för att kunna följa Du använder en offentlig nyckel, och offentliga nycklar är endast läsbara. Logga in med en privat nyckel för att kunna dölja ett ord eller en mening @@ -3863,6 +4151,7 @@ Tysta aviseringar Ljud avstängt. Klicka för att ta bort ljudlöst Mina Blossom-filer + Min träning Mina listor Namn är obligatoriskt %1$dms @@ -4126,6 +4415,7 @@ Fjärrsignatärapp Fjärrsignatär + Den här listan har privata medlemmar som bara dess skapare kan se %1$d tillgång paketerad %1$d tillgångar buntade @@ -4656,12 +4946,21 @@ Kalendrar Schack Upptäck + Redigera cache + Redigera jakt + Cache + Jakt + Cachejakter + Geocacher Hem Importera följare + Logga ett fynd Media Meddelanden Spellistor Musik + Göm en cache + Ny jakt Aviseringar Avsnitt Poddar @@ -4851,6 +5150,18 @@ Begäran om försvinnande skickad Bättre kvalitet med mindre filstorlek, men inte alla enheter stöder H.265-uppspelning. Använd H.265/HEVC-codec + Accepterade ett samarbetsomnämnande + Avböjde ett samarbetsomnämnande + Omnämnande accepterat + Ljud från + Med + Inspirerad av + Referenser + Privat lista + + %1$d video + %1$d videor + Nedladdning av video har startat… Videospelarens knappar Videon sparad i telefonens videogalleri @@ -4910,165 +5221,18 @@ Mottagarens Lightning-tjänst på %1$s är inte tillgänglig. Den beräknades från Lightning Address "%2$s". Fel: %3$s. Kontrollera om servern är uppe och om Lightning Addressen är korrekt Det gick inte att skapa en blixtfaktura från %1$s: Elementet pr hittades inte i den resulterande JSON. Det gick inte att skapa en blixt faktura. Meddelande från %1$s: %2$s + Den här enheten tar nu emot dina Marmot-inbjudningar. + Det gick inte att publicera KeyPackage: %1$s + Inget relä accepterade KeyPackage. Kontrollera dina reläinställningar och försök igen. + Skickar… + Kunde inte skickas. Tryck för att försöka igen. + + %1$d nytt meddelande + %1$d nya meddelanden + - - - - - Geocache - Traditionell - Multicache - Mystery - Mikro - Liten - Normal - Stor - Annan storlek - D%1$d - T%1$d - Först att hitta (FTF) - Konstverk - Key Quest - Verifierade fynd - Tagen - Arkiverad - Kört fast? Tryck för att visa ledtråden - Går den inte att läsa? Tryck för den andra versionen - I närheten - Hittad! - Verifierat fynd - Beviset stämmer inte - Läser in cache… - Gömd av %1$s - Navigera - Kopiera koordinater - Koordinater kopierade - Foton - Key Quest-uppdrag - Loggar - Ingen har loggat den här cachen ännu. Bli först. - Jag hittade den - Hittade den inte - Lägg till en anteckning - Rapportera behov av underhåll - Lägg till i en jakt - Ny jakt… - Du har inte skapat någon jakt ännu. - Redan med i den här jakten - - %1$d cache här - %1$d cacher här - - Ta bort det här fotot - Den här cachen har arkiverats. Den är inte längre i spel. - FTF tagen av %1$s - FTF är fortfarande ledig - Ägare - Redigera den här cachen - Arkivera den här cachen - Arkivera den. Hittare ser att den är ur spel, och beskrivningen går fortfarande att läsa. - Lås den första hittaren - Publicerar en F-tagg som anger %1$s. Detta är permanent. - Inga verifierade fynd ännu, så det finns ingen att låsa. - Visa verifieringskoden - Skriv ut den och lägg den i cachen. Amethyst sparar den inte — om du tappar bort den kan hittare inte längre bevisa att de var där. - Hittade inte (DNF) - Anteckning - Behöver underhåll - Logga ett fynd - Cache - Din logg - Hur gick det? - Lägg till ett foto - Ditt Key Quest-svar - Bevis på närvaro - Skanna koden i cachen - Skanna en annan kod - Verifierat — du var vid cachen - Signeras en gång med koden i lådan. Nyckeln sparas aldrig och lämnar aldrig den här skärmen. - Det där är ingen verifieringskod. - Den koden hör till en annan cache. - Den här cachen kräver inget bevis, så du kan logga den direkt. - Din egen plats bifogas inte till den här loggen. - Publicera - Lägg till en logg - I närheten - Karta - Jakter - Fynd - Mina - Ej hittade - D ≤ %1$d - T ≤ %1$d - Har ett pris - Bevis krävs - Använd min plats - Sorterat efter avstånd - Nyaste först - Göm en cache - Inga cacher här ännu. Prova en bredare följlista, eller göm den första. - Du har inte loggat något fynd ännu. - Du har inte gömt någon cache ännu. - Inga jakter ännu. - Fynd - FTF - Verifierade - Gömda - Centrera på mig - Tryck länge på kartan för att gömma en cache där - Öppna - Var - Vad - Extra - Bevis - Reläer - Detta publicerar en offentlig plats med cirka 5 meters noggrannhet. Alla på Nostr kan se den, och en publicerad händelse kan inte återkallas. - Välj platsen - Publicerar %1$s, plus grövre celler ned till 3 tecken för närhetssökning. - Namn - Beskrivning - Typ - Storlek - Svårighetsgrad - Terräng - Ledtråd - Hittare kommer att se - Kryptera ledtråden (ROT13) - Key Quest-uppdrag - Tillägg - Kräv bevis på närvaro - En verifieringsnyckel har skapats för den här cachen. - Var hittare ska logga - Publicera - Ge cachen ett namn. - Välj var cachen är gömd. - %1$d av %2$d hittade - Starta jakten - - %1$d cache - %1$d cacher - - Titel - Beskrivning - Bannerbild - Cacher, i ordning - Lägg till en cache - Flytta upp - Flytta ned - Ta bort - Sidtema - Kartstil - Ge jakten en titel. - Lägg till minst en cache. - Kunde inte publicera. Kontrollera reläanslutningen och försök igen. - Cordn-grupper Koordinatorgrupp Levereras av en koordinator, inte av reläer Vad den här koordinatorn kan se @@ -5106,6 +5270,9 @@ Nås via Den här länken anger ingen koordinator, så den kan inte följas på egen hand. Fråga den som skickade den vilken koordinator som för vidare gruppen. Att granska en länk går inte med i något, och att ha en gör dig inte till medlem. + Cordn-grupplänk cordn koordinator grupp länk inbjudan mls chatt metadata integritet exponering coordinator group link invite Cordn-koordinatorer @@ -5114,6 +5281,10 @@ cordn nyckelpaket publicera inbjudan reserv koordinator mls key package last resort Cordn-säkerhetskopia cordn säkerhetskopia återställ exportera importera lösenfras grupper återställning backup restore + Cordn-grupper + End-to-end-krypterade grupper som ordnas av en koordinator du väljer, i stället för av reläer. + Grupp %1$s Inga meddelanden ännu Cordn-grupp, förs vidare av %1$s @@ -5122,8 +5293,19 @@ Foto Video Fil + Cordn-grupp + Krypterad gruppchatt som ordnas av en koordinator du väljer. + Koordinerad + Bäst för team som vill ha en pålitlig ordning i konversationen. + Skapa Cordn-grupp + End-to-end-krypterad med MLS — koordinatorn kan aldrig läsa ett meddelande. + En gemensam ordning för alla, så att historiken inte kan kastas om av en felaktig klocka. + Koordinatorn får veta vilka som är med i gruppen och när ni pratar, även om den inte kan läsa vad ni säger. Inbjuden till en cordn-grupp? Granska inbjudningar + Cordn Grupper som levereras av en koordinator du väljer, i stället för av reläer. Granska en grupplänk @@ -5173,453 +5355,6 @@ Ställ in en mediaserver först — de krypterade dokumenten behöver någonstans att ligga medan den andra telefonen hämtar dem. Vad som lämnar den här enheten Ditt grupptillstånd krypteras och laddas upp till din mediaserver så att den andra telefonen kan hämta det. Det är oläsligt för servern, som bara ser en storlek och en tidpunkt. Radera blobbarna efteråt om du inte vill lämna något kvar. - Radera - Redigera - Redigerar ditt meddelande - Gå till meddelandet - Fäst - Svara - Lossa - Ändringen kunde inte genomföras. - Koordinatorn svarade inte, så inget ändrades. Försök igen om en stund. - %1$s har inte publicerat något nyckelpaket till den här koordinatorn, så de kan inte läggas till ännu. Be dem att först öppna cordn på den här koordinatorn. - Du kan inte ta bort dig själv från en cordn-grupp. - %1$s kunde inte ges någon väg in i gruppen, så tillägget avbröts. - %1$s är inte med i den här gruppen. - Den personen - Den här koordinatorn lämnade ut ett nyckelpaket som tillhör någon annan, så %1$s lades inte till. Inget ändrades. - Dina gruppkrypteringsnycklar, dina nyckelpaket och varje meddelande i de grupperna. Den som öppnar filen kan läsa konversationerna. Lösenfrasen är det enda som skyddar den när den väl lämnat Amethyst. - Vad filen innehåller - Din nsec kan inte återskapa cordn-grupper. En cordn-grupp lever som krypteringstillstånd på den här enheten och som en ordnad ström hos en koordinator som inte kan läsa den — förlorar du enheten utan säkerhetskopia är gruppen borta, inklusive allt som redan sagts i den. - Exportera - Säkerhetskopian sparad. - Det fungerade inte. Kontrollera lösenfrasen och filen. - Lösenfras - Återställ - Återställning ersätter cordn-grupperna på den här enheten med dem i filen. Den är till för en telefon som tagit över från en annan — inte ett sätt att använda samma grupper på två enheter samtidigt. - Alla cordn-grupper som nu finns på den här enheten tas bort och ersätts med dem i filen.\n\nÅterställ inte till en andra telefon medan den första fortfarande används. Båda skulle ha samma gruppnycklar, och så snart båda har skickat ett meddelande går gruppen sönder för alla — tyst, och utan väg tillbaka. - Ersätta den här enhetens cordn-grupper? - Återställ - Återställd. - Allt som skickas här krypteras för den här gruppen. Koordinatorn förmedlar det utan att kunna läsa det. - Inga meddelanden ännu - Nya meddelanden - I går - Meddelande - Lägg till - Kunde inte öppna den koordinatorn. - Kopiera koordinatorns npub - Leta efter koordinatorer - Lägg till - Läser annonser som dina reläer redan har. Ingen koordinator kontaktas, så ingen av dem får veta att du letade. - Kunde inte läsa annonser - Ingen annonserar på dina reläer. - Annonserade för %1$s sedan - Senast annonserad %1$s - - Inget hittades, och %1$d av dina reläer svarade inte. - Inget hittades, och %1$d av dina reläer svarade inte. - - En koordinator ordnar en grupps meddelanden och är det enda som kan göra det. Förlorar man den förloras ordningen, och en andra koordinator speglar inte den första — så det här är namngivna saker du väljer, inte utbytbara reläer. - - Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. - Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. - - Svarar. - Ett anrop misslyckades; försöker igen. - Inget har efterfrågats ännu. - Dölj dem som slutat annonsera - Fråga vem den är - Koordinatornyckel - Ett namn för den (ditt eget, valfritt) - Det här namnet är ditt och stannar på den här enheten. En koordinator kan inte bevisa ett namn, så den ombeds aldrig uppge något. - Fler åtgärder - Inga koordinatorer ännu. - Rensa bort - Att ta bort behåller grupperna på den här enheten, så om du lägger till koordinatorn igen kommer de tillbaka. Att rensa bort raderar dem: gruppnycklarna, lässtatusen och dina nyckelpaket försvinner, och de grupperna kan bara nås igen via en ny inbjudan.\n\nKoordinatorn meddelas inte och behåller allt den redan hade. - Rensa bort den här koordinatorn? - %1$s - Svarar på - %1$s +%2$d till - Ta bort - Byt namn - Säger sig vara: %1$s - En koordinators namn och version är strängar den själv valt. Dess publika nyckel är det enda som identifierar den. - Svarade, men angav inget namn. - Visa alla %1$d - Visa färre - - Visa %1$d som slutat annonsera - Visa %1$d som slutat annonsera - - Dessa har inte annonserat på över en månad. Att skapa en grupp på en som försvunnit kommer att misslyckas. - Skapa grupp - - Skapa och bjud in %1$d person - Skapa och bjud in %1$d personer - - Lägg till någon - Admin - - %1$d person kan lägga till och ta bort - %1$d personer kan lägga till och ta bort - - Bara jag hanterar den här gruppen - Bara du kan bjuda in, ta bort medlemmar eller ändra gruppens uppgifter. Du kan lämna över det till någon annan senare. - Koordinator - Ändra - Hitta koordinatorer - Klar - En annan koordinator - Koordinatorns publika nyckel (hex eller npub) - Reläer den svarar på, ett per rad - - Når %1$d person — alla du valt - Når alla dina %1$d personer - - Når %1$d av %2$d — resten skulle bli utanför - Inte kontrollerad — den tillfrågas om du väljer den - Beskrivning (valfritt) - Alla kan lägga till och ta bort - Alla i gruppen kan bjuda in, ta bort medlemmar och ändra gruppens uppgifter. Vilken medlem som helst kan utse admins senare, vilket upphäver detta. - En cordn-grupp ordnas av en koordinator du väljer. Gruppen skapas på den här enheten — koordinatorn får veta att gruppen finns först när du bjuder in någon eller skickar ett meddelande. - Gruppen kunde inte skapas. - Gruppnamn - Ingen koordinator vald - Varje inbjudan är ett Welcome-meddelande som lämnas hos koordinatorn, så de kan misslyckas en i taget. Ingen har meddelats: de ser det nästa gång de öppnar sin app. - Koordinatorn avvisade inbjudan - Skicka en länk - Tillagd i gruppen, men inbjudan nådde inte fram — försök igen från gruppskärmen - Öppna chatten - Ingen nyckel här, så inget Welcome-meddelande kunde lämnas - Gruppen är skapad - Väntar på dem - Klar - Ingen tillagd ännu. En grupp kan skapas tom och inbjudningar skickas senare, men att ange personer nu är det som visar vilken koordinator som kan nå dem. - Ingen ännu — tryck för att lägga till personer - Att ange personer nu är det som talar om för oss vilken koordinator som kan nå dem. Var och en får ett Welcome-meddelande som lämnas hos den koordinatorn, och som de hämtar nästa gång de öppnar sin app. - - Nåbar via %1$d koordinator - Nåbar via %1$d koordinatorer - - Ingen nyckel hos någon koordinator du använder - Inte kontrollerad ännu - Vem som kan lägga till och ta bort - Namnge den - Vilka som är med - Var den finns - Ny Cordn-grupp - - %1$d person kan inte nås av någon koordinator du använder - %1$d personer kan inte nås av någon koordinator du använder - - Du, alltid admin - Alla i gruppen kommer att se meddelandet som återkallat, och Amethyst slutar visa texten. Koordinatorn har fortfarande den krypterade kopia som skickades, och den som redan har läst det har redan läst det. - Återkalla meddelande - Accepterat av koordinatorn - Uppfattat - Vad den här koordinatorn kan se - Gruppinfo - Den här gruppen är inte tillgänglig. Dess koordinator kan ha tagits bort. - Lägg till en koordinator i Inställningar för att börja använda cordn-grupper. - - %1$d grupp - %1$d grupper - - Inget kommer fram från de här grupperna. Amethyst fortsätter att försöka. - Koordinatorer - Inga cordn-grupper ännu - En cordn-grupp finns hos en koordinator som håller reda på medlemskapet och skickar meddelanden vidare, utan att någonsin se vad någon skriver. - Starta en - Lägg till någon - Ingen hittades med det namnet. - De kan bara läggas till om de har publicerat ett nyckelpaket till den här koordinatorn. - Namn, npub eller namn@domän - Admin - - Gruppens %1$d admin förblir oförändrad. - Gruppens %1$d admins förblir oförändrade. - - Koordinator - Koordinatornyckel - Koordinatorlänk - Kopiera koordinatorns nprofile - Redigera uppgifter - Den här gruppen har inga admins: alla kan lägga till, ta bort och byta namn, permanent. - Epok - Grupp-id - Kan läggas till via den här koordinatorn - Medlemmar - %1$s slutar ta emot meddelanden härifrån. De behåller det de redan har läst, och detta kan inte ångras — de skulle behöva bjudas in igen. - Ta bort från gruppen? - Ta bort - Spara - Tekniska detaljer - Gå med - Avböj - Att avböja är permanent. För att komma in igen måste du bjudas in på nytt. - Kontrolleras när du öppnar den här skärmen, och aldrig annars — att fråga en koordinator om någon har bjudit in dig avslöjar att du finns här, så Amethyst gör det inte i bakgrunden. - Kunde inte läsa inbjudningar. - - %1$d person är redan med i den här gruppen - %1$d personer är redan med i den här gruppen - - +%1$d till - Du har ingen koordinator öppen. cordn-inbjudningar kommer via en koordinator, så det finns ingenstans att leta ännu. - Inga väntande inbjudningar. - Kontrollera igen - En inbjudan lämnades till en annan enhet - Cordn-inbjudningar - %1$s svarade inte - Via %1$s - Publicering signerar ett belägg med din egen kontonyckel. Den koordinatorn vet då att kontot finns och går att bjuda in — permanent, och oavsett om någon bjuder in dig. - Andra kan bara lägga till dig i en cordn-grupp genom att ta ett nyckelpaket du publicerat till den koordinatorn. cordn har ingen annan plats att förvara ett, så utan något publicerat kan ingen bjuda in dig — och inget talar om för dem varför. - Kunde inte läsa nyckelpaketen. - Inget reservpaket. När engångspaketen tar slut misslyckas inbjudningar. - Ett reservpaket är publicerat, så en inbjudan kan fortfarande göras när engångspaketen tagit slut. - - %1$d paket som den här enheten inte kan öppna - %1$d paket som den här enheten inte kan öppna - - Koordinatorn lämnar ut dessa till den som bjuder in dig, men den privata halvan finns inte på den här enheten — den tillhör en annan installation. Om den installationen är borta ger en inbjudan med något av dessa ett Welcome-meddelande som ingen någonsin kan öppna. - Publicera ett - Publicera reservpaket - - %1$d engångspaket tillgängligt. - %1$d engångspaket tillgängliga. - - När du väl har publicerat här ersätter Amethyst tyst paketen allteftersom de används. Det första publicerar den aldrig åt dig. - Återkalla alla - Återkalla dem - Be att få gå med i gruppen - Detta publicerar ett nyckelpaket med din egen kontonyckel och talar om för koordinatorn att du vill gå med i gruppen — oavsett om någon svarar. - Kunde inte be att få gå med. - Förfrågan skickad. En medlem i gruppen måste lägga till dig; inbjudan dyker upp under cordn-inbjudningar. - Skanna - Kunde inte öppna den filen. - Ingen mediaserver är inställd för det här kontot, så filen har ingenstans att ta vägen. Välj en i Inställningar. - Filen kunde inte läsas. - Kunde inte skicka den filen. - - %1$d medlem - %1$d medlemmar - - Meddelandet raderat - Koordinator - Markör - Skickat - Meddelandedetaljer - redigerat - Fäst av %1$s - - %1$d fäst meddelande i den här gruppen - %1$d fästa meddelanden i den här gruppen - - Nästa fästa meddelande - Föregående fästa meddelande - Visa alla fästa meddelanden - Fästa meddelanden - Reaktioner - Lägg till - Bara en admin kan godkänna. I en grupp utan admins är det alla. - Sök efter förfrågningar - Avfärda - Kunde inte läsa förfrågningarna. - Ingen väntar på att få gå med. - Förfrågningar om att gå med - Skicka - Det gick inte att skicka. - Den här gruppens koordinator är inte öppen, så inget kan skickas ännu. - Kopierad - Kopiera länk - Alla som har den här länken kan be att få gå med. En admin måste fortfarande släppa in dem. - Dela den här gruppen - Spela upp röstmeddelande - Spela in ett röstmeddelande - Stoppa och skicka - Amethyst behöver kameran för att skanna QR-koder. Inget spelas in eller laddas upp — bilderna avkodas på enheten och kastas sedan. - Tillåt kamera - Kameraåtkomst är avstängd för Amethyst. Du kan slå på den igen i Androids inställningar. - Öppna inställningar - QR-avkodaren kunde inte starta på den här enheten. Du kan fortfarande klistra in koden som text. - Tänd lampan - Släck lampan - Det är mörkt — prova lampan - Skanna en bild - Klistra in - Ingen QR-kod hittades i bilden - Det finns inget i urklipp att skanna - Tryck på koden du vill ha - - Fångade %1$s av %2$s del — fortsätt - Fångade %1$s av %2$s delar — fortsätt - - Återställ zoom - Amethyst kan inte öppna den här koden - Den här koden innehåller en privat nyckel eller en parningshemlighet, så innehållet visas inte här. Använd skärmen som förväntar sig den. - Det här är en privat nyckel. Klistra in den på inloggningsskärmen i stället. - Det här är en plånboksanslutning. Lägg till den under Plånboksinställningar. - Det här är en fjärrsignatäradress. Amethyst delar ut sådana så att andra appar kan be den signera — den kan inte logga in med en. - Det här är en app som vill ansluta till din signatär. Öppna den från NIP-46-signatärskärmen. - Det här är en Lightning-faktura. - Det här är en Cashu-token. - Det här är en webblänk. - Det här är vanlig text, inte en Nostr-kod. - Det här är en Nostr-kod, men inte en som den här skärmen kan öppna. - Profil - Anteckning - Nostr-kod - Relä - Privat nyckel - Plånboksanslutning - Fjärrsignatäradress - Anslutningsbegäran till signatär - Lightning-faktura - Cashu-token - Webblänk - Vanlig text - Öppna länk - Kopiera - Kopierat - Skanna igen - Använd träningspassen du redan har registrerat - Anslut Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) för att bygga Min träning, din egen träningssammanfattning, och för att fylla i det här passet från ett som din klocka redan har registrerat i stället för att skriva in varje siffra för hand. - Amethyst använder Health Connect för att bygga Min träning — din egen träningssammanfattning, på din telefon. - Min träning (meny → Du → Min träning) visar hur du har tränat: hur mycket du gjorde den här veckan jämfört med förra, hur din tid fördelas mellan löpning, cykling, promenader, simning och gym, dina bästa prestationer, hur många dagar du tränade och din nuvarande svit. Amethyst bygger den bilden från träningspassen som din klocka eller träningsapp redan har sparat i Health Connect, tillsammans med pass du har loggat i Amethyst. Allt räknas ut på din telefon och visas för dig. - Träning · vilken aktivitet du utförde, när den började och hur länge den varade. Det här är själva passet: det styr antalet pass, din träningstid, uppdelningen per aktivitet, dina aktiva dagar och din svit. - Distans · hur långt du tog dig, visat som din distans den här veckan jämfört med förra, ditt veckosnitt, din distans per aktivitet och din längsta distans. - Aktiva och totala kalorier · energin ett pass förbrände, visat som din energi den här veckan och hur den står sig mot förra veckan. Aktiva kalorier används när din källa registrerar dem; totala kalorier används som reserv för källor som bara registrerar total energi. - Puls · genomsnitt och maxvärde för varje pass, sammanslaget till din genomsnittliga och högsta puls för perioden. Det är så du skiljer en lätt vecka från en tuff med samma distans. - Steg · antalet steg under en löptur, promenad eller vandring, visat som ditt veckosnitt för steg och ditt högsta antal steg. - Höjdmeter · hur mycket du klättrade, visat som ditt veckosnitt för stigning och din största stigning. Det är det som skiljer en platt vecka från en kuperad. - Läser bara träningspass från de senaste 4 veckorna, och bara medan Min träning eller passredigeraren är öppen. Läser aldrig i bakgrunden. - Skickar aldrig dina hälsodata någonstans. Min träning räknas ut på din telefon och stannar där. Om du senare vill publicera ett pass trycker du på Dela för det passet, läser inlägget som Amethyst fyllt i och publicerar det själv — ett pass i taget, aldrig automatiskt. - Hela funktionen är valfri. Återkalla behörigheterna i Health Connect när som helst — Min träning går då tillbaka till sin uppmaning — eller stäng av förslagen i redigeraren under Inställningar → Skrivinställningar. Resten av Amethyst fortsätter att fungera. - Min träning - Senaste 4 veckorna - Läser in distans, kalorier och puls… - De senaste fyra hela veckorna av din träning: passen du har publicerat, plus allt Health Connect registrerar på den här telefonen. Amethyst räknar ut sammanfattningen på enheten och publicerar ingenting av den. - Den här veckan - jämfört med förra veckan - Veckosnitt - Pass - Aktiva dagar - Dagar i rad - Tid - %1$d h %2$d min - %1$d min - %1$d s - Distans - Kalorier - Steg - Stigning - Snittpuls - Maxpuls - Per aktivitet - Bästa prestationer - Längsta distans - Längsta pass - Största stigning - Flest steg - Högsta puls - Senaste passen - Inget loggat de senaste 4 veckorna. Pass du publicerar, och de som din klocka registrerar, visas här. - Följ din träning - Anslut Health Connect för att se dina veckosummor, utvecklingen vecka för vecka, bästa prestationer och träningssvit. Allt stannar på din telefon. - Anslut Health Connect för att lägga till passen din klocka registrerar, med puls, steg och stigning. - Anslut - Dela det här passet - bpm - kcal - km - mi - m - ft - Läser in - Väntar på att dina privata meddelanden ska läsas in… - %1$02d:%2$02d - %1$d:%2$02d:%3$02d - Gruppen uppdaterad - Bilaga - Meddelande - %1$s: %2$s - Skapa en grupp - Marmot-inbjudningar går till en annan enhet och kan inte öppnas här. - Avfärda - Publicera från den här enheten - Öppna zappad anteckning - Undertexter - Slå på eller av undertexter (dolt när videon saknar sådana) - Inte skickat - Försök igen - Skickar… - Skickar fortfarande… - Det här kontot har tagits bort från Amethyst. - Av - Stäng av undertexter - Slå på undertexter - Undertexter - Applista - Artikellista - Bokmärkessamling - Favoritflöden - Git-författare - Git-förråd - Betrodda wikiförfattare - Betrodda wikireläer - Intressen - Mediaföljningar - Startpaket för media - Tystat för typ %1$s - Bildlista - Grupper - Videosamarbete - Videolista - Videoundertexter - Amethyst för Android kan inte logga in med en bunker://-adress. Den delar ut sådana så att andra appar kan be den signera, men den kan inte logga in med en. Använd en extern signatärapp eller Amethyst Desktop. - Det där är en app som vill ansluta till din signatär, inte ett sätt att logga in. Logga in först och öppna den sedan från NIP-46-signatärskärmen. - Min träning - Den här listan har privata medlemmar som bara dess skapare kan se - Redigera cache - Redigera jakt - Cache - Jakt - Cachejakter - Geocacher - Logga ett fynd - Göm en cache - Ny jakt - Accepterade ett samarbetsomnämnande - Avböjde ett samarbetsomnämnande - Omnämnande accepterat - Ljud från - Med - Inspirerad av - Referenser - Privat lista - - %1$d video - %1$d videor - - Den här enheten tar nu emot dina Marmot-inbjudningar. - Det gick inte att publicera KeyPackage: %1$s - Inget relä accepterade KeyPackage. Kontrollera dina reläinställningar och försök igen. - Skickar… - Kunde inte skickas. Tryck för att försöka igen. - - %1$d nytt meddelande - %1$d nya meddelanden - - Cordn-grupper - End-to-end-krypterade grupper som ordnas av en koordinator du väljer, i stället för av reläer. - Cordn-grupp - Krypterad gruppchatt som ordnas av en koordinator du väljer. - Koordinerad - Bäst för team som vill ha en pålitlig ordning i konversationen. - Skapa Cordn-grupp - End-to-end-krypterad med MLS — koordinatorn kan aldrig läsa ett meddelande. - En gemensam ordning för alla, så att historiken inte kan kastas om av en felaktig klocka. - Koordinatorn får veta vilka som är med i gruppen och när ni pratar, även om den inte kan läsa vad ni säger. 3D-objekt %1$d hörn · %2$d ytor Det här 3D-objektet kunde inte läsas (regel %1$s) @@ -5662,6 +5397,7 @@ %1$d byte av något som den här klienten inte läser %1$d byte av något som den här klienten inte läser + Stäng Kopiera länk Sök @@ -6092,4 +5828,268 @@ Tjänsterna du litar på för att rangordna och verifiera personer åt dig. lista över betrodda reläer Reläer du litar på att ansluta till utan att fråga. + Radera + Redigera + Redigerar ditt meddelande + Gå till meddelandet + Fäst + Svara + Lossa + Ändringen kunde inte genomföras. + Koordinatorn svarade inte, så inget ändrades. Försök igen om en stund. + %1$s har inte publicerat något nyckelpaket till den här koordinatorn, så de kan inte läggas till ännu. Be dem att först öppna cordn på den här koordinatorn. + Du kan inte ta bort dig själv från en cordn-grupp. + %1$s kunde inte ges någon väg in i gruppen, så tillägget avbröts. + %1$s är inte med i den här gruppen. + Den personen + Den här koordinatorn lämnade ut ett nyckelpaket som tillhör någon annan, så %1$s lades inte till. Inget ändrades. + Dina gruppkrypteringsnycklar, dina nyckelpaket och varje meddelande i de grupperna. Den som öppnar filen kan läsa konversationerna. Lösenfrasen är det enda som skyddar den när den väl lämnat Amethyst. + Vad filen innehåller + Din nsec kan inte återskapa cordn-grupper. En cordn-grupp lever som krypteringstillstånd på den här enheten och som en ordnad ström hos en koordinator som inte kan läsa den — förlorar du enheten utan säkerhetskopia är gruppen borta, inklusive allt som redan sagts i den. + Exportera + Säkerhetskopian sparad. + Det fungerade inte. Kontrollera lösenfrasen och filen. + Lösenfras + Återställ + Återställning ersätter cordn-grupperna på den här enheten med dem i filen. Den är till för en telefon som tagit över från en annan — inte ett sätt att använda samma grupper på två enheter samtidigt. + Alla cordn-grupper som nu finns på den här enheten tas bort och ersätts med dem i filen.\n\nÅterställ inte till en andra telefon medan den första fortfarande används. Båda skulle ha samma gruppnycklar, och så snart båda har skickat ett meddelande går gruppen sönder för alla — tyst, och utan väg tillbaka. + Ersätta den här enhetens cordn-grupper? + Återställ + Återställd. + Allt som skickas här krypteras för den här gruppen. Koordinatorn förmedlar det utan att kunna läsa det. + Inga meddelanden ännu + Nya meddelanden + I går + Meddelande + Lägg till + Kunde inte öppna den koordinatorn. + Kopiera koordinatorns npub + Leta efter koordinatorer + Lägg till + Läser annonser som dina reläer redan har. Ingen koordinator kontaktas, så ingen av dem får veta att du letade. + Kunde inte läsa annonser + Ingen annonserar på dina reläer. + Annonserade för %1$s sedan + Senast annonserad %1$s + + Inget hittades, och %1$d av dina reläer svarade inte. + Inget hittades, och %1$d av dina reläer svarade inte. + + En koordinator ordnar en grupps meddelanden och är det enda som kan göra det. Förlorar man den förloras ordningen, och en andra koordinator speglar inte den första — så det här är namngivna saker du väljer, inte utbytbara reläer. + + Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. + Svarar inte — %1$d anrop i rad har misslyckats. Grupper på den synkroniseras inte. + + Svarar. + Ett anrop misslyckades; försöker igen. + Inget har efterfrågats ännu. + Dölj dem som slutat annonsera + Fråga vem den är + Koordinatornyckel + Ett namn för den (ditt eget, valfritt) + Det här namnet är ditt och stannar på den här enheten. En koordinator kan inte bevisa ett namn, så den ombeds aldrig uppge något. + Fler åtgärder + Inga koordinatorer ännu. + Rensa bort + Att ta bort behåller grupperna på den här enheten, så om du lägger till koordinatorn igen kommer de tillbaka. Att rensa bort raderar dem: gruppnycklarna, lässtatusen och dina nyckelpaket försvinner, och de grupperna kan bara nås igen via en ny inbjudan.\n\nKoordinatorn meddelas inte och behåller allt den redan hade. + Rensa bort den här koordinatorn? + %1$s + Svarar på + %1$s +%2$d till + Ta bort + Byt namn + Säger sig vara: %1$s + En koordinators namn och version är strängar den själv valt. Dess publika nyckel är det enda som identifierar den. + Svarade, men angav inget namn. + Visa alla %1$d + Visa färre + + Visa %1$d som slutat annonsera + Visa %1$d som slutat annonsera + + Dessa har inte annonserat på över en månad. Att skapa en grupp på en som försvunnit kommer att misslyckas. + Skapa grupp + + Skapa och bjud in %1$d person + Skapa och bjud in %1$d personer + + Lägg till någon + Admin + + %1$d person kan lägga till och ta bort + %1$d personer kan lägga till och ta bort + + Bara jag hanterar den här gruppen + Bara du kan bjuda in, ta bort medlemmar eller ändra gruppens uppgifter. Du kan lämna över det till någon annan senare. + Koordinator + Ändra + Hitta koordinatorer + Klar + En annan koordinator + Koordinatorns publika nyckel (hex eller npub) + Reläer den svarar på, ett per rad + + Når %1$d person — alla du valt + Når alla dina %1$d personer + + Når %1$d av %2$d — resten skulle bli utanför + Inte kontrollerad — den tillfrågas om du väljer den + Beskrivning (valfritt) + Alla kan lägga till och ta bort + Alla i gruppen kan bjuda in, ta bort medlemmar och ändra gruppens uppgifter. Vilken medlem som helst kan utse admins senare, vilket upphäver detta. + En cordn-grupp ordnas av en koordinator du väljer. Gruppen skapas på den här enheten — koordinatorn får veta att gruppen finns först när du bjuder in någon eller skickar ett meddelande. + Gruppen kunde inte skapas. + Gruppnamn + Ingen koordinator vald + Varje inbjudan är ett Welcome-meddelande som lämnas hos koordinatorn, så de kan misslyckas en i taget. Ingen har meddelats: de ser det nästa gång de öppnar sin app. + Koordinatorn avvisade inbjudan + Skicka en länk + Tillagd i gruppen, men inbjudan nådde inte fram — försök igen från gruppskärmen + Öppna chatten + Ingen nyckel här, så inget Welcome-meddelande kunde lämnas + Gruppen är skapad + Väntar på dem + Klar + Ingen tillagd ännu. En grupp kan skapas tom och inbjudningar skickas senare, men att ange personer nu är det som visar vilken koordinator som kan nå dem. + Ingen ännu — tryck för att lägga till personer + Att ange personer nu är det som talar om för oss vilken koordinator som kan nå dem. Var och en får ett Welcome-meddelande som lämnas hos den koordinatorn, och som de hämtar nästa gång de öppnar sin app. + + Nåbar via %1$d koordinator + Nåbar via %1$d koordinatorer + + Ingen nyckel hos någon koordinator du använder + Inte kontrollerad ännu + Vem som kan lägga till och ta bort + Namnge den + Vilka som är med + Var den finns + Ny Cordn-grupp + + %1$d person kan inte nås av någon koordinator du använder + %1$d personer kan inte nås av någon koordinator du använder + + Du, alltid admin + Alla i gruppen kommer att se meddelandet som återkallat, och Amethyst slutar visa texten. Koordinatorn har fortfarande den krypterade kopia som skickades, och den som redan har läst det har redan läst det. + Återkalla meddelande + Accepterat av koordinatorn + Uppfattat + Vad den här koordinatorn kan se + Gruppinfo + Den här gruppen är inte tillgänglig. Dess koordinator kan ha tagits bort. + Lägg till en koordinator i Inställningar för att börja använda cordn-grupper. + + %1$d grupp + %1$d grupper + + Inget kommer fram från de här grupperna. Amethyst fortsätter att försöka. + Koordinatorer + Inga cordn-grupper ännu + En cordn-grupp finns hos en koordinator som håller reda på medlemskapet och skickar meddelanden vidare, utan att någonsin se vad någon skriver. + Starta en + Lägg till någon + Ingen hittades med det namnet. + De kan bara läggas till om de har publicerat ett nyckelpaket till den här koordinatorn. + Namn, npub eller namn@domän + Admin + + Gruppens %1$d admin förblir oförändrad. + Gruppens %1$d admins förblir oförändrade. + + Koordinator + Koordinatornyckel + Koordinatorlänk + Kopiera koordinatorns nprofile + Redigera uppgifter + Den här gruppen har inga admins: alla kan lägga till, ta bort och byta namn, permanent. + Epok + Grupp-id + Kan läggas till via den här koordinatorn + Medlemmar + %1$s slutar ta emot meddelanden härifrån. De behåller det de redan har läst, och detta kan inte ångras — de skulle behöva bjudas in igen. + Ta bort från gruppen? + Ta bort + Spara + Tekniska detaljer + Gå med + Avböj + Att avböja är permanent. För att komma in igen måste du bjudas in på nytt. + Kontrolleras när du öppnar den här skärmen, och aldrig annars — att fråga en koordinator om någon har bjudit in dig avslöjar att du finns här, så Amethyst gör det inte i bakgrunden. + Kunde inte läsa inbjudningar. + + %1$d person är redan med i den här gruppen + %1$d personer är redan med i den här gruppen + + +%1$d till + Du har ingen koordinator öppen. cordn-inbjudningar kommer via en koordinator, så det finns ingenstans att leta ännu. + Inga väntande inbjudningar. + Kontrollera igen + En inbjudan lämnades till en annan enhet + Cordn-inbjudningar + %1$s svarade inte + Via %1$s + Publicering signerar ett belägg med din egen kontonyckel. Den koordinatorn vet då att kontot finns och går att bjuda in — permanent, och oavsett om någon bjuder in dig. + Andra kan bara lägga till dig i en cordn-grupp genom att ta ett nyckelpaket du publicerat till den koordinatorn. cordn har ingen annan plats att förvara ett, så utan något publicerat kan ingen bjuda in dig — och inget talar om för dem varför. + Kunde inte läsa nyckelpaketen. + Inget reservpaket. När engångspaketen tar slut misslyckas inbjudningar. + Ett reservpaket är publicerat, så en inbjudan kan fortfarande göras när engångspaketen tagit slut. + + %1$d paket som den här enheten inte kan öppna + %1$d paket som den här enheten inte kan öppna + + Koordinatorn lämnar ut dessa till den som bjuder in dig, men den privata halvan finns inte på den här enheten — den tillhör en annan installation. Om den installationen är borta ger en inbjudan med något av dessa ett Welcome-meddelande som ingen någonsin kan öppna. + Publicera ett + Publicera reservpaket + + %1$d engångspaket tillgängligt. + %1$d engångspaket tillgängliga. + + När du väl har publicerat här ersätter Amethyst tyst paketen allteftersom de används. Det första publicerar den aldrig åt dig. + Återkalla alla + Återkalla dem + Be att få gå med i gruppen + Detta publicerar ett nyckelpaket med din egen kontonyckel och talar om för koordinatorn att du vill gå med i gruppen — oavsett om någon svarar. + Kunde inte be att få gå med. + Förfrågan skickad. En medlem i gruppen måste lägga till dig; inbjudan dyker upp under cordn-inbjudningar. + Skanna + Kunde inte öppna den filen. + Ingen mediaserver är inställd för det här kontot, så filen har ingenstans att ta vägen. Välj en i Inställningar. + Filen kunde inte läsas. + Kunde inte skicka den filen. + + %1$d medlem + %1$d medlemmar + + Meddelandet raderat + Koordinator + Markör + Skickat + Meddelandedetaljer + redigerat + Fäst av %1$s + + %1$d fäst meddelande i den här gruppen + %1$d fästa meddelanden i den här gruppen + + Nästa fästa meddelande + Föregående fästa meddelande + Visa alla fästa meddelanden + Fästa meddelanden + Reaktioner + Lägg till + Bara en admin kan godkänna. I en grupp utan admins är det alla. + Sök efter förfrågningar + Avfärda + Kunde inte läsa förfrågningarna. + Ingen väntar på att få gå med. + Förfrågningar om att gå med + Skicka + Det gick inte att skicka. + Den här gruppens koordinator är inte öppen, så inget kan skickas ännu. + Kopierad + Kopiera länk + Alla som har den här länken kan be att få gå med. En admin måste fortfarande släppa in dem. + Dela den här gruppen + Spela upp röstmeddelande + Spela in ett röstmeddelande + Stoppa och skicka diff --git a/docs/changelog/translators.json b/docs/changelog/translators.json index 18d1805a8c..0af8e0c262 100644 --- a/docs/changelog/translators.json +++ b/docs/changelog/translators.json @@ -123,6 +123,12 @@ "Vietnamese" ] }, + { + "user": "summoner001", + "languages": [ + "Hungarian" + ] + }, { "user": "maxblake2015", "languages": [ @@ -135,12 +141,6 @@ "Hindi" ] }, - { - "user": "summoner001", - "languages": [ - "Hungarian" - ] - }, { "user": "markkks", "languages": [ From 00102f5676fec11fdbc18506d8b06309496f074c Mon Sep 17 00:00:00 2001 From: davotoula <1747287+davotoula@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:10:56 +0000 Subject: [PATCH 16/19] chore: sync Crowdin translations and seed translator npub placeholders --- docs/changelog/translators.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/changelog/translators.json b/docs/changelog/translators.json index 0af8e0c262..eb7c79c88a 100644 --- a/docs/changelog/translators.json +++ b/docs/changelog/translators.json @@ -147,6 +147,15 @@ "Russian" ] }, + { + "user": "davotoula", + "languages": [ + "Czech", + "German", + "Portuguese, Brazilian", + "Swedish" + ] + }, { "user": "greenart7c3", "languages": [] @@ -219,10 +228,6 @@ "user": "adhrasreoshiathoi", "languages": [] }, - { - "user": "davotoula", - "languages": [] - }, { "user": "crackadoo", "languages": [] From 90e346a0e54b9f86bc5d7122101f6ddca002f3e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 14:00:14 +0000 Subject: [PATCH 17/19] fix(test): resolve the playback-error description outside composition be7be5b6 dropped the Context argument from the description lookup in PlaybackErrorOverlayFitTest, which turned it into a call to the @Composable stringRes overload from a plain test function, so the androidTest source set stopped compiling. Load it with loadStringRes in runBlocking, like the test's other lookups, and drop the now-unused targetContext. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BkG4KTqmnprUK9BAcwAyc8 --- .../composable/PlaybackErrorOverlayFitTest.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/service/playback/composable/PlaybackErrorOverlayFitTest.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/service/playback/composable/PlaybackErrorOverlayFitTest.kt index ae3095d27e..1b86d61661 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/service/playback/composable/PlaybackErrorOverlayFitTest.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/service/playback/composable/PlaybackErrorOverlayFitTest.kt @@ -39,13 +39,11 @@ import androidx.compose.ui.unit.dp import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.error_video_open_in_browser import com.vitorpamplona.amethyst.commons.resources.error_video_playback_failed import com.vitorpamplona.amethyst.commons.resources.error_video_playback_failed_description import com.vitorpamplona.amethyst.commons.ui.loadStringRes -import com.vitorpamplona.amethyst.commons.ui.stringRes import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.junit.Assert.assertTrue @@ -70,8 +68,6 @@ class PlaybackErrorOverlayFitTest { @get:Rule val rule = createComposeRule() - private val targetContext = InstrumentationRegistry.getInstrumentation().targetContext - /** * Built outside composition on purpose: the mock and its error state are fixtures for the whole * test, not per-composition state. Creating them inside `setContent` would rebuild both on every @@ -141,10 +137,12 @@ class PlaybackErrorOverlayFitTest { assertButtonUsable() rule .onNodeWithText( - stringRes( - Res.string.error_video_playback_failed_description, - "ERROR_CODE_PARSING_MANIFEST_MALFORMED", - ), + runBlocking { + loadStringRes( + Res.string.error_video_playback_failed_description, + "ERROR_CODE_PARSING_MANIFEST_MALFORMED", + ) + }, ).assertDoesNotExist() } From 8187028c354634439454c0416cd52b5c5069471d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 14:18:07 +0000 Subject: [PATCH 18/19] fix: audit findings in the code moved to commons Pre-existing bugs found while auditing the moved files, none introduced by the move: - showAmount/showAmountInteger rounded half-even (Kotlin's BigDecimal div) so 12,500 read "12k", and picked the unit before rounding so 999,500 read "1000k". - AgentWorkBoard.merge ordered every band by upvotes, not just the queue. - SuspendableConfirmation could resume twice (tap + dismiss) and left a dead dialog when the upload was cancelled. - TorDialogViewModel.save rejected Internal/Off over a hidden, invalid port, and accepted out-of-range ports for External. - ExpandingCirclesAnimation ran an infinite transition whenever the composer was open; the circles now exist only while recording and animate in the draw phase. - DebouncedPublisher.flush cancelled and restarted a publish already in flight (a second signer request). - RelayFeedViewModel read the wrong note for the DM list's first emission, and compared the flow instead of its value. - ScheduledFlag built a DateFormat per composition; Cashu discovery progress could step backwards; Desktop's chess view model kept polling after an account switch replaced it. New tests: ZapFormatterTest, SuspendableConfirmationTest, TorDialogViewModelTest, and a DebouncedPublisher case. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BkG4KTqmnprUK9BAcwAyc8 --- .../2026-08-30-commons-migration-sweep.md | 65 +++++++ .../amethyst/commons/model/buzz/AgentWork.kt | 4 +- .../service/upload/SuspendableConfirmation.kt | 25 ++- .../commons/tor/TorDialogViewModel.kt | 27 ++- .../commons/util/DebouncedPublisher.kt | 11 +- .../commons/viewmodels/RelayFeedViewModel.kt | 6 +- .../commons/cashu/CashuWalletDiscovery.kt | 8 +- .../amethyst/commons/util/ZapFormatter.kt | 32 ++-- .../commons/util/ZapFormatterNoDecimals.kt | 19 +- .../upload/SuspendableConfirmationTest.kt | 95 ++++++++++ .../commons/tor/TorDialogViewModelTest.kt | 61 +++++++ .../commons/util/DebouncedPublisherTest.kt | 26 +++ .../amethyst/commons/util/ZapFormatterTest.kt | 66 +++++++ .../commons/audio/RecordingIndicators.kt | 172 ++++++------------ .../ui/StreamingStatusFlags.kt | 21 ++- .../amethyst/desktop/chess/ChessScreen.kt | 6 + 16 files changed, 483 insertions(+), 161 deletions(-) create mode 100644 commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmationTest.kt create mode 100644 commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModelTest.kt create mode 100644 commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterTest.kt diff --git a/commons/plans/2026-08-30-commons-migration-sweep.md b/commons/plans/2026-08-30-commons-migration-sweep.md index c67ba3f177..e5ee522cd6 100644 --- a/commons/plans/2026-08-30-commons-migration-sweep.md +++ b/commons/plans/2026-08-30-commons-migration-sweep.md @@ -1686,3 +1686,68 @@ The same sweep finds 46 more movable files outside `ui/`. The notable groups are (`InFlightInvoices`, `PaymentPromptLedger`, the `bud10` resolver trio), `service/playback` leaves (`HlsLivenessCache`, `SessionRegistry`, `WssDataStreamCollector`, the player control buttons), and `model/MediaAspectRatioCache`. + +### Audit of this round + +Three reviewers read the 69 moved files. I read the chess merge and the hand edits. The +edits kept behaviour: `codePointAtKmp`/`codePointCharCount` match the JDK calls, +`padStart` matches `%02d` for every Int, `toInt()` throws the same exception as +`parseInt`, and lifecycle's `ViewModelProvider` calls the `KClass` factory overload +first. None of the findings below came from the move; all are on `main`. + +**Fixed:** + +- **Zap amounts rounded half-even** (`showAmount`, `showAmountInteger`). Kotlin's + `BigDecimal.div` already rounds to the dividend's scale with HALF_EVEN, so the + `setScale(0, HALF_UP)` after it never ran: 12,500 read "12k" and 2,500,000 read "2M". + The unit was also picked before rounding, so 999,500 read "1000k". Both now use + `divide(_, 0, HALF_UP)` and pick the unit after rounding. The `BigDecimal(0.01)` built + on every call is now a constant. The `showAmount` KDoc examples were wrong and now match + what it returns. `ZapFormatterTest` covers this. +- **`AgentWorkBoard.merge` sorted every band by upvotes.** Its KDoc says upvotes order + only the queue. An old upvoted job sat above a fresh one in Shipped, and every job sat + above every workflow run in Working. Upvotes now count only in the queue. +- **`SuspendableConfirmation`** could resume its continuation twice: a button tap plus + an outside-tap dismiss before the dialog recomposed away threw "Already resumed". A + cancelled upload also left the dialog up with dead buttons. It now resumes only while + active, and clears the dialog on cancellation, but only if the dialog is still its own. + `SuspendableConfirmationTest` covers this. +- **`TorDialogViewModel.save`** parsed the SOCKS port even with the field hidden. Junk + typed while External was selected blocked saving Internal or Off. It also took 0 or + 70000. Now only External requires a port in 1..65535; otherwise the last saved port is + kept. `TorDialogViewModelTest` covers this. +- **`ExpandingCirclesAnimation`** built its infinite transition before checking + `isRecording`. The composer toolbar always shows the record button, so it asked for a + frame on every vsync while the composer was open. The transition now exists only while + recording. The circles and the stop dot read their animated values in `graphicsLayer`, + so a frame redraws a layer instead of recomposing. +- **`DebouncedPublisher.flush`** treated a job whose `publish()` was already running as + pending. Leaving the nav picker while a publish waited on a remote signer cancelled it + and started a second signing request. `flush` now acts only during the delay. A new case + in `DebouncedPublisherTest` covers this. +- **`RelayFeedViewModel`:** + - The DM list's first emission read `nip65RelayListNote` as a `DmRelayListEvent`, + which is always null. It now reads `dmRelayListNote`. + - `subscribeTo`/`unsubscribeTo` compared the `MutableStateFlow` itself to a `User`, + so `unsubscribeTo` never cleared it. They now compare `.value`. +- **`ScheduledFlag`** built a `DateFormat` on every composition of a feed card. It is + now remembered per start time. +- **`CashuWalletDiscovery`:** up to 50 relays finishing at once could write the crawl + progress out of order, so it stepped backwards. The write is now monotonic. +- **Desktop `ChessScreen`:** polling runs on the screen's scope. When `remember` replaced + the view model on an account switch, the old one kept polling. A `DisposableEffect` now + stops it. + +**Reported, not changed (need an owner's call):** + +- `FilterLastMessageFollowingPublicChats` asks each relay for kind 41 with `limit = 1` + across all followed channels, so only one channel's metadata comes back per relay. One + busy channel can also fill the kind-42 `limit = 100`. The fix (a limit per channel, or a + filter per channel) changes relay load. +- `PitchShifter`'s brute-force WSOLA search costs about 500 multiply-adds per output + sample, and the loop never checks for cancellation. Decimating the correlation changes + the audio, and a cancellation check changes its API. +- `RelayFeedViewModel.invalidateData` re-emits the same user into a `StateFlow`, which + drops it, so a refresh only spins for a second. +- `ScheduledFlag` still reads `TimeUtils.now()` once, so a card composed before the start + time keeps the date after it passes. diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt index 98d2de1c56..15897a6b29 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/buzz/AgentWork.kt @@ -132,7 +132,9 @@ object AgentWorkBoard { (runs.map { from(it) } + jobs.map { from(it) }) .sortedWith( compareBy { it.state.ordinal } - .thenByDescending { it.upvotes ?: 0 } + // Upvotes are the queue's priority signal only. Elsewhere they would put an old + // upvoted job above a fresh one, and every job above every workflow run. + .thenByDescending { if (it.state == AgentWorkState.QUEUED) it.upvotes ?: 0 else 0 } .thenByDescending { it.updatedAt }, ) } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt index 44a457a83c..3b4c1bac21 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmation.kt @@ -44,17 +44,24 @@ class SuspendableConfirmation { suspend fun awaitConfirmation(): Boolean = mutex.withLock { suspendCancellableCoroutine { continuation -> - state = + var callbacks: ConfirmationCallbacks? = null + + // The dialog can fire two of its callbacks (a button tap and an outside-tap + // dismiss) before the recomposition that removes it; only the first may resume. + fun answer(confirmed: Boolean) { + if (state === callbacks) state = null + if (continuation.isActive) continuation.resume(confirmed) + } + + callbacks = ConfirmationCallbacks( - onConfirm = { - state = null - continuation.resume(true) - }, - onCancel = { - state = null - continuation.resume(false) - }, + onConfirm = { answer(true) }, + onCancel = { answer(false) }, ) + state = callbacks + + // An upload cancelled while waiting must not leave a dialog with dead buttons. + continuation.invokeOnCancellation { if (state === callbacks) state = null } } } } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt index 8158665f84..a01c9a2054 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModel.kt @@ -65,8 +65,12 @@ class TorDialogViewModel : ViewModel() { ) } + /** The last port that was actually saved, kept when the field is hidden and holds junk. */ + private var savedSocksPort = TorSettings().externalSocksPort + fun reset(torSettings: TorSettings) { torType.value = torSettings.torType + savedSocksPort = torSettings.externalSocksPort socksPortStr.value = torSettings.externalSocksPort.toString() onionRelaysViaTor.value = torSettings.onionRelaysViaTor dmRelaysViaTor.value = torSettings.dmRelaysViaTor @@ -81,10 +85,26 @@ class TorDialogViewModel : ViewModel() { mediaUploadsViaTor.value = torSettings.mediaUploadsViaTor } - fun save(): TorSettings = - TorSettings( + /** + * The port field is only shown for [TorType.EXTERNAL], so only then is a bad value the user's + * to fix: it throws, and the dialog shows its "invalid port" message. With the field hidden, a + * leftover unparsable value must not block saving Internal or Off; the last saved port stays. + */ + fun save(): TorSettings { + val typedPort = + socksPortStr.value + .trim() + .toIntOrNull() + ?.takeIf { it in 1..65535 } + val port = + when { + typedPort != null -> typedPort + torType.value == TorType.EXTERNAL -> throw IllegalArgumentException("Invalid SOCKS port: ${socksPortStr.value}") + else -> savedSocksPort + } + return TorSettings( torType = torType.value, - externalSocksPort = socksPortStr.value.toInt(), + externalSocksPort = port, onionRelaysViaTor = onionRelaysViaTor.value, dmRelaysViaTor = dmRelaysViaTor.value, newRelaysViaTor = newRelaysViaTor.value, @@ -97,6 +117,7 @@ class TorDialogViewModel : ViewModel() { nip05VerificationsViaTor = nip05VerificationsViaTor.value, mediaUploadsViaTor = mediaUploadsViaTor.value, ) + } fun setPreset(preset: TorPresetType) { when (preset) { diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt index eff052fcb0..c361099f9c 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisher.kt @@ -45,6 +45,13 @@ class DebouncedPublisher( ) { private var pending: Job? = null + /** + * True only while [pending] is still waiting out its delay. Once [publish] has started, the + * job is still active but there is nothing left to hurry: flushing then would cancel a publish + * that may be parked on a remote signer and start a second one. + */ + private var waiting = false + /** Records an edit: restarts the wait, so a run of edits publishes once, after the last one. */ fun schedule() = start(debounceMs) @@ -53,15 +60,17 @@ class DebouncedPublisher( * pending, so a caller can flush on every exit path without publishing the same state twice. */ fun flush() { - if (pending?.isActive != true) return + if (!waiting || pending?.isActive != true) return start(0) } private fun start(delayMs: Long) { pending?.cancel() + waiting = true pending = launch { delay(delayMs) + waiting = false publish() } } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt index be771d2bf8..37845cb984 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/RelayFeedViewModel.kt @@ -155,7 +155,7 @@ class RelayFeedViewModel : emit(emptyList()) } }.onStart { - emit(convert((currentUser.value?.nip65RelayListNote?.event as? DmRelayListEvent)?.relays()?.toSet(), currentUser.value?.relayState()?.data)) + emit(convert((currentUser.value?.dmRelayListNote?.event as? DmRelayListEvent)?.relays()?.toSet(), currentUser.value?.relayState()?.data)) }.flowOn(Dispatchers.IO) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) @@ -179,13 +179,13 @@ class RelayFeedViewModel : @OptIn(FlowPreview::class) fun subscribeTo(user: User) { - if (currentUser != user) { + if (currentUser.value != user) { currentUser.tryEmit(user) } } fun unsubscribeTo(user: User) { - if (currentUser == user) { + if (currentUser.value == user) { currentUser.tryEmit(null) invalidateData() } diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt index 54aae743e5..772e18e7fb 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/cashu/CashuWalletDiscovery.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope @@ -157,7 +158,12 @@ class CashuWalletDiscovery( } }, onRelayComplete = { - _state.value = State.Crawling(completed.incrementAndGet(), relays.size) + // Up to MAX_CONCURRENT_RELAYS relays finish at once: a coroutine preempted + // between the increment and the write would move the progress backwards. + val done = completed.incrementAndGet() + _state.update { current -> + if (current is State.Crawling && current.relaysCompleted >= done) current else State.Crawling(done, relays.size) + } }, ) } diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt index a0ad25666c..1e32ae89c6 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatter.kt @@ -31,6 +31,9 @@ val OneMega = BigDecimal(1_000_000) val TenKilo = BigDecimal(10_000) val OneKilo = BigDecimal(1_000) +/** Below this an amount rounds to nothing. Built once: it was a new `BigDecimal` per call. */ +internal val MinDisplayableAmount = BigDecimal(0.01) + private val dfGBig = ThreadLocal.withInitial { DecimalFormat("#.#G") } private val dfGSmall = ThreadLocal.withInitial { DecimalFormat("#.0G") } private val dfMBig = ThreadLocal.withInitial { DecimalFormat("#.#M") } @@ -42,23 +45,28 @@ private val dfN = ThreadLocal.withInitial { DecimalFormat("#") } * Formats a BigDecimal amount to human-readable format with G/M/K suffixes. * Returns empty string for null or very small amounts. * + * Amounts are rounded half-up to a whole number of the unit before the unit is picked, so + * 999,500 reads "1.0M" rather than "1000k". + * * Examples: - * - 1500 -> "1.5k" - * - 2500000 -> "2.5M" + * - 1500 -> "1500" + * - 12500 -> "13k" + * - 2500000 -> "3.0M" * - 10000000000 -> "10G" */ fun showAmount(amount: BigDecimal?): String { if (amount == null) return "" - if (amount.abs() < BigDecimal(0.01)) return "" + if (amount.abs() < MinDisplayableAmount) return "" + if (amount < TenKilo) return dfN.get()!!.format(amount) - return when { - amount >= TenGiga -> dfGBig.get()!!.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) - amount >= OneGiga -> dfGSmall.get()!!.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) - amount >= TenMega -> dfMBig.get()!!.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) - amount >= OneMega -> dfMSmall.get()!!.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) - amount >= TenKilo -> dfK.get()!!.format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP)) - else -> dfN.get()!!.format(amount) - } + // `divide(_, 0, HALF_UP)`, not `div(_).setScale(0, HALF_UP)`: Kotlin's `div` already rounds + // to the dividend's scale with HALF_EVEN, so the HALF_UP never ran and 12,500 read "12k". + val kilos = amount.divide(OneKilo, 0, RoundingMode.HALF_UP) + if (kilos < OneKilo) return dfK.get()!!.format(kilos) + val megas = amount.divide(OneMega, 0, RoundingMode.HALF_UP) + if (megas < OneKilo) return (if (megas < BigDecimal.TEN) dfMSmall else dfMBig).get()!!.format(megas) + val gigas = amount.divide(OneGiga, 0, RoundingMode.HALF_UP) + return (if (gigas < BigDecimal.TEN) dfGSmall else dfGBig).get()!!.format(gigas) } /** @@ -67,7 +75,7 @@ fun showAmount(amount: BigDecimal?): String { */ fun showAmountWithZero(amount: BigDecimal?): String { if (amount == null) return "0" - if (amount.abs() < BigDecimal(0.01)) return "0" + if (amount.abs() < MinDisplayableAmount) return "0" return showAmount(amount) } diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt index 5207240aef..8dc0ae07d1 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterNoDecimals.kt @@ -46,14 +46,17 @@ private val dfN = fun showAmountInteger(amount: BigDecimal?): String { if (amount == null) return "" - if (amount.abs() < BigDecimal(0.01)) return "" + if (amount.abs() < MinDisplayableAmount) return "" + if (amount < TenKilo) return dfN.get()?.format(amount) ?: "" - return when { - amount >= OneGiga -> dfG.get()?.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) ?: "" - amount >= OneMega -> dfM.get()?.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) ?: "" - amount >= TenKilo -> dfK.get()?.format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP)) ?: "" - else -> dfN.get()?.format(amount) ?: "" - } + // Round before picking the unit, so 999,500 reads "1M" rather than "1000k". + // `divide(_, 0, HALF_UP)` rather than `div(_).setScale(0, HALF_UP)`: Kotlin's `div` already + // rounds to the dividend's scale with HALF_EVEN, which left 12,500 as "12k". + val kilos = amount.divide(OneKilo, 0, RoundingMode.HALF_UP) + if (kilos < OneKilo) return dfK.get()?.format(kilos) ?: "" + val megas = amount.divide(OneMega, 0, RoundingMode.HALF_UP) + if (megas < OneKilo) return dfM.get()?.format(megas) ?: "" + return dfG.get()?.format(amount.divide(OneGiga, 0, RoundingMode.HALF_UP)) ?: "" } fun showAmountInteger(amount: Int?): String { @@ -64,7 +67,7 @@ fun showAmountInteger(amount: Int?): String { fun showAmountIntegerWithZero(amount: BigDecimal?): String { if (amount == null) return "0" - if (amount.abs() < BigDecimal(0.01)) return "0" + if (amount.abs() < MinDisplayableAmount) return "0" return showAmountInteger(amount) } diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmationTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmationTest.kt new file mode 100644 index 0000000000..9befbfb67e --- /dev/null +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/service/upload/SuspendableConfirmationTest.kt @@ -0,0 +1,95 @@ +/* + * 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.commons.service.upload + +import kotlinx.coroutines.async +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Test + +class SuspendableConfirmationTest { + @Test + fun confirmResumesTheWaiterAndClosesTheDialog() = + runTest { + val confirmation = SuspendableConfirmation() + val answer = async { confirmation.awaitConfirmation() } + runCurrent() + + assertNotNull(confirmation.state) + confirmation.state!!.onConfirm() + + assertEquals(true, answer.await()) + assertNull(confirmation.state) + } + + @Test + fun twoCallbacksFromTheSameDialogResumeOnlyOnce() = + runTest { + // A button tap and an outside-tap dismiss can both land before the dialog recomposes + // away. The second used to resume the continuation again and throw. + val confirmation = SuspendableConfirmation() + val answer = async { confirmation.awaitConfirmation() } + runCurrent() + + val callbacks = confirmation.state!! + callbacks.onConfirm() + callbacks.onCancel() + + assertEquals(true, answer.await()) + assertNull(confirmation.state) + } + + @Test + fun cancellingTheWaiterClosesTheDialog() = + runTest { + val confirmation = SuspendableConfirmation() + val answer = async { confirmation.awaitConfirmation() } + runCurrent() + assertNotNull(confirmation.state) + + answer.cancel() + runCurrent() + + assertNull(confirmation.state) + } + + @Test + fun aStaleCallbackDoesNotCloseTheNextDialog() = + runTest { + val confirmation = SuspendableConfirmation() + val first = async { confirmation.awaitConfirmation() } + runCurrent() + val firstCallbacks = confirmation.state!! + firstCallbacks.onCancel() + assertEquals(false, first.await()) + + val second = async { confirmation.awaitConfirmation() } + runCurrent() + firstCallbacks.onConfirm() + + assertNotNull(confirmation.state) + confirmation.state!!.onConfirm() + assertEquals(true, second.await()) + } +} diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModelTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModelTest.kt new file mode 100644 index 0000000000..01092252ff --- /dev/null +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/tor/TorDialogViewModelTest.kt @@ -0,0 +1,61 @@ +/* + * 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.commons.tor + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows +import org.junit.Test + +class TorDialogViewModelTest { + private fun dialogFor(settings: TorSettings) = TorDialogViewModel().apply { reset(settings) } + + @Test + fun externalSavesTheTypedPort() { + val dialog = dialogFor(TorSettings(torType = TorType.EXTERNAL, externalSocksPort = 9050)) + dialog.socksPortStr.value = "9150" + + assertEquals(9150, dialog.save().externalSocksPort) + } + + @Test + fun externalRejectsAPortThatIsNotANumberOrOutOfRange() { + val dialog = dialogFor(TorSettings(torType = TorType.EXTERNAL)) + + for (bad in listOf("90a0", "", "0", "70000", "-1")) { + dialog.socksPortStr.value = bad + assertThrows(IllegalArgumentException::class.java) { dialog.save() } + } + } + + @Test + fun aHiddenJunkPortDoesNotBlockSavingInternal() { + // The port field only shows for External. Typing junk there and then switching to + // Internal used to make every save fail with "invalid port", with the field hidden. + val dialog = dialogFor(TorSettings(torType = TorType.EXTERNAL, externalSocksPort = 9150)) + dialog.socksPortStr.value = "90a0" + dialog.torType.value = TorType.INTERNAL + + val saved = dialog.save() + + assertEquals(TorType.INTERNAL, saved.torType) + assertEquals(9150, saved.externalSocksPort) + } +} diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt index d90d6a823a..40636cf461 100644 --- a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/DebouncedPublisherTest.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -103,6 +104,31 @@ class DebouncedPublisherTest { assertEquals(1, published) } + @Test + fun flushDuringAnInFlightPublishDoesNotRestartIt() = + runTest { + // A publish can sit for seconds waiting on a remote signer. Leaving the screen then + // flushes; that must not cancel the signing request and send a second one. + var started = 0 + var finished = 0 + val publisher = + publisher { + started++ + delay(5_000) + finished++ + } + + publisher.schedule() + advanceTimeBy(debounce + 100) + assertEquals(1, started) + + publisher.flush() + advanceUntilIdle() + + assertEquals(1, started) + assertEquals(1, finished) + } + @Test fun flushWithNothingPendingIsANoOp() = runTest { diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterTest.kt new file mode 100644 index 0000000000..634aee9390 --- /dev/null +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/util/ZapFormatterTest.kt @@ -0,0 +1,66 @@ +/* + * 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.commons.util + +import org.junit.Assert.assertEquals +import org.junit.Test +import java.math.BigDecimal +import java.util.Locale + +class ZapFormatterTest { + private fun inEnglish(block: () -> T): T { + val previous = Locale.getDefault() + Locale.setDefault(Locale.US) + try { + return block() + } finally { + Locale.setDefault(previous) + } + } + + @Test + fun halvesRoundUpNotToEven() = + inEnglish { + // Kotlin's BigDecimal `div` rounds HALF_EVEN, so 12,500 used to read "12k". + assertEquals("13k", showAmountInteger(12_500)) + assertEquals("14k", showAmountInteger(13_500)) + assertEquals("3M", showAmountInteger(2_500_000)) + assertEquals("13k", showAmount(BigDecimal(12_500))) + } + + @Test + fun roundingUpToTheNextUnitSwitchesUnit() = + inEnglish { + assertEquals("999k", showAmountInteger(999_499)) + assertEquals("1M", showAmountInteger(999_500)) + assertEquals("1G", showAmountInteger(999_500_000)) + assertEquals("1.0M", showAmount(BigDecimal(999_500))) + } + + @Test + fun smallAmountsStayWhole() = + inEnglish { + assertEquals("0", showAmountInteger(0)) + assertEquals("9999", showAmountInteger(9_999)) + assertEquals("", showAmount(BigDecimal("0.001"))) + assertEquals("0", showAmountWithZero(BigDecimal("0.001"))) + } +} diff --git a/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt index be2eb5eced..63b8a299b6 100644 --- a/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/audio/RecordingIndicators.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.commons.audio +import androidx.compose.animation.core.InfiniteTransition import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat @@ -29,6 +30,7 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -38,12 +40,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -63,113 +63,56 @@ fun ExpandingCirclesAnimation( modifier: Modifier = Modifier, isRecording: Boolean, primaryColor: Color = MaterialTheme.colorScheme.primary, +) { + // The composer shows this button all the time. An infinite transition asks for a frame on + // every vsync for as long as it is composed, so it only exists while recording. + if (isRecording) { + ExpandingCircles(modifier, primaryColor) + } +} + +@Composable +private fun ExpandingCircles( + modifier: Modifier, + primaryColor: Color, ) { val infiniteTransition = rememberInfiniteTransition(label = "expanding_circles") - // First circle animation - val scale1 by infiniteTransition.animateFloat( - initialValue = 0f, - targetValue = 2.5f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle1_scale", - ) - - val alpha1 by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 0f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle1_alpha", - ) - - // Second circle animation (offset by 500ms) - val scale2 by infiniteTransition.animateFloat( - initialValue = 0f, - targetValue = 2.5f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, delayMillis = 500, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle2_scale", - ) - - val alpha2 by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 0f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, delayMillis = 500, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle2_alpha", - ) - - // Third circle animation (offset by 1000ms) - val scale3 by infiniteTransition.animateFloat( - initialValue = 0f, - targetValue = 2.5f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, delayMillis = 1000, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle3_scale", - ) - - val alpha3 by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 0f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1500, delayMillis = 1000, easing = LinearEasing), - repeatMode = RepeatMode.Restart, - ), - label = "circle3_alpha", - ) - - if (!isRecording) return - Box(modifier = modifier, contentAlignment = Alignment.Center) { - // Circle 1 - Box( - modifier = - Modifier - .matchParentSize() - .scale(scale1) - .alpha(alpha1) - .background(primaryColor.copy(alpha = 0.3f), CircleShape), - ) - - // Circle 2 - Box( - modifier = - Modifier - .matchParentSize() - .scale(scale2) - .alpha(alpha2) - .background(primaryColor.copy(alpha = 0.2f), CircleShape), - ) - - // Circle 3 - Box( - modifier = - Modifier - .matchParentSize() - .scale(scale3) - .alpha(alpha3) - .background(primaryColor.copy(alpha = 0.1f), CircleShape), - ) + ExpandingCircle(infiniteTransition, delayMillis = 0, color = primaryColor.copy(alpha = 0.3f), label = "circle1") + ExpandingCircle(infiniteTransition, delayMillis = 500, color = primaryColor.copy(alpha = 0.2f), label = "circle2") + ExpandingCircle(infiniteTransition, delayMillis = 1000, color = primaryColor.copy(alpha = 0.1f), label = "circle3") } } +@Composable +private fun BoxScope.ExpandingCircle( + transition: InfiniteTransition, + delayMillis: Int, + color: Color, + label: String, +) { + val spec = + infiniteRepeatable( + animation = tween(durationMillis = 1500, delayMillis = delayMillis, easing = LinearEasing), + repeatMode = RepeatMode.Restart, + ) + val scale = transition.animateFloat(initialValue = 0f, targetValue = 2.5f, animationSpec = spec, label = "${label}_scale") + val alpha = transition.animateFloat(initialValue = 1f, targetValue = 0f, animationSpec = spec, label = "${label}_alpha") + + // Read in the draw phase so each frame redraws the layer instead of recomposing. + Box( + modifier = + Modifier + .matchParentSize() + .graphicsLayer { + scaleX = scale.value + scaleY = scale.value + this.alpha = alpha.value + }.background(color, CircleShape), + ) +} + /** * Floating recording indicator showing elapsed time */ @@ -218,16 +161,17 @@ fun FloatingRecordingIndicator( ) { // Pulsing stop square val infiniteTransition = rememberInfiniteTransition(label = "recording_stop") - val dotAlpha by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 0.5f, - animationSpec = - infiniteRepeatable( - animation = tween(durationMillis = 1000), - repeatMode = RepeatMode.Reverse, - ), - label = "dot_alpha", - ) + val dotAlpha = + infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 0.5f, + animationSpec = + infiniteRepeatable( + animation = tween(durationMillis = 1000), + repeatMode = RepeatMode.Reverse, + ), + label = "dot_alpha", + ) Icon( symbol = MaterialSymbols.Stop, @@ -235,7 +179,7 @@ fun FloatingRecordingIndicator( tint = Color.White, modifier = Modifier - .alpha(dotAlpha) + .graphicsLayer { alpha = dotAlpha.value } .padding(end = 8.dp), ) diff --git a/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt index 7528de89ad..cfc643fb71 100644 --- a/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/nip53LiveActivities/ui/StreamingStatusFlags.kt @@ -112,16 +112,19 @@ fun OfflineFlag() { @Composable fun ScheduledFlag(starts: Long?) { + // Remembered: this sits on feed cards, and building a DateFormat loads locale data each time. val startsIn = - starts?.let { - if (it > TimeUtils.now()) { - SimpleDateFormat - .getDateTimeInstance( - DateFormat.SHORT, - DateFormat.SHORT, - ).format(Date(starts * 1000)) - } else { - null + remember(starts) { + starts?.let { + if (it > TimeUtils.now()) { + SimpleDateFormat + .getDateTimeInstance( + DateFormat.SHORT, + DateFormat.SHORT, + ).format(Date(starts * 1000)) + } else { + null + } } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt index 271312a224..4f35817288 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt @@ -48,6 +48,7 @@ import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -116,6 +117,11 @@ fun ChessScreen( scope = scope, ) } + // The polling runs on this screen's scope, which outlives an account switch: stop the old + // account's view model when `remember` replaces it, or both keep polling. + DisposableEffect(viewModel) { + onDispose { viewModel.stopPolling() } + } val connectedRelays by relayManager.connectedRelays.collectAsState() val broadcastStatus by viewModel.broadcastStatus.collectAsState() val activeGames by viewModel.activeGames.collectAsState() From de34f392a9012c4f111dd6fb197940a94d160316 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 14:33:13 +0000 Subject: [PATCH 19/19] fix(quartz): a superseded replaceable is REPLACED (OK false), not a duplicate A replaceable/addressable version that a stored one already beats is not written. Since 18c576a068 the SQLite store has reported it as SUPERSEDED, "duplicate: a newer version ...", and RelaySession acks every `duplicate:` reason with OK true. So a client was told its event is on the relay when no REQ will ever return it. NIP-01's third OK field is `true` when the event was accepted. classifyRowError now answers RejectionReason.REPLACED, `replaced: a newer version exists`, which RelaySession sends as OK false. That is strfry's answer too (`false, "replaced: have newer event"`). It also names the case correctly: the relay does not have THIS event, it has a newer one. The W09 work is untouched. Classification still asks the database rather than the driver's message, so Android's null-message constraint failures still classify correctly, and a byte-for-byte re-offer of a stored version is still DUPLICATE (OK true). The MDK `wn` loop 18c576a068 was fixing (a second KeyPackage minted in the same second) now gets `OK false replaced:` instead of raw constraint text, which was the unclassifiable part. Retrying a `replaced:` rejection cannot succeed, so that is for the client to stop doing, not for the relay to paper over with OK true. RejectionReason.SUPERSEDED stays, @Deprecated in favour of REPLACED, for source compatibility. NostrServerTest and InsertOutcomeClassificationTest flip their expectations, and the event-store-semantics skill updates W01/W02 and W09 and adds a changelog entry. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AdSGU88PYrRVyVHjiEc5sw --- .claude/skills/event-store-semantics/SKILL.md | 25 +++++++++++------- .../quartz/nip01Core/store/RejectionReason.kt | 25 +++++++++++++----- .../store/sqlite/SQLiteEventStore.kt | 6 ++--- .../nip01Core/relay/server/NostrServerTest.kt | 26 ++++++++++--------- .../sqlite/InsertOutcomeClassificationTest.kt | 18 ++++++------- 5 files changed, 60 insertions(+), 40 deletions(-) diff --git a/.claude/skills/event-store-semantics/SKILL.md b/.claude/skills/event-store-semantics/SKILL.md index a98f901693..e14608e2fc 100644 --- a/.claude/skills/event-store-semantics/SKILL.md +++ b/.claude/skills/event-store-semantics/SKILL.md @@ -143,11 +143,13 @@ messages quoted below (they surface as the NIP-01 `OK false` reason). kinds. A `BEFORE INSERT` trigger deletes any stored version that is *older* — meaning `created_at` smaller, **or equal `created_at` with lexicographically larger id** (NIP-01 lowest-id-wins). Inserting a version that is *not* newer under that ordering leaves the stored -row in place and fails the unique index → rejected with `RejectionReason.SUPERSEDED` -(`duplicate: a newer version of this replaceable event is already stored`), which the relay -session answers with `OK true` exactly like an id duplicate (NIP-01 `duplicate:` prefix; same -reply nostr-rs-relay gives). Net contract: exactly one version stored; newest wins; ties broken -by lowest id; older re-inserts blocked but acknowledged as already covered. +row in place and fails the unique index → rejected with `RejectionReason.REPLACED` +(`replaced: a newer version exists`), which the relay session answers with `OK false`: the +event was not written, and NIP-01's `true` means accepted. Same reply strfry gives +(`false, "replaced: have newer event"`); nostr-rs-relay answers `true, "duplicate:"` instead, +and `RejectionReason.SUPERSEDED`, which did the same, is deprecated. Net contract: exactly one +version stored; newest wins; ties broken by lowest id; older re-inserts blocked and reported as +such. **STORE-W02 — addressable supersession.** Same as W01 with unique index `(kind, pubkey, d_tag)` over `30000 ≤ kind < 40000`. Nuance: `d_tag` is populated from the @@ -193,16 +195,15 @@ in input order; OK frames pair by event id, not order. exception text.** `SQLiteEventStore.classifyRowError` rolls the row's savepoint back and then asks the connection (which now shows pre-insert state): id already present → `DUPLICATE`; a stored version that beats this one at the replaceable/addressable coordinate (the exact -complement of the supersession predicate in W01/W02) → `SUPERSEDED`; otherwise `Failed`. +complement of the supersession predicate in W01/W02) → `REPLACED`; otherwise `Failed`. Message text is only a fast path and a fallback for trigger RAISEs (`blocked:`, `not allowed`), which leave no database-visible trace. This matters because the message is driver-specific — the bundled JVM driver writes `UNIQUE constraint failed: event_headers.id`, Android's throws an `android.database.SQLException` with a **null** message — so a text-only classifier answered `OK false` on Android for events the store already held. Corollary: re-offering a stored -replaceable/addressable event **byte-for-byte** is `DUPLICATE`, not `SUPERSEDED` (it violates -both indexes and only the id answer is driver-independent); a stale *different* version is -still `SUPERSEDED`. Both carry the `duplicate:` prefix, so the relay reply is `OK true` either -way. +replaceable/addressable event **byte-for-byte** is `DUPLICATE` (`OK true`: it is stored), not +`REPLACED` (it violates both indexes and only the id answer is driver-independent); a stale +*different* version is `REPLACED` (`OK false`: it is not). --- @@ -338,6 +339,10 @@ non-itemizable cases). Add one line per behavior change, newest first: `YYYY-MM-DD — what changed`. +- 2026-09-27 (pending) W01/W02, W09 — a stale replaceable/addressable version is `REPLACED` + (`replaced:` → `OK false`) again, not `SUPERSEDED` (`duplicate:` → `OK true`): it is not + written, and `OK true` told the client it was. `SUPERSEDED` is deprecated. Byte-for-byte + re-offers stay `DUPLICATE`. - 2026-09-18 (pending) W09, W01/W02 — insert-failure classification now queries the database instead of parsing the driver's exception message (Android's is null, so duplicates were reported as `Failed`/`OK false`). A byte-for-byte re-offer of a stored replaceable/addressable diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/RejectionReason.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/RejectionReason.kt index 00d6ccc7dc..f9b6378e8b 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/RejectionReason.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/RejectionReason.kt @@ -51,17 +51,30 @@ object RejectionReason { const val DUPLICATE = "duplicate: already have this event" /** - * A replaceable or addressable event that a stored version already supersedes - * (newer `created_at`, or the same `created_at` and a lower id). Nothing is - * written, and — like [DUPLICATE] — the relay answers `OK true`: NIP-01 keeps - * `duplicate:` as the machine-readable prefix for "already covered", and that - * is what nostr-rs-relay sends here too, so clients that retry on anything - * else (MDK's `wn`) settle instead of re-offering the same event forever. + * No longer produced by any store: a superseded version is [REPLACED]. Its + * `duplicate:` prefix made the relay answer `OK true` for an event that was + * never written, which NIP-01 reserves for an accepted one. */ + @Deprecated( + "A stale replaceable/addressable version is not written, so it is REPLACED (OK false), not a duplicate.", + ReplaceWith("RejectionReason.REPLACED", "com.vitorpamplona.quartz.nip01Core.store.RejectionReason"), + ) const val SUPERSEDED = "duplicate: a newer version of this replaceable event is already stored" const val EXPIRED = "blocked: Cannot insert an expired event" const val DELETED = "blocked: a deletion event exists" const val VANISHED = "blocked: a request to vanish event exists" + + /** + * A replaceable or addressable event that a stored version already supersedes + * (newer `created_at`, or the same `created_at` and a lower id): STORE-W01/W02. + * Nothing is written, so the relay answers `OK false` — NIP-01's third field is + * `true` only when the event was accepted, and acking it would tell the client + * its event is on this relay when no REQ will ever return it. `replaced:` is + * also strfry's answer (`false, "replaced: have newer event"`), and it does not + * misname the case the way `duplicate:` would: the relay does not have THIS + * event, it has a newer one. A client should not retry it; nothing a retry can + * change. A byte-for-byte re-offer of the stored version is [DUPLICATE] instead. + */ const val REPLACED = "replaced: a newer version exists" const val INSERT_FAILED = "error: insert failed" } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/SQLiteEventStore.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/SQLiteEventStore.kt index 9f267c3d6c..a9caa7a1fe 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/SQLiteEventStore.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/SQLiteEventStore.kt @@ -581,9 +581,9 @@ class SQLiteEventStore( } // The replaceable / addressable unique indexes fire only when the supersession // trigger found nothing older to delete, i.e. the stored version already wins - // (STORE-W01/W02). Same shape as a duplicate: nothing to write, `OK true`. + // (STORE-W01/W02). Nothing was written, so REPLACED: `OK false`, not a duplicate. if (message.contains(SUPERSEDED_CONSTRAINT) || isSupersededByStored(event, db)) { - return IEventStore.InsertOutcome.Rejected(RejectionReason.SUPERSEDED) + return IEventStore.InsertOutcome.Rejected(RejectionReason.REPLACED) } return if (message.contains("constraint", ignoreCase = true)) { @@ -618,7 +618,7 @@ class SQLiteEventStore( * trigger *would* have deleted doesn't count. That precision matters * here: this is the fallback for unrecognized failures, and a disk * error while inserting a winning replaceable event must stay `Failed` - * rather than turn into a silent `OK true`. An equal id is the + * rather than be reported as having lost to a stored version. An equal id is the * duplicate case and is answered before this one. */ private fun isSupersededByStored( diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/server/NostrServerTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/server/NostrServerTest.kt index bde070947f..a45654e7f2 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/server/NostrServerTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/server/NostrServerTest.kt @@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.server.policies.EmptyPolicy import com.vitorpamplona.quartz.nip01Core.relay.server.policies.IRelayPolicy import com.vitorpamplona.quartz.nip01Core.store.IEventStore +import com.vitorpamplona.quartz.nip01Core.store.RejectionReason import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventStore import com.vitorpamplona.quartz.utils.EventFactory import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -151,12 +152,12 @@ class NostrServerTest { /** * STORE-W01: a replaceable event older than the stored version is not written, - * and the relay acknowledges it the way nostr-rs-relay does — `OK true` with the - * NIP-01 `duplicate:` prefix — rather than leaking the unique-index text as a - * rejection the client would keep retrying. + * so the relay answers `OK false` with `replaced:` (strfry's answer) — never + * `OK true`, which NIP-01 keeps for an accepted event, and never the raw + * unique-index text, which no client can classify. */ @Test - fun olderReplaceableIsAcknowledgedAsDuplicateNotRejected() = + fun olderReplaceableIsRejectedAsReplaced() = runTest { val dispatcher = UnconfinedTestDispatcher(testScheduler) val store = EventStore(null) @@ -172,8 +173,8 @@ class NostrServerTest { val okMessages = collector.rawMessagesContaining("OK") assertEquals(2, okMessages.size) assertTrue(okMessages[0].contains(",true,")) - assertTrue(okMessages[1].contains(",true,"), "older version must be acked, got ${okMessages[1]}") - assertTrue(okMessages[1].contains("duplicate:"), "older version must carry the duplicate: prefix") + assertTrue(okMessages[1].contains(",false,"), "older version was not stored, so it must not be acked, got ${okMessages[1]}") + assertTrue(okMessages[1].contains(RejectionReason.PREFIX_REPLACED), "older version must carry the replaced: prefix") val stored = store.query(Filter(kinds = listOf(0))) assertEquals(listOf(newer.id), stored.map { it.id }, "the newer version stays the only stored one") @@ -183,12 +184,13 @@ class NostrServerTest { /** * STORE-W02 tie: two addressable events with the same `d` tag and the same - * `created_at` — the lower id wins, the other is acknowledged as superseded. - * This is the exact shape MDK's `wn keys publish` produces when it mints a - * second KeyPackage within the same second as the first. + * `created_at` — the lower id wins, the other is rejected as `replaced:`. This + * is the shape MDK's `wn keys publish` produces when it mints a second + * KeyPackage within the same second as the first; the loser is not stored, + * so the client must learn that rather than be told it was accepted. */ @Test - fun sameSecondAddressableTieLoserIsAcknowledgedAsDuplicate() = + fun sameSecondAddressableTieLoserIsRejectedAsReplaced() = runTest { val dispatcher = UnconfinedTestDispatcher(testScheduler) val store = EventStore(null) @@ -204,8 +206,8 @@ class NostrServerTest { val okMessages = collector.rawMessagesContaining("OK") assertEquals(2, okMessages.size) - assertTrue(okMessages[1].contains(",true,"), "tie loser must be acked, got ${okMessages[1]}") - assertTrue(okMessages[1].contains("duplicate:")) + assertTrue(okMessages[1].contains(",false,"), "tie loser was not stored, so it must not be acked, got ${okMessages[1]}") + assertTrue(okMessages[1].contains(RejectionReason.PREFIX_REPLACED)) val stored = store.query(Filter(kinds = listOf(30443))) assertEquals(listOf(lowerId.id), stored.map { it.id }, "lowest id wins the tie") diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/InsertOutcomeClassificationTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/InsertOutcomeClassificationTest.kt index 2bdcba4dc0..a731bb4d61 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/InsertOutcomeClassificationTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/store/sqlite/InsertOutcomeClassificationTest.kt @@ -34,9 +34,9 @@ import kotlin.test.assertTrue /** * `batchInsert` must name *why* a row didn't land, because the relay turns - * that reason into the NIP-01 answer: [RejectionReason.DUPLICATE] and - * [RejectionReason.SUPERSEDED] are `OK true` ("already covered"), everything - * else is `OK false`, which clients retry. + * that reason into the NIP-01 answer: [RejectionReason.DUPLICATE] is `OK true` + * (the event is already here), everything else is `OK false` — including + * [RejectionReason.REPLACED], a stale version that was not written. * * This suite pins the classification **independently of the SQLite driver's * exception text**. The bundled JVM driver raises @@ -81,18 +81,18 @@ class InsertOutcomeClassificationTest : BaseDBTest() { } @Test - fun olderReplaceableIsRejectedAsSuperseded() = + fun olderReplaceableIsRejectedAsReplaced() = forEachDB { db -> val time = TimeUtils.now() val older = signer.sign(MetadataEvent.createNew("Vitor 1", createdAt = time)) val newer = signer.sign(MetadataEvent.createNew("Vitor 2", createdAt = time + 1)) assertEquals(IEventStore.InsertOutcome.Accepted, db.batchInsert(listOf(newer))[0]) - assertRejected(RejectionReason.SUPERSEDED, db.batchInsert(listOf(older))[0]) + assertRejected(RejectionReason.REPLACED, db.batchInsert(listOf(older))[0]) } @Test - fun sameSecondReplaceableTieLoserIsRejectedAsSuperseded() = + fun sameSecondReplaceableTieLoserIsRejectedAsReplaced() = forEachDB { db -> val time = TimeUtils.now() // NIP-01 breaks a created_at tie by lowest id, so the winner is @@ -104,18 +104,18 @@ class InsertOutcomeClassificationTest : BaseDBTest() { ).sortedBy { it.id } assertEquals(IEventStore.InsertOutcome.Accepted, db.batchInsert(listOf(winner))[0]) - assertRejected(RejectionReason.SUPERSEDED, db.batchInsert(listOf(loser))[0]) + assertRejected(RejectionReason.REPLACED, db.batchInsert(listOf(loser))[0]) } @Test - fun olderAddressableIsRejectedAsSuperseded() = + fun olderAddressableIsRejectedAsReplaced() = forEachDB { db -> val time = TimeUtils.now() val older = signer.sign(LongFormContentEvent.build("v1", "title", dTag = "blog", createdAt = time)) val newer = signer.sign(LongFormContentEvent.build("v2", "title", dTag = "blog", createdAt = time + 1)) assertEquals(IEventStore.InsertOutcome.Accepted, db.batchInsert(listOf(newer))[0]) - assertRejected(RejectionReason.SUPERSEDED, db.batchInsert(listOf(older))[0]) + assertRejected(RejectionReason.REPLACED, db.batchInsert(listOf(older))[0]) } @Test