Skip to content

Add video stacks and native iOS zoom playback - #9316

Merged
klopez4212 merged 11 commits into
mainfrom
kennylopez-ios-video-stack-20261110
Nov 10, 2026
Merged

klopez4212 merged 11 commits into
mainfrom
kennylopez-ios-video-stack-20261110

Conversation

@klopez4212

@klopez4212 klopez4212 commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Videos previously rendered outside photo stacks and opened only the Flutter video viewer. Message bubbles now group trailing videos and photos in their original order, with poster thumbnails or a visible play placeholder. On iOS 18+, the native zoom gallery (enabled by default) opens both standalone videos and mixed stacks with AVKit playback controls, and closing restores the selected stack item.

Flutter downloads only the selected video using fresh media authentication, streams it to a bounded temporary file, and cancels/deletes it when selection changes or the viewer closes. UIKit releases the old player before that file is removed. Failed downloads and playback expose Retry. Video URLs never enter the image decoder, and photo-only actions are hidden on video pages.

The existing Flutter viewers remain the fallback. Native zoom defaults on; --dart-define=BUZZ_NATIVE_PHOTO_ZOOM=false remains an explicit build-time opt-out. Unsupported platforms/formats, unavailable native presentation, and bridge failures use the Flutter fallback. Native video pages use Close or accessibility escape to return with zoom; the photo-specific downward drag recognizer is disabled there to avoid competing with AVKit controls.

Related issue

Stacked on #9314 (PNG/HEIC photo formats); merge that first. No matching open video-stack/zoom PR found.

Testing

  • Default-on follow-up: presentation tests also cover unavailable, null, and throwing bridge responses.

  • just mobile-check mobile-test: analysis clean, 4,601 mobile tests passed, plus push-configuration and secp256k1 package tests.

  • Full native-enabled mobile suite: 4,626 passed, 68 skipped; rerun passed at dc752dccaa664cb56a19cbe517a266d1407a1100.

  • Mixed-stack widget journey covers order, video opening, returning to selection, and the photo-only fallback gallery.

  • Native bridge tests cover video snapshot presentation, selected-only dispatch, stale sessions and keeping video URLs out of image decoding.

  • Real HTTP download journey covers authenticated requests, Retry, byte limits, cancellation and temporary-file cleanup.

  • Native simulator fixture decodes a real MP4 and verifies selected-only loading, player release on paging and teardown. Removing playback cleanup makes the regression fail; restoring it passes.

  • No physical-device installation or end-to-end zoom recording captured.

Widget previews

Actual Flutter renders with sample video URLs and no poster metadata; these show the stack layout, not device playback.

Received, light Sent, dark
Received video stack without poster images Sent video stack without poster images

Human verification pending

On a normal build on iOS 18+ (no extra flag required), open a message containing photos and videos, page between them, play a video, and close. Verify playback controls work, only the selected video plays, and closing returns to the selected stack item.

klopez4212 and others added 5 commits November 9, 2026 19:32
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
…ez-ios-video-stack-20261110

Signed-off-by: kenny lopez <[email protected]>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
@klopez4212
klopez4212 marked this pull request as ready for review November 10, 2026 15:13
@klopez4212
klopez4212 requested a review from a team as a code owner November 10, 2026 15:13
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T15:57:09.623942Z 6742e14 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c52bf71a88

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

klopez4212 and others added 4 commits November 10, 2026 07:21
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bfb181ab0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Base automatically changed from kennylopez-ios-photo-formats-20261110 to main November 10, 2026 15:34
klopez4212 and others added 2 commits November 10, 2026 07:47
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <[email protected]>
…stack-20261110

Signed-off-by: kenny lopez <[email protected]>

# Conflicts:
#	mobile/ios/Runner/NativePhotoZoomCoordinator.swift
#	mobile/test/features/channels/native_photo_zoom_preview_test.dart
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 10, 2026
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: e2dc54e22fc113f298889d76c3acf674cb46db4b...6742e140742f09049054ded08109ba37d01764db
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: MEDIUM

One release-build reliability regression prevents native video presentation.

Findings

[MEDIUM] Native video presentation always falls back in release builds

  • Category: Reliability
  • Location: mobile/lib/features/channels/native_photo_zoom_preview.dart:179 (source)
  • Description: Opening a video calls _snapshot, which reads debugNeedsPaint outside an assertion. Flutter initializes this debug getter's result only inside an assert, so reading it in release or profile mode throws LateInitializationError. The catch in open converts this failure into Flutter fallback before native presentation begins.
  • Impact: Production builds cannot open the native gallery with a video selected, losing native video presentation and mixed-media paging. The debug widget tests do not expose this failure.
  • Recommendation: Remove the production read of debugNeedsPaint or confine it to an assertion. Use release-safe attachment and size checks, snapshot after painting, and verify video entry in a release or profile build.

Notes

  • Review used read-only source inspection. Builds and tests were not executed, as instructed.

