Commit Graph
14248 Commits
Author SHA1 Message Date
Vitor PamplonaandGitHub cb8efd070e Merge pull request #3091 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 17:45:59 -04:00
Crowdin Bot 47af414e47 New Crowdin translations by GitHub Action 2026-05-28 21:38:48 +00:00
Vitor PamplonaandGitHub a0af93bbbe Merge pull request #3090 from vitorpamplona/claude/exciting-meitner-KeUBf
Add NIP-71 audio-track support with language and bitrate tags
2026-05-28 17:36:56 -04:00
Claude 7efe6fdf2a feat(nip71): support audio-track imeta variants per nostr-protocol/nips#2255
Adds the audio-track imeta properties from NIP-71 PR #2255 so video
events can advertise external audio tracks (multi-language, alternate
bitrates) alongside video variants:

- New imeta properties: bitrate, duration (float seconds), waveform,
  and l <code> <standard> [ov] for language with an original-version flag
- Extends VideoMeta with bitrate, duration, waveform, language fields
  plus isAudio/isVideo helpers
- VideoEvent exposes audioTracks()/videoTracks() so players can prefer
  separate audio tracks over in-video audio while switching resolution
- Round-trip test against the PR's spec example
2026-05-28 21:15:08 +00:00
David KasparandGitHub d2e5364074 Merge pull request #3089 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 18:23:40 +02:00
Crowdin Bot 0ff512c09e New Crowdin translations by GitHub Action 2026-05-28 15:57:45 +00:00
Vitor PamplonaandGitHub e9a1215063 Merge pull request #3088 from vitorpamplona/claude/intelligent-fermat-8y2or
Auto-stick feeds to top on prepend with StickToTopOnPrepend
2026-05-28 11:55:51 -04:00
Vitor PamplonaandGitHub 938e9401b2 Merge pull request #3087 from vitorpamplona/claude/sweet-gates-pLvsz
Fix StrictMode violation in ML Kit translation initialization
2026-05-28 11:54:15 -04:00
Claude b1da3c2161 fix: move ML Kit translation off the UI dispatcher
LaunchedEffect runs on Dispatchers.Main by default. The first call to
LanguageTranslatorService triggers its class init, which loads a Properties
file from inside the play-services AAR via ZipFile/RandomAccessFile and
trips StrictMode's DiskReadViolation on the UI thread.

Wrap the translateAndCache call in withContext(Dispatchers.IO) so MLKit's
first-touch init happens off the UI dispatcher.
2026-05-28 15:52:21 +00:00
David KasparandGitHub b9bc21d78e Merge pull request #3084 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 14:29:20 +02:00
Crowdin Bot 6e9a905e64 New Crowdin translations by GitHub Action 2026-05-28 12:28:31 +00:00
davotoulaandClaude Opus 4.7 d27d215d97 i18n: add cs/de/sv plurals for music track counts; teach skill to diff <plurals>
The find-missing-translations skill only diffed <string name=, so missing
<plurals> resources slipped through. Updated Steps 2, 2.5, 3 to diff
<plurals> independently and added 3 missing music playlist plurals
across cs/de/sv with correct CLDR category coverage (Czech: one/few/many/other).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:26:55 +02:00
David KasparandGitHub a70e0e6cf9 Merge pull request #3083 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 14:18:12 +02:00
Crowdin Bot a8d8d704c4 New Crowdin translations by GitHub Action 2026-05-28 12:16:37 +00:00
davotoulaandClaude Opus 4.7 b98c18351a i18n: add cs/de/sv translations for music tracks/playlists, video error fallback, wallet reorder
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:14:46 +02:00
David KasparandGitHub 03fef56d08 Merge pull request #3082 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 14:09:54 +02:00
Crowdin Bot e1b5c7e24b New Crowdin translations by GitHub Action 2026-05-28 12:08:43 +00:00
David KasparandGitHub e376f71d0b Merge pull request #3081 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 14:06:28 +02:00
Crowdin Bot a5aefc3233 New Crowdin translations by GitHub Action 2026-05-28 12:05:11 +00:00
davotoulaandClaude Opus 4.7 4596102a66 i18n: add Czech, German, Swedish translations for NIP-82 sections, music upload banner, send
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:01:26 +02:00
David KasparandGitHub 196175691e Merge pull request #3080 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-28 13:57:42 +02:00
Crowdin Bot 648f517a3f New Crowdin translations by GitHub Action 2026-05-28 11:38:10 +00:00
David KasparandGitHub 38fe2861b0 Merge pull request #3079 from davotoula/fix/namecoin-password-toggle-icon
Use distinct icons for password visibility toggle
2026-05-28 13:36:13 +02:00
davotoula ace6e979d9 fix(namecoin): use distinct icons for password visibility toggle
The trailing icon used MaterialSymbols.Lock in both branches of the
visibility conditional, making it a no-op. Switch to Visibility /
VisibilityOff to match the AccountBackupScreen convention.
2026-05-28 12:01:45 +02:00
Claude ae05bb9abd refactor(feeds): hoist auto-stick into Saveable* wrappers
Pulls StickToTopOnPrepend out of every per-feed callsite and into
SaveableFeedContentState, SaveableGridFeedContentState, SaveableFeedState,
and SaveableGridFeedState — the same wrappers that already own
WatchScrollToTop. A new FeedContentState-keyed overload derives the head
key from feedContent → Loaded.feed → list.firstOrNull()?.idHex, so the
wrappers can wire auto-stick without any per-feed plumbing.

