fix: launch review 0.6.9 issues (#6203)

* chore: replace teamspaces with workspaces

* fix: set delete account button height to 32

* feat: use 'deletemyaccount' as confirmation text

* fix: remove input text after closing account deletion dialog

* feat: replace the existing error page with the new style

* fix: update photo library usage description

* chore: remove beta

* feat: add more acceptable confirm texts

* feat: optimize account deletion UX
This commit is contained in:
Lucas.Xu 2024-09-06 10:39:10 +08:00 committed by GitHub
parent 4f592e80b6
commit c400fdc01d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 114 additions and 102 deletions

View File

@ -47,7 +47,7 @@
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>Allow access to photos to upload photos from your library.</string>
<string>AppFlowy needs access to your photos to let you add images to your documents</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>

View File

@ -69,8 +69,10 @@ class _MobileBoardPageState extends State<MobileBoardPage> {
loading: (_) => const Center(
child: CircularProgressIndicator.adaptive(),
),
error: (err) => AppFlowyErrorPage(
error: err.error,
error: (err) => Center(
child: AppFlowyErrorPage(
error: err.error,
),
),
ready: (data) => const _BoardContent(),
orElse: () => const SizedBox.shrink(),

View File

@ -115,12 +115,13 @@ class _DeleteAccountBottomSheet extends StatefulWidget {
}
class _DeleteAccountBottomSheetState extends State<_DeleteAccountBottomSheet> {
final emailController = TextEditingController();
final controller = TextEditingController();
final isChecked = ValueNotifier(false);
@override
void dispose() {
emailController.dispose();
controller.dispose();
isChecked.dispose();
super.dispose();
}
@ -153,10 +154,12 @@ class _DeleteAccountBottomSheetState extends State<_DeleteAccountBottomSheet> {
SizedBox(
height: 36.0,
child: FlowyTextField(
controller: emailController,
controller: controller,
textStyle: const TextStyle(fontSize: 14.0),
hintStyle: const TextStyle(fontSize: 14.0),
hintText: LocaleKeys.settings_user_email.tr(),
hintText: LocaleKeys
.newSettings_myAccount_deleteAccount_confirmHint3
.tr(),
),
),
const VSpace(18.0),
@ -167,8 +170,9 @@ class _DeleteAccountBottomSheetState extends State<_DeleteAccountBottomSheet> {
textColor: Theme.of(context).colorScheme.error,
onPressed: () => deleteMyAccount(
context,
emailController.text.trim(),
controller.text.trim(),
isChecked.value,
onSuccess: () => Navigator.of(context).pop(),
),
),
const VSpace(12.0),

View File

@ -1,8 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart' hide Card;
import 'package:flutter/services.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/board/mobile_board_page.dart';
@ -19,6 +16,7 @@ import 'package:appflowy/plugins/database/widgets/card/card_bloc.dart';
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/desktop_board_card_cell_style.dart';
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
import 'package:appflowy/shared/conditional_listenable_builder.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_board/appflowy_board.dart';
@ -27,13 +25,13 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart' hide Card;
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../widgets/card/card.dart';
import '../../widgets/cell/card_cell_builder.dart';
import '../application/board_bloc.dart';
import 'toolbar/board_setting_bar.dart';
import 'widgets/board_focus_scope.dart';
import 'widgets/board_hidden_groups.dart';
@ -202,9 +200,10 @@ class _DesktopBoardPageState extends State<DesktopBoardPage> {
loading: (_) => const Center(
child: CircularProgressIndicator.adaptive(),
),
error: (err) => FlowyErrorPage.message(
err.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
error: (err) => Center(
child: AppFlowyErrorPage(
error: err.error,
),
),
orElse: () => _BoardContent(
onEditStateChanged: widget.onEditStateChanged,

View File

@ -1,22 +1,19 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/row/row_service.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculations_row.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart';
import 'package:appflowy/plugins/database/tab_bar/desktop/setting_menu.dart';
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
import 'package:appflowy/workspace/application/action_navigation/navigation_action.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
import 'package:provider/provider.dart';
@ -26,7 +23,6 @@ import '../../application/row/row_controller.dart';
import '../../tab_bar/tab_bar_view.dart';
import '../../widgets/row/row_detail.dart';
import '../application/grid_bloc.dart';
import 'grid_scroll.dart';
import 'layout/layout.dart';
import 'layout/sizes.dart';
@ -153,8 +149,9 @@ class _GridPageState extends State<GridPage> {
},
),
builder: (context, state) => state.loadingState.map(
loading: (_) =>
const Center(child: CircularProgressIndicator.adaptive()),
loading: (_) => const Center(
child: CircularProgressIndicator.adaptive(),
),
finish: (result) => result.successOrFail.fold(
(_) => GridShortcuts(
child: GridPageContent(
@ -162,9 +159,10 @@ class _GridPageState extends State<GridPage> {
view: widget.view,
),
),
(err) => FlowyErrorPage.message(
err.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
(err) => Center(
child: AppFlowyErrorPage(
error: err,
),
),
),
idle: (_) => const SizedBox.shrink(),

View File

@ -105,8 +105,10 @@ class _MobileGridPageState extends State<MobileGridPage> {
_openRow(context, widget.initialRowId, true);
return result.successOrFail.fold(
(_) => GridShortcuts(child: GridPageContent(view: widget.view)),
(err) => AppFlowyErrorPage(
error: err,
(err) => Center(
child: AppFlowyErrorPage(
error: err,
),
),
);
},

View File

@ -7,6 +7,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/me
import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_result.dart';
import 'package:appflowy/plugins/inline_actions/service_handler.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/shared/list_extension.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
@ -18,7 +19,6 @@ 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/dialog/styled_dialogs.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
// const _channel = "InlinePageReference";
@ -177,9 +177,8 @@ class InlinePageReferenceService extends InlineActionsDelegate {
if (context.mounted) {
return Dialogs.show(
context,
child: FlowyErrorPage.message(
e.msg,
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
child: AppFlowyErrorPage(
error: e,
),
);
}

View File

@ -1,10 +1,10 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/workspace/application/workspace/prelude.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.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:go_router/go_router.dart';
@ -33,9 +33,10 @@ class DesktopWorkspaceStartScreen extends StatelessWidget {
Widget _renderBody(WorkspaceState state) {
final body = state.successOrFailure.fold(
(_) => _renderList(state.workspaces),
(error) => FlowyErrorPage.message(
error.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
(error) => Center(
child: AppFlowyErrorPage(
error: error,
),
),
);
return body;

View File

@ -1,10 +1,10 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/workspace/application/workspace/prelude.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.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:go_router/go_router.dart';
@ -129,9 +129,10 @@ class _MobileWorkspaceStartScreenState
);
},
(error) {
return FlowyErrorPage.message(
error.toString(),
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
return Center(
child: AppFlowyErrorPage(
error: error,
),
);
},
);

View File

@ -15,6 +15,14 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:toastification/toastification.dart';
const _confirmText = 'DELETE MY ACCOUNT';
const _acceptableConfirmTexts = [
'delete my account',
'deletemyaccount',
'DELETE MY ACCOUNT',
'DELETEMYACCOUNT',
];
class AccountDeletionButton extends StatefulWidget {
const AccountDeletionButton({
super.key,
@ -25,12 +33,12 @@ class AccountDeletionButton extends StatefulWidget {
}
class _AccountDeletionButtonState extends State<AccountDeletionButton> {
final TextEditingController emailController = TextEditingController();
final textEditingController = TextEditingController();
final isCheckedNotifier = ValueNotifier(false);
@override
void dispose() {
emailController.dispose();
textEditingController.dispose();
isCheckedNotifier.dispose();
super.dispose();
}
@ -65,9 +73,10 @@ class _AccountDeletionButtonState extends State<AccountDeletionButton> {
const HSpace(32),
FlowyTextButton(
LocaleKeys.button_deleteAccount.tr(),
constraints: const BoxConstraints(minHeight: 32),
padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 10),
fillColor: Colors.transparent,
radius: Corners.s12Border,
radius: Corners.s8Border,
hoverColor: Theme.of(context).colorScheme.error.withOpacity(0.1),
fontColor: Theme.of(context).colorScheme.error,
fontHoverColor: Colors.white,
@ -76,6 +85,7 @@ class _AccountDeletionButtonState extends State<AccountDeletionButton> {
lineHeight: 18.0 / 12.0,
onPressed: () {
isCheckedNotifier.value = false;
textEditingController.clear();
showCancelAndDeleteDialog(
context: context,
@ -83,13 +93,21 @@ class _AccountDeletionButtonState extends State<AccountDeletionButton> {
LocaleKeys.newSettings_myAccount_deleteAccount_title.tr(),
description: '',
builder: (_) => _AccountDeletionDialog(
emailController: emailController,
controller: textEditingController,
isChecked: isCheckedNotifier,
),
onDelete: () => deleteMyAccount(
context,
emailController.text.trim(),
textEditingController.text.trim(),
isCheckedNotifier.value,
onSuccess: () {
Navigator.of(context).popUntil((route) {
if (route.settings.name == '/') {
return true;
}
return false;
});
},
),
);
},
@ -103,11 +121,11 @@ class _AccountDeletionButtonState extends State<AccountDeletionButton> {
class _AccountDeletionDialog extends StatelessWidget {
const _AccountDeletionDialog({
required this.emailController,
required this.controller,
required this.isChecked,
});
final TextEditingController emailController;
final TextEditingController controller;
final ValueNotifier<bool> isChecked;
@override
@ -125,8 +143,8 @@ class _AccountDeletionDialog extends StatelessWidget {
),
const VSpace(12.0),
FlowyTextField(
hintText: LocaleKeys.settings_user_email.tr(),
controller: emailController,
hintText: _confirmText,
controller: controller,
),
const VSpace(16),
Row(
@ -163,11 +181,19 @@ class _AccountDeletionDialog extends StatelessWidget {
}
}
bool _isConfirmTextValid(String text) {
// don't convert the text to lower case or upper case,
// just check if the text is in the list
return _acceptableConfirmTexts.contains(text);
}
Future<void> deleteMyAccount(
BuildContext context,
String email,
bool isChecked,
) async {
String confirmText,
bool isChecked, {
VoidCallback? onSuccess,
VoidCallback? onFailure,
}) async {
final bottomPadding = PlatformExtension.isMobile
? MediaQuery.of(context).viewInsets.bottom
: 0.0;
@ -184,34 +210,17 @@ Future<void> deleteMyAccount(
return;
}
// fetch the user email from server instead of reading from provider,
// this is to avoid the email doesn't match the real user's email
final userEmail = await UserBackendService.getCurrentUserProfile()
.fold((s) => s.email, (_) => null);
if (!context.mounted) {
return;
}
if (userEmail == null) {
showToastNotification(
context,
type: ToastificationType.error,
bottomPadding: bottomPadding,
message: LocaleKeys
.newSettings_myAccount_deleteAccount_failedToGetCurrentUser
.tr(),
);
return;
}
if (email.isEmpty || email.toLowerCase() != userEmail.toLowerCase()) {
if (confirmText.isEmpty || !_isConfirmTextValid(confirmText)) {
showToastNotification(
context,
type: ToastificationType.warning,
bottomPadding: bottomPadding,
message: LocaleKeys
.newSettings_myAccount_deleteAccount_emailValidationFailed
.newSettings_myAccount_deleteAccount_confirmTextValidationFailed
.tr(),
);
return;
@ -221,7 +230,7 @@ Future<void> deleteMyAccount(
await UserBackendService.deleteCurrentAccount().fold(
(s) {
Log.info('account deletion success, email: $email');
Log.info('account deletion success');
loading.stop();
showToastNotification(
@ -234,13 +243,7 @@ Future<void> deleteMyAccount(
// delay 1 second to make sure the toast notification is shown
Future.delayed(const Duration(seconds: 1), () async {
// pop to the home screen
Navigator.of(context).popUntil((route) {
if (route.settings.name == '/') {
return true;
}
return false;
});
onSuccess?.call();
// restart the application
await getIt<AuthService>().signOut();
@ -248,7 +251,7 @@ Future<void> deleteMyAccount(
});
},
(f) {
Log.error('account deletion failed, email: $email, error: $f');
Log.error('account deletion failed, error: $f');
loading.stop();
showToastNotification(
@ -257,6 +260,8 @@ Future<void> deleteMyAccount(
bottomPadding: bottomPadding,
message: f.msg,
);
onFailure?.call();
},
);
}

View File

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/util/int64_extension.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/billing/settings_billing_bloc.dart';
@ -21,8 +20,8 @@ import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../generated/locale_keys.g.dart';
@ -82,9 +81,10 @@ class _SettingsBillingViewState extends State<SettingsBillingView> {
if (state.error != null) {
return Padding(
padding: const EdgeInsets.all(16),
child: FlowyErrorPage.message(
state.error!.msg,
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
child: Center(
child: AppFlowyErrorPage(
error: state.error!,
),
),
);
}

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/util/int64_extension.dart';
import 'package:appflowy/util/theme_extension.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
@ -19,7 +20,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.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';
@ -75,9 +75,10 @@ class _SettingsPlanViewState extends State<SettingsPlanView> {
if (state.error != null) {
return Padding(
padding: const EdgeInsets.all(16),
child: FlowyErrorPage.message(
state.error!.msg,
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
child: Center(
child: AppFlowyErrorPage(
error: state.error!,
),
),
);
}

View File

@ -1,6 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart';
@ -23,6 +20,8 @@ import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SettingsShortcutsView extends StatefulWidget {

View File

@ -310,7 +310,7 @@
"cloudSupabaseUrlCanNotBeEmpty": "The supabase url can't be empty",
"cloudSupabaseAnonKey": "Supabase anon key",
"cloudSupabaseAnonKeyCanNotBeEmpty": "The anon key can't be empty",
"cloudAppFlowy": "AppFlowy Cloud Beta",
"cloudAppFlowy": "AppFlowy Cloud",
"cloudAppFlowySelfHost": "AppFlowy Cloud Self-hosted",
"appFlowyCloudUrlCanNotBeEmpty": "The cloud url can't be empty",
"clickToCopy": "Click to copy",
@ -1408,4 +1408,4 @@
}
}
}
}
}

View File

@ -942,7 +942,7 @@
"cloudSupabaseUrlCanNotBeEmpty": "The supabase url can't be empty",
"cloudSupabaseAnonKey": "Supabase anon key",
"cloudSupabaseAnonKeyCanNotBeEmpty": "The anon key can't be empty",
"cloudAppFlowy": "@:appName Cloud Beta",
"cloudAppFlowy": "@:appName Cloud",
"cloudAppFlowySelfHost": "@:appName Cloud Self-hosted",
"appFlowyCloudUrlCanNotBeEmpty": "The cloud url can't be empty",
"clickToCopy": "Click to copy",
@ -2251,16 +2251,17 @@
"deleteAccount": {
"title": "Delete Account",
"subtitle": "Permanently delete your account and all of your data.",
"description": "Permanently delete your account and remove access from all teamspaces.",
"description": "Permanently delete your account and remove access from all workspaces.",
"deleteMyAccount": "Delete my account",
"dialogTitle": "Delete account",
"dialogContent1": "Are you sure you want to permanently delete your account?",
"dialogContent2": "This action cannot be undone, and will remove access from all teamspaces, erasing your entire account, including private workspaces, and removing you from all shared workspaces.",
"confirmHint1": "Please type in your email address to confirm.",
"dialogContent2": "This action cannot be undone, and will remove access from all workspaces, erasing your entire account, including private workspaces, and removing you from all shared workspaces.",
"confirmHint1": "Please type \"DELETE MY ACCOUNT\" to confirm.",
"confirmHint2": "I understand that this action is irreversible and will permanently delete my account and all associated data.",
"confirmHint3": "DELETE MY ACCOUNT",
"checkToConfirmError": "You must check the box to confirm deletion",
"failedToGetCurrentUser": "Failed to get current user email",
"emailValidationFailed": "Your email address does not match the account email address",
"confirmTextValidationFailed": "Your confirmation text does not match \"DELETE MY ACCOUNT\"",
"deleteAccountSuccess": "Account deleted successfully"
}
},
@ -2304,7 +2305,7 @@
"unsplash": "Unsplash",
"pageCover": "Page cover",
"none": "None",
"photoPermissionDescription": "Allow access to the photo library for uploading images.",
"photoPermissionDescription": "@:appName needs access to your photos to let you add images to your documents",
"openSettings": "Open Settings",
"photoPermissionTitle": "@:appName would like to access your photo library",
"doNotAllow": "Don't Allow",