Group native multi-image uploads into a gallery (insertPendingAttachments batch)#1111
Open
svara wants to merge 1 commit into
Open
Group native multi-image uploads into a gallery (insertPendingAttachments batch)#1111svara wants to merge 1 commit into
svara wants to merge 1 commit into
Conversation
Native clients (iOS/Android) currently insert each uploaded image one at a time
via insertPendingAttachment, so they never reach the web uploadFiles /
GalleryUploader path and multi-image uploads never form a gallery.
Add Contents#insertPendingAttachments(files): it routes the whole batch through
the same Uploader/GalleryUploader path the web uses, creating bridge-managed
(uploadUrl-less) upload nodes so the editor doesn't kick off a DirectUpload —
the host app still owns the upload and drives each returned handle
(setUploadProgress/setAttributes/remove) as it settles.
- $createPendingUploadNode builds the uploadUrl-less node
- Uploader gains a { pending: true } mode selecting that factory
- the pending-attachment handle is extracted and shared by the single and
batch entry points
The single insertPendingAttachment keeps its existing no-gallery behaviour for
back-compat with the current bridge during rollout.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR restores native iOS/Android multi-image upload behavior by adding a batch pending-attachment insertion API that routes through the existing Uploader/GalleryUploader path, allowing multiple inserted images to be grouped into a single gallery (matching web behavior) without initiating web DirectUploads.
Changes:
- Add
Contents#insertPendingAttachments(files)to insert a batch of pending upload nodes and return one handle per file (in input order) for host-driven progress/materialization/removal. - Extend
Uploader.for(...)to acceptoptions, and add a{ pending: true }mode that builds uploadUrl-less pending nodes. - Add Playwright coverage for native-bridge batch insertion and gallery grouping/materialization/dissolution behavior.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| test/browser/tests/attachments/native_bridge_gallery.test.js | Adds browser tests asserting batch pending insertion groups images into galleries and preserves handle semantics. |
| src/editor/contents/uploader.js | Adds options plumbing and a pending-node creation branch so pending uploads don’t start DirectUpload. |
| src/editor/contents.js | Adds $createPendingUploadNode and new insertPendingAttachments(files) API, refactoring handle creation into #pendingAttachmentHandle. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c117f90 to
8b2fa86
Compare
Comment on lines
+308
to
+316
| insertPendingAttachments(files) { | ||
| if (!this.editorElement.supportsAttachments) return [] | ||
|
|
||
| let nodeKeys = [] | ||
| this.editor.update(() => { | ||
| const uploader = Uploader.for(this.editorElement, Array.from(files), { pending: true }) | ||
| uploader.$uploadFiles() | ||
| nodeKeys = (uploader.nodes ?? []).map(node => node.getKey()) | ||
| }, { tag: HISTORY_MERGE_TAG }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Regression
Before the BC5 / Lexxy migration (Trix → Lexical), selecting multiple images at once in the native iOS/Android apps grouped them into a single image gallery, matching the web. After the migration, native multi-image uploads stopped grouping — each image lands as its own standalone attachment, stacked vertically.
Why it broke
On the web, every upload entry point (drag-and-drop, paste, file picker) funnels through
Uploader.for(...), which delegates toGalleryUploaderwhenever the batch contains more than one previewable image (GalleryUploader.isMultipleImageUpload).GalleryUploadercreates oneImageGalleryNodeand inserts the images into it. That is the one and only place a gallery is formed.The native bridge never reaches that path. The host app uploads each file itself and hands the editor one pending attachment at a time via
Contents#insertPendingAttachment(file), which inserts a loneActionTextAttachmentUploadNodeat the cursor. Because images arrive and are inserted individually,isMultipleImageUploadis never consulted and no gallery is ever created.Trix had no equivalent gap: its
attachmentGalleryFilterran over the whole document and grouped adjacent image attachments regardless of how they were inserted, so even the one-at-a-time native path produced a gallery. Lexxy has no always-on grouping pass — grouping happens only at insertion time insideGalleryUploader— so the native path regressed.Fix
Give the bridge a batch entry point that flows through the exact same
Uploader/GalleryUploaderpath the web uses, rather than teaching Lexxy a second, parallel grouping mechanism.New
Contents#insertPendingAttachments(files):Uploader.for(editorElement, files, { pending: true }), soGalleryUploadermakes the grouping decision with the sameisMultipleImageUpload/ selection rules as the web — multiple images → one gallery, a single image → standalone, non-image files placed after the gallery;{ pending: true }mode so the upload nodes are built without anuploadUrl($createPendingUploadNode) and the editor does not kick off a web DirectUpload — the host app still owns the upload;setUploadProgress/setAttributes(materialize) /removeas it settles.The pending-attachment handle is extracted into a shared
#pendingAttachmentHandleand reused by both the singularinsertPendingAttachmentand the new batch method.Reuses the existing gallery logic — no new grouping code
GalleryUploaderandImageGalleryNodeare unchanged. The only addition to the uploader is a{ pending: true }branch inUploader#$createUploadNodesthat swaps$createUploadNodefor the uploadUrl-less$createPendingUploadNode:Galleries form, accept non-image siblings, materialize, and dissolve through the same code that already backs web drag/drop/paste — including replacing each pending node with its real attachment (the gallery survives) and removing the second-to-last image (the gallery dissolves).
Back-compat during rollout
The singular
insertPendingAttachmentis kept as-is (still no gallery) so older native builds that insert one at a time keep working. New native builds call the batch method. Once all clients are updated the singular path can be retired.Tests
test/browser/tests/attachments/native_bridge_gallery.test.js(6 tests):Companion change
bc3 wires the bridge to this API (
insert-pending-attachmentscontroller case +LexxyEditor#insertPendingAttachments): basecamp/bc3 branchnative-bridge-image-gallery. It needs a Lexxy npm/gem release before it can bump off the local-dev pin.