Skip to content

feat(chat): send, store and open photo messages - #1681

Merged
bmc08gt merged 4 commits into
code/cashfrom
feat/chat-media-transport
Oct 6, 2026
Merged

bmc08gt merged 4 commits into
code/cashfrom
feat/chat-media-transport

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

The transport and storage half of chat photos, ported from
code-ios-app#959. Nothing in the app calls it yet; the UI follows in a
stacked PR.

  • Encoding: photos are downscaled and encoded as JPEG down a quality
    ladder of 0.9, 0.8, 0.7, 0.6 until they fit the blob policy, with EXIF
    orientation baked in and a BlurHash for the placeholder. The
    chat_media.json vectors are the iOS file, byte for byte.
  • Encrypted chats: the blob is sealed with the shared-core ChatCipher,
    uploaded with the chat field, and sent as EncryptedContent. A photo
    that can't be opened loads as ChatPhotoUnavailable.
  • Sending: one message per photo, each retried on its own. Finalization
    polls GetBlobs every 2s, at most 30 times, counted only while the app
    is in the foreground.
  • Storage: pending uploads live in Room (schema 43) with the JPEG in
    filesDir, so a send resumes after the app is killed.
  • Loading: a Coil fetcher keyed chat-media-<blobId>, registered in
    FlipcashApp.
  • Fixes found on the way: captions were never sent, because caption
    inside the builder's apply resolved to the builder's own field; and
    a sent message disappeared when the stream delivered its stored copy
    before the send returned, because clearing the pending row deleted by
    its client id.
  • Blob calls (upload, getBlobs) now carry a 15s deadline, as on iOS.
    Without one, an upload started while offline never returned, and the
    photo sat at Sending with no retry. This also bounds blob reads
    (avatars, photo downloads), which used to wait indefinitely.

First of three stacked PRs for chat photos (port of code-payments/code-ios-app#959). Next: the composer layout, then the photo UI.

The transport and storage half of chat photos, ported from
code-ios-app#959. Nothing in the app calls it yet; the UI follows in a
stacked PR.

- Encoding: photos are downscaled and encoded as JPEG down a quality
  ladder of 0.9, 0.8, 0.7, 0.6 until they fit the blob policy, with EXIF
  orientation baked in and a BlurHash for the placeholder. The
  chat_media.json vectors are the iOS file, byte for byte.
- Encrypted chats: the blob is sealed with the shared-core ChatCipher,
  uploaded with the `chat` field, and sent as EncryptedContent. A photo
  that can't be opened loads as ChatPhotoUnavailable.
- Sending: one message per photo, each retried on its own. Finalization
  polls GetBlobs every 2s, at most 30 times, counted only while the app
  is in the foreground.
- Storage: pending uploads live in Room (schema 43) with the JPEG in
  filesDir, so a send resumes after the app is killed.
- Loading: a Coil fetcher keyed `chat-media-<blobId>`, registered in
  FlipcashApp.
- Fixes found on the way: captions were never sent, because `caption`
  inside the builder's `apply` resolved to the builder's own field; and
  a sent message disappeared when the stream delivered its stored copy
  before the send returned, because clearing the pending row deleted by
  its client id.
@bmc08gt
bmc08gt force-pushed the feat/chat-media-transport branch from 37fc323 to cca6d0b Compare October 6, 2026 00:58
@github-actions github-actions Bot added the type: feature New functionality label Oct 6, 2026
A photo that finished uploading while it sat in the composer confirms
within about 100 ms of the tap. Room hasn't drawn the pending row by
then, so the bubble's first frame already reads Sent and the bar never
shows. On the Seeker the new bubble landed as Delivered with no bar in
any frame.

The Sent stage now waits until 800 ms after the row was inserted. The
message is still confirmed in the database at once; only the bar's
phase waits, and a later post in the same batch isn't held up.
BlobStorageApi queued every call with wait-for-ready and no deadline, so a
photo sent while the phone was offline never left "Sending": the reserve call
waited for a channel that could not resolve its host, and the upload's
retries and the bubble's "Not sent" never ran. Unary blob calls now fail
after 15s, the same deadline iOS puts on them (CallOptions.unaryDefault).
A photo whose upload failed before the send (offline, say) went straight
to a failed row, because the send only waited on the existing upload.
The send now restarts a retryable failure, so the row shows Sending until
the upload settles.
@bmc08gt
bmc08gt added this pull request to stack #1685 October 6, 2026 16:08
@bmc08gt
bmc08gt merged commit 3abc1d4 into code/cash Oct 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: network gRPC, connectivity, API, exchange rates type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant