diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index a4dec86c..72d2862f 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -70,6 +70,14 @@ "errorEmptyField": "This field can not be empty", "errorInvalidURL": "Invalid URL provided", "errorInvalidQrcode": "Invalid QR-Code provided", + "errorRouteNotFound": "Route not found: {route}", + "@errorRouteNotFound" : { + "placeholders": { + "route": { + "type": "String" + } + } + }, "actionYes": "Yes", "actionNo": "No", "actionClose": "Close", diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index 609d5a97..79b78e87 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -269,6 +269,12 @@ abstract class AppLocalizations { /// **'Invalid QR-Code provided'** String get errorInvalidQrcode; + /// No description provided for @errorRouteNotFound. + /// + /// In en, this message translates to: + /// **'Route not found: {route}'** + String errorRouteNotFound(String route); + /// No description provided for @actionYes. /// /// In en, this message translates to: diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index 73ff480e..32f6f603 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -123,6 +123,11 @@ class AppLocalizationsEn extends AppLocalizations { @override String get errorInvalidQrcode => 'Invalid QR-Code provided'; + @override + String errorRouteNotFound(String route) { + return 'Route not found: $route'; + } + @override String get actionYes => 'Yes'; diff --git a/packages/neon/neon/lib/src/pages/route_not_found.dart b/packages/neon/neon/lib/src/pages/route_not_found.dart new file mode 100644 index 00000000..884172f2 --- /dev/null +++ b/packages/neon/neon/lib/src/pages/route_not_found.dart @@ -0,0 +1,64 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:neon/blocs.dart'; +import 'package:neon/l10n/localizations.dart'; +import 'package:neon/src/router.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class RouteNotFoundPage extends StatefulWidget { + const RouteNotFoundPage({ + required this.uri, + super.key, + }); + + final Uri uri; + + @override + State createState() => _RouteNotFoundPageState(); +} + +class _RouteNotFoundPageState extends State { + @override + void initState() { + super.initState(); + + unawaited(_checkLaunchable()); + } + + Future _checkLaunchable() async { + final accountsBloc = Provider.of(context, listen: false); + if (!accountsBloc.hasAccounts) { + return; + } + + final activeAccount = accountsBloc.activeAccount.value!; + + final launched = await launchUrl( + activeAccount.completeUri(widget.uri), + mode: LaunchMode.externalApplication, + ); + if (!launched) { + return; + } + + if (context.mounted) { + const HomeRoute().go(context); + } + } + + @override + Widget build(final BuildContext context) => Scaffold( + appBar: AppBar( + leading: CloseButton( + onPressed: () { + const HomeRoute().go(context); + }, + ), + ), + body: Center( + child: Text(AppLocalizations.of(context).errorRouteNotFound(widget.uri.toString())), + ), + ); +} diff --git a/packages/neon/neon/lib/src/router.dart b/packages/neon/neon/lib/src/router.dart index 8db0007f..a0af9af9 100644 --- a/packages/neon/neon/lib/src/router.dart +++ b/packages/neon/neon/lib/src/router.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:meta/meta.dart'; import 'package:neon/src/blocs/accounts.dart'; @@ -17,6 +17,7 @@ import 'package:neon/src/pages/login_check_server_status.dart'; import 'package:neon/src/pages/login_flow.dart'; import 'package:neon/src/pages/login_qrcode.dart'; import 'package:neon/src/pages/nextcloud_app_settings.dart'; +import 'package:neon/src/pages/route_not_found.dart'; import 'package:neon/src/pages/settings.dart'; import 'package:neon/src/utils/stream_listenable.dart'; import 'package:provider/provider.dart'; @@ -33,6 +34,7 @@ class AppRouter extends GoRouter { refreshListenable: StreamListenable(accountsBloc.activeAccount), navigatorKey: navigatorKey, initialLocation: const HomeRoute().location, + errorPageBuilder: _buildErrorPage, redirect: (final context, final state) { final loginQrcode = LoginQrcode.tryParse(state.uri.toString()); if (loginQrcode != null) { @@ -43,6 +45,13 @@ class AppRouter extends GoRouter { ).location; } + if (accountsBloc.hasAccounts && state.uri.hasScheme) { + final strippedUri = accountsBloc.activeAccount.value!.stripUri(state.uri); + if (strippedUri != state.uri) { + return strippedUri.toString(); + } + } + // redirect to loginscreen when no account is logged in if (!accountsBloc.hasAccounts && !state.uri.toString().startsWith(const LoginRoute().location)) { return const LoginRoute().location; @@ -52,6 +61,12 @@ class AppRouter extends GoRouter { }, routes: $appRoutes, ); + + static Page _buildErrorPage(final BuildContext context, final GoRouterState state) => MaterialPage( + child: RouteNotFoundPage( + uri: state.uri, + ), + ); } @immutable