Fix local dev config and ALOTTTT OFF FLUTTER, im tired boss..

This commit is contained in:
Wowieee4
2026-05-26 21:09:13 +07:00
parent b8ad8df993
commit a629357e8c
72 changed files with 2566 additions and 530 deletions
@@ -0,0 +1,131 @@
import 'package:flutter/services.dart';
import '../network/api_client.dart';
enum HardwareShortcutAction {
callGuardian,
startWalkguide,
stopWalkguide,
sendSos,
openNotification,
}
class HardwareShortcutBinding {
final HardwareShortcutAction action;
final int buttonCode;
final String buttonName;
final bool enabled;
const HardwareShortcutBinding({
required this.action,
required this.buttonCode,
required this.buttonName,
required this.enabled,
});
}
class HardwareShortcutListener {
final ApiClient _apiClient;
final Map<int, HardwareShortcutBinding> _bindings = {};
bool _listening = false;
void Function(HardwareShortcutAction action)? _onAction;
void Function(int buttonCode, String buttonName)? _captureCallback;
HardwareShortcutListener(this._apiClient);
Future<void> startListening({
required void Function(HardwareShortcutAction action) onAction,
}) async {
_onAction = onAction;
if (!_listening) {
HardwareKeyboard.instance.addHandler(_handleKeyEvent);
_listening = true;
}
await loadFromBackend();
}
void stopListening() {
if (!_listening) return;
HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
_listening = false;
}
Future<void> loadFromBackend() async {
final response = await _apiClient.dio.get('/user/shortcuts');
final body = response.data;
final data = body is Map ? body['data'] : body;
if (data is! List) return;
_bindings
..clear()
..addEntries(
data
.whereType<Map>()
.map((item) => _bindingFromJson(Map<String, dynamic>.from(item)))
.whereType<HardwareShortcutBinding>()
.map((binding) => MapEntry(binding.buttonCode, binding)),
);
}
void captureNextButton(void Function(int buttonCode, String buttonName) onCapture) {
_captureCallback = onCapture;
}
bool _handleKeyEvent(KeyEvent event) {
if (event is! KeyDownEvent) return false;
final code = _buttonCode(event.logicalKey);
final name = event.logicalKey.keyLabel.isNotEmpty
? event.logicalKey.keyLabel
: event.logicalKey.debugName ?? 'Button $code';
final capture = _captureCallback;
if (capture != null) {
_captureCallback = null;
capture(code, name);
return true;
}
final binding = _bindings[code];
if (binding == null || !binding.enabled) return false;
_onAction?.call(binding.action);
return true;
}
int _buttonCode(LogicalKeyboardKey key) {
if (key == LogicalKeyboardKey.audioVolumeUp) return 24;
if (key == LogicalKeyboardKey.audioVolumeDown) return 25;
return key.keyId & 0xFFFFFFFF;
}
}
HardwareShortcutBinding? _bindingFromJson(Map<String, dynamic> item) {
final action = _actionFromBackend(item['shortcutKey']?.toString());
final rawCode = item['buttonCode'];
final enabled = item['enabled'] != false;
final code = rawCode is int ? rawCode : int.tryParse(rawCode?.toString() ?? '');
if (action == null || code == null || code <= 0) return null;
return HardwareShortcutBinding(
action: action,
buttonCode: code,
buttonName: item['buttonName']?.toString() ?? 'Button $code',
enabled: enabled,
);
}
HardwareShortcutAction? _actionFromBackend(String? key) {
switch (key) {
case 'CALL_GUARDIAN':
return HardwareShortcutAction.callGuardian;
case 'START_WALKGUIDE':
return HardwareShortcutAction.startWalkguide;
case 'STOP_WALKGUIDE':
return HardwareShortcutAction.stopWalkguide;
case 'SEND_SOS':
return HardwareShortcutAction.sendSos;
case 'OPEN_NOTIFICATION':
return HardwareShortcutAction.openNotification;
default:
return null;
}
}
@@ -1,8 +1,5 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../network/api_client.dart';
import '../storage/local_database.dart';
class OfflineRequest {
final String method;
@@ -33,26 +30,35 @@ class OfflineRequest {
}
class OfflineQueueService {
static const _key = 'offline_request_queue';
final LocalDatabase _database;
OfflineQueueService(this._database);
Future<void> enqueue(OfflineRequest request) async {
final prefs = await SharedPreferences.getInstance();
final queue = await readAll();
queue.add(request);
await prefs.setString(_key, jsonEncode(queue.map((e) => e.toJson()).toList()));
await _database.offlineRequests.insert(OfflineRequestRecord(
method: request.method,
path: request.path,
body: request.body,
createdAt: request.createdAt,
));
}
Future<List<OfflineRequest>> readAll() async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_key);
if (raw == null || raw.isEmpty) return [];
final decoded = jsonDecode(raw) as List<dynamic>;
return decoded.map((e) => OfflineRequest.fromJson(Map<String, dynamic>.from(e as Map))).toList();
final records = await _database.offlineRequests.getAll();
return records
.map(
(record) => OfflineRequest(
method: record.method,
path: record.path,
body: record.body,
createdAt: record.createdAt,
),
)
.toList();
}
Future<void> clear() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_key);
await _database.offlineRequests.clear();
}
Future<int> syncPending(ApiClient apiClient) async {
@@ -79,11 +85,21 @@ class OfflineQueueService {
}
}
final prefs = await SharedPreferences.getInstance();
if (remaining.isEmpty) {
await prefs.remove(_key);
await clear();
} else {
await prefs.setString(_key, jsonEncode(remaining.map((e) => e.toJson()).toList()));
await _database.offlineRequests.replaceAll(
remaining
.map(
(request) => OfflineRequestRecord(
method: request.method,
path: request.path,
body: request.body,
createdAt: request.createdAt,
),
)
.toList(),
);
}
return synced;
}