Commit Graph
17679 Commits
Author SHA1 Message Date
Vitor PamplonaandGitHub eddffdbd3e Merge pull request #3702 from vitorpamplona/claude/ots-notes-lifecycle-ok3mz3
Anchor OTS attestations to target notes, replace verification cache
2026-07-24 18:44:29 -04:00
Claude fef1d15f12 fix(ots): never persist the non-terminal Verifying sentinel on the note
verifyOts wrote otsVerification = Verifying before the suspending blockchain
verifyState() call and only overwrote it with the real verdict afterwards. That
verification runs inside LoadOts's cancellable LaunchedEffect, so a row scrolling
off-screen cancels the coroutine at the network suspension point — the verdict
write never happens and the note is left stuck at Verifying. cacheVerifyOts
treats Verifying as terminal, so earliestOtsVerifiedTime then returns null and the
confirmed-timestamp pill silently vanishes for the note's lifetime. The old
VerificationStateCache had the same write but its LRU eventually evicted the stuck
entry; anchoring the verdict on the long-lived note removed that recovery.

Store only terminal verdicts (Verified / Error / NetworkError). A cancelled or
in-flight verification leaves the field null and simply retries on the next read;
the cost is at worst two concurrent first-time verifications, already benign.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014D5fenZbAhCbDiwv7Rtpvj
2026-07-24 22:42:51 +00:00
Vitor PamplonaandGitHub 8e2846b3f5 Merge pull request #3700 from vitorpamplona/claude/chat-picture-sending-consistency-qit0pz
Add image attachment support to minichat threads
2026-07-24 18:39:30 -04:00
Claude a65d2b9342 refactor(ots): anchor OTS attestations on the Note lifecycle, drop the verification cache
NIP-03 OpenTimestamps attestations (kind 1040) were stored loosely in the main
note cache and found via a full-cache scan, with their blockchain verdicts held
in a separate, id-keyed VerificationStateCache LRU. Nothing tied either to the
lifecycle of the note being timestamped, so a deleted/pruned note leaked its
attestations, and consume(OtsEvent) invalidated the attestation's own
(observer-less) flow instead of the target's — so a live-arriving proof never
pinged the target's UI.

Mirror the recent edits→Note migration:

- Note gains a `timestamps` child collection (like `edits`/`reactions`), wired
  into clearChildLinks/removeNote, so an attestation survives exactly as long as
  its target and is collected when the target is pruned or deleted.
- consume(OtsEvent) anchors the proof on its target via the `e` tag and
  invalidates the target's `ots` flow; unlinkAndRemove detaches it symmetrically.
- Each attestation memoizes its own verdict in `Note.otsVerification`, so the
  result shares the note's lifecycle. This replaces VerificationStateCache
  (deleted) and the full-cache scan: the OTS pill now folds `note.timestamps`
  via the new Note.earliestOtsVerifiedTime / cacheVerifyOts helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014D5fenZbAhCbDiwv7Rtpvj
2026-07-24 22:18:27 +00:00
Claude 6ce61f0dc8 Merge remote-tracking branch 'origin/main' into claude/chat-picture-sending-consistency-qit0pz
# Conflicts:
#	commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/actions/ConcordActions.kt
2026-07-24 22:05:36 +00:00
Vitor PamplonaandGitHub 89cbba42a5 Merge pull request #3699 from vitorpamplona/claude/blossom-files-gallery-re25b4
Redesign Blossom blob manager UI with gallery grid and full-screen viewer
2026-07-24 17:49:15 -04:00
Claude 6f40d997c6 feat(minichat): allow sending pictures in thread replies
Every chat composer in Amethyst already had a picture/media attach button
(SelectFromGallery) except the minichat "thread" screen — the kind-1111
reply composer opened from the "N replies" chip — which was text-only. This
brings it to parity with every other chat.

- quartz: ChannelChat.imageReply() — a kind-1111 thread reply carrying
  encrypted image imeta(s), combining reply()'s NIP-22 pointers with
  imageMessage()'s ciphertext-URL/imeta handling (+ round-trip test).
- commons: ConcordActions.buildChannelImageReply().
- Account.sendMinichatReply() now accepts imetas and routes per backend:
  Concord sends an encrypted image reply; NIP-28/NIP-29 public chats append
  the URL to the content and carry a plaintext imeta on the comment; Buzz
  appends the URL to the stream message content.
- Extract toConcordImeta()/toPlainImetas() into a shared UploadImetas.kt so
  the minichat and Concord composers build imeta the same way.
