Files
sm-emby-share/lib/app.dart
T
2026-07-14 11:11:36 +08:00

128 lines
4.1 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, TargetPlatform;
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:forui/forui.dart';
import 'features/update/update_dialog.dart';
import 'providers/appearance_provider.dart';
import 'providers/di_providers.dart';
import 'providers/server_sync_provider.dart';
import 'providers/sm_account_provider.dart';
import 'providers/update_provider.dart';
import 'router/app_router.dart' show appRouterProvider, rootNavigatorKey;
import 'shared/theme/app_scroll_behavior.dart';
import 'shared/theme/app_theme.dart';
import 'shared/utils/app_logger.dart';
class SmPlayerApp extends ConsumerStatefulWidget {
const SmPlayerApp({super.key});
@override
ConsumerState<SmPlayerApp> createState() => _SmPlayerAppState();
}
class _SmPlayerAppState extends ConsumerState<SmPlayerApp> {
bool _updateDialogShown = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
try {
final queue = await ref.read(traktSyncQueueProvider.future);
await queue.drain();
} catch (e) {
AppLogger.debug('Trakt', 'startup drain skipped', e);
}
unawaited(ref.read(updateProvider.notifier).silentCheck());
unawaited(_autoSyncPull());
});
}
Future<void> _autoSyncPull() async {
try {
final account = await ref.read(smAccountProvider.future);
if (!account.hasVip) return;
final outcome = await ref.read(serverSyncProvider.notifier).pull();
if (outcome.isError) {
AppLogger.debug('CloudSync', 'auto-pull failed: ${outcome.message}');
}
} catch (e) {
AppLogger.debug('CloudSync', 'auto-pull skipped', e);
}
}
@override
Widget build(BuildContext context) {
final router = ref.watch(appRouterProvider);
final appearance = ref.watch(appearanceProvider);
final flutterMode = appearance.maybeWhen(
data: (d) => d.themeMode,
orElse: () => ThemeMode.system,
);
return MaterialApp.router(
title: 'smPlayer',
debugShowCheckedModeBanner: false,
locale: const Locale('zh', 'CN'),
supportedLocales: const [Locale('zh', 'CN'), Locale('en')],
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: flutterMode,
scrollBehavior: const AppScrollBehavior(),
routerConfig: router,
builder: (context, child) {
final zinc = Theme.of(context).brightness == Brightness.dark
? FThemes.zinc.dark
: FThemes.zinc.light;
return FTheme(
data: _isTouchPlatform ? zinc.touch : zinc.desktop,
child: FToaster(
child: Consumer(
builder: (context, ref, _) {
ref.listen<UpdateState>(updateProvider, (previous, next) {
if (_updateDialogShown ||
next.status != UpdateStatus.available ||
!next.silent) {
return;
}
_updateDialogShown = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final navContext = rootNavigatorKey.currentContext;
if (navContext == null) return;
showFDialog(
context: navContext,
builder: (_, _, _) => const UpdateDialog(),
);
});
});
return child ?? const SizedBox();
},
child: child,
),
),
);
},
);
}
}
bool get _isTouchPlatform => switch (defaultTargetPlatform) {
TargetPlatform.android ||
TargetPlatform.iOS ||
TargetPlatform.fuchsia => true,
_ => false,
};