An experiment in building a better XMPP client. This time using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
moxxyv2/lib/main.dart

309 lines
12 KiB

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get_it/get_it.dart';
import 'package:logging/logging.dart';
import 'package:moxplatform/moxplatform.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/service/service.dart';
import 'package:moxxyv2/shared/commands.dart';
import 'package:moxxyv2/shared/synchronized_queue.dart';
import 'package:moxxyv2/ui/bloc/addcontact_bloc.dart';
import 'package:moxxyv2/ui/bloc/blocklist_bloc.dart';
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
import 'package:moxxyv2/ui/bloc/crop_bloc.dart';
import 'package:moxxyv2/ui/bloc/cropbackground_bloc.dart';
import 'package:moxxyv2/ui/bloc/devices_bloc.dart';
import 'package:moxxyv2/ui/bloc/login_bloc.dart';
import 'package:moxxyv2/ui/bloc/navigation_bloc.dart';
import 'package:moxxyv2/ui/bloc/newconversation_bloc.dart';
import 'package:moxxyv2/ui/bloc/own_devices_bloc.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/bloc/profile_bloc.dart';
import 'package:moxxyv2/ui/bloc/sendfiles_bloc.dart';
import 'package:moxxyv2/ui/bloc/server_info_bloc.dart';
import 'package:moxxyv2/ui/bloc/share_selection_bloc.dart';
import 'package:moxxyv2/ui/bloc/sharedmedia_bloc.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/events.dart';
/*
import "package:moxxyv2/ui/pages/register/register.dart";
import "package:moxxyv2/ui/pages/postregister/postregister.dart";
*/
import 'package:moxxyv2/ui/pages/addcontact.dart';
import 'package:moxxyv2/ui/pages/blocklist.dart';
import 'package:moxxyv2/ui/pages/conversation/conversation.dart';
import 'package:moxxyv2/ui/pages/conversations.dart';
import 'package:moxxyv2/ui/pages/crop.dart';
import 'package:moxxyv2/ui/pages/intro.dart';
import 'package:moxxyv2/ui/pages/login.dart';
import 'package:moxxyv2/ui/pages/newconversation.dart';
import 'package:moxxyv2/ui/pages/profile/devices.dart';
import 'package:moxxyv2/ui/pages/profile/own_devices.dart';
import 'package:moxxyv2/ui/pages/profile/profile.dart';
import 'package:moxxyv2/ui/pages/sendfiles.dart';
import 'package:moxxyv2/ui/pages/server_info.dart';
import 'package:moxxyv2/ui/pages/settings/about.dart';
import 'package:moxxyv2/ui/pages/settings/appearance/appearance.dart';
import 'package:moxxyv2/ui/pages/settings/appearance/cropbackground.dart';
import 'package:moxxyv2/ui/pages/settings/conversation.dart';
import 'package:moxxyv2/ui/pages/settings/debugging.dart';
import 'package:moxxyv2/ui/pages/settings/licenses.dart';
import 'package:moxxyv2/ui/pages/settings/network.dart';
import 'package:moxxyv2/ui/pages/settings/privacy/privacy.dart';
import 'package:moxxyv2/ui/pages/settings/settings.dart';
import 'package:moxxyv2/ui/pages/share_selection.dart';
import 'package:moxxyv2/ui/pages/sharedmedia.dart';
import 'package:moxxyv2/ui/pages/splashscreen/splashscreen.dart';
import 'package:moxxyv2/ui/service/data.dart';
import 'package:moxxyv2/ui/service/progress.dart';
import 'package:moxxyv2/ui/theme.dart';
import 'package:page_transition/page_transition.dart';
import 'package:share_handler/share_handler.dart';
void setupLogging() {
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
Logger.root.onRecord.listen((record) {
// ignore: avoid_print
print('[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}');
});
GetIt.I.registerSingleton<Logger>(Logger('MoxxyMain'));
}
Future<void> setupUIServices() async {
GetIt.I.registerSingleton<UIProgressService>(UIProgressService());
GetIt.I.registerSingleton<UIDataService>(UIDataService());
}
void setupBlocs(GlobalKey<NavigatorState> navKey) {
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navigationKey: navKey));
GetIt.I.registerSingleton<ConversationsBloc>(ConversationsBloc());
GetIt.I.registerSingleton<NewConversationBloc>(NewConversationBloc());
GetIt.I.registerSingleton<ConversationBloc>(ConversationBloc());
GetIt.I.registerSingleton<BlocklistBloc>(BlocklistBloc()); GetIt.I.registerSingleton<ProfileBloc>(ProfileBloc());
GetIt.I.registerSingleton<PreferencesBloc>(PreferencesBloc());
GetIt.I.registerSingleton<AddContactBloc>(AddContactBloc());
GetIt.I.registerSingleton<SharedMediaBloc>(SharedMediaBloc());
GetIt.I.registerSingleton<CropBloc>(CropBloc());
GetIt.I.registerSingleton<SendFilesBloc>(SendFilesBloc());
GetIt.I.registerSingleton<CropBackgroundBloc>(CropBackgroundBloc());
GetIt.I.registerSingleton<ShareSelectionBloc>(ShareSelectionBloc());
GetIt.I.registerSingleton<ServerInfoBloc>(ServerInfoBloc());
GetIt.I.registerSingleton<DevicesBloc>(DevicesBloc());
GetIt.I.registerSingleton<OwnDevicesBloc>(OwnDevicesBloc());
}
// TODO(Unknown): Replace all Column(children: [ Padding(), Padding, ...]) with a
// Padding(padding: ..., child: Column(children: [ ... ]))
// TODO(Unknown): Theme the switches
void main() async {
setupLogging();
await setupUIServices();
setupEventHandler();
final navKey = GlobalKey<NavigatorState>();
setupBlocs(navKey);
await initializeServiceIfNeeded();
runApp(
MultiBlocProvider(
providers: [
BlocProvider<NavigationBloc>(
create: (_) => GetIt.I.get<NavigationBloc>(),
),
BlocProvider<LoginBloc>(
create: (_) => LoginBloc(),
),
BlocProvider<ConversationsBloc>(
create: (_) => GetIt.I.get<ConversationsBloc>(),
),
BlocProvider<NewConversationBloc>(
create: (_) => GetIt.I.get<NewConversationBloc>(),
),
BlocProvider<ConversationBloc>(
create: (_) => GetIt.I.get<ConversationBloc>(),
),
BlocProvider<BlocklistBloc>(
create: (_) => GetIt.I.get<BlocklistBloc>(),
),
BlocProvider<ProfileBloc>(
create: (_) => GetIt.I.get<ProfileBloc>(),
),
BlocProvider<PreferencesBloc>(
create: (_) => GetIt.I.get<PreferencesBloc>(),
),
BlocProvider<AddContactBloc>(
create: (_) => GetIt.I.get<AddContactBloc>(),
),
BlocProvider<SharedMediaBloc>(
create: (_) => GetIt.I.get<SharedMediaBloc>(),
),
BlocProvider<CropBloc>(
create: (_) => GetIt.I.get<CropBloc>(),
),
BlocProvider<SendFilesBloc>(
create: (_) => GetIt.I.get<SendFilesBloc>(),
),
BlocProvider<CropBackgroundBloc>(
create: (_) => GetIt.I.get<CropBackgroundBloc>(),
),
BlocProvider<ShareSelectionBloc>(
create: (_) => GetIt.I.get<ShareSelectionBloc>(),
),
BlocProvider<ServerInfoBloc>(
create: (_) => GetIt.I.get<ServerInfoBloc>(),
),
BlocProvider<DevicesBloc>(
create: (_) => GetIt.I.get<DevicesBloc>(),
),
BlocProvider<OwnDevicesBloc>(
create: (_) => GetIt.I.get<OwnDevicesBloc>(),
),
],
child: TranslationProvider(
child: MyApp(navKey),
),
),
);
}
class MyApp extends StatefulWidget {
const MyApp(this.navigationKey, { super.key });
final GlobalKey<NavigatorState> navigationKey;
@override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> with WidgetsBindingObserver {
MyAppState();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_setupSharingHandler();
// Lift the UI block
GetIt.I.get<SynchronizedQueue<Map<String, dynamic>?>>().removeQueueLock();
}
Future<void> _handleSharedMedia(SharedMedia media) async {
final attachments = media.attachments ?? [];
GetIt.I.get<ShareSelectionBloc>().add(
ShareSelectionRequestedEvent(
attachments.map((a) => a!.path).toList(),
media.content,
media.content != null ? ShareSelectionType.text : ShareSelectionType.media,
),
);
}
Future<void> _setupSharingHandler() async {
final handler = ShareHandlerPlatform.instance;
final media = await handler.getInitialSharedMedia();
// Shared while the app was closed
if (media != null) {
if (GetIt.I.get<UIDataService>().isLoggedIn) {
await _handleSharedMedia(media);
}
await handler.resetInitialSharedMedia();
}
// Shared while the app is stil running
handler.sharedMediaStream.listen((SharedMedia media) async {
if (GetIt.I.get<UIDataService>().isLoggedIn) {
await _handleSharedMedia(media);
}
await handler.resetInitialSharedMedia();
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
final sender = MoxplatformPlugin.handler.getDataSender();
switch (state) {
case AppLifecycleState.paused:
sender.sendData(
SetCSIStateCommand(active: false),
);
GetIt.I.get<ConversationBloc>().add(AppStateChanged(false));
break;
case AppLifecycleState.resumed:
sender.sendData(
SetCSIStateCommand(active: true),
);
GetIt.I.get<ConversationBloc>().add(AppStateChanged(true));
break;
case AppLifecycleState.detached:
case AppLifecycleState.inactive:
break;
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: TranslationProvider.of(context).flutterLocale,
supportedLocales: LocaleSettings.supportedLocales,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
title: 'Moxxy',
theme: getThemeData(context, Brightness.light),
darkTheme: getThemeData(context, Brightness.dark),
navigatorKey: widget.navigationKey,
onGenerateRoute: (settings) {
switch (settings.name) {
case introRoute: return Intro.route;
case loginRoute: return Login.route;
case conversationsRoute: return ConversationsPage.route;
case newConversationRoute: return NewConversationPage.route;
case conversationRoute: return PageTransition<dynamic>(
type: PageTransitionType.rightToLeft,
settings: settings,
child: const ConversationPage(),
);
case sharedMediaRoute: return SharedMediaPage.route;
case blocklistRoute: return BlocklistPage.route;
case profileRoute: return ProfilePage.route;
case settingsRoute: return SettingsPage.route;
case aboutRoute: return SettingsAboutPage.route;
case licensesRoute: return SettingsLicensesPage.route;
case networkRoute: return NetworkPage.route;
case privacyRoute: return PrivacyPage.route;
case debuggingRoute: return DebuggingPage.route;
case addContactRoute: return AddContactPage.route;
case cropRoute: return CropPage.route;
case sendFilesRoute: return SendFilesPage.route;
case backgroundCroppingRoute: return CropBackgroundPage.route;
case shareSelectionRoute: return ShareSelectionPage.route;
case serverInfoRoute: return ServerInfoPage.route;
case conversationSettingsRoute: return ConversationSettingsPage.route;
case devicesRoute: return DevicesPage.route;
case ownDevicesRoute: return OwnDevicesPage.route;
case appearanceRoute: return AppearanceSettingsPage.route;
}
return null;
},
home: const Splashscreen(),
);
}
}