From 54ec6aafcb0fedd4ce487b3aceeb388f1bae5eb3 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Sat, 13 Apr 2024 22:49:18 +0200 Subject: [PATCH 01/14] feat: basic three-page modal for Matrix setup --- lib/l10n/app_en.arb | 7 +++ .../settings/advanced_settings_page.dart | 2 + lib/sync/ui/homeserver_config_page.dart | 52 +++++++++++++++ lib/sync/ui/matrix_settings_modal.dart | 63 +++++++++++++++++++ lib/sync/ui/room_config_page.dart | 53 ++++++++++++++++ lib/sync/ui/unverified_devices_page.dart | 52 +++++++++++++++ pubspec.yaml | 2 +- 7 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 lib/sync/ui/homeserver_config_page.dart create mode 100644 lib/sync/ui/matrix_settings_modal.dart create mode 100644 lib/sync/ui/room_config_page.dart create mode 100644 lib/sync/ui/unverified_devices_page.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 2858a03d7..52492924a 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -202,6 +202,13 @@ "settingsMatrixSaveLabel": "Save", "settingsMatrixStartVerificationLabel": "Start Verification", "settingsMatrixTitle": "Matrix Sync Settings", + "settingsMatrixHomeserverConfigTitle": "Matrix Homeserver Setup", + "settingsMatrixRoomConfigTitle": "Matrix Sync Room Setup", + "settingsMatrixCancel": "Cancel", + "settingsMatrixDone": "Done", + "settingsMatrixNextPage": "Next Page", + "settingsMatrixPreviousPage": "Previous Page", + "settingsMatrixUnverifiedDevicesPage": "Unverified Devices", "settingsMatrixUserLabel": "User", "settingsMatrixVerificationCancelledLabel": "Cancelled on other device...", "settingsMatrixVerificationSuccessConfirm": "Got it", diff --git a/lib/pages/settings/advanced_settings_page.dart b/lib/pages/settings/advanced_settings_page.dart index 6d70aa90b..03ba7595a 100644 --- a/lib/pages/settings/advanced_settings_page.dart +++ b/lib/pages/settings/advanced_settings_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:lotti/pages/settings/outbox/outbox_badge.dart'; import 'package:lotti/pages/settings/sliver_box_adapter_page.dart'; +import 'package:lotti/sync/ui/matrix_settings_modal.dart'; import 'package:lotti/widgets/settings/settings_card.dart'; import 'package:lotti/widgets/settings/settings_icon.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -31,6 +32,7 @@ class AdvancedSettingsPage extends StatelessWidget { title: localizations.settingsMatrixTitle, path: '/settings/advanced/matrix_settings', ), + const MatrixSettingsCard(), SettingsNavCard( trailing: OutboxBadgeIcon( icon: SettingsIcon(MdiIcons.mailboxOutline), diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart new file mode 100644 index 000000000..09d4a11e5 --- /dev/null +++ b/lib/sync/ui/homeserver_config_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +SliverWoltModalSheetPage homeServerConfigPage({ + required BuildContext context, + required TextTheme textTheme, + required ValueNotifier pageIndexNotifier, +}) { + final localizations = AppLocalizations.of(context)!; + + return WoltModalSheetPage( + hasSabGradient: false, + stickyActionBar: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () => Navigator.of(context).pop(), + child: Center( + child: Text(localizations.settingsMatrixCancel), + ), + ), + const SizedBox(height: 8), + FilledButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value + 1, + child: Center( + child: Text(localizations.settingsMatrixNextPage), + ), + ), + ], + ), + ), + topBarTitle: Text( + localizations.settingsMatrixHomeserverConfigTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: const SizedBox( + height: 300, + child: Column(), + ), + ); +} diff --git a/lib/sync/ui/matrix_settings_modal.dart b/lib/sync/ui/matrix_settings_modal.dart new file mode 100644 index 000000000..73e15fb07 --- /dev/null +++ b/lib/sync/ui/matrix_settings_modal.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/sync/ui/homeserver_config_page.dart'; +import 'package:lotti/sync/ui/room_config_page.dart'; +import 'package:lotti/sync/ui/unverified_devices_page.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:lotti/widgets/settings/settings_card.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +class MatrixSettingsCard extends StatelessWidget { + const MatrixSettingsCard({super.key}); + + @override + Widget build(BuildContext context) { + final pageIndexNotifier = ValueNotifier(0); + + final localizations = AppLocalizations.of(context)!; + return SettingsCard( + title: localizations.settingsMatrixTitle, + onTap: () { + WoltModalSheet.show( + context: context, + pageIndexNotifier: pageIndexNotifier, + pageListBuilder: (modalSheetContext) { + final textTheme = Theme.of(context).textTheme; + return [ + homeServerConfigPage( + context: modalSheetContext, + textTheme: textTheme, + pageIndexNotifier: pageIndexNotifier, + ), + roomConfigPage( + context: modalSheetContext, + textTheme: textTheme, + pageIndexNotifier: pageIndexNotifier, + ), + unverifiedDevicesPage( + context: modalSheetContext, + textTheme: textTheme, + pageIndexNotifier: pageIndexNotifier, + ), + ]; + }, + modalTypeBuilder: (context) { + final size = MediaQuery.of(context).size.width; + if (size < WoltModalConfig.pageBreakpoint) { + return WoltModalType.bottomSheet; + } else { + return WoltModalType.dialog; + } + }, + onModalDismissedWithBarrierTap: () { + Navigator.of(context).pop(); + }, + maxDialogWidth: WoltModalConfig.maxDialogWidth, + minDialogWidth: WoltModalConfig.minDialogWidth, + minPageHeight: WoltModalConfig.minPageHeight, + maxPageHeight: WoltModalConfig.maxPageHeight, + ); + }, + ); + } +} diff --git a/lib/sync/ui/room_config_page.dart b/lib/sync/ui/room_config_page.dart new file mode 100644 index 000000000..82dc8efbe --- /dev/null +++ b/lib/sync/ui/room_config_page.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +SliverWoltModalSheetPage roomConfigPage({ + required BuildContext context, + required TextTheme textTheme, + required ValueNotifier pageIndexNotifier, +}) { + final localizations = AppLocalizations.of(context)!; + + return WoltModalSheetPage( + hasSabGradient: false, + stickyActionBar: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value - 1, + child: Center( + child: Text(localizations.settingsMatrixPreviousPage), + ), + ), + const SizedBox(height: 8), + FilledButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value + 1, + child: Center( + child: Text(localizations.settingsMatrixNextPage), + ), + ), + ], + ), + ), + topBarTitle: Text( + localizations.settingsMatrixRoomConfigTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: const SizedBox( + height: 300, + child: Column(), + ), + ); +} diff --git a/lib/sync/ui/unverified_devices_page.dart b/lib/sync/ui/unverified_devices_page.dart new file mode 100644 index 000000000..43310c901 --- /dev/null +++ b/lib/sync/ui/unverified_devices_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +SliverWoltModalSheetPage unverifiedDevicesPage({ + required BuildContext context, + required TextTheme textTheme, + required ValueNotifier pageIndexNotifier, +}) { + final localizations = AppLocalizations.of(context)!; + + return WoltModalSheetPage( + hasSabGradient: false, + stickyActionBar: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value - 1, + child: Center( + child: Text(localizations.settingsMatrixPreviousPage), + ), + ), + const SizedBox(height: 8), + FilledButton( + onPressed: () => Navigator.of(context).pop(), + child: Center( + child: Text(localizations.settingsMatrixDone), + ), + ), + ], + ), + ), + topBarTitle: Text( + localizations.settingsMatrixUnverifiedDevicesPage, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: const SizedBox( + height: 300, + child: Column(), + ), + ); +} diff --git a/pubspec.yaml b/pubspec.yaml index c7dd39ced..66fa21fa0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.447+2447 +version: 0.9.448+2448 msix_config: display_name: LottiApp From d5fb08114a1edd47670089762db031c6388bb0e5 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Sun, 14 Apr 2024 01:35:05 +0200 Subject: [PATCH 02/14] feat: add homeserver form and user QR code pages --- lib/l10n/app_en.arb | 1 + lib/sync/ui/homeserver_config_page.dart | 197 +++++++++++++++++- lib/sync/ui/matrix_logged_in_config_page.dart | 125 +++++++++++ lib/sync/ui/matrix_settings_modal.dart | 14 ++ lib/sync/ui/room_config_page.dart | 1 - lib/sync/ui/unverified_devices_page.dart | 1 - lib/widgets/misc/wolt_modal_config.dart | 2 +- lib/widgets/sync/matrix/matrix_settings.dart | 2 +- pubspec.yaml | 2 +- 9 files changed, 332 insertions(+), 13 deletions(-) create mode 100644 lib/sync/ui/matrix_logged_in_config_page.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 52492924a..7f430e040 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -207,6 +207,7 @@ "settingsMatrixCancel": "Cancel", "settingsMatrixDone": "Done", "settingsMatrixNextPage": "Next Page", + "settingsMatrixQrTextPage": "Scan this QR code from the device where you created the sync room.", "settingsMatrixPreviousPage": "Previous Page", "settingsMatrixUnverifiedDevicesPage": "Unverified Devices", "settingsMatrixUserLabel": "User", diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart index 09d4a11e5..8ec791c87 100644 --- a/lib/sync/ui/homeserver_config_page.dart +++ b/lib/sync/ui/homeserver_config_page.dart @@ -1,8 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:lotti/classes/config.dart'; +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/themes/theme.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; +const matrixUserKey = 'user'; +const matrixPasswordKey = 'password'; +const matrixHomeServerKey = 'home_server'; +const matrixRoomIdKey = 'room_id'; + SliverWoltModalSheetPage homeServerConfigPage({ required BuildContext context, required TextTheme textTheme, @@ -11,7 +23,6 @@ SliverWoltModalSheetPage homeServerConfigPage({ final localizations = AppLocalizations.of(context)!; return WoltModalSheetPage( - hasSabGradient: false, stickyActionBar: Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding), child: Row( @@ -25,10 +36,15 @@ SliverWoltModalSheetPage homeServerConfigPage({ ), const SizedBox(height: 8), FilledButton( - onPressed: () => - pageIndexNotifier.value = pageIndexNotifier.value + 1, - child: Center( - child: Text(localizations.settingsMatrixNextPage), + key: const Key('matrix_login'), + onPressed: () { + getIt().loginAndListen(); + Future.delayed(const Duration(milliseconds: 300)) + .then((value) => pageIndexNotifier.value = 1); + }, + child: Text( + localizations.settingsMatrixLoginButtonLabel, + semanticsLabel: localizations.settingsMatrixLoginButtonLabel, ), ), ], @@ -44,9 +60,174 @@ SliverWoltModalSheetPage homeServerConfigPage({ icon: const Icon(Icons.close), onPressed: Navigator.of(context).pop, ), - child: const SizedBox( - height: 300, - child: Column(), + child: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: HomeserverSettingsWidget( + pageIndexNotifier: pageIndexNotifier, + ), ), ); } + +class HomeserverSettingsWidget extends ConsumerStatefulWidget { + const HomeserverSettingsWidget({ + required this.pageIndexNotifier, + super.key, + }); + + final ValueNotifier pageIndexNotifier; + + @override + ConsumerState createState() => + _HomeserverSettingsWidgetState(); +} + +class _HomeserverSettingsWidgetState + extends ConsumerState { + final _formKey = GlobalKey(); + final _matrixService = getIt(); + + bool _dirty = false; + MatrixConfig? _previous; + + @override + void initState() { + super.initState(); + if (_matrixService.isLoggedIn()) { + widget.pageIndexNotifier.value = widget.pageIndexNotifier.value + 1; + } + + _matrixService.loadConfig().then((persisted) { + _previous = persisted; + + if (persisted != null) { + _formKey.currentState?.patchValue({ + matrixHomeServerKey: persisted.homeServer, + matrixUserKey: persisted.user, + matrixPasswordKey: persisted.password, + matrixRoomIdKey: persisted.roomId, + }); + + setState(() => _dirty = false); + } + }); + } + + void onFormChanged() { + _formKey.currentState?.save(); + setState(() => _dirty = true); + } + + Future onSavePressed() async { + final currentState = _formKey.currentState; + + if (currentState == null) { + return; + } + + final formData = currentState.value; + currentState.save(); + + if (currentState.validate()) { + final config = MatrixConfig( + homeServer: formData[matrixHomeServerKey] as String? ?? + _previous?.homeServer ?? + '', + user: formData[matrixUserKey] as String? ?? _previous?.user ?? '', + password: + formData[matrixPasswordKey] as String? ?? _previous?.password ?? '', + roomId: formData[matrixRoomIdKey] as String? ?? _previous?.roomId ?? '', + ); + + await _matrixService.setConfig(config); + setState(() => _dirty = false); + } + } + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + void maybePop() => Navigator.of(context).maybePop(); + + return FormBuilder( + key: _formKey, + autovalidateMode: AutovalidateMode.disabled, + onChanged: onFormChanged, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FormBuilderTextField( + name: matrixHomeServerKey, + validator: FormBuilderValidators.required(), + decoration: inputDecoration( + labelText: localizations.settingsMatrixHomeServerLabel, + themeData: Theme.of(context), + ), + ), + const SizedBox(height: 20), + FormBuilderTextField( + name: matrixUserKey, + validator: FormBuilderValidators.required(), + decoration: inputDecoration( + labelText: localizations.settingsMatrixUserLabel, + themeData: Theme.of(context), + ), + ), + const SizedBox(height: 20), + FormBuilderTextField( + name: matrixPasswordKey, + obscureText: true, + validator: FormBuilderValidators.required(), + decoration: inputDecoration( + labelText: localizations.settingsMatrixPasswordLabel, + themeData: Theme.of(context), + ), + ), + SizedBox( + height: 80, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (_previous != null) + OutlinedButton( + key: const Key('matrix_config_delete'), + onPressed: () async { + await _matrixService.logout(); + await _matrixService.deleteConfig(); + setState(() { + _dirty = false; + }); + maybePop(); + }, + child: Text( + localizations.settingsMatrixDeleteLabel, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + semanticsLabel: 'Delete Matrix Config', + ), + ), + if (_dirty) + OutlinedButton( + key: const Key('matrix_config_save'), + onPressed: () { + onSavePressed(); + maybePop(); + }, + child: Text( + localizations.settingsMatrixSaveLabel, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + semanticsLabel: 'Save Matrix Config', + ), + ), + ], + ), + ), + const SizedBox(height: 80), + ], + ), + ); + } +} diff --git a/lib/sync/ui/matrix_logged_in_config_page.dart b/lib/sync/ui/matrix_logged_in_config_page.dart new file mode 100644 index 000000000..ba7031976 --- /dev/null +++ b/lib/sync/ui/matrix_logged_in_config_page.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +const matrixUserKey = 'user'; +const matrixPasswordKey = 'password'; +const matrixHomeServerKey = 'home_server'; +const matrixRoomIdKey = 'room_id'; + +SliverWoltModalSheetPage homeServerLoggedInPage({ + required BuildContext context, + required TextTheme textTheme, + required ValueNotifier pageIndexNotifier, +}) { + final matrixService = getIt(); + + final localizations = AppLocalizations.of(context)!; + + return WoltModalSheetPage( + stickyActionBar: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + key: const Key('matrix_logout'), + onPressed: () { + pageIndexNotifier.value = 0; + matrixService.logout(); + }, + child: Text( + localizations.settingsMatrixLogoutButtonLabel, + semanticsLabel: localizations.settingsMatrixLogoutButtonLabel, + ), + ), + const SizedBox(height: 8), + FilledButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value + 1, + child: Center( + child: Text(localizations.settingsMatrixNextPage), + ), + ), + ], + ), + ), + topBarTitle: Text( + localizations.settingsMatrixHomeserverConfigTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: const Padding( + padding: EdgeInsets.all(WoltModalConfig.pagePadding), + child: HomeserverLoggedInWidget(), + ), + ); +} + +class HomeserverLoggedInWidget extends ConsumerStatefulWidget { + const HomeserverLoggedInWidget({super.key}); + + @override + ConsumerState createState() => + _HomeserverLoggedInWidgetState(); +} + +class _HomeserverLoggedInWidgetState + extends ConsumerState { + final _matrixService = getIt(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + + final userId = _matrixService.client.userID; + + return Column( + children: [ + const SizedBox(height: 20), + if (userId != null) + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(8), + child: QrImageView( + data: userId, + padding: EdgeInsets.zero, + size: 280, + key: const Key('QrImage'), + ), + ), + ), + ), + const SizedBox(height: 20), + Text( + '${_matrixService.client.userID}', + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 10), + Text( + localizations.settingsMatrixQrTextPage, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 80), + ], + ); + } +} diff --git a/lib/sync/ui/matrix_settings_modal.dart b/lib/sync/ui/matrix_settings_modal.dart index 73e15fb07..eab46a5cf 100644 --- a/lib/sync/ui/matrix_settings_modal.dart +++ b/lib/sync/ui/matrix_settings_modal.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; import 'package:lotti/sync/ui/homeserver_config_page.dart'; +import 'package:lotti/sync/ui/matrix_logged_in_config_page.dart'; import 'package:lotti/sync/ui/room_config_page.dart'; import 'package:lotti/sync/ui/unverified_devices_page.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; @@ -18,6 +21,12 @@ class MatrixSettingsCard extends StatelessWidget { return SettingsCard( title: localizations.settingsMatrixTitle, onTap: () { + if (getIt().isLoggedIn()) { + pageIndexNotifier.value = 1; + } else { + pageIndexNotifier.value = 0; + } + WoltModalSheet.show( context: context, pageIndexNotifier: pageIndexNotifier, @@ -29,6 +38,11 @@ class MatrixSettingsCard extends StatelessWidget { textTheme: textTheme, pageIndexNotifier: pageIndexNotifier, ), + homeServerLoggedInPage( + context: modalSheetContext, + textTheme: textTheme, + pageIndexNotifier: pageIndexNotifier, + ), roomConfigPage( context: modalSheetContext, textTheme: textTheme, diff --git a/lib/sync/ui/room_config_page.dart b/lib/sync/ui/room_config_page.dart index 82dc8efbe..a38f25fa8 100644 --- a/lib/sync/ui/room_config_page.dart +++ b/lib/sync/ui/room_config_page.dart @@ -11,7 +11,6 @@ SliverWoltModalSheetPage roomConfigPage({ final localizations = AppLocalizations.of(context)!; return WoltModalSheetPage( - hasSabGradient: false, stickyActionBar: Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding), child: Row( diff --git a/lib/sync/ui/unverified_devices_page.dart b/lib/sync/ui/unverified_devices_page.dart index 43310c901..cce737c13 100644 --- a/lib/sync/ui/unverified_devices_page.dart +++ b/lib/sync/ui/unverified_devices_page.dart @@ -11,7 +11,6 @@ SliverWoltModalSheetPage unverifiedDevicesPage({ final localizations = AppLocalizations.of(context)!; return WoltModalSheetPage( - hasSabGradient: false, stickyActionBar: Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding), child: Row( diff --git a/lib/widgets/misc/wolt_modal_config.dart b/lib/widgets/misc/wolt_modal_config.dart index d57ddf2ac..4231b2749 100644 --- a/lib/widgets/misc/wolt_modal_config.dart +++ b/lib/widgets/misc/wolt_modal_config.dart @@ -3,6 +3,6 @@ class WoltModalConfig { static const pagePadding = 16.0; static const maxDialogWidth = 560.0; static const minDialogWidth = 400.0; - static const minPageHeight = 0.0; + static const minPageHeight = 0.3; static const maxPageHeight = 0.9; } diff --git a/lib/widgets/sync/matrix/matrix_settings.dart b/lib/widgets/sync/matrix/matrix_settings.dart index 4c94ea665..f82b5d2f3 100644 --- a/lib/widgets/sync/matrix/matrix_settings.dart +++ b/lib/widgets/sync/matrix/matrix_settings.dart @@ -32,7 +32,7 @@ class MatrixSettingsWidget extends ConsumerStatefulWidget { class _MatrixSettingsWidgetState extends ConsumerState { final _matrixService = getIt(); final _formKey = GlobalKey(); - final GlobalKey _qrKey = GlobalKey(debugLabel: 'matrix_QR_key'); + final _qrKey = GlobalKey(debugLabel: 'matrix_QR_key'); bool _dirty = false; MatrixConfig? _previous; diff --git a/pubspec.yaml b/pubspec.yaml index 66fa21fa0..ecca6befc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2448 +version: 0.9.448+2449 msix_config: display_name: LottiApp From 68c019b88dab150eca19357f3e9196af00b5f1b9 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Sun, 14 Apr 2024 14:01:48 +0200 Subject: [PATCH 03/14] feat: use Riverpod for login/logout --- lib/l10n/app_en.arb | 2 +- lib/sync/state/matrix_login_provider.dart | 36 +++++ lib/sync/state/matrix_login_provider.g.dart | 57 ++++++++ lib/sync/ui/homeserver_config_page.dart | 69 ++++++--- lib/sync/ui/matrix_logged_in_config_page.dart | 137 +++++++++++------- pubspec.lock | 72 +++++++++ pubspec.yaml | 3 + 7 files changed, 295 insertions(+), 81 deletions(-) create mode 100644 lib/sync/state/matrix_login_provider.dart create mode 100644 lib/sync/state/matrix_login_provider.g.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7f430e040..6b4c98f77 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -207,7 +207,7 @@ "settingsMatrixCancel": "Cancel", "settingsMatrixDone": "Done", "settingsMatrixNextPage": "Next Page", - "settingsMatrixQrTextPage": "Scan this QR code from the device where you created the sync room.", + "settingsMatrixQrTextPage": "Scan this QR code to invite device to a sync room.", "settingsMatrixPreviousPage": "Previous Page", "settingsMatrixUnverifiedDevicesPage": "Unverified Devices", "settingsMatrixUserLabel": "User", diff --git a/lib/sync/state/matrix_login_provider.dart b/lib/sync/state/matrix_login_provider.dart new file mode 100644 index 000000000..5d58a97b6 --- /dev/null +++ b/lib/sync/state/matrix_login_provider.dart @@ -0,0 +1,36 @@ +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:matrix/matrix.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'matrix_login_provider.g.dart'; + +@riverpod +class MatrixLoginController extends _$MatrixLoginController { + final _matrixService = getIt(); + + @override + Future build() async { + return ref.watch(loginStateStreamProvider).value; + } + + Future login() => _matrixService.login(); + Future logout() => _matrixService.logout(); +} + +@riverpod +Stream loginStateStream(LoginStateStreamRef ref) { + return getIt().client.onLoginStateChanged.stream; +} + +@riverpod +Future loggedInUserId(LoggedInUserIdRef ref) async { + final matrixService = getIt(); + final loginState = ref.watch(loginStateStreamProvider).valueOrNull; + + if (loginState == LoginState.loggedIn) { + return matrixService.client.userID; + } + + return null; +} diff --git a/lib/sync/state/matrix_login_provider.g.dart b/lib/sync/state/matrix_login_provider.g.dart new file mode 100644 index 000000000..cc6fa6f7f --- /dev/null +++ b/lib/sync/state/matrix_login_provider.g.dart @@ -0,0 +1,57 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matrix_login_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$loginStateStreamHash() => r'e643511b02c1a28f58434438896b333d978320d7'; + +/// See also [loginStateStream]. +@ProviderFor(loginStateStream) +final loginStateStreamProvider = AutoDisposeStreamProvider.internal( + loginStateStream, + name: r'loginStateStreamProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$loginStateStreamHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef LoginStateStreamRef = AutoDisposeStreamProviderRef; +String _$loggedInUserIdHash() => r'e4c038bb0be41c5ae7749e5f0981dad4611394f7'; + +/// See also [loggedInUserId]. +@ProviderFor(loggedInUserId) +final loggedInUserIdProvider = AutoDisposeFutureProvider.internal( + loggedInUserId, + name: r'loggedInUserIdProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$loggedInUserIdHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef LoggedInUserIdRef = AutoDisposeFutureProviderRef; +String _$matrixLoginControllerHash() => + r'3fb97078ff542a174f4a09aa988aae4be681b33d'; + +/// See also [MatrixLoginController]. +@ProviderFor(MatrixLoginController) +final matrixLoginControllerProvider = AutoDisposeAsyncNotifierProvider< + MatrixLoginController, LoginState?>.internal( + MatrixLoginController.new, + name: r'matrixLoginControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixLoginControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MatrixLoginController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart index 8ec791c87..6f5c1f546 100644 --- a/lib/sync/ui/homeserver_config_page.dart +++ b/lib/sync/ui/homeserver_config_page.dart @@ -6,6 +6,7 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:lotti/classes/config.dart'; import 'package:lotti/get_it.dart'; import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/sync/state/matrix_login_provider.dart'; import 'package:lotti/themes/theme.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; @@ -23,7 +24,43 @@ SliverWoltModalSheetPage homeServerConfigPage({ final localizations = AppLocalizations.of(context)!; return WoltModalSheetPage( - stickyActionBar: Padding( + stickyActionBar: HomeserverConfigPageStickyActionBar( + pageIndexNotifier: pageIndexNotifier, + ), + topBarTitle: Text( + localizations.settingsMatrixHomeserverConfigTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: HomeserverSettingsWidget( + pageIndexNotifier: pageIndexNotifier, + ), + ), + ); +} + +class HomeserverConfigPageStickyActionBar extends ConsumerWidget { + const HomeserverConfigPageStickyActionBar({ + required this.pageIndexNotifier, + super.key, + }); + + final ValueNotifier pageIndexNotifier; + @override + Widget build( + BuildContext context, + WidgetRef ref, + ) { + final localizations = AppLocalizations.of(context)!; + + return Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -37,10 +74,10 @@ SliverWoltModalSheetPage homeServerConfigPage({ const SizedBox(height: 8), FilledButton( key: const Key('matrix_login'), - onPressed: () { - getIt().loginAndListen(); - Future.delayed(const Duration(milliseconds: 300)) - .then((value) => pageIndexNotifier.value = 1); + onPressed: () async { + await ref.read(matrixLoginControllerProvider.notifier).login(); + await Future.delayed(const Duration(milliseconds: 300)); + pageIndexNotifier.value = 1; }, child: Text( localizations.settingsMatrixLoginButtonLabel, @@ -49,24 +86,8 @@ SliverWoltModalSheetPage homeServerConfigPage({ ), ], ), - ), - topBarTitle: Text( - localizations.settingsMatrixHomeserverConfigTitle, - style: textTheme.titleMedium, - ), - isTopBarLayerAlwaysVisible: true, - trailingNavBarWidget: IconButton( - padding: const EdgeInsets.all(WoltModalConfig.pagePadding), - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), - child: Padding( - padding: const EdgeInsets.all(WoltModalConfig.pagePadding), - child: HomeserverSettingsWidget( - pageIndexNotifier: pageIndexNotifier, - ), - ), - ); + ); + } } class HomeserverSettingsWidget extends ConsumerStatefulWidget { @@ -94,7 +115,7 @@ class _HomeserverSettingsWidgetState void initState() { super.initState(); if (_matrixService.isLoggedIn()) { - widget.pageIndexNotifier.value = widget.pageIndexNotifier.value + 1; + widget.pageIndexNotifier.value = 1; } _matrixService.loadConfig().then((persisted) { diff --git a/lib/sync/ui/matrix_logged_in_config_page.dart b/lib/sync/ui/matrix_logged_in_config_page.dart index ba7031976..ffb78e33b 100644 --- a/lib/sync/ui/matrix_logged_in_config_page.dart +++ b/lib/sync/ui/matrix_logged_in_config_page.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:lotti/get_it.dart'; -import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/sync/state/matrix_login_provider.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; @@ -17,21 +16,53 @@ SliverWoltModalSheetPage homeServerLoggedInPage({ required TextTheme textTheme, required ValueNotifier pageIndexNotifier, }) { - final matrixService = getIt(); - final localizations = AppLocalizations.of(context)!; return WoltModalSheetPage( - stickyActionBar: Padding( + stickyActionBar: LoggedInPageStickyActionBar( + pageIndexNotifier: pageIndexNotifier, + ), + topBarTitle: Text( + localizations.settingsMatrixHomeserverConfigTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: const Padding( + padding: EdgeInsets.all(WoltModalConfig.pagePadding), + child: HomeserverLoggedInWidget(), + ), + ); +} + +class LoggedInPageStickyActionBar extends ConsumerWidget { + const LoggedInPageStickyActionBar({ + required this.pageIndexNotifier, + super.key, + }); + + final ValueNotifier pageIndexNotifier; + @override + Widget build( + BuildContext context, + WidgetRef ref, + ) { + final localizations = AppLocalizations.of(context)!; + + return Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ OutlinedButton( key: const Key('matrix_logout'), - onPressed: () { + onPressed: () async { + await ref.read(matrixLoginControllerProvider.notifier).logout(); pageIndexNotifier.value = 0; - matrixService.logout(); }, child: Text( localizations.settingsMatrixLogoutButtonLabel, @@ -40,30 +71,15 @@ SliverWoltModalSheetPage homeServerLoggedInPage({ ), const SizedBox(height: 8), FilledButton( - onPressed: () => - pageIndexNotifier.value = pageIndexNotifier.value + 1, + onPressed: () => pageIndexNotifier.value = 2, child: Center( child: Text(localizations.settingsMatrixNextPage), ), ), ], ), - ), - topBarTitle: Text( - localizations.settingsMatrixHomeserverConfigTitle, - style: textTheme.titleMedium, - ), - isTopBarLayerAlwaysVisible: true, - trailingNavBarWidget: IconButton( - padding: const EdgeInsets.all(WoltModalConfig.pagePadding), - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), - child: const Padding( - padding: EdgeInsets.all(WoltModalConfig.pagePadding), - child: HomeserverLoggedInWidget(), - ), - ); + ); + } } class HomeserverLoggedInWidget extends ConsumerStatefulWidget { @@ -76,8 +92,6 @@ class HomeserverLoggedInWidget extends ConsumerStatefulWidget { class _HomeserverLoggedInWidgetState extends ConsumerState { - final _matrixService = getIt(); - @override void initState() { super.initState(); @@ -86,40 +100,51 @@ class _HomeserverLoggedInWidgetState @override Widget build(BuildContext context) { final localizations = AppLocalizations.of(context)!; + final userIdAsyncValue = ref.watch(loggedInUserIdProvider); + + return userIdAsyncValue.map( + data: (data) { + final userId = data.valueOrNull; - final userId = _matrixService.client.userID; + if (userId == null) { + return const SizedBox.shrink(); + } - return Column( - children: [ - const SizedBox(height: 20), - if (userId != null) - Center( - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(8), - child: QrImageView( - data: userId, - padding: EdgeInsets.zero, - size: 280, - key: const Key('QrImage'), + return Column( + children: [ + const SizedBox(height: 20), + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(8), + child: QrImageView( + data: userId, + padding: EdgeInsets.zero, + size: 240, + key: const Key('QrImage'), + ), ), ), ), - ), - const SizedBox(height: 20), - Text( - '${_matrixService.client.userID}', - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 10), - Text( - localizations.settingsMatrixQrTextPage, - style: Theme.of(context).textTheme.bodySmall, - ), - const SizedBox(height: 80), - ], + const SizedBox(height: 20), + Text(userId, style: Theme.of(context).textTheme.bodyLarge), + const SizedBox(height: 10), + Text( + localizations.settingsMatrixQrTextPage, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 80), + ], + ); + }, + error: (error) { + return Text(error.valueOrNull.toString()); + }, + loading: (loading) { + return const CircularProgressIndicator(); + }, ); } } diff --git a/pubspec.lock b/pubspec.lock index a6bf1362a..de59e3c94 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -289,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" cli_util: dependency: transitive description: @@ -401,6 +409,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + custom_lint: + dependency: transitive + description: + name: custom_lint + sha256: "7c0aec12df22f9082146c354692056677f1e70bc43471644d1fdb36c6fdda799" + url: "https://pub.dev" + source: hosted + version: "0.6.4" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: d7dc41e709dde223806660268678be7993559e523eb3164e2a1425fd6f7615a9 + url: "https://pub.dev" + source: hosted + version: "0.6.4" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: a85e8f78f4c52f6c63cdaf8c872eb573db0231dcdf3c3a5906d493c1f8bc20e6 + url: "https://pub.dev" + source: hosted + version: "0.6.3" dart_geohash: dependency: "direct main" description: @@ -1202,6 +1234,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" html: dependency: transitive description: @@ -2119,6 +2159,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.1" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: "8b71f03fc47ae27d13769496a1746332df4cec43918aeba9aff1e232783a780f" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: e5e796c0eba4030c704e9dae1b834a6541814963292839dcf9638d53eba84f5c + url: "https://pub.dev" + source: hosted + version: "2.3.5" + riverpod_generator: + dependency: "direct main" + description: + name: riverpod_generator + sha256: d451608bf17a372025fc36058863737636625dfdb7e3cbf6142e0dfeb366ab22 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + riverpod_lint: + dependency: "direct main" + description: + name: riverpod_lint + sha256: "3c67c14ccd16f0c9d53e35ef70d06cd9d072e2fb14557326886bbde903b230a5" + url: "https://pub.dev" + source: hosted + version: "2.3.10" rxdart: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ecca6befc..eae2cc248 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -142,6 +142,9 @@ dependencies: record: ^5.0.4 research_package: ^1.3.2 retry: ^3.1.0 + riverpod_annotation: ^2.3.5 + riverpod_generator: ^2.4.0 + riverpod_lint: ^2.3.10 rxdart: ^0.27.7 share_plus: ^8.0.2 sqflite: ^2.0.1 From c8a79199d824dc38c776f2d61f9ff6c55120b9c1 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Mon, 15 Apr 2024 23:10:51 +0200 Subject: [PATCH 04/14] feat: only login on load when previously logged in --- lib/get_it.dart | 2 +- lib/sync/matrix/client.dart | 5 ++-- lib/sync/matrix/matrix_service.dart | 28 +++++++++++++++++-- lib/sync/state/matrix_login_provider.dart | 3 +- lib/sync/state/matrix_login_provider.g.dart | 2 +- lib/sync/ui/homeserver_config_page.dart | 1 - lib/sync/ui/matrix_logged_in_config_page.dart | 2 +- pubspec.yaml | 2 +- 8 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/get_it.dart b/lib/get_it.dart index 96f52ddaa..94d16703d 100644 --- a/lib/get_it.dart +++ b/lib/get_it.dart @@ -75,7 +75,7 @@ Future registerSingletons() async { ..registerSingleton(Maintenance()) ..registerSingleton(NavService()); - unawaited(getIt().loginAndListen()); + unawaited(getIt().init()); await initConfigFlags(getIt(), inMemoryDatabase: false); } diff --git a/lib/sync/matrix/client.dart b/lib/sync/matrix/client.dart index e5ffe1bec..a7cf41a28 100644 --- a/lib/sync/matrix/client.dart +++ b/lib/sync/matrix/client.dart @@ -54,8 +54,9 @@ Future createMatrixDeviceName() async { return '$deviceName $dateHhMm ${uuid.v1().substring(0, 4)}'; } -Future matrixLogin({ +Future matrixConnect({ required MatrixService service, + required bool shouldAttemptLogin, }) async { final loggingDb = getIt(); @@ -87,7 +88,7 @@ Future matrixLogin({ waitUntilLoadCompletedLoaded: false, ); - if (!service.isLoggedIn()) { + if (!service.isLoggedIn() && shouldAttemptLogin) { final initialDeviceDisplayName = service.deviceDisplayName ?? await createMatrixDeviceName(); diff --git a/lib/sync/matrix/matrix_service.dart b/lib/sync/matrix/matrix_service.dart index 4108ff531..eb4e8382d 100644 --- a/lib/sync/matrix/matrix_service.dart +++ b/lib/sync/matrix/matrix_service.dart @@ -78,15 +78,37 @@ class MatrixService { final incomingKeyVerificationController = StreamController.broadcast(); - Future loginAndListen() async { + Future init() async { await loadConfig(); - await login(); + await connect(); + if (_client.onLoginStateChanged.value == LoginState.loggedIn) { + await listen(); + } + } + + Future listen() async { await startKeyVerificationListener(); await listenToTimeline(); } + Future loginAndListen() async { + await loadConfig(); + await login(); + await listen(); + } + Client get client => _client; - Future login() => matrixLogin(service: this); + + Future login() => matrixConnect( + service: this, + shouldAttemptLogin: true, + ); + + Future connect() => matrixConnect( + service: this, + shouldAttemptLogin: false, + ); + Future joinRoom(String roomId) => joinMatrixRoom(roomId: roomId, service: this); diff --git a/lib/sync/state/matrix_login_provider.dart b/lib/sync/state/matrix_login_provider.dart index 5d58a97b6..4ade2052d 100644 --- a/lib/sync/state/matrix_login_provider.dart +++ b/lib/sync/state/matrix_login_provider.dart @@ -26,7 +26,8 @@ Stream loginStateStream(LoginStateStreamRef ref) { @riverpod Future loggedInUserId(LoggedInUserIdRef ref) async { final matrixService = getIt(); - final loginState = ref.watch(loginStateStreamProvider).valueOrNull; + final loginState = ref.watch(loginStateStreamProvider).valueOrNull ?? + matrixService.client.onLoginStateChanged.value; if (loginState == LoginState.loggedIn) { return matrixService.client.userID; diff --git a/lib/sync/state/matrix_login_provider.g.dart b/lib/sync/state/matrix_login_provider.g.dart index cc6fa6f7f..1db4461b2 100644 --- a/lib/sync/state/matrix_login_provider.g.dart +++ b/lib/sync/state/matrix_login_provider.g.dart @@ -21,7 +21,7 @@ final loginStateStreamProvider = AutoDisposeStreamProvider.internal( ); typedef LoginStateStreamRef = AutoDisposeStreamProviderRef; -String _$loggedInUserIdHash() => r'e4c038bb0be41c5ae7749e5f0981dad4611394f7'; +String _$loggedInUserIdHash() => r'5461bb2f51c4fd99c3ccdbfab6cdf6e2aa0acacc'; /// See also [loggedInUserId]. @ProviderFor(loggedInUserId) diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart index 6f5c1f546..abf46ff69 100644 --- a/lib/sync/ui/homeserver_config_page.dart +++ b/lib/sync/ui/homeserver_config_page.dart @@ -76,7 +76,6 @@ class HomeserverConfigPageStickyActionBar extends ConsumerWidget { key: const Key('matrix_login'), onPressed: () async { await ref.read(matrixLoginControllerProvider.notifier).login(); - await Future.delayed(const Duration(milliseconds: 300)); pageIndexNotifier.value = 1; }, child: Text( diff --git a/lib/sync/ui/matrix_logged_in_config_page.dart b/lib/sync/ui/matrix_logged_in_config_page.dart index ffb78e33b..b38f7cc2c 100644 --- a/lib/sync/ui/matrix_logged_in_config_page.dart +++ b/lib/sync/ui/matrix_logged_in_config_page.dart @@ -107,7 +107,7 @@ class _HomeserverLoggedInWidgetState final userId = data.valueOrNull; if (userId == null) { - return const SizedBox.shrink(); + return const CircularProgressIndicator(); } return Column( diff --git a/pubspec.yaml b/pubspec.yaml index eae2cc248..b9e2e4e59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2449 +version: 0.9.448+2450 msix_config: display_name: LottiApp From 3f3399d7d4d794bdfc3e8b58c4d44581bc840d26 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Mon, 15 Apr 2024 23:18:20 +0200 Subject: [PATCH 05/14] chore: upgrade dependencies --- .../xcshareddata/swiftpm/Package.resolved | 33 ------------------- pubspec.lock | 32 +++++++++--------- pubspec.yaml | 2 +- 3 files changed, 17 insertions(+), 50 deletions(-) delete mode 100644 macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 44710c6c2..000000000 --- a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,33 +0,0 @@ -{ - "originHash" : "8f61689e55c5551e76f2c686d145061dc1fa621a58cbca576565ebfabc15c894", - "pins" : [ - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-transformers", - "kind" : "remoteSourceControl", - "location" : "https://github.com/huggingface/swift-transformers.git", - "state" : { - "revision" : "74b94211bdc741694ed7e700a1104c72e5ba68fe", - "version" : "0.1.7" - } - }, - { - "identity" : "whisperkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/argmaxinc/whisperkit", - "state" : { - "revision" : "61df12a322aa9ff840a46f78115ad0ad93873e2e", - "version" : "0.5.0" - } - } - ], - "version" : 3 -} diff --git a/pubspec.lock b/pubspec.lock index de59e3c94..6891500af 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -445,10 +445,10 @@ packages: dependency: transitive description: name: dart_quill_delta - sha256: "540b80375458ac63cf7de4b0e674399c3d8790150023103043ac8a50c76895ba" + sha256: "5e5ab83a28ab7045d55ade8759762673bffc4931b09286424989b9feb3cd4b83" url: "https://pub.dev" source: hosted - version: "9.3.6" + version: "9.3.7" dart_style: dependency: "direct dev" description: @@ -502,10 +502,10 @@ packages: dependency: transitive description: name: dio - sha256: "0978e9a3e45305a80a7210dbeaf79d6ee8bee33f70c8e542dc654c952070217f" + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" url: "https://pub.dev" source: hosted - version: "5.4.2+1" + version: "5.4.3+1" drift: dependency: "direct main" description: @@ -913,10 +913,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 + sha256: a701df4866f9a38bb8e4450a54c143bbeeb0ce2381e7df5a36e1006f3b43bb28 url: "https://pub.dev" source: hosted - version: "17.0.0" + version: "17.0.1" flutter_local_notifications_linux: dependency: transitive description: @@ -950,10 +950,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "31c12de79262b5431c5492e9c89948aa789158435f707d3519a7fdef6af28af7" + sha256: "04c4722cc36ec5af38acc38ece70d22d3c2123c61305d555750a091517bbe504" url: "https://pub.dev" source: hosted - version: "0.6.22+1" + version: "0.6.23" flutter_native_splash: dependency: "direct dev" description: @@ -990,10 +990,10 @@ packages: dependency: "direct main" description: name: flutter_quill - sha256: "8950da24ad6622d48333997e7a454d5154842f909c962b1f1b87409b031ced06" + sha256: c37ab7bd479daa7b20ef281b528d8eea10a2c4f149edb1f94acdbc41af0fbaf3 url: "https://pub.dev" source: hosted - version: "9.3.6" + version: "9.3.7" flutter_riverpod: dependency: "direct main" description: @@ -1141,10 +1141,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: ae30b28cc73053f79fd46b15f430db16cae22a0554e6cd25333c840b310b0270 + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 url: "https://pub.dev" source: hosted - version: "7.6.9" + version: "7.7.0" glados: dependency: "direct dev" description: @@ -2688,10 +2688,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" url_launcher_windows: dependency: transitive description: @@ -2888,10 +2888,10 @@ packages: dependency: transitive description: name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" window_manager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b9e2e4e59..d48b93f79 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2450 +version: 0.9.448+2451 msix_config: display_name: LottiApp From 0453e350ab9160aaaddd11f50691f595c2ae3446 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Tue, 16 Apr 2024 00:34:33 +0200 Subject: [PATCH 06/14] refactor: use isLoggedInProvider on config page --- lib/sync/state/matrix_login_provider.dart | 8 +++++ lib/sync/state/matrix_login_provider.g.dart | 14 ++++++++ lib/sync/ui/homeserver_config_page.dart | 5 +-- .../xcshareddata/swiftpm/Package.resolved | 33 +++++++++++++++++++ pubspec.yaml | 2 +- 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/lib/sync/state/matrix_login_provider.dart b/lib/sync/state/matrix_login_provider.dart index 4ade2052d..fa4d86aaf 100644 --- a/lib/sync/state/matrix_login_provider.dart +++ b/lib/sync/state/matrix_login_provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:lotti/get_it.dart'; import 'package:lotti/sync/matrix/matrix_service.dart'; import 'package:matrix/matrix.dart'; @@ -23,6 +25,12 @@ Stream loginStateStream(LoginStateStreamRef ref) { return getIt().client.onLoginStateChanged.stream; } +@riverpod +Future isLoggedIn(IsLoggedInRef ref) async { + final loginState = ref.watch(loginStateStreamProvider).value; + return loginState == LoginState.loggedIn; +} + @riverpod Future loggedInUserId(LoggedInUserIdRef ref) async { final matrixService = getIt(); diff --git a/lib/sync/state/matrix_login_provider.g.dart b/lib/sync/state/matrix_login_provider.g.dart index 1db4461b2..867cbbeb3 100644 --- a/lib/sync/state/matrix_login_provider.g.dart +++ b/lib/sync/state/matrix_login_provider.g.dart @@ -21,6 +21,20 @@ final loginStateStreamProvider = AutoDisposeStreamProvider.internal( ); typedef LoginStateStreamRef = AutoDisposeStreamProviderRef; +String _$isLoggedInHash() => r'e6fca2a8e06f31e155da7448053c88df2f721a12'; + +/// See also [isLoggedIn]. +@ProviderFor(isLoggedIn) +final isLoggedInProvider = AutoDisposeFutureProvider.internal( + isLoggedIn, + name: r'isLoggedInProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$isLoggedInHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef IsLoggedInRef = AutoDisposeFutureProviderRef; String _$loggedInUserIdHash() => r'5461bb2f51c4fd99c3ccdbfab6cdf6e2aa0acacc'; /// See also [loggedInUserId]. diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart index abf46ff69..e56a59933 100644 --- a/lib/sync/ui/homeserver_config_page.dart +++ b/lib/sync/ui/homeserver_config_page.dart @@ -113,7 +113,9 @@ class _HomeserverSettingsWidgetState @override void initState() { super.initState(); - if (_matrixService.isLoggedIn()) { + + final isLoggedIn = ref.read(isLoggedInProvider).valueOrNull ?? false; + if (isLoggedIn) { widget.pageIndexNotifier.value = 1; } @@ -212,7 +214,6 @@ class _HomeserverSettingsWidgetState OutlinedButton( key: const Key('matrix_config_delete'), onPressed: () async { - await _matrixService.logout(); await _matrixService.deleteConfig(); setState(() { _dirty = false; diff --git a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..44710c6c2 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,33 @@ +{ + "originHash" : "8f61689e55c5551e76f2c686d145061dc1fa621a58cbca576565ebfabc15c894", + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-transformers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/huggingface/swift-transformers.git", + "state" : { + "revision" : "74b94211bdc741694ed7e700a1104c72e5ba68fe", + "version" : "0.1.7" + } + }, + { + "identity" : "whisperkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/argmaxinc/whisperkit", + "state" : { + "revision" : "61df12a322aa9ff840a46f78115ad0ad93873e2e", + "version" : "0.5.0" + } + } + ], + "version" : 3 +} diff --git a/pubspec.yaml b/pubspec.yaml index d48b93f79..d3a2776b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2451 +version: 0.9.448+2452 msix_config: display_name: LottiApp From 1481115dedc747e6849184e0ee30211a5888e2ed Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Tue, 16 Apr 2024 16:02:19 +0200 Subject: [PATCH 07/14] chore: upgrade dependencies --- integration_test/build/.last_build_id | 1 + lib/sync/matrix/client.dart | 2 + pubspec.lock | 208 +++++++++++++++++++++++--- pubspec.yaml | 6 +- 4 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 integration_test/build/.last_build_id diff --git a/integration_test/build/.last_build_id b/integration_test/build/.last_build_id new file mode 100644 index 000000000..5016923a1 --- /dev/null +++ b/integration_test/build/.last_build_id @@ -0,0 +1 @@ +e581f07cdf2a87cf03a4c057574d76e4 \ No newline at end of file diff --git a/lib/sync/matrix/client.dart b/lib/sync/matrix/client.dart index a7cf41a28..e3def8b4d 100644 --- a/lib/sync/matrix/client.dart +++ b/lib/sync/matrix/client.dart @@ -25,6 +25,8 @@ Client createMatrixClient({ databaseBuilder: (_) async { final docDir = getIt(); final path = '${docDir.path}/matrix/'; + // TODO(matthiasn): use MatrixSdkDatabase instead + // ignore: deprecated_member_use final db = HiveCollectionsDatabase(hiveDbName ?? 'lotti_sync', path); await db.open(); return db; diff --git a/pubspec.lock b/pubspec.lock index 6891500af..db881f667 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: "direct main" description: name: arb_utils - sha256: "4af356416a2cb31dad60e64f65bb79339b3b7bfc8d31eb9946f5e25851d3f1d8" + sha256: "783b57d5c2860101048b7189ef2950db7622c670aa24162600cc4d3023326d92" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.7.1" archive: dependency: "direct dev" description: @@ -289,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + chunked_stream: + dependency: transitive + description: + name: chunked_stream + sha256: b2fde5f81d780f0c1699b8347cae2e413412ae947fc6e64727cc48c6bb54c95c + url: "https://pub.dev" + source: hosted + version: "1.4.2" ci: dependency: transitive description: @@ -297,6 +305,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + circular_buffer: + dependency: transitive + description: + name: circular_buffer + sha256: "2889afcfc97aa0d9a4930ae5fdf206aea7d0ac88a3649acec9130565cd8f45d8" + url: "https://pub.dev" + source: hosted + version: "0.11.0" cli_util: dependency: transitive description: @@ -401,14 +417,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + csv: + dependency: transitive + description: + name: csv + sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c + url: "https://pub.dev" + source: hosted + version: "6.0.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" custom_lint: dependency: transitive description: @@ -433,6 +457,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.3" + dart_console: + dependency: transitive + description: + name: dart_console + sha256: "2f94b62ac492892bc5091b8f5d416937974405ececab7ad96e5f8aae9d9f1527" + url: "https://pub.dev" + source: hosted + version: "4.0.2" dart_geohash: dependency: "direct main" description: @@ -465,6 +497,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + dcli: + dependency: transitive + description: + name: dcli + sha256: c076448cee6fe563e1964ade9d4653d855e69d204547791fff0d0fe96c7bd75a + url: "https://pub.dev" + source: hosted + version: "4.0.1-beta.4" + dcli_common: + dependency: transitive + description: + name: dcli_common + sha256: c8af0ed248e67b7293cc7ca24a11cc6f3762009ce2a4d0d03c1368e6b0f15fb9 + url: "https://pub.dev" + source: hosted + version: "4.0.1-beta.4" + dcli_core: + dependency: transitive + description: + name: dcli_core + sha256: "915b6d7aa9cc039de4be263ecbdac0d372286059f5dba02a8b171748afb4883c" + url: "https://pub.dev" + source: hosted + version: "4.0.1-beta.4" + dcli_terminal: + dependency: transitive + description: + name: dcli_terminal + sha256: "5449d06b49966796237c5953abc8763dfbc3c29d180ba41f8e182d76c8379840" + url: "https://pub.dev" + source: hosted + version: "4.0.1-beta.4" delta_markdown: dependency: "direct main" description: @@ -1129,6 +1193,14 @@ packages: description: flutter source: sdk version: "0.0.0" + functional_data: + dependency: transitive + description: + name: functional_data + sha256: "76d17dc707c40e552014f5a49c0afcc3f1e3f05e800cd6b7872940bfe41a5039" + url: "https://pub.dev" + source: hosted + version: "1.2.0" geoclue: dependency: "direct main" description: @@ -1161,6 +1233,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + globbing: + dependency: transitive + description: + name: globbing + sha256: "4f89cfaf6fa74c9c1740a96259da06bd45411ede56744e28017cc534a12b6e2d" + url: "https://pub.dev" + source: hosted + version: "1.0.0" google_fonts: dependency: "direct main" description: @@ -1314,6 +1394,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + ini: + dependency: transitive + description: + name: ini + sha256: "12a76c53591ffdf86d1265be3f986888a6dfeb34a85957774bc65912d989a173" + url: "https://pub.dev" + source: hosted + version: "2.1.0" integration_test: dependency: "direct dev" description: flutter @@ -1391,6 +1479,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json2yaml: + dependency: transitive + description: + name: json2yaml + sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51 + url: "https://pub.dev" + source: hosted + version: "3.0.1" json_annotation: dependency: "direct main" description: @@ -1579,18 +1675,10 @@ packages: dependency: "direct main" description: name: matrix - sha256: f829dd542f354e5073e3b43aaed3adc2829e762a9ec50a3f186ffd7dddc36d5e + sha256: "36c7e13d5d7420898f2597d6f5f0611a9da8114a0fde11f41b9e54cd1140b05f" url: "https://pub.dev" source: hosted - version: "0.26.1" - matrix_api_lite: - dependency: transitive - description: - name: matrix_api_lite - sha256: e78b333f49733f3ec4c014532ddcb5e9c68639b9b6ff0d89c094a6c284b80b74 - url: "https://pub.dev" - source: hosted - version: "1.7.4" + version: "0.27.0" media_kit: dependency: "direct main" description: @@ -1695,6 +1783,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.3" + native_synchronization: + dependency: transitive + description: + name: native_synchronization + sha256: ff200fe0a64d733ff7d4dde2005271c297db81007604c8cd21037959858133ab + url: "https://pub.dev" + source: hosted + version: "0.2.0" nested: dependency: transitive description: @@ -1975,6 +2071,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + url: "https://pub.dev" + source: hosted + version: "6.0.1" process: dependency: transitive description: @@ -2007,6 +2111,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pubspec_lock: + dependency: transitive + description: + name: pubspec_lock + sha256: ed5fc1ecd0cdc0e14475a091afcb2c4cbb00e74cebff17635e9abbec18d76cc4 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + pubspec_manager: + dependency: transitive + description: + name: pubspec_manager + sha256: b2674161f3f69ed07bc164dc4172afc190d56f060a293a8eb6fa691e5ddf9b5c + url: "https://pub.dev" + source: hosted + version: "1.0.0" pubspec_parse: dependency: transitive description: @@ -2207,6 +2327,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + scope: + dependency: transitive + description: + name: scope + sha256: "80cf1cb727791fdaaa4131817974a6084815ed59b9ab02ef352c3a1badea488b" + url: "https://pub.dev" + source: hosted + version: "4.1.0" screen_retriever: dependency: transitive description: @@ -2223,6 +2351,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.2" + settings_yaml: + dependency: transitive + description: + name: settings_yaml + sha256: abfd2a6a2c48883154ffe11ddd4f72ce43cc2d7f030ae060f7237fb3b2bddbf7 + url: "https://pub.dev" + source: hosted + version: "8.1.0-alpha.2" share_plus: dependency: "direct main" description: @@ -2492,6 +2628,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + strings: + dependency: transitive + description: + name: strings + sha256: b33f40c4dd3e597bf6d9e7f4f4dc282dad0f19b07d9f320cb5c2183859cbccf5 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + sum_types: + dependency: transitive + description: + name: sum_types + sha256: c0a0fad9a518d011987e1d9f27fc336194294e55dafdc3699363e52aa5776e09 + url: "https://pub.dev" + source: hosted + version: "0.3.5" super_clipboard: dependency: transitive description: @@ -2524,6 +2676,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0+1" + system_info2: + dependency: transitive + description: + name: system_info2 + sha256: "65206bbef475217008b5827374767550a5420ce70a04d2d7e94d1d2253f3efc9" + url: "https://pub.dev" + source: hosted + version: "4.0.0" term_glyph: dependency: transitive description: @@ -2708,6 +2868,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.4.0" + validators2: + dependency: transitive + description: + name: validators2 + sha256: "5c63054b2f47b6a3f39e0d0e3f5d38829db4545250144a34c9e1585466de4814" + url: "https://pub.dev" + source: hosted + version: "5.0.0" vector_graphics: dependency: "direct main" description: @@ -2760,10 +2928,10 @@ packages: dependency: transitive description: name: video_player_android - sha256: "821cff3446bbde255e8d03c12fe1f9810c69fee2c26c394545b13d824ba63c2e" + sha256: "134e1ad410d67e18a19486ed9512c72dfc6d8ffb284d0e8f2e99e903d1ba8fa3" url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.14" video_player_avfoundation: dependency: transitive description: @@ -2880,18 +3048,18 @@ packages: dependency: transitive description: name: win32 - sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" + sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.3.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.2" window_manager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d3a2776b7..50cb1be81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2452 +version: 0.9.448+2453 msix_config: display_name: LottiApp @@ -18,7 +18,7 @@ environment: sdk: '>=3.0.1 <4.0.0' dependencies: - arb_utils: ^0.3.1 + arb_utils: ^0.7.1 audio_video_progress_bar: ^2.0.1 auto_size_text: ^3.0.0 beamer: ^1.5.2 @@ -111,7 +111,7 @@ dependencies: lottie: ^3.0.0 material_design_icons_flutter: ^7.0.7096 - matrix: ^0.26.0 + matrix: ^0.27.0 # matrix: # git: # url: https://github.com/matthiasn/matrix-dart-sdk From a40535b4deb1d7496e72e0db906966231c18326c Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Tue, 16 Apr 2024 16:47:43 +0200 Subject: [PATCH 08/14] refactor: add MatrixConfigController --- lib/sync/state/matrix_config_provider.dart | 23 +++ lib/sync/state/matrix_config_provider.g.dart | 27 +++ lib/sync/ui/homeserver_config_page.dart | 197 +++++++++---------- pubspec.yaml | 2 +- 4 files changed, 148 insertions(+), 101 deletions(-) create mode 100644 lib/sync/state/matrix_config_provider.dart create mode 100644 lib/sync/state/matrix_config_provider.g.dart diff --git a/lib/sync/state/matrix_config_provider.dart b/lib/sync/state/matrix_config_provider.dart new file mode 100644 index 000000000..9ed009c67 --- /dev/null +++ b/lib/sync/state/matrix_config_provider.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:lotti/classes/config.dart'; +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'matrix_config_provider.g.dart'; + +@riverpod +class MatrixConfigController extends _$MatrixConfigController { + final _matrixService = getIt(); + + @override + Future build() async { + return _matrixService.loadConfig(); + } + + Future setConfig(MatrixConfig config) => + _matrixService.setConfig(config); + + Future deleteConfig() => _matrixService.deleteConfig(); +} diff --git a/lib/sync/state/matrix_config_provider.g.dart b/lib/sync/state/matrix_config_provider.g.dart new file mode 100644 index 000000000..1a9f86381 --- /dev/null +++ b/lib/sync/state/matrix_config_provider.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matrix_config_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$matrixConfigControllerHash() => + r'cbd923cca2db5ddefa756337770b191874b07f50'; + +/// See also [MatrixConfigController]. +@ProviderFor(MatrixConfigController) +final matrixConfigControllerProvider = AutoDisposeAsyncNotifierProvider< + MatrixConfigController, MatrixConfig?>.internal( + MatrixConfigController.new, + name: r'matrixConfigControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixConfigControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MatrixConfigController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/sync/ui/homeserver_config_page.dart b/lib/sync/ui/homeserver_config_page.dart index e56a59933..26b55d7f0 100644 --- a/lib/sync/ui/homeserver_config_page.dart +++ b/lib/sync/ui/homeserver_config_page.dart @@ -4,8 +4,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:lotti/classes/config.dart'; -import 'package:lotti/get_it.dart'; -import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/sync/state/matrix_config_provider.dart'; import 'package:lotti/sync/state/matrix_login_provider.dart'; import 'package:lotti/themes/theme.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; @@ -105,10 +104,7 @@ class HomeserverSettingsWidget extends ConsumerStatefulWidget { class _HomeserverSettingsWidgetState extends ConsumerState { final _formKey = GlobalKey(); - final _matrixService = getIt(); - bool _dirty = false; - MatrixConfig? _previous; @override void initState() { @@ -118,21 +114,6 @@ class _HomeserverSettingsWidgetState if (isLoggedIn) { widget.pageIndexNotifier.value = 1; } - - _matrixService.loadConfig().then((persisted) { - _previous = persisted; - - if (persisted != null) { - _formKey.currentState?.patchValue({ - matrixHomeServerKey: persisted.homeServer, - matrixUserKey: persisted.user, - matrixPasswordKey: persisted.password, - matrixRoomIdKey: persisted.roomId, - }); - - setState(() => _dirty = false); - } - }); } void onFormChanged() { @@ -151,17 +132,18 @@ class _HomeserverSettingsWidgetState currentState.save(); if (currentState.validate()) { + final previous = ref.read(matrixConfigControllerProvider).value; + final config = MatrixConfig( homeServer: formData[matrixHomeServerKey] as String? ?? - _previous?.homeServer ?? + previous?.homeServer ?? '', - user: formData[matrixUserKey] as String? ?? _previous?.user ?? '', + user: formData[matrixUserKey] as String? ?? previous?.user ?? '', password: - formData[matrixPasswordKey] as String? ?? _previous?.password ?? '', - roomId: formData[matrixRoomIdKey] as String? ?? _previous?.roomId ?? '', + formData[matrixPasswordKey] as String? ?? previous?.password ?? '', ); - await _matrixService.setConfig(config); + await ref.read(matrixConfigControllerProvider.notifier).setConfig(config); setState(() => _dirty = false); } } @@ -171,84 +153,99 @@ class _HomeserverSettingsWidgetState final localizations = AppLocalizations.of(context)!; void maybePop() => Navigator.of(context).maybePop(); - return FormBuilder( - key: _formKey, - autovalidateMode: AutovalidateMode.disabled, - onChanged: onFormChanged, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FormBuilderTextField( - name: matrixHomeServerKey, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixHomeServerLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 20), - FormBuilderTextField( - name: matrixUserKey, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixUserLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 20), - FormBuilderTextField( - name: matrixPasswordKey, - obscureText: true, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixPasswordLabel, - themeData: Theme.of(context), - ), - ), - SizedBox( - height: 80, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (_previous != null) - OutlinedButton( - key: const Key('matrix_config_delete'), - onPressed: () async { - await _matrixService.deleteConfig(); - setState(() { - _dirty = false; - }); - maybePop(); - }, - child: Text( - localizations.settingsMatrixDeleteLabel, - style: TextStyle( - color: Theme.of(context).colorScheme.error, + final config = ref.watch(matrixConfigControllerProvider); + + return config.map( + data: (data) { + final config = data.value; + return FormBuilder( + key: _formKey, + autovalidateMode: AutovalidateMode.disabled, + onChanged: onFormChanged, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FormBuilderTextField( + name: matrixHomeServerKey, + validator: FormBuilderValidators.required(), + initialValue: config?.homeServer, + decoration: inputDecoration( + labelText: localizations.settingsMatrixHomeServerLabel, + themeData: Theme.of(context), + ), + ), + const SizedBox(height: 20), + FormBuilderTextField( + name: matrixUserKey, + validator: FormBuilderValidators.required(), + initialValue: config?.user, + decoration: inputDecoration( + labelText: localizations.settingsMatrixUserLabel, + themeData: Theme.of(context), + ), + ), + const SizedBox(height: 20), + FormBuilderTextField( + name: matrixPasswordKey, + initialValue: config?.password, + obscureText: true, + validator: FormBuilderValidators.required(), + decoration: inputDecoration( + labelText: localizations.settingsMatrixPasswordLabel, + themeData: Theme.of(context), + ), + ), + SizedBox( + height: 80, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (config != null) + OutlinedButton( + key: const Key('matrix_config_delete'), + onPressed: () async { + await ref + .read(matrixConfigControllerProvider.notifier) + .deleteConfig(); + + setState(() { + _dirty = false; + }); + maybePop(); + }, + child: Text( + localizations.settingsMatrixDeleteLabel, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + semanticsLabel: 'Delete Matrix Config', + ), ), - semanticsLabel: 'Delete Matrix Config', - ), - ), - if (_dirty) - OutlinedButton( - key: const Key('matrix_config_save'), - onPressed: () { - onSavePressed(); - maybePop(); - }, - child: Text( - localizations.settingsMatrixSaveLabel, - style: TextStyle( - color: Theme.of(context).colorScheme.error, + if (_dirty) + OutlinedButton( + key: const Key('matrix_config_save'), + onPressed: () { + onSavePressed(); + maybePop(); + }, + child: Text( + localizations.settingsMatrixSaveLabel, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + semanticsLabel: 'Save Matrix Config', + ), ), - semanticsLabel: 'Save Matrix Config', - ), - ), - ], - ), + ], + ), + ), + const SizedBox(height: 80), + ], ), - const SizedBox(height: 80), - ], - ), + ); + }, + error: (_) => const CircularProgressIndicator(), + loading: (_) => const CircularProgressIndicator(), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 50cb1be81..bbe2aca59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2453 +version: 0.9.448+2454 msix_config: display_name: LottiApp From 3e389069eb265642888b6898fe529ee676cc1ad1 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Wed, 17 Apr 2024 00:20:10 +0200 Subject: [PATCH 09/14] feat: unverified devices & stats --- lib/l10n/app_en.arb | 1 + .../state/matrix_unverified_provider.dart | 18 +++++ .../state/matrix_unverified_provider.g.dart | 28 +++++++ lib/sync/ui/matrix_settings_modal.dart | 6 ++ lib/sync/ui/matrix_stats_page.dart | 53 ++++++++++++++ lib/sync/ui/room_config_page.dart | 34 ++++++++- lib/sync/ui/unverified_devices_page.dart | 73 +++++++++++++++++-- pubspec.yaml | 2 +- 8 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 lib/sync/state/matrix_unverified_provider.dart create mode 100644 lib/sync/state/matrix_unverified_provider.g.dart create mode 100644 lib/sync/ui/matrix_stats_page.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 6b4c98f77..1edb76bd7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -204,6 +204,7 @@ "settingsMatrixTitle": "Matrix Sync Settings", "settingsMatrixHomeserverConfigTitle": "Matrix Homeserver Setup", "settingsMatrixRoomConfigTitle": "Matrix Sync Room Setup", + "settingsMatrixStatsTitle": "Matrix Stats", "settingsMatrixCancel": "Cancel", "settingsMatrixDone": "Done", "settingsMatrixNextPage": "Next Page", diff --git a/lib/sync/state/matrix_unverified_provider.dart b/lib/sync/state/matrix_unverified_provider.dart new file mode 100644 index 000000000..2222714b8 --- /dev/null +++ b/lib/sync/state/matrix_unverified_provider.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:matrix/matrix.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'matrix_unverified_provider.g.dart'; + +@riverpod +class MatrixUnverifiedController extends _$MatrixUnverifiedController { + final _matrixService = getIt(); + + @override + Future> build() async { + return _matrixService.getUnverifiedDevices(); + } +} diff --git a/lib/sync/state/matrix_unverified_provider.g.dart b/lib/sync/state/matrix_unverified_provider.g.dart new file mode 100644 index 000000000..d5499275e --- /dev/null +++ b/lib/sync/state/matrix_unverified_provider.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matrix_unverified_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$matrixUnverifiedControllerHash() => + r'6d71be5d70a7de473515d11252b523919b21a94f'; + +/// See also [MatrixUnverifiedController]. +@ProviderFor(MatrixUnverifiedController) +final matrixUnverifiedControllerProvider = AutoDisposeAsyncNotifierProvider< + MatrixUnverifiedController, List>.internal( + MatrixUnverifiedController.new, + name: r'matrixUnverifiedControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixUnverifiedControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MatrixUnverifiedController + = AutoDisposeAsyncNotifier>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/sync/ui/matrix_settings_modal.dart b/lib/sync/ui/matrix_settings_modal.dart index eab46a5cf..d20ca1dd1 100644 --- a/lib/sync/ui/matrix_settings_modal.dart +++ b/lib/sync/ui/matrix_settings_modal.dart @@ -4,6 +4,7 @@ import 'package:lotti/get_it.dart'; import 'package:lotti/sync/matrix/matrix_service.dart'; import 'package:lotti/sync/ui/homeserver_config_page.dart'; import 'package:lotti/sync/ui/matrix_logged_in_config_page.dart'; +import 'package:lotti/sync/ui/matrix_stats_page.dart'; import 'package:lotti/sync/ui/room_config_page.dart'; import 'package:lotti/sync/ui/unverified_devices_page.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; @@ -53,6 +54,11 @@ class MatrixSettingsCard extends StatelessWidget { textTheme: textTheme, pageIndexNotifier: pageIndexNotifier, ), + matrixStatsPage( + context: modalSheetContext, + textTheme: textTheme, + pageIndexNotifier: pageIndexNotifier, + ), ]; }, modalTypeBuilder: (context) { diff --git a/lib/sync/ui/matrix_stats_page.dart b/lib/sync/ui/matrix_stats_page.dart new file mode 100644 index 000000000..de944c624 --- /dev/null +++ b/lib/sync/ui/matrix_stats_page.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:lotti/widgets/sync/matrix/incoming_stats.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; + +SliverWoltModalSheetPage matrixStatsPage({ + required BuildContext context, + required TextTheme textTheme, + required ValueNotifier pageIndexNotifier, +}) { + final localizations = AppLocalizations.of(context)!; + + return WoltModalSheetPage( + stickyActionBar: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + OutlinedButton( + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value - 1, + child: Center( + child: Text(localizations.settingsMatrixPreviousPage), + ), + ), + const SizedBox(height: 8), + FilledButton( + onPressed: () => Navigator.of(context).pop(), + child: Center( + child: Text(localizations.settingsMatrixDone), + ), + ), + ], + ), + ), + topBarTitle: Text( + localizations.settingsMatrixStatsTitle, + style: textTheme.titleMedium, + ), + isTopBarLayerAlwaysVisible: true, + trailingNavBarWidget: IconButton( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding), + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + child: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding) + + const EdgeInsets.only(bottom: 80), + child: const IncomingStats(), + ), + ); +} diff --git a/lib/sync/ui/room_config_page.dart b/lib/sync/ui/room_config_page.dart index a38f25fa8..9917503c3 100644 --- a/lib/sync/ui/room_config_page.dart +++ b/lib/sync/ui/room_config_page.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; @@ -44,9 +47,34 @@ SliverWoltModalSheetPage roomConfigPage({ icon: const Icon(Icons.close), onPressed: Navigator.of(context).pop, ), - child: const SizedBox( - height: 300, - child: Column(), + child: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding) + + const EdgeInsets.only(bottom: 80), + child: const RoomSetup(), ), ); } + +class RoomSetup extends ConsumerWidget { + const RoomSetup({super.key}); + + @override + Widget build( + BuildContext context, + WidgetRef ref, + ) { + final matrixService = getIt(); + + return Column( + children: [ + OutlinedButton( + key: const Key('matrix_create_room'), + onPressed: () async { + await matrixService.createRoom(); + }, + child: const Text('Create room'), + ), + ], + ); + } +} diff --git a/lib/sync/ui/unverified_devices_page.dart b/lib/sync/ui/unverified_devices_page.dart index cce737c13..2facb96e4 100644 --- a/lib/sync/ui/unverified_devices_page.dart +++ b/lib/sync/ui/unverified_devices_page.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lotti/sync/state/matrix_unverified_provider.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:lotti/widgets/sync/imap_config_status.dart'; +import 'package:lotti/widgets/sync/matrix/device_card.dart'; +import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; SliverWoltModalSheetPage unverifiedDevicesPage({ @@ -25,9 +30,10 @@ SliverWoltModalSheetPage unverifiedDevicesPage({ ), const SizedBox(height: 8), FilledButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () => + pageIndexNotifier.value = pageIndexNotifier.value + 1, child: Center( - child: Text(localizations.settingsMatrixDone), + child: Text(localizations.settingsMatrixNextPage), ), ), ], @@ -43,9 +49,66 @@ SliverWoltModalSheetPage unverifiedDevicesPage({ icon: const Icon(Icons.close), onPressed: Navigator.of(context).pop, ), - child: const SizedBox( - height: 300, - child: Column(), + child: Padding( + padding: const EdgeInsets.all(WoltModalConfig.pagePadding) + + const EdgeInsets.only(bottom: 80), + child: const UnverifiedDevices(), ), ); } + +class UnverifiedDevices extends ConsumerWidget { + const UnverifiedDevices({super.key}); + + @override + Widget build( + BuildContext context, + WidgetRef ref, + ) { + final localizations = AppLocalizations.of(context)!; + final unverifiedDevices = + ref.watch(matrixUnverifiedControllerProvider).value ?? []; + + void refreshList() { + ref.invalidate(matrixUnverifiedControllerProvider); + } + + if (unverifiedDevices.isEmpty) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(localizations.settingsMatrixNoUnverifiedLabel), + const StatusIndicator( + Colors.greenAccent, + semanticsLabel: 'No unverified devices', + ), + ], + ); + } + + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + localizations.settingsMatrixListUnverifiedLabel, + semanticsLabel: localizations.settingsMatrixListUnverifiedLabel, + ), + IconButton( + key: const Key('matrix_list_unverified'), + onPressed: refreshList, + icon: Icon(MdiIcons.refresh), + ), + ], + ), + ...unverifiedDevices.map( + (deviceKeys) => DeviceCard( + deviceKeys, + refreshListCallback: refreshList, + ), + ), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index bbe2aca59..2f8010ad7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2454 +version: 0.9.448+2455 msix_config: display_name: LottiApp From eb576fe09fbb4df1b3af3cae735bb5d96c873fde Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Wed, 17 Apr 2024 00:54:03 +0200 Subject: [PATCH 10/14] feat: refresh unverified & riverpod matrix stats --- lib/sync/state/matrix_stats_provider.dart | 27 ++++++++ lib/sync/state/matrix_stats_provider.g.dart | 43 +++++++++++++ .../state/matrix_unverified_provider.dart | 7 +++ .../state/matrix_unverified_provider.g.dart | 2 +- lib/sync/ui/matrix_stats_page.dart | 61 ++++++++++++++++++- pubspec.yaml | 2 +- 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 lib/sync/state/matrix_stats_provider.dart create mode 100644 lib/sync/state/matrix_stats_provider.g.dart diff --git a/lib/sync/state/matrix_stats_provider.dart b/lib/sync/state/matrix_stats_provider.dart new file mode 100644 index 000000000..fea51d93d --- /dev/null +++ b/lib/sync/state/matrix_stats_provider.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/sync/matrix/stats.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'matrix_stats_provider.g.dart'; + +@riverpod +Stream matrixStatsStream(MatrixStatsStreamRef ref) { + return getIt().messageCountsController.stream; +} + +@riverpod +class MatrixStatsController extends _$MatrixStatsController { + final _matrixService = getIt(); + + @override + Future build() async { + return ref.watch(matrixStatsStreamProvider).value ?? + MatrixStats( + sentCount: _matrixService.sentCount, + messageCounts: _matrixService.messageCounts, + ); + } +} diff --git a/lib/sync/state/matrix_stats_provider.g.dart b/lib/sync/state/matrix_stats_provider.g.dart new file mode 100644 index 000000000..e86f92df0 --- /dev/null +++ b/lib/sync/state/matrix_stats_provider.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matrix_stats_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$matrixStatsStreamHash() => r'61129a3672151b81f0f870d8fd93b3668caba669'; + +/// See also [matrixStatsStream]. +@ProviderFor(matrixStatsStream) +final matrixStatsStreamProvider = + AutoDisposeStreamProvider.internal( + matrixStatsStream, + name: r'matrixStatsStreamProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixStatsStreamHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef MatrixStatsStreamRef = AutoDisposeStreamProviderRef; +String _$matrixStatsControllerHash() => + r'da8fe40c5a48b972f2732832d446bb88e67b4cb7'; + +/// See also [MatrixStatsController]. +@ProviderFor(MatrixStatsController) +final matrixStatsControllerProvider = AutoDisposeAsyncNotifierProvider< + MatrixStatsController, MatrixStats>.internal( + MatrixStatsController.new, + name: r'matrixStatsControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixStatsControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MatrixStatsController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/sync/state/matrix_unverified_provider.dart b/lib/sync/state/matrix_unverified_provider.dart index 2222714b8..0bddf8c2b 100644 --- a/lib/sync/state/matrix_unverified_provider.dart +++ b/lib/sync/state/matrix_unverified_provider.dart @@ -9,6 +9,13 @@ part 'matrix_unverified_provider.g.dart'; @riverpod class MatrixUnverifiedController extends _$MatrixUnverifiedController { + MatrixUnverifiedController() { + Timer.periodic(const Duration(milliseconds: 100), (timer) { + build(); + ref.onDispose(timer.cancel); + }); + } + final _matrixService = getIt(); @override diff --git a/lib/sync/state/matrix_unverified_provider.g.dart b/lib/sync/state/matrix_unverified_provider.g.dart index d5499275e..d21fd8ac6 100644 --- a/lib/sync/state/matrix_unverified_provider.g.dart +++ b/lib/sync/state/matrix_unverified_provider.g.dart @@ -7,7 +7,7 @@ part of 'matrix_unverified_provider.dart'; // ************************************************************************** String _$matrixUnverifiedControllerHash() => - r'6d71be5d70a7de473515d11252b523919b21a94f'; + r'00600bf6cbe8a4fcd67784d2ad9e5c2beef51781'; /// See also [MatrixUnverifiedController]. @ProviderFor(MatrixUnverifiedController) diff --git a/lib/sync/ui/matrix_stats_page.dart b/lib/sync/ui/matrix_stats_page.dart index de944c624..32304a0b0 100644 --- a/lib/sync/ui/matrix_stats_page.dart +++ b/lib/sync/ui/matrix_stats_page.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lotti/sync/state/matrix_stats_provider.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; -import 'package:lotti/widgets/sync/matrix/incoming_stats.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; SliverWoltModalSheetPage matrixStatsPage({ @@ -51,3 +52,61 @@ SliverWoltModalSheetPage matrixStatsPage({ ), ); } + +class IncomingStats extends ConsumerWidget { + const IncomingStats({super.key}); + + @override + Widget build( + BuildContext context, + WidgetRef ref, + ) { + final stats = ref.watch(matrixStatsControllerProvider); + + return stats.map( + data: (data) { + final value = data.value; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Sent messages: ${value.sentCount}'), + const SizedBox(height: 10), + DataTable( + columns: const [ + DataColumn( + label: Expanded( + child: Text( + 'Message Type', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ), + ), + DataColumn( + label: Expanded( + child: Text( + 'Count', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ), + ), + ], + rows: [ + ...value.messageCounts.keys.map( + (k) => DataRow( + cells: [ + DataCell(Text(k)), + DataCell(Text(value.messageCounts[k].toString())), + ], + ), + ), + ], + ), + ], + ); + }, + error: (error) => const CircularProgressIndicator(), + loading: (loading) => const CircularProgressIndicator(), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 2f8010ad7..33afd0680 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2455 +version: 0.9.448+2456 msix_config: display_name: LottiApp From 844cb481719cd1471accde35d9ca36032ca6c8e0 Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Wed, 17 Apr 2024 22:46:18 +0200 Subject: [PATCH 11/14] feat: create and leave room --- lib/database/settings_db.dart | 7 +++++ lib/sync/matrix/consts.dart | 1 + lib/sync/matrix/matrix_service.dart | 4 +++ lib/sync/matrix/room.dart | 28 +++++++++++++++++++ lib/sync/state/matrix_room_provider.dart | 27 ++++++++++++++++++ lib/sync/state/matrix_room_provider.g.dart | 27 ++++++++++++++++++ .../state/matrix_unverified_provider.g.dart | 2 +- lib/sync/ui/room_config_page.dart | 28 ++++++++++++------- pubspec.yaml | 2 +- 9 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 lib/sync/state/matrix_room_provider.dart create mode 100644 lib/sync/state/matrix_room_provider.g.dart diff --git a/lib/database/settings_db.dart b/lib/database/settings_db.dart index 93ac1797c..61f1f7155 100644 --- a/lib/database/settings_db.dart +++ b/lib/database/settings_db.dart @@ -34,6 +34,13 @@ class SettingsDb extends _$SettingsDb { return into(settings).insertOnConflictUpdate(settingsItem); } + Future removeSettingsItem(String configKey) async { + final existing = await watchSettingsItemByKey(configKey).first; + if (existing.isNotEmpty) { + await delete(settings).delete(existing.first); + } + } + Stream> watchSettingsItemByKey(String configKey) { return settingsItemByKey(configKey).watch(); } diff --git a/lib/sync/matrix/consts.dart b/lib/sync/matrix/consts.dart index 486e1190e..32b2b5415 100644 --- a/lib/sync/matrix/consts.dart +++ b/lib/sync/matrix/consts.dart @@ -1,4 +1,5 @@ const configNotFound = 'Could not find Matrix Config'; const syncMessageType = 'com.lotti.sync.message'; const String matrixConfigKey = 'MATRIX_CONFIG'; +const String matrixRoomKey = 'MATRIX_ROOM'; const String lastReadMatrixEventId = 'LAST_READ_MATRIX_EVENT_ID'; diff --git a/lib/sync/matrix/matrix_service.dart b/lib/sync/matrix/matrix_service.dart index eb4e8382d..54a1385d7 100644 --- a/lib/sync/matrix/matrix_service.dart +++ b/lib/sync/matrix/matrix_service.dart @@ -130,6 +130,10 @@ class MatrixService { invite: invite, ); + Future getRoom() => getMatrixRoom(client: _client); + + Future leaveRoom() => leaveMatrixRoom(client: _client); + Future inviteToSyncRoom({ required String userId, }) => diff --git a/lib/sync/matrix/room.dart b/lib/sync/matrix/room.dart index cebc8e6b1..3599b4b32 100644 --- a/lib/sync/matrix/room.dart +++ b/lib/sync/matrix/room.dart @@ -1,7 +1,9 @@ import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import 'package:lotti/database/logging_db.dart'; +import 'package:lotti/database/settings_db.dart'; import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/consts.dart'; import 'package:lotti/sync/matrix/matrix_service.dart'; import 'package:matrix/matrix.dart'; @@ -59,9 +61,35 @@ Future createMatrixRoom({ ); final room = client.getRoomById(roomId); await room?.enableEncryption(); + + await saveMatrixRoom(client: client, roomId: roomId); return roomId; } +Future leaveMatrixRoom({ + required Client client, +}) async { + final roomId = await getMatrixRoom(client: client); + + if (roomId != null) { + await client.leaveRoom(roomId); + await getIt().removeSettingsItem(matrixRoomKey); + } +} + +Future saveMatrixRoom({ + required Client client, + required String roomId, +}) async { + await getIt().saveSettingsItem( + matrixRoomKey, + roomId, + ); +} + +Future getMatrixRoom({required Client client}) => + getIt().itemByKey(matrixRoomKey); + Future inviteToMatrixRoom({ required MatrixService service, required String userId, diff --git a/lib/sync/state/matrix_room_provider.dart b/lib/sync/state/matrix_room_provider.dart new file mode 100644 index 000000000..6a1d1e103 --- /dev/null +++ b/lib/sync/state/matrix_room_provider.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:lotti/get_it.dart'; +import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'matrix_room_provider.g.dart'; + +@riverpod +class MatrixRoomController extends _$MatrixRoomController { + final _matrixService = getIt(); + + @override + Future build() async { + return _matrixService.getRoom(); + } + + Future createRoom() async { + await _matrixService.createRoom(); + ref.invalidateSelf(); + } + + Future leaveRoom() async { + await _matrixService.leaveRoom(); + ref.invalidateSelf(); + } +} diff --git a/lib/sync/state/matrix_room_provider.g.dart b/lib/sync/state/matrix_room_provider.g.dart new file mode 100644 index 000000000..95a49cd55 --- /dev/null +++ b/lib/sync/state/matrix_room_provider.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matrix_room_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$matrixRoomControllerHash() => + r'6d3d0ff9f4a356b32b19fb5aabc1368c8dd47fbe'; + +/// See also [MatrixRoomController]. +@ProviderFor(MatrixRoomController) +final matrixRoomControllerProvider = + AutoDisposeAsyncNotifierProvider.internal( + MatrixRoomController.new, + name: r'matrixRoomControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$matrixRoomControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MatrixRoomController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/sync/state/matrix_unverified_provider.g.dart b/lib/sync/state/matrix_unverified_provider.g.dart index d21fd8ac6..7714c3c5c 100644 --- a/lib/sync/state/matrix_unverified_provider.g.dart +++ b/lib/sync/state/matrix_unverified_provider.g.dart @@ -7,7 +7,7 @@ part of 'matrix_unverified_provider.dart'; // ************************************************************************** String _$matrixUnverifiedControllerHash() => - r'00600bf6cbe8a4fcd67784d2ad9e5c2beef51781'; + r'94a4f7b479c7716c74343211d9e392e510ecde80'; /// See also [MatrixUnverifiedController]. @ProviderFor(MatrixUnverifiedController) diff --git a/lib/sync/ui/room_config_page.dart b/lib/sync/ui/room_config_page.dart index 9917503c3..d183992fc 100644 --- a/lib/sync/ui/room_config_page.dart +++ b/lib/sync/ui/room_config_page.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:lotti/get_it.dart'; -import 'package:lotti/sync/matrix/matrix_service.dart'; +import 'package:lotti/sync/state/matrix_room_provider.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; @@ -63,17 +62,26 @@ class RoomSetup extends ConsumerWidget { BuildContext context, WidgetRef ref, ) { - final matrixService = getIt(); + final room = ref.watch(matrixRoomControllerProvider).value; + final roomNotifier = ref.read(matrixRoomControllerProvider.notifier); + final isRoomDefined = room != null; return Column( children: [ - OutlinedButton( - key: const Key('matrix_create_room'), - onPressed: () async { - await matrixService.createRoom(); - }, - child: const Text('Create room'), - ), + if (isRoomDefined) Text(room), + const SizedBox(height: 20), + if (isRoomDefined) + OutlinedButton( + key: const Key('matrix_leave_room'), + onPressed: roomNotifier.leaveRoom, + child: const Text('Leave room'), + ) + else + OutlinedButton( + key: const Key('matrix_create_room'), + onPressed: roomNotifier.createRoom, + child: const Text('Create room'), + ), ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index 33afd0680..4bdcc5f9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2456 +version: 0.9.448+2457 msix_config: display_name: LottiApp From 9ddad34d8c5100d1ebef0a442eca4443e4ed95db Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Thu, 18 Apr 2024 00:52:57 +0200 Subject: [PATCH 12/14] feat: room invites --- lib/classes/config.dart | 1 - lib/classes/config.freezed.dart | 35 ++----- lib/classes/config.g.dart | 2 - lib/sync/matrix/client.dart | 2 +- lib/sync/matrix/matrix_service.dart | 7 +- lib/sync/matrix/room.dart | 14 ++- lib/sync/matrix/send_message.dart | 3 +- lib/sync/state/matrix_room_provider.dart | 9 ++ lib/sync/state/matrix_room_provider.g.dart | 2 +- lib/sync/ui/room_config_page.dart | 101 +++++++++++++++++-- lib/widgets/sync/matrix/matrix_settings.dart | 2 - 11 files changed, 126 insertions(+), 52 deletions(-) diff --git a/lib/classes/config.dart b/lib/classes/config.dart index b49129fae..6ef6330f9 100644 --- a/lib/classes/config.dart +++ b/lib/classes/config.dart @@ -23,7 +23,6 @@ class MatrixConfig with _$MatrixConfig { required String homeServer, required String user, required String password, - String? roomId, }) = _MatrixConfig; factory MatrixConfig.fromJson(Map json) => diff --git a/lib/classes/config.freezed.dart b/lib/classes/config.freezed.dart index 9d2579409..2c4ce5739 100644 --- a/lib/classes/config.freezed.dart +++ b/lib/classes/config.freezed.dart @@ -238,7 +238,6 @@ mixin _$MatrixConfig { String get homeServer => throw _privateConstructorUsedError; String get user => throw _privateConstructorUsedError; String get password => throw _privateConstructorUsedError; - String? get roomId => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -252,7 +251,7 @@ abstract class $MatrixConfigCopyWith<$Res> { MatrixConfig value, $Res Function(MatrixConfig) then) = _$MatrixConfigCopyWithImpl<$Res, MatrixConfig>; @useResult - $Res call({String homeServer, String user, String password, String? roomId}); + $Res call({String homeServer, String user, String password}); } /// @nodoc @@ -271,7 +270,6 @@ class _$MatrixConfigCopyWithImpl<$Res, $Val extends MatrixConfig> Object? homeServer = null, Object? user = null, Object? password = null, - Object? roomId = freezed, }) { return _then(_value.copyWith( homeServer: null == homeServer @@ -286,10 +284,6 @@ class _$MatrixConfigCopyWithImpl<$Res, $Val extends MatrixConfig> ? _value.password : password // ignore: cast_nullable_to_non_nullable as String, - roomId: freezed == roomId - ? _value.roomId - : roomId // ignore: cast_nullable_to_non_nullable - as String?, ) as $Val); } } @@ -302,7 +296,7 @@ abstract class _$$MatrixConfigImplCopyWith<$Res> __$$MatrixConfigImplCopyWithImpl<$Res>; @override @useResult - $Res call({String homeServer, String user, String password, String? roomId}); + $Res call({String homeServer, String user, String password}); } /// @nodoc @@ -319,7 +313,6 @@ class __$$MatrixConfigImplCopyWithImpl<$Res> Object? homeServer = null, Object? user = null, Object? password = null, - Object? roomId = freezed, }) { return _then(_$MatrixConfigImpl( homeServer: null == homeServer @@ -334,10 +327,6 @@ class __$$MatrixConfigImplCopyWithImpl<$Res> ? _value.password : password // ignore: cast_nullable_to_non_nullable as String, - roomId: freezed == roomId - ? _value.roomId - : roomId // ignore: cast_nullable_to_non_nullable - as String?, )); } } @@ -346,10 +335,7 @@ class __$$MatrixConfigImplCopyWithImpl<$Res> @JsonSerializable() class _$MatrixConfigImpl implements _MatrixConfig { const _$MatrixConfigImpl( - {required this.homeServer, - required this.user, - required this.password, - this.roomId}); + {required this.homeServer, required this.user, required this.password}); factory _$MatrixConfigImpl.fromJson(Map json) => _$$MatrixConfigImplFromJson(json); @@ -360,12 +346,10 @@ class _$MatrixConfigImpl implements _MatrixConfig { final String user; @override final String password; - @override - final String? roomId; @override String toString() { - return 'MatrixConfig(homeServer: $homeServer, user: $user, password: $password, roomId: $roomId)'; + return 'MatrixConfig(homeServer: $homeServer, user: $user, password: $password)'; } @override @@ -377,14 +361,12 @@ class _$MatrixConfigImpl implements _MatrixConfig { other.homeServer == homeServer) && (identical(other.user, user) || other.user == user) && (identical(other.password, password) || - other.password == password) && - (identical(other.roomId, roomId) || other.roomId == roomId)); + other.password == password)); } @JsonKey(ignore: true) @override - int get hashCode => - Object.hash(runtimeType, homeServer, user, password, roomId); + int get hashCode => Object.hash(runtimeType, homeServer, user, password); @JsonKey(ignore: true) @override @@ -404,8 +386,7 @@ abstract class _MatrixConfig implements MatrixConfig { const factory _MatrixConfig( {required final String homeServer, required final String user, - required final String password, - final String? roomId}) = _$MatrixConfigImpl; + required final String password}) = _$MatrixConfigImpl; factory _MatrixConfig.fromJson(Map json) = _$MatrixConfigImpl.fromJson; @@ -417,8 +398,6 @@ abstract class _MatrixConfig implements MatrixConfig { @override String get password; @override - String? get roomId; - @override @JsonKey(ignore: true) _$$MatrixConfigImplCopyWith<_$MatrixConfigImpl> get copyWith => throw _privateConstructorUsedError; diff --git a/lib/classes/config.g.dart b/lib/classes/config.g.dart index 7c339043c..732540c5f 100644 --- a/lib/classes/config.g.dart +++ b/lib/classes/config.g.dart @@ -29,7 +29,6 @@ _$MatrixConfigImpl _$$MatrixConfigImplFromJson(Map json) => homeServer: json['homeServer'] as String, user: json['user'] as String, password: json['password'] as String, - roomId: json['roomId'] as String?, ); Map _$$MatrixConfigImplToJson(_$MatrixConfigImpl instance) => @@ -37,7 +36,6 @@ Map _$$MatrixConfigImplToJson(_$MatrixConfigImpl instance) => 'homeServer': instance.homeServer, 'user': instance.user, 'password': instance.password, - 'roomId': instance.roomId, }; _$SyncConfigImpl _$$SyncConfigImplFromJson(Map json) => diff --git a/lib/sync/matrix/client.dart b/lib/sync/matrix/client.dart index e3def8b4d..eaf84b66e 100644 --- a/lib/sync/matrix/client.dart +++ b/lib/sync/matrix/client.dart @@ -109,7 +109,7 @@ Future matrixConnect({ ); } - final roomId = matrixConfig.roomId; + final roomId = await service.getRoom(); if (roomId != null) { await service.joinRoom(roomId); diff --git a/lib/sync/matrix/matrix_service.dart b/lib/sync/matrix/matrix_service.dart index 54a1385d7..e09a4d369 100644 --- a/lib/sync/matrix/matrix_service.dart +++ b/lib/sync/matrix/matrix_service.dart @@ -112,6 +112,11 @@ class MatrixService { Future joinRoom(String roomId) => joinMatrixRoom(roomId: roomId, service: this); + Future saveRoom(String roomId) => saveMatrixRoom( + roomId: roomId, + client: client, + ); + bool isLoggedIn() { // TODO(unassigned): find non-deprecated solution // ignore: deprecated_member_use @@ -126,7 +131,7 @@ class MatrixService { List? invite, }) => createMatrixRoom( - client: _client, + service: this, invite: invite, ); diff --git a/lib/sync/matrix/room.dart b/lib/sync/matrix/room.dart index 3599b4b32..a7191e026 100644 --- a/lib/sync/matrix/room.dart +++ b/lib/sync/matrix/room.dart @@ -33,7 +33,7 @@ Future joinMatrixRoom({ service ..syncRoom = syncRoom - ..syncRoomId = joinRes; + ..syncRoomId = roomId; return joinRes; } catch (e, stackTrace) { @@ -49,10 +49,12 @@ Future joinMatrixRoom({ } Future createMatrixRoom({ - required Client client, + required MatrixService service, List? invite, }) async { final name = DateFormat('yyyy-MM-dd_HH-mm-ss').format(DateTime.now()); + final client = service.client; + final roomId = await client.createRoom( visibility: Visibility.private, name: name, @@ -63,6 +65,8 @@ Future createMatrixRoom({ await room?.enableEncryption(); await saveMatrixRoom(client: client, roomId: roomId); + await joinMatrixRoom(roomId: roomId, service: service); + return roomId; } @@ -72,8 +76,12 @@ Future leaveMatrixRoom({ final roomId = await getMatrixRoom(client: client); if (roomId != null) { - await client.leaveRoom(roomId); await getIt().removeSettingsItem(matrixRoomKey); + try { + await client.leaveRoom(roomId); + } catch (e) { + debugPrint(e.toString()); + } } } diff --git a/lib/sync/matrix/send_message.dart b/lib/sync/matrix/send_message.dart index 4873ba542..1b1d68fe4 100644 --- a/lib/sync/matrix/send_message.dart +++ b/lib/sync/matrix/send_message.dart @@ -31,8 +31,7 @@ Future sendMessage( try { final msg = json.encode(syncMessage); final syncRoom = service.syncRoom; - final roomId = - myRoomId ?? service.syncRoomId ?? service.matrixConfig?.roomId; + final roomId = myRoomId ?? service.syncRoomId; void incrementSentCount() { service.sentCount = service.sentCount + 1; diff --git a/lib/sync/state/matrix_room_provider.dart b/lib/sync/state/matrix_room_provider.dart index 6a1d1e103..dd6556b9c 100644 --- a/lib/sync/state/matrix_room_provider.dart +++ b/lib/sync/state/matrix_room_provider.dart @@ -20,6 +20,15 @@ class MatrixRoomController extends _$MatrixRoomController { ref.invalidateSelf(); } + Future inviteToRoom(String userId) async { + await _matrixService.inviteToSyncRoom(userId: userId); + } + + Future joinRoom(String roomId) async { + await _matrixService.saveRoom(roomId); + await _matrixService.joinRoom(roomId); + } + Future leaveRoom() async { await _matrixService.leaveRoom(); ref.invalidateSelf(); diff --git a/lib/sync/state/matrix_room_provider.g.dart b/lib/sync/state/matrix_room_provider.g.dart index 95a49cd55..1b0f61236 100644 --- a/lib/sync/state/matrix_room_provider.g.dart +++ b/lib/sync/state/matrix_room_provider.g.dart @@ -7,7 +7,7 @@ part of 'matrix_room_provider.dart'; // ************************************************************************** String _$matrixRoomControllerHash() => - r'6d3d0ff9f4a356b32b19fb5aabc1368c8dd47fbe'; + r'00e6f5b3ff54ad1b4ec489de25aff0959bf8da7c'; /// See also [MatrixRoomController]. @ProviderFor(MatrixRoomController) diff --git a/lib/sync/ui/room_config_page.dart b/lib/sync/ui/room_config_page.dart index d183992fc..be473f17c 100644 --- a/lib/sync/ui/room_config_page.dart +++ b/lib/sync/ui/room_config_page.dart @@ -1,8 +1,13 @@ +import 'dart:async'; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lotti/sync/state/matrix_room_provider.dart'; +import 'package:lotti/utils/platform.dart'; import 'package:lotti/widgets/misc/wolt_modal_config.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; SliverWoltModalSheetPage roomConfigPage({ @@ -49,39 +54,113 @@ SliverWoltModalSheetPage roomConfigPage({ child: Padding( padding: const EdgeInsets.all(WoltModalConfig.pagePadding) + const EdgeInsets.only(bottom: 80), - child: const RoomSetup(), + child: const RoomConfig(), ), ); } -class RoomSetup extends ConsumerWidget { - const RoomSetup({super.key}); +class RoomConfig extends ConsumerStatefulWidget { + const RoomConfig({super.key}); + + @override + ConsumerState createState() => _RoomConfigState(); +} + +class _RoomConfigState extends ConsumerState { + final _qrKey = GlobalKey(debugLabel: 'matrix_QR_key'); + QRViewController? controller; + bool showCam = false; + final joinRoomController = TextEditingController(); + String manualRoomId = ''; @override - Widget build( - BuildContext context, - WidgetRef ref, - ) { + Widget build(BuildContext context) { final room = ref.watch(matrixRoomControllerProvider).value; final roomNotifier = ref.read(matrixRoomControllerProvider.notifier); final isRoomDefined = room != null; + final camDimension = + max(MediaQuery.of(context).size.width - 100, 300).toDouble(); + + void onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) async { + final userId = scanData.code; + + debugPrint('scanned: $userId'); + if (userId != null) { + await roomNotifier.inviteToRoom(userId); + setState(() { + showCam = false; + }); + } + }); + } + + Future invitePressed() async { + setState(() { + showCam = true; + }); + } + + Future joinRoom() async { + await roomNotifier.joinRoom(manualRoomId); + } + return Column( children: [ - if (isRoomDefined) Text(room), + if (isRoomDefined) SelectableText(room), const SizedBox(height: 20), - if (isRoomDefined) + if (isRoomDefined) ...[ + OutlinedButton( + key: const Key('matrix_invite_to_room'), + onPressed: invitePressed, + child: const Text('Invite'), + ), + const SizedBox(height: 20), OutlinedButton( key: const Key('matrix_leave_room'), onPressed: roomNotifier.leaveRoom, child: const Text('Leave room'), - ) - else + ), + const SizedBox(height: 20), + if (showCam && isMobile) + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + height: camDimension, + width: camDimension, + child: QRView( + key: _qrKey, + onQRViewCreated: onQRViewCreated, + ), + ), + ), + ] else ...[ + TextField( + controller: joinRoomController, + onChanged: (s) { + setState(() { + manualRoomId = s; + }); + }, + ), + if (manualRoomId.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 20), + child: OutlinedButton( + key: const Key('matrix_join_room'), + onPressed: joinRoom, + child: const Text('Join room'), + ), + ), + const SizedBox(height: 20), OutlinedButton( key: const Key('matrix_create_room'), onPressed: roomNotifier.createRoom, child: const Text('Create room'), ), + ], ], ); } diff --git a/lib/widgets/sync/matrix/matrix_settings.dart b/lib/widgets/sync/matrix/matrix_settings.dart index f82b5d2f3..c75430ad3 100644 --- a/lib/widgets/sync/matrix/matrix_settings.dart +++ b/lib/widgets/sync/matrix/matrix_settings.dart @@ -49,7 +49,6 @@ class _MatrixSettingsWidgetState extends ConsumerState { matrixHomeServerKey: persisted.homeServer, matrixUserKey: persisted.user, matrixPasswordKey: persisted.password, - matrixRoomIdKey: persisted.roomId, }); setState(() => _dirty = false); @@ -80,7 +79,6 @@ class _MatrixSettingsWidgetState extends ConsumerState { user: formData[matrixUserKey] as String? ?? _previous?.user ?? '', password: formData[matrixPasswordKey] as String? ?? _previous?.password ?? '', - roomId: formData[matrixRoomIdKey] as String? ?? _previous?.roomId ?? '', ); await _matrixService.setConfig(config); From b52acffb09baa4f4e3a24a5cced91e241cd39d7d Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Thu, 18 Apr 2024 01:21:15 +0200 Subject: [PATCH 13/14] refactor: remove previous config page --- lib/beamer/locations/settings_location.dart | 7 - .../settings/sync/matrix_settings_page.dart | 27 -- lib/widgets/sync/matrix/matrix_settings.dart | 274 ------------------ pubspec.yaml | 2 +- 4 files changed, 1 insertion(+), 309 deletions(-) delete mode 100644 lib/pages/settings/sync/matrix_settings_page.dart delete mode 100644 lib/widgets/sync/matrix/matrix_settings.dart diff --git a/lib/beamer/locations/settings_location.dart b/lib/beamer/locations/settings_location.dart index 485639a7d..d60d47785 100644 --- a/lib/beamer/locations/settings_location.dart +++ b/lib/beamer/locations/settings_location.dart @@ -23,7 +23,6 @@ import 'package:lotti/pages/settings/measurables/measurables_page.dart'; import 'package:lotti/pages/settings/outbox/outbox_monitor.dart'; import 'package:lotti/pages/settings/settings_page.dart'; import 'package:lotti/pages/settings/speech/speech_settings_page.dart'; -import 'package:lotti/pages/settings/sync/matrix_settings_page.dart'; import 'package:lotti/pages/settings/sync/sync_assistant_page.dart'; import 'package:lotti/pages/settings/tags/create_tag_page.dart'; import 'package:lotti/pages/settings/tags/tag_edit_page.dart'; @@ -252,12 +251,6 @@ class SettingsLocation extends BeamLocation { child: SyncAssistantPage(), ), - if (pathContains('advanced/matrix_settings')) - const BeamPage( - key: ValueKey('settings-matrix_settings'), - child: MatrixSettingsPage(), - ), - if (pathContains('advanced/outbox_monitor')) const BeamPage( key: ValueKey('settings-outbox_monitor'), diff --git a/lib/pages/settings/sync/matrix_settings_page.dart b/lib/pages/settings/sync/matrix_settings_page.dart deleted file mode 100644 index bf768b069..000000000 --- a/lib/pages/settings/sync/matrix_settings_page.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:lotti/pages/settings/sliver_box_adapter_page.dart'; -import 'package:lotti/widgets/sync/matrix/matrix_settings.dart'; - -class MatrixSettingsPage extends StatelessWidget { - const MatrixSettingsPage({super.key}); - - @override - Widget build(BuildContext context) { - final localizations = AppLocalizations.of(context)!; - - return Scaffold( - body: SliverBoxAdapterPage( - title: localizations.settingsMatrixTitle, - showBackButton: true, - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 64, - horizontal: 32, - ), - child: MatrixSettingsWidget(), - ), - ), - ); - } -} diff --git a/lib/widgets/sync/matrix/matrix_settings.dart b/lib/widgets/sync/matrix/matrix_settings.dart deleted file mode 100644 index c75430ad3..000000000 --- a/lib/widgets/sync/matrix/matrix_settings.dart +++ /dev/null @@ -1,274 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:lotti/classes/config.dart'; -import 'package:lotti/get_it.dart'; -import 'package:lotti/sync/matrix/matrix_service.dart'; -import 'package:lotti/themes/theme.dart'; -import 'package:lotti/utils/platform.dart'; -import 'package:lotti/widgets/sync/matrix/incoming_stats.dart'; -import 'package:lotti/widgets/sync/matrix/unverified_devices.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; -import 'package:qr_flutter/qr_flutter.dart'; - -const matrixUserKey = 'user'; -const matrixPasswordKey = 'password'; -const matrixHomeServerKey = 'home_server'; -const matrixRoomIdKey = 'room_id'; - -class MatrixSettingsWidget extends ConsumerStatefulWidget { - const MatrixSettingsWidget({super.key}); - - @override - ConsumerState createState() => - _MatrixSettingsWidgetState(); -} - -class _MatrixSettingsWidgetState extends ConsumerState { - final _matrixService = getIt(); - final _formKey = GlobalKey(); - final _qrKey = GlobalKey(debugLabel: 'matrix_QR_key'); - - bool _dirty = false; - MatrixConfig? _previous; - QRViewController? controller; - - @override - void initState() { - super.initState(); - _matrixService.loadConfig().then((persisted) { - _previous = persisted; - - if (persisted != null) { - _formKey.currentState?.patchValue({ - matrixHomeServerKey: persisted.homeServer, - matrixUserKey: persisted.user, - matrixPasswordKey: persisted.password, - }); - - setState(() => _dirty = false); - } - }); - } - - void onFormChanged() { - _formKey.currentState?.save(); - setState(() => _dirty = true); - } - - Future onSavePressed() async { - final currentState = _formKey.currentState; - - if (currentState == null) { - return; - } - - final formData = currentState.value; - currentState.save(); - - if (currentState.validate()) { - final config = MatrixConfig( - homeServer: formData[matrixHomeServerKey] as String? ?? - _previous?.homeServer ?? - '', - user: formData[matrixUserKey] as String? ?? _previous?.user ?? '', - password: - formData[matrixPasswordKey] as String? ?? _previous?.password ?? '', - ); - - await _matrixService.setConfig(config); - setState(() => _dirty = false); - } - } - - @override - Widget build(BuildContext context) { - final localizations = AppLocalizations.of(context)!; - void maybePop() => Navigator.of(context).maybePop(); - - final camDimension = - max(MediaQuery.of(context).size.width - 100, 300).toDouble(); - - void onQRViewCreated(QRViewController controller) { - this.controller = controller; - controller.scannedDataStream.listen((scanData) async { - final jsonString = scanData.code; - if (jsonString != null) { - final parsed = json.decode(jsonString) as Map; - final scannedConfig = MatrixConfig.fromJson(parsed); - await _matrixService.setConfig(scannedConfig); - maybePop(); - } - }); - } - - return SingleChildScrollView( - child: FormBuilder( - key: _formKey, - autovalidateMode: AutovalidateMode.disabled, - onChanged: onFormChanged, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FormBuilderTextField( - name: matrixHomeServerKey, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixHomeServerLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 20), - FormBuilderTextField( - name: matrixUserKey, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixUserLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 20), - FormBuilderTextField( - name: matrixPasswordKey, - obscureText: true, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixPasswordLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 20), - FormBuilderTextField( - name: matrixRoomIdKey, - validator: FormBuilderValidators.required(), - decoration: inputDecoration( - labelText: localizations.settingsMatrixRoomIdLabel, - themeData: Theme.of(context), - ), - ), - const SizedBox(height: 40), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (_previous != null) - OutlinedButton( - key: const Key('matrix_config_delete'), - onPressed: () async { - await _matrixService.logout(); - await _matrixService.deleteConfig(); - setState(() { - _dirty = false; - }); - maybePop(); - }, - child: Text( - localizations.settingsMatrixDeleteLabel, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - semanticsLabel: 'Delete Matrix Config', - ), - ), - if (_dirty) - OutlinedButton( - key: const Key('matrix_config_save'), - onPressed: () { - onSavePressed(); - maybePop(); - }, - child: Text( - localizations.settingsMatrixSaveLabel, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - semanticsLabel: 'Save Matrix Config', - ), - ), - ], - ), - const SizedBox(height: 40), - if (_previous != null && !_dirty) - Center( - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(8), - child: QrImageView( - data: jsonEncode(_previous), - padding: EdgeInsets.zero, - size: 280, - key: const Key('QrImage'), - ), - ), - ), - ), - const SizedBox(height: 40), - Text('Device ID: ${_matrixService.deviceId}'), - Text('Device Name: ${_matrixService.deviceName}'), - const SizedBox(height: 40), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (_matrixService.isLoggedIn()) - OutlinedButton( - key: const Key('matrix_logout'), - onPressed: () { - _matrixService.logout(); - maybePop(); - }, - child: Text( - localizations.settingsMatrixLogoutButtonLabel, - semanticsLabel: - localizations.settingsMatrixLogoutButtonLabel, - ), - ) - else - OutlinedButton( - key: const Key('matrix_login'), - onPressed: () { - _matrixService.loginAndListen(); - maybePop(); - }, - child: Text( - localizations.settingsMatrixLoginButtonLabel, - semanticsLabel: - localizations.settingsMatrixLoginButtonLabel, - ), - ), - ], - ), - const SizedBox(height: 40), - if (isMobile && _previous == null && !_dirty) - ClipRRect( - borderRadius: BorderRadius.circular(8), - child: SizedBox( - height: camDimension, - width: camDimension, - child: QRView( - key: _qrKey, - onQRViewCreated: onQRViewCreated, - ), - ), - ), - if (_matrixService.isLoggedIn()) const UnverifiedDevices(), - const SizedBox(height: 40), - const IncomingStats(), - OutlinedButton( - key: const Key('matrix_create_room'), - onPressed: () async { - await _matrixService.createRoom(); - }, - child: const Text('Create room'), - ), - ], - ), - ), - ); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 4bdcc5f9e..c6756b705 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2457 +version: 0.9.448+2458 msix_config: display_name: LottiApp From 9dd12f1cbf5083665a8b98ecde9fe673f7d11ace Mon Sep 17 00:00:00 2001 From: Matthias Nehlsen Date: Thu, 18 Apr 2024 01:58:22 +0200 Subject: [PATCH 14/14] feat: handle event not found --- .gitignore | 1 + lib/pages/settings/advanced_settings_page.dart | 4 ---- lib/sync/matrix/room.dart | 6 ++++++ lib/sync/matrix/timeline.dart | 9 +++++++-- pubspec.lock | 12 ++++++++++-- pubspec.yaml | 10 ++-------- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index ba66f92da..0592e3e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ whisper.cpp/whisper.cpp-*/ integration_test/docker/config/jetstream/ integration_test/docker/config/logs/ +integration_test/docker/config/media_store/ diff --git a/lib/pages/settings/advanced_settings_page.dart b/lib/pages/settings/advanced_settings_page.dart index 03ba7595a..bdc88e282 100644 --- a/lib/pages/settings/advanced_settings_page.dart +++ b/lib/pages/settings/advanced_settings_page.dart @@ -28,10 +28,6 @@ class AdvancedSettingsPage extends StatelessWidget { title: localizations.settingsSyncCfgTitle, path: '/settings/advanced/sync_settings', ), - SettingsNavCard( - title: localizations.settingsMatrixTitle, - path: '/settings/advanced/matrix_settings', - ), const MatrixSettingsCard(), SettingsNavCard( trailing: OutboxBadgeIcon( diff --git a/lib/sync/matrix/room.dart b/lib/sync/matrix/room.dart index a7191e026..eacdba7bd 100644 --- a/lib/sync/matrix/room.dart +++ b/lib/sync/matrix/room.dart @@ -35,6 +35,12 @@ Future joinMatrixRoom({ ..syncRoom = syncRoom ..syncRoomId = roomId; + loggingDb.captureEvent( + 'joined $roomId $joinRes', + domain: 'MATRIX_SERVICE', + subDomain: 'joinRoom', + ); + return joinRes; } catch (e, stackTrace) { debugPrint('$e'); diff --git a/lib/sync/matrix/timeline.dart b/lib/sync/matrix/timeline.dart index c6be8b357..cf4e696a3 100644 --- a/lib/sync/matrix/timeline.dart +++ b/lib/sync/matrix/timeline.dart @@ -61,8 +61,13 @@ Future processNewTimelineEvents({ try { final lastReadEventContextId = service.lastReadEventContextId; + await service.client.sync(); + final hasMessage = await service.syncRoom + ?.getEventById(lastReadEventContextId.toString()) != + null; + final timeline = await service.syncRoom?.getTimeline( - eventContextId: lastReadEventContextId, + eventContextId: hasMessage ? lastReadEventContextId : null, ); if (timeline == null) { @@ -102,10 +107,10 @@ Future processNewTimelineEvents({ try { if (eventId.startsWith(r'$')) { service.lastReadEventContextId = eventId; + await setLastReadMatrixEventId(eventId); } await timeline.setReadMarker(eventId: eventId); - await setLastReadMatrixEventId(eventId); } catch (e) { debugPrint('$e'); } diff --git a/pubspec.lock b/pubspec.lock index db881f667..0f2d665de 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1675,10 +1675,18 @@ packages: dependency: "direct main" description: name: matrix - sha256: "36c7e13d5d7420898f2597d6f5f0611a9da8114a0fde11f41b9e54cd1140b05f" + sha256: f829dd542f354e5073e3b43aaed3adc2829e762a9ec50a3f186ffd7dddc36d5e url: "https://pub.dev" source: hosted - version: "0.27.0" + version: "0.26.1" + matrix_api_lite: + dependency: transitive + description: + name: matrix_api_lite + sha256: e78b333f49733f3ec4c014532ddcb5e9c68639b9b6ff0d89c094a6c284b80b74 + url: "https://pub.dev" + source: hosted + version: "1.7.4" media_kit: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c6756b705..6c469c82b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lotti description: Achieve your goals and keep your data private with Lotti. publish_to: 'none' -version: 0.9.448+2458 +version: 0.9.448+2459 msix_config: display_name: LottiApp @@ -110,13 +110,7 @@ dependencies: location: ^6.0.1 lottie: ^3.0.0 material_design_icons_flutter: ^7.0.7096 - - matrix: ^0.27.0 -# matrix: -# git: -# url: https://github.com/matthiasn/matrix-dart-sdk -# ref: 1816235c - + matrix: ^0.26.1 media_kit: ^1.0.2 media_kit_libs_android_audio: ^1.0.3 media_kit_libs_ios_audio: ^1.0.4