Skip to content

Fix list-view thumbnail spacing, scroll-to-post on fullscreen close, and feed→fullscreen position sync#47

Merged
wrngwrld merged 16 commits into
mainfrom
copilot/fix-gif-decoder-initialization
Apr 27, 2026
Merged

Fix list-view thumbnail spacing, scroll-to-post on fullscreen close, and feed→fullscreen position sync#47
wrngwrld merged 16 commits into
mainfrom
copilot/fix-gif-decoder-initialization

Conversation

Copilot AI commented Apr 27, 2026

Copy link
Copy Markdown
Contributor
  • Fix controls unreachable (Stack ordering)
  • Fix ~1s playback pause on iOS in all fullscreen players (media-kit #964)
    • thread_media_viewer_page.dart: mirror feed player _applyAudioMode (setAudioTrack.auto on iOS before play)
    • thread_video_player_page.dart: same + call _applyAudioMode in _openAndPlay before play
    • saved_media_viewer_page.dart: add _applyAudioMode and call it in _open before play

Copilot AI and others added 3 commits April 27, 2026 09:05
…ewer

- Remove .gif from _isVideo() in ThreadMediaViewerPage so GIFs are
  rendered via Image.network() (Flutter built-in animated GIF support)
  instead of the media_kit decoder which fails to initialize
- Fix open/setPlaylistMode ordering in _ThreadMediaVideoPage,
  ThreadVideoPlayerPage, and SavedMediaVideoPage: call open(play:false)
  first, then setPlaylistMode(loop), then play() — mirrors the working
  pattern in FeedVideoPlayer and ensures loop mode is applied after
  open() clears the player's internal playlist state

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/111e3f35-b94d-4cd7-af6f-b6b54a1e1578

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
- Bump version to 0.11.2 in pubspec.yaml
- Set VisibilityDetectorController.instance.updateInterval = Duration.zero
  in main() so visibility callbacks fire every frame instead of every
  500 ms (the default), eliminating the 1-2 s wait before playback starts
- Tighten _scheduleVisibilityRecheck from 110 ms to 50 ms and
  _scheduleRecoveryCheck from 700 ms to 350 ms for snappier fallback paths

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/4224a8a7-a0a4-44cd-884e-3c74ef750a5d

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
…ion sync

- list_post.dart: center avatar/text column vertically against thumbnail
  (CrossAxisAlignment.center) — eliminates the blank gap below the
  user name / No. row when thumbnail is taller than the text
- thread_media_viewer_page.dart: fix scroll-to-post on dismiss
  · PopScope canPop→false + onPopInvokedWithResult always calls
    _closeViewer(), so iOS swipe-back and Android back button both
    return the current post id to the thread list
  · Add startPosition param to ThreadMediaViewerPage and
    _ThreadMediaVideoPage; seek after open for the initial page
- thread_video_player_page.dart: add startPosition param and seek
  after open (used by the FeedVideoPlayer fullscreen-expand button)
- feed_video_player.dart: expose positionNotifier (ValueNotifier<Duration>)
  so callers can track current playback position; pass _position to
  ThreadVideoPlayerPage when fullscreen button is tapped
- thread_page_post.dart: create _feedVideoPosition ValueNotifier,
  pass it to FeedVideoPlayer, and supply its value as startPosition
  when opening ThreadMediaViewerPage

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/f577b24b-4054-46fa-9a1c-63ce31b54a77

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
- ios/Runner.xcodeproj/project.pbxproj: bump MARKETING_VERSION 0.11.1
  → 0.11.2 and CURRENT_PROJECT_VERSION 63 → 64 to match pubspec.yaml

- lib/main.dart: change VisibilityDetectorController.instance.updateInterval
  from Duration.zero → const Duration(milliseconds:100). Duration.zero fires
  the visibility callback on every frame; with multiple video widgets in a
  thread that caused excessive native-player lifecycle work on iOS, leading
  to crashes under scroll pressure.

- lib/widgets/feed_video_player.dart:
  · Reduce iOS deferred-disposal timer from 8 s → 3 s and hard cap from
    18 → 5 entries. Fewer concurrently alive native players = less memory
    pressure and fewer file-descriptor leaks.
  · Add throttled setState in _positionSub: once _hasFirstFrame is true,
    call setState() at most every 250 ms. Previously _position was updated
    without setState so LinearProgressIndicator only refreshed when an
    unrelated state change occurred, making it appear frozen or laggy.
  · Reset _lastPositionUiUpdate in _resetForNewSource() and dispose() so
    stale timestamps don't suppress the first progress-bar update.

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/cae14e4b-8739-445a-894a-c1e83c3befb8

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
Copilot AI and others added 2 commits April 27, 2026 11:02
…ayer pool

- New lib/services/feed_player_pool.dart: global fixed-size pool (2 slots iOS, 3 Android) so at most N+1 native Player instances ever exist app-wide
- Rewrite FeedVideoPlayer to acquire/release pool slots instead of creating/disposing Players on scroll; no more deferred-dispose timer chain
- Fix _applyAudioMode: remove AudioTrack.no() calls (root cause of ~1s stutter per media-kit #964); only apply AudioTrack.auto() on unmute; mute purely via setVolume(0)
- Remove the 90ms Future.delayed workaround in _playIfNeeded (it was itself triggering the stutter)
- Move PlaylistMode.loop setup to pool initialisation (before open()), persists across open() calls
- Add _acquiredPlayerKey guard to correctly release pool slots even when widget.playerKey changes via didUpdateWidget
- Dispose pool on app shutdown in AppWithTheme.dispose()

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/af374ccc-d8cc-4433-add8-328dbba28f7a

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
wrngwrld and others added 3 commits April 27, 2026 13:28
…pauseAll() before fullscreen open

- Increase pool size from 2→3 on iOS and 3→4 on Android so 3-4
  simultaneously visible feed videos can all acquire a slot
- Replace MaterialPageRoute with CupertinoPageRoute when opening the
  fullscreen player so the iOS edge-swipe back gesture works
- Call FeedPlayerPool.instance.pauseAll() both in the fullscreen button
  onPressed (synchronous, instant) and in ThreadVideoPlayerPage.initState()
  so pool players release the AVAudioSession before the new player opens,
  eliminating the ~1s stutter on first playback

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/51811454-7c70-4d90-a661-3f588d58be86

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
…tall

device_info_plus 12.4.0 calls [[NSProcessInfo processInfo] isiOSAppOnVision]
which only exists in the visionOS-capable iOS SDK headers (Xcode 15.3+).
On a standard macos-latest CI runner the compile fails with:
  "No visible @interface for 'NSProcessInfo' declares the selector 'isiOSAppOnVision'"

Add a post_install Ruby block that finds FPPDeviceInfoPlusPlugin.m in the
Pods sandbox and comments out all lines referencing the missing selector,
allowing the build to succeed without requiring visionOS SDK headers.

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/9910df16-07c6-4fa0-a38c-b1cea5f181ce

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
device_info_plus 12.4.0 requires Xcode 26.1.1+ and Flutter 3.38.1+,
neither of which are available on the CI runner (Flutter 3.35.6, macos-latest).
12.4.0 introduced the isiOSAppOnVision NSProcessInfo call that fails to compile
without the visionOS SDK headers.

Pin to >=12.1.0 <12.4.0 so pub resolves to 12.3.0 which works with the
current CI stack. Remove the Podfile post_install hack that was patching
FPPDeviceInfoPlusPlugin.m, since the root cause is now addressed properly.

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/29a8df7b-571c-425b-a700-ff33dc247b44

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
…equirements

- Revert device_info_plus back to ^12.1.0 (no longer need the version cap)
- CI: bump flutter-version from 3.35.6 to 3.38.1 in both jobs
- CI: add maxim-lobanov/setup-xcode@v1 step (xcode-version: 26.1) to iOS job
- android/settings.gradle: AGP 8.9.1 -> 8.12.1, Kotlin 2.1.0 -> 2.2.0

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/1bd3ed11-d772-4248-88fe-59dcb00a8823

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
Previously _open() was always called in initState (even for inactive
adjacent pages pre-built by PageView during the scroll animation).
didUpdateWidget then called _player.play() immediately on activation, but
if _player.open(play:false) was still in flight that play() was a no-op;
open() then completed leaving the player paused, causing a visible pause
before the trailing `if (isActive) play()` in _open() fired.

Fix mirrors _ThreadMediaVideoPage._openAndPlay():
- Only call _open() in initState when the page is initially active
- Call _open() (not _player.play()) in didUpdateWidget on activation, so
  play() is always sequenced after open() in the same async chain
- Add generation counter so a superseded async open never starts playback
  after a newer one has taken over

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/d0de1a29-a048-436a-a06f-889054725dc9

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
- PopScope(canPop: false) → canPop: true so iOS swipe-back gesture works.
  Explicit back button still calls _closeViewer() returning the post ID
  for thread scroll; swipe-back returns null result which is safely
  handled by the existing onDismiss(null) early-return in thread_page.

- Remove tap-to-toggle (_showControls / _controlsHideTimer /
  _startControlsAutoHide) from _ThreadMediaVideoPage. Controls are now
  always visible, matching the SavedMediaVideoPage overlay. This also
  eliminates the bug where clicking play/pause or the slider would
  simultaneously toggle controls off via the outer GestureDetector.

- Remove the outer GestureDetector(onTap) wrapper from the build method;
  the scrub GestureDetector (Positioned.fill left:24) is unchanged.

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/fbf85620-3359-4abb-9637-a674e1539e59

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
…o they receive touches

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/809758e6-e39d-4743-9b4f-bc30d0533337

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
… to prevent ~1s iOS pause (media-kit #964)

Agent-Logs-Url: https://github.com/NekoSurf/NekoSurf/sessions/36330f43-4793-4b09-a2c2-f1bf420371b9

Co-authored-by: wrngwrld <48834041+wrngwrld@users.noreply.github.com>
@wrngwrld
wrngwrld marked this pull request as ready for review April 27, 2026 14:08
@wrngwrld
wrngwrld merged commit 845c1f7 into main Apr 27, 2026
4 checks passed
@wrngwrld
wrngwrld deleted the copilot/fix-gif-decoder-initialization branch April 27, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants