Use backend rescan summary in group editor
This commit is contained in:
@@ -80,39 +80,19 @@ class _GroupEditScreenState extends ConsumerState<GroupEditScreen> {
|
||||
}
|
||||
|
||||
Future<void> _rescan() async {
|
||||
final beforeIds = ref
|
||||
.read(devicesProvider)
|
||||
.data
|
||||
.map((device) => device.groupMemberId)
|
||||
.toSet();
|
||||
|
||||
setState(() => _rescanning = true);
|
||||
try {
|
||||
await ref.read(apiProvider).rescanNetwork();
|
||||
|
||||
var changed = false;
|
||||
for (var attempt = 0; attempt < 6; attempt++) {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
final response = await ref.read(apiProvider).rescanNetwork();
|
||||
await ref.read(devicesProvider.notifier).load();
|
||||
final currentIds = ref
|
||||
.read(devicesProvider)
|
||||
.data
|
||||
.map((device) => device.groupMemberId)
|
||||
.toSet();
|
||||
if (!_sameSet(beforeIds, currentIds)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final summary = response.data is Map
|
||||
? Map<String, dynamic>.from(response.data as Map)
|
||||
: const <String, dynamic>{};
|
||||
final message = formatRescanSummary(summary);
|
||||
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
changed
|
||||
? 'Список устройств обновился'
|
||||
: 'Сканирование завершилось, но новых устройств пока не видно',
|
||||
),
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
@@ -427,17 +407,31 @@ class _GroupEditScreenState extends ConsumerState<GroupEditScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
bool _sameSet(Set<String> left, Set<String> right) {
|
||||
if (left.length != right.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int _summaryInt(Map<String, dynamic> summary, String key) {
|
||||
final value = summary[key];
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
for (final item in left) {
|
||||
if (!right.contains(item)) {
|
||||
return false;
|
||||
return int.tryParse(value?.toString() ?? '') ?? 0;
|
||||
}
|
||||
|
||||
String formatRescanSummary(Map<String, dynamic> summary) {
|
||||
final found = _summaryInt(summary, 'found');
|
||||
final added = _summaryInt(summary, 'added');
|
||||
final updated = _summaryInt(summary, 'updated');
|
||||
final removed = _summaryInt(summary, 'removed_offline');
|
||||
|
||||
if (added == 0 && updated == 0 && removed == 0 && found == 0) {
|
||||
return 'Сканирование завершено: устройства не найдены';
|
||||
}
|
||||
|
||||
if (added == 0 && removed == 0) {
|
||||
return 'Сканирование завершено: найдено $found, обновлено $updated';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Сканирование завершено: найдено $found, новых $added, обновлено $updated, убрано $removed';
|
||||
}
|
||||
|
||||
class _ConflictBanner extends StatelessWidget {
|
||||
|
||||
@@ -82,6 +82,39 @@ void main() {
|
||||
expect(api.createdGroupMacs, ['AA:BB']);
|
||||
});
|
||||
|
||||
testWidgets('group edit screen shows backend rescan summary', (
|
||||
tester,
|
||||
) async {
|
||||
final api = FakeIgnisApi(
|
||||
devicesData: {
|
||||
'devices': [
|
||||
{'mac': 'AA:BB', 'name': 'Лампа 1'},
|
||||
],
|
||||
},
|
||||
groupsData: <Object>[],
|
||||
)..rescanNetworkData = {
|
||||
'status': 'ok',
|
||||
'found': 3,
|
||||
'added': 1,
|
||||
'updated': 2,
|
||||
'removed_offline': 1,
|
||||
'pending_removal': 0,
|
||||
'online': 3,
|
||||
};
|
||||
|
||||
await pumpTestApp(tester, child: const GroupEditScreen(), api: api);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byTooltip('Пересканировать сеть'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.text('Сканирование завершено: найдено 3, новых 1, обновлено 2, убрано 1'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(api.rescanCalls, 1);
|
||||
});
|
||||
|
||||
testWidgets('api keys screen validates and shows created key banner', (
|
||||
tester,
|
||||
) async {
|
||||
|
||||
@@ -36,6 +36,7 @@ class FakeIgnisApi extends IgnisApi {
|
||||
Object? eventLogData;
|
||||
Object? apiKeysData;
|
||||
Object? authData;
|
||||
Object? rescanNetworkData;
|
||||
|
||||
Object? devicesError;
|
||||
Object? scenesError;
|
||||
@@ -329,7 +330,17 @@ class FakeIgnisApi extends IgnisApi {
|
||||
if (error != null) throw error;
|
||||
return Response(
|
||||
requestOptions: RequestOptions(path: '/devices/rescan'),
|
||||
data: <String, dynamic>{'ok': true},
|
||||
data:
|
||||
rescanNetworkData ??
|
||||
<String, dynamic>{
|
||||
'status': 'ok',
|
||||
'found': 0,
|
||||
'added': 0,
|
||||
'updated': 0,
|
||||
'removed_offline': 0,
|
||||
'pending_removal': 0,
|
||||
'online': 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user