Files
2026-10-03 15:00:55 +05:30

180 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;
/// Single source of truth for time-zone display prefs (v1: local-only).
/// Mirrors LocaleProvider: ChangeNotifier + SharedPreferences + notifyListeners.
/// Backend sync (AppUser.multitime) is a later phase; for now prefs stay on-device
/// so the settings card in the screenshot works standalone + multilingual.
class TimezoneProvider with ChangeNotifier {
static const _zoneKey = 'app_timezone_id';
static const _patternKey = 'app_date_pattern';
static const _is24hKey = 'app_time_24h';
static const _weekStartKey = 'app_week_start';
/// Curated quick-pick list matching the settings mock.
static const commonZoneIds = [
'Asia/Kolkata',
'Asia/Dubai',
'Asia/Singapore',
'Asia/Kuala_Lumpur',
'Asia/Hong_Kong',
'Asia/Shanghai',
'Asia/Taipei',
'Asia/Tokyo',
'Asia/Seoul',
'Asia/Bangkok',
'Asia/Jakarta',
'Asia/Manila',
'Asia/Karachi',
'Europe/London',
'Europe/Paris',
'Europe/Berlin',
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Los_Angeles',
'America/Toronto',
'Australia/Sydney',
'UTC',
];
static const datePatterns = ['dd-MM-yyyy', 'MM-dd-yyyy', 'yyyy-MM-dd'];
static const weekDays = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
];
String _zoneId = 'Asia/Kolkata';
String get zoneId => _zoneId;
String _datePattern = 'dd-MM-yyyy';
String get datePattern => _datePattern;
bool _is24h = true;
bool get is24h => _is24h;
String _weekStart = 'Monday';
String get weekStart => _weekStart;
bool _ready = false;
bool get ready => _ready;
/// Short city label: 'Asia/Kolkata' -> 'Kolkata', 'UTC' -> 'UTC'.
static String cityOf(String id) {
if (id == 'UTC') return 'UTC';
final parts = id.split('/');
final raw = parts.isNotEmpty ? parts.last : id;
return raw.replaceAll('_', ' ');
}
Future<void> init() async {
final prefs = await SharedPreferences.getInstance();
final z = prefs.getString(_zoneKey);
if (z != null && z.isNotEmpty) _zoneId = z;
final p = prefs.getString(_patternKey);
if (p != null && datePatterns.contains(p)) _datePattern = p;
final h = prefs.getBool(_is24hKey);
if (h != null) _is24h = h;
final w = prefs.getString(_weekStartKey);
if (w != null && weekDays.contains(w)) _weekStart = w;
_ready = true;
notifyListeners();
}
Future<void> setZone(String id) async {
_zoneId = id;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_zoneKey, id);
notifyListeners();
}
Future<void> setDatePattern(String p) async {
if (!datePatterns.contains(p)) return;
_datePattern = p;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_patternKey, p);
notifyListeners();
}
Future<void> setIs24h(bool v) async {
_is24h = v;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_is24hKey, v);
notifyListeners();
}
Future<void> setWeekStart(String d) async {
if (!weekDays.contains(d)) return;
_weekStart = d;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_weekStartKey, d);
notifyListeners();
}
/// Explicit save (button in the mock). Values are already persisted on
/// change; this re-persists + notifies so callers can show a snackbar.
Future<void> save() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_zoneKey, _zoneId);
await prefs.setString(_patternKey, _datePattern);
await prefs.setBool(_is24hKey, _is24h);
await prefs.setString(_weekStartKey, _weekStart);
notifyListeners();
}
tz.Location get location {
try {
return tz.getLocation(_zoneId);
} catch (_) {
return tz.getLocation('UTC');
}
}
tz.TZDateTime nowInZone() {
try {
return tz.TZDateTime.now(location);
} catch (_) {
return tz.TZDateTime.now(tz.getLocation('UTC'));
}
}
/// e.g. 'GMT+5:30', 'GMT-4:00', 'GMT'. DST-aware via timezone DB.
String offsetLabel([tz.TZDateTime? at]) {
try {
final dt = at ?? nowInZone();
final offset = dt.timeZoneOffset;
if (offset == Duration.zero) return 'GMT';
final sign = offset.isNegative ? '-' : '+';
final abs = offset.abs();
final h = abs.inHours.toString();
final m = (abs.inMinutes % 60).toString().padLeft(2, '0');
return 'GMT$sign$h:$m';
} catch (_) {
return '';
}
}
/// 'Kolkata (GMT+5:30)' style label for dropdown rows.
String displayLabel(String id) {
try {
final loc = tz.getLocation(id);
final now = tz.TZDateTime.now(loc);
final offset = now.timeZoneOffset;
final sign = offset.isNegative ? '-' : '+';
final abs = offset.abs();
final off = offset == Duration.zero
? 'GMT'
: 'GMT$sign${abs.inHours}:${(abs.inMinutes % 60).toString().padLeft(2, '0')}';
return '${cityOf(id)} ($off)';
} catch (_) {
return cityOf(id);
}
}
}