feat(app): P0 batch 3 — offline detection with engine pause/resume + UI banner

P0-4 离线感知:
  - connectivity_plus → NetworkAvailability(online/offline/unknown)
  - engine_scope 三重绑定:有账号+有网 → start;任一失去 → stop(在途不中断)
  - 网络恢复 → start + onNetworkRestored(waitingNetwork 即时重试)
  - HomePage 顶部离线黄条(wifi_off + i18n 双语)

P0-5 更正:EXIF 方向已由服务端 sharp .rotate() 自动转正——v2 分析误报

397/397 全绿、analyze 0

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-28 11:23:51 +08:00
parent 638dd9b45d
commit 33f347cdea
11 changed files with 643 additions and 6 deletions
+42 -3
View File
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../app/locale_controller.dart';
import '../core/api_client.dart';
import '../core/network_status.dart';
import '../features/albums/providers.dart';
import '../features/uploads/engine_scope.dart';
import '../features/uploads/queue_controller.dart';
@@ -38,10 +39,48 @@ class _HomePageState extends ConsumerState<HomePage> {
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final badgeCount = ref.watch(uploadBadgeCountProvider);
// P0-4:离线黄条(网络恢复自动消失)。
final network = ref.watch(networkStatusProvider).value;
final offline = network == NetworkAvailability.offline;
return Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: const [AlbumsTab(), QueueTab(), SettingsTab()],
body: Column(
children: [
if (offline)
Material(
color: Theme.of(context).colorScheme.tertiaryContainer,
child: SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Icon(
Icons.wifi_off_rounded,
size: 16,
color: Theme.of(context).colorScheme.onTertiaryContainer,
),
const SizedBox(width: 8),
Expanded(
child: Text(
l10n.networkOfflineBanner,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color:
Theme.of(context).colorScheme.onTertiaryContainer,
),
),
),
],
),
),
),
),
Expanded(
child: IndexedStack(
index: _selectedIndex,
children: const [AlbumsTab(), QueueTab(), SettingsTab()],
),
),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,