- MinichatScreen: add the SelectFromGallery leading icon + ChatFileUpload
  dialog, encrypting only when the backend is end-to-end (Concord).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8FD5xm8nyEKzd8dk9VfT1
2026-07-24 21:36:05 +00:00
Claude ccd991c847 feat: video thumbnails + full-screen zoomable viewer for Blossom files
Videos in the gallery now show a decoded first frame (Coil
VideoFrameDecoder) with a play badge instead of a generic glyph; the same
preview is reused in the detail header, with a type-glyph fallback for
blobs Coil can't decode (e.g. HLS playlists).

Tapping an image or video tile now opens a full-screen viewer: images are
zoomable/pannable (engawapg zoomable, matching ZoomableImageDialog) and
videos play inline. Its top bar carries back, a new share action (system
share sheet for the blob URL), and a button that reveals the file's
storage matrix and sync/copy/open/share/report/delete actions in a bottom
drawer. Non-visual blobs (PDFs, arbitrary files) still open straight to
that action sheet. The actions list is now a shared BlobActionsContent so
the sheet and the viewer drawer stay identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EDLTQZM6yX13EwaYFNWTz7
2026-07-24 21:26:35 +00:00
Vitor PamplonaandGitHub f8f3631a15 Merge pull request #3698 from vitorpamplona/claude/chat-updates-concord-75jx1g
Anchor message edits to their targets instead of workspace state
2026-07-24 17:08:00 -04:00
Claude 8c5b71c3a5 Merge remote-tracking branch 'origin/main' into claude/chat-updates-concord-75jx1g
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageActionSheet.kt
2026-07-24 20:54:48 +00:00
Claude 3fb44dba60 feat: gallery view for My Blossom Files with per-file detail sheet
Replace the single-column list of large blob cards with an adaptive
thumbnail grid so many files no longer mean an endless scroll. Each tile
shows an image preview (or a type glyph) plus a corner badge summarizing
how many of the user's servers hold it (green check when on all, amber
cloud with a present/total count otherwise).

Tapping a tile opens a bottom sheet with the file's details: hash,
type/size, the per-server storage matrix ("Stored on"), the sync
(mirror-to-missing) button, and the copy/open/report/delete actions that
previously lived behind the card's overflow menu.

The ViewModel is unchanged — only the presentation layer moved from
list-of-cards to grid-plus-detail-sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EDLTQZM6yX13EwaYFNWTz7
2026-07-24 20:38:43 +00:00
Claude 893a270c65 fix(edits): unlink deleted edits from their message; one collector per row
Audit of the three edit paths (feed 1010 / Buzz 40003 / Concord 3302) found:

1. Bug (feed regression): a deleted edit kept overlaying its message. Edits
   anchor on the target's Note.edits with no `replyTo` back-link, and
   removeNote didn't cover `edits`, so unlinkAndRemove never dropped them — the
   old cache-scan resolver dropped deleted edits for free, Note.edits did not.
   Fix: removeNote now also removeEdit()s, and unlinkAndRemove resolves the
   edit's `e`-tag target and unlinks it there (editedTargetIdOf covers all
   three kinds). New test: deleting an edit un-overlays and unlinks it.

2. Perf: every chat row ran two edits-flow collectors (observeConcordEdit +
   observeBuzzEdit). A message is only ever one kind, so they're merged into a
   single observeChatEdit that resolves latestConcordEdit() ?: latestBuzzEdit()
   — one collector per row, dispatched by the winning edit's event type.

3. Nits: latestBuzzEdit now tie-breaks by idHex (deterministic on same-second
   edits, matching Concord); dropped a redundant takeIf in latestConcordEdit.

The author check stays at read time on purpose: an edit can be consumed before
its target loads (author unknown), so an attach-time gate would wrongly drop
early-arriving legit edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 20:18:53 +00:00
Vitor PamplonaandGitHub 5d72a0415c Merge pull request #3696 from vitorpamplona/claude/geode-release-process-q1dp17
Add geode standalone Nostr relay with release pipeline
2026-07-24 16:08:14 -04:00
Claude f9ad60c423 refactor: move edit-overlay resolvers off LocalCache onto Note
findLatestModificationForNote (and the Buzz resolver) were pure Note.edits
filters with no LocalCache state — they only lived there for historical
reasons (back when resolving edits meant scanning the whole cache). Now that
every edit is a hard-referenced child of its message, resolution is a cheap
in-memory fold that belongs on the note.

