mirror of
https://github.com/block/buzz.git
synced 2026-11-14 13:21:02 +00:00
Replace the signed-in mobile welcome screen with **Add a community**: QR scan first, then grey secondary actions for community links, finding saved communities, and creation. Hide **Find my communities** when none are known; refresh on entry/resume and every 30 seconds while active, retaining results on transient failure. Community switcher → Add keeps its two-action flow and uses the same grey secondary link button. Validation: Flutter analysis, full mobile suite (4,014 tests), Builderlab-enabled full suite (4,036 tests), widget interactions, and independent agent review. Draft pending native sign-in and human testing. Snapshots render the production Flutter widgets; native navigation/status bars are omitted. | Communities available | No communities | Switcher → Add | |---|---|---| |  |  |  | --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
57 lines
1.8 KiB
Dart
57 lines
1.8 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
|
|
/// Refreshes account destinations only while the entry screen is active.
|
|
/// A failed refresh preserves the last successful result for this account.
|
|
bool useKnownCommunities({
|
|
required String? accountKey,
|
|
required bool active,
|
|
required Future<bool> Function() load,
|
|
}) {
|
|
final available = useMemoized(() => ValueNotifier(false), [accountKey]);
|
|
useEffect(() => available.dispose, [available]);
|
|
useListenable(available);
|
|
final latestLoad = useRef(load)..value = load;
|
|
useEffect(() {
|
|
if (accountKey == null || !active) return null;
|
|
var disposed = false;
|
|
var loading = false;
|
|
Future<void> refresh() async {
|
|
if (loading) return;
|
|
loading = true;
|
|
try {
|
|
final result = await latestLoad.value();
|
|
if (!disposed) available.value = result;
|
|
} catch (_) {
|
|
// Discovery is optional; an unavailable service is not an empty list.
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
Timer? timer;
|
|
void lifecycleChanged(AppLifecycleState? state) {
|
|
timer?.cancel();
|
|
timer = null;
|
|
if (state != null && state != AppLifecycleState.resumed) return;
|
|
unawaited(refresh());
|
|
timer = Timer.periodic(const Duration(seconds: 30), (_) {
|
|
unawaited(refresh());
|
|
});
|
|
}
|
|
|
|
// Lifecycle callbacks must stop timers immediately: paused apps do not
|
|
// render a frame, so a rebuild-driven effect may never run its cleanup.
|
|
final listener = AppLifecycleListener(onStateChange: lifecycleChanged);
|
|
lifecycleChanged(WidgetsBinding.instance.lifecycleState);
|
|
return () {
|
|
disposed = true;
|
|
timer?.cancel();
|
|
listener.dispose();
|
|
};
|
|
}, [accountKey, active, available]);
|
|
return available.value;
|
|
}
|