From 6dddb42f5e75c44bdb259c8d7610652d95a59e91 Mon Sep 17 00:00:00 2001 From: Julian KOUNE Date: Fri, 15 Sep 2023 15:46:44 +0200 Subject: [PATCH] feat: add error page --- assets/images/ic_error_page.svg | 161 +++++++++++++++++++++ assets/images/ic_error_page_background.svg | 7 + assets/l10n/intl_en.arb | 5 +- lib/config/go_routes/go_router.dart | 8 + lib/pages/chat/chat.dart | 6 +- lib/pages/chat/chat_view.dart | 10 +- lib/pages/error_page/error_page.dart | 114 +++++++++++++++ lib/pages/error_page/error_page_style.dart | 63 ++++++++ lib/resource/image_paths.dart | 3 + lib/widgets/twake_app.dart | 4 + 10 files changed, 370 insertions(+), 11 deletions(-) create mode 100644 assets/images/ic_error_page.svg create mode 100644 assets/images/ic_error_page_background.svg create mode 100644 lib/pages/error_page/error_page.dart create mode 100644 lib/pages/error_page/error_page_style.dart diff --git a/assets/images/ic_error_page.svg b/assets/images/ic_error_page.svg new file mode 100644 index 0000000000..566bf82e8b --- /dev/null +++ b/assets/images/ic_error_page.svg @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/ic_error_page_background.svg b/assets/images/ic_error_page_background.svg new file mode 100644 index 0000000000..7af80d7604 --- /dev/null +++ b/assets/images/ic_error_page_background.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index c220c70253..f2c3952357 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2713,5 +2713,8 @@ } }, "notInAChatYet": "You're not in a chat yet", - "blankChatTitle": "Choose a chat or hit #EditIcon# to make one." + "blankChatTitle": "Choose a chat or hit #EditIcon# to make one.", + "errorPageTitle": "Something's not right", + "errorPageDescription": "That page doesn't exist.", + "errorPageButton": "Back to chat" } \ No newline at end of file diff --git a/lib/config/go_routes/go_router.dart b/lib/config/go_routes/go_router.dart index fb71811287..dccf819ac6 100644 --- a/lib/config/go_routes/go_router.dart +++ b/lib/config/go_routes/go_router.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pages/chat_blank/chat_blank.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; import 'package:fluffychat/pages/chat_draft/draft_chat.dart'; import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart'; +import 'package:fluffychat/pages/error_page/error_page.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart'; import 'package:fluffychat/pages/share/share.dart'; import 'package:fluffychat/pages/story/story_page.dart'; @@ -103,6 +104,13 @@ abstract class AppRoutes { const LogViewer(), ), ), + GoRoute( + path: '/error', + pageBuilder: (context, state) => defaultPageBuilder( + context, + const ErrorPage(), + ), + ), ShellRoute( pageBuilder: (context, state, child) => defaultPageBuilder( context, diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 68ddcde6bc..90f13bb687 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -309,6 +309,10 @@ class ChatController extends State ); super.initState(); SchedulerBinding.instance.addPostFrameCallback((_) async { + if (room == null) { + return context.go("/error"); + } + _askToAcceptInvitation(); if (shareFile != null && room != null && shareFile!.filePath != null) { final sendFileInteractor = getIt.get(); @@ -323,7 +327,7 @@ class ChatController extends State } void _askToAcceptInvitation() async { - if (room!.membership != Membership.invite) return; + if (room?.membership != Membership.invite) return; final result = await showDialog( context: context, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 627b4789c0..1756a1cae4 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -115,15 +115,7 @@ class ChatView extends StatelessWidget { controller.sendingClient ??= client; controller.room = controller.sendingClient!.getRoomById(controller.roomId!); if (controller.room == null) { - return Scaffold( - appBar: AppBar( - toolbarHeight: 120, - title: Text(L10n.of(context)!.oopsSomethingWentWrong), - ), - body: Center( - child: Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat), - ), - ); + return const SizedBox.shrink(); } final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0; diff --git a/lib/pages/error_page/error_page.dart b/lib/pages/error_page/error_page.dart new file mode 100644 index 0000000000..908c68f9f4 --- /dev/null +++ b/lib/pages/error_page/error_page.dart @@ -0,0 +1,114 @@ +import 'package:fluffychat/pages/error_page/error_page_style.dart'; +import 'package:fluffychat/resource/image_paths.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; + +class ErrorPage extends StatelessWidget { + const ErrorPage({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return Scaffold( + // Add invisible appbar to make status bar on Android tablets bright. + appBar: AppBar( + automaticallyImplyLeading: false, + elevation: 0, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + ), + extendBodyBehindAppBar: true, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ErrorPageStyle.responsiveUtils.isMobile(context) + ? const _ErrorPageBackgroundMobile() + : const _ErrorPageBackgroundWeb(), + const _ErrorPageText(), + const _ErrorPageBackButton(), + ], + ), + ), + ); + } +} + +class _ErrorPageBackgroundWeb extends StatelessWidget { + const _ErrorPageBackgroundWeb(); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + SvgPicture.asset( + ImagePaths.icErrorPageBackground, + colorFilter: ErrorPageStyle.backgroundColorFilter(context), + fit: BoxFit.contain, + ), + SvgPicture.asset( + ImagePaths.icErrorPage, + width: ErrorPageStyle.backgroundIconWidthWeb, + fit: BoxFit.contain, + ), + ], + ); + } +} + +class _ErrorPageBackgroundMobile extends StatelessWidget { + const _ErrorPageBackgroundMobile(); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + ImagePaths.icErrorPage, + width: ErrorPageStyle.backgroundIconWidthMobile, + fit: BoxFit.contain, + ); + } +} + +class _ErrorPageBackButton extends StatelessWidget { + const _ErrorPageBackButton(); + + @override + Widget build(BuildContext context) { + return TextButton.icon( + icon: const Icon(Icons.arrow_back), + onPressed: () => _goToRooms, + label: Text( + L10n.of(context)!.errorPageButton, + ), + style: ErrorPageStyle.buttonStyle(context), + ); + } + + void _goToRooms(BuildContext context) { + context.go('/rooms'); + } +} + +class _ErrorPageText extends StatelessWidget { + const _ErrorPageText(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: ErrorPageStyle.textPadding, + child: Column( + children: [ + Text( + L10n.of(context)!.errorPageTitle, + style: ErrorPageStyle.titleTextStyle(context), + ), + const SizedBox(height: ErrorPageStyle.textsGap), + Text( + L10n.of(context)!.errorPageDescription, + style: ErrorPageStyle.descriptionTextStyle(context), + ), + ], + ), + ); + } +} diff --git a/lib/pages/error_page/error_page_style.dart b/lib/pages/error_page/error_page_style.dart new file mode 100644 index 0000000000..19721de8da --- /dev/null +++ b/lib/pages/error_page/error_page_style.dart @@ -0,0 +1,63 @@ +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:linagora_design_flutter/linagora_design_flutter.dart'; + +class ErrorPageStyle { + static final ResponsiveUtils responsiveUtils = getIt.get(); + + static ColorFilter backgroundColorFilter(BuildContext context) => + ColorFilter.mode( + Theme.of(context).colorScheme.primaryContainer, + BlendMode.srcATop, + ); + + static const double backgroundIconWidthWeb = 448.0; + static const double backgroundIconWidthMobile = 280.0; + + static const EdgeInsets textPadding = EdgeInsets.symmetric(vertical: 32.0); + + static TextStyle? titleTextStyle(BuildContext context) => + ErrorPageStyle.responsiveUtils.isMobile(context) + ? Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ) + : Theme.of(context).textTheme.headlineLarge?.copyWith( + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ); + static TextStyle? descriptionTextStyle(BuildContext context) => + ErrorPageStyle.responsiveUtils.isMobile(context) + ? Theme.of(context).textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: LinagoraRefColors.material().tertiary[20], + ) + : Theme.of(context).textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: LinagoraRefColors.material().tertiary[20], + ); + + static ButtonStyle buttonStyle(BuildContext context) => ButtonStyle( + iconSize: MaterialStateProperty.all(18), + textStyle: MaterialStateProperty.all( + Theme.of(context).textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onPrimary, + ), + ), + backgroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.primary, + ), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.onPrimary, + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + ), + ); + + static const double textsGap = 12.0; +} diff --git a/lib/resource/image_paths.dart b/lib/resource/image_paths.dart index ff6e63b71f..bf1080c38a 100644 --- a/lib/resource/image_paths.dart +++ b/lib/resource/image_paths.dart @@ -32,6 +32,9 @@ class ImagePaths { static String get icUsersOutline => _getImagePath('ic_users_outline.svg'); static String get icReply => _getImagePath('ic_reply.svg'); static String get icEmptyPage => _getImagePath('ic_empty_page.svg'); + static String get icErrorPage => _getImagePath('ic_error_page.svg'); + static String get icErrorPageBackground => + _getImagePath('ic_error_page_background.svg'); static String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/widgets/twake_app.dart b/lib/widgets/twake_app.dart index 019c040dea..050bb6841b 100644 --- a/lib/widgets/twake_app.dart +++ b/lib/widgets/twake_app.dart @@ -33,6 +33,10 @@ class TwakeApp extends StatefulWidget { static final GoRouter router = GoRouter( routes: AppRoutes.routes, debugLogDiagnostics: true, + onException: (context, state, router) { + Logs().e('GoRouter exception: ${state.error}'); + return router.go('/error'); + }, ); @override