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/error_snackbar.dart';
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
import 'package:blind_master/BlindMasterResources/timezone_picker.dart';
import 'package:blind_master/BlindMasterScreens/accountManagement/change_password_screen.dart';
import 'package:blind_master/BlindMasterScreens/accountManagement/change_email_screen.dart';
import 'package:flutter/material.dart';
@@ -17,6 +18,7 @@ class _AccountScreenState extends State<AccountScreen> {
String? name;
String? email;
String? createdAt;
String? timezone;
bool isLoading = true;
@override
@@ -38,7 +40,8 @@ class _AccountScreenState extends State<AccountScreen> {
setState(() {
name = body['name'] ?? 'N/A';
email = body['email'] ?? 'N/A';
timezone = body['timezone'] as String?;
// Parse and format the created_at timestamp
if (body['created_at'] != null) {
try {
@@ -67,6 +70,64 @@ class _AccountScreenState extends State<AccountScreen> {
}
}
String _tzAbbrev(String? ianaKey) {
if (ianaKey == null) return 'None (UTC)';
final match = kTimezones.firstWhere(
(tz) => tz['tz'] == ianaKey,
orElse: () => {'display': ianaKey, 'tz': ianaKey},
);
final display = match['display']!;
final parenMatch = RegExp(r'\(([^)]+)\)').firstMatch(display);
return parenMatch?.group(1) ?? display;
}
Future<void> _handleChangeTimezone() async {
String? selected = timezone;
final confirmed = await showDialog<bool>(
context: context,
builder: (dialogContext) {
return StatefulBuilder(
builder: (context, setDialogState) {
return AlertDialog(
title: const Text('Account Timezone'),
content: TimezonePicker(
value: selected,
label: 'Default Timezone',
onChanged: (tz) => setDialogState(() => selected = tz),
),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(false),
child: const Text('Cancel'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColorLight,
foregroundColor: Colors.white,
),
onPressed: () => Navigator.of(dialogContext).pop(true),
child: const Text('Save'),
),
],
);
},
);
},
);
if (confirmed == true && selected != null && mounted) {
try {
final response = await securePost({'timezone': selected}, 'update_user_timezone');
if (response == null) throw Exception('No response');
if (response.statusCode != 200) throw Exception('Failed to update timezone');
setState(() => timezone = selected);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e));
}
}
}
Future<void> _handleDeleteAccount() async {
final primaryColor = Theme.of(context).primaryColorLight;
@@ -268,6 +329,13 @@ class _AccountScreenState extends State<AccountScreen> {
);
},
),
Divider(height: 1),
ListTile(
leading: const Icon(Icons.access_time),
title: Text('Timezone: ${_tzAbbrev(timezone)}'),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: _handleChangeTimezone,
),
],
),
),