mirror of
https://github.com/krille-chan/fluffychat
synced 2024-10-05 13:52:46 +00:00
feat: New splash screen
This commit is contained in:
parent
ea790f43ac
commit
a4bbef19f2
6 changed files with 149 additions and 104 deletions
|
@ -2550,6 +2550,7 @@
|
||||||
"wrongRecoveryKey": "Sorry... this does not seem to be the correct recovery key.",
|
"wrongRecoveryKey": "Sorry... this does not seem to be the correct recovery key.",
|
||||||
"startConversation": "Start conversation",
|
"startConversation": "Start conversation",
|
||||||
"commandHint_sendraw": "Send raw json",
|
"commandHint_sendraw": "Send raw json",
|
||||||
"databaseMigrationTitle": "Database is optimized",
|
"databaseMigrationTitle": "Optimizing your chats... This can take a few moments!",
|
||||||
"databaseMigrationBody": "Please wait. This may take a moment."
|
"loadingChats": "Loading your chats...",
|
||||||
|
"reportError": "Report error"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -22,15 +21,23 @@ void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
Logs().nativeColors = !PlatformInfos.isIOS;
|
Logs().nativeColors = !PlatformInfos.isIOS;
|
||||||
|
|
||||||
|
// Do not send online presences when app is in background fetch mode.
|
||||||
final store = await SharedPreferences.getInstance();
|
final store = await SharedPreferences.getInstance();
|
||||||
final clients = await ClientManager.getClients(store: store);
|
var isMigratingDatabase = false;
|
||||||
|
final clientsFuture = ClientManager.getClients(
|
||||||
|
store: store,
|
||||||
|
onMigration: () {
|
||||||
|
isMigratingDatabase = true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// If the app starts in detached mode, we assume that it is in
|
// If the app starts in detached mode, we assume that it is in
|
||||||
// background fetch mode for processing push notifications. This is
|
// background fetch mode for processing push notifications. This is
|
||||||
// currently only supported on Android.
|
// currently only supported on Android.
|
||||||
if (PlatformInfos.isAndroid &&
|
if (PlatformInfos.isAndroid &&
|
||||||
AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) {
|
AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) {
|
||||||
// Do not send online presences when app is in background fetch mode.
|
final clients = await clientsFuture;
|
||||||
for (final client in clients) {
|
for (final client in clients) {
|
||||||
client.syncPresence = PresenceType.offline;
|
client.syncPresence = PresenceType.offline;
|
||||||
}
|
}
|
||||||
|
@ -46,15 +53,26 @@ void main() async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isMigratingDatabase) {
|
||||||
|
final clients = await clientsFuture;
|
||||||
|
// Preload first client
|
||||||
|
final firstClient = clients.firstOrNull;
|
||||||
|
await firstClient?.roomsLoading;
|
||||||
|
await firstClient?.accountDataLoading;
|
||||||
|
}
|
||||||
|
|
||||||
// Started in foreground mode.
|
// Started in foreground mode.
|
||||||
Logs().i(
|
Logs().i(
|
||||||
'${AppConfig.applicationName} started in foreground mode. Rendering GUI...',
|
'${AppConfig.applicationName} started in foreground mode. Rendering GUI...',
|
||||||
);
|
);
|
||||||
await startGui(clients, store);
|
await startGui(clientsFuture, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the pincode for the applock and start the flutter engine.
|
/// Fetch the pincode for the applock and start the flutter engine.
|
||||||
Future<void> startGui(List<Client> clients, SharedPreferences store) async {
|
Future<void> startGui(
|
||||||
|
Future<List<Client>> clientsFuture,
|
||||||
|
SharedPreferences store,
|
||||||
|
) async {
|
||||||
// Fetch the pin for the applock if existing for mobile applications.
|
// Fetch the pin for the applock if existing for mobile applications.
|
||||||
String? pin;
|
String? pin;
|
||||||
if (PlatformInfos.isMobile) {
|
if (PlatformInfos.isMobile) {
|
||||||
|
@ -66,13 +84,14 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload first client
|
|
||||||
final firstClient = clients.firstOrNull;
|
|
||||||
await firstClient?.roomsLoading;
|
|
||||||
await firstClient?.accountDataLoading;
|
|
||||||
|
|
||||||
ErrorWidget.builder = (details) => FluffyChatErrorWidget(details);
|
ErrorWidget.builder = (details) => FluffyChatErrorWidget(details);
|
||||||
runApp(FluffyChatApp(clients: clients, pincode: pin, store: store));
|
runApp(
|
||||||
|
FluffyChatApp(
|
||||||
|
pincode: pin,
|
||||||
|
clientsFuture: clientsFuture,
|
||||||
|
store: store,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Watches the lifecycle changes to start the application when it
|
/// Watches the lifecycle changes to start the application when it
|
||||||
|
@ -96,7 +115,7 @@ class AppStarter with WidgetsBindingObserver {
|
||||||
for (final client in clients) {
|
for (final client in clients) {
|
||||||
client.syncPresence = PresenceType.online;
|
client.syncPresence = PresenceType.online;
|
||||||
}
|
}
|
||||||
startGui(clients, store);
|
startGui(Future.value(clients), store);
|
||||||
// We must make sure that the GUI is only started once.
|
// We must make sure that the GUI is only started once.
|
||||||
guiStarted = true;
|
guiStarted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:matrix/encryption/utils/key_verification.dart';
|
import 'package:matrix/encryption/utils/key_verification.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:universal_html/html.dart' as html;
|
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
|
||||||
import 'package:fluffychat/utils/custom_http_client.dart';
|
import 'package:fluffychat/utils/custom_http_client.dart';
|
||||||
import 'package:fluffychat/utils/custom_image_resizer.dart';
|
import 'package:fluffychat/utils/custom_image_resizer.dart';
|
||||||
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart';
|
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart';
|
||||||
|
@ -23,8 +15,8 @@ import 'matrix_sdk_extensions/flutter_matrix_sdk_database_builder.dart';
|
||||||
abstract class ClientManager {
|
abstract class ClientManager {
|
||||||
static const String clientNamespace = 'im.fluffychat.store.clients';
|
static const String clientNamespace = 'im.fluffychat.store.clients';
|
||||||
static Future<List<Client>> getClients({
|
static Future<List<Client>> getClients({
|
||||||
bool initialize = true,
|
|
||||||
required SharedPreferences store,
|
required SharedPreferences store,
|
||||||
|
void Function()? onMigration,
|
||||||
}) async {
|
}) async {
|
||||||
if (PlatformInfos.isLinux) {
|
if (PlatformInfos.isLinux) {
|
||||||
Hive.init((await getApplicationSupportDirectory()).path);
|
Hive.init((await getApplicationSupportDirectory()).path);
|
||||||
|
@ -44,28 +36,20 @@ abstract class ClientManager {
|
||||||
await store.setStringList(clientNamespace, clientNames.toList());
|
await store.setStringList(clientNamespace, clientNames.toList());
|
||||||
}
|
}
|
||||||
final clients = clientNames.map(createClient).toList();
|
final clients = clientNames.map(createClient).toList();
|
||||||
if (initialize) {
|
await Future.wait(
|
||||||
FlutterLocalNotificationsPlugin? flutterLocalNotificationsPlugin;
|
clients.map(
|
||||||
await Future.wait(
|
(client) => client
|
||||||
clients.map(
|
.init(
|
||||||
(client) => client
|
waitForFirstSync: false,
|
||||||
.init(
|
waitUntilLoadCompletedLoaded: false,
|
||||||
waitForFirstSync: false,
|
onMigration: onMigration,
|
||||||
waitUntilLoadCompletedLoaded: false,
|
)
|
||||||
onMigration: () {
|
.catchError(
|
||||||
sendMigrationNotification(
|
(e, s) => Logs().e('Unable to initialize client', e, s),
|
||||||
flutterLocalNotificationsPlugin ??=
|
),
|
||||||
FlutterLocalNotificationsPlugin(),
|
),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
)
|
|
||||||
.catchError(
|
|
||||||
(e, s) => Logs().e('Unable to initialize client', e, s),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
flutterLocalNotificationsPlugin?.cancel(0);
|
|
||||||
}
|
|
||||||
if (clients.length > 1 && clients.any((c) => !c.isLogged())) {
|
if (clients.length > 1 && clients.any((c) => !c.isLogged())) {
|
||||||
final loggedOutClients = clients.where((c) => !c.isLogged()).toList();
|
final loggedOutClients = clients.where((c) => !c.isLogged()).toList();
|
||||||
for (final client in loggedOutClients) {
|
for (final client in loggedOutClients) {
|
||||||
|
@ -130,42 +114,4 @@ abstract class ClientManager {
|
||||||
enableDehydratedDevices: true,
|
enableDehydratedDevices: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendMigrationNotification(
|
|
||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin,
|
|
||||||
) async {
|
|
||||||
final l10n = lookupL10n(Locale(Platform.localeName));
|
|
||||||
|
|
||||||
if (kIsWeb) {
|
|
||||||
html.Notification(
|
|
||||||
l10n.databaseMigrationTitle,
|
|
||||||
body: l10n.databaseMigrationBody,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await flutterLocalNotificationsPlugin.initialize(
|
|
||||||
const InitializationSettings(
|
|
||||||
android: AndroidInitializationSettings('notifications_icon'),
|
|
||||||
iOS: DarwinInitializationSettings(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
flutterLocalNotificationsPlugin.show(
|
|
||||||
0,
|
|
||||||
l10n.databaseMigrationTitle,
|
|
||||||
l10n.databaseMigrationBody,
|
|
||||||
const NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails(
|
|
||||||
AppConfig.pushNotificationsChannelId,
|
|
||||||
AppConfig.pushNotificationsChannelName,
|
|
||||||
channelDescription: AppConfig.pushNotificationsChannelDescription,
|
|
||||||
importance: Importance.max,
|
|
||||||
priority: Priority.max,
|
|
||||||
fullScreenIntent: true, // To show notification popup
|
|
||||||
showProgress: true,
|
|
||||||
),
|
|
||||||
iOS: DarwinNotificationDetails(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,13 @@ class ErrorReporter {
|
||||||
|
|
||||||
const ErrorReporter(this.context, [this.message]);
|
const ErrorReporter(this.context, [this.message]);
|
||||||
|
|
||||||
void onErrorCallback(Object error, [StackTrace? stackTrace]) async {
|
void onErrorCallback(
|
||||||
|
Object error, [
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
OkCancelResult? consent,
|
||||||
|
]) async {
|
||||||
Logs().e(message ?? 'Error caught', error, stackTrace);
|
Logs().e(message ?? 'Error caught', error, stackTrace);
|
||||||
final consent = await showOkCancelAlertDialog(
|
consent ??= await showOkCancelAlertDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: error.toLocalizedString(context),
|
title: error.toLocalizedString(context),
|
||||||
message: L10n.of(context)!.reportErrorDescription,
|
message: L10n.of(context)!.reportErrorDescription,
|
||||||
|
|
|
@ -101,7 +101,6 @@ Future<void> _tryPushHelper(
|
||||||
);
|
);
|
||||||
|
|
||||||
client ??= (await ClientManager.getClients(
|
client ??= (await ClientManager.getClients(
|
||||||
initialize: false,
|
|
||||||
store: await SharedPreferences.getInstance(),
|
store: await SharedPreferences.getInstance(),
|
||||||
))
|
))
|
||||||
.first;
|
.first;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
@ -7,24 +8,25 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/routes.dart';
|
import 'package:fluffychat/config/routes.dart';
|
||||||
import 'package:fluffychat/config/themes.dart';
|
import 'package:fluffychat/config/themes.dart';
|
||||||
|
import 'package:fluffychat/utils/error_reporter.dart';
|
||||||
import 'package:fluffychat/widgets/app_lock.dart';
|
import 'package:fluffychat/widgets/app_lock.dart';
|
||||||
import 'package:fluffychat/widgets/theme_builder.dart';
|
import 'package:fluffychat/widgets/theme_builder.dart';
|
||||||
import '../config/app_config.dart';
|
import '../config/app_config.dart';
|
||||||
import '../utils/custom_scroll_behaviour.dart';
|
import '../utils/custom_scroll_behaviour.dart';
|
||||||
import 'matrix.dart';
|
import 'matrix.dart';
|
||||||
|
|
||||||
class FluffyChatApp extends StatelessWidget {
|
class FluffyChatApp extends StatefulWidget {
|
||||||
final Widget? testWidget;
|
final Widget? testWidget;
|
||||||
final List<Client> clients;
|
|
||||||
final String? pincode;
|
final String? pincode;
|
||||||
|
final Future<List<Client>> clientsFuture;
|
||||||
final SharedPreferences store;
|
final SharedPreferences store;
|
||||||
|
|
||||||
const FluffyChatApp({
|
const FluffyChatApp({
|
||||||
super.key,
|
super.key,
|
||||||
this.testWidget,
|
this.testWidget,
|
||||||
required this.clients,
|
|
||||||
required this.store,
|
|
||||||
this.pincode,
|
this.pincode,
|
||||||
|
required this.clientsFuture,
|
||||||
|
required this.store,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// getInitialLink may rereturn the value multiple times if this view is
|
/// getInitialLink may rereturn the value multiple times if this view is
|
||||||
|
@ -36,6 +38,18 @@ class FluffyChatApp extends StatelessWidget {
|
||||||
// the current path.
|
// the current path.
|
||||||
static final GoRouter router = GoRouter(routes: AppRoutes.routes);
|
static final GoRouter router = GoRouter(routes: AppRoutes.routes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FluffyChatApp> createState() => _FluffyChatAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FluffyChatAppState extends State<FluffyChatApp> {
|
||||||
|
List<Client>? clients;
|
||||||
|
bool isMigratingDatabase = false;
|
||||||
|
|
||||||
|
Future<List<Client>> loadClient() async {
|
||||||
|
return clients ??= await widget.clientsFuture;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ThemeBuilder(
|
return ThemeBuilder(
|
||||||
|
@ -48,21 +62,83 @@ class FluffyChatApp extends StatelessWidget {
|
||||||
scrollBehavior: CustomScrollBehavior(),
|
scrollBehavior: CustomScrollBehavior(),
|
||||||
localizationsDelegates: L10n.localizationsDelegates,
|
localizationsDelegates: L10n.localizationsDelegates,
|
||||||
supportedLocales: L10n.supportedLocales,
|
supportedLocales: L10n.supportedLocales,
|
||||||
routerConfig: router,
|
routerConfig: FluffyChatApp.router,
|
||||||
builder: (context, child) => AppLockWidget(
|
builder: (context, child) => FutureBuilder(
|
||||||
pincode: pincode,
|
future: loadClient(),
|
||||||
clients: clients,
|
builder: (context, snapshot) {
|
||||||
// Need a navigator above the Matrix widget for
|
final data = snapshot.data;
|
||||||
// displaying dialogs
|
if (data == null) {
|
||||||
child: Navigator(
|
final error = snapshot.error;
|
||||||
onGenerateRoute: (_) => MaterialPageRoute(
|
final label = error != null
|
||||||
builder: (_) => Matrix(
|
? L10n.of(context)!.reportErrorDescription
|
||||||
clients: clients,
|
: L10n.of(context)!.databaseMigrationTitle;
|
||||||
store: store,
|
return Scaffold(
|
||||||
child: testWidget ?? child,
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (error == null)
|
||||||
|
Image.asset(
|
||||||
|
'assets/logo.png',
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Text('😭', style: TextStyle(fontSize: 100)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SizedBox(
|
||||||
|
width: FluffyThemes.columnWidth,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: error == null ? 20 : 16,
|
||||||
|
color: error == null
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (error != null)
|
||||||
|
OutlinedButton.icon(
|
||||||
|
onPressed: () =>
|
||||||
|
ErrorReporter(context, 'INITIALIZATION ERROR')
|
||||||
|
.onErrorCallback(
|
||||||
|
error,
|
||||||
|
snapshot.stackTrace,
|
||||||
|
OkCancelResult.ok,
|
||||||
|
),
|
||||||
|
label: Text(L10n.of(context)!.reportError),
|
||||||
|
icon: const Icon(Icons.favorite),
|
||||||
|
),
|
||||||
|
if (error == null)
|
||||||
|
const CircularProgressIndicator.adaptive(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return AppLockWidget(
|
||||||
|
pincode: widget.pincode,
|
||||||
|
clients: data,
|
||||||
|
// Need a navigator above the Matrix widget for
|
||||||
|
// displaying dialogs
|
||||||
|
child: Navigator(
|
||||||
|
onGenerateRoute: (_) => MaterialPageRoute(
|
||||||
|
builder: (_) => Matrix(
|
||||||
|
clients: data,
|
||||||
|
store: widget.store,
|
||||||
|
child: widget.testWidget ?? child,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue