init
This commit is contained in:
+211
@@ -0,0 +1,211 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'api/support_api.dart';
|
||||
import 'app_scope.dart';
|
||||
import 'inbox.dart';
|
||||
import 'push.dart';
|
||||
import 'screens/inbox_screen.dart';
|
||||
import 'screens/login_screen.dart';
|
||||
import 'screens/thread_screen.dart';
|
||||
import 'session.dart';
|
||||
|
||||
class KickKingsApp extends StatefulWidget {
|
||||
const KickKingsApp({
|
||||
super.key,
|
||||
required this.firebaseReady,
|
||||
this.session,
|
||||
this.inbox,
|
||||
this.push,
|
||||
this.api,
|
||||
});
|
||||
|
||||
final bool firebaseReady;
|
||||
final SessionController? session;
|
||||
final InboxController? inbox;
|
||||
final PushService? push;
|
||||
final SupportApi? api;
|
||||
|
||||
@override
|
||||
State<KickKingsApp> createState() => _KickKingsAppState();
|
||||
}
|
||||
|
||||
class _KickKingsAppState extends State<KickKingsApp> {
|
||||
final _navigatorKey = GlobalKey<NavigatorState>();
|
||||
final _messengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
late final RouteObserver<ModalRoute<void>> _routeObserver;
|
||||
late final SupportApi _api;
|
||||
late final SessionController _session;
|
||||
late final InboxController _inbox;
|
||||
late final PushService _push;
|
||||
var _wasSignedIn = false;
|
||||
var _openScheduled = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_routeObserver = RouteObserver<ModalRoute<void>>();
|
||||
_api = widget.api ?? SupportApi();
|
||||
_session =
|
||||
widget.session ?? SessionController(store: SessionStore(), api: _api);
|
||||
_inbox = widget.inbox ?? InboxController(api: _api);
|
||||
_push = widget.push ?? PushService(firebaseReady: widget.firebaseReady);
|
||||
_api.onUnauthorized = _session.handleUnauthorized;
|
||||
_session.addListener(_onSession);
|
||||
unawaited(_boot());
|
||||
}
|
||||
|
||||
Future<void> _boot() async {
|
||||
await _session.restore();
|
||||
await _push.listen(
|
||||
onOpenTicket: _rememberTicket,
|
||||
onForegroundMessage: _onForegroundMessage,
|
||||
onToken: _registerToken,
|
||||
isSignedIn: () => _session.isSignedIn,
|
||||
);
|
||||
if (_session.isSignedIn) {
|
||||
await _registerCurrentDevice();
|
||||
}
|
||||
_openPendingTicket();
|
||||
}
|
||||
|
||||
void _onSession() {
|
||||
final signedIn = _session.isSignedIn;
|
||||
if (signedIn && !_wasSignedIn) {
|
||||
unawaited(_registerCurrentDevice());
|
||||
_openPendingTicket();
|
||||
}
|
||||
if (!signedIn && _wasSignedIn) {
|
||||
_inbox.reset();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_navigatorKey.currentState?.popUntil((route) => route.isFirst);
|
||||
});
|
||||
}
|
||||
_wasSignedIn = signedIn;
|
||||
}
|
||||
|
||||
Future<void> _registerCurrentDevice() async {
|
||||
if (!_session.isSignedIn) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await _push.register(_registerToken);
|
||||
} on Object {
|
||||
// A denied permission or a missing Firebase config leaves the inbox usable.
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _registerToken(String token, String platform) async {
|
||||
if (!_session.isSignedIn) {
|
||||
return;
|
||||
}
|
||||
await _api.registerDevice(token: token, platform: platform);
|
||||
await _session.saveFcmToken(token);
|
||||
}
|
||||
|
||||
void _onForegroundMessage(RemoteMessage message) {
|
||||
if (!_session.isSignedIn) {
|
||||
return;
|
||||
}
|
||||
unawaited(_inbox.refreshList());
|
||||
final ticketId = message.data['ticketId'];
|
||||
if (ticketId is String &&
|
||||
ticketId.isNotEmpty &&
|
||||
ticketId == _inbox.activeTicketId) {
|
||||
unawaited(_inbox.refreshThread());
|
||||
}
|
||||
final title = message.notification?.title;
|
||||
if (title != null && title.isNotEmpty) {
|
||||
_messengerKey.currentState?.showSnackBar(SnackBar(content: Text(title)));
|
||||
}
|
||||
}
|
||||
|
||||
void _rememberTicket(String ticketId) {
|
||||
_session.pendingTicketId = ticketId;
|
||||
_openPendingTicket();
|
||||
}
|
||||
|
||||
void _openPendingTicket() {
|
||||
if (_openScheduled) {
|
||||
return;
|
||||
}
|
||||
final ticketId = _session.pendingTicketId;
|
||||
if (ticketId == null || !_session.isSignedIn) {
|
||||
return;
|
||||
}
|
||||
_openScheduled = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_openScheduled = false;
|
||||
if (!mounted ||
|
||||
_session.pendingTicketId != ticketId ||
|
||||
!_session.isSignedIn) {
|
||||
return;
|
||||
}
|
||||
if (_inbox.activeTicketId == ticketId) {
|
||||
_session.pendingTicketId = null;
|
||||
unawaited(_inbox.refreshThread());
|
||||
return;
|
||||
}
|
||||
_session.pendingTicketId = null;
|
||||
_navigatorKey.currentState?.push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => ThreadScreen(ticketId: ticketId),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_session.removeListener(_onSession);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScope(
|
||||
session: _session,
|
||||
inbox: _inbox,
|
||||
routeObserver: _routeObserver,
|
||||
child: MaterialApp(
|
||||
title: 'Kick Kings',
|
||||
navigatorKey: _navigatorKey,
|
||||
scaffoldMessengerKey: _messengerKey,
|
||||
navigatorObservers: [_routeObserver],
|
||||
theme: _theme(Brightness.light),
|
||||
darkTheme: _theme(Brightness.dark),
|
||||
home: ListenableBuilder(
|
||||
listenable: _session,
|
||||
builder: (context, _) {
|
||||
if (!_session.ready) {
|
||||
return const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
if (!_session.isSignedIn) {
|
||||
return const LoginScreen();
|
||||
}
|
||||
return const InboxScreen();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ThemeData _theme(Brightness brightness) {
|
||||
final scheme = ColorScheme.fromSeed(
|
||||
seedColor: const Color(0xFF0F6B4C),
|
||||
brightness: brightness,
|
||||
);
|
||||
return ThemeData(
|
||||
colorScheme: scheme,
|
||||
useMaterial3: true,
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: scheme.surface,
|
||||
foregroundColor: scheme.onSurface,
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user