mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-09-11 19:17:43 +03:00
feat: settings manage data (#5265)
* feat: settings manage data page * fix: changes after merge * test: fix failing integration test * fix: missing localizations
This commit is contained in:
parent
38fa9f7942
commit
8273d66c50
@ -101,7 +101,7 @@ void main() {
|
||||
|
||||
// open settings and restore the location
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.manageData);
|
||||
await tester.restoreLocation();
|
||||
|
||||
expect(
|
||||
|
@ -39,10 +39,14 @@ extension AppFlowySettings on WidgetTester {
|
||||
|
||||
/// Restore the AppFlowy data storage location
|
||||
Future<void> restoreLocation() async {
|
||||
final button =
|
||||
find.byTooltip(LocaleKeys.settings_files_recoverLocationTooltips.tr());
|
||||
final button = find.text(LocaleKeys.settings_common_reset.tr());
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
await pumpAndSettle();
|
||||
|
||||
final confirmButton = find.text(LocaleKeys.button_confirm.tr());
|
||||
expect(confirmButton, findsOneWidget);
|
||||
await tapButton(confirmButton);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/presentation/helpers/helpers.dart';
|
||||
@ -6,7 +8,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../application/encrypt_secret_bloc.dart';
|
||||
@ -98,23 +99,20 @@ class _EncryptSecretScreenState extends State<EncryptSecretScreen> {
|
||||
controller: _textEditingController,
|
||||
hintText:
|
||||
LocaleKeys.settings_menu_inputTextFieldHint.tr(),
|
||||
onChanged: (p0) {},
|
||||
onChanged: (_) {},
|
||||
),
|
||||
),
|
||||
OkCancelButton(
|
||||
alignment: MainAxisAlignment.end,
|
||||
onOkPressed: () {
|
||||
context.read<EncryptSecretBloc>().add(
|
||||
EncryptSecretEvent.setEncryptSecret(
|
||||
_textEditingController.text,
|
||||
onOkPressed: () =>
|
||||
context.read<EncryptSecretBloc>().add(
|
||||
EncryptSecretEvent.setEncryptSecret(
|
||||
_textEditingController.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onCancelPressed: () {
|
||||
context.read<EncryptSecretBloc>().add(
|
||||
const EncryptSecretEvent.cancelInputSecret(),
|
||||
);
|
||||
},
|
||||
onCancelPressed: () => context
|
||||
.read<EncryptSecretBloc>()
|
||||
.add(const EncryptSecretEvent.cancelInputSecret()),
|
||||
mode: TextButtonMode.normal,
|
||||
),
|
||||
const VSpace(6),
|
||||
|
@ -12,8 +12,8 @@ enum SettingsPage {
|
||||
// NEW
|
||||
account,
|
||||
workspace,
|
||||
manageData,
|
||||
// OLD
|
||||
files,
|
||||
notifications,
|
||||
cloud,
|
||||
shortcuts,
|
||||
|
@ -0,0 +1,493 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/appflowy_cache_manager.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/startup/tasks/rust_sdk.dart';
|
||||
import 'package:appflowy/workspace/application/settings/setting_file_importer_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_export_file_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/file_picker/file_picker_service.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class SettingsManageDataView extends StatelessWidget {
|
||||
const SettingsManageDataView({super.key, required this.userProfile});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<SettingsLocationCubit>(
|
||||
create: (_) => SettingsLocationCubit(),
|
||||
child: BlocBuilder<SettingsLocationCubit, SettingsLocationState>(
|
||||
builder: (context, state) {
|
||||
return SettingsBody(
|
||||
title: LocaleKeys.settings_manageDataPage_title.tr(),
|
||||
description: LocaleKeys.settings_manageDataPage_description.tr(),
|
||||
children: [
|
||||
SettingsCategory(
|
||||
title:
|
||||
LocaleKeys.settings_manageDataPage_dataStorage_title.tr(),
|
||||
tooltip:
|
||||
LocaleKeys.settings_manageDataPage_dataStorage_tooltip.tr(),
|
||||
actions: [
|
||||
if (state.mapOrNull(didReceivedPath: (_) => true) == true)
|
||||
SettingAction(
|
||||
icon: const FlowySvg(FlowySvgs.restore_s),
|
||||
label: LocaleKeys.settings_common_reset.tr(),
|
||||
onPressed: () => SettingsAlertDialog(
|
||||
title: LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_resetDialog_title
|
||||
.tr(),
|
||||
subtitle: LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_resetDialog_description
|
||||
.tr(),
|
||||
implyLeading: true,
|
||||
confirm: () async {
|
||||
final directory =
|
||||
await appFlowyApplicationDataDirectory();
|
||||
final path = directory.path;
|
||||
if (!context.mounted ||
|
||||
state.mapOrNull(didReceivedPath: (e) => e.path) ==
|
||||
path) {
|
||||
return;
|
||||
}
|
||||
|
||||
await context
|
||||
.read<SettingsLocationCubit>()
|
||||
.resetDataStoragePathToApplicationDefault();
|
||||
await runAppFlowy(isAnon: true);
|
||||
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
},
|
||||
).show(context),
|
||||
),
|
||||
],
|
||||
children: state
|
||||
.map(
|
||||
initial: (_) => [const CircularProgressIndicator()],
|
||||
didReceivedPath: (event) => [
|
||||
_CurrentPath(path: event.path),
|
||||
_DataPathActions(currentPath: event.path),
|
||||
],
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_manageDataPage_importData_title.tr(),
|
||||
tooltip:
|
||||
LocaleKeys.settings_manageDataPage_importData_tooltip.tr(),
|
||||
children: const [_ImportDataField()],
|
||||
),
|
||||
if (kDebugMode) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_files_exportData.tr(),
|
||||
children: const [SettingsExportFileWidget()],
|
||||
),
|
||||
],
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_manageDataPage_cache_title.tr(),
|
||||
children: [
|
||||
SingleSettingAction(
|
||||
labelMaxLines: 4,
|
||||
label: LocaleKeys.settings_manageDataPage_cache_description
|
||||
.tr(),
|
||||
buttonLabel:
|
||||
LocaleKeys.settings_manageDataPage_cache_title.tr(),
|
||||
onPressed: () {
|
||||
SettingsAlertDialog(
|
||||
title: LocaleKeys
|
||||
.settings_manageDataPage_cache_dialog_title
|
||||
.tr(),
|
||||
subtitle: LocaleKeys
|
||||
.settings_manageDataPage_cache_dialog_description
|
||||
.tr(),
|
||||
confirm: () async {
|
||||
await getIt<FlowyCacheManager>().clearAllCache();
|
||||
if (context.mounted) {
|
||||
showSnackBarMessage(
|
||||
context,
|
||||
LocaleKeys
|
||||
.settings_manageDataPage_cache_dialog_successHint
|
||||
.tr(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
).show(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
// Uncomment if we need to enable encryption
|
||||
// if (userProfile.authenticator == AuthenticatorPB.Supabase) ...[
|
||||
// const SettingsCategorySpacer(),
|
||||
// BlocProvider(
|
||||
// create: (_) => EncryptSecretBloc(user: userProfile),
|
||||
// child: SettingsCategory(
|
||||
// title: LocaleKeys.settings_manageDataPage_encryption_title
|
||||
// .tr(),
|
||||
// tooltip: LocaleKeys
|
||||
// .settings_manageDataPage_encryption_tooltip
|
||||
// .tr(),
|
||||
// description: userProfile.encryptionType ==
|
||||
// EncryptionTypePB.NoEncryption
|
||||
// ? LocaleKeys
|
||||
// .settings_manageDataPage_encryption_descriptionNoEncryption
|
||||
// .tr()
|
||||
// : LocaleKeys
|
||||
// .settings_manageDataPage_encryption_descriptionEncrypted
|
||||
// .tr(),
|
||||
// children: [_EncryptDataSetting(userProfile: userProfile)],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class _EncryptDataSetting extends StatelessWidget {
|
||||
// const _EncryptDataSetting({required this.userProfile});
|
||||
|
||||
// final UserProfilePB userProfile;
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return BlocProvider<EncryptSecretBloc>.value(
|
||||
// value: context.read<EncryptSecretBloc>(),
|
||||
// child: BlocBuilder<EncryptSecretBloc, EncryptSecretState>(
|
||||
// builder: (context, state) {
|
||||
// if (state.loadingState?.isLoading() == true) {
|
||||
// return const Row(
|
||||
// children: [
|
||||
// SizedBox(
|
||||
// width: 20,
|
||||
// height: 20,
|
||||
// child: CircularProgressIndicator(
|
||||
// strokeWidth: 3,
|
||||
// ),
|
||||
// ),
|
||||
// HSpace(16),
|
||||
// FlowyText.medium(
|
||||
// 'Encrypting data...',
|
||||
// fontSize: 14,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (userProfile.encryptionType == EncryptionTypePB.NoEncryption) {
|
||||
// return Row(
|
||||
// children: [
|
||||
// SizedBox(
|
||||
// height: 42,
|
||||
// child: FlowyTextButton(
|
||||
// LocaleKeys.settings_manageDataPage_encryption_action.tr(),
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 24,
|
||||
// vertical: 12,
|
||||
// ),
|
||||
// fontWeight: FontWeight.w600,
|
||||
// radius: BorderRadius.circular(12),
|
||||
// fillColor: Theme.of(context).colorScheme.primary,
|
||||
// hoverColor: const Color(0xFF005483),
|
||||
// fontHoverColor: Colors.white,
|
||||
// onPressed: () => SettingsAlertDialog(
|
||||
// title: LocaleKeys
|
||||
// .settings_manageDataPage_encryption_dialog_title
|
||||
// .tr(),
|
||||
// subtitle: LocaleKeys
|
||||
// .settings_manageDataPage_encryption_dialog_description
|
||||
// .tr(),
|
||||
// confirmLabel: LocaleKeys
|
||||
// .settings_manageDataPage_encryption_dialog_title
|
||||
// .tr(),
|
||||
// implyLeading: true,
|
||||
// // Generate a secret one time for the user
|
||||
// confirm: () => context
|
||||
// .read<EncryptSecretBloc>()
|
||||
// .add(const EncryptSecretEvent.setEncryptSecret('')),
|
||||
// ).show(context),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// // Show encryption secret for copy/save
|
||||
// return const SizedBox.shrink();
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class _ImportDataField extends StatefulWidget {
|
||||
const _ImportDataField();
|
||||
|
||||
@override
|
||||
State<_ImportDataField> createState() => _ImportDataFieldState();
|
||||
}
|
||||
|
||||
class _ImportDataFieldState extends State<_ImportDataField> {
|
||||
final _fToast = FToast();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fToast.init(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_fToast.removeQueuedCustomToasts();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<SettingFileImportBloc>(
|
||||
create: (context) => SettingFileImportBloc(),
|
||||
child: BlocConsumer<SettingFileImportBloc, SettingFileImportState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.successOrFail != current.successOrFail,
|
||||
listener: (_, state) => state.successOrFail?.fold(
|
||||
(_) => _showToast(LocaleKeys.settings_menu_importSuccess.tr()),
|
||||
(_) => _showToast(LocaleKeys.settings_menu_importFailed.tr()),
|
||||
),
|
||||
builder: (context, state) {
|
||||
return DottedBorder(
|
||||
radius: const Radius.circular(8),
|
||||
dashPattern: const [2, 2],
|
||||
borderType: BorderType.RRect,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
// When dragging files are enabled
|
||||
// FlowyText.regular('Drag file here or'),
|
||||
// const VSpace(8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 42,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.settings_manageDataPage_importData_action
|
||||
.tr(),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
fontWeight: FontWeight.w600,
|
||||
radius: BorderRadius.circular(12),
|
||||
fillColor: Theme.of(context).colorScheme.primary,
|
||||
hoverColor: const Color(0xFF005483),
|
||||
fontHoverColor: Colors.white,
|
||||
onPressed: () async {
|
||||
final path = await getIt<FilePickerService>()
|
||||
.getDirectoryPath();
|
||||
if (path == null || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.read<SettingFileImportBloc>().add(
|
||||
SettingFileImportEvent
|
||||
.importAppFlowyDataFolder(
|
||||
path,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const VSpace(8),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.settings_manageDataPage_importData_description
|
||||
.tr(),
|
||||
// 'Supported filetypes:\nCSV, Notion, Text, and Markdown',
|
||||
maxLines: 3,
|
||||
lineHeight: 1.5,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showToast(String message) {
|
||||
_fToast.showToast(
|
||||
child: FlowyMessageToast(message: message),
|
||||
gravity: ToastGravity.CENTER,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CurrentPath extends StatefulWidget {
|
||||
const _CurrentPath({required this.path});
|
||||
|
||||
final String path;
|
||||
|
||||
@override
|
||||
State<_CurrentPath> createState() => _CurrentPathState();
|
||||
}
|
||||
|
||||
class _CurrentPathState extends State<_CurrentPath> {
|
||||
Timer? linkCopiedTimer;
|
||||
bool showCopyMessage = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
linkCopiedTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerDown: (_) => _copyLink(widget.path),
|
||||
child: FlowyHover(
|
||||
style: const HoverStyle.transparent(),
|
||||
resetHoverOnRebuild: false,
|
||||
builder: (_, isHovering) => FlowyText.regular(
|
||||
widget.path,
|
||||
lineHeight: 1.5,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
decoration: isHovering ? TextDecoration.underline : null,
|
||||
color: const Color(0xFF005483),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(8),
|
||||
showCopyMessage
|
||||
? SizedBox(
|
||||
height: 36,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_actions_copiedHint
|
||||
.tr(),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
fontWeight: FontWeight.w500,
|
||||
radius: BorderRadius.circular(12),
|
||||
fillColor: AFThemeExtension.of(context).tint7,
|
||||
hoverColor: AFThemeExtension.of(context).tint7,
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 100),
|
||||
child: SettingAction(
|
||||
tooltip: LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_actions_copy
|
||||
.tr(),
|
||||
icon: const FlowySvg(
|
||||
FlowySvgs.copy_s,
|
||||
size: Size.square(24),
|
||||
),
|
||||
onPressed: () => _copyLink(widget.path),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _copyLink(String? path) {
|
||||
AppFlowyClipboard.setData(text: path);
|
||||
setState(() => showCopyMessage = true);
|
||||
linkCopiedTimer?.cancel();
|
||||
linkCopiedTimer = Timer(
|
||||
const Duration(milliseconds: 300),
|
||||
() => mounted ? setState(() => showCopyMessage = false) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DataPathActions extends StatelessWidget {
|
||||
const _DataPathActions({required this.currentPath});
|
||||
|
||||
final String currentPath;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 42,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.settings_manageDataPage_dataStorage_actions_change.tr(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
fontWeight: FontWeight.w600,
|
||||
radius: BorderRadius.circular(12),
|
||||
fillColor: Theme.of(context).colorScheme.primary,
|
||||
hoverColor: const Color(0xFF005483),
|
||||
fontHoverColor: Colors.white,
|
||||
onPressed: () async {
|
||||
final path = await getIt<FilePickerService>().getDirectoryPath();
|
||||
if (!context.mounted || path == null || currentPath == path) {
|
||||
return;
|
||||
}
|
||||
|
||||
await context.read<SettingsLocationCubit>().setCustomPath(path);
|
||||
await runAppFlowy(isAnon: true);
|
||||
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
const HSpace(16),
|
||||
SettingAction(
|
||||
tooltip: LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_actions_openTooltip
|
||||
.tr(),
|
||||
label:
|
||||
LocaleKeys.settings_manageDataPage_dataStorage_actions_open.tr(),
|
||||
icon: const FlowySvg(FlowySvgs.folder_m, size: Size.square(16)),
|
||||
onPressed: () => afLaunchUrlString('file://$currentPath'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_manage_data_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_workspace_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_customize_shortcuts_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_system_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
@ -79,8 +79,8 @@ class SettingsDialog extends StatelessWidget {
|
||||
);
|
||||
case SettingsPage.workspace:
|
||||
return SettingsWorkspaceView(userProfile: user);
|
||||
case SettingsPage.files:
|
||||
return const SettingsFileSystemView();
|
||||
case SettingsPage.manageData:
|
||||
return SettingsManageDataView(userProfile: user);
|
||||
case SettingsPage.notifications:
|
||||
return const SettingsNotificationsView();
|
||||
case SettingsPage.cloud:
|
||||
|
@ -21,11 +21,7 @@ class SettingAction extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconWidget = tooltip != null && tooltip!.isNotEmpty
|
||||
? FlowyTooltip(message: tooltip, child: icon)
|
||||
: icon;
|
||||
|
||||
return GestureDetector(
|
||||
final child = GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onPressed,
|
||||
child: SizedBox(
|
||||
@ -36,7 +32,7 @@ class SettingAction extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
iconWidget,
|
||||
icon,
|
||||
if (label != null) ...[
|
||||
const HSpace(4),
|
||||
FlowyText.regular(label!),
|
||||
@ -47,5 +43,14 @@ class SettingAction extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (tooltip != null) {
|
||||
return FlowyTooltip(
|
||||
message: tooltip!,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
@ -1,167 +0,0 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/setting_file_importer_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/file_picker/file_picker_service.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class ImportAppFlowyData extends StatefulWidget {
|
||||
const ImportAppFlowyData({super.key});
|
||||
|
||||
@override
|
||||
State<ImportAppFlowyData> createState() => _ImportAppFlowyDataState();
|
||||
}
|
||||
|
||||
class _ImportAppFlowyDataState extends State<ImportAppFlowyData> {
|
||||
final _fToast = FToast();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fToast.init(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SettingFileImportBloc(),
|
||||
child: BlocListener<SettingFileImportBloc, SettingFileImportState>(
|
||||
listener: (context, state) {
|
||||
state.successOrFail?.fold(
|
||||
(_) {
|
||||
_showToast(LocaleKeys.settings_menu_importSuccess.tr());
|
||||
},
|
||||
(_) {
|
||||
_showToast(LocaleKeys.settings_menu_importFailed.tr());
|
||||
},
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<SettingFileImportBloc, SettingFileImportState>(
|
||||
builder: (context, state) {
|
||||
final List<Widget> children = [
|
||||
const ImportAppFlowyDataButton(),
|
||||
const VSpace(6),
|
||||
];
|
||||
|
||||
if (state.loadingState.isLoading()) {
|
||||
children.add(const AppFlowyDataImportingTip());
|
||||
} else {
|
||||
children.add(const AppFlowyDataImportTip());
|
||||
}
|
||||
|
||||
return Column(children: children);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showToast(String message) {
|
||||
_fToast.showToast(
|
||||
child: FlowyMessageToast(message: message),
|
||||
gravity: ToastGravity.CENTER,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppFlowyDataImportTip extends StatelessWidget {
|
||||
const AppFlowyDataImportTip({super.key});
|
||||
|
||||
final url = "https://docs.appflowy.io/docs/appflowy/product/data-storage";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Opacity(
|
||||
opacity: 0.6,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: LocaleKeys.settings_menu_importAppFlowyDataDescription.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall!,
|
||||
),
|
||||
TextSpan(
|
||||
text: " ${LocaleKeys.settings_menu_importGuide.tr()} ",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => afLaunchUrlString(url),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImportAppFlowyDataButton extends StatefulWidget {
|
||||
const ImportAppFlowyDataButton({super.key});
|
||||
|
||||
@override
|
||||
State<ImportAppFlowyDataButton> createState() =>
|
||||
_ImportAppFlowyDataButtonState();
|
||||
}
|
||||
|
||||
class _ImportAppFlowyDataButtonState extends State<ImportAppFlowyDataButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SettingFileImportBloc, SettingFileImportState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: FlowyButton(
|
||||
disable: state.loadingState.isLoading(),
|
||||
text:
|
||||
FlowyText(LocaleKeys.settings_menu_importAppFlowyData.tr()),
|
||||
onTap: () async {
|
||||
final path =
|
||||
await getIt<FilePickerService>().getDirectoryPath();
|
||||
if (path == null || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.read<SettingFileImportBloc>().add(
|
||||
SettingFileImportEvent.importAppFlowyDataFolder(path),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.loadingState.isLoading())
|
||||
const LinearProgressIndicator(minHeight: 1),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppFlowyDataImportingTip extends StatelessWidget {
|
||||
const AppFlowyDataImportingTip({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Opacity(
|
||||
opacity: 0.6,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: LocaleKeys.settings_menu_importingAppFlowyDataTip.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall!,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_exporter_widget.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import '../../../../../generated/locale_keys.g.dart';
|
||||
|
||||
class SettingsExportFileWidget extends StatefulWidget {
|
||||
const SettingsExportFileWidget({
|
||||
super.key,
|
||||
});
|
||||
const SettingsExportFileWidget({super.key});
|
||||
|
||||
@override
|
||||
State<SettingsExportFileWidget> createState() =>
|
||||
|
@ -1,80 +0,0 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/appflowy_cache_manager.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsFileCacheWidget extends StatelessWidget {
|
||||
const SettingsFileCacheWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5.0),
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.settings_files_clearCache.tr(),
|
||||
fontSize: 13,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const VSpace(8),
|
||||
Opacity(
|
||||
opacity: 0.6,
|
||||
child: FlowyText(
|
||||
LocaleKeys.settings_files_clearCacheDesc.tr(),
|
||||
fontSize: 10,
|
||||
maxLines: 3,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const _ClearCacheButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ClearCacheButton extends StatelessWidget {
|
||||
const _ClearCacheButton();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
tooltipText: LocaleKeys.settings_files_clearCache.tr(),
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.delete_s,
|
||||
size: const Size.square(18),
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
onPressed: () {
|
||||
NavigatorAlertDialog(
|
||||
title: LocaleKeys.settings_files_areYouSureToClearCache.tr(),
|
||||
confirm: () async {
|
||||
await getIt<FlowyCacheManager>().clearAllCache();
|
||||
if (context.mounted) {
|
||||
showSnackBarMessage(
|
||||
context,
|
||||
LocaleKeys.settings_files_clearCacheSuccess.tr(),
|
||||
);
|
||||
}
|
||||
},
|
||||
).show(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/file_picker/file_picker_service.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/buttons/secondary_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import '../../../../../generated/locale_keys.g.dart';
|
||||
import '../../../../../startup/startup.dart';
|
||||
import '../../../../../startup/tasks/prelude.dart';
|
||||
|
||||
class SettingsFileLocationCustomizer extends StatefulWidget {
|
||||
const SettingsFileLocationCustomizer({super.key});
|
||||
|
||||
@override
|
||||
State<SettingsFileLocationCustomizer> createState() =>
|
||||
SettingsFileLocationCustomizerState();
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
class SettingsFileLocationCustomizerState
|
||||
extends State<SettingsFileLocationCustomizer> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<SettingsLocationCubit>(
|
||||
create: (_) => SettingsLocationCubit(),
|
||||
child: BlocBuilder<SettingsLocationCubit, SettingsLocationState>(
|
||||
builder: (context, state) {
|
||||
return state.when(
|
||||
initial: () => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
didReceivedPath: (path) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// display file paths.
|
||||
_path(path),
|
||||
|
||||
// display the icons
|
||||
_buttons(path),
|
||||
],
|
||||
),
|
||||
const VSpace(10),
|
||||
IntrinsicHeight(
|
||||
child: Opacity(
|
||||
opacity: 0.6,
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.settings_menu_customPathPrompt.tr(),
|
||||
maxLines: 13,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _path(String path) {
|
||||
return Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
LocaleKeys.settings_files_defaultLocation.tr(),
|
||||
fontSize: 13,
|
||||
overflow: TextOverflow.visible,
|
||||
).padding(horizontal: 5),
|
||||
const VSpace(5),
|
||||
_CopyableText(
|
||||
usingPath: path,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buttons(String path) {
|
||||
final List<Widget> children = [];
|
||||
children.addAll([
|
||||
Flexible(
|
||||
child: _ChangeStoragePathButton(
|
||||
usingPath: path,
|
||||
),
|
||||
),
|
||||
const HSpace(10),
|
||||
]);
|
||||
|
||||
children.add(
|
||||
_OpenStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
);
|
||||
|
||||
children.add(
|
||||
_RecoverDefaultStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
);
|
||||
|
||||
return Flexible(
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.end, children: children),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CopyableText extends StatelessWidget {
|
||||
const _CopyableText({
|
||||
required this.usingPath,
|
||||
});
|
||||
|
||||
final String usingPath;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyHover(
|
||||
builder: (_, onHover) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: usingPath));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: FlowyText(
|
||||
LocaleKeys.settings_files_pathCopiedSnackbar.tr(),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 20,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText.regular(
|
||||
usingPath,
|
||||
fontSize: 12,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (onHover) ...[
|
||||
const HSpace(5),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.settings_files_copy.tr(),
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChangeStoragePathButton extends StatefulWidget {
|
||||
const _ChangeStoragePathButton({
|
||||
required this.usingPath,
|
||||
});
|
||||
|
||||
final String usingPath;
|
||||
|
||||
@override
|
||||
State<_ChangeStoragePathButton> createState() =>
|
||||
_ChangeStoragePathButtonState();
|
||||
}
|
||||
|
||||
class _ChangeStoragePathButtonState extends State<_ChangeStoragePathButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyTooltip(
|
||||
message: LocaleKeys.settings_files_changeLocationTooltips.tr(),
|
||||
child: SecondaryTextButton(
|
||||
LocaleKeys.settings_files_change.tr(),
|
||||
mode: TextButtonMode.small,
|
||||
onPressed: () async {
|
||||
// pick the new directory and reload app
|
||||
final path = await getIt<FilePickerService>().getDirectoryPath();
|
||||
if (path == null || widget.usingPath == path) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
await context.read<SettingsLocationCubit>().setCustomPath(path);
|
||||
await runAppFlowy(isAnon: true);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OpenStorageButton extends StatelessWidget {
|
||||
const _OpenStorageButton({
|
||||
required this.usingPath,
|
||||
});
|
||||
|
||||
final String usingPath;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
tooltipText: LocaleKeys.settings_files_openCurrentDataFolder.tr(),
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.open_folder_lg,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
onPressed: () async {
|
||||
final uri = Directory(usingPath).uri;
|
||||
await afLaunchUrl(uri, context: context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RecoverDefaultStorageButton extends StatefulWidget {
|
||||
const _RecoverDefaultStorageButton({
|
||||
required this.usingPath,
|
||||
});
|
||||
|
||||
final String usingPath;
|
||||
|
||||
@override
|
||||
State<_RecoverDefaultStorageButton> createState() =>
|
||||
_RecoverDefaultStorageButtonState();
|
||||
}
|
||||
|
||||
class _RecoverDefaultStorageButtonState
|
||||
extends State<_RecoverDefaultStorageButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
tooltipText: LocaleKeys.settings_files_recoverLocationTooltips.tr(),
|
||||
icon: const FlowySvg(
|
||||
FlowySvgs.restore_s,
|
||||
size: Size.square(20),
|
||||
),
|
||||
onPressed: () async {
|
||||
// reset to the default directory and reload app
|
||||
final directory = await appFlowyApplicationDataDirectory();
|
||||
final path = directory.path;
|
||||
if (widget.usingPath == path) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
await context
|
||||
.read<SettingsLocationCubit>()
|
||||
.resetDataStoragePathToApplicationDefault();
|
||||
await runAppFlowy(isAnon: true);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category_spacer.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/setting_file_import_appflowy_data_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_export_file_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_cache_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_customize_location_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class SettingsFileSystemView extends StatelessWidget {
|
||||
const SettingsFileSystemView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsBody(
|
||||
title: LocaleKeys.settings_menu_files.tr(),
|
||||
children: const [
|
||||
SettingsFileLocationCustomizer(),
|
||||
SettingsCategorySpacer(),
|
||||
if (kDebugMode) ...[
|
||||
SettingsExportFileWidget(),
|
||||
],
|
||||
ImportAppFlowyData(),
|
||||
SettingsCategorySpacer(),
|
||||
SettingsFileCacheWidget(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -72,10 +72,10 @@ class SettingsMenu extends StatelessWidget {
|
||||
changeSelectedPage: changeSelectedPage,
|
||||
),
|
||||
SettingsMenuElement(
|
||||
page: SettingsPage.files,
|
||||
page: SettingsPage.manageData,
|
||||
selectedPage: currentPage,
|
||||
label: LocaleKeys.settings_menu_files.tr(),
|
||||
icon: const Icon(Icons.file_present_outlined),
|
||||
label: LocaleKeys.settings_manageDataPage_menuLabel.tr(),
|
||||
icon: const FlowySvg(FlowySvgs.settings_data_m),
|
||||
changeSelectedPage: changeSelectedPage,
|
||||
),
|
||||
SettingsMenuElement(
|
||||
|
@ -417,6 +417,52 @@
|
||||
"deleteWorkspace": "Delete workspace"
|
||||
}
|
||||
},
|
||||
"manageDataPage": {
|
||||
"menuLabel": "Manage data",
|
||||
"title": "Manage data",
|
||||
"description": "Manage data local storage or Import your existing data into Appflowy. You can secure your data with end to end encryption.",
|
||||
"dataStorage": {
|
||||
"title": "File storage location",
|
||||
"tooltip": "The location where your files are stored",
|
||||
"actions": {
|
||||
"change": "Change path",
|
||||
"open": "Open folder",
|
||||
"openTooltip": "Open current data folder location",
|
||||
"copy": "Copy path",
|
||||
"copiedHint": "Link copied!"
|
||||
},
|
||||
"resetDialog": {
|
||||
"title": "Are you sure?",
|
||||
"description": "Resetting the path to the default data location will not delete your data. If you want to re-import your current data, you should copy the path of your current location first."
|
||||
}
|
||||
},
|
||||
"importData": {
|
||||
"title": "Import data",
|
||||
"tooltip": "Import data from AppFlowy backups/data folders",
|
||||
"description": "Copy data from an external AppFlowy data folder and import it into the current AppFlowy data folder",
|
||||
"action": "Browse folder"
|
||||
},
|
||||
"encryption": {
|
||||
"title": "Encryption",
|
||||
"tooltip": "Manage how your data is stored and encrypted",
|
||||
"descriptionNoEncryption": "Turning on encryption will encrypt all data. This can not be undone.",
|
||||
"descriptionEncrypted": "Your data is encrypted.",
|
||||
"action": "Encrypt data",
|
||||
"dialog": {
|
||||
"title": "Encrypt all your data?",
|
||||
"description": "Encrypting all your data will keep your data safe and secure. This action can NOT be undone. Are you sure you want to continue?"
|
||||
}
|
||||
},
|
||||
"cache": {
|
||||
"title": "Clear cache",
|
||||
"description": "If you encounter issues with images not loading or fonts not displaying correctly, try clearing your cache. This action will not remove your user data.",
|
||||
"dialog": {
|
||||
"title": "Are you sure?",
|
||||
"description": "Clearing the cache will cause images and fonts to be re-downloaded on load. This action will not remove or modify your data.",
|
||||
"successHint": "Cache cleared!"
|
||||
}
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"reset": "Reset"
|
||||
},
|
||||
@ -1624,4 +1670,4 @@
|
||||
"betaTooltip": "We currently only support searching for pages",
|
||||
"fromTrashHint": "From trash"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user