Removes the explicit StickToTopOnPrepend calls from FeedLoaded,
PictureFeedLoaded, ArticlesFeedLoaded, NestsFeedLoaded,
WebBookmarksFeedLoaded, GalleryFeedLoaded, DiscoverFeedLoaded,
DiscoverFeedColumnsLoaded, and ChatroomListFeedView — they all consume
listStates created by one of the four wrappers above.

Kept as explicit calls:
- UserFeedView (custom listState, no wrapper)
- CardFeedView (CardFeedContentState — different type)
- TabNotesNewThreads (custom listState, no wrapper)
- BrowseEmojiSetsScreen (doesn't use SaveableGridFeedContentState)

Also extracts the list/grid bodies to a private stickToTopOnPrepend
core that takes the state object as the LaunchedEffect key plus
lambdas for sampling / scroll, and switches the cached flag from
mutableStateOf to a plain BooleanArray holder (read only from effects,
never composition — no snapshot tracking needed). Adds the missing
"why isScrollInProgress gating is safe" line to the KDoc.
2026-05-28 02:09:40 +00:00
Claude ac3f351458 feat(feeds): stick to top when items prepend and user is at top
Adds StickToTopOnPrepend, a Compose helper that auto-scrolls back to
index 0 whenever new items land at the head of a feed — but only if the
user was already at the very top right before the update. Wired into
every FeedLoaded variant: Home/Hashtag (FeedLoaded), Notifications
(CardFeedView), Pictures, Articles, Discover (list + grid),
WebBookmarks, ProfileGallery, BrowseEmojiSets, Chatroom list,
UserFeed, NestsFeed, and TabNotesNewThreads.

Why this was broken: every feed uses stable key = item.idHex, so when N
items prepend Compose preserves the user's visual anchor by shifting
firstVisibleItemIndex from 0 to N. The existing
WatchScrollToTop only fires on explicit tab-bar taps, and the
LaunchedEffect(items.firstOrNull()) { if (firstVisibleItemIndex <= 1) }
pattern (used in ChatFeedView and PublicChatsFeedLoaded) breaks the
moment more than one item arrives in the same batch.

How the helper avoids the race: it tracks "was at top" continuously via
snapshotFlow but only flips it true → false when isScrollInProgress is
true. Data-driven index shifts happen with isScrollInProgress == false,
so they never poison the cached value. When firstItemKey changes and
the cached value is still true, we snap back to 0 with an instant
(non-animated) scroll so the prepend appears as in-place growth instead
of a visible jump-then-scroll.

ChatFeedView is left alone — it already works because reverseLayout
masks the prepend shift.
2026-05-27 23:35:56 +00:00
Vitor PamplonaandGitHub 522a83c4de Merge pull request #3078 from vitorpamplona/claude/pensive-turing-6UC1W
Add dedicated NIP-82 software app detail screen
2026-05-27 18:38:03 -04:00
Claude a2b4e5fa70 feat(nip82): compact apps feed card + dedicated detail screen
Rework the NIP-82 Software Applications feed item so it scans cleanly at
list density and split the detail content into its own route.

Feed card (RenderSoftwareApplication): icon + name + summary, full
description (3 lines), platforms / license chips, and a latest-version
chip resolved from LocalCache. Drops the screenshots strip, website /
repo link rows, and #topic chips at feed scale. The card is tappable
and the standard ReactionsRow now hosts replies, boosts, likes, zaps,
and share underneath each card.

New SoftwareAppDetailScreen (Route.SoftwareAppDetail) reached via the
card tap, the routeFor() dispatch, and naddr deep links. Layout: 72dp
header, screenshots carousel, About, Platforms, Topics (#tag chips
clickable through to Route.Hashtag), Links, ReactionsRow, latest
release with bundled assets, a collapsible "show older releases"
section, and the NIP-22 comment thread inline (driven off the app's
address tag through ThreadFeedViewModel + ThreadFilterAssembler).
2026-05-27 22:23:59 +00:00
Vitor PamplonaandGitHub 097f9a9a5b Merge pull request #3077 from mstrofnone/feat/desktop-namecoin-core-rpc-backend
feat(desktop): Namecoin Core RPC backend + composite fallback
2026-05-27 16:07:01 -04:00
m d9d7b44e91 feat(desktop): Namecoin Core RPC backend + composite fallback
Brings Amethyst Desktop to feature parity with Android for the Namecoin
resolution backend stack landed in #3056 / #3068. Three pieces:

- DesktopNamecoinPreferences now persists backend, namecoinCoreRpc,
  fallbackToCustomElectrumx and fallbackToDefaultElectrumx (KEY_BACKEND
  / KEY_CORE_RPC / KEY_FALLBACK_*), mirroring NamecoinSharedPreferences.
  Mutators are non-suspend because java.util.prefs is synchronous,
  unlike Android's coroutine-backed DataStore. The Jackson mapper now
  rejects unknown properties on read so kotlinx `@Serializable`
  computed getters (e.g. NamecoinCoreRpcConfig.isUsable) round-trip
  cleanly through java.util.prefs.

- DesktopNamecoinNameService takes an optional OkHttpClient provider,
  lazily constructs a NamecoinCoreRpcClient when supplied, and builds
  a fresh CompositeNamecoinBackend per lookup based on current
  NamecoinSettings. Same shape as AppModules#buildNamecoinBackend.
  Exposes the underlying RPC client (rpcClient) and a probeCoreRpc(cfg)
  helper for the Settings Test RPC button.

- NamecoinSettingsSection gains a backend radio selector, a Core RPC
  subform (URL / username / password / Save / Test RPC) with a TOFU
  cert-pin AlertDialog mirroring Android's NamecoinCoreRpcSection, and
  a fallback toggles section. The same KEY_PINNED_CERTS list is shared
  with both ElectrumXClient and NamecoinCoreRpcClient via
  setDynamicCerts(...), matching Android's behaviour where both
  backends consume one trust store.

- Main.kt wires DesktopHttpClient.currentClient() in as the Core RPC
  HTTP provider so .onion RPC URLs flow through the existing Tor
  routing without extra plumbing, and propagates the new mutators to
  the Settings UI.

- Extends DesktopNamecoinPreferencesTest with 8 new cases covering
  default state, backend round-trip, Core RPC URL/user/pass/pin-flag
  round-trip, fallback toggles, reset clearing, and a full
  multi-field round-trip across a fresh preferences instance.

Verification on the canonical workspace clone:
- ./gradlew :commons:jvmTest --tests *Namecoin* — BUILD SUCCESSFUL
- ./gradlew :amethyst:compilePlayDebugKotlin — BUILD SUCCESSFUL
- ./gradlew :desktopApp:compileKotlin :desktopApp:test — BUILD SUCCESSFUL
- ./gradlew :amethyst:testPlayDebugUnitTest --tests *Namecoin* — BUILD SUCCESSFUL
- ./gradlew :amethyst:spotlessCheck :commons:spotlessCheck
  :desktopApp:spotlessCheck — BUILD SUCCESSFUL
2026-05-28 06:01:54 +10:00
Vitor PamplonaandGitHub f898e4be57 Merge pull request #3067 from vitorpamplona/claude/confident-allen-AOGU6
Add music tracks and playlists support with NIP-51 events
2026-05-27 15:59:41 -04:00
Claude bf02a235e0 Merge remote-tracking branch 'origin/main' into claude/confident-allen-AOGU6
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
2026-05-27 19:55:07 +00:00
Vitor PamplonaandGitHub 08520f553f Merge pull request #3076 from mstrofnone/feat/desktop-namecoin-pinned-certs
feat(desktop): persist TOFU-pinned Namecoin ElectrumX certs
2026-05-27 15:45:19 -04:00
m b5b70fe693 feat(desktop): persist TOFU-pinned Namecoin ElectrumX certs
Mirrors Android's NamecoinSharedPreferences pinned-cert API on Desktop so
user-accepted TLS pins survive process restart. Same JSON-list shape, same
distinct-append semantics, same wipe-on-reset behaviour.

What's new
- DesktopNamecoinPreferences gains addPinnedCert / loadPinnedCerts /
  clearPinnedCerts (sync rather than suspend, since java.util.prefs is
  synchronous). reset() now clears pinned certs too, matching Android.
- DesktopNamecoinNameService accepts a pinnedCertsProvider and pushes the
  loaded list into ElectrumXClient.setDynamicCerts at init, mirroring
  Android's AppModules.kt wiring. Exposes the underlying client so the
  Settings UI can call testServer() and re-apply pins live.
- Desktop NamecoinSettingsSection grows an optional Test Connection + TOFU
  pin sub-section: runs ElectrumXClient.testServer per active server,
  collects PEM + SHA-256 fingerprint from successful TLS handshakes, and
  prompts the user to pin each new cert via AlertDialog. UI hidden when
  no service is wired (so existing call sites stay valid).
- Main.kt wires both halves together and updates the freshly-pinned cert
  list into the live client without waiting for restart.

Persistence is plain java.util.prefs (same backing store as the rest of
DesktopNamecoinPreferences) — explicitly NOT EncryptedSharedPreferences.
Pinned cert PEMs are public material; no secrets stored.

Tests
- DesktopNamecoinPreferencesTest: +6 cases covering empty default,
  persistence + reload, dedup, blank input ignored, reset wipes, and
  independence from settings copies.

Verification
- ./gradlew :desktopApp:compileKotlin — BUILD SUCCESSFUL
- ./gradlew :desktopApp:test — BUILD SUCCESSFUL (16 tests, 0 failures)
- ./gradlew :amethyst:compilePlayDebugKotlin — BUILD SUCCESSFUL
- ./gradlew :amethyst:spotlessCheck :commons:spotlessCheck :desktopApp:spotlessCheck — BUILD SUCCESSFUL

Stack note
Stacked behind #3072 (merged 2026-05-27). Next: PR-C for the full
Namecoin Core RPC backend + composite fallback persistence + UI.
2026-05-28 05:41:22 +10:00
David KasparandGitHub 7c4220ba3d Merge pull request #3073 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-27 21:41:18 +02:00
Claude 8a55bba1f2 feat(music): parallel uploads + survive-screen-leave + visible progress
Two issues with the send flow on the new-music-track screen:

 1. Cover + audio uploaded sequentially. As cover finished, its picker
    reverted to the placeholder while audio was still going, making the
    user think the operation was done.
 2. The coroutine ran on the screen's viewModelScope, so leaving the
    screen cancelled it.

Rework the upload pipeline:

 * Snapshot the form into an immutable SendSnapshot before launching, so
   user edits to the form during the in-flight upload don't poison what
   gets published, and the coroutine sees stable inputs even after the
   per-screen VM is cleared.
 * Run cover + audio uploads in parallel via async/awaitAll. Either
   side failing throws an UploadException that the outer catch routes
   to the global toast manager.
 * Launch through accountViewModel.launchSigner so the work runs on the
   AccountViewModel.viewModelScope — survives screen leave. Errors and
   success both surface as global toasts.
 * Keep coverMedia/audioMedia/pickedAudioName populated through the
   ENTIRE upload+publish, not just per-phase. Only clear them on
   success, so each picker keeps showing the user the file it's still
   uploading.
 * Single isSending flag replaces the separate isUploading/isPublishing
   booleans; drives an inline progress banner + disables the Send
   button + dims the pickers to read-only while in flight.
 * One-shot completionEvents SharedFlow that the screen subscribes to.
   If the user is still on the screen when the operation finishes, we
   popBack; if they've already left, no one is listening and we don't
   pop someone else's stack frame.

Also tighten the playback Row height in MusicTrack from 100dp to 80dp.
With a 75dp play button the previous 100dp container left a ~12.5dp
empty band top + bottom that the user flagged as too tall.
2026-05-27 19:39:50 +00:00
Vitor PamplonaandGitHub 80991c6c8b Merge pull request #3074 from vitorpamplona/claude/gallant-darwin-VfniN
Fix route hint re-firing on activity recreation
2026-05-27 15:33:28 -04:00
Vitor PamplonaandGitHub e937b460e1 Merge pull request #3072 from mstrofnone/feat/commons-namecoin-settings-extension
refactor(namecoin): consolidate NamecoinSettings into commons
2026-05-27 15:31:19 -04:00
Claude 187cb407c9 fix(new-user): stop Import Follow List from reappearing after dismiss
The route hint set by newKey() (Route.ImportFollowsSelectUser) was stored
on AccountState.LoggedIn and never cleared. LoggedInPage re-assigned
accountViewModel.firstRoute = route on every recomposition, so any
Activity recreation (rotation, dark-mode toggle, returning from
background) made the screen pop up again even after the user dismissed
it via popBack.

Consume the route once via remember(state) in AccountScreen, and gate the
firstRoute assignment behind LaunchedEffect(Unit) so it only fires once
per composition lifetime.
2026-05-27 19:26:28 +00:00
Claude 3082095a4b feat(music): auto-fill metadata from picked audio + drop audio URL field
Five changes:
 1. Drop the audio-URL text field from the composer. Upload tile at the
    top is the single source of truth; the ViewModel still threads the
    existing url through MusicTrackEvent.edit so edit mode keeps it.
 2. Auto-fill title / artist / album / duration from MediaMetadataRetriever
    when an audio file is picked. Only fills empty fields — anything the
    user already typed wins. Duration is the exception: derived numbers
    overwrite, since they're not opinions.
 3. Rename the SavingTopBar 'Save' button to 'Send' for this screen
    (introduces SendingTopBar + R.string.send so other screens can reuse).
 4. Fix silent data corruption in AddToMusicPlaylistViewModel.init:
    the previous `if account already set, return` guard kept the FIRST
    track address the VM ever saw. Subsequent invocations with a
    different trackAddress (process recreation, navigation reusing the
    back-stack entry) routed toggle() at the stale track, adding the
    wrong track to playlists. Now refreshes trackAddress on every call
    and restarts the scan job when it changes.
 5. Inset modifier order on NewMusicTrackScreen tightened in the prior
    commit, kept here for continuity.
2026-05-27 19:18:41 +00:00
Claude 9c9cf8844d fix(music): save audio to MediaStore.Audio (not Video) and trim composer
Three fixes in one:

1. MediaSaverToDisk crash when saving a music URL. The fall-through
   branch routed every non-image, non-PDF MIME to
   MediaStore.Video.EXTERNAL_CONTENT_URI; an audio/mpeg blob inserted
   into the Video collection fails with IllegalArgumentException. Add a
   dedicated audio/* branch that writes to MediaStore.Audio with
   DIRECTORY_MUSIC.

2. Drop the redundant cover-URL text field from the new-music-track
   composer. The upload tile at the top is the source of truth for the
   cover — having a second field at the bottom asked the user to set
   the same thing twice. ViewModel still threads coverUrl through to
   MusicTrackEvent.edit so editing an existing track keeps its image.

3. Tighten the new-music-track Scaffold's inset modifier order:
   padding(pad) -> consumeWindowInsets(pad) -> imePadding() ->
   horizontal padding -> verticalScroll. Without consumeWindowInsets,
   imePadding double-counts the nav-bar inset on Android 11+ and the
   bottom field gets shoved too far up when the keyboard opens.
2026-05-27 19:08:54 +00:00
Crowdin Bot 2c9d9c3129 New Crowdin translations by GitHub Action 2026-05-27 19:08:46 +00:00
Vitor PamplonaandGitHub 58f1ee9606 Merge pull request #3069 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-27 15:07:14 -04:00
Vitor PamplonaandGitHub 63b91012fb Merge pull request #3071 from vitorpamplona/claude/trusting-mccarthy-SA1aI
Add Software Apps feed with follow list filtering
2026-05-27 15:07:05 -04:00
m 67edb32fa7 refactor(namecoin): consolidate NamecoinSettings into commons
Two NamecoinSettings classes had drifted:

- commons (used by Desktop): only enabled + customServers
- amethyst service.namecoin (used by Android): full schema with backend,
  namecoinCoreRpc, fallbackToCustomElectrumx, fallbackToDefaultElectrumx

This left Desktop unable to persist any of the Namecoin Core RPC or
fallback-policy state introduced in the Android settings UI. Promote
the rich Android version into commons as the single source of truth
and delete the Android duplicate.

- Move the rich schema (backend, namecoinCoreRpc, fallback toggles,
  hasUsableCoreRpc, toFallbackPolicy) into the commons NamecoinSettings.
- Delete amethyst/service/namecoin/NamecoinSettings.kt and its test.
- Repoint the two Android imports (NamecoinSharedPreferences,
  NamecoinSettingsSection) at the commons class. No behaviour change on
  Android.
- Fold the Android-only backend/RPC/fallback test cases into the commons
  NamecoinSettingsTest so the shared schema stays covered.

Desktop persistence (DesktopNamecoinPreferences) still only reads/writes
enabled + customServers; the extra commons fields fall back to defaults
on the existing Desktop store. Wiring those new fields into Desktop is
the next change.
2026-05-28 05:06:44 +10:00
Vitor PamplonaandGitHub 7692bee0a5 Merge pull request #3070 from mstrofnone/fix/namecoin-search-id-d-prefixes
fix(search): route d/ and id/ Namecoin namespaces through the resolution row
2026-05-27 15:06:10 -04:00
m 216176eeb7 fix(search): route d/ and id/ Namecoin namespaces through the resolution row
The inline Namecoin resolution row in the global search bar (and the
on-chain zap recipient field) is gated by looksLikeNamecoinIdentifier,
which previously only matched the '.bit' shapes. Direct namespace
references like 'id/mstrofnone' or 'd/mstrofnone' — both accepted by
NamecoinNameResolver.isNamecoinIdentifier — fell through the gate and
the resolver was never called, so no on-chain feedback appeared in the
search bar.

Bring the UI gate in line with the resolver:

  - accept 'd/<name>' (domain namespace, direct reference)
  - accept 'id/<name>' (identity namespace)
  - lower the length floor so single-character labels (valid, if
    expensive, on chain) still trigger; '.bit' inputs keep their
    5-char floor

Doc comment for the row's behaviour and the NamecoinResolutionRow
KDoc are updated to reflect the broader accepted set. New unit tests
cover both new prefixes (with case-insensitivity and leading '@'),
short single-label names, bare-prefix rejection, and explicit
non-routing for other Namecoin namespaces ('a/', 'u/').
2026-05-28 04:46:55 +10:00
Claude ea227f1968 fix(music): playlist cover chip is wrap-content again, not full-cover
The previous cover used SubcomposeAsyncImage with a single content lambda
that stacked the loaded image and the chip as BoxScope siblings. Under
Coil's subcompose-layout pass the chip's wrap-content Box got measured
to the cover's full size, painting a translucent black square over the
whole artwork.

Drop SubcomposeAsyncImage. Use rememberAsyncImagePainter + a plain
Image + painter-state observation inside a normal Box. The image fills
the box (Modifier.matchParentSize) and the chip sits as a normal
BoxScope child with Modifier.align(BottomStart).padding(12.dp) —
predictable wrap-content sizing.
2026-05-27 18:33:22 +00:00
Claude 506ed94bfd fix(software-apps): add top-nav filter and route NIP-82 events through LocalCache
The Apps screen was missing the top-nav follow-list spinner that other
single-feed screens (Articles, Longs, Pictures) use, and nothing was
loading because the relay subscription only queried the user's own
outbox and `SoftwareApplicationEvent` (kind 32267) was never consumed
by `LocalCache.justConsumeInnerInner` — it fell through to the
"Event Not Supported" else branch and got dropped. `ReleaseArtifactSetEvent`
(kind 30063) and `SoftwareAssetEvent` (kind 3063) had the same gap.

- Register `SoftwareApplicationEvent`, `SoftwareAssetEvent`, and
  `ReleaseArtifactSetEvent` in the `LocalCache` consume dispatch.
- Add `defaultSoftwareAppsFollowList` setting (Account/Settings/Prefs)
  and `liveSoftwareAppsFollowLists{,PerRelay}` flows.
- Reshape `SoftwareAppsSubAssembler` to extend
  `PerUserAndFollowListEoseManager<_, TopFilter>` and assemble per-list
  relay filters via `makeSoftwareAppsFilter`, mirroring Pictures/Longs.
- Add a `SoftwareAppsTopBar` with the standard `FeedFilterSpinner` and
  a `WatchAccountForSoftwareAppsScreen` to invalidate the DAL when the
  selected list changes.
- DAL now applies `FilterByListParams` so the rendered feed honors the
  active top-nav list.
2026-05-27 18:25:39 +00:00
Crowdin Bot 2b6798e78a New Crowdin translations by GitHub Action 2026-05-27 18:25:22 +00:00