import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:collection/collection.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get_storage/get_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'config/setting_keys.dart'; import 'utils/background_push.dart'; import 'widgets/fluffy_chat_app.dart'; void main() async { Logs().i('Welcome to ${AppConfig.applicationName} <3'); // #Pangea try { await dotenv.load(fileName: ".env"); } catch (e) { Logs().e('Failed to load .env file', e); } // await dotenv.load(fileName: ".env"); // await dotenv.load(fileName: Environment.fileName); await Future.wait([ ErrorHandler.initialize(), PLanguageStore.initialize(), GoogleAnalytics.initialize(), ]); /// /// PangeaLanguage must be initialized before the runApp /// Then where ever you need language functions simply call PangeaLanguage pangeaLanguage = PangeaLanguage() /// pangeaLanguage.getList or whatever function you need /// await GetStorage.init(); // Pangea# // Our background push shared isolate accesses flutter-internal things very early in the startup proccess // To make sure that the parts of flutter needed are started up already, we need to ensure that the // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); // #Pangea // await vod.init(wasmPath: './assets/assets/vodozemac/'); // Pangea# Logs().nativeColors = !PlatformInfos.isIOS; final store = await SharedPreferences.getInstance(); final clients = await ClientManager.getClients(store: store); // If the app starts in detached mode, we assume that it is in // background fetch mode for processing push notifications. This is // currently only supported on Android. if (PlatformInfos.isAndroid && AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) { // Do not send online presences when app is in background fetch mode. for (final client in clients) { client.backgroundSync = false; client.syncPresence = PresenceType.offline; } // In the background fetch mode we do not want to waste ressources with // starting the Flutter engine but process incoming push notifications. BackgroundPush.clientOnly(clients.first); // To start the flutter engine afterwards we add an custom observer. WidgetsBinding.instance.addObserver(AppStarter(clients, store)); Logs().i( '${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', ); return; } // Started in foreground mode. Logs().i( '${AppConfig.applicationName} started in foreground mode. Rendering GUI...', ); await startGui(clients, store); } /// Fetch the pincode for the applock and start the flutter engine. Future startGui(List clients, SharedPreferences store) async { // Fetch the pin for the applock if existing for mobile applications. String? pin; if (PlatformInfos.isMobile) { try { pin = await const FlutterSecureStorage().read(key: SettingKeys.appLockKey); } catch (e, s) { Logs().d('Unable to read PIN from Secure storage', e, s); } } // Preload first client final firstClient = clients.firstOrNull; await firstClient?.roomsLoading; await firstClient?.accountDataLoading; // #Pangea // errors seems to happen a lot when users switch better production / staging // while testing by accident. If the account is a production account but server is // staging or vice versa, logout. if (firstClient?.userID?.domain != null) { final isStagingUser = firstClient!.userID!.domain!.contains("staging"); final isStagingServer = Environment.synapseURL.contains("staging"); if (isStagingServer != isStagingUser) { await firstClient.logout(); } } await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, // Lock to portrait mode ]); // Pangea# // #Pangea // runApp(FluffyChatApp(clients: clients, pincode: pin, store: store)); runApp( ChangeNotifierProvider( create: (_) => LocaleProvider(), child: FluffyChatApp(clients: clients, pincode: pin, store: store), ), ); // Pangea# } /// Watches the lifecycle changes to start the application when it /// is no longer detached. class AppStarter with WidgetsBindingObserver { final List clients; final SharedPreferences store; bool guiStarted = false; AppStarter(this.clients, this.store); @override void didChangeAppLifecycleState(AppLifecycleState state) { if (guiStarted) return; if (state == AppLifecycleState.detached) return; Logs().i( '${AppConfig.applicationName} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', ); // Switching to foreground mode needs to reenable send online sync presence. for (final client in clients) { client.backgroundSync = true; client.syncPresence = PresenceType.online; } startGui(clients, store); // We must make sure that the GUI is only started once. guiStarted = true; } }