diff --git a/lib/components/adaptive_page_layout.dart b/lib/components/adaptive_page_layout.dart deleted file mode 100644 index 1351a68a..00000000 --- a/lib/components/adaptive_page_layout.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; - -enum FocusPage { FIRST, SECOND } - -class AdaptivePageLayout extends StatelessWidget { - final Widget firstScaffold; - final Widget secondScaffold; - final FocusPage primaryPage; - final double minWidth; - - static const double defaultMinWidth = 400; - static bool columnMode(BuildContext context) => - MediaQuery.of(context).size.width > 2 * defaultMinWidth; - - AdaptivePageLayout( - {this.firstScaffold, - this.secondScaffold, - this.primaryPage = FocusPage.FIRST, - this.minWidth = defaultMinWidth, - Key key}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return OrientationBuilder(builder: (context, orientation) { - if (orientation == Orientation.portrait || !columnMode(context)) { - if (primaryPage == FocusPage.FIRST) { - return firstScaffold; - } else { - return secondScaffold; - } - } - return Row( - children: [ - Container( - width: minWidth, - child: firstScaffold, - ), - Container( - width: 1, - color: Theme.of(context).secondaryHeaderColor, //Color(0xFFE8E8E8), - ), - Expanded( - child: Container( - child: secondScaffold, - ), - ) - ], - ); - }); - } -} diff --git a/lib/components/chat_settings_popup_menu.dart b/lib/components/chat_settings_popup_menu.dart index 80e75f48..1d767919 100644 --- a/lib/components/chat_settings_popup_menu.dart +++ b/lib/components/chat_settings_popup_menu.dart @@ -1,10 +1,8 @@ import 'dart:async'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/chat_details.dart'; -import 'package:fluffychat/views/chat_list.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -95,9 +93,8 @@ class _ChatSettingsPopupMenuState extends State { final success = await showFutureLoadingDialog( context: context, future: () => widget.room.leave()); if (success.error == null) { - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute(context, ChatListView()), - (Route r) => false); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveAllOthers('/'); } } break; @@ -117,12 +114,9 @@ class _ChatSettingsPopupMenuState extends State { startCallAction(context); break; case 'details': - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatDetails(widget.room), - ), - ); + await AdaptivePageLayout.of(context).pushNamedAndRemoveAllOthers( + '/rooms/${widget.room.id}/details'); + break; } }, diff --git a/lib/components/default_drawer.dart b/lib/components/default_drawer.dart index 78e764ea..9f6da884 100644 --- a/lib/components/default_drawer.dart +++ b/lib/components/default_drawer.dart @@ -1,11 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/archive.dart'; -import 'package:fluffychat/views/discover_view.dart'; -import 'package:fluffychat/views/new_group.dart'; -import 'package:fluffychat/views/new_private_chat.dart'; -import 'package:fluffychat/views/settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -13,15 +8,9 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'matrix.dart'; class DefaultDrawer extends StatelessWidget { - void _drawerTapAction(BuildContext context, Widget view) { + void _drawerTapAction(BuildContext context, String route) { Navigator.of(context).pop(); - Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute( - context, - view, - ), - (r) => r.isFirst, - ); + AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst(route); } void _setStatus(BuildContext context) async { @@ -64,12 +53,12 @@ class DefaultDrawer extends StatelessWidget { ListTile( leading: Icon(Icons.people_outline), title: Text(L10n.of(context).createNewGroup), - onTap: () => _drawerTapAction(context, NewGroupView()), + onTap: () => _drawerTapAction(context, '/newgroup'), ), ListTile( leading: Icon(Icons.person_add_outlined), title: Text(L10n.of(context).newPrivateChat), - onTap: () => _drawerTapAction(context, NewPrivateChatView()), + onTap: () => _drawerTapAction(context, '/newprivatechat'), ), Divider(height: 1), ListTile( @@ -77,7 +66,7 @@ class DefaultDrawer extends StatelessWidget { title: Text(L10n.of(context).archive), onTap: () => _drawerTapAction( context, - Archive(), + '/archive', ), ), ListTile( @@ -85,7 +74,7 @@ class DefaultDrawer extends StatelessWidget { title: Text(L10n.of(context).discoverGroups), onTap: () => _drawerTapAction( context, - DiscoverView(), + '/discover', ), ), Divider(height: 1), @@ -94,7 +83,7 @@ class DefaultDrawer extends StatelessWidget { title: Text(L10n.of(context).settings), onTap: () => _drawerTapAction( context, - SettingsView(), + '/settings', ), ), ], diff --git a/lib/components/encryption_button.dart b/lib/components/encryption_button.dart index a2611a93..c2f57d97 100644 --- a/lib/components/encryption_button.dart +++ b/lib/components/encryption_button.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/chat_encryption_settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -23,12 +22,8 @@ class _EncryptionButtonState extends State { void _enableEncryptionAction() async { if (widget.room.encrypted) { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatEncryptionSettingsView(widget.room.id), - ), - ); + await AdaptivePageLayout.of(context) + .pushNamed('/rooms/${widget.room.id}/encryption'); return; } if (!widget.room.client.encryptionEnabled) { diff --git a/lib/components/list_items/chat_list_item.dart b/lib/components/list_items/chat_list_item.dart index 449c9827..feccce93 100644 --- a/lib/components/list_items/chat_list_item.dart +++ b/lib/components/list_items/chat_list_item.dart @@ -1,19 +1,17 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:circular_check_box/circular_check_box.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/event_extension.dart'; import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/room_status_extension.dart'; -import 'package:fluffychat/views/chat.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:pedantic/pedantic.dart'; -import '../../utils/app_route.dart'; import '../../utils/date_time_extension.dart'; -import '../../views/chat.dart'; import '../avatar.dart'; import '../dialogs/send_file_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; @@ -102,11 +100,8 @@ class ChatListItem extends StatelessWidget { } Matrix.of(context).shareContent = null; } - await Navigator.pushAndRemoveUntil( - context, - AppRoute.defaultRoute(context, ChatView(room.id)), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}'); } } } diff --git a/lib/components/list_items/message.dart b/lib/components/list_items/message.dart index 56ed4632..0de325ff 100644 --- a/lib/components/list_items/message.dart +++ b/lib/components/list_items/message.dart @@ -1,12 +1,12 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/components/message_content.dart'; import 'package:fluffychat/components/reply_content.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/event_extension.dart'; import 'package:fluffychat/utils/string_color.dart'; import 'package:flutter/material.dart'; -import '../adaptive_page_layout.dart'; import '../avatar.dart'; import '../matrix.dart'; import '../message_reactions.dart'; @@ -88,8 +88,7 @@ class Message extends StatelessWidget { color: color, borderRadius: BorderRadius.circular(radius), ), - constraints: - BoxConstraints(maxWidth: AdaptivePageLayout.defaultMinWidth), + constraints: BoxConstraints(maxWidth: FluffyThemes.columnWidth), child: Stack( children: [ Column( diff --git a/lib/components/list_items/public_room_list_item.dart b/lib/components/list_items/public_room_list_item.dart index d22d8a67..b6ccbd9a 100644 --- a/lib/components/list_items/public_room_list_item.dart +++ b/lib/components/list_items/public_room_list_item.dart @@ -1,10 +1,9 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import '../../utils/app_route.dart'; -import '../../views/chat.dart'; import '../avatar.dart'; import '../matrix.dart'; @@ -19,12 +18,8 @@ class PublicRoomListItem extends StatelessWidget { future: () => _joinRoomAndWait(context), ); if (success.error == null) { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatView(success.result), - ), - ); + await AdaptivePageLayout.of(context) + .pushNamed('/rooms/${success.result}'); } } diff --git a/lib/components/matrix.dart b/lib/components/matrix.dart index 686c937d..a0f13fc9 100644 --- a/lib/components/matrix.dart +++ b/lib/components/matrix.dart @@ -3,14 +3,13 @@ import 'dart:io'; import 'dart:convert'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/utils/firebase_controller.dart'; import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/sentry_controller.dart'; -import 'package:fluffychat/views/settings_3pid.dart'; import 'package:flushbar/flushbar.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -38,7 +37,16 @@ class Matrix extends StatefulWidget { final Widget child; - Matrix({this.child, Key key}) : super(key: key); + final GlobalKey apl; + + final BuildContext context; + + Matrix({ + this.child, + @required this.apl, + @required this.context, + Key key, + }) : super(key: key); @override MatrixState createState() => MatrixState(); @@ -52,7 +60,7 @@ class MatrixState extends State { Client client; Store store = Store(); @override - BuildContext context; + BuildContext get context => widget.context; Map get shareContent => _shareContent; set shareContent(Map content) { @@ -76,29 +84,16 @@ class MatrixState extends State { } void _initWithStore() async { - var initLoginState = client.onLoginStateChanged.stream.first; try { client.init(); - final firstLoginState = await initLoginState; - if (firstLoginState == LoginState.logged) { - if (PlatformInfos.isMobile) { - await FirebaseController.setupFirebase( - this, - clientName, - ); - } - } final storeItem = await store.getItem(SettingKeys.showNoPid); final configOptionMissing = storeItem == null || storeItem.isEmpty; if (configOptionMissing || (!configOptionMissing && storeItem == '1')) { if (configOptionMissing) { await store.setItem(SettingKeys.showNoPid, '0'); } - await Matrix.of(context) - .client - .requestThirdPartyIdentifiers() - .then((l) { + await client.requestThirdPartyIdentifiers().then((l) { if (l.isEmpty) { Flushbar( title: L10n.of(context).warning, @@ -110,12 +105,8 @@ class MatrixState extends State { borderRadius: BorderRadius.circular(6), ), child: Text(L10n.of(context).edit), - onPressed: () => Navigator.of(context).push( - AppRoute.defaultRoute( - context, - Settings3PidView(), - ), - ), + onPressed: () => + AdaptivePageLayout.of(context).pushNamed('/settings/3pid'), ), flushbarStyle: FlushbarStyle.FLOATING, ).show(context); @@ -133,6 +124,7 @@ class MatrixState extends State { StreamSubscription onKeyVerificationRequestSub; StreamSubscription onJitsiCallSub; StreamSubscription onNotification; + StreamSubscription onLoginStateChanged; StreamSubscription onUiaRequest; StreamSubscription onFocusSub; StreamSubscription onBlurSub; @@ -301,6 +293,8 @@ class MatrixState extends State { } } + LoginState loginState; + void initMatrix() { clientName = '${AppConfig.applicationName} ${kIsWeb ? 'Web' : Platform.operatingSystem}'; @@ -380,6 +374,19 @@ class MatrixState extends State { onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true); onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false); } + onLoginStateChanged ??= client.onLoginStateChanged.stream.listen((state) { + if (loginState != state) { + loginState = state; + widget.apl.currentState.pushNamedAndRemoveAllOthers('/'); + if (loginState == LoginState.logged) { + FirebaseController.context = context; + FirebaseController.setupFirebase( + this, + clientName, + ).catchError(SentryController.captureException); + } + } + }); onUiaRequest ??= client.onUiaRequest.stream.listen(_onUiaRequest); if (kIsWeb || Platform.isLinux) { client.onSync.stream.first.then((s) { diff --git a/lib/components/user_bottom_sheet.dart b/lib/components/user_bottom_sheet.dart index 7ea9de04..94500a43 100644 --- a/lib/components/user_bottom_sheet.dart +++ b/lib/components/user_bottom_sheet.dart @@ -1,12 +1,11 @@ import 'dart:math'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart'; -import 'package:fluffychat/utils/app_route.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; -import 'package:fluffychat/views/chat.dart'; import 'package:flutter/material.dart'; import 'content_banner.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -15,7 +14,6 @@ import '../utils/presence_extension.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'matrix.dart'; import 'dialogs/key_verification_dialog.dart'; -import '../utils/app_route.dart'; class UserBottomSheet extends StatelessWidget { final User user; @@ -76,12 +74,8 @@ class UserBottomSheet extends StatelessWidget { break; case 'message': final roomId = await user.startDirectChat(); - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute( - context, - ChatView(roomId), - ), - (Route r) => r.isFirst); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}'); break; } } @@ -161,8 +155,8 @@ class UserBottomSheet extends StatelessWidget { } return Center( child: Container( - width: min(MediaQuery.of(context).size.width, - AdaptivePageLayout.defaultMinWidth * 1.5), + width: min( + MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), child: SafeArea( child: Material( elevation: 4, diff --git a/lib/config/routes.dart b/lib/config/routes.dart new file mode 100644 index 00000000..d3314afb --- /dev/null +++ b/lib/config/routes.dart @@ -0,0 +1,228 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/components/matrix.dart'; +import 'package:fluffychat/views/archive.dart'; +import 'package:fluffychat/views/auth_web_view.dart'; +import 'package:fluffychat/views/chat.dart'; +import 'package:fluffychat/views/chat_details.dart'; +import 'package:fluffychat/views/chat_encryption_settings.dart'; +import 'package:fluffychat/views/chat_list.dart'; +import 'package:fluffychat/views/chat_permissions_settings.dart'; +import 'package:fluffychat/views/discover_view.dart'; +import 'package:fluffychat/views/empty_page.dart'; +import 'package:fluffychat/views/homeserver_picker.dart'; +import 'package:fluffychat/views/invitation_selection.dart'; +import 'package:fluffychat/views/loading_view.dart'; +import 'package:fluffychat/views/log_view.dart'; +import 'package:fluffychat/views/login.dart'; +import 'package:fluffychat/views/new_group.dart'; +import 'package:fluffychat/views/new_private_chat.dart'; +import 'package:fluffychat/views/settings.dart'; +import 'package:fluffychat/views/settings_3pid.dart'; +import 'package:fluffychat/views/settings_devices.dart'; +import 'package:fluffychat/views/settings_emotes.dart'; +import 'package:fluffychat/views/settings_ignore_list.dart'; +import 'package:fluffychat/views/settings_multiple_emotes.dart'; +import 'package:fluffychat/views/settings_notifications.dart'; +import 'package:fluffychat/views/settings_style.dart'; +import 'package:fluffychat/views/sign_up.dart'; +import 'package:fluffychat/views/sign_up_password.dart'; +import 'package:flutter/material.dart'; + +class FluffyRoutes { + final BuildContext context; + + const FluffyRoutes(this.context); + + ViewData onGenerateRoute(RouteSettings settings) { + final parts = settings.name.split('/'); + Logs().v(settings.name); + + // Routes if the app is loading + if (Matrix.of(context).loginState == null) { + return ViewData(mainView: (_) => LoadingView()); + // Routes if user is NOT logged in + } else if (Matrix.of(context).loginState == LoginState.loggedOut) { + switch (parts[1]) { + case '': + return ViewData(mainView: (_) => HomeserverPicker()); + case 'login': + return ViewData(mainView: (_) => Login()); + case 'signup': + if (parts.length == 5 && parts[2] == 'password') { + return ViewData( + mainView: (_) => SignUpPassword( + parts[3], + displayname: parts[4], + avatar: settings.arguments, + ), + ); + } + return ViewData(mainView: (_) => SignUp()); + case 'authwebview': + if (parts.length == 4) { + return ViewData( + mainView: (_) => AuthWebView( + parts[2], + Uri.decodeComponent(parts[3]), + settings.arguments, + ), + ); + } + } + } + // Routes IF user is logged in + else { + switch (parts[1]) { + case '': + return ViewData( + mainView: (_) => ChatList(), + emptyView: (_) => EmptyPage(), + ); + case 'rooms': + if (parts.length == 3) { + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + ); + } else if (parts.length == 4) { + final roomId = parts[2]; + final action = parts[3]; + switch (action) { + case 'details': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + rightView: (_) => ChatDetails(roomId), + ); + case 'encryption': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + rightView: (_) => ChatEncryptionSettings(roomId), + ); + case 'permissions': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + rightView: (_) => ChatPermissionsSettings(roomId), + ); + case 'invite': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + rightView: (_) => InvitationSelection(roomId), + ); + case 'emotes': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => Chat(parts[2]), + rightView: (_) => MultipleEmotesSettings(roomId), + ); + } + } + return ViewData( + mainView: (_) => ChatList(), + emptyView: (_) => EmptyPage(), + ); + case 'archive': + return ViewData( + mainView: (_) => Archive(), + emptyView: (_) => Chat(parts[2]), + ); + case 'discover': + return ViewData( + mainView: (_) => + DiscoverPage(alias: parts.length == 3 ? parts[2] : null), + emptyView: (_) => EmptyPage(), + ); + case 'logs': + return ViewData( + mainView: (_) => LogViewer(), + ); + case 'newgroup': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => NewGroup(), + ); + case 'newprivatechat': + return ViewData( + leftView: (_) => ChatList(), + mainView: (_) => NewPrivateChat(), + ); + case 'settings': + if (parts.length == 3) { + final action = parts[2]; + switch (action) { + case '3pid': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => Settings3Pid(), + ); + case 'devices': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => DevicesSettings(), + ); + case 'emotes': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => EmotesSettings(room: settings.arguments), + ); + case 'ignore': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => SettingsIgnoreList(), + ); + case 'notifications': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => SettingsNotifications(), + ); + case 'style': + return ViewData( + leftView: (_) => Settings(), + mainView: (_) => SettingsStyle(), + ); + } + } + return ViewData( + mainView: (_) => Settings(), + emptyView: (_) => EmptyPage(), + ); + } + } + + // If route cant be found: + return ViewData( + mainView: (_) => Center( + child: Text('Route "${settings.name}" not found...'), + ), + ); + } +} + +class SettingsDevices {} + +class FadeRoute extends PageRouteBuilder { + final Widget page; + FadeRoute({this.page}) + : super( + pageBuilder: ( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) => + page, + transitionsBuilder: ( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) => + FadeTransition( + opacity: animation, + child: child, + ), + ); +} diff --git a/lib/config/themes.dart b/lib/config/themes.dart index 58d147ad..28cd1a44 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; abstract class FluffyThemes { + static const double columnWidth = 360.0; static ThemeData light = ThemeData( primaryColorDark: Colors.white, primaryColorLight: Color(0xff121212), diff --git a/lib/main.dart b/lib/main.dart index 7f1df9be..89b6c495 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,22 +2,19 @@ import 'dart:async'; import 'package:adaptive_theme/adaptive_theme.dart'; -import 'package:famedlysdk/famedlysdk.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; +import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/utils/sentry_controller.dart'; -import 'package:fluffychat/views/homeserver_picker.dart'; -import 'package:flushbar/flushbar_helper.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:universal_html/prefer_universal/html.dart' as html; import 'components/matrix.dart'; import 'config/themes.dart'; -import 'utils/localized_exception_extension.dart'; import 'app_config.dart'; -import 'views/chat_list.dart'; void main() async { SystemChrome.setSystemUIOverlayStyle( @@ -31,51 +28,38 @@ void main() async { } class App extends StatelessWidget { + final GlobalKey _apl = + GlobalKey(); @override Widget build(BuildContext context) { - return Matrix( - child: Builder( - builder: (BuildContext context) => AdaptiveTheme( - light: FluffyThemes.light, - dark: FluffyThemes.dark, - initial: AdaptiveThemeMode.system, - builder: (theme, darkTheme) => MaterialApp( - title: '${AppConfig.applicationName}', - theme: theme, - darkTheme: darkTheme, - localizationsDelegates: L10n.localizationsDelegates, - supportedLocales: L10n.supportedLocales, - locale: kIsWeb - ? Locale(html.window.navigator.language.split('-').first) - : null, - home: FutureBuilder( - future: - Matrix.of(context).client.onLoginStateChanged.stream.first, - builder: (context, snapshot) { - LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait; - LoadingDialog.defaultBackLabel = L10n.of(context).close; - LoadingDialog.defaultOnError = - (Object e) => e.toLocalizedString(context); - if (snapshot.hasError) { - WidgetsBinding.instance - .addPostFrameCallback((_) => FlushbarHelper.createError( - title: L10n.of(context).oopsSomethingWentWrong, - message: snapshot.error.toString(), - ).show(context)); - return HomeserverPicker(); - } - if (!snapshot.hasData) { - return Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); - } - if (Matrix.of(context).client.isLogged()) { - return ChatListView(); - } - return HomeserverPicker(); - }, + return AdaptiveTheme( + light: FluffyThemes.light, + dark: FluffyThemes.dark, + initial: AdaptiveThemeMode.system, + builder: (theme, darkTheme) => MaterialApp( + title: '${AppConfig.applicationName}', + theme: theme, + darkTheme: darkTheme, + localizationsDelegates: L10n.localizationsDelegates, + supportedLocales: L10n.supportedLocales, + locale: kIsWeb + ? Locale(html.window.navigator.language.split('-').first) + : null, + home: Builder( + builder: (context) => Matrix( + context: context, + apl: _apl, + child: Builder( + builder: (context) => AdaptivePageLayout( + key: _apl, + onGenerateRoute: FluffyRoutes(context).onGenerateRoute, + dividerColor: Theme.of(context).dividerColor, + columnWidth: FluffyThemes.columnWidth, + routeBuilder: (builder, settings) => + _apl.currentState.columnMode(context) + ? FadeRoute(page: builder(context)) + : CupertinoPageRoute(builder: builder), + ), ), ), ), diff --git a/lib/utils/app_route.dart b/lib/utils/app_route.dart index c9f25b84..a44ebf60 100644 --- a/lib/utils/app_route.dart +++ b/lib/utils/app_route.dart @@ -1,10 +1,11 @@ -import 'package:fluffychat/components/adaptive_page_layout.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class AppRoute extends PageRouteBuilder { static Route defaultRoute(BuildContext context, Widget page) { - return context != null && !AdaptivePageLayout.columnMode(context) + return context != null && + !AdaptivePageLayout.of(context).columnMode(context) ? CupertinoPageRoute( builder: (BuildContext context) => page, ) diff --git a/lib/utils/firebase_controller.dart b/lib/utils/firebase_controller.dart index 426549c5..f2bad9f2 100644 --- a/lib/utils/firebase_controller.dart +++ b/lib/utils/firebase_controller.dart @@ -7,8 +7,6 @@ import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/chat.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -112,12 +110,8 @@ abstract class FirebaseController { roomId = (message['data'] ?? message)['room_id']; } if (roomId?.isEmpty ?? true) throw ('Bad roomId'); - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute( - context, - ChatView(roomId), - ), - (r) => r.isFirst); + await matrix.widget.apl.currentState + .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}'); } catch (_) { await FlushbarHelper.createError(message: 'Failed to open chat...') .show(context); diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index 21db6cd1..e00e5f09 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -1,11 +1,9 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/app_config.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/chat.dart'; -import 'package:fluffychat/views/discover_view.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -74,12 +72,8 @@ class UrlLauncher { } if (room != null) { // we have the room, so....just open it! - await Navigator.pushAndRemoveUntil( - context, - AppRoute.defaultRoute( - context, ChatView(room.id, scrollToEventId: event)), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}/$event'); return; } if (roomIdOrAlias.sigil == '!') { @@ -101,21 +95,12 @@ class UrlLauncher { await showFutureLoadingDialog( context: context, future: () => Future.delayed(const Duration(seconds: 2))); - await Navigator.pushAndRemoveUntil( - context, - AppRoute.defaultRoute( - context, ChatView(response.result, scrollToEventId: event)), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst( + '/rooms/${response.result}/$event'); } } else { - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute( - context, - DiscoverView(alias: roomIdOrAlias), - ), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/discover/${roomIdOrAlias}'); } } else if (identityParts.primaryIdentifier.sigil == '@') { final user = User( @@ -124,11 +109,9 @@ class UrlLauncher { ); var roomId = matrix.client.getDirectChatFromUserId(user.id); if (roomId != null) { - await Navigator.pushAndRemoveUntil( - context, - AppRoute.defaultRoute(context, ChatView(roomId)), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}'); + return; } @@ -142,14 +125,10 @@ class UrlLauncher { future: () => user.startDirectChat(), )) .result; - Navigator.of(context).pop(); if (roomId != null) { - await Navigator.pushAndRemoveUntil( - context, - AppRoute.defaultRoute(context, ChatView(roomId)), - (r) => r.isFirst, - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}'); } } } diff --git a/lib/views/archive.dart b/lib/views/archive.dart index 4261d81e..3037739d 100644 --- a/lib/views/archive.dart +++ b/lib/views/archive.dart @@ -1,5 +1,4 @@ import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/list_items/chat_list_item.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:flutter/material.dart'; @@ -35,36 +34,28 @@ class _ArchiveState extends State { @override Widget build(BuildContext context) { - return AdaptivePageLayout( - firstScaffold: Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).archive), - elevation: _scrolledToTop ? 0 : null, - ), - body: FutureBuilder>( - future: getArchive(context), - builder: (BuildContext context, snapshot) { - if (!snapshot.hasData) { - return Center(child: CircularProgressIndicator()); - } else { - archive = snapshot.data; - return ListView.builder( - controller: _scrollController, - itemCount: archive.length, - itemBuilder: (BuildContext context, int i) => ChatListItem( - archive[i], - onForget: () => setState(() => archive.removeAt(i))), - ); - } - }, - ), + return Scaffold( + appBar: AppBar( + title: Text(L10n.of(context).archive), + elevation: _scrolledToTop ? 0 : null, ), - secondScaffold: Scaffold( - body: Center( - child: Image.asset('assets/logo.png', width: 100, height: 100), - ), + body: FutureBuilder>( + future: getArchive(context), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } else { + archive = snapshot.data; + return ListView.builder( + controller: _scrollController, + itemCount: archive.length, + itemBuilder: (BuildContext context, int i) => ChatListItem( + archive[i], + onForget: () => setState(() => archive.removeAt(i))), + ); + } + }, ), - primaryPage: FocusPage.FIRST, ); } } diff --git a/lib/views/auth_web_view.dart b/lib/views/auth_web_view.dart index 920d0879..7ab829db 100644 --- a/lib/views/auth_web_view.dart +++ b/lib/views/auth_web_view.dart @@ -1,3 +1,4 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -23,7 +24,7 @@ class AuthWebView extends StatelessWidget { leading: IconButton( icon: Icon(Icons.close), onPressed: () { - Navigator.of(context).pop(); + AdaptivePageLayout.of(context).pop(); onAuthDone(); }, ), diff --git a/lib/views/chat.dart b/lib/views/chat.dart index 1f796a65..ea4378f1 100644 --- a/lib/views/chat.dart +++ b/lib/views/chat.dart @@ -3,14 +3,15 @@ import 'dart:io'; import 'dart:math'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:emoji_picker/emoji_picker.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/chat_settings_popup_menu.dart'; import 'package:fluffychat/components/connection_status_header.dart'; import 'package:fluffychat/components/dialogs/recording_dialog.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/encryption_button.dart'; @@ -19,7 +20,6 @@ import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/reply_content.dart'; import 'package:fluffychat/components/user_bottom_sheet.dart'; import 'package:fluffychat/config/app_emojis.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/room_status_extension.dart'; @@ -38,39 +38,19 @@ import '../components/dialogs/send_file_dialog.dart'; import '../components/input_bar.dart'; import '../utils/filtered_timeline_extension.dart'; import '../utils/matrix_file_extension.dart'; -import 'chat_details.dart'; -import 'chat_list.dart'; -class ChatView extends StatelessWidget { +class Chat extends StatefulWidget { final String id; final String scrollToEventId; - const ChatView(this.id, {Key key, this.scrollToEventId}) : super(key: key); - - @override - Widget build(BuildContext context) { - // TODO: implement build - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList( - activeChat: id, - ), - secondScaffold: _Chat(id, scrollToEventId: scrollToEventId), - ); - } -} - -class _Chat extends StatefulWidget { - final String id; - final String scrollToEventId; - - const _Chat(this.id, {Key key, this.scrollToEventId}) : super(key: key); + Chat(this.id, {Key key, this.scrollToEventId}) + : super(key: key ?? Key('chatroom-$id')); @override _ChatState createState() => _ChatState(); } -class _ChatState extends State<_Chat> { +class _ChatState extends State { Room room; Timeline timeline; @@ -343,7 +323,7 @@ class _ChatState extends State<_Chat> { }; } setState(() => selectedEvents.clear()); - Navigator.of(context).popUntil((r) => r.isFirst); + AdaptivePageLayout.of(context).popUntilIsFirst(); } void sendAgainAction(Timeline timeline) { @@ -509,12 +489,8 @@ class _ChatState extends State<_Chat> { '${room.directChatMatrixID} ', ), ) - : () => Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatDetails(room), - ), - ), + : () => AdaptivePageLayout.of(context) + .pushNamed('/rooms/${room.id}/details'), title: Text( room.getLocalizedDisplayname( MatrixLocals(L10n.of(context))), @@ -643,8 +619,7 @@ class _ChatState extends State<_Chat> { horizontal: max( 0, (MediaQuery.of(context).size.width - - AdaptivePageLayout.defaultMinWidth * - 3.5) / + FluffyThemes.columnWidth * 3.5) / 2), ), reverse: true, diff --git a/lib/views/chat_details.dart b/lib/views/chat_details.dart index 33b9ddae..02143b30 100644 --- a/lib/views/chat_details.dart +++ b/lib/views/chat_details.dart @@ -1,40 +1,36 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:fluffychat/app_config.dart'; +import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; -import 'package:fluffychat/views/chat_permissions_settings.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/chat_settings_popup_menu.dart'; import 'package:fluffychat/components/content_banner.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/list_items/participant_list_item.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; -import 'package:fluffychat/views/chat_list.dart'; -import 'package:fluffychat/views/invitation_selection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:image_picker/image_picker.dart'; import 'package:matrix_link_text/link_text.dart'; -import './settings_emotes.dart'; -import './settings_multiple_emotes.dart'; import '../utils/url_launcher.dart'; class ChatDetails extends StatefulWidget { - final Room room; + final String roomId; - const ChatDetails(this.room); + const ChatDetails(this.roomId); @override _ChatDetailsState createState() => _ChatDetailsState(); } class _ChatDetailsState extends State { + Room room; List members; void setDisplaynameAction(BuildContext context) async { final input = await showTextInputDialog( @@ -42,7 +38,7 @@ class _ChatDetailsState extends State { title: L10n.of(context).changeTheNameOfTheGroup, textFields: [ DialogTextField( - initialText: widget.room.getLocalizedDisplayname( + initialText: room.getLocalizedDisplayname( MatrixLocals( L10n.of(context), ), @@ -53,7 +49,7 @@ class _ChatDetailsState extends State { if (input == null) return; final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.setName(input.single), + future: () => room.setName(input.single), ); if (success.error == null) { await FlushbarHelper.createSuccess( @@ -74,9 +70,9 @@ class _ChatDetailsState extends State { ], ); if (input == null) return; - final domain = widget.room.client.userID.domain; + final domain = room.client.userID.domain; final canonicalAlias = '%23' + input.single + '%3A' + domain; - final aliasEvent = widget.room.getState('m.room.aliases', domain); + final aliasEvent = room.getState('m.room.aliases', domain); final aliases = aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : []; if (aliases.indexWhere((s) => s == canonicalAlias) == -1) { @@ -84,22 +80,19 @@ class _ChatDetailsState extends State { newAliases.add(canonicalAlias); final response = await showFutureLoadingDialog( context: context, - future: () => - widget.room.client.requestRoomAliasInformations(canonicalAlias), + future: () => room.client.requestRoomAliasInformations(canonicalAlias), ); if (response.error != null) { final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.client - .createRoomAlias(canonicalAlias, widget.room.id), + future: () => room.client.createRoomAlias(canonicalAlias, room.id), ); if (success.error != null) return; } } await showFutureLoadingDialog( context: context, - future: () => widget.room.client - .sendState(widget.room.id, 'm.room.canonical_alias', { + future: () => room.client.sendState(room.id, 'm.room.canonical_alias', { 'alias': input.single, }), ); @@ -112,7 +105,7 @@ class _ChatDetailsState extends State { textFields: [ DialogTextField( hintText: L10n.of(context).setGroupDescription, - initialText: widget.room.topic, + initialText: room.topic, minLines: 1, maxLines: 4, ) @@ -121,7 +114,7 @@ class _ChatDetailsState extends State { if (input == null) return; final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.setDescription(input.single), + future: () => room.setDescription(input.single), ); if (success.error == null) { await FlushbarHelper.createSuccess( @@ -156,7 +149,7 @@ class _ChatDetailsState extends State { final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.setAvatar(file), + future: () => room.setAvatar(file), ); if (success.error == null) { await FlushbarHelper.createSuccess( @@ -167,7 +160,7 @@ class _ChatDetailsState extends State { void requestMoreMembersAction(BuildContext context) async { final participants = await showFutureLoadingDialog( - context: context, future: () => widget.room.requestParticipants()); + context: context, future: () => room.requestParticipants()); if (participants.error == null) { setState(() => members = participants.result); } @@ -175,7 +168,8 @@ class _ChatDetailsState extends State { @override Widget build(BuildContext context) { - if (widget.room == null) { + room ??= Matrix.of(context).client.getRoomById(widget.roomId); + if (room == null) { return Scaffold( appBar: AppBar( title: Text(L10n.of(context).oopsSomethingWentWrong), @@ -185,381 +179,344 @@ class _ChatDetailsState extends State { ), ); } - members ??= widget.room.getParticipants(); + members ??= room.getParticipants(); members.removeWhere((u) => u.membership == Membership.leave); final actualMembersCount = - widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount; + room.mInvitedMemberCount + room.mJoinedMemberCount; final canRequestMoreMembers = members.length < actualMembersCount; - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList( - activeChat: widget.room.id, - ), - secondScaffold: StreamBuilder( - stream: widget.room.onUpdate.stream, - builder: (context, snapshot) { - return Scaffold( - body: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) => [ - SliverAppBar( - expandedHeight: 300.0, - floating: true, - pinned: true, - actions: [ - if (widget.room.canonicalAlias?.isNotEmpty ?? false) - IconButton( - icon: Icon(Icons.share_outlined), - onPressed: () => FluffyShare.share( - AppConfig.inviteLinkPrefix + - widget.room.canonicalAlias, - context), - ), - ChatSettingsPopupMenu(widget.room, false) - ], - title: Text( - widget.room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context))), - style: TextStyle( - color: Theme.of(context) - .appBarTheme - .textTheme - .headline6 - .color)), - backgroundColor: Theme.of(context).appBarTheme.color, - flexibleSpace: FlexibleSpaceBar( - background: ContentBanner(widget.room.avatar, - onEdit: widget.room.canSendEvent('m.room.avatar') - ? () => setAvatarAction(context) - : null), - ), + return StreamBuilder( + stream: room.onUpdate.stream, + builder: (context, snapshot) { + return Scaffold( + body: NestedScrollView( + headerSliverBuilder: + (BuildContext context, bool innerBoxIsScrolled) => [ + SliverAppBar( + expandedHeight: 300.0, + floating: true, + pinned: true, + actions: [ + if (room.canonicalAlias?.isNotEmpty ?? false) + IconButton( + icon: Icon(Icons.share_outlined), + onPressed: () => FluffyShare.share( + AppConfig.inviteLinkPrefix + room.canonicalAlias, + context), + ), + ChatSettingsPopupMenu(room, false) + ], + title: Text( + room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context))), + style: TextStyle( + color: Theme.of(context) + .appBarTheme + .textTheme + .headline6 + .color)), + backgroundColor: Theme.of(context).appBarTheme.color, + flexibleSpace: FlexibleSpaceBar( + background: ContentBanner(room.avatar, + onEdit: room.canSendEvent('m.room.avatar') + ? () => setAvatarAction(context) + : null), ), - ], - body: ListView.builder( - itemCount: - members.length + 1 + (canRequestMoreMembers ? 1 : 0), - itemBuilder: (BuildContext context, int i) => i == 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ListTile( - leading: widget.room.canSendEvent('m.room.topic') - ? CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.edit_outlined), - ) - : null, - title: Text( - '${L10n.of(context).groupDescription}:', - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold)), - subtitle: LinkText( - text: widget.room.topic?.isEmpty ?? true - ? L10n.of(context).addGroupDescription - : widget.room.topic, - linkStyle: TextStyle(color: Colors.blueAccent), - textStyle: TextStyle( - fontSize: 14, - color: Theme.of(context) - .textTheme - .bodyText2 - .color, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), - ), - onTap: widget.room.canSendEvent('m.room.topic') - ? () => setTopicAction(context) - : null, - ), - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).settings, + ), + ], + body: ListView.builder( + itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0), + itemBuilder: (BuildContext context, int i) => i == 0 + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ListTile( + leading: room.canSendEvent('m.room.topic') + ? CircleAvatar( + backgroundColor: Theme.of(context) + .scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.edit_outlined), + ) + : null, + title: Text('${L10n.of(context).groupDescription}:', style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold)), + subtitle: LinkText( + text: room.topic?.isEmpty ?? true + ? L10n.of(context).addGroupDescription + : room.topic, + linkStyle: TextStyle(color: Colors.blueAccent), + textStyle: TextStyle( + fontSize: 14, + color: + Theme.of(context).textTheme.bodyText2.color, + ), + onLinkTap: (url) => + UrlLauncher(context, url).launchUrl(), + ), + onTap: room.canSendEvent('m.room.topic') + ? () => setTopicAction(context) + : null, + ), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).settings, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, ), ), - if (widget.room.canSendEvent('m.room.name')) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.people_outlined), - ), - title: Text( - L10n.of(context).changeTheNameOfTheGroup), - subtitle: Text(widget.room - .getLocalizedDisplayname( - MatrixLocals(L10n.of(context)))), - onTap: () => setDisplaynameAction(context), - ), - if (widget.room - .canSendEvent('m.room.canonical_alias') && - widget.room.joinRules == JoinRules.public) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.link_outlined), - ), - onTap: () => setCanonicalAliasAction(context), - title: Text(L10n.of(context).setInvitationLink), - subtitle: Text( - (widget.room.canonicalAlias?.isNotEmpty ?? - false) - ? widget.room.canonicalAlias - : L10n.of(context).none), - ), + ), + if (room.canSendEvent('m.room.name')) ListTile( leading: CircleAvatar( backgroundColor: Theme.of(context).scaffoldBackgroundColor, foregroundColor: Colors.grey, - child: Icon(Icons.insert_emoticon_outlined), + child: Icon(Icons.people_outlined), ), - title: Text(L10n.of(context).emoteSettings), - subtitle: Text(L10n.of(context).setCustomEmotes), - onTap: () async { - // okay, we need to test if there are any emote state events other than the default one - // if so, we need to be directed to a selection screen for which pack we want to look at - // otherwise, we just open the normal one. - if ((widget.room.states - .states['im.ponies.room_emotes'] ?? - {}) - .keys - .any((String s) => s.isNotEmpty)) { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - MultipleEmotesSettingsView( - room: widget.room), - ), - ); - } else { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - EmotesSettingsView(room: widget.room), - ), - ); - } - }, + title: Text( + L10n.of(context).changeTheNameOfTheGroup), + subtitle: Text(room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)))), + onTap: () => setDisplaynameAction(context), ), + if (room.canSendEvent('m.room.canonical_alias') && + room.joinRules == JoinRules.public) + ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.link_outlined), + ), + onTap: () => setCanonicalAliasAction(context), + title: Text(L10n.of(context).setInvitationLink), + subtitle: Text( + (room.canonicalAlias?.isNotEmpty ?? false) + ? room.canonicalAlias + : L10n.of(context).none), + ), + ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.insert_emoticon_outlined), + ), + title: Text(L10n.of(context).emoteSettings), + subtitle: Text(L10n.of(context).setCustomEmotes), + onTap: () async { + // okay, we need to test if there are any emote state events other than the default one + // if so, we need to be directed to a selection screen for which pack we want to look at + // otherwise, we just open the normal one. + if ((room.states + .states['im.ponies.room_emotes'] ?? + {}) + .keys + .any((String s) => s.isNotEmpty)) { + await AdaptivePageLayout.of(context) + .pushNamed('/rooms/${room.id}/emotes'); + } else { + await AdaptivePageLayout.of(context) + .pushNamed('/settings/emotes'); + } + }, + ), + PopupMenuButton( + child: ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.public_outlined)), + title: Text( + L10n.of(context).whoIsAllowedToJoinThisGroup), + subtitle: Text( + room.joinRules.getLocalizedString( + MatrixLocals(L10n.of(context))), + ), + ), + onSelected: (JoinRules joinRule) => + showFutureLoadingDialog( + context: context, + future: () => room.setJoinRules(joinRule), + ), + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.public, + child: Text(JoinRules.public + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.invite, + child: Text(JoinRules.invite + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + ], + ), + PopupMenuButton( + child: ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.visibility_outlined), + ), + title: Text( + L10n.of(context).visibilityOfTheChatHistory), + subtitle: Text( + room.historyVisibility.getLocalizedString( + MatrixLocals(L10n.of(context))), + ), + ), + onSelected: (HistoryVisibility historyVisibility) => + showFutureLoadingDialog( + context: context, + future: () => + room.setHistoryVisibility(historyVisibility), + ), + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.invited, + child: Text(HistoryVisibility.invited + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.joined, + child: Text(HistoryVisibility.joined + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.shared, + child: Text(HistoryVisibility.shared + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.world_readable, + child: Text(HistoryVisibility.world_readable + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + ], + ), + if (room.joinRules == JoinRules.public) PopupMenuButton( child: ListTile( leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.public_outlined)), - title: Text(L10n.of(context) - .whoIsAllowedToJoinThisGroup), + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.info_outline), + ), + title: Text( + L10n.of(context).areGuestsAllowedToJoin), subtitle: Text( - widget.room.joinRules.getLocalizedString( + room.guestAccess.getLocalizedString( MatrixLocals(L10n.of(context))), ), ), - onSelected: (JoinRules joinRule) => + onSelected: (GuestAccess guestAccess) => showFutureLoadingDialog( context: context, - future: () => - widget.room.setJoinRules(joinRule), + future: () => room.setGuestAccess(guestAccess), ), itemBuilder: (BuildContext context) => - >[ - if (widget.room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.public, - child: Text(JoinRules.public - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - if (widget.room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.invite, - child: Text(JoinRules.invite - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - ], - ), - PopupMenuButton( - child: ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.visibility_outlined), - ), - title: Text(L10n.of(context) - .visibilityOfTheChatHistory), - subtitle: Text( - widget.room.historyVisibility - .getLocalizedString( + >[ + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.can_join, + child: Text( + GuestAccess.can_join.getLocalizedString( MatrixLocals(L10n.of(context))), - ), - ), - onSelected: - (HistoryVisibility historyVisibility) => - showFutureLoadingDialog( - context: context, - future: () => widget.room - .setHistoryVisibility(historyVisibility), - ), - itemBuilder: (BuildContext context) => - >[ - if (widget.room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.invited, - child: Text(HistoryVisibility.invited - .getLocalizedString( - MatrixLocals(L10n.of(context)))), + ), ), - if (widget.room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.joined, - child: Text(HistoryVisibility.joined - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - if (widget.room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.shared, - child: Text(HistoryVisibility.shared - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - if (widget.room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.world_readable, - child: Text(HistoryVisibility.world_readable - .getLocalizedString( - MatrixLocals(L10n.of(context)))), + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.forbidden, + child: Text( + GuestAccess.forbidden.getLocalizedString( + MatrixLocals(L10n.of(context))), + ), ), ], ), - if (widget.room.joinRules == JoinRules.public) - PopupMenuButton( - child: ListTile( + ListTile( + title: Text(L10n.of(context).editChatPermissions), + subtitle: + Text(L10n.of(context).whoCanPerformWhichAction), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.edit_attributes_outlined), + ), + onTap: () => AdaptivePageLayout.of(context) + .pushNamed('/rooms/${room.id}/permissions'), + ), + Divider(thickness: 1), + ListTile( + title: Text( + actualMembersCount > 1 + ? L10n.of(context).countParticipants( + actualMembersCount.toString()) + : L10n.of(context).emptyChat, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), + ), + ), + room.canInvite + ? ListTile( + title: Text(L10n.of(context).inviteContact), leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.info_outline), + child: Icon(Icons.add_outlined), + backgroundColor: + Theme.of(context).primaryColor, + foregroundColor: Colors.white, ), - title: Text( - L10n.of(context).areGuestsAllowedToJoin), - subtitle: Text( - widget.room.guestAccess.getLocalizedString( - MatrixLocals(L10n.of(context))), - ), - ), - onSelected: (GuestAccess guestAccess) => - showFutureLoadingDialog( - context: context, - future: () => - widget.room.setGuestAccess(guestAccess), - ), - itemBuilder: (BuildContext context) => - >[ - if (widget.room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.can_join, - child: Text( - GuestAccess.can_join.getLocalizedString( - MatrixLocals(L10n.of(context))), - ), - ), - if (widget.room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.forbidden, - child: Text( - GuestAccess.forbidden - .getLocalizedString( - MatrixLocals(L10n.of(context))), - ), - ), - ], - ), - ListTile( - title: Text(L10n.of(context).editChatPermissions), - subtitle: Text( - L10n.of(context).whoCanPerformWhichAction), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.edit_attributes_outlined), - ), - onTap: () => Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatPermissionsSettingsView( - roomId: widget.room.id), - ), + onTap: () => AdaptivePageLayout.of(context) + .pushNamed('/rooms/${room.id}/invite'), + ) + : Container(), + ], + ) + : i < members.length + 1 + ? ParticipantListItem(members[i - 1]) + : ListTile( + title: Text(L10n.of(context) + .loadCountMoreParticipants( + (actualMembersCount - members.length) + .toString())), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + child: Icon( + Icons.refresh, + color: Colors.grey, ), ), - Divider(thickness: 1), - ListTile( - title: Text( - actualMembersCount > 1 - ? L10n.of(context).countParticipants( - actualMembersCount.toString()) - : L10n.of(context).emptyChat, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - widget.room.canInvite - ? ListTile( - title: Text(L10n.of(context).inviteContact), - leading: CircleAvatar( - child: Icon(Icons.add_outlined), - backgroundColor: - Theme.of(context).primaryColor, - foregroundColor: Colors.white, - ), - onTap: () => Navigator.of(context).push( - AppRoute.defaultRoute( - context, - InvitationSelection(widget.room), - ), - ), - ) - : Container(), - ], - ) - : i < members.length + 1 - ? ParticipantListItem(members[i - 1]) - : ListTile( - title: Text(L10n.of(context) - .loadCountMoreParticipants( - (actualMembersCount - members.length) - .toString())), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - child: Icon( - Icons.refresh, - color: Colors.grey, - ), - ), - onTap: () => requestMoreMembersAction(context), - ), - ), + onTap: () => requestMoreMembersAction(context), + ), ), - ); - }), - ); + ), + ); + }); } } diff --git a/lib/views/chat_encryption_settings.dart b/lib/views/chat_encryption_settings.dart index 29c27e23..fb3ef946 100644 --- a/lib/views/chat_encryption_settings.dart +++ b/lib/views/chat_encryption_settings.dart @@ -1,32 +1,14 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart'; -import 'package:fluffychat/views/chat_list.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../components/dialogs/key_verification_dialog.dart'; -class ChatEncryptionSettingsView extends StatelessWidget { - final String id; - - const ChatEncryptionSettingsView(this.id, {Key key}) : super(key: key); - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - firstScaffold: ChatList( - activeChat: id, - ), - secondScaffold: ChatEncryptionSettings(id), - primaryPage: FocusPage.SECOND, - ); - } -} - class ChatEncryptionSettings extends StatefulWidget { final String id; diff --git a/lib/views/chat_list.dart b/lib/views/chat_list.dart index b907efba..2478a4d7 100644 --- a/lib/views/chat_list.dart +++ b/lib/views/chat_list.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/components/connection_status_header.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart'; @@ -14,29 +15,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; -import '../components/adaptive_page_layout.dart'; import '../components/list_items/chat_list_item.dart'; import '../components/matrix.dart'; -import '../utils/app_route.dart'; import '../utils/matrix_file_extension.dart'; import '../utils/url_launcher.dart'; -import 'empty_page.dart'; -import 'homeserver_picker.dart'; -import 'new_private_chat.dart'; enum SelectMode { normal, share, select } -class ChatListView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.FIRST, - firstScaffold: ChatList(), - secondScaffold: EmptyPage(), - ); - } -} - class ChatList extends StatefulWidget { final String activeChat; @@ -86,9 +71,7 @@ class _ChatListState extends State { void _processIncomingSharedFiles(List files) { if (files?.isEmpty ?? true) return; - if (Navigator.of(context).canPop()) { - Navigator.of(context).popUntil((r) => r.isFirst); - } + AdaptivePageLayout.of(context).popUntilIsFirst(); final file = File(files.first.path); Matrix.of(context).shareContent = { @@ -102,9 +85,7 @@ class _ChatListState extends State { void _processIncomingSharedText(String text) { if (text == null) return; - if (Navigator.of(context).canPop()) { - Navigator.of(context).popUntil((r) => r.isFirst); - } + AdaptivePageLayout.of(context).popUntilIsFirst(); if (text.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) || (text.toLowerCase().startsWith(AppConfig.schemePrefix) && !RegExp(r'\s').hasMatch(text))) { @@ -194,205 +175,178 @@ class _ChatListState extends State { @override Widget build(BuildContext context) { - return StreamBuilder( - stream: Matrix.of(context).client.onLoginStateChanged.stream, + return StreamBuilder( + stream: Matrix.of(context).onShareContentChanged.stream, builder: (context, snapshot) { - if (snapshot.data == LoginState.loggedOut) { - Timer(Duration(seconds: 1), () { - Matrix.of(context).clean(); - Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute(context, HomeserverPicker()), - (r) => false); - }); + final selectMode = Matrix.of(context).shareContent == null + ? _selectedRoomIds.isEmpty + ? SelectMode.normal + : SelectMode.select + : SelectMode.share; + if (selectMode == SelectMode.share) { + _selectedRoomIds.clear(); } - return StreamBuilder( - stream: Matrix.of(context).onShareContentChanged.stream, - builder: (context, snapshot) { - final selectMode = Matrix.of(context).shareContent == null - ? _selectedRoomIds.isEmpty - ? SelectMode.normal - : SelectMode.select - : SelectMode.share; - if (selectMode == SelectMode.share) { - _selectedRoomIds.clear(); - } - Room selectedRoom; - if (_selectedRoomIds.length == 1) { - selectedRoom = Matrix.of(context) - .client - .getRoomById(_selectedRoomIds.single); - } - return Scaffold( - drawer: - selectMode != SelectMode.normal ? null : DefaultDrawer(), - appBar: AppBar( - centerTitle: false, - elevation: _scrolledToTop ? 0 : null, - leading: selectMode == SelectMode.share - ? IconButton( - icon: Icon(Icons.close), - onPressed: () => - Matrix.of(context).shareContent = null, - ) - : selectMode == SelectMode.select - ? IconButton( - icon: Icon(Icons.close), - onPressed: () => - setState(_selectedRoomIds.clear), - ) - : null, - titleSpacing: 0, - actions: selectMode != SelectMode.select - ? null - : [ - if (_selectedRoomIds.length == 1) - IconButton( - tooltip: L10n.of(context).toggleUnread, - icon: Icon(selectedRoom.isUnread - ? Icons.mark_chat_read_outlined - : Icons.mark_chat_unread_outlined), - onPressed: () => _toggleUnread(context), - ), - if (_selectedRoomIds.length == 1) - IconButton( - tooltip: L10n.of(context).toggleFavorite, - icon: Icon(Icons.push_pin_outlined), - onPressed: () => _toggleFavouriteRoom(context), - ), - if (_selectedRoomIds.length == 1) - IconButton( - icon: Icon(selectedRoom.pushRuleState == - PushRuleState.notify - ? Icons.notifications_off_outlined - : Icons.notifications_outlined), - tooltip: L10n.of(context).toggleMuted, - onPressed: () => _toggleMuted(context), - ), - IconButton( - icon: Icon(Icons.archive_outlined), - tooltip: L10n.of(context).archive, - onPressed: () => _archiveAction(context), - ), - ], - title: selectMode == SelectMode.share - ? Text(L10n.of(context).share) - : selectMode == SelectMode.select - ? Text(_selectedRoomIds.length.toString()) - : DefaultAppBarSearchField( - searchController: searchController, - hintText: L10n.of(context).searchForAChat, - onChanged: (_) => setState(() => null), - ), - ), - floatingActionButton: AdaptivePageLayout.columnMode(context) - ? null - : FloatingActionButton( - child: Icon(Icons.add_outlined), - backgroundColor: Theme.of(context).primaryColor, - onPressed: () => Navigator.of(context) - .pushAndRemoveUntil( - AppRoute.defaultRoute( - context, NewPrivateChatView()), - (r) => r.isFirst), + Room selectedRoom; + if (_selectedRoomIds.length == 1) { + selectedRoom = + Matrix.of(context).client.getRoomById(_selectedRoomIds.single); + } + return Scaffold( + drawer: selectMode != SelectMode.normal ? null : DefaultDrawer(), + appBar: AppBar( + centerTitle: false, + elevation: _scrolledToTop ? 0 : null, + leading: selectMode == SelectMode.share + ? IconButton( + icon: Icon(Icons.close), + onPressed: () => Matrix.of(context).shareContent = null, + ) + : selectMode == SelectMode.select + ? IconButton( + icon: Icon(Icons.close), + onPressed: () => setState(_selectedRoomIds.clear), + ) + : null, + titleSpacing: 0, + actions: selectMode != SelectMode.select + ? null + : [ + if (_selectedRoomIds.length == 1) + IconButton( + tooltip: L10n.of(context).toggleUnread, + icon: Icon(selectedRoom.isUnread + ? Icons.mark_chat_read_outlined + : Icons.mark_chat_unread_outlined), + onPressed: () => _toggleUnread(context), ), - body: Column( - children: [ - ConnectionStatusHeader(), - Expanded( - child: StreamBuilder( - stream: Matrix.of(context) - .client - .onSync - .stream - .where((s) => s.hasRoomUpdate), - builder: (context, snapshot) { - return FutureBuilder( - future: waitForFirstSync(context), - builder: (BuildContext context, snapshot) { - if (snapshot.hasData) { - var rooms = List.from( - Matrix.of(context).client.rooms); - rooms.removeWhere((Room room) => - room.lastEvent == null || - (searchMode && - !room.displayname - .toLowerCase() - .contains(searchController.text - .toLowerCase() ?? - ''))); - if (rooms.isEmpty && (!searchMode)) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - searchMode - ? Icons.search_outlined - : Icons.chat_bubble_outline, - size: 80, - color: Colors.grey, - ), - Text(searchMode - ? L10n.of(context).noRoomsFound - : L10n.of(context) - .startYourFirstChat), - ], - ), - ); - } - final totalCount = rooms.length; - return ListView.separated( - controller: _scrollController, - separatorBuilder: (BuildContext context, - int i) => - i == totalCount - ? ListTile( - title: Text( - L10n.of(context) - .publicRooms + - ':', - style: TextStyle( - fontWeight: - FontWeight.bold, - color: Theme.of(context) - .primaryColor, - ), - ), - ) - : Container(), - itemCount: totalCount, - itemBuilder: - (BuildContext context, int i) => - ChatListItem( - rooms[i], - selected: _selectedRoomIds - .contains(rooms[i].id), - onTap: selectMode == SelectMode.select - ? () => - _toggleSelection(rooms[i].id) - : null, - onLongPress: selectMode != - SelectMode.share - ? () => - _toggleSelection(rooms[i].id) - : null, - activeChat: - widget.activeChat == rooms[i].id, - ), - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - }, - ); - }), + if (_selectedRoomIds.length == 1) + IconButton( + tooltip: L10n.of(context).toggleFavorite, + icon: Icon(Icons.push_pin_outlined), + onPressed: () => _toggleFavouriteRoom(context), + ), + if (_selectedRoomIds.length == 1) + IconButton( + icon: Icon( + selectedRoom.pushRuleState == PushRuleState.notify + ? Icons.notifications_off_outlined + : Icons.notifications_outlined), + tooltip: L10n.of(context).toggleMuted, + onPressed: () => _toggleMuted(context), + ), + IconButton( + icon: Icon(Icons.archive_outlined), + tooltip: L10n.of(context).archive, + onPressed: () => _archiveAction(context), ), ], - ), - ); - }); + title: selectMode == SelectMode.share + ? Text(L10n.of(context).share) + : selectMode == SelectMode.select + ? Text(_selectedRoomIds.length.toString()) + : DefaultAppBarSearchField( + searchController: searchController, + hintText: L10n.of(context).searchForAChat, + onChanged: (_) => setState(() => null), + ), + ), + floatingActionButton: + AdaptivePageLayout.of(context).columnMode(context) + ? null + : FloatingActionButton( + child: Icon(Icons.add_outlined), + backgroundColor: Theme.of(context).primaryColor, + onPressed: () => AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/newprivatechat'), + ), + body: Column( + children: [ + ConnectionStatusHeader(), + Expanded( + child: StreamBuilder( + stream: Matrix.of(context) + .client + .onSync + .stream + .where((s) => s.hasRoomUpdate), + builder: (context, snapshot) { + return FutureBuilder( + future: waitForFirstSync(context), + builder: (BuildContext context, snapshot) { + if (snapshot.hasData) { + var rooms = List.from( + Matrix.of(context).client.rooms); + rooms.removeWhere((Room room) => + room.lastEvent == null || + (searchMode && + !room.displayname.toLowerCase().contains( + searchController.text.toLowerCase() ?? + ''))); + if (rooms.isEmpty && (!searchMode)) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + searchMode + ? Icons.search_outlined + : Icons.chat_bubble_outline, + size: 80, + color: Colors.grey, + ), + Text(searchMode + ? L10n.of(context).noRoomsFound + : L10n.of(context) + .startYourFirstChat), + ], + ), + ); + } + final totalCount = rooms.length; + return ListView.separated( + controller: _scrollController, + separatorBuilder: + (BuildContext context, int i) => + i == totalCount + ? ListTile( + title: Text( + L10n.of(context).publicRooms + + ':', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context) + .primaryColor, + ), + ), + ) + : Container(), + itemCount: totalCount, + itemBuilder: (BuildContext context, int i) => + ChatListItem( + rooms[i], + selected: + _selectedRoomIds.contains(rooms[i].id), + onTap: selectMode == SelectMode.select + ? () => _toggleSelection(rooms[i].id) + : null, + onLongPress: selectMode != SelectMode.share + ? () => _toggleSelection(rooms[i].id) + : null, + activeChat: widget.activeChat == rooms[i].id, + ), + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + }, + ); + }), + ), + ], + ), + ); }); } } diff --git a/lib/views/chat_permissions_settings.dart b/lib/views/chat_permissions_settings.dart index 21fd7351..212f44a8 100644 --- a/lib/views/chat_permissions_settings.dart +++ b/lib/views/chat_permissions_settings.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; @@ -9,29 +8,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'chat_list.dart'; - -class ChatPermissionsSettingsView extends StatelessWidget { - final String roomId; - - const ChatPermissionsSettingsView({Key key, this.roomId}) : super(key: key); - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - firstScaffold: ChatList( - activeChat: roomId, - ), - secondScaffold: ChatPermissionsSettings(roomId: roomId), - primaryPage: FocusPage.SECOND, - ); - } -} - class ChatPermissionsSettings extends StatelessWidget { final String roomId; - const ChatPermissionsSettings({Key key, @required this.roomId}) - : super(key: key); + const ChatPermissionsSettings(this.roomId, {Key key}) : super(key: key); void _editPowerLevel(BuildContext context, String key, int currentLevel, {String category}) async { diff --git a/lib/views/discover_view.dart b/lib/views/discover_view.dart index 6490adba..d31ffcd2 100644 --- a/lib/views/discover_view.dart +++ b/lib/views/discover_view.dart @@ -1,32 +1,15 @@ import 'dart:async'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/chat.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'empty_page.dart'; - -class DiscoverView extends StatelessWidget { - final String alias; - - const DiscoverView({Key key, this.alias}) : super(key: key); - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - firstScaffold: DiscoverPage(alias: alias), - secondScaffold: EmptyPage(), - ); - } -} - class DiscoverPage extends StatefulWidget { final String alias; @@ -110,12 +93,8 @@ class _DiscoverPageState extends State { ), ); if (success.error == null) { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatView(success.result), - ), - ); + await AdaptivePageLayout.of(context) + .pushNamedAndRemoveUntilIsFirst('/rooms/${success.result}'); } } diff --git a/lib/views/homeserver_picker.dart b/lib/views/homeserver_picker.dart index 67d4c935..382f002f 100644 --- a/lib/views/homeserver_picker.dart +++ b/lib/views/homeserver_picker.dart @@ -1,19 +1,16 @@ import 'dart:math'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/app_config.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/utils/sentry_controller.dart'; -import 'package:fluffychat/views/sign_up.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'login.dart'; - class HomeserverPicker extends StatelessWidget { Future _setHomeserverAction(BuildContext context) async { const prefix = 'https://'; @@ -45,8 +42,8 @@ class HomeserverPicker extends StatelessWidget { context: context, future: () => checkHomeserver(homeserver, Matrix.of(context).client)); if (success.result == true) { - await Navigator.of(context) - .push(AppRoute(AppConfig.enableRegistration ? SignUp() : Login())); + await AdaptivePageLayout.of(context) + .pushNamed(AppConfig.enableRegistration ? '/signup' : '/login'); } } diff --git a/lib/views/image_view.dart b/lib/views/image_view.dart index 2b730497..84637f0b 100644 --- a/lib/views/image_view.dart +++ b/lib/views/image_view.dart @@ -1,3 +1,4 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/components/image_bubble.dart'; import 'package:fluffychat/components/matrix.dart'; @@ -13,7 +14,7 @@ class ImageView extends StatelessWidget { void _forwardAction(BuildContext context) async { Matrix.of(context).shareContent = event.content; - Navigator.of(context).popUntil((r) => r.isFirst); + AdaptivePageLayout.of(context).popUntilIsFirst(); } @override diff --git a/lib/views/invitation_selection.dart b/lib/views/invitation_selection.dart index 1d826ef0..0c12c330 100644 --- a/lib/views/invitation_selection.dart +++ b/lib/views/invitation_selection.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; @@ -11,11 +10,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../utils/localized_exception_extension.dart'; -import 'chat_list.dart'; class InvitationSelection extends StatefulWidget { - final Room room; - const InvitationSelection(this.room, {Key key}) : super(key: key); + final String roomId; + const InvitationSelection(this.roomId, {Key key}) : super(key: key); @override _InvitationSelectionState createState() => _InvitationSelectionState(); @@ -27,11 +25,12 @@ class _InvitationSelectionState extends State { bool loading = false; List foundProfiles = []; Timer coolDown; + Room room; Future> getContacts(BuildContext context) async { var client2 = Matrix.of(context).client; final client = client2; - var participants = await widget.room.requestParticipants(); + var participants = await room.requestParticipants(); participants.removeWhere( (u) => ![Membership.join, Membership.invite].contains(u.membership), ); @@ -59,7 +58,7 @@ class _InvitationSelectionState extends State { void inviteAction(BuildContext context, String id) async { final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.invite(id), + future: () => room.invite(id), ); if (success.error == null) { await FlushbarHelper.createSuccess( @@ -107,7 +106,7 @@ class _InvitationSelectionState extends State { Profile.fromJson({'user_id': '@$text'}), ]); } - final participants = widget.room + final participants = room .getParticipants() .where((user) => [Membership.join, Membership.invite].contains(user.membership)) @@ -119,60 +118,57 @@ class _InvitationSelectionState extends State { @override Widget build(BuildContext context) { - final groupName = widget.room.name?.isEmpty ?? false - ? L10n.of(context).group - : widget.room.name; - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(activeChat: widget.room.id), - secondScaffold: Scaffold( - appBar: AppBar( - titleSpacing: 0, - title: DefaultAppBarSearchField( - autofocus: true, - hintText: L10n.of(context).inviteContactToGroup(groupName), - onChanged: (String text) => searchUserWithCoolDown(context, text), - ), - ), - body: foundProfiles.isNotEmpty - ? ListView.builder( - itemCount: foundProfiles.length, + room ??= Matrix.of(context).client.getRoomById(widget.roomId); + final groupName = + room.name?.isEmpty ?? false ? L10n.of(context).group : room.name; + return Scaffold( + appBar: AppBar( + titleSpacing: 0, + title: DefaultAppBarSearchField( + autofocus: true, + hintText: L10n.of(context).inviteContactToGroup(groupName), + onChanged: (String text) => searchUserWithCoolDown(context, text), + ), + ), + body: foundProfiles.isNotEmpty + ? ListView.builder( + itemCount: foundProfiles.length, + itemBuilder: (BuildContext context, int i) => ListTile( + leading: Avatar( + foundProfiles[i].avatarUrl, + foundProfiles[i].displayname ?? foundProfiles[i].userId, + ), + title: Text( + foundProfiles[i].displayname ?? + foundProfiles[i].userId.localpart, + ), + subtitle: Text(foundProfiles[i].userId), + onTap: () => inviteAction(context, foundProfiles[i].userId), + ), + ) + : FutureBuilder>( + future: getContacts(context), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator(), + ); + } + var contacts = snapshot.data; + return ListView.builder( + itemCount: contacts.length, itemBuilder: (BuildContext context, int i) => ListTile( leading: Avatar( - foundProfiles[i].avatarUrl, - foundProfiles[i].displayname ?? foundProfiles[i].userId, + contacts[i].avatarUrl, + contacts[i].calcDisplayname(), ), - title: Text( - foundProfiles[i].displayname ?? - foundProfiles[i].userId.localpart, - ), - subtitle: Text(foundProfiles[i].userId), - onTap: () => inviteAction(context, foundProfiles[i].userId), + title: Text(contacts[i].calcDisplayname()), + subtitle: Text(contacts[i].id), + onTap: () => inviteAction(context, contacts[i].id), ), - ) - : FutureBuilder>( - future: getContacts(context), - builder: (BuildContext context, snapshot) { - if (!snapshot.hasData) { - return Center( - child: CircularProgressIndicator(), - ); - } - var contacts = snapshot.data; - return ListView.builder( - itemCount: contacts.length, - itemBuilder: (BuildContext context, int i) => ListTile( - leading: Avatar( - contacts[i].avatarUrl, - contacts[i].calcDisplayname(), - ), - title: Text(contacts[i].calcDisplayname()), - subtitle: Text(contacts[i].id), - onTap: () => inviteAction(context, contacts[i].id), - ), - ); - }, - )), + ); + }, + ), ); } } diff --git a/lib/views/loading_view.dart b/lib/views/loading_view.dart new file mode 100644 index 00000000..dc7cd00d --- /dev/null +++ b/lib/views/loading_view.dart @@ -0,0 +1,38 @@ +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/components/matrix.dart'; +import 'package:flushbar/flushbar_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import 'chat_list.dart'; +import 'homeserver_picker.dart'; + +class LoadingView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: Matrix.of(context).client.onLoginStateChanged.stream.first, + builder: (context, snapshot) { + if (snapshot.hasError) { + WidgetsBinding.instance + .addPostFrameCallback((_) => FlushbarHelper.createError( + title: L10n.of(context).oopsSomethingWentWrong, + message: snapshot.error.toString(), + ).show(context)); + return HomeserverPicker(); + } + if (!snapshot.hasData) { + return Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } + if (Matrix.of(context).client.isLogged()) { + return ChatList(); + } + return HomeserverPicker(); + }, + ); + } +} diff --git a/lib/views/login.dart b/lib/views/login.dart index 5521d701..8883e862 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -5,15 +5,10 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/utils/firebase_controller.dart'; -import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'chat_list.dart'; - class Login extends StatefulWidget { @override _LoginState createState() => _LoginState(); @@ -57,14 +52,8 @@ class _LoginState extends State { setState(() => passwordError = exception.toString()); return setState(() => loading = false); } - await FirebaseController.setupFirebase( - matrix, - matrix.clientName, - ).catchError(SentryController.captureException); - setState(() => loading = false); - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute(context, ChatListView()), (r) => false); + if (mounted) setState(() => loading = false); } Timer _coolDown; diff --git a/lib/views/new_group.dart b/lib/views/new_group.dart index eb634a0b..6e823571 100644 --- a/lib/views/new_group.dart +++ b/lib/views/new_group.dart @@ -1,33 +1,16 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart' as sdk; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:pedantic/pedantic.dart'; -import 'chat.dart'; -import 'chat_list.dart'; -import 'invitation_selection.dart'; - -class NewGroupView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: _NewGroup(), - ); - } -} - -class _NewGroup extends StatefulWidget { +class NewGroup extends StatefulWidget { @override _NewGroupState createState() => _NewGroupState(); } -class _NewGroupState extends State<_NewGroup> { +class _NewGroupState extends State { TextEditingController controller = TextEditingController(); bool publicGroup = false; @@ -45,24 +28,11 @@ class _NewGroupState extends State<_NewGroup> { name: controller.text.isNotEmpty ? controller.text : null, ), ); - Navigator.of(context).pop(); + AdaptivePageLayout.of(context).popUntilIsFirst(); if (roomID != null) { - unawaited( - Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatView(roomID.result), - ), - ), - ); - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => InvitationSelection( - matrix.client.getRoomById(roomID.result), - ), - ), - ); + await AdaptivePageLayout.of(context).pushNamed('/rooms/${roomID.result}'); + await AdaptivePageLayout.of(context) + .pushNamed('/rooms/${roomID.result}/invite'); } } diff --git a/lib/views/new_private_chat.dart b/lib/views/new_private_chat.dart index c23b2319..52edd788 100644 --- a/lib/views/new_private_chat.dart +++ b/lib/views/new_private_chat.dart @@ -1,35 +1,20 @@ import 'dart:async'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'chat.dart'; -import 'chat_list.dart'; - -class NewPrivateChatView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: _NewPrivateChat(), - ); - } -} - -class _NewPrivateChat extends StatefulWidget { +class NewPrivateChat extends StatefulWidget { @override _NewPrivateChatState createState() => _NewPrivateChatState(); } -class _NewPrivateChatState extends State<_NewPrivateChat> { +class _NewPrivateChatState extends State { TextEditingController controller = TextEditingController(); final _formKey = GlobalKey(); bool loading = false; @@ -59,15 +44,10 @@ class _NewPrivateChatState extends State<_NewPrivateChat> { context: context, future: () => user.startDirectChat(), ); - Navigator.of(context).pop(); if (roomID.error == null) { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - ChatView(roomID.result), - ), - ); + await AdaptivePageLayout.of(context) + .popAndPushNamed('/rooms/${roomID.result}'); } } diff --git a/lib/views/settings.dart b/lib/views/settings.dart index 07b6c5f6..d10cdcdf 100644 --- a/lib/views/settings.dart +++ b/lib/views/settings.dart @@ -1,9 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:fluffychat/components/dialogs/bootstrap_dialog.dart'; -import 'package:fluffychat/views/log_view.dart'; -import 'package:fluffychat/views/settings_3pid.dart'; -import 'package:fluffychat/views/settings_notifications.dart'; -import 'package:fluffychat/views/settings_style.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; @@ -12,33 +9,16 @@ import 'package:fluffychat/utils/beautify_string_extension.dart'; import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/sentry_controller.dart'; -import 'package:fluffychat/views/settings_devices.dart'; -import 'package:fluffychat/views/settings_ignore_list.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:image_picker/image_picker.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../components/adaptive_page_layout.dart'; import '../components/content_banner.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import '../components/matrix.dart'; -import '../utils/app_route.dart'; import '../app_config.dart'; import '../config/setting_keys.dart'; -import 'chat_list.dart'; -import 'settings_emotes.dart'; - -class SettingsView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: Settings(), - ); - } -} class Settings extends StatefulWidget { @override @@ -333,12 +313,8 @@ class _SettingsState extends State { ListTile( trailing: Icon(Icons.notifications_outlined), title: Text(L10n.of(context).notifications), - onTap: () async => await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - SettingsNotificationsView(), - ), - ), + onTap: () => AdaptivePageLayout.of(context) + .pushNamed('/settings/notifications'), ), ListTile( title: Text( @@ -351,12 +327,8 @@ class _SettingsState extends State { ), ListTile( title: Text(L10n.of(context).changeTheme), - onTap: () async => await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - SettingsStyleView(), - ), - ), + onTap: () => + AdaptivePageLayout.of(context).pushNamed('/settings/style'), trailing: Icon(Icons.style_outlined), ), SwitchListTile( @@ -392,12 +364,8 @@ class _SettingsState extends State { ), ListTile( title: Text(L10n.of(context).emoteSettings), - onTap: () async => await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - EmotesSettingsView(), - ), - ), + onTap: () => + AdaptivePageLayout.of(context).pushNamed('/settings/emotes'), trailing: Icon(Icons.insert_emoticon_outlined), ), Divider(thickness: 1), @@ -425,22 +393,14 @@ class _SettingsState extends State { ListTile( trailing: Icon(Icons.devices_other_outlined), title: Text(L10n.of(context).devices), - onTap: () async => await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - DevicesSettingsView(), - ), - ), + onTap: () => + AdaptivePageLayout.of(context).pushNamed('/settings/devices'), ), ListTile( trailing: Icon(Icons.block_outlined), title: Text(L10n.of(context).ignoredUsers), - onTap: () async => await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - SettingsIgnoreListView(), - ), - ), + onTap: () => + AdaptivePageLayout.of(context).pushNamed('/settings/ignore'), ), ListTile( trailing: Icon(Icons.bug_report_outlined), @@ -458,12 +418,8 @@ class _SettingsState extends State { ListTile( trailing: Icon(Icons.email_outlined), title: Text(L10n.of(context).passwordRecovery), - onTap: () => Navigator.of(context).push( - AppRoute.defaultRoute( - context, - Settings3PidView(), - ), - ), + onTap: () => + AdaptivePageLayout.of(context).pushNamed('/settings/3pid'), ), ListTile( trailing: Icon(Icons.exit_to_app_outlined), @@ -607,9 +563,7 @@ class _SettingsState extends State { fontWeight: FontWeight.bold, ), ), - onTap: () => Navigator.of(context).push( - AppRoute.defaultRoute(context, LogViewer()), - ), + onTap: () => AdaptivePageLayout.of(context).pushNamed('/logs'), ), ListTile( trailing: Icon(Icons.help_outlined), diff --git a/lib/views/settings_3pid.dart b/lib/views/settings_3pid.dart index 96c9040c..fe2a65b9 100644 --- a/lib/views/settings_3pid.dart +++ b/lib/views/settings_3pid.dart @@ -1,24 +1,10 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'chat_list.dart'; - -class Settings3PidView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: Settings3Pid(), - ); - } -} - class Settings3Pid extends StatefulWidget { static int sendAttempt = 0; diff --git a/lib/views/settings_devices.dart b/lib/views/settings_devices.dart index b19e0681..5bf6c63a 100644 --- a/lib/views/settings_devices.dart +++ b/lib/views/settings_devices.dart @@ -4,21 +4,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import '../components/adaptive_page_layout.dart'; import '../components/matrix.dart'; import '../utils/date_time_extension.dart'; -import 'chat_list.dart'; - -class DevicesSettingsView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: DevicesSettings(), - ); - } -} class DevicesSettings extends StatefulWidget { @override diff --git a/lib/views/settings_emotes.dart b/lib/views/settings_emotes.dart index a5f1f4e3..15ac32ea 100644 --- a/lib/views/settings_emotes.dart +++ b/lib/views/settings_emotes.dart @@ -10,26 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:image_picker/image_picker.dart'; -import '../components/adaptive_page_layout.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import '../components/matrix.dart'; -import 'chat_list.dart'; - -class EmotesSettingsView extends StatelessWidget { - final Room room; - final String stateKey; - - EmotesSettingsView({this.room, this.stateKey}); - - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: EmotesSettings(room: room, stateKey: stateKey), - ); - } -} class EmotesSettings extends StatefulWidget { final Room room; diff --git a/lib/views/settings_ignore_list.dart b/lib/views/settings_ignore_list.dart index 94b08360..887d8592 100644 --- a/lib/views/settings_ignore_list.dart +++ b/lib/views/settings_ignore_list.dart @@ -1,23 +1,10 @@ import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/components/avatar.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../components/matrix.dart'; -import 'chat_list.dart'; - -class SettingsIgnoreListView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: SettingsIgnoreList(), - ); - } -} class SettingsIgnoreList extends StatelessWidget { final controller = TextEditingController(); diff --git a/lib/views/settings_multiple_emotes.dart b/lib/views/settings_multiple_emotes.dart index 76aa358c..80d6fbd9 100644 --- a/lib/views/settings_multiple_emotes.dart +++ b/lib/views/settings_multiple_emotes.dart @@ -1,33 +1,17 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; +import 'package:fluffychat/components/matrix.dart'; import 'package:flutter/material.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import '../components/adaptive_page_layout.dart'; -import '../utils/app_route.dart'; -import 'chat_list.dart'; -import 'settings_emotes.dart'; - -class MultipleEmotesSettingsView extends StatelessWidget { - final Room room; - - MultipleEmotesSettingsView({this.room}); - - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: MultipleEmotesSettings(room: room), - ); - } -} class MultipleEmotesSettings extends StatelessWidget { - final Room room; + final String roomId; - MultipleEmotesSettings({this.room}); + MultipleEmotesSettings(this.roomId, {Key key}) : super(key: key); @override Widget build(BuildContext context) { + final room = Matrix.of(context).client.getRoomById(roomId); return Scaffold( appBar: AppBar( title: Text(L10n.of(context).emotePacks), @@ -58,11 +42,9 @@ class MultipleEmotesSettings extends StatelessWidget { return ListTile( title: Text(packName), onTap: () async { - await Navigator.of(context).push( - AppRoute.defaultRoute( - context, - EmotesSettingsView(room: room, stateKey: keys[i]), - ), + await AdaptivePageLayout.of(context).pushNamed( + '/settings/emotes', + arguments: room, ); }, ); diff --git a/lib/views/settings_notifications.dart b/lib/views/settings_notifications.dart index 64b11ce4..f3238484 100644 --- a/lib/views/settings_notifications.dart +++ b/lib/views/settings_notifications.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/app_config.dart'; import 'package:flutter/foundation.dart'; @@ -11,7 +10,6 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:open_noti_settings/open_noti_settings.dart'; import '../components/matrix.dart'; -import 'chat_list.dart'; class NotificationSettingsItem { final PushRuleKind type; @@ -20,17 +18,6 @@ class NotificationSettingsItem { NotificationSettingsItem(this.type, this.key, this.title); } -class SettingsNotificationsView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: SettingsNotifications(), - ); - } -} - class SettingsNotifications extends StatelessWidget { static List items = [ NotificationSettingsItem( diff --git a/lib/views/settings_style.dart b/lib/views/settings_style.dart index ab40ada0..a18ae6aa 100644 --- a/lib/views/settings_style.dart +++ b/lib/views/settings_style.dart @@ -1,25 +1,12 @@ import 'dart:io'; import 'package:adaptive_theme/adaptive_theme.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:image_picker/image_picker.dart'; import '../components/matrix.dart'; -import 'chat_list.dart'; - -class SettingsStyleView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AdaptivePageLayout( - primaryPage: FocusPage.SECOND, - firstScaffold: ChatList(), - secondScaffold: SettingsStyle(), - ); - } -} class SettingsStyle extends StatefulWidget { @override diff --git a/lib/views/sign_up.dart b/lib/views/sign_up.dart index 6637a0ab..18366b1c 100644 --- a/lib/views/sign_up.dart +++ b/lib/views/sign_up.dart @@ -1,12 +1,10 @@ import 'dart:math'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/login.dart'; -import 'package:fluffychat/views/sign_up_password.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -61,11 +59,9 @@ class _SignUpState extends State { return setState(() => loading = false); } setState(() => loading = false); - await Navigator.of(context).push( - AppRoute( - SignUpPassword(preferredUsername, - avatar: avatar, displayname: usernameController.text), - ), + await AdaptivePageLayout.of(context).pushNamed( + '/signup/password/${Uri.encodeComponent(preferredUsername)}/${Uri.encodeComponent(usernameController.text)}', + arguments: avatar, ); } @@ -171,9 +167,8 @@ class _SignUpState extends State { fontSize: 16, ), ), - onPressed: () => Navigator.of(context).push( - AppRoute(Login()), - ), + onPressed: () => + AdaptivePageLayout.of(context).pushNamed('/login'), ), ), ]), diff --git a/lib/views/sign_up_password.dart b/lib/views/sign_up_password.dart index a06492ac..4cb61494 100644 --- a/lib/views/sign_up_password.dart +++ b/lib/views/sign_up_password.dart @@ -1,16 +1,13 @@ import 'dart:math'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/auth_web_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'chat_list.dart'; - class SignUpPassword extends StatefulWidget { final MatrixFile avatar; final String username; @@ -69,17 +66,11 @@ class _SignUpPasswordState extends State { ), ); } else { - await Navigator.of(context).push( - AppRoute.defaultRoute( + await AdaptivePageLayout.of(context).pushNamed( + '/authwebview', + arguments: () => _signUpAction( context, - AuthWebView( - currentStage, - exception.session, - () => _signUpAction( - context, - auth: AuthenticationData(session: exception.session), - ), - ), + auth: AuthenticationData(session: exception.session), ), ); return; @@ -111,9 +102,7 @@ class _SignUpPasswordState extends State { .show(context); } } - await Navigator.of(context).pushAndRemoveUntil( - AppRoute.defaultRoute(context, ChatListView()), (r) => false); - setState(() => loading = false); + if (mounted) setState(() => loading = false); } @override