Home Screen: Inform on connection lose

This commit is contained in:
2026-03-24 21:58:42 +01:00
parent b4ff5884b9
commit 400dee57d4
2 changed files with 34 additions and 14 deletions
+9 -1
View File
@@ -149,6 +149,11 @@ final colorProvider = StateNotifierProvider<ColorNotifier, BusylightColor>(
}, },
); );
// ── Connection lost indicator ─────────────────────────────────────────────────
// Set to true by the polling notifier when a poll fails, false on success.
final connectionLostProvider = StateProvider<bool>((_) => false);
// ── Background polling ──────────────────────────────────────────────────────── // ── Background polling ────────────────────────────────────────────────────────
// Periodically pulls status + color from the device and silently updates state. // Periodically pulls status + color from the device and silently updates state.
@@ -181,8 +186,11 @@ class PollingNotifier extends StateNotifier<void> {
_ref.read(busylightStatusProvider.notifier).setLocalStatus(status); _ref.read(busylightStatusProvider.notifier).setLocalStatus(status);
_ref.read(colorProvider.notifier).silentSet(color); _ref.read(colorProvider.notifier).silentSet(color);
_ref.read(brightnessProvider.notifier).silentSet(color.brightness); _ref.read(brightnessProvider.notifier).silentSet(color.brightness);
// Connection restored — clear the lost flag
_ref.read(connectionLostProvider.notifier).state = false;
} catch (_) { } catch (_) {
// Silently ignore poll errors — connection issues are shown on manual refresh // Signal connectivity issue to the UI
_ref.read(connectionLostProvider.notifier).state = true;
} }
} }
+25 -13
View File
@@ -118,10 +118,11 @@ class _BodyState extends ConsumerState<_Body> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final statusAsync = ref.watch(busylightStatusProvider); final statusAsync = ref.watch(busylightStatusProvider);
final brightness = ref.watch(brightnessProvider); final brightness = ref.watch(brightnessProvider);
final color = ref.watch(colorProvider); final color = ref.watch(colorProvider);
final presets = ref.watch(presetsProvider); final presets = ref.watch(presetsProvider);
final connectionLost = ref.watch(connectionLostProvider);
final status = statusAsync.valueOrNull ?? BusylightStatus.off; final status = statusAsync.valueOrNull ?? BusylightStatus.off;
final displayColor = _statusColor(status, color); final displayColor = _statusColor(status, color);
@@ -129,7 +130,7 @@ class _BodyState extends ConsumerState<_Body> {
return ListView( return ListView(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
children: [ children: [
// Live color preview dot // Live color preview dot — shows wifi-off icon when connection lost
Center( Center(
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
@@ -137,10 +138,12 @@ class _BodyState extends ConsumerState<_Body> {
height: 100, height: 100,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: status == BusylightStatus.off color: connectionLost
? Colors.grey.shade900 ? Colors.grey.shade900
: displayColor.withOpacity(brightness), : status == BusylightStatus.off
boxShadow: status != BusylightStatus.off ? Colors.grey.shade900
: displayColor.withOpacity(brightness),
boxShadow: (!connectionLost && status != BusylightStatus.off)
? [BoxShadow( ? [BoxShadow(
color: displayColor.withOpacity(0.5 * brightness), color: displayColor.withOpacity(0.5 * brightness),
blurRadius: 40, blurRadius: 40,
@@ -148,13 +151,20 @@ class _BodyState extends ConsumerState<_Body> {
)] )]
: null, : null,
), ),
child: connectionLost
? const Icon(Icons.wifi_off, color: Colors.grey, size: 36)
: null,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Center( Center(
child: Text( child: Text(
status.label.toUpperCase(), connectionLost ? 'NO CONNECTION' : status.label.toUpperCase(),
style: const TextStyle(color: Colors.grey, letterSpacing: 2, fontSize: 13), style: TextStyle(
color: connectionLost ? Colors.red.shade700 : Colors.grey,
letterSpacing: 2,
fontSize: 13,
),
), ),
), ),
const SizedBox(height: 36), const SizedBox(height: 36),
@@ -177,9 +187,11 @@ class _BodyState extends ConsumerState<_Body> {
BusylightStatus.off, BusylightStatus.off,
].map((s) => StatusButton( ].map((s) => StatusButton(
status: s, status: s,
isActive: status == s, isActive: !connectionLost && status == s,
isPending: _pendingStatus == s, isPending: _pendingStatus == s,
onTap: _pendingStatus == null ? () => _setStatus(s) : () {}, onTap: (!connectionLost && _pendingStatus == null)
? () => _setStatus(s)
: () {},
)).toList(), )).toList(),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
@@ -188,7 +200,7 @@ class _BodyState extends ConsumerState<_Body> {
_PresetsScroller( _PresetsScroller(
presets: presets, presets: presets,
pendingPresetId: _pendingPresetId, pendingPresetId: _pendingPresetId,
onPresetTap: (_pendingStatus == null && _pendingPresetId == null) onPresetTap: (!connectionLost && _pendingStatus == null && _pendingPresetId == null)
? _applyPreset ? _applyPreset
: (_) {}, : (_) {},
onPresetDelete: (preset) => ref.read(presetsProvider.notifier).remove(preset.id), onPresetDelete: (preset) => ref.read(presetsProvider.notifier).remove(preset.id),