Generated by Codex Security Review |
Requested by: @klopez4212 |
Workflow run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6742e14074

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

uri,
abortTrigger: stop.future,
)..headers.addAll(owner.auth.headersFor(url));
final response = await owner.client.send(request);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restart the stall timer after response headers

When response headers arrive near the 20-second stall limit but the first body chunk needs additional time, this request is aborted even though neither phase individually stalled for 20 seconds: the timer starts before send() and is not restarted until after body.moveNext() returns. Restart it immediately after send() completes, as boundedGet does, so slow-but-progressing video downloads do not incorrectly surface Retry.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Reset the stall timer after receiving headers

Agreed: headers at 15 seconds followed by the first body chunk 10 seconds later currently abort at 20 seconds, despite progress. Please restart the timer after send(), matching boundedGet.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

status.text = image == nil ? "Loading photo…" : nil
init(index: Int, label: String, image: UIImage?, isVideo: Bool = false) {
self.index = index; self.isVideo = isVideo; super.init(nibName: nil, bundle: nil)
imageView.image = image; imageView.accessibilityLabel = label; imageView.isAccessibilityElement = !isVideo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expose video labels to VoiceOver

When a video has an authored Markdown or imeta label, that label is assigned only to imageView, which is explicitly removed from the accessibility tree for video pages; the AVPlayer view is never given the label, so VoiceOver users lose the video's supplied description. Preserve the label on one accessible owner without hiding the player's controls.

AGENTS.md reference: AGENTS.md:L273-L281

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve the video’s authored accessibility description

The gallery passes the Markdown/imeta label through, but this assigns it only to an inaccessible image view for videos. Please retain it on an accessible description element without grouping away AVKit’s controls.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed this PR and the Nexus review-tracker notes. Approving with the two non-blocking suggestions above.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@klopez4212
klopez4212 merged commit b40f221 into main Nov 10, 2026
53 checks passed
@klopez4212
klopez4212 deleted the kennylopez-ios-video-stack-20261110 branch November 10, 2026 18:03
philazar added a commit that referenced this pull request Nov 11, 2026
Co-Authored-By: Claude Opus 5.5 <[email protected]>

* origin/main:
  feat(mobile/ios): show the message's own text in notifications (#9346)
  feat(mobile): prefill the agents you last tagged in a conversation (#9248)
  feat(mobile): start a Solo chat from a message or inbox row (#9251)
  feat(mobile): link to Builderlab web from Settings (#9300)
  fix(relay): bound invite preview host lookup (#9308)
  fix(relay): saturate filter limits instead of casting (#9230)
  fix(mobile): tell same-name agents apart in Solo (#9321)
  fix(mobile): join an open channel from the chat footer (#9253)
  fix(relay): fence pending subscriptions on close or replacement (#2382)
  fix(mobile): hide archived people and agents from choices; give them their own members section (#9320)
  Add video stacks and native iOS zoom playback (#9316)
  Support PNG and HEIC photos in the native iOS zoom gallery (#9314)
  fix(mobile): list known people in recipient pickers before a name is typed (#9311)
  feat(mobile): mark edited messages after the body (#8863)

Signed-off-by: Philip Azar <[email protected]>
philazar added a commit that referenced this pull request Nov 11, 2026
Co-Authored-By: Claude Opus 5.5 <[email protected]>

* origin/main: (22 commits)
  chore(deps): bump hickory-proto to 0.26.2 for RUSTSEC-2026-0487..0491 (#9351)
  feat(mobile/ios): show the message's own text in notifications (#9346)
  feat(mobile): prefill the agents you last tagged in a conversation (#9248)
  feat(mobile): start a Solo chat from a message or inbox row (#9251)
  feat(mobile): link to Builderlab web from Settings (#9300)
  fix(relay): bound invite preview host lookup (#9308)
  fix(relay): saturate filter limits instead of casting (#9230)
  fix(mobile): tell same-name agents apart in Solo (#9321)
  fix(mobile): join an open channel from the chat footer (#9253)
  fix(relay): fence pending subscriptions on close or replacement (#2382)
  fix(mobile): hide archived people and agents from choices; give them their own members section (#9320)
  Add video stacks and native iOS zoom playback (#9316)
  Support PNG and HEIC photos in the native iOS zoom gallery (#9314)
  fix(mobile): list known people in recipient pickers before a name is typed (#9311)
  feat(mobile): mark edited messages after the body (#8863)
  Add swipeable photo stacks and native iOS gallery preview (#9259)
  ci(docker): skip relay image publishes for main pushes outside the build context (#9310)
  Add manual-only relay image publishing (#9303)
  fix(mobile): bump Flutter to 3.47.9 for Android TLS fix (#9309)
  Serve video and poster previews for invite links (#9306)
  ...

Signed-off-by: Philip Azar <[email protected]>

This branch was previously deployed

1 inactive deployment
codex-review — 6742e140 Deployed Nov 10, 2026 by klopez4212 via Run Codex Security Review #14243
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants