refactor: Migrate routes to go router

This commit is contained in:
krille-chan 2023-08-07 18:40:02 +02:00
parent 739edde729
commit ee957ab1f6
No known key found for this signature in database
52 changed files with 602 additions and 630 deletions

View file

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/add_story/add_story.dart'; import 'package:fluffychat/pages/add_story/add_story.dart';
import 'package:fluffychat/pages/archive/archive.dart'; import 'package:fluffychat/pages/archive/archive.dart';
import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat.dart';
@ -28,351 +30,402 @@ import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pages/story/story_page.dart'; import 'package:fluffychat/pages/story/story_page.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/loading_view.dart';
import 'package:fluffychat/widgets/layouts/side_view_layout.dart'; import 'package:fluffychat/widgets/layouts/side_view_layout.dart';
import 'package:fluffychat/widgets/layouts/two_column_layout.dart'; import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
import 'package:fluffychat/widgets/log_view.dart'; import 'package:fluffychat/widgets/log_view.dart';
class AppRoutes { class AppRoutes {
final bool columnMode; final List<Client> clients;
AppRoutes(this.columnMode); bool get isLoggedIn => clients.any((client) => client.isLogged());
List<VRouteElement> get routes => [ AppRoutes(this.clients);
..._homeRoutes,
if (columnMode) ..._tabletRoutes,
if (!columnMode) ..._mobileRoutes,
];
List<VRouteElement> get _mobileRoutes => [ List<RouteBase> get routes => [
VWidget( GoRoute(
path: '/rooms', path: '/',
widget: const ChatList(), redirect: (context, state) => isLoggedIn ? '/rooms' : '/home',
stackedRoutes: [
VWidget(
path: '/stories/create',
widget: const AddStoryPage(),
),
VWidget(
path: '/stories/:roomid',
widget: const StoryPage(),
stackedRoutes: [
VWidget(
path: 'share',
widget: const AddStoryPage(),
),
],
),
VWidget(
path: '/spaces/:roomid',
widget: const ChatDetails(),
stackedRoutes: _chatDetailsRoutes,
),
VWidget(
path: ':roomid',
widget: const ChatPage(),
stackedRoutes: [
VWidget(
path: 'encryption',
widget: const ChatEncryptionSettings(),
),
VWidget(
path: 'invite',
widget: const InvitationSelection(),
),
VWidget(
path: 'details',
widget: const ChatDetails(),
stackedRoutes: _chatDetailsRoutes,
),
],
),
VWidget(
path: '/settings',
widget: const Settings(),
stackedRoutes: _settingsRoutes,
),
VWidget(
path: '/archive',
widget: const Archive(),
stackedRoutes: [
VWidget(
path: ':roomid',
widget: const ChatPage(),
buildTransition: _dynamicTransition,
),
],
),
VWidget(
path: '/newprivatechat',
widget: const NewPrivateChat(),
),
VWidget(
path: '/newgroup',
widget: const NewGroup(),
),
VWidget(
path: '/newspace',
widget: const NewSpace(),
),
],
), ),
]; GoRoute(
List<VRouteElement> get _tabletRoutes => [
VNester(
path: '/rooms',
widgetBuilder: (child) => TwoColumnLayout(
mainView: const ChatList(),
sideView: child,
),
buildTransition: _fadeTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const EmptyPage(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: '/stories/create',
buildTransition: _fadeTransition,
widget: const AddStoryPage(),
),
VWidget(
path: '/stories/:roomid',
buildTransition: _fadeTransition,
widget: const StoryPage(),
stackedRoutes: [
VWidget(
path: 'share',
widget: const AddStoryPage(),
),
],
),
VWidget(
path: '/spaces/:roomid',
widget: const ChatDetails(),
buildTransition: _fadeTransition,
stackedRoutes: _chatDetailsRoutes,
),
VWidget(
path: '/newprivatechat',
widget: const NewPrivateChat(),
buildTransition: _fadeTransition,
),
VWidget(
path: '/newgroup',
widget: const NewGroup(),
buildTransition: _fadeTransition,
),
VWidget(
path: '/newspace',
widget: const NewSpace(),
buildTransition: _fadeTransition,
),
VNester(
path: ':roomid',
widgetBuilder: (child) => SideViewLayout(
mainView: const ChatPage(),
sideView: child,
),
buildTransition: _fadeTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const ChatPage(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'encryption',
widget: const ChatEncryptionSettings(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'details',
widget: const ChatDetails(),
buildTransition: _fadeTransition,
stackedRoutes: _chatDetailsRoutes,
),
VWidget(
path: 'invite',
widget: const InvitationSelection(),
buildTransition: _fadeTransition,
),
],
),
],
),
],
),
VWidget(
path: '/rooms',
widget: const TwoColumnLayout(
mainView: ChatList(),
sideView: EmptyPage(),
),
buildTransition: _fadeTransition,
stackedRoutes: [
VNester(
path: '/settings',
widgetBuilder: (child) => TwoColumnLayout(
mainView: const Settings(),
sideView: child,
),
buildTransition: _dynamicTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const EmptyPage(),
buildTransition: _dynamicTransition,
stackedRoutes: _settingsRoutes,
),
],
),
VNester(
path: '/archive',
widgetBuilder: (child) => TwoColumnLayout(
mainView: const Archive(),
sideView: child,
),
buildTransition: _fadeTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const EmptyPage(),
buildTransition: _dynamicTransition,
),
VWidget(
path: ':roomid',
widget: const ChatPage(),
buildTransition: _dynamicTransition,
),
],
),
],
),
];
List<VRouteElement> get _homeRoutes => [
VWidget(path: '/', widget: const LoadingView()),
VWidget(
path: '/home', path: '/home',
widget: const HomeserverPicker(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _fadeTransition, context,
stackedRoutes: [ const HomeserverPicker(),
VWidget( ),
redirect: (context, state) => isLoggedIn ? '/rooms' : null,
routes: [
GoRoute(
path: 'login', path: 'login',
widget: const Login(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _fadeTransition, context,
const Login(),
),
redirect: (context, state) => isLoggedIn ? '/rooms' : null,
), ),
VWidget( ],
path: 'logs', ),
widget: const LogViewer(), GoRoute(
buildTransition: _dynamicTransition, path: '/logs',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const LogViewer(),
),
),
ShellRoute(
pageBuilder: (context, state, child) => defaultPageBuilder(
context,
FluffyThemes.isColumnMode(context) &&
state.fullPath?.startsWith('/rooms/settings') == false
? TwoColumnLayout(
displayNavigationRail:
state.path?.startsWith('/rooms/settings') != true,
mainView: ChatList(
activeChat: state.pathParameters['roomid'],
displayNavigationRail:
state.path?.startsWith('/rooms/settings') != true,
),
sideView: child,
)
: child,
),
routes: [
GoRoute(
path: '/rooms',
redirect: (context, state) => !isLoggedIn ? '/home' : null,
pageBuilder: (context, state) => defaultPageBuilder(
context,
FluffyThemes.isColumnMode(context)
? const EmptyPage()
: ChatList(
activeChat: state.pathParameters['roomid'],
),
),
routes: [
GoRoute(
path: 'stories/create',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const AddStoryPage(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'stories/:roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const StoryPage(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
routes: [
GoRoute(
path: 'share',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const AddStoryPage(),
),
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
],
),
GoRoute(
path: 'spaces/:roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
ChatDetails(
roomId: state.pathParameters['roomid']!,
),
),
routes: _chatDetailsRoutes,
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'archive',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const Archive(),
),
routes: [
GoRoute(
path: ':roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
ChatPage(
roomId: state.pathParameters['roomid']!,
),
),
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
],
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'newprivatechat',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const NewPrivateChat(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'newgroup',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const NewGroup(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'newspace',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const NewSpace(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
),
ShellRoute(
pageBuilder: (context, state, child) => defaultPageBuilder(
context,
FluffyThemes.isColumnMode(context)
? TwoColumnLayout(
mainView: const Settings(),
sideView: child,
displayNavigationRail: false,
)
: child,
),
routes: [
GoRoute(
path: 'settings',
pageBuilder: (context, state) => defaultPageBuilder(
context,
FluffyThemes.isColumnMode(context)
? const EmptyPage()
: const Settings(),
),
routes: _settingsRoutes,
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
],
),
GoRoute(
path: ':roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
ChatPage(
roomId: state.pathParameters['roomid']!,
),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
routes: [
GoRoute(
path: 'encryption',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const ChatEncryptionSettings(),
),
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
GoRoute(
path: 'invite',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const InvitationSelection(),
),
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
ShellRoute(
pageBuilder: (context, state, child) =>
defaultPageBuilder(
context,
!FluffyThemes.isThreeColumnMode(context)
? child
: SideViewLayout(
mainView: ChatPage(
roomId: state.pathParameters['roomid']!,
),
sideView: child,
),
),
routes: [
GoRoute(
path: 'details',
pageBuilder: (context, state) => defaultPageBuilder(
context,
ChatDetails(
roomId: state.pathParameters['roomid']!,
),
),
routes: _chatDetailsRoutes,
redirect: (context, state) =>
!isLoggedIn ? '/home' : null,
),
],
),
],
),
],
), ),
], ],
), ),
]; ];
List<VRouteElement> get _chatDetailsRoutes => [ List<RouteBase> get _chatDetailsRoutes => [
VWidget( GoRoute(
path: 'permissions', path: 'permissions',
widget: const ChatPermissionsSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const ChatPermissionsSettings(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'invite', path: 'invite',
widget: const InvitationSelection(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const InvitationSelection(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'multiple_emotes', path: 'multiple_emotes',
widget: const MultipleEmotesSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const MultipleEmotesSettings(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'emotes', path: 'emotes',
widget: const EmotesSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const EmotesSettings(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'emotes/:state_key', path: 'emotes/:state_key',
widget: const EmotesSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const EmotesSettings(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
]; ];
List<VRouteElement> get _settingsRoutes => [ List<RouteBase> get _settingsRoutes => [
VWidget( GoRoute(
path: 'notifications', path: 'notifications',
widget: const SettingsNotifications(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const SettingsNotifications(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'style', path: 'style',
widget: const SettingsStyle(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const SettingsStyle(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'devices', path: 'devices',
widget: const DevicesSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const DevicesSettings(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'chat', path: 'chat',
widget: const SettingsChat(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
stackedRoutes: [ const SettingsChat(),
VWidget( ),
routes: [
GoRoute(
path: 'emotes', path: 'emotes',
widget: const EmotesSettings(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const EmotesSettings(),
),
), ),
], ],
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'addaccount', path: 'addaccount',
widget: const HomeserverPicker(), redirect: (context, state) => !isLoggedIn ? '/home' : null,
buildTransition: _fadeTransition, pageBuilder: (context, state) => defaultPageBuilder(
stackedRoutes: [ context,
VWidget( const HomeserverPicker(),
),
routes: [
GoRoute(
path: 'login', path: 'login',
widget: const Login(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _fadeTransition, context,
const Login(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
], ],
), ),
VWidget( GoRoute(
path: 'security', path: 'security',
widget: const SettingsSecurity(), redirect: (context, state) => !isLoggedIn ? '/home' : null,
buildTransition: _dynamicTransition, pageBuilder: (context, state) => defaultPageBuilder(
stackedRoutes: [ context,
VWidget( const SettingsSecurity(),
),
routes: [
GoRoute(
path: 'stories', path: 'stories',
widget: const SettingsStories(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const SettingsStories(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: 'ignorelist', path: 'ignorelist',
widget: const SettingsIgnoreList(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const SettingsIgnoreList(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
VWidget( GoRoute(
path: '3pid', path: '3pid',
widget: const Settings3Pid(), pageBuilder: (context, state) => defaultPageBuilder(
buildTransition: _dynamicTransition, context,
const Settings3Pid(),
),
redirect: (context, state) => !isLoggedIn ? '/home' : null,
), ),
], ],
), ),
VWidget(
path: 'logs',
widget: const LogViewer(),
buildTransition: _dynamicTransition,
),
]; ];
FadeTransition Function(dynamic, dynamic, dynamic)? get _dynamicTransition => Page defaultPageBuilder(BuildContext context, Widget child) =>
columnMode ? _fadeTransition : null; CustomTransitionPage(
child: child,
FadeTransition _fadeTransition(animation1, _, child) => transitionDuration: FluffyThemes.animationDuration,
FadeTransition(opacity: animation1, child: child); reverseTransitionDuration: FluffyThemes.animationDuration,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FluffyThemes.isColumnMode(context)
? FadeTransition(opacity: animation, child: child)
: CupertinoPageTransition(
primaryRouteAnimation: animation,
secondaryRouteAnimation: secondaryAnimation,
linearTransition: false,
child: child,
),
);
} }

View file

@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'app_config.dart'; import 'app_config.dart';
@ -17,8 +15,8 @@ abstract class FluffyThemes {
static bool isColumnMode(BuildContext context) => static bool isColumnMode(BuildContext context) =>
isColumnModeByWidth(MediaQuery.of(context).size.width); isColumnModeByWidth(MediaQuery.of(context).size.width);
static bool getDisplayNavigationRail(BuildContext context) => static bool isThreeColumnMode(BuildContext context) =>
!VRouter.of(context).path.startsWith('/settings'); MediaQuery.of(context).size.width > FluffyThemes.columnWidth * 3.5;
static const fallbackTextStyle = TextStyle( static const fallbackTextStyle = TextStyle(
fontFamily: 'Roboto', fontFamily: 'Roboto',

View file

@ -1,18 +1,20 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter_app_lock/flutter_app_lock.dart'; import 'package:flutter_app_lock/flutter_app_lock.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'config/setting_keys.dart';
import 'utils/background_push.dart'; import 'utils/background_push.dart';
import 'widgets/fluffy_chat_app.dart'; import 'widgets/fluffy_chat_app.dart';
import 'widgets/lock_screen.dart'; import 'widgets/lock_screen.dart';
void main() async { void main() async {
Logs().i('Welcome to FluffyChat');
// Our background push shared isolate accesses flutter-internal things very early in the startup proccess // Our background push shared isolate accesses flutter-internal things very early in the startup proccess
// To make sure that the parts of flutter needed are started up already, we need to ensure that the // To make sure that the parts of flutter needed are started up already, we need to ensure that the
// widget bindings are initialized already. // widget bindings are initialized already.
@ -26,29 +28,24 @@ void main() async {
await firstClient?.roomsLoading; await firstClient?.roomsLoading;
await firstClient?.accountDataLoading; await firstClient?.accountDataLoading;
String? pin;
if (PlatformInfos.isMobile) { if (PlatformInfos.isMobile) {
BackgroundPush.clientOnly(clients.first); BackgroundPush.clientOnly(clients.first);
} try {
pin =
final queryParameters = <String, String>{}; await const FlutterSecureStorage().read(key: SettingKeys.appLockKey);
if (kIsWeb) { } catch (e, s) {
queryParameters Logs().d('Unable to read PIN from Secure storage', e, s);
.addAll(Uri.parse(html.window.location.href).queryParameters); }
} }
runApp( runApp(
PlatformInfos.isMobile PlatformInfos.isMobile
? AppLock( ? AppLock(
builder: (args) => FluffyChatApp( builder: (args) => FluffyChatApp(clients: clients),
clients: clients,
queryParameters: queryParameters,
),
lockScreen: const LockScreen(), lockScreen: const LockScreen(),
enabled: false, enabled: pin?.isNotEmpty ?? false,
) )
: FluffyChatApp( : FluffyChatApp(clients: clients),
clients: clients,
queryParameters: queryParameters,
),
); );
} }

View file

@ -207,7 +207,7 @@ class AddStoryController extends State<AddStoryPage> {
}, },
); );
if (postResult.error == null) { if (postResult.error == null) {
VRouter.of(context).pop(); context.pop();
} }
} }

View file

@ -38,14 +38,17 @@ import 'sticker_picker_dialog.dart';
class ChatPage extends StatelessWidget { class ChatPage extends StatelessWidget {
final Widget? sideView; final Widget? sideView;
final String roomId;
const ChatPage({Key? key, this.sideView}) : super(key: key); const ChatPage({
Key? key,
this.sideView,
required this.roomId,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final roomId = context.vRouter.pathParameters['roomid']; final room = Matrix.of(context).client.getRoomById(roomId);
final room =
roomId == null ? null : Matrix.of(context).client.getRoomById(roomId);
if (room == null) { if (room == null) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(L10n.of(context)!.oopsSomethingWentWrong)), appBar: AppBar(title: Text(L10n.of(context)!.oopsSomethingWentWrong)),
@ -58,7 +61,11 @@ class ChatPage extends StatelessWidget {
), ),
); );
} }
return ChatPageWithRoom(sideView: sideView, room: room); return ChatPageWithRoom(
key: Key('chat_page_$roomId'),
sideView: sideView,
room: room,
);
} }
} }
@ -188,7 +195,7 @@ class ChatController extends State<ChatPageWithRoom> {
); );
final roomId = success.result; final roomId = success.result;
if (roomId == null) return; if (roomId == null) return;
VRouter.of(context).toSegments(['rooms', roomId]); context.go(['', 'rooms', roomId].join('/'));
} }
void leaveChat() async { void leaveChat() async {
@ -197,7 +204,7 @@ class ChatController extends State<ChatPageWithRoom> {
future: room.leave, future: room.leave,
); );
if (success.error != null) return; if (success.error != null) return;
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard; EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
@ -329,7 +336,7 @@ class ChatController extends State<ChatPageWithRoom> {
// "load more" button is visible on the screen // "load more" button is visible on the screen
SchedulerBinding.instance.addPostFrameCallback((_) async { SchedulerBinding.instance.addPostFrameCallback((_) async {
if (mounted) { if (mounted) {
final event = VRouter.of(context).queryParameters['event']; final event = GoRouterState.of(context).uri.queryParameters['event'];
if (event != null) { if (event != null) {
scrollToEventId(event); scrollToEventId(event);
} }
@ -803,7 +810,7 @@ class ChatController extends State<ChatPageWithRoom> {
}; };
} }
setState(() => selectedEvents.clear()); setState(() => selectedEvents.clear());
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
void sendAgainAction() { void sendAgainAction() {
@ -901,7 +908,7 @@ class ChatController extends State<ChatPageWithRoom> {
future: room.forget, future: room.forget,
); );
if (result.error != null) return; if (result.error != null) return;
VRouter.of(context).to('/archive'); context.go('/rooms/archive');
} }
void typeEmoji(Emoji? emoji) { void typeEmoji(Emoji? emoji) {
@ -1017,7 +1024,7 @@ class ChatController extends State<ChatPageWithRoom> {
future: room.leave, future: room.leave,
); );
if (result.error == null) { if (result.error == null) {
VRouter.of(context).toSegments(['rooms', result.result!]); context.go(['', 'rooms', result.result!].join('/'));
} }
} }

View file

@ -36,8 +36,7 @@ class ChatAppBarTitle extends StatelessWidget {
) )
: controller.isArchived : controller.isArchived
? null ? null
: () => : () => context.go(['', 'rooms', room.id, 'details'].join('/')),
VRouter.of(context).toSegments(['rooms', room.id, 'details']),
child: Row( child: Row(
children: [ children: [
Hero( Hero(

View file

@ -4,7 +4,6 @@ import 'package:badges/badges.dart';
import 'package:desktop_drop/desktop_drop.dart'; import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
@ -147,15 +146,16 @@ class ChatView extends StatelessWidget {
} }
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0; final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
return VWidgetGuard( return WillPopScope(
onSystemPop: (redirector) async { onWillPop: () async {
if (controller.selectedEvents.isNotEmpty) { if (controller.selectedEvents.isNotEmpty) {
controller.clearSelectedEvents(); controller.clearSelectedEvents();
redirector.stopRedirection(); return false;
} else if (controller.showEmojiPicker) { } else if (controller.showEmojiPicker) {
controller.emojiPickerAction(); controller.emojiPickerAction();
redirector.stopRedirection(); return false;
} }
return true;
}, },
child: GestureDetector( child: GestureDetector(
onTapDown: (_) => controller.setReadMarker(), onTapDown: (_) => controller.setReadMarker(),

View file

@ -38,8 +38,8 @@ class EncryptionButton extends StatelessWidget {
? Colors.orange ? Colors.orange
: null, : null,
), ),
onPressed: () => VRouter.of(context) onPressed: () =>
.toSegments(['rooms', room.id, 'encryption']), context.go(['', 'rooms', room.id, 'encryption'].join('/')),
), ),
); );
}, },

View file

@ -19,7 +19,12 @@ import 'package:fluffychat/widgets/matrix.dart';
enum AliasActions { copy, delete, setCanonical } enum AliasActions { copy, delete, setCanonical }
class ChatDetails extends StatefulWidget { class ChatDetails extends StatefulWidget {
const ChatDetails({Key? key}) : super(key: key); final String roomId;
const ChatDetails({
Key? key,
required this.roomId,
}) : super(key: key);
@override @override
ChatDetailsController createState() => ChatDetailsController(); ChatDetailsController createState() => ChatDetailsController();
@ -32,7 +37,7 @@ class ChatDetailsController extends State<ChatDetails> {
void toggleDisplaySettings() => void toggleDisplaySettings() =>
setState(() => displaySettings = !displaySettings); setState(() => displaySettings = !displaySettings);
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => widget.roomId;
void setDisplaynameAction() async { void setDisplaynameAction() async {
final room = Matrix.of(context).client.getRoomById(roomId!)!; final room = Matrix.of(context).client.getRoomById(roomId!)!;
@ -257,9 +262,9 @@ class ChatDetailsController extends State<ChatDetails> {
if ((room.states['im.ponies.room_emotes'] ?? <String, Event>{}) if ((room.states['im.ponies.room_emotes'] ?? <String, Event>{})
.keys .keys
.any((String s) => s.isNotEmpty)) { .any((String s) => s.isNotEmpty)) {
VRouter.of(context).to('multiple_emotes'); context.go('/rooms/${room.id}/details/multiple_emotes');
} else { } else {
VRouter.of(context).to('emotes'); context.go('/rooms/${room.id}/details/emotes');
} }
} }

View file

@ -53,10 +53,11 @@ class ChatDetailsView extends StatelessWidget {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close_outlined), icon: const Icon(Icons.close_outlined),
onPressed: () => onPressed: () =>
VRouter.of(context).path.startsWith('/spaces/') GoRouterState.of(context).uri.path.startsWith('/spaces/')
? VRouter.of(context).pop() ? context.pop()
: VRouter.of(context) : context.go(
.toSegments(['rooms', controller.roomId!]), ['', 'rooms', controller.roomId!].join('/'),
),
), ),
elevation: Theme.of(context).appBarTheme.elevation, elevation: Theme.of(context).appBarTheme.elevation,
expandedHeight: 300.0, expandedHeight: 300.0,
@ -380,8 +381,8 @@ class ChatDetailsView extends StatelessWidget {
Icons.edit_attributes_outlined, Icons.edit_attributes_outlined,
), ),
), ),
onTap: () => onTap: () => context
VRouter.of(context).to('permissions'), .go('/rooms/${room.id}/details/permissions'),
), ),
], ],
const Divider(height: 1), const Divider(height: 1),
@ -408,7 +409,8 @@ class ChatDetailsView extends StatelessWidget {
radius: Avatar.defaultSize / 2, radius: Avatar.defaultSize / 2,
child: const Icon(Icons.add_outlined), child: const Icon(Icons.add_outlined),
), ),
onTap: () => VRouter.of(context).to('invite'), onTap: () =>
context.go('/rooms/${room.id}/invite'),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
], ],

View file

@ -20,7 +20,7 @@ class ChatEncryptionSettings extends StatefulWidget {
} }
class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> { class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
Room get room => Matrix.of(context).client.getRoomById(roomId!)!; Room get room => Matrix.of(context).client.getRoomById(roomId!)!;

View file

@ -28,7 +28,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close_outlined), icon: const Icon(Icons.close_outlined),
onPressed: () => onPressed: () =>
VRouter.of(context).toSegments(['rooms', controller.roomId!]), context.go(['', 'rooms', controller.roomId!].join('/')),
), ),
title: Text(L10n.of(context)!.encryption), title: Text(L10n.of(context)!.encryption),
actions: [ actions: [

View file

@ -56,8 +56,14 @@ enum ActiveFilter {
class ChatList extends StatefulWidget { class ChatList extends StatefulWidget {
static BuildContext? contextForVoip; static BuildContext? contextForVoip;
final bool displayNavigationRail;
final String? activeChat;
const ChatList({Key? key}) : super(key: key); const ChatList({
Key? key,
this.displayNavigationRail = false,
required this.activeChat,
}) : super(key: key);
@override @override
ChatListController createState() => ChatListController(); ChatListController createState() => ChatListController();
@ -259,7 +265,7 @@ class ChatListController extends State<ChatList>
Stream<Client> get clientStream => _clientStream.stream; Stream<Client> get clientStream => _clientStream.stream;
void addAccountAction() => VRouter.of(context).to('/settings/account'); void addAccountAction() => context.go('/rooms/settings/account');
void _onScroll() { void _onScroll() {
final newScrolledToTop = scrollController.position.pixels <= 0; final newScrolledToTop = scrollController.position.pixels <= 0;
@ -271,7 +277,7 @@ class ChatListController extends State<ChatList>
void editSpace(BuildContext context, String spaceId) async { void editSpace(BuildContext context, String spaceId) async {
await Matrix.of(context).client.getRoomById(spaceId)!.postLoad(); await Matrix.of(context).client.getRoomById(spaceId)!.postLoad();
if (mounted) { if (mounted) {
VRouter.of(context).toSegments(['spaces', spaceId]); context.go('/rooms/spaces/$spaceId');
} }
} }
@ -281,7 +287,7 @@ class ChatListController extends State<ChatList>
final selectedRoomIds = <String>{}; final selectedRoomIds = <String>{};
String? get activeChat => VRouter.of(context).pathParameters['roomid']; String? get activeChat => widget.activeChat;
SelectMode get selectMode => Matrix.of(context).shareContent != null SelectMode get selectMode => Matrix.of(context).shareContent != null
? SelectMode.share ? SelectMode.share
@ -300,7 +306,7 @@ class ChatListController extends State<ChatList>
name: file.path, name: file.path,
).detectFileType, ).detectFileType,
}; };
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
void _processIncomingSharedText(String? text) { void _processIncomingSharedText(String? text) {
@ -315,12 +321,12 @@ class ChatListController extends State<ChatList>
'msgtype': 'm.text', 'msgtype': 'm.text',
'body': text, 'body': text,
}; };
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
void _processIncomingUris(String? text) async { void _processIncomingUris(String? text) async {
if (text == null) return; if (text == null) return;
VRouter.of(context).to('/rooms'); context.go('/rooms');
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
UrlLauncher(context, text).openMatrixToUrl(); UrlLauncher(context, text).openMatrixToUrl();
}); });
@ -582,7 +588,7 @@ class ChatListController extends State<ChatList>
} }
void setActiveClient(Client client) { void setActiveClient(Client client) {
VRouter.of(context).to('/rooms'); context.go('/rooms');
setState(() { setState(() {
activeFilter = AppConfig.separateChatTypes activeFilter = AppConfig.separateChatTypes
? ActiveFilter.messages ? ActiveFilter.messages
@ -595,7 +601,7 @@ class ChatListController extends State<ChatList>
} }
void setActiveBundle(String bundle) { void setActiveBundle(String bundle) {
VRouter.of(context).to('/rooms'); context.go('/rooms');
setState(() { setState(() {
selectedRoomIds.clear(); selectedRoomIds.clear();
Matrix.of(context).activeBundle = bundle; Matrix.of(context).activeBundle = bundle;

View file

@ -61,7 +61,7 @@ class ChatListItem extends StatelessWidget {
} }
if (room.membership == Membership.leave) { if (room.membership == Membership.leave) {
VRouter.of(context).toSegments(['archive', room.id]); context.go(['', 'archive', room.id].join('/'));
} }
if (room.membership == Membership.join) { if (room.membership == Membership.join) {
@ -86,7 +86,7 @@ class ChatListItem extends StatelessWidget {
Matrix.of(context).shareContent = null; Matrix.of(context).shareContent = null;
} }
VRouter.of(context).toSegments(['rooms', room.id]); context.go(['', 'rooms', room.id].join('/'));
} }
} }

View file

@ -86,13 +86,12 @@ class ChatListView extends StatelessWidget {
stream: Matrix.of(context).onShareContentChanged.stream, stream: Matrix.of(context).onShareContentChanged.stream,
builder: (_, __) { builder: (_, __) {
final selectMode = controller.selectMode; final selectMode = controller.selectMode;
return VWidgetGuard( return WillPopScope(
onSystemPop: (redirector) async { onWillPop: () async {
final selMode = controller.selectMode; final selMode = controller.selectMode;
if (selMode != SelectMode.normal) { if (selMode != SelectMode.normal) {
controller.cancelAction(); controller.cancelAction();
redirector.stopRedirection(); return false;
return;
} }
if (controller.activeFilter != if (controller.activeFilter !=
(AppConfig.separateChatTypes (AppConfig.separateChatTypes
@ -100,14 +99,14 @@ class ChatListView extends StatelessWidget {
: ActiveFilter.allChats)) { : ActiveFilter.allChats)) {
controller controller
.onDestinationSelected(AppConfig.separateChatTypes ? 1 : 0); .onDestinationSelected(AppConfig.separateChatTypes ? 1 : 0);
redirector.stopRedirection(); return false;
return;
} }
return true;
}, },
child: Row( child: Row(
children: [ children: [
if (FluffyThemes.isColumnMode(context) && if (FluffyThemes.isColumnMode(context) &&
FluffyThemes.getDisplayNavigationRail(context)) ...[ controller.widget.displayNavigationRail) ...[
Builder( Builder(
builder: (context) { builder: (context) {
final allSpaces = final allSpaces =
@ -193,8 +192,7 @@ class ChatListView extends StatelessWidget {
LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.controlLeft,
LogicalKeyboardKey.keyN LogicalKeyboardKey.keyN
}, },
onKeysPressed: () => onKeysPressed: () => context.go('/rooms/newprivatechat'),
VRouter.of(context).to('/newprivatechat'),
helpLabel: L10n.of(context)!.newChat, helpLabel: L10n.of(context)!.newChat,
child: selectMode == SelectMode.normal && child: selectMode == SelectMode.normal &&
!controller.isSearchMode !controller.isSearchMode

View file

@ -261,16 +261,16 @@ class ClientChooserButton extends StatelessWidget {
cancelLabel: L10n.of(context)!.cancel, cancelLabel: L10n.of(context)!.cancel,
); );
if (consent != OkCancelResult.ok) return; if (consent != OkCancelResult.ok) return;
VRouter.of(context).to('/settings/addaccount'); context.go('/rooms/settings/addaccount');
break; break;
case SettingsAction.newStory: case SettingsAction.newStory:
VRouter.of(context).to('/stories/create'); context.go('/rooms/stories/create');
break; break;
case SettingsAction.newGroup: case SettingsAction.newGroup:
VRouter.of(context).to('/newgroup'); context.go('/rooms/newgroup');
break; break;
case SettingsAction.newSpace: case SettingsAction.newSpace:
VRouter.of(context).to('/newspace'); context.go('/rooms/newspace');
break; break;
case SettingsAction.invite: case SettingsAction.invite:
FluffyShare.share( FluffyShare.share(
@ -282,10 +282,10 @@ class ClientChooserButton extends StatelessWidget {
); );
break; break;
case SettingsAction.settings: case SettingsAction.settings:
VRouter.of(context).to('/settings'); context.go('/rooms/settings');
break; break;
case SettingsAction.archive: case SettingsAction.archive:
VRouter.of(context).to('/archive'); context.go('/rooms/archive');
break; break;
} }
} }

View file

@ -73,13 +73,13 @@ class _SpaceViewState extends State<SpaceView> {
} }
if (spaceChild.roomType == 'm.space') { if (spaceChild.roomType == 'm.space') {
if (spaceChild.roomId == widget.controller.activeSpaceId) { if (spaceChild.roomId == widget.controller.activeSpaceId) {
VRouter.of(context).toSegments(['spaces', spaceChild.roomId]); context.go('/rooms/spaces/${spaceChild.roomId}');
} else { } else {
widget.controller.setActiveSpace(spaceChild.roomId); widget.controller.setActiveSpace(spaceChild.roomId);
} }
return; return;
} }
VRouter.of(context).toSegments(['rooms', spaceChild.roomId]); context.go(['', 'rooms', spaceChild.roomId].join('/'));
} }
void _onSpaceChildContextMenu([ void _onSpaceChildContextMenu([
@ -234,13 +234,13 @@ class _SpaceViewState extends State<SpaceView> {
); );
final spaceChildren = response.rooms; final spaceChildren = response.rooms;
final canLoadMore = response.nextBatch != null; final canLoadMore = response.nextBatch != null;
return VWidgetGuard( return WillPopScope(
onSystemPop: (redirector) async { onWillPop: () async {
if (parentSpace != null) { if (parentSpace != null) {
widget.controller.setActiveSpace(parentSpace.id); widget.controller.setActiveSpace(parentSpace.id);
redirector.stopRedirection(); return false;
return;
} }
return true;
}, },
child: CustomScrollView( child: CustomScrollView(
controller: widget.scrollController, controller: widget.scrollController,

View file

@ -22,13 +22,13 @@ class StartChatFloatingActionButton extends StatelessWidget {
switch (activeFilter) { switch (activeFilter) {
case ActiveFilter.allChats: case ActiveFilter.allChats:
case ActiveFilter.messages: case ActiveFilter.messages:
VRouter.of(context).to('/newprivatechat'); context.go('/rooms/newprivatechat');
break; break;
case ActiveFilter.groups: case ActiveFilter.groups:
VRouter.of(context).to('/newgroup'); context.go('/rooms/newgroup');
break; break;
case ActiveFilter.spaces: case ActiveFilter.spaces:
VRouter.of(context).to('/newspace'); context.go('/rooms/newspace');
break; break;
} }
} }

View file

@ -23,7 +23,7 @@ class StoriesHeader extends StatelessWidget {
const StoriesHeader({required this.filter, Key? key}) : super(key: key); const StoriesHeader({required this.filter, Key? key}) : super(key: key);
void _addToStoryAction(BuildContext context) => void _addToStoryAction(BuildContext context) =>
VRouter.of(context).to('/stories/create'); context.go('/rooms/stories/create');
void _goToStoryAction(BuildContext context, String roomId) async { void _goToStoryAction(BuildContext context, String roomId) async {
final room = Matrix.of(context).client.getRoomById(roomId); final room = Matrix.of(context).client.getRoomById(roomId);
@ -35,7 +35,7 @@ class StoriesHeader extends StatelessWidget {
); );
if (result.error != null) return; if (result.error != null) return;
} }
VRouter.of(context).toSegments(['stories', roomId]); context.go(['', 'stories', roomId].join('/'));
} }
void _contextualActions(BuildContext context, Room room) async { void _contextualActions(BuildContext context, Room room) async {
@ -249,7 +249,7 @@ class _StoryButton extends StatelessWidget {
child: FloatingActionButton.small( child: FloatingActionButton.small(
heroTag: null, heroTag: null,
onPressed: () => onPressed: () =>
VRouter.of(context).to('/stories/create'), context.go('/rooms/stories/create'),
child: const Icon( child: const Icon(
Icons.add_outlined, Icons.add_outlined,
size: 16, size: 16,

View file

@ -21,7 +21,7 @@ class ChatPermissionsSettings extends StatefulWidget {
} }
class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> { class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
void editPowerLevel( void editPowerLevel(
BuildContext context, BuildContext context,
String key, String key,
@ -105,7 +105,7 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => room.client.upgradeRoom(roomId!, newVersion), future: () => room.client.upgradeRoom(roomId!, newVersion),
).then((_) => VRouter.of(context).pop()); ).then((_) => context.pop());
} }
@override @override

View file

@ -19,12 +19,12 @@ class ChatPermissionsSettingsView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: VRouter.of(context).path.startsWith('/spaces/') leading: GoRouterState.of(context).uri.path.startsWith('/spaces/')
? null ? null
: IconButton( : IconButton(
icon: const Icon(Icons.close_outlined), icon: const Icon(Icons.close_outlined),
onPressed: () => VRouter.of(context) onPressed: () =>
.toSegments(['rooms', controller.roomId!]), context.go(['', 'rooms', controller.roomId!].join('/')),
), ),
title: Text(L10n.of(context)!.editChatPermissions), title: Text(L10n.of(context)!.editChatPermissions),
), ),

View file

@ -155,7 +155,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
return list; return list;
} }
void login() => VRouter.of(context).to('login'); void login() => context.go('/home/login');
@override @override
void initState() { void initState() {

View file

@ -21,7 +21,7 @@ class ImageViewerController extends State<ImageViewer> {
/// Forward this image to another room. /// Forward this image to another room.
void forwardAction() { void forwardAction() {
Matrix.of(context).shareContent = widget.event.content; Matrix.of(context).shareContent = widget.event.content;
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
/// Save this file with a system call. /// Save this file with a system call.

View file

@ -28,7 +28,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
List<Profile> foundProfiles = []; List<Profile> foundProfiles = [];
Timer? coolDown; Timer? coolDown;
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
Future<List<User>> getContacts(BuildContext context) async { Future<List<User>> getContacts(BuildContext context) async {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;

View file

@ -20,12 +20,12 @@ class InvitationSelectionView extends StatelessWidget {
final groupName = room.name.isEmpty ? L10n.of(context)!.group : room.name; final groupName = room.name.isEmpty ? L10n.of(context)!.group : room.name;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: VRouter.of(context).path.startsWith('/spaces/') leading: GoRouterState.of(context).uri.path.startsWith('/spaces/')
? null ? null
: IconButton( : IconButton(
icon: const Icon(Icons.close_outlined), icon: const Icon(Icons.close_outlined),
onPressed: () => VRouter.of(context) onPressed: () =>
.toSegments(['rooms', controller.roomId!]), context.go(['', 'rooms', controller.roomId!].join('/')),
), ),
titleSpacing: 0, titleSpacing: 0,
title: SizedBox( title: SizedBox(

View file

@ -108,10 +108,10 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
var title = Text(L10n.of(context)!.verifyTitle); var title = Text(L10n.of(context)!.verifyTitle);
Widget body; Widget body;
final buttons = <Widget>[]; final buttons = <Widget>[];
switch (widget.request.state) { switch (widget.request.state) {
case KeyVerificationState.showQRSuccess: case KeyVerificationState.showQRSuccess:
case KeyVerificationState.confirmQRScan: case KeyVerificationState.confirmQRScan:
case KeyVerificationState.askChoice:
throw 'Not implemented'; throw 'Not implemented';
case KeyVerificationState.askSSSS: case KeyVerificationState.askSSSS:
// prompt the user for their ssss passphrase / key // prompt the user for their ssss passphrase / key
@ -200,6 +200,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
), ),
); );
break; break;
case KeyVerificationState.askChoice:
case KeyVerificationState.waitingAccept: case KeyVerificationState.waitingAccept:
body = Center( body = Center(
child: Column( child: Column(

View file

@ -37,7 +37,7 @@ class NewGroupController extends State<NewGroup> {
}, },
); );
if (roomID.error == null) { if (roomID.error == null) {
VRouter.of(context).toSegments(['rooms', roomID.result!, 'invite']); context.go(['', 'rooms', roomID.result!, 'invite'].join('/'));
} }
} }

View file

@ -31,7 +31,7 @@ class NewPrivateChatView extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: TextButton( child: TextButton(
onPressed: () => VRouter.of(context).to('/newgroup'), onPressed: () => context.go('/rooms/newgroup'),
child: Text( child: Text(
L10n.of(context)!.createNewGroup, L10n.of(context)!.createNewGroup,
style: style:

View file

@ -38,7 +38,7 @@ class NewSpaceController extends State<NewSpace> {
), ),
); );
if (roomID.error == null) { if (roomID.error == null) {
VRouter.of(context).toSegments(['spaces', roomID.result!]); context.go('/rooms/spaces/${roomID.result!}');
} }
} }

View file

@ -23,7 +23,7 @@ class SettingsView extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: CloseButton( leading: CloseButton(
onPressed: VRouter.of(context).pop, onPressed: () => context.go('/rooms'),
), ),
title: Text(L10n.of(context)!.settings), title: Text(L10n.of(context)!.settings),
actions: [ actions: [
@ -153,31 +153,31 @@ class SettingsView extends StatelessWidget {
ListTile( ListTile(
leading: const Icon(Icons.format_paint_outlined), leading: const Icon(Icons.format_paint_outlined),
title: Text(L10n.of(context)!.changeTheme), title: Text(L10n.of(context)!.changeTheme),
onTap: () => VRouter.of(context).to('/settings/style'), onTap: () => context.go('/rooms/settings/style'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),
ListTile( ListTile(
leading: const Icon(Icons.notifications_outlined), leading: const Icon(Icons.notifications_outlined),
title: Text(L10n.of(context)!.notifications), title: Text(L10n.of(context)!.notifications),
onTap: () => VRouter.of(context).to('/settings/notifications'), onTap: () => context.go('/rooms/settings/notifications'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),
ListTile( ListTile(
leading: const Icon(Icons.devices_outlined), leading: const Icon(Icons.devices_outlined),
title: Text(L10n.of(context)!.devices), title: Text(L10n.of(context)!.devices),
onTap: () => VRouter.of(context).to('/settings/devices'), onTap: () => context.go('/rooms/settings/devices'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),
ListTile( ListTile(
leading: const Icon(Icons.forum_outlined), leading: const Icon(Icons.forum_outlined),
title: Text(L10n.of(context)!.chat), title: Text(L10n.of(context)!.chat),
onTap: () => VRouter.of(context).to('/settings/chat'), onTap: () => context.go('/rooms/settings/chat'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),
ListTile( ListTile(
leading: const Icon(Icons.shield_outlined), leading: const Icon(Icons.shield_outlined),
title: Text(L10n.of(context)!.security), title: Text(L10n.of(context)!.security),
onTap: () => VRouter.of(context).to('/settings/security'), onTap: () => context.go('/rooms/settings/security'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),
const Divider(thickness: 1), const Divider(thickness: 1),

View file

@ -29,7 +29,7 @@ class SettingsChatView extends StatelessWidget {
children: [ children: [
ListTile( ListTile(
title: Text(L10n.of(context)!.emoteSettings), title: Text(L10n.of(context)!.emoteSettings),
onTap: () => VRouter.of(context).to('emotes'), onTap: () => context.go('/rooms/settings/chat/emotes'),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
leading: const Icon(Icons.emoji_emotions_outlined), leading: const Icon(Icons.emoji_emotions_outlined),
), ),

View file

@ -29,12 +29,12 @@ class EmotesSettings extends StatefulWidget {
} }
class EmotesSettingsController extends State<EmotesSettings> { class EmotesSettingsController extends State<EmotesSettings> {
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
Room? get room => Room? get room =>
roomId != null ? Matrix.of(context).client.getRoomById(roomId!) : null; roomId != null ? Matrix.of(context).client.getRoomById(roomId!) : null;
String? get stateKey => VRouter.of(context).pathParameters['state_key']; String? get stateKey => GoRouterState.of(context).pathParameters['state_key'];
bool showSave = false; bool showSave = false;
TextEditingController newImageCodeController = TextEditingController(); TextEditingController newImageCodeController = TextEditingController();

View file

@ -13,7 +13,7 @@ class MultipleEmotesSettings extends StatefulWidget {
} }
class MultipleEmotesSettingsController extends State<MultipleEmotesSettings> { class MultipleEmotesSettingsController extends State<MultipleEmotesSettings> {
String? get roomId => VRouter.of(context).pathParameters['roomid']; String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
@override @override
Widget build(BuildContext context) => MultipleEmotesSettingsView(this); Widget build(BuildContext context) => MultipleEmotesSettingsView(this);
} }

View file

@ -49,8 +49,9 @@ class MultipleEmotesSettingsView extends StatelessWidget {
return ListTile( return ListTile(
title: Text(packName), title: Text(packName),
onTap: () async { onTap: () async {
VRouter.of(context).toSegments( context.go(
['rooms', room.id, 'details', 'emotes', keys[i]], ['', 'rooms', room.id, 'details', 'emotes', keys[i]]
.join('/'),
); );
}, },
); );

View file

@ -27,13 +27,13 @@ class SettingsSecurityView extends StatelessWidget {
leading: const Icon(Icons.camera_outlined), leading: const Icon(Icons.camera_outlined),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
title: Text(L10n.of(context)!.whoCanSeeMyStories), title: Text(L10n.of(context)!.whoCanSeeMyStories),
onTap: () => VRouter.of(context).to('stories'), onTap: () => context.go('/rooms/settings/security/stories'),
), ),
ListTile( ListTile(
leading: const Icon(Icons.block_outlined), leading: const Icon(Icons.block_outlined),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
title: Text(L10n.of(context)!.ignoredUsers), title: Text(L10n.of(context)!.ignoredUsers),
onTap: () => VRouter.of(context).to('ignorelist'), onTap: () => context.go('/rooms/settings/security/ignorelist'),
), ),
ListTile( ListTile(
leading: const Icon(Icons.password_outlined), leading: const Icon(Icons.password_outlined),
@ -47,7 +47,7 @@ class SettingsSecurityView extends StatelessWidget {
leading: const Icon(Icons.mail_outlined), leading: const Icon(Icons.mail_outlined),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
title: Text(L10n.of(context)!.passwordRecovery), title: Text(L10n.of(context)!.passwordRecovery),
onTap: () => VRouter.of(context).to('3pid'), onTap: () => context.go('/rooms/settings/security/3pid'),
), ),
if (Matrix.of(context).client.encryption != null) ...{ if (Matrix.of(context).client.encryption != null) ...{
const Divider(thickness: 1), const Divider(thickness: 1),

View file

@ -120,7 +120,7 @@ class StoryPageController extends State<StoryPage> {
void share() async { void share() async {
Matrix.of(context).shareContent = currentEvent?.content; Matrix.of(context).shareContent = currentEvent?.content;
hold(); hold();
VRouter.of(context).to('share'); context.go('share');
} }
void displaySeenByUsers() async { void displaySeenByUsers() async {
@ -199,7 +199,7 @@ class StoryPageController extends State<StoryPage> {
return room.ownPowerLevel >= 100; return room.ownPowerLevel >= 100;
} }
String get roomId => VRouter.of(context).pathParameters['roomid'] ?? ''; String get roomId => GoRouterState.of(context).pathParameters['roomid'] ?? '';
Future<VideoPlayerController?>? loadVideoControllerFuture; Future<VideoPlayerController?>? loadVideoControllerFuture;
@ -228,9 +228,9 @@ class StoryPageController extends State<StoryPage> {
void skip() { void skip() {
if (index + 1 >= max) { if (index + 1 >= max) {
if (isOwnStory) { if (isOwnStory) {
VRouter.of(context).to('/stories/create'); context.go('/rooms/stories/create');
} else { } else {
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
return; return;
} }
@ -497,7 +497,8 @@ class StoryPageController extends State<StoryPage> {
currentEvent!.senderFromMemoryOrFallback.startDirectChat(), currentEvent!.senderFromMemoryOrFallback.startDirectChat(),
); );
if (roomIdResult.error != null) return; if (roomIdResult.error != null) return;
VRouter.of(context).toSegments(['rooms', roomIdResult.result!]); context.go(['', 'rooms', roomIdResult.result!].join('/'));
break; break;
} }
} }

View file

@ -147,8 +147,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
future: () => widget.user.startDirectChat(), future: () => widget.user.startDirectChat(),
); );
if (roomIdResult.error != null) return; if (roomIdResult.error != null) return;
VRouter.of(widget.outerContext) widget.outerContext.go(['', 'rooms', roomIdResult.result!].join('/'));
.toSegments(['rooms', roomIdResult.result!]);
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
break; break;
case UserBottomSheetAction.ignore: case UserBottomSheetAction.ignore:

View file

@ -37,7 +37,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.
import 'package:fluffychat/utils/push_helper.dart'; import 'package:fluffychat/utils/push_helper.dart';
import '../config/app_config.dart'; import '../config/app_config.dart';
import '../config/setting_keys.dart'; import '../config/setting_keys.dart';
import '../widgets/fluffy_chat_app.dart'; import '../widgets/matrix.dart';
import 'famedlysdk_store.dart'; import 'famedlysdk_store.dart';
import 'platform_infos.dart'; import 'platform_infos.dart';
@ -52,8 +52,8 @@ class BackgroundPush {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlugin();
Client client; Client client;
BuildContext? context; MatrixState? matrix;
GlobalKey<VRouterState> get router => FluffyChatApp.routerKey; BuildContext? get context => matrix?.navigatorContext;
String? _fcmToken; String? _fcmToken;
void Function(String errorMsg, {Uri? link})? onFcmError; void Function(String errorMsg, {Uri? link})? onFcmError;
L10n? l10n; L10n? l10n;
@ -77,6 +77,7 @@ class BackgroundPush {
onRoomSync ??= client.onSync.stream onRoomSync ??= client.onSync.stream
.where((s) => s.hasRoomUpdate) .where((s) => s.hasRoomUpdate)
.listen((s) => _onClearingPush(getFromServer: false)); .listen((s) => _onClearingPush(getFromServer: false));
final context = this.context;
firebase?.setListeners( firebase?.setListeners(
onMessage: (message) => pushHelper( onMessage: (message) => pushHelper(
PushNotification.fromJson( PushNotification.fromJson(
@ -84,7 +85,9 @@ class BackgroundPush {
), ),
client: client, client: client,
l10n: l10n, l10n: l10n,
activeRoomId: router.currentState?.pathParameters['roomid'], activeRoomId: context == null
? null
: GoRouterState.of(context).pathParameters['roomid'],
onSelectNotification: goToRoom, onSelectNotification: goToRoom,
), ),
); );
@ -104,12 +107,11 @@ class BackgroundPush {
} }
factory BackgroundPush( factory BackgroundPush(
Client client, MatrixState matrix, {
BuildContext context, {
final void Function(String errorMsg, {Uri? link})? onFcmError, final void Function(String errorMsg, {Uri? link})? onFcmError,
}) { }) {
final instance = BackgroundPush.clientOnly(client); final instance = BackgroundPush.clientOnly(matrix.client);
instance.context = context; instance.matrix = matrix;
// ignore: prefer_initializing_formals // ignore: prefer_initializing_formals
instance.onFcmError = onFcmError; instance.onFcmError = onFcmError;
return instance; return instance;
@ -250,8 +252,7 @@ class BackgroundPush {
.then((details) { .then((details) {
if (details == null || if (details == null ||
!details.didNotificationLaunchApp || !details.didNotificationLaunchApp ||
_wentToRoomOnStartup || _wentToRoomOnStartup) {
router.currentState == null) {
return; return;
} }
_wentToRoomOnStartup = true; _wentToRoomOnStartup = true;
@ -303,7 +304,7 @@ class BackgroundPush {
try { try {
final roomId = response?.payload; final roomId = response?.payload;
Logs().v('[Push] Attempting to go to room $roomId...'); Logs().v('[Push] Attempting to go to room $roomId...');
if (router.currentState == null || roomId == null) { if (roomId == null) {
return; return;
} }
await client.roomsLoading; await client.roomsLoading;
@ -314,7 +315,7 @@ class BackgroundPush {
?.content ?.content
.tryGet<String>('type') == .tryGet<String>('type') ==
ClientStoriesExtension.storiesRoomType; ClientStoriesExtension.storiesRoomType;
router.currentState!.toSegments([isStory ? 'stories' : 'rooms', roomId]); context?.go(['', isStory ? 'stories' : 'rooms', roomId].join('/'));
} catch (e, s) { } catch (e, s) {
Logs().e('[Push] Failed to open room', e, s); Logs().e('[Push] Failed to open room', e, s);
} }
@ -390,11 +391,14 @@ class BackgroundPush {
); );
// UP may strip the devices list // UP may strip the devices list
data['devices'] ??= []; data['devices'] ??= [];
final context = this.context;
await pushHelper( await pushHelper(
PushNotification.fromJson(data), PushNotification.fromJson(data),
client: client, client: client,
l10n: l10n, l10n: l10n,
activeRoomId: router.currentState?.pathParameters['roomid'], activeRoomId: context == null
? null
: GoRouterState.of(context).pathParameters['roomid'],
); );
} }

View file

@ -46,20 +46,29 @@ abstract class PlatformInfos {
final version = await PlatformInfos.getVersion(); final version = await PlatformInfos.getVersion();
showAboutDialog( showAboutDialog(
context: context, context: context,
useRootNavigator: false,
children: [ children: [
Text('Version: $version'), Text('Version: $version'),
OutlinedButton( TextButton.icon(
onPressed: () => launchUrlString(AppConfig.sourceCodeUrl), onPressed: () => launchUrlString(AppConfig.sourceCodeUrl),
child: Text(L10n.of(context)!.sourceCode), icon: const Icon(Icons.source_outlined),
label: Text(L10n.of(context)!.sourceCode),
), ),
OutlinedButton( TextButton.icon(
onPressed: () => launchUrlString(AppConfig.emojiFontUrl), onPressed: () => launchUrlString(AppConfig.emojiFontUrl),
child: const Text(AppConfig.emojiFontName), icon: const Icon(Icons.emoji_emotions_outlined),
label: const Text(AppConfig.emojiFontName),
), ),
OutlinedButton( Builder(
onPressed: () => VRouter.of(context).to('logs'), builder: (context) {
child: const Text('Logs'), return TextButton.icon(
onPressed: () {
Navigator.of(context).pop();
context.go('/logs');
},
icon: const Icon(Icons.list_outlined),
label: const Text('Logs'),
);
},
), ),
], ],
applicationIcon: Image.asset( applicationIcon: Image.asset(

View file

@ -49,7 +49,7 @@ extension UiaRequestManager on MatrixState {
case AuthenticationTypes.emailIdentity: case AuthenticationTypes.emailIdentity:
if (currentThreepidCreds == null) { if (currentThreepidCreds == null) {
return uiaRequest.cancel( return uiaRequest.cancel(
UiaException(L10n.of(widget.context)!.serverRequiresEmail), UiaException(L10n.of(navigatorContext)!.serverRequiresEmail),
); );
} }
final auth = AuthenticationThreePidCreds( final auth = AuthenticationThreePidCreds(

View file

@ -172,17 +172,20 @@ class UrlLauncher {
if (room != null) { if (room != null) {
if (room.isSpace) { if (room.isSpace) {
// TODO: Implement navigate to space // TODO: Implement navigate to space
VRouter.of(context).toSegments(['rooms']); context.go(['', 'rooms'].join('/'));
return; return;
} }
// we have the room, so....just open it // we have the room, so....just open it
if (event != null) { if (event != null) {
VRouter.of(context).toSegments( context.go(
['rooms', room.id], Uri(
queryParameters: {'event': event}, pathSegments: ['rooms', room.id],
queryParameters: {'event': event},
).toString(),
); );
} else { } else {
VRouter.of(context).toSegments(['rooms', room.id]); context.go(['', 'rooms', room.id].join('/'));
} }
return; return;
} else { } else {
@ -216,12 +219,14 @@ class UrlLauncher {
future: () => Future.delayed(const Duration(seconds: 2)), future: () => Future.delayed(const Duration(seconds: 2)),
); );
if (event != null) { if (event != null) {
VRouter.of(context).toSegments( context.go(
['rooms', response.result!], Uri(
queryParameters: {'event': event}, pathSegments: ['rooms', response.result!],
queryParameters: {'event': event},
).toString(),
); );
} else { } else {
VRouter.of(context).toSegments(['rooms', response.result!]); context.go(['', 'rooms', response.result!].join('/'));
} }
} }
} }

View file

@ -12,14 +12,15 @@ import 'package:webrtc_interface/webrtc_interface.dart' hide Navigator;
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/dialer/dialer.dart'; import 'package:fluffychat/pages/dialer/dialer.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import '../../utils/famedlysdk_store.dart'; import '../../utils/famedlysdk_store.dart';
import '../../utils/voip/callkeep_manager.dart'; import '../../utils/voip/callkeep_manager.dart';
import '../../utils/voip/user_media_manager.dart'; import '../../utils/voip/user_media_manager.dart';
import '../widgets/matrix.dart';
class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
final Client client; final MatrixState matrix;
VoipPlugin(this.client) { Client get client => matrix.client;
VoipPlugin(this.matrix) {
voip = VoIP(client, this); voip = VoIP(client, this);
Connectivity() Connectivity()
.onConnectivityChanged .onConnectivityChanged
@ -40,6 +41,7 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
late VoIP voip; late VoIP voip;
ConnectivityResult? _currentConnectivity; ConnectivityResult? _currentConnectivity;
OverlayEntry? overlayEntry; OverlayEntry? overlayEntry;
BuildContext get context => matrix.navigatorContext;
void _handleNetworkChanged(ConnectivityResult result) async { void _handleNetworkChanged(ConnectivityResult result) async {
/// Got a new connectivity status! /// Got a new connectivity status!
@ -59,9 +61,8 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
} }
void addCallingOverlay(String callId, CallSession call) { void addCallingOverlay(String callId, CallSession call) {
final context = kIsWeb final context =
? ChatList.contextForVoip! kIsWeb ? ChatList.contextForVoip! : this.context; // web is weird
: FluffyChatApp.matrixKey.currentContext!; // web is weird
if (overlayEntry != null) { if (overlayEntry != null) {
Logs().e('[VOIP] addCallingOverlay: The call session already exists?'); Logs().e('[VOIP] addCallingOverlay: The call session already exists?');
@ -166,8 +167,7 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
addCallingOverlay(call.callId, call); addCallingOverlay(call.callId, call);
try { try {
if (!hasCallingAccount) { if (!hasCallingAccount) {
ScaffoldMessenger.of(FluffyChatApp.matrixKey.currentContext!) ScaffoldMessenger.of(context).showSnackBar(
.showSnackBar(
const SnackBar( const SnackBar(
content: Text( content: Text(
'No calling accounts found (used for native calls UI)', 'No calling accounts found (used for native calls UI)',

View file

@ -154,7 +154,7 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
future: () => widget.room.leave(), future: () => widget.room.leave(),
); );
if (success.error == null) { if (success.error == null) {
VRouter.of(context).to('/rooms'); context.go('/rooms');
} }
} }
break; break;
@ -195,10 +195,10 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
); );
void _showChatDetails() { void _showChatDetails() {
if (VRouter.of(context).path.endsWith('/details')) { if (GoRouterState.of(context).uri.path.endsWith('/details')) {
VRouter.of(context).toSegments(['rooms', widget.room.id]); context.go(['', 'rooms', widget.room.id].join('/'));
} else { } else {
VRouter.of(context).toSegments(['rooms', widget.room.id, 'details']); context.go(['', 'rooms', widget.room.id, 'details'].join('/'));
} }
} }
} }

View file

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -12,17 +11,14 @@ import '../config/app_config.dart';
import '../utils/custom_scroll_behaviour.dart'; import '../utils/custom_scroll_behaviour.dart';
import 'matrix.dart'; import 'matrix.dart';
class FluffyChatApp extends StatefulWidget { class FluffyChatApp extends StatelessWidget {
final Widget? testWidget; final Widget? testWidget;
final List<Client> clients; final List<Client> clients;
final Map<String, String>? queryParameters;
static GlobalKey<VRouterState> routerKey = GlobalKey<VRouterState>();
static GlobalKey<MatrixState> matrixKey = GlobalKey<MatrixState>();
const FluffyChatApp({ const FluffyChatApp({
Key? key, Key? key,
this.testWidget, this.testWidget,
required this.clients, required this.clients,
this.queryParameters,
}) : super(key: key); }) : super(key: key);
/// getInitialLink may rereturn the value multiple times if this view is /// getInitialLink may rereturn the value multiple times if this view is
@ -30,59 +26,23 @@ class FluffyChatApp extends StatefulWidget {
/// in with qr code or magic link. /// in with qr code or magic link.
static bool gotInitialLink = false; static bool gotInitialLink = false;
@override
FluffyChatAppState createState() => FluffyChatAppState();
}
class FluffyChatAppState extends State<FluffyChatApp> {
bool? columnMode;
String? _initialUrl;
@override
void initState() {
super.initState();
_initialUrl =
widget.clients.any((client) => client.isLogged()) ? '/rooms' : '/home';
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ThemeBuilder( return ThemeBuilder(
builder: (context, themeMode, primaryColor) => LayoutBuilder( builder: (context, themeMode, primaryColor) => MaterialApp.router(
builder: (context, constraints) { title: AppConfig.applicationName,
final isColumnMode = themeMode: themeMode,
FluffyThemes.isColumnModeByWidth(constraints.maxWidth); theme: FluffyThemes.buildTheme(Brightness.light, primaryColor),
if (isColumnMode != columnMode) { darkTheme: FluffyThemes.buildTheme(Brightness.dark, primaryColor),
Logs().v('Set Column Mode = $isColumnMode'); scrollBehavior: CustomScrollBehavior(),
WidgetsBinding.instance.addPostFrameCallback((_) { localizationsDelegates: L10n.localizationsDelegates,
setState(() { supportedLocales: L10n.supportedLocales,
_initialUrl = FluffyChatApp.routerKey.currentState?.url; routerConfig: GoRouter(routes: AppRoutes(clients).routes),
columnMode = isColumnMode; builder: (context, child) => Matrix(
FluffyChatApp.routerKey = GlobalKey<VRouterState>(); context: context,
}); clients: clients,
}); child: child,
} ),
return VRouter(
key: FluffyChatApp.routerKey,
title: AppConfig.applicationName,
debugShowCheckedModeBanner: false,
themeMode: themeMode,
theme: FluffyThemes.buildTheme(Brightness.light, primaryColor),
darkTheme: FluffyThemes.buildTheme(Brightness.dark, primaryColor),
scrollBehavior: CustomScrollBehavior(),
logs: kReleaseMode ? VLogs.none : VLogs.info,
localizationsDelegates: L10n.localizationsDelegates,
supportedLocales: L10n.supportedLocales,
initialUrl: _initialUrl ?? '/',
routes: AppRoutes(columnMode ?? false).routes,
builder: (context, child) => Matrix(
key: FluffyChatApp.matrixKey,
context: context,
clients: widget.clients,
child: child,
),
);
},
), ),
); );
} }

View file

@ -1,31 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/update_checker_no_store.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/matrix.dart';
class LoadingView extends StatelessWidget {
const LoadingView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) async {
await UpdateCheckerNoStore(context).checkUpdate();
VRouter.of(context).to(
Matrix.of(context).widget.clients.any(
(client) =>
client.onLoginStateChanged.value == LoginState.loggedIn,
)
? '/rooms'
: '/home',
queryParameters: VRouter.of(context).queryParameters,
);
},
);
return const EmptyPage(loading: true);
}
}

View file

@ -1,7 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
class SideViewLayout extends StatelessWidget { class SideViewLayout extends StatelessWidget {
@ -12,14 +10,12 @@ class SideViewLayout extends StatelessWidget {
: super(key: key); : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var currentUrl = Uri.decodeFull(VRouter.of(context).url);
if (!currentUrl.endsWith('/')) currentUrl += '/';
final hideSideView = currentUrl.split('/').length == 4;
final sideView = this.sideView; final sideView = this.sideView;
final hideSideView =
!FluffyThemes.isThreeColumnMode(context) || sideView == null;
return sideView == null return sideView == null
? mainView ? mainView
: MediaQuery.of(context).size.width < FluffyThemes.columnWidth * 3.5 && : hideSideView
!hideSideView
? sideView ? sideView
: Row( : Row(
children: [ children: [

View file

@ -1,15 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../config/themes.dart';
class TwoColumnLayout extends StatelessWidget { class TwoColumnLayout extends StatelessWidget {
final Widget mainView; final Widget mainView;
final Widget sideView; final Widget sideView;
final bool displayNavigationRail;
const TwoColumnLayout({ const TwoColumnLayout({
Key? key, Key? key,
required this.mainView, required this.mainView,
required this.sideView, required this.sideView,
required this.displayNavigationRail,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -20,8 +20,7 @@ class TwoColumnLayout extends StatelessWidget {
Container( Container(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(), decoration: const BoxDecoration(),
width: 360.0 + width: 360.0 + (displayNavigationRail ? 64 : 0),
(FluffyThemes.getDisplayNavigationRail(context) ? 64 : 0),
child: mainView, child: mainView,
), ),
Container( Container(

View file

@ -34,9 +34,9 @@ extension LocalNotificationsExtension on MatrixState {
final event = Event.fromJson(eventUpdate.content, room); final event = Event.fromJson(eventUpdate.content, room);
final title = final title =
room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!)); room.getLocalizedDisplayname(MatrixLocals(L10n.of(navigatorContext)!));
final body = await event.calcLocalizedBody( final body = await event.calcLocalizedBody(
MatrixLocals(L10n.of(widget.context)!), MatrixLocals(L10n.of(navigatorContext)!),
withSenderNamePrefix: withSenderNamePrefix:
!room.isDirectChat || room.lastEvent?.senderId == client.userID, !room.isDirectChat || room.lastEvent?.senderId == client.userID,
plaintextBody: true, plaintextBody: true,
@ -95,11 +95,11 @@ extension LocalNotificationsExtension on MatrixState {
actions: [ actions: [
NotificationAction( NotificationAction(
DesktopNotificationActions.openChat.name, DesktopNotificationActions.openChat.name,
L10n.of(widget.context)!.openChat, L10n.of(navigatorContext)!.openChat,
), ),
NotificationAction( NotificationAction(
DesktopNotificationActions.seen.name, DesktopNotificationActions.seen.name,
L10n.of(widget.context)!.markAsRead, L10n.of(navigatorContext)!.markAsRead,
), ),
], ],
hints: [ hints: [
@ -114,7 +114,7 @@ extension LocalNotificationsExtension on MatrixState {
room.setReadMarker(event.eventId, mRead: event.eventId); room.setReadMarker(event.eventId, mRead: event.eventId);
break; break;
case DesktopNotificationActions.openChat: case DesktopNotificationActions.openChat:
VRouter.of(navigatorContext).toSegments(['rooms', room.id]); navigatorContext.go(['', 'rooms', room.id].join('/'));
break; break;
} }
}); });

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
class LogViewer extends StatefulWidget { class LogViewer extends StatefulWidget {
@ -22,7 +23,9 @@ class LogViewerState extends State<LogViewer> {
backgroundColor: Colors.black, backgroundColor: Colors.black,
appBar: AppBar( appBar: AppBar(
title: Text(logLevel.toString()), title: Text(logLevel.toString()),
leading: const BackButton(), leading: BackButton(
onPressed: () => context.go('/'),
),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.zoom_in_outlined), icon: const Icon(Icons.zoom_in_outlined),

View file

@ -8,9 +8,7 @@ import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:flutter_app_lock/flutter_app_lock.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@ -18,7 +16,6 @@ import 'package:image_picker/image_picker.dart';
import 'package:matrix/encryption.dart'; import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@ -33,7 +30,6 @@ import '../pages/key_verification/key_verification_dialog.dart';
import '../utils/account_bundles.dart'; import '../utils/account_bundles.dart';
import '../utils/background_push.dart'; import '../utils/background_push.dart';
import '../utils/famedlysdk_store.dart'; import '../utils/famedlysdk_store.dart';
import 'fluffy_chat_app.dart';
import 'local_notifications_extension.dart'; import 'local_notifications_extension.dart';
// import 'package:flutter_secure_storage/flutter_secure_storage.dart'; // import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -41,8 +37,7 @@ import 'local_notifications_extension.dart';
class Matrix extends StatefulWidget { class Matrix extends StatefulWidget {
final Widget? child; final Widget? child;
GlobalKey<VRouterState> get router => FluffyChatApp.routerKey; @Deprecated('')
final BuildContext context; final BuildContext context;
final List<Client> clients; final List<Client> clients;
@ -177,7 +172,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
ClientManager.addClientNameToStore(_loginClientCandidate!.clientName); ClientManager.addClientNameToStore(_loginClientCandidate!.clientName);
_registerSubs(_loginClientCandidate!.clientName); _registerSubs(_loginClientCandidate!.clientName);
_loginClientCandidate = null; _loginClientCandidate = null;
widget.router.currentState!.to('/rooms'); navigatorContext.go('/rooms');
}); });
return candidate; return candidate;
} }
@ -244,7 +239,8 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
bool webHasFocus = true; bool webHasFocus = true;
String? get activeRoomId => navigatorContext.vRouter.pathParameters['roomid']; String? get activeRoomId =>
GoRouterState.of(navigatorContext).pathParameters['roomid'];
final linuxNotifications = final linuxNotifications =
PlatformInfos.isLinux ? NotificationsClient() : null; PlatformInfos.isLinux ? NotificationsClient() : null;
@ -334,15 +330,21 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
); );
if (state != LoginState.loggedIn) { if (state != LoginState.loggedIn) {
widget.router.currentState?.to( navigatorContext.go(
'/rooms', Uri(
queryParameters: widget.router.currentState?.queryParameters ?? {}, path: '/rooms',
queryParameters:
GoRouterState.of(navigatorContext).uri.queryParameters,
).toString(),
); );
} }
} else { } else {
widget.router.currentState?.to( navigatorContext.go(
state == LoginState.loggedIn ? '/rooms' : '/home', Uri(
queryParameters: widget.router.currentState?.queryParameters ?? {}, path: state == LoginState.loggedIn ? '/rooms' : '/home',
queryParameters:
GoRouterState.of(navigatorContext).uri.queryParameters,
).toString(),
); );
} }
}); });
@ -375,23 +377,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} }
void initMatrix() { void initMatrix() {
// Display the app lock
if (PlatformInfos.isMobile) {
WidgetsBinding.instance.addPostFrameCallback((_) {
([TargetPlatform.linux].contains(Theme.of(context).platform)
? SharedPreferences.getInstance()
.then((prefs) => prefs.getString(SettingKeys.appLockKey))
: const FlutterSecureStorage()
.read(key: SettingKeys.appLockKey))
.then((lock) {
if (lock?.isNotEmpty ?? false) {
AppLock.of(widget.context)!.enable();
AppLock.of(widget.context)!.showLockScreen();
}
});
});
}
_initWithStore(); _initWithStore();
for (final c in widget.clients) { for (final c in widget.clients) {
@ -405,8 +390,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
if (PlatformInfos.isMobile) { if (PlatformInfos.isMobile) {
backgroundPush = BackgroundPush( backgroundPush = BackgroundPush(
client, this,
context,
onFcmError: (errorMsg, {Uri? link}) async { onFcmError: (errorMsg, {Uri? link}) async {
final result = await showOkCancelAlertDialog( final result = await showOkCancelAlertDialog(
barrierDismissible: true, barrierDismissible: true,
@ -435,7 +419,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
voipPlugin = null; voipPlugin = null;
return; return;
} }
voipPlugin = webrtcIsSupported ? VoipPlugin(client) : null; voipPlugin = webrtcIsSupported ? VoipPlugin(this) : null;
} }
@override @override
@ -529,30 +513,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} }
} }
class FixedThreepidCreds extends ThreepidCreds {
FixedThreepidCreds({
required String sid,
required String clientSecret,
String? idServer,
String? idAccessToken,
}) : super(
sid: sid,
clientSecret: clientSecret,
idServer: idServer,
idAccessToken: idAccessToken,
);
@override
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['sid'] = sid;
data['client_secret'] = clientSecret;
if (idServer != null) data['id_server'] = idServer;
if (idAccessToken != null) data['id_access_token'] = idAccessToken;
return data;
}
}
class _AccountBundleWithClient { class _AccountBundleWithClient {
final Client? client; final Client? client;
final AccountBundle? bundle; final AccountBundle? bundle;

View file

@ -25,7 +25,8 @@ class ProfileBottomSheet extends StatelessWidget {
future: () => client.startDirectChat(userId), future: () => client.startDirectChat(userId),
); );
if (result.error == null) { if (result.error == null) {
VRouter.of(context).toSegments(['rooms', result.result!]); context.go(['', 'rooms', result.result!].join('/'));
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
return; return;
} }

View file

@ -31,7 +31,6 @@ class PublicRoomBottomSheet extends StatelessWidget {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
final chunk = this.chunk; final chunk = this.chunk;
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
final router = VRouter.of(context);
final result = await showFutureLoadingDialog<String>( final result = await showFutureLoadingDialog<String>(
context: context, context: context,
future: () async { future: () async {
@ -54,7 +53,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
navigator.pop(); navigator.pop();
// don't open the room if the joined room is a space // don't open the room if the joined room is a space
if (!client.getRoomById(result.result!)!.isSpace) { if (!client.getRoomById(result.result!)!.isSpace) {
router.toSegments(['rooms', result.result!]); context.go(['', 'rooms', result.result!].join('/'));
} }
return; return;
} }