New NoteEditOverlays.kt collects all three as Note extensions, so every edit
kind resolves the same way and none touches LocalCache:
- Note.textNoteModifications() (1010, author-only + NIP-40, version list)
- Note.latestBuzzEdit() (40003, author-only, newest by created_at)
- Note.latestConcordEdit() (3302, author-only, newest by CORD-02 send time)

Callers updated: observeEdits, observeNoteModifications, observeBuzzEdit,
observeConcordEdit, and the Buzz test. observeConcordEdit also drops its
early-return-before-produceState guards (a conditional-hook hazard) since the
resolver returns null for a non-Concord note anyway. LocalCache no longer
carries any edit-filtering logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 19:59:54 +00:00
Claude 9fa1d769f9 refactor: drop cachedModificationEventsForNote, now a redundant alias
It existed to serve observeEdits a cheap synchronous value (an LRU read)
distinct from the expensive IO-only findLatestModificationForNote cache scan.
Since edits fold from Note.edits, findLatestModificationForNote is itself
cheap and thread-safe, and cachedModificationEventsForNote had become a plain
alias for it. observeEdits now calls findLatestModificationForNote directly —
the same function observeNoteModifications already uses — and the LocalCache +
AccountViewModel aliases are removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 19:38:11 +00:00
Claude e8739f5e83 ci: run geode tests in a dedicated isolated job
The first cut appended :geode:test to the build-desktop matrix command.
That was wrong twice over: geode is JVM-only, so it ran 3× across the
ubuntu/macos/windows matrix, and — because org.gradle.parallel=true —
its default suite's CPU-heavy throughput benchmarks (a 1M-event mirror
sync, WireReqFloor, NegentropyServerReconcile) ran concurrently with the
timing-sensitive quartz relay-client tests, flaking
NostrClientReqBypassingRelayLimitsTest.denseSecondBeyondCapIsSteppedPastWithoutStalling.

Move :geode:test into its own test-geode job (needs: lint, ubuntu, JVM
21) so it runs once and its benchmark load can't starve another module's
timing assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KCdJwdhGtmLZ12ViS56S3k
2026-07-24 19:37:09 +00:00
Vitor PamplonaandGitHub 99af7f58a3 Merge pull request #3695 from davotoula/fix/exoplayer-ownership-leak
Stop the decoder-budget counter from leaking and disabling the warm player cache
2026-07-24 15:36:07 -04:00
Claude 0a30231196 feat(geode): add a release & distribution pipeline
geode was runnable only via ./gradlew :geode:run and was absent from CI.
Give it the same release process as the amy CLI (it's the same kind of
application-plugin JVM module), plus the pieces a long-running server
daemon needs that a one-shot CLI does not.

- Main.kt: add terminal --version/-V and --help/-h flags so a packaged
  binary has a fast, exit-0 command (Homebrew test block, package smoke
  checks, Docker healthcheck).
- build.gradle.kts: jlinkRuntime + geodeImage (portable flat app-image
  with a bundled JRE, plus config.example.toml + geode.service under
  share/) + jpackageDeb/jpackageRpm, mirroring cli/. No Compose to
  exclude — geode depends only on :quartz.
- Dockerfile + .dockerignore: multi-stage image (gradle installDist ->
  temurin JRE), the primary channel for relay operators.
- packaging/: systemd unit, macOS hardened-runtime entitlements, and a
  reference Homebrew formula.
- scripts/asset-name.sh: geode_asset_name/collect_geode_assets under the
  canonical geode-<version>-<family>-<arch>.<ext> scheme.
- create-release.yml: build-geode matrix (tarball + deb/rpm + no-JRE jvm
  bundle, with a serve+NIP-11 smoke test of the jlink image) and a
  docker-geode job pushing ghcr.io/<owner>/geode:<version> (+ :latest).
- bump-homebrew-geode-formula.yml: auto-sync the reference formula on
  stable releases.
- build.yml: run :geode:test in CI (it ran in no workflow before).
- README.md + plans/2026-07-24-geode-release.md: operator docs + design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KCdJwdhGtmLZ12ViS56S3k
2026-07-24 17:56:50 +00:00
Claude 8dcee46cdf fix(buzz): only the author's own kind-40003 edit may rewrite a message
The Buzz edit overlay applied the newest kind-40003 by created_at regardless
of who signed it, so a 40003 signed by anyone — targeting someone else's
message — would rewrite that message in every reader's UI. The send side
already gates Edit to your own messages, but the apply side re-checked
nothing and effectively trusted the relay to reject cross-author edits.

