[feat] make error widget sexy (#2825)

* feat: add translations

* feat: style error page

* chore: update api usage

* feat: add GitHub redirect

* chore: rebase error?

* chore: remove todo comment

* chore: remove impossible code

* fix: update url launcher invocation

* chore: use new api

* fix: analyzer errors

* fix: revert changes in Cargo.toml
This commit is contained in:
Alex Wallen 2023-06-20 16:03:39 -10:00 committed by GitHub
parent e50d708c21
commit 5cced92646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 240 additions and 20 deletions

View File

@ -476,5 +476,10 @@
"name": "Calendar layout"
},
"referencedCalendarPrefix": "View of"
},
"errorDialog": {
"title": "AppFlowy Error",
"howToFixFallback": "We're sorry for the inconvenience! Submit an issue on our GitHub page that describes your error.",
"github": "View on GitHub"
}
}
}

View File

@ -95,7 +95,7 @@ class BoardPage extends StatelessWidget {
(_) => BoardContent(
onEditStateChanged: onEditStateChanged,
),
(err) => FlowyErrorPage(err.toString()),
(err) => FlowyErrorPage.message(err.toString(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),),
);
},
);

View File

@ -125,7 +125,10 @@ class _GridPageState extends State<GridPage> {
(_) => GridShortcuts(
child: GridPageContent(view: widget.view),
),
(err) => FlowyErrorPage(err.toString()),
(err) => FlowyErrorPage.message(
err.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
),
),
);
},

View File

