From 0651612d46161737e48cf30924cd68c1de8e1383 Mon Sep 17 00:00:00 2001 From: Thomas Way Date: Sun, 5 Apr 2026 04:03:32 +0100 Subject: [PATCH] fix(mobile): fix stale refs in use timer The timer hook preserved the values of the original local variables, which caused issues when hiding controls for videos. The callback can be changed so that it always sees the latest value with useRef, and it can be simplified significantly using a function rather than state class. --- .../asset_viewer/bottom_bar.widget.dart | 2 +- mobile/lib/utils/hooks/timer_hook.dart | 45 ++++++------------- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 1f327bf79f..b51960bb05 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -74,7 +74,7 @@ class ViewerBottomBar extends ConsumerWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - if (asset.isVideo) VideoControls(key: ValueKey(asset.heroTag), videoPlayerName: asset.heroTag), + if (asset.isVideo) VideoControls(videoPlayerName: asset.heroTag), if (!isReadonlyModeEnabled) Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), ], diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart index 36b78d8631..c49b7aeb04 100644 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ b/mobile/lib/utils/hooks/timer_hook.dart @@ -1,36 +1,17 @@ import 'package:async/async.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -RestartableTimer useTimer(Duration duration, void Function() callback) { - return use(_TimerHook(duration: duration, callback: callback)); -} - -class _TimerHook extends Hook { - final Duration duration; - final void Function() callback; - - const _TimerHook({required this.duration, required this.callback}); - @override - HookState> createState() => _TimerHookState(); -} - -class _TimerHookState extends HookState { - late RestartableTimer timer; - @override - void initHook() { - super.initHook(); - timer = RestartableTimer(hook.duration, hook.callback); - } - - @override - RestartableTimer build(BuildContext context) { - return timer; - } - - @override - void dispose() { - timer.cancel(); - super.dispose(); - } +RestartableTimer useTimer(Duration duration, VoidCallback callback) { + final latest = useRef(callback); + latest.value = callback; + + final timer = useMemoized( + () => RestartableTimer(duration, () => latest.value()), + [duration], + ); + + useEffect(() => timer.cancel, [timer]); + + return timer; }