217 lines
6.9 KiB
Dart
217 lines
6.9 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:miaoji_zhang/shared/api/api_client.dart';
|
|
import 'package:miaoji_zhang/shared/services/local_database.dart';
|
|
import 'package:miaoji_zhang/shared/services/session_store.dart';
|
|
|
|
class LedgerInfo {
|
|
final int id;
|
|
final String name;
|
|
final String iconKey;
|
|
final bool isDefault;
|
|
final int transactionCount;
|
|
|
|
const LedgerInfo({
|
|
required this.id,
|
|
required this.name,
|
|
required this.iconKey,
|
|
required this.isDefault,
|
|
required this.transactionCount,
|
|
});
|
|
|
|
factory LedgerInfo.fromJson(Map<String, dynamic> json) => LedgerInfo(
|
|
id: (json['id'] as num).toInt(),
|
|
name: json['name'] as String,
|
|
iconKey: json['iconKey'] as String? ?? 'wallet',
|
|
isDefault: json['isDefault'] as bool? ?? false,
|
|
transactionCount: (json['txCount'] as num?)?.toInt() ?? 0,
|
|
);
|
|
}
|
|
|
|
class CurrentLedgerStore extends ChangeNotifier {
|
|
CurrentLedgerStore._();
|
|
|
|
static final instance = CurrentLedgerStore._();
|
|
final _dio = ApiClient.instance.dio;
|
|
|
|
List<LedgerInfo> _ledgers = const [];
|
|
LedgerInfo? _current;
|
|
Future<void>? _loading;
|
|
|
|
List<LedgerInfo> get ledgers => List.unmodifiable(_ledgers);
|
|
LedgerInfo? get current => _current;
|
|
int? get currentId => _current?.id;
|
|
String get currentName => _current?.name ?? '账本';
|
|
|
|
Future<void> ensureLoaded({bool force = false}) {
|
|
if (!force && _current != null) return Future.value();
|
|
return _loading ??= _load().whenComplete(() => _loading = null);
|
|
}
|
|
|
|
Future<void> loadCached({bool force = false}) {
|
|
if (!force && _current != null) return Future.value();
|
|
return _loading ??= _loadCached().whenComplete(() => _loading = null);
|
|
}
|
|
|
|
Future<void> refreshRemote() async {
|
|
if (!SessionStore.instance.isAccount ||
|
|
SessionStore.instance.shouldUseLocalOnly) {
|
|
return;
|
|
}
|
|
final response = await _dio.get('/api/ledgers');
|
|
final values = response.data as List;
|
|
LocalDatabase.instance.cacheLedgers(values);
|
|
_apply(values);
|
|
}
|
|
|
|
Future<void> _loadCached() async {
|
|
_apply(LocalDatabase.instance.ledgers());
|
|
}
|
|
|
|
Future<void> _load() async {
|
|
final session = SessionStore.instance;
|
|
List<dynamic> values;
|
|
if (session.shouldUseLocalOnly) {
|
|
values = LocalDatabase.instance.ledgers();
|
|
} else {
|
|
try {
|
|
final response = await _dio.get('/api/ledgers');
|
|
values = response.data as List;
|
|
LocalDatabase.instance.cacheLedgers(values);
|
|
} catch (error) {
|
|
if (!isConnectivityError(error) || !session.isAccount) rethrow;
|
|
values = LocalDatabase.instance.ledgers();
|
|
}
|
|
}
|
|
_apply(values);
|
|
}
|
|
|
|
void _apply(List<dynamic> values) {
|
|
final items = values
|
|
.map((item) => LedgerInfo.fromJson(item as Map<String, dynamic>))
|
|
.toList();
|
|
final current =
|
|
items.where((item) => item.isDefault).firstOrNull ??
|
|
(items.isEmpty ? null : items.first);
|
|
final changed =
|
|
_current?.id != current?.id ||
|
|
_ledgers.length != items.length ||
|
|
Iterable<int>.generate(items.length).any((index) {
|
|
final before = _ledgers[index];
|
|
final after = items[index];
|
|
return before.id != after.id ||
|
|
before.name != after.name ||
|
|
before.iconKey != after.iconKey ||
|
|
before.isDefault != after.isDefault ||
|
|
before.transactionCount != after.transactionCount;
|
|
});
|
|
_ledgers = items;
|
|
_current = current;
|
|
if (changed) notifyListeners();
|
|
}
|
|
|
|
Future<void> select(int id) async {
|
|
if (_current?.id == id) return;
|
|
final session = SessionStore.instance;
|
|
if (session.shouldUseLocalOnly || id < 0) {
|
|
LocalDatabase.instance.selectLedger(id);
|
|
if (session.isAccount && session.cloudSyncEnabled) {
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'select', {'id': id});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
return;
|
|
}
|
|
try {
|
|
await _dio.put('/api/ledgers/$id/default');
|
|
} catch (error) {
|
|
if (!isConnectivityError(error)) rethrow;
|
|
LocalDatabase.instance.selectLedger(id);
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'select', {'id': id});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
}
|
|
|
|
Future<void> create(String name) async {
|
|
final session = SessionStore.instance;
|
|
if (session.shouldUseLocalOnly) {
|
|
final id = LocalDatabase.instance.createLedger(name.trim());
|
|
if (session.isAccount && session.cloudSyncEnabled) {
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'create', {
|
|
'name': name.trim(),
|
|
'iconKey': 'wallet',
|
|
});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
return;
|
|
}
|
|
try {
|
|
await _dio.post(
|
|
'/api/ledgers',
|
|
data: {'name': name.trim(), 'iconKey': 'wallet'},
|
|
);
|
|
} catch (error) {
|
|
if (!isConnectivityError(error)) rethrow;
|
|
final id = LocalDatabase.instance.createLedger(name.trim());
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'create', {
|
|
'name': name.trim(),
|
|
'iconKey': 'wallet',
|
|
});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
}
|
|
|
|
Future<void> rename(int id, String name, String iconKey) async {
|
|
final session = SessionStore.instance;
|
|
if (session.shouldUseLocalOnly || id < 0) {
|
|
LocalDatabase.instance.renameLedger(id, name.trim(), iconKey);
|
|
if (session.isAccount && session.cloudSyncEnabled) {
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'update', {
|
|
'name': name.trim(),
|
|
'iconKey': iconKey,
|
|
});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
return;
|
|
}
|
|
try {
|
|
await _dio.put(
|
|
'/api/ledgers/$id',
|
|
data: {'name': name.trim(), 'iconKey': iconKey},
|
|
);
|
|
} catch (error) {
|
|
if (!isConnectivityError(error)) rethrow;
|
|
LocalDatabase.instance.renameLedger(id, name.trim(), iconKey);
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'update', {
|
|
'name': name.trim(),
|
|
'iconKey': iconKey,
|
|
});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
}
|
|
|
|
Future<void> delete(int id) async {
|
|
final session = SessionStore.instance;
|
|
if (session.shouldUseLocalOnly || id < 0) {
|
|
LocalDatabase.instance.deleteLedger(id);
|
|
if (session.isAccount && session.cloudSyncEnabled) {
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'delete', {'id': id});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
return;
|
|
}
|
|
try {
|
|
await _dio.delete('/api/ledgers/$id');
|
|
} catch (error) {
|
|
if (!isConnectivityError(error)) rethrow;
|
|
LocalDatabase.instance.deleteLedger(id);
|
|
LocalDatabase.instance.enqueueSync('ledger', id, 'delete', {'id': id});
|
|
}
|
|
await ensureLoaded(force: true);
|
|
}
|
|
|
|
void clear() {
|
|
_ledgers = const [];
|
|
_current = null;
|
|
notifyListeners();
|
|
}
|
|
}
|