@ -1,9 +1,11 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/grid/application/row/row_document_bloc.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -35,8 +37,9 @@ class RowDocument extends StatelessWidget {
loading: () => const Center(
child: CircularProgressIndicator.adaptive(),
),
error: (error) => FlowyErrorPage(
error: (error) => FlowyErrorPage.message(
error.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
),
finish: () => RowEditor(
viewPB: state.viewPB!,
@ -94,8 +97,9 @@ class _RowEditorState extends State<RowEditor> {
),
finish: (result) {
return result.fold(
(error) => FlowyErrorPage(
(error) => FlowyErrorPage.message(
error.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
),
(_) {
final editorState = documentBloc.editorState;

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
@ -14,11 +15,11 @@ import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart'
hide DocumentEvent;
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart' as p;
class DocumentPage extends StatefulWidget {
@ -65,7 +66,10 @@ class _DocumentPageState extends State<DocumentPage> {
return state.loadingState.when(
loading: () => const SizedBox.shrink(),
finish: (result) => result.fold(
(error) => FlowyErrorPage(error.toString()),
(error) => FlowyErrorPage.message(
error.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
),
(data) {
if (state.forceClose) {
widget.onDeleted();

View File

@ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/database_view_service.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
@ -31,14 +32,22 @@ extension InsertDatabase on EditorState {
await apply(transaction);
}
Future<void> insertReferencePage(ViewPB childView) async {
Future<void> insertReferencePage(
ViewPB childView,
) async {
final selection = this.selection;
if (selection == null || !selection.isCollapsed) {
return;
throw FlowyError(
msg:
"Could not insert the reference page because the current selection was null or collapsed.",
);
}
final node = getNodeAtPath(selection.end.path);
if (node == null) {
return;
throw FlowyError(
msg:
"Could not insert the reference page because the current node at the selection does not exist.",
);
}
// get the database id that the view is associated with
@ -60,10 +69,11 @@ extension InsertDatabase on EditorState {
databaseId: databaseId,
).then((value) => value.swap().toOption().toNullable());
// TODO(a-wallen): Show error dialog here.
// Maybe extend the FlowyErrorPage.
if (ref == null) {
return;
throw FlowyError(
msg:
"The `ViewBackendService` failed to create a database reference view",
);
}
final transaction = this.transaction;

View File

@ -1,11 +1,14 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
@ -37,9 +40,19 @@ void showLinkToPageMenu(
editorState: editorState,
layoutType: pageType,
hintText: pageType.toHintText(),
onSelected: (appPB, viewPB) {
editorState.insertReferencePage(viewPB);
linkToPageMenuEntry.remove();
onSelected: (appPB, viewPB) async {
try {
await editorState.insertReferencePage(viewPB);
linkToPageMenuEntry.remove();
} on FlowyError catch (e) {
Dialogs.show(
FlowyErrorPage.message(
e.msg,
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
),
context,
);
}
},
),
),

View File

@ -44,7 +44,7 @@ class WelcomeScreen extends StatelessWidget {
Widget _renderBody(WelcomeState state) {
final body = state.successOrFailure.fold(
(_) => _renderList(state.workspaces),
(error) => FlowyErrorPage(error.toString()),
(error) => FlowyErrorPage.message(error.toString(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),),
);
return body;
}

View File

@ -1,11 +1,191 @@
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
class FlowyErrorPage extends StatelessWidget {
final String error;
const FlowyErrorPage(this.error, {Key? key}) : super(key: key);
factory FlowyErrorPage.error(
Error e, {
required String howToFix,
Key? key,
}) =>
FlowyErrorPage._(
e.toString(),
stackTrace: e.stackTrace?.toString(),
howToFix: howToFix,
key: key,
);
factory FlowyErrorPage.message(
String message, {
required String howToFix,
String? stackTrace,
Key? key,
}) =>
FlowyErrorPage._(
message,
key: key,
stackTrace: stackTrace,
howToFix: howToFix,
);
factory FlowyErrorPage.exception(
Exception e, {
required String howToFix,
String? stackTrace,
Key? key,
}) =>
FlowyErrorPage._(
e.toString(),
stackTrace: stackTrace,
key: key,
howToFix: howToFix,
);
const FlowyErrorPage._(
this.message, {
required this.howToFix,
this.stackTrace,
super.key,
});
static const _titleFontSize = 24.0;
static const _titleToMessagePadding = 8.0;
final String message;
final String? stackTrace;
final String howToFix;
@override
Widget build(BuildContext context) {
return Text(error);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const FlowyText.medium(
"AppFlowy Error",
fontSize: _titleFontSize,
),
const SizedBox(
height: _titleToMessagePadding,
),
FlowyText.semibold(
message,
),
const SizedBox(
height: _titleToMessagePadding,
),
FlowyText.regular(
howToFix,
),
const SizedBox(
height: _titleToMessagePadding,
),
const GitHubRedirectButton(),
const SizedBox(
height: _titleToMessagePadding,
),
if (stackTrace != null) StackTracePreview(stackTrace!),
],
),
);
}
}
class StackTracePreview extends StatelessWidget {
const StackTracePreview(
this.stackTrace, {
super.key,
});
final String stackTrace;
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 350,
maxWidth: 450,
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const Align(
alignment: Alignment.centerLeft,
child: FlowyText.semibold(
"Stack Trace",
),
),
Container(
height: 120,
padding: const EdgeInsets.symmetric(vertical: 8),
child: SingleChildScrollView(
child: Text(
stackTrace,
style: Theme.of(context).textTheme.bodySmall,
),
),
),
Align(
alignment: Alignment.centerRight,
child: FlowyButton(
hoverColor: Theme.of(context).colorScheme.onBackground,
text: const FlowyText(
"Copy",
),
useIntrinsicWidth: true,
onTap: () => Clipboard.setData(
ClipboardData(text: stackTrace),
),
),
),
],
),
),
),
);
}
}
class GitHubRedirectButton extends StatelessWidget {
const GitHubRedirectButton({super.key});
static const _height = 32.0;
Uri get _gitHubNewBugUri => Uri(
scheme: 'https',
host: 'github.com',
path: '/AppFlowy-IO/AppFlowy/issues/new',
query:
'assignees=&labels=&projects=&template=bug_report.yaml&title=%5BBug%5D+',
);
@override
Widget build(BuildContext context) {
return FlowyButton(
leftIconSize: const Size.square(_height),
text: const FlowyText(
"AppFlowy",
),
useIntrinsicWidth: true,
leftIcon: Padding(
padding: const EdgeInsets.all(4.0),
child: svgWidget('login/github-mark'),
),
onTap: () async {
if (await canLaunchUrl(_gitHubNewBugUri)) {
await launchUrl(_gitHubNewBugUri);
}
},
);
}
}

View File

@ -20,6 +20,7 @@ dependencies:
animations: ^2.0.7
loading_indicator: ^3.1.0
async:
url_launcher: ^6.1.11
# Federated Platform Interface
flowy_infra_ui_platform_interface: