mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[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:
parent
e50d708c21
commit
5cced92646
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user