Fix local dev config and ALOTTTT OFF FLUTTER, im tired boss..
This commit is contained in:
+91
-33
@@ -13,6 +13,7 @@ import '../../../app/injection_container.dart';
|
||||
import '../../../core/network/api_client.dart';
|
||||
import '../../../core/services/websocket_service.dart';
|
||||
import '../../../core/storage/secure_storage.dart';
|
||||
import '../../../core/utils/operation_guard.dart';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// GUARDIAN DASHBOARD SCREEN
|
||||
@@ -57,6 +58,7 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
duration: const Duration(milliseconds: 600),
|
||||
);
|
||||
bool _sosAlert = false;
|
||||
List<Map<String, dynamic>> _pendingSos = const [];
|
||||
|
||||
// ── Refresh button animation ─────────────────────────────────────────────────
|
||||
late final AnimationController _refreshCtrl = AnimationController(
|
||||
@@ -89,7 +91,8 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
_error = null;
|
||||
});
|
||||
}
|
||||
try {
|
||||
await guarded<void>(
|
||||
() async {
|
||||
_guardianName =
|
||||
await sl<SecureStorage>().getDisplayName() ?? 'Guardian';
|
||||
|
||||
@@ -103,7 +106,8 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
final dashboard = results[0] as Map<String, dynamic>?;
|
||||
final activityList =
|
||||
results[1] as List<Map<String, dynamic>>;
|
||||
final sosPending = results[2] as int;
|
||||
final sosPendingEvents = results[2] as List<Map<String, dynamic>>;
|
||||
final sosPending = sosPendingEvents.length;
|
||||
|
||||
// Extract latest GPS from dashboard
|
||||
final lastLoc = dashboard?['lastLocation'] as Map<String, dynamic>?;
|
||||
@@ -150,6 +154,7 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
recentActivity: activityList,
|
||||
isPaired: userStatus != null || dashboard != null,
|
||||
);
|
||||
_pendingSos = sosPendingEvents;
|
||||
if (newLatLng != null) {
|
||||
_liveLatLng = newLatLng;
|
||||
}
|
||||
@@ -163,32 +168,31 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
|
||||
// Move map to latest location
|
||||
if (newLatLng != null) {
|
||||
try {
|
||||
_mapController.move(newLatLng, 15);
|
||||
} catch (_) {}
|
||||
_moveMapSafely(newLatLng);
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
},
|
||||
onError: (e) => setState(() {
|
||||
_loading = false;
|
||||
_error = _friendlyError(e);
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> _fetchDashboard() async {
|
||||
try {
|
||||
return await guarded<Map<String, dynamic>?>(
|
||||
() async {
|
||||
final res = await _api
|
||||
.get('/guardian/dashboard')
|
||||
.timeout(const Duration(seconds: 8));
|
||||
final d = res.data['data'];
|
||||
return d is Map ? Map<String, dynamic>.from(d) : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> _fetchActivity() async {
|
||||
try {
|
||||
return await guarded<List<Map<String, dynamic>>>(
|
||||
() async {
|
||||
final res = await _api
|
||||
.get('/guardian/activity-logs',
|
||||
queryParameters: {'size': 5, 'page': 0})
|
||||
@@ -202,12 +206,15 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
.map((e) => Map<String, dynamic>.from(e))
|
||||
.toList();
|
||||
}
|
||||
} catch (_) {}
|
||||
return const [];
|
||||
return const [];
|
||||
},
|
||||
) ??
|
||||
const [];
|
||||
}
|
||||
|
||||
Future<int> _fetchSosPending() async {
|
||||
try {
|
||||
Future<List<Map<String, dynamic>>> _fetchSosPending() async {
|
||||
return await guarded<List<Map<String, dynamic>>>(
|
||||
() async {
|
||||
final res = await _api
|
||||
.get('/guardian/sos-events',
|
||||
queryParameters: {'size': 10, 'page': 0})
|
||||
@@ -219,17 +226,21 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
return content
|
||||
.whereType<Map>()
|
||||
.where((e) => e['status'] == 'TRIGGERED')
|
||||
.length;
|
||||
.map((e) => Map<String, dynamic>.from(e))
|
||||
.toList();
|
||||
}
|
||||
} catch (_) {}
|
||||
return 0;
|
||||
return const [];
|
||||
},
|
||||
) ??
|
||||
const [];
|
||||
}
|
||||
|
||||
// ── WebSocket subscription ──────────────────────────────────────────────────
|
||||
void _subscribeWebSocket() {
|
||||
final ws = sl<WebSocketService>();
|
||||
Future.microtask(() async {
|
||||
try {
|
||||
await guarded<void>(
|
||||
() async {
|
||||
final userId = await _getLinkedUserId();
|
||||
if (userId == null) return;
|
||||
ws.subscribeLocation(userId, (lat, lng) {
|
||||
@@ -239,26 +250,30 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
_liveLatLng = newPos;
|
||||
_liveConnected = true;
|
||||
});
|
||||
try {
|
||||
_mapController.move(newPos, 15);
|
||||
} catch (_) {}
|
||||
_moveMapSafely(newPos);
|
||||
});
|
||||
ws.subscribeSos((sosData) {
|
||||
if (!mounted) return;
|
||||
_triggerSosFlash();
|
||||
setState(() {
|
||||
_pendingSos = [
|
||||
Map<String, dynamic>.from(sosData),
|
||||
..._pendingSos,
|
||||
];
|
||||
_data = _data?.copyWith(
|
||||
unreadSos: (_data?.unreadSos ?? 0) + 1);
|
||||
});
|
||||
_showSosSnackbar(sosData);
|
||||
});
|
||||
if (mounted) setState(() => _liveConnected = true);
|
||||
} catch (_) {}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<String?> _getLinkedUserId() async {
|
||||
try {
|
||||
return await guarded<String?>(
|
||||
() async {
|
||||
final res = await _api
|
||||
.get('/shared/pairing/status')
|
||||
.timeout(const Duration(seconds: 5));
|
||||
@@ -267,8 +282,9 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
return d['pairedWithId']?.toString() ??
|
||||
d['userId']?.toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _triggerSosFlash() {
|
||||
@@ -303,14 +319,56 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
),
|
||||
]),
|
||||
action: SnackBarAction(
|
||||
label: 'Lihat',
|
||||
label: 'Tangani',
|
||||
textColor: Colors.white,
|
||||
onPressed: () => context.go('/guardian/logs'),
|
||||
onPressed: _handleLatestSos,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleLatestSos() async {
|
||||
final sosId = _pendingSos
|
||||
.map((e) => e['id'])
|
||||
.where((id) => id != null)
|
||||
.map((id) => int.tryParse(id.toString()))
|
||||
.firstWhere((id) => id != null, orElse: () => null);
|
||||
if (sosId == null) {
|
||||
await _loadAll(silent: true);
|
||||
return;
|
||||
}
|
||||
await guarded<void>(
|
||||
() async {
|
||||
await _api
|
||||
.put('/guardian/sos/$sosId/resolve')
|
||||
.timeout(const Duration(seconds: 8));
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_pendingSos =
|
||||
_pendingSos.where((e) => e['id']?.toString() != '$sosId').toList();
|
||||
_data = _data?.copyWith(unreadSos: _pendingSos.length);
|
||||
if (_pendingSos.isEmpty) {
|
||||
_sosAlert = false;
|
||||
_sosCtrl.stop();
|
||||
}
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('SOS ditandai sudah ditangani.')),
|
||||
);
|
||||
},
|
||||
onError: (_) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Gagal menandai SOS. Coba refresh.')),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _moveMapSafely(LatLng position) {
|
||||
guarded<void>(() async => _mapController.move(position, 15));
|
||||
}
|
||||
|
||||
Future<void> _refresh() async {
|
||||
HapticFeedback.lightImpact();
|
||||
_refreshCtrl.forward(from: 0);
|
||||
@@ -517,10 +575,10 @@ class _GuardianDashboardScreenState extends State<GuardianDashboardScreen>
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => context.go('/guardian/logs'),
|
||||
onPressed: _handleLatestSos,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white),
|
||||
child: const Text('Tangani'),
|
||||
child: const Text('Handle'),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
|
||||
Reference in New Issue
Block a user