feat: surface device and scene load errors

This commit is contained in:
Artem Kokos
2026-04-23 20:24:08 +07:00
parent 90a86e932d
commit 1c40852ac6
4 changed files with 521 additions and 191 deletions

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../app/error_message.dart';
import '../providers/providers.dart';
import 'color_picker.dart';
@@ -90,14 +91,12 @@ class _GroupCardState extends ConsumerState<GroupCard> {
color: Colors.white38,
),
tooltip: 'Включить на 4 часа',
onPressed: () =>
ref.read(groupsProvider.notifier).setTimer4h(id),
onPressed: () => _setTimer4h(id),
),
Switch(
value: isOn,
activeThumbColor: Colors.deepOrange,
onChanged: (v) =>
ref.read(groupsProvider.notifier).toggleGroup(id, v),
onChanged: (v) => _toggleGroup(id, v),
),
],
),
@@ -204,6 +203,32 @@ class _GroupCardState extends ConsumerState<GroupCard> {
),
);
}
Future<void> _toggleGroup(String id, bool value) async {
try {
await ref.read(groupsProvider.notifier).toggleGroup(id, value);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Ошибка управления группой: ${describeLoadError(e)}'),
),
);
}
}
Future<void> _setTimer4h(String id) async {
try {
await ref.read(groupsProvider.notifier).setTimer4h(id);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Ошибка создания таймера: ${describeLoadError(e)}'),
),
);
}
}
}
/// Слайдер с иконкой и подписью
@@ -324,16 +349,18 @@ class _SceneSelector extends ConsumerStatefulWidget {
}
class _SceneSelectorState extends ConsumerState<_SceneSelector> {
bool _loadStarted = false;
@override
void initState() {
super.initState();
Future.microtask(() => ref.read(scenesProvider.notifier).load());
}
@override
Widget build(BuildContext context) {
final scenes = ref.watch(scenesProvider);
final scenesState = ref.watch(scenesProvider);
final scenes = scenesState.data;
if (scenes.isEmpty && !_loadStarted) {
// Загрузить сцены при первом показе
_loadStarted = true;
Future.microtask(() => ref.read(scenesProvider.notifier).load());
if ((scenesState.isIdle || scenesState.isLoading) && scenes.isEmpty) {
return const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
@@ -346,6 +373,34 @@ class _SceneSelectorState extends ConsumerState<_SceneSelector> {
);
}
if (scenesState.hasError && scenes.isEmpty) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Не удалось загрузить сцены',
style: const TextStyle(color: Colors.white54, fontSize: 12),
),
if (scenesState.errorMessage != null)
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
scenesState.errorMessage!,
style: const TextStyle(color: Colors.white30, fontSize: 11),
),
),
TextButton.icon(
onPressed: _loadScenes,
icon: const Icon(Icons.refresh, size: 16),
label: const Text('Повторить'),
),
],
),
);
}
if (scenes.isEmpty) {
return const Padding(
padding: EdgeInsets.all(8.0),
@@ -356,34 +411,75 @@ class _SceneSelectorState extends ConsumerState<_SceneSelector> {
);
}
return Wrap(
spacing: 8,
runSpacing: 4,
children: scenes.map((scene) {
String sceneName;
String sceneId;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (scenesState.hasError)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
const Icon(
Icons.warning_amber_rounded,
size: 16,
color: Colors.deepOrange,
),
const SizedBox(width: 6),
const Expanded(
child: Text(
'Сцены не обновились',
style: TextStyle(color: Colors.white38, fontSize: 12),
),
),
IconButton(
tooltip: 'Повторить',
onPressed: _loadScenes,
icon: const Icon(Icons.refresh, size: 16),
),
],
),
),
Wrap(
spacing: 8,
runSpacing: 4,
children: scenes.map((scene) {
String sceneName;
String sceneId;
if (scene is String) {
sceneName = scene;
sceneId = scene;
} else if (scene is Map) {
sceneName = (scene['name'] ?? scene['id'] ?? scene.toString())
.toString();
sceneId = (scene['id'] ?? scene['name'] ?? scene.toString())
.toString();
} else {
sceneName = scene.toString();
sceneId = scene.toString();
}
if (scene is String) {
sceneName = scene;
sceneId = scene;
} else if (scene is Map) {
sceneName = (scene['name'] ?? scene['id'] ?? scene.toString())
.toString();
sceneId = (scene['id'] ?? scene['name'] ?? scene.toString())
.toString();
} else {
sceneName = scene.toString();
sceneId = scene.toString();
}
return ActionChip(
label: Text(sceneName, style: const TextStyle(fontSize: 12)),
backgroundColor: Colors.white10,
onPressed: () => ref
.read(groupsProvider.notifier)
.setScene(widget.groupId, sceneId),
);
}).toList(),
return ActionChip(
label: Text(sceneName, style: const TextStyle(fontSize: 12)),
backgroundColor: Colors.white10,
onPressed: () => _setScene(sceneId),
);
}).toList(),
),
],
);
}
Future<void> _loadScenes() => ref.read(scenesProvider.notifier).load();
Future<void> _setScene(String sceneId) async {
try {
await ref.read(groupsProvider.notifier).setScene(widget.groupId, sceneId);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ошибка сцены: ${describeLoadError(e)}')),
);
}
}
}