TimeZone support

This commit is contained in:
2026-03-21 20:30:37 -05:00
parent 64f6024b4e
commit 06b71d6caf
7 changed files with 295 additions and 378 deletions

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
import 'package:blind_master/BlindMasterResources/timezone_picker.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
@@ -21,19 +22,28 @@ class EditGroupDialog extends StatefulWidget {
}
class _EditGroupDialogState extends State<EditGroupDialog> {
late final TextEditingController _nameController;
List<Map<String, dynamic>> devices = [];
Map<int, List<Map<String, dynamic>>> devicePeripherals = {};
Set<int> selectedPeripheralIds = {};
String? _selectedTimezone;
bool isLoading = true;
String? errorMessage;
@override
void initState() {
super.initState();
_nameController = TextEditingController();
selectedPeripheralIds = widget.currentPeripheralIds.toSet();
_loadDevicesAndPeripherals();
}
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
Future<void> _loadDevicesAndPeripherals() async {
setState(() {
isLoading = true;
@@ -41,6 +51,16 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
});
try {
// Fetch current group timezone
final tzResponse = await secureGet(
'group_timezone',
queryParameters: {'groupId': widget.groupId.toString()},
);
if (tzResponse != null && tzResponse.statusCode == 200) {
final tzBody = jsonDecode(tzResponse.body);
_selectedTimezone = tzBody['timezone'] as String?;
}
// Fetch devices
final devicesResponse = await secureGet('device_list');
if (devicesResponse == null || devicesResponse.statusCode != 200) {
@@ -89,20 +109,42 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
}
Future<void> _updateGroup() async {
final newName = _nameController.text.trim();
if (selectedPeripheralIds.length < 2) {
setState(() {
errorMessage = 'Please select at least 2 blinds';
});
setState(() { errorMessage = 'Please select at least 2 blinds'; });
return;
}
try {
// Rename if a new name was entered
if (newName.isNotEmpty) {
final renameResponse = await securePost(
{'groupId': widget.groupId, 'newName': newName},
'rename_group',
);
if (renameResponse == null) throw Exception('No response');
if (renameResponse.statusCode == 409) {
setState(() { errorMessage = 'A group with this name already exists'; });
return;
}
if (renameResponse.statusCode != 200) throw Exception('Failed to rename group');
}
// Update timezone if set
if (_selectedTimezone != null) {
final tzResponse = await securePost(
{'groupId': widget.groupId, 'timezone': _selectedTimezone},
'update_group_timezone',
);
if (tzResponse == null || tzResponse.statusCode != 200) {
throw Exception('Failed to update timezone');
}
}
// Update members
final response = await securePost(
{
'groupId': widget.groupId,
'peripheral_ids': selectedPeripheralIds.toList(),
},
'update_group'
{'groupId': widget.groupId, 'peripheral_ids': selectedPeripheralIds.toList()},
'update_group',
);
if (response != null && response.statusCode == 200) {
@@ -113,22 +155,16 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
backgroundColor: Colors.green,
),
);
Navigator.of(context).pop();
Navigator.of(context).pop(newName.isNotEmpty ? newName : null);
}
} else if (response != null && response.statusCode == 409) {
final errorBody = jsonDecode(response.body);
setState(() {
errorMessage = errorBody['error'] ?? 'This combination of blinds already exists';
});
setState(() { errorMessage = errorBody['error'] ?? 'This combination of blinds already exists'; });
} else {
setState(() {
errorMessage = 'Failed to update group';
});
setState(() { errorMessage = 'Failed to update group'; });
}
} catch (e) {
setState(() {
errorMessage = 'Error updating group: ${e.toString()}';
});
setState(() { errorMessage = 'Error updating group: ${e.toString()}'; });
}
}
@@ -146,11 +182,28 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _nameController,
decoration: InputDecoration(
labelText: 'New Group Name (blank to keep current)',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
),
onChanged: (_) {
if (errorMessage != null) setState(() => errorMessage = null);
},
),
const SizedBox(height: 12),
TimezonePicker(
value: _selectedTimezone,
label: 'Timezone (blank to keep current)',
onChanged: (tz) => setState(() => _selectedTimezone = tz),
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: selectedPeripheralIds.length >= 2
? Theme.of(context).primaryColorLight.withValues(alpha: 0.5)
? Theme.of(context).primaryColorLight.withValues(alpha: 0.5)
: Colors.orange.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(10),
),