Enforce author-only on apply, matching feed (1010) and Concord (3302) edits:
observeBuzzEdit now resolves through LocalCache.findLatestBuzzEditForNote,
which keeps only edits whose author is the original message's author (newest
by created_at — Buzz's own last-write-wins rule otherwise). There is no Buzz
feature that edits another user's message; moderation is delete/hide.

Added a test: a verified forged edit by a different author lands in the store
but never overrides the message, while the real author's later edit does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 17:54:23 +00:00
Claude 98d8f88c0a refactor: unify all message edits onto Note.edits
All three edit kinds now anchor on the message they edit via the same
Note.edits collection, instead of each maintaining its own store:

- Feed edits (kind 1010): consume(TextNoteModificationEvent) calls
  editedNote.addEdit(note); findLatestModificationForNote folds note.edits
  (author-only, NIP-40 expiry) instead of scanning the whole cache. Drops
  the O(all-notes) scan and the 20-entry modificationCache LRU;
  cachedModificationEventsForNote is now synchronous (no Loading state).
- Buzz edits (kind 40003): consume(StreamMessageEditEvent) calls
  target.addEdit(note); observeBuzzEdit reads note.edits (newest by
  created_at, no author gate — Buzz's own rule). Removes the channel-keyed
  BuzzWorkspaceState edit store, its editUpdates/editFor/effectiveContentFor/
  addEdit and the pruneEdits reaping (edits now prune with their message).
- Concord edits (kind 3302): already on note.edits.

Each reader keeps its own semantics by filtering note.edits on its event
type; the shared field only unifies storage + lifecycle, so an edit lives
exactly as long as the message it edits. Buzz edit tests rewritten against
note.edits (6/6 green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 17:39:03 +00:00
davotoula 874209ab39 Code review:
- don't double-return the player if register() throws
- guarantee pool teardown even if a session retire throws
- simplify pool ownership code, one funnel and paired counters
2026-07-24 19:18:34 +02:00
davotoula 0bd08bf013 Manual testing fixes:
- fix(playback): remove pool from livePools after its own teardown
- fix(playback): floor decoder decrements and report teardown drift
- fix(playback): own the player across the acquire-to-register window
- fix(playback): give every pooled player exactly one owner
2026-07-24 19:18:34 +02:00
davotoula bb41a867a4 feat(playback): add SessionRegistry owning session reachability
test(playback): pin SessionRegistry's identity-vs-equality drop guard
2026-07-24 19:18:34 +02:00
Vitor PamplonaandGitHub 94210d202d Merge pull request #3694 from vitorpamplona/claude/remove-module-warnings-s7v2q0
Refactor persistent collections API and fix deprecation warnings
2026-07-24 13:03:07 -04:00
Claude a7748dfda5 fix: clear compiler warnings across all modules
Sweep of Kotlin compiler warnings in every module's main source sets
(quartz, commons, cli, desktopApp, amethyst, nappletHost).

Genuine code fixes:
- Drop unnecessary !!/safe-calls and redundant elvis/casts (OkHttp's
  now-non-null `body`, smart-cast callbacks, non-null String receivers).
- Remove provably-redundant conditions (`canvas == null` after a
  non-null content check; `account != null` implied by `canModerate`).
- Migrate deprecated kotlinx.collections.immutable persistent ops
  (add/remove/put/addAll -> adding/removing/putting/addingAll).
- Migrate LocalClipboardManager -> LocalClipboard (+ scoped setText),
  ContextCompat.startActivity -> context.startActivity, TabRow ->
  SecondaryTabRow, and @ConsistentCopyVisibility on a private-ctor data class.
- Delete dead ReceiveDialog.onGenerate param (never invoked).
- Fix a platform-Boolean type-mismatch on a ThreadLocal read.

Deprecations with no available successor are narrowly @Suppress-ed with
a reason: androidx.security.crypto (EncryptedSharedPreferences/MasterKey),
androidx.privacysandbox.ui, WebView.databaseEnabled, BluetoothDevice
.connectGatt, media3 setEnableAudioTrackPlaybackParams, FirebaseMessaging
.token, and InputMethodManager.SHOW_IMPLICIT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xb9YbBqhdZsHxMzitmyvn
2026-07-24 16:36:17 +00:00
Vitor PamplonaandGitHub 32d0105744 Merge pull request #3693 from vitorpamplona/claude/changelog-0-13-0-sht28q
docs: update v1.13.00 changelog with Buzz, Notifications, and more
2026-07-24 12:35:22 -04:00
Vitor PamplonaandGitHub 62e7d9bb7a Merge pull request #3692 from vitorpamplona/claude/geode-quartz-eventstore-selection-zht375
Add pluggable event store backend selection via StoreFactory
2026-07-24 12:30:49 -04:00
Claude 7d527bdce3 feat(geode): let operators pick any quartz IEventStore backend
Geode hard-wired the SQLite EventStore. Add a `[database].backend`
selector (and `--store` CLI flag) so an operator can choose the store
implementation:

  - "sqlite" (default): the SQLite EventStore, unchanged.
  - "fs": quartz's filesystem FsEventStore, rooted at [database].file.
  - any other value: a fully-qualified class name of a custom
    IEventStore on the classpath, instantiated reflectively via one of
    `(NormalizedRelayUrl?, IndexingStrategy)`, `(NormalizedRelayUrl?)`,
    or `()` — the "plug in anything" escape hatch.

Store construction moves into a new StoreFactory (mirrors cli's
StoreFactory) shared by the serve path and the import/export verbs, so
both open the same store from the same config. The SQLite-only
`PRAGMA optimize` maintenance loop now runs only when the resolved
store is the SQLite one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GG3TBvLUv5uB5js1naG5sc
2026-07-24 16:15:21 +00:00
Claude fa2179ac19 docs(changelog): fold post-2026-07-20 work into v1.13.0 notes
Adds Buzz agent workspaces, the push-notification redesign, notifications
tab paging, desktop moderation & safety plus NIP-88 polls, DM report
warnings, Concord cold-boot/rank-gating fixes, SQLite search/relay
performance, amy git NIP-34 parity and amy buzz, the Buzz quartz protocol
surface, geode index work, and JVM 17 / CI / bidi build items.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018o1pqCDfqqSu2UHRPuqTK1
2026-07-24 15:50:08 +00:00
Claude 2213031f9f fix(concord): anchor edits to their message so they survive cache eviction
LocalCache.notes is a soft cache, and a Concord rumor is decrypted exactly
once per session (the community session dedups re-delivered wraps), so a
kind-3302 edit left orphaned there could be GC'd on navigation and never
re-downloaded — the message would silently revert to its pre-edit text.
Resetting the channel EOSE doesn't help: the re-delivered wrap is swallowed
by the session's isNew dedup, so its rumor never re-emits.

Fix it the way reactions/replies already survive: attach the edit to the
message it edits. consume(ConcordChatEditEvent) now calls target.addEdit(note),
so the edit is held for exactly as long as its channel-retained message (and
released with it via clearChildLinks). observeConcordEdit reads note.edits
directly — author-matching, latest by CORD-02 §4 send time — instead of
scanning/observing the soft cache.

- Note: new hard-held `edits` collection + addEdit/removeEdit, wired into
  clearChildLinks like the other child-event links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HdLnAa4Pa1pFV9FTYVTB6
2026-07-24 15:48:29 +00:00
Vitor PamplonaandGitHub c6fd7095d4 Merge pull request #3690 from davotoula/fix/localcache-duplicate-streammessage-branch
Remove unreachable duplicate StreamMessageV2Event branch in computeReplyTo
2026-07-24 11:37:50 -04:00
Vitor PamplonaandGitHub c5369da6db Merge pull request #3691 from vitorpamplona/claude/calendar-rsvp-buttons-layout-408xgu
Improve RSVP button layout and text handling
2026-07-24 10:09:25 -04:00
David KasparandGitHub 8a61e8c5ff Merge pull request #3689 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-24 16:02:35 +02:00
vitorpamplonaandgithub-actions[bot] d15c0d1eab chore: sync Crowdin translations and seed translator npub placeholders 2026-07-24 12:54:13 +00:00
Vitor PamplonaandGitHub 7c1300b735 Merge pull request #3687 from nrobi144/feat/desktop-moderation-safety
feat(desktop): Moderation & Safety — mute/block enforcement, NIP-56 report, NIP-36 content warning
2026-07-24 08:51:16 -04:00
davotoulaandClaude Opus 4.8 34ceaafa78 fix(buzz): remove unreachable duplicate StreamMessageV2Event branch in computeReplyTo
LocalCache.computeReplyTo had two `is StreamMessageV2Event ->` branches in the
same `when`. The `when` matches the first, so the second (the older
buzzThreadRoot+buzzThreadReply variant) was dead code — the condition Sonar
flags as duplicating the earlier one. The surviving first branch is the newer,
deliberate Concord-style threading behavior that links a 40002 thread reply into
its parent's replies via buzzThreadReply. Also drops the now-unused
buzzThreadRoot import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoYFvsXvPigRWebuoGiRoG
2026-07-24 14:38:02 +02:00
davotoula f02490a078 Merge remote-tracking branch 'upstream/main' into main-upstream 2026-07-24 14:24:49 +02:00
David KasparandGitHub 6fb78c33b6 Merge pull request #3688 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-24 14:10:22 +02:00
davotoula 5aab994649 update cs,sv,de,pt 2026-07-24 13:07:57 +02:00
davotoulaandgithub-actions[bot] 611f030207 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-24 10:52:56 +00:00
David KasparandGitHub bf8c2413a9 Merge pull request #3681 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-24 12:50:08 +02:00
nrobi144andClaude Opus 4.8 1a0b9eb2d9 docs(desktop): moderation test results (PASS) with published-report logs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 12:17:28 +03:00
nrobi144andClaude Opus 4.8 55d11acd61 feat(desktop): unify note ⋮ + right-click menus and add snackbar feedback
- rememberNoteMenuActions is now the single source for the note menu; both the
  ⋮ overflow (ShareMenu) and the feed right-click ContextMenuArea render the same
  items (copy ×5 / broadcast / mute / report).
- LocalSnackbarHost exposes the app SnackbarHostState so moderation actions show a
  confirmation: 'Muted user', 'Report sent', 'Reported & muted', 'Broadcast to
  relays', and failure toasts. Wired for the note menu + profile ⋮ actions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 12:04:23 +03:00
nrobi144andClaude Opus 4.8 c555265f0b docs(desktop): moderation UX + reporting manual testing sheet
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:57:03 +03:00
nrobi144andClaude Opus 4.8 65579ffc18 feat(desktop): log moderation publishes (report/mute) + surface zero-relay sends
Report/mute actions published silently with no feedback. Add a DesktopModeration
logger: every report/mute logs kind+id+relay-count on publish, WARNs when there
are 0 connected relays (so a dropped publish is visible instead of silent), and
signing/publish failures are caught + logged (were swallowed by the launching scope).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:55:36 +03:00
nrobi144andClaude Opus 4.8 df442bf7b5 fix(desktop): discoverable note moderation — right-click menu + ⋮ overflow
Manual testing showed the note moderation actions were hard to find:
- Provide LocalDesktopIAccount on MainContent's own (non-null) provider so every
  deck/feed/profile/settings surface reliably resolves the account.
- Add a right-click ContextMenuArea on feed notes: Mute user / Report… / Copy text
  (moderation items for other authors on a writeable account).
- Swap the note action-row Share icon for a MoreVert (⋮) overflow — the menu it
  opens already carries copy/broadcast + mute/report, matching the profile ⋮.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:51:51 +03:00
vitorpamplonaandgithub-actions[bot] 7cc4c63996 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-24 04:32:24 +00:00
Vitor PamplonaandGitHub 313e04b7df Merge pull request #3683 from vitorpamplona/claude/push-notification-design-fzyutz
Redesign push notifications with per-kind styles and channels
2026-07-24 00:29:22 -04:00
Claude 02c59b332e fix: keep calendar RSVP buttons on a single line
The Going / Maybe / Can't go buttons in the calendar feed card wrapped
to two lines because the default button content padding (24dp horizontal)
left no room for "Can't go" in an equal-weight third of the row.

Tighten the content padding, shrink the row spacing, and constrain each
label to a single line so the three buttons fit horizontally.
2026-07-24 04:04:07 +00:00
Claude d91c15d97b fix: attribute reply-notification parent to its real author, not always "Me"
The parent message shown above a reply in the MessagingStyle notification was
unconditionally labeled "Me" with the account's avatar. That's wrong whenever
the account is notified as the *root* author of a thread but the direct parent
belongs to someone else — e.g. Vitor starts a thread, fiatjaf replies, a third
person replies to fiatjaf: fiatjaf's note was rendered as Vitor. This surfaced
through the NIP-22 comment and public-chat reply paths, which notify on
root-authorship, not just direct-parent authorship.

ReplyNotification now receives the parent Note (not a bare content string) and
resolves its actual author: attributed to the MessagingStyle `me` Person only
when the parent truly is the account's, otherwise shown as the real author with
their name + avatar, observed for enrichment like the replier. postConversation
reuses the `me` Person for a self-authored parent so it still renders as you.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0122uQ8BLHLeHDni81RBP26r
2026-07-24 03:45:49 +00:00