feat: make core pages local-first offline

This commit is contained in:
2026-08-21 12:40:01 +08:00
parent 8828851631
commit fefa2d74e6
20 changed files with 921 additions and 188 deletions
+25 -11
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:miaoji_zhang/shared/api/api_client.dart';
import 'package:miaoji_zhang/shared/api/business_api.dart';
@@ -6,6 +8,7 @@ import 'package:miaoji_zhang/shared/services/current_ledger_store.dart';
import 'package:miaoji_zhang/shared/theme/app_theme.dart';
import 'package:miaoji_zhang/shared/widgets/app_icons.dart';
import 'package:miaoji_zhang/shared/widgets/async_error_view.dart';
import 'package:miaoji_zhang/shared/widgets/backend_status_icon.dart';
import 'package:miaoji_zhang/shared/widgets/category_icon.dart';
import 'package:miaoji_zhang/shared/services/shanghai_time.dart';
import 'package:miaoji_zhang/shared/services/session_store.dart';
@@ -25,13 +28,14 @@ class _ReportPageState extends State<ReportPage> {
String? _error;
DateTime _anchor = ShanghaiTime.now;
_ReportKind _kind = _ReportKind.monthly;
int _loadRevision = 0;
@override
void initState() {
super.initState();
PublicConfigApi.companionNotifier.addListener(_refreshCompanion);
CurrentLedgerStore.instance.addListener(_load);
_load();
unawaited(_load());
}
@override
@@ -46,24 +50,33 @@ class _ReportPageState extends State<ReportPage> {
}
Future<void> _load() async {
final revision = ++_loadRevision;
setState(() {
_loading = true;
_error = null;
});
try {
final report = switch (_kind) {
_ReportKind.weekly => await ReportApi.weekly(_anchor),
_ReportKind.monthly => await ReportApi.monthlyPeriod(
_anchor.year,
_anchor.month,
),
_ReportKind.yearly => await ReportApi.yearly(_anchor.year),
final period = switch (_kind) {
_ReportKind.weekly => 'week',
_ReportKind.monthly => 'month',
_ReportKind.yearly => 'year',
};
if (mounted) setState(() => _report = report);
final local = ReportApi.local(period, _anchor);
if (mounted && revision == _loadRevision) {
setState(() => _report = local);
}
final remote = await ReportApi.remote(period, _anchor);
if (mounted && revision == _loadRevision) {
setState(() => _report = remote);
}
} catch (error) {
if (mounted) setState(() => _error = apiErrorMessage(error));
if (mounted && revision == _loadRevision && _report == null) {
setState(() => _error = apiErrorMessage(error));
}
} finally {
if (mounted) setState(() => _loading = false);
if (mounted && revision == _loadRevision) {
setState(() => _loading = false);
}
}
}
@@ -108,6 +121,7 @@ class _ReportPageState extends State<ReportPage> {
return Scaffold(
appBar: AppBar(
title: Text(SessionStore.instance.aiEnabled ? 'AI 报告' : '报告'),
actions: [BackendStatusIcon(onRetry: _load)],
),
body: Column(
children: [