mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-09-11 19:17:43 +03:00
fix: launch review 0.5.8 (#5367)
This commit is contained in:
parent
b7bc847107
commit
b2978e0d6c
@ -0,0 +1,32 @@
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('MoreViewActions', () {
|
||||
testWidgets('can duplicate and delete from menu', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final pageFinder = find.byType(ViewItem);
|
||||
expect(pageFinder, findsNWidgets(1));
|
||||
|
||||
// Duplicate
|
||||
await tester.openMoreViewActions();
|
||||
await tester.duplicateByMoreViewActions();
|
||||
|
||||
expect(pageFinder, findsNWidgets(2));
|
||||
|
||||
// Delete
|
||||
await tester.openMoreViewActions();
|
||||
await tester.deleteByMoreViewActions();
|
||||
|
||||
expect(pageFinder, findsNWidgets(1));
|
||||
});
|
||||
});
|
||||
}
|
@ -6,6 +6,9 @@ import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_create_and_delete_test.dart'
|
||||
as document_create_and_delete_test;
|
||||
import 'document_option_action_test.dart' as document_option_action_test;
|
||||
import 'document_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
import 'document_more_actions_test.dart' as document_more_actions_test;
|
||||
import 'document_text_direction_test.dart' as document_text_direction_test;
|
||||
import 'document_with_cover_image_test.dart' as document_with_cover_image_test;
|
||||
import 'document_with_database_test.dart' as document_with_database_test;
|
||||
@ -16,8 +19,6 @@ import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
|
||||
import 'document_with_outline_block_test.dart' as document_with_outline_block;
|
||||
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
|
||||
import 'edit_document_test.dart' as document_edit_test;
|
||||
import 'document_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@ -38,4 +39,5 @@ void startTesting() {
|
||||
document_option_action_test.main();
|
||||
document_with_image_block_test.main();
|
||||
document_inline_page_reference_test.main();
|
||||
document_more_actions_test.main();
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
@ -22,15 +27,13 @@ import 'package:appflowy/workspace/presentation/notifications/widgets/flowy_tab.
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_tab_bar.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/more_view_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/common_view_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.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/widget/buttons/primary_button.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'emoji.dart';
|
||||
@ -564,6 +567,44 @@ extension CommonOperations on WidgetTester {
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> openMoreViewActions() async {
|
||||
final button = find.byType(MoreViewActions);
|
||||
await tap(button);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Presses on the Duplicate ViewAction in the [MoreViewActions] popup.
|
||||
///
|
||||
/// [openMoreViewActions] must be called beforehand!
|
||||
///
|
||||
Future<void> duplicateByMoreViewActions() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(ListView),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewActionType.duplicate,
|
||||
),
|
||||
);
|
||||
await tap(button);
|
||||
await pump();
|
||||
}
|
||||
|
||||
/// Presses on the Delete ViewAction in the [MoreViewActions] popup.
|
||||
///
|
||||
/// [openMoreViewActions] must be called beforehand!
|
||||
///
|
||||
Future<void> deleteByMoreViewActions() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(ListView),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewActionType.delete,
|
||||
),
|
||||
);
|
||||
await tap(button);
|
||||
await pump();
|
||||
}
|
||||
}
|
||||
|
||||
extension SettingsFinder on CommonFinders {
|
||||
|
@ -29,9 +29,7 @@ class FontPickerScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
class LanguagePickerPage extends StatefulWidget {
|
||||
const LanguagePickerPage({
|
||||
super.key,
|
||||
});
|
||||
const LanguagePickerPage({super.key});
|
||||
|
||||
@override
|
||||
State<LanguagePickerPage> createState() => _LanguagePickerPageState();
|
||||
@ -43,7 +41,6 @@ class _LanguagePickerPageState extends State<LanguagePickerPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
availableFonts = _availableFonts;
|
||||
}
|
||||
|
||||
@ -90,7 +87,6 @@ class _FontSelectorState extends State<FontSelector> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
availableFonts = _availableFonts;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_header.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_search_bar.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
@ -83,7 +84,9 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
|
||||
},
|
||||
itemBuilder: (context, emojiId, emoji, callback) {
|
||||
return FlowyIconButton(
|
||||
iconPadding: const EdgeInsets.all(2.0),
|
||||
iconPadding: PlatformExtension.isWindows
|
||||
? const EdgeInsets.only(bottom: 2.0)
|
||||
: const EdgeInsets.all(2),
|
||||
icon: FlowyText(
|
||||
emoji,
|
||||
fontSize: 28.0,
|
||||
|
@ -113,7 +113,7 @@ typedef WorkspaceSettingNotifyValue
|
||||
class UserWorkspaceListener {
|
||||
UserWorkspaceListener();
|
||||
|
||||
PublishNotifier<WorkspaceSettingNotifyValue>? _settingChangedNotifier =
|
||||
final PublishNotifier<WorkspaceSettingNotifyValue> _settingChangedNotifier =
|
||||
PublishNotifier();
|
||||
|
||||
FolderNotificationListener? _listener;
|
||||
@ -122,7 +122,7 @@ class UserWorkspaceListener {
|
||||
void Function(WorkspaceSettingNotifyValue)? onSettingUpdated,
|
||||
}) {
|
||||
if (onSettingUpdated != null) {
|
||||
_settingChangedNotifier?.addPublishListener(onSettingUpdated);
|
||||
_settingChangedNotifier.addPublishListener(onSettingUpdated);
|
||||
}
|
||||
|
||||
// The "current-workspace" is predefined in the backend. Do not try to
|
||||
@ -140,13 +140,11 @@ class UserWorkspaceListener {
|
||||
switch (ty) {
|
||||
case FolderNotification.DidUpdateWorkspaceSetting:
|
||||
result.fold(
|
||||
(payload) => _settingChangedNotifier?.value =
|
||||
(payload) => _settingChangedNotifier.value =
|
||||
FlowyResult.success(WorkspaceSettingPB.fromBuffer(payload)),
|
||||
(error) =>
|
||||
_settingChangedNotifier?.value = FlowyResult.failure(error),
|
||||
(error) => _settingChangedNotifier.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -154,8 +152,6 @@ class UserWorkspaceListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
|
||||
_settingChangedNotifier?.dispose();
|
||||
_settingChangedNotifier = null;
|
||||
_settingChangedNotifier.dispose();
|
||||
}
|
||||
}
|
||||
|
5
frontend/appflowy_flutter/lib/util/theme_extension.dart
Normal file
5
frontend/appflowy_flutter/lib/util/theme_extension.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension IsLightMode on ThemeData {
|
||||
bool get isLightMode => brightness == Brightness.light;
|
||||
}
|
@ -8,6 +8,6 @@ extension TimeFormatter on UserTimeFormatPB {
|
||||
}
|
||||
|
||||
final _toFormat = {
|
||||
UserTimeFormatPB.TwelveHour: DateFormat.Hm(),
|
||||
UserTimeFormatPB.TwentyFourHour: DateFormat.jm(),
|
||||
UserTimeFormatPB.TwentyFourHour: DateFormat.Hm(),
|
||||
UserTimeFormatPB.TwelveHour: DateFormat.jm(),
|
||||
};
|
||||
|
@ -56,7 +56,13 @@ class WorkspaceSettingsBloc
|
||||
?.role ??
|
||||
AFRolePB.Guest;
|
||||
|
||||
emit(state.copyWith(members: members, myRole: role));
|
||||
emit(
|
||||
state.copyWith(
|
||||
workspace: currentWorkspaceInList,
|
||||
members: members,
|
||||
myRole: role,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error('Failed to get or create current workspace');
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ class _WorkspaceIconState extends State<WorkspaceIcon> {
|
||||
child: EmojiText(
|
||||
emoji: widget.workspace.icon,
|
||||
fontSize: widget.iconSize,
|
||||
lineHeight: 1,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
@ -78,46 +79,46 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
|
||||
],
|
||||
),
|
||||
|
||||
// Enable when/if we need change email feature
|
||||
// // Only show change email if the user is authenticated and not using local auth
|
||||
// if (isAuthEnabled &&
|
||||
// state.userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||
// const SettingsCategorySpacer(),
|
||||
// SettingsCategory(
|
||||
// title: LocaleKeys.settings_accountPage_email_title.tr(),
|
||||
// children: [
|
||||
// SingleSettingAction(
|
||||
// label: state.userProfile.email,
|
||||
// buttonLabel: LocaleKeys
|
||||
// .settings_accountPage_email_actions_change
|
||||
// .tr(),
|
||||
// onPressed: () => SettingsAlertDialog(
|
||||
// title: LocaleKeys
|
||||
// .settings_accountPage_email_actions_change
|
||||
// .tr(),
|
||||
// confirmLabel: LocaleKeys.button_save.tr(),
|
||||
// confirm: () {
|
||||
// context.read<SettingsUserViewBloc>().add(
|
||||
// SettingsUserEvent.updateUserEmail(
|
||||
// _emailController.text,
|
||||
// ),
|
||||
// );
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// children: [
|
||||
// SettingsInputField(
|
||||
// label: LocaleKeys.settings_accountPage_email_title
|
||||
// .tr(),
|
||||
// value: state.userProfile.email,
|
||||
// hideActions: true,
|
||||
// textController: _emailController,
|
||||
// ),
|
||||
// ],
|
||||
// ).show(context),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// Only show email if the user is authenticated and not using local auth
|
||||
if (isAuthEnabled &&
|
||||
state.userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_accountPage_email_title.tr(),
|
||||
children: [
|
||||
FlowyText.regular(state.userProfile.email),
|
||||
// Enable when/if we need change email feature
|
||||
// SingleSettingAction(
|
||||
// label: state.userProfile.email,
|
||||
// buttonLabel: LocaleKeys
|
||||
// .settings_accountPage_email_actions_change
|
||||
// .tr(),
|
||||
// onPressed: () => SettingsAlertDialog(
|
||||
// title: LocaleKeys
|
||||
// .settings_accountPage_email_actions_change
|
||||
// .tr(),
|
||||
// confirmLabel: LocaleKeys.button_save.tr(),
|
||||
// confirm: () {
|
||||
// context.read<SettingsUserViewBloc>().add(
|
||||
// SettingsUserEvent.updateUserEmail(
|
||||
// _emailController.text,
|
||||
// ),
|
||||
// );
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// children: [
|
||||
// SettingsInputField(
|
||||
// label: LocaleKeys.settings_accountPage_email_title
|
||||
// .tr(),
|
||||
// value: state.userProfile.email,
|
||||
// hideActions: true,
|
||||
// textController: _emailController,
|
||||
// ),
|
||||
// ],
|
||||
// ).show(context),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
/// Enable when we have change password feature and 2FA
|
||||
// const SettingsCategorySpacer(),
|
||||
|
@ -9,6 +9,7 @@ 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/util/theme_extension.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';
|
||||
@ -55,6 +56,9 @@ class SettingsManageDataView extends StatelessWidget {
|
||||
actions: [
|
||||
if (state.mapOrNull(didReceivedPath: (_) => true) == true)
|
||||
SettingAction(
|
||||
tooltip: LocaleKeys
|
||||
.settings_manageDataPage_dataStorage_actions_resetTooltip
|
||||
.tr(),
|
||||
icon: const FlowySvg(FlowySvgs.restore_s),
|
||||
label: LocaleKeys.settings_common_reset.tr(),
|
||||
onPressed: () => SettingsAlertDialog(
|
||||
@ -375,6 +379,8 @@ class _CurrentPathState extends State<_CurrentPath> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isLM = Theme.of(context).isLightMode;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
@ -392,7 +398,9 @@ class _CurrentPathState extends State<_CurrentPath> {
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
decoration: isHovering ? TextDecoration.underline : null,
|
||||
color: const Color(0xFF005483),
|
||||
color: isLM
|
||||
? const Color(0xFF005483)
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
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';
|
||||
@ -18,12 +19,12 @@ import 'package:appflowy/workspace/presentation/settings/shared/af_dropdown_menu
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/document_color_setting_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_list_tile.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_actionable_input.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/settings_dashed_divider.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_input_field.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_radio_select.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart';
|
||||
@ -41,39 +42,22 @@ 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/dialog/styled_dialogs.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class SettingsWorkspaceView extends StatefulWidget {
|
||||
class SettingsWorkspaceView extends StatelessWidget {
|
||||
const SettingsWorkspaceView({super.key, required this.userProfile});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
State<SettingsWorkspaceView> createState() => _SettingsWorkspaceViewState();
|
||||
}
|
||||
|
||||
class _SettingsWorkspaceViewState extends State<SettingsWorkspaceView> {
|
||||
final TextEditingController _workspaceNameController =
|
||||
TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_workspaceNameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<WorkspaceSettingsBloc>(
|
||||
create: (context) => WorkspaceSettingsBloc()
|
||||
..add(WorkspaceSettingsEvent.initial(userProfile: widget.userProfile)),
|
||||
..add(WorkspaceSettingsEvent.initial(userProfile: userProfile)),
|
||||
child: BlocConsumer<WorkspaceSettingsBloc, WorkspaceSettingsState>(
|
||||
listener: (context, state) {
|
||||
if ((state.workspace?.name ?? '') != _workspaceNameController.text) {
|
||||
_workspaceNameController.text = state.workspace?.name ?? '';
|
||||
}
|
||||
|
||||
if (state.deleteWorkspace) {
|
||||
context.read<UserWorkspaceBloc>().add(
|
||||
UserWorkspaceEvent.deleteWorkspace(
|
||||
@ -97,44 +81,11 @@ class _SettingsWorkspaceViewState extends State<SettingsWorkspaceView> {
|
||||
description: LocaleKeys.settings_workspacePage_description.tr(),
|
||||
children: [
|
||||
// We don't allow changing workspace name/icon for local/offline
|
||||
if (state.workspace != null &&
|
||||
widget.userProfile.authenticator !=
|
||||
AuthenticatorPB.Local) ...[
|
||||
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_workspacePage_workspaceName_title
|
||||
.tr(),
|
||||
children: [
|
||||
SettingsActionableInput(
|
||||
controller: _workspaceNameController,
|
||||
onSave: (value) => _saveWorkspaceName(
|
||||
context,
|
||||
current: state.workspace!.name,
|
||||
name: value,
|
||||
),
|
||||
actions: [
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.button_save.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: () => _saveWorkspaceName(
|
||||
context,
|
||||
current: state.workspace!.name,
|
||||
name: _workspaceNameController.text,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
children: const [_WorkspaceNameSetting()],
|
||||
),
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_workspacePage_workspaceIcon_title
|
||||
@ -143,7 +94,10 @@ class _SettingsWorkspaceViewState extends State<SettingsWorkspaceView> {
|
||||
.settings_workspacePage_workspaceIcon_description
|
||||
.tr(),
|
||||
children: [
|
||||
_WorkspaceIconSetting(workspace: state.workspace!),
|
||||
_WorkspaceIconSetting(
|
||||
enableEdit: state.myRole.isOwner,
|
||||
workspace: state.workspace,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -195,9 +149,7 @@ class _SettingsWorkspaceViewState extends State<SettingsWorkspaceView> {
|
||||
title: LocaleKeys.settings_workspacePage_language_title.tr(),
|
||||
children: const [LanguageDropdown()],
|
||||
),
|
||||
if (state.workspace != null &&
|
||||
widget.userProfile.authenticator !=
|
||||
AuthenticatorPB.Local) ...[
|
||||
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||
SingleSettingAction(
|
||||
label: LocaleKeys.settings_workspacePage_manageWorkspace_title
|
||||
.tr(),
|
||||
@ -244,17 +196,115 @@ class _SettingsWorkspaceViewState extends State<SettingsWorkspaceView> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _saveWorkspaceName(
|
||||
BuildContext context, {
|
||||
required String current,
|
||||
class _WorkspaceNameSetting extends StatefulWidget {
|
||||
const _WorkspaceNameSetting();
|
||||
|
||||
@override
|
||||
State<_WorkspaceNameSetting> createState() => _WorkspaceNameSettingState();
|
||||
}
|
||||
|
||||
class _WorkspaceNameSettingState extends State<_WorkspaceNameSetting> {
|
||||
final TextEditingController workspaceNameController = TextEditingController();
|
||||
late final FocusNode focusNode;
|
||||
bool isEditing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
focusNode = FocusNode(
|
||||
onKeyEvent: (_, event) {
|
||||
if (event is KeyDownEvent &&
|
||||
event.logicalKey == LogicalKeyboardKey.escape &&
|
||||
isEditing &&
|
||||
mounted) {
|
||||
setState(() => isEditing = false);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
return KeyEventResult.ignored;
|
||||
},
|
||||
)..addListener(() {
|
||||
if (!focusNode.hasFocus && isEditing && mounted) {
|
||||
_saveWorkspaceName(name: workspaceNameController.text);
|
||||
setState(() => isEditing = false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
focusNode.dispose();
|
||||
workspaceNameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<WorkspaceSettingsBloc, WorkspaceSettingsState>(
|
||||
listener: (_, state) {
|
||||
final newName = state.workspace?.name;
|
||||
if (newName != null && newName != workspaceNameController.text) {
|
||||
workspaceNameController.text = newName;
|
||||
}
|
||||
},
|
||||
builder: (_, state) {
|
||||
if (isEditing) {
|
||||
return Flexible(
|
||||
child: SettingsInputField(
|
||||
textController: workspaceNameController,
|
||||
value: workspaceNameController.text,
|
||||
focusNode: focusNode..requestFocus(),
|
||||
onCancel: () => setState(() => isEditing = false),
|
||||
onSave: (_) {
|
||||
_saveWorkspaceName(name: workspaceNameController.text);
|
||||
setState(() => isEditing = false);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.5),
|
||||
child: FlowyText.regular(
|
||||
workspaceNameController.text,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
if (state.myRole.isOwner) ...[
|
||||
const HSpace(4),
|
||||
FlowyTooltip(
|
||||
message: LocaleKeys
|
||||
.settings_workspacePage_workspaceName_editTooltip
|
||||
.tr(),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => setState(() => isEditing = true),
|
||||
child: const FlowyHover(
|
||||
resetHoverOnRebuild: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: FlowySvg(FlowySvgs.edit_s),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _saveWorkspaceName({
|
||||
required String name,
|
||||
}) {
|
||||
if (name.isNotEmpty && name != current) {
|
||||
if (name.isNotEmpty) {
|
||||
context.read<WorkspaceSettingsBloc>().add(
|
||||
WorkspaceSettingsEvent.updateWorkspaceName(
|
||||
_workspaceNameController.text,
|
||||
),
|
||||
WorkspaceSettingsEvent.updateWorkspaceName(name),
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
@ -300,12 +350,21 @@ class LanguageDropdown extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _WorkspaceIconSetting extends StatelessWidget {
|
||||
const _WorkspaceIconSetting({required this.workspace});
|
||||
const _WorkspaceIconSetting({required this.enableEdit, this.workspace});
|
||||
|
||||
final UserWorkspacePB workspace;
|
||||
final bool enableEdit;
|
||||
final UserWorkspacePB? workspace;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (workspace == null) {
|
||||
return const SizedBox(
|
||||
height: 64,
|
||||
width: 64,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
height: 64,
|
||||
width: 64,
|
||||
@ -316,9 +375,9 @@ class _WorkspaceIconSetting extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(1),
|
||||
child: WorkspaceIcon(
|
||||
workspace: workspace,
|
||||
iconSize: workspace.icon.isNotEmpty == true ? 46 : 20,
|
||||
enableEdit: true,
|
||||
workspace: workspace!,
|
||||
iconSize: workspace!.icon.isNotEmpty == true ? 46 : 20,
|
||||
enableEdit: enableEdit,
|
||||
onSelected: (r) => context
|
||||
.read<WorkspaceSettingsBloc>()
|
||||
.add(WorkspaceSettingsEvent.updateWorkspaceIcon(r.emoji)),
|
||||
@ -508,6 +567,7 @@ class _DateTimeFormatLabel extends StatelessWidget {
|
||||
now.timeZoneName,
|
||||
],
|
||||
),
|
||||
maxLines: 2,
|
||||
fontSize: 16,
|
||||
color: AFThemeExtension.of(context).secondaryTextColor,
|
||||
);
|
||||
@ -712,6 +772,9 @@ class AppearanceSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
child: t != themeMode
|
||||
? null
|
||||
: const _SelectedModeIndicator(),
|
||||
),
|
||||
const VSpace(6),
|
||||
FlowyText.regular(getLabel(t), textAlign: TextAlign.center),
|
||||
@ -735,6 +798,38 @@ class AppearanceSelector extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
class _SelectedModeIndicator extends StatelessWidget {
|
||||
const _SelectedModeIndicator();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 4,
|
||||
left: 4,
|
||||
child: Material(
|
||||
shape: const CircleBorder(),
|
||||
elevation: 2,
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
height: 16,
|
||||
width: 16,
|
||||
child: const FlowySvg(
|
||||
FlowySvgs.settings_selected_theme_m,
|
||||
size: Size.square(16),
|
||||
blendMode: BlendMode.dstIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FontSelectorDropdown extends StatelessWidget {
|
||||
const _FontSelectorDropdown();
|
||||
|
||||
@ -777,6 +872,7 @@ class _FontSelectorDropdown extends StatelessWidget {
|
||||
selectedValue: appearance.font,
|
||||
value: font,
|
||||
label: font.fontFamilyDisplayName,
|
||||
fontFamily: font,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
@ -10,7 +12,12 @@ DropdownMenuEntry<T> buildDropdownMenuEntry<T>(
|
||||
T? selectedValue,
|
||||
Widget? leadingWidget,
|
||||
Widget? trailingWidget,
|
||||
String? fontFamily,
|
||||
}) {
|
||||
final fontFamilyUsed = fontFamily != null
|
||||
? getGoogleFontSafely(fontFamily).fontFamily ?? defaultFontFamily
|
||||
: defaultFontFamily;
|
||||
|
||||
return DropdownMenuEntry<T>(
|
||||
style: ButtonStyle(
|
||||
foregroundColor:
|
||||
@ -26,7 +33,12 @@ DropdownMenuEntry<T> buildDropdownMenuEntry<T>(
|
||||
leadingIcon: leadingWidget,
|
||||
labelWidget: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: FlowyText.medium(label, fontSize: 14, textAlign: TextAlign.start),
|
||||
child: FlowyText.medium(
|
||||
label,
|
||||
fontSize: 14,
|
||||
textAlign: TextAlign.start,
|
||||
fontFamily: fontFamilyUsed,
|
||||
),
|
||||
),
|
||||
trailingIcon: Row(
|
||||
children: [
|
||||
|
@ -1,9 +1,13 @@
|
||||
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/flutter/af_dropdown_menu.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class SettingsDropdown<T> extends StatefulWidget {
|
||||
const SettingsDropdown({
|
||||
@ -37,6 +41,10 @@ class _SettingsDropdownState<T> extends State<SettingsDropdown<T>> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fontFamily = context.read<AppearanceSettingsCubit>().state.font;
|
||||
final fontFamilyUsed =
|
||||
getGoogleFontSafely(fontFamily).fontFamily ?? defaultFontFamily;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -45,6 +53,10 @@ class _SettingsDropdownState<T> extends State<SettingsDropdown<T>> {
|
||||
expandedInsets: widget.expandWidth ? EdgeInsets.zero : null,
|
||||
initialSelection: widget.selectedOption,
|
||||
dropdownMenuEntries: widget.options,
|
||||
textStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge
|
||||
?.copyWith(fontFamily: fontFamilyUsed),
|
||||
menuStyle: MenuStyle(
|
||||
maximumSize:
|
||||
const MaterialStatePropertyAll(Size(double.infinity, 250)),
|
||||
|
@ -5,7 +5,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/af_role_pb_extension.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.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/members/workspace_member_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
@ -37,8 +36,6 @@ class WorkspaceMembersPage extends StatelessWidget {
|
||||
title: LocaleKeys.settings_appearance_members_title.tr(),
|
||||
children: [
|
||||
if (state.myRole.canInvite) const _InviteMember(),
|
||||
if (state.myRole.canInvite && state.members.isNotEmpty)
|
||||
const SettingsCategorySpacer(),
|
||||
if (state.members.isNotEmpty)
|
||||
_MemberList(
|
||||
members: state.members,
|
||||
|
@ -0,0 +1,10 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="12" fill="#14AE5C"/>
|
||||
<circle cx="12" cy="12" r="9" fill="white"/>
|
||||
<mask id="mask0_3623_112333" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
|
||||
<rect width="24" height="24" fill="#66CF80"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3623_112333)">
|
||||
<path d="M10.598 16.6L17.648 9.55L16.248 8.15L10.598 13.8L7.74805 10.95L6.34805 12.35L10.598 16.6ZM11.998 22C10.6147 22 9.31471 21.7375 8.09805 21.2125C6.88138 20.6875 5.82305 19.975 4.92305 19.075C4.02305 18.175 3.31055 17.1167 2.78555 15.9C2.26055 14.6833 1.99805 13.3833 1.99805 12C1.99805 10.6167 2.26055 9.31667 2.78555 8.1C3.31055 6.88333 4.02305 5.825 4.92305 4.925C5.82305 4.025 6.88138 3.3125 8.09805 2.7875C9.31471 2.2625 10.6147 2 11.998 2C13.3814 2 14.6814 2.2625 15.898 2.7875C17.1147 3.3125 18.173 4.025 19.073 4.925C19.973 5.825 20.6855 6.88333 21.2105 8.1C21.7355 9.31667 21.998 10.6167 21.998 12C21.998 13.3833 21.7355 14.6833 21.2105 15.9C20.6855 17.1167 19.973 18.175 19.073 19.075C18.173 19.975 17.1147 20.6875 15.898 21.2125C14.6814 21.7375 13.3814 22 11.998 22Z" fill="#66CF80"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -353,7 +353,8 @@
|
||||
"description": "Customize your workspace appearance, theme, font, text layout, date-/time-format, and language.",
|
||||
"workspaceName": {
|
||||
"title": "Workspace name",
|
||||
"savedMessage": "Saved workspace name"
|
||||
"savedMessage": "Saved workspace name",
|
||||
"editTooltip": "Edit workspace name"
|
||||
},
|
||||
"workspaceIcon": {
|
||||
"title": "Workspace icon",
|
||||
@ -429,7 +430,8 @@
|
||||
"open": "Open folder",
|
||||
"openTooltip": "Open current data folder location",
|
||||
"copy": "Copy path",
|
||||
"copiedHint": "Link copied!"
|
||||
"copiedHint": "Path copied!",
|
||||
"resetTooltip": "Reset to default location"
|
||||
},
|
||||
"resetDialog": {
|
||||
"title": "Are you sure?",
|
||||
@ -1673,4 +1675,4 @@
|
||||
"betaTooltip": "We currently only support searching for pages",
|
||||
"fromTrashHint": "From trash"
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ pub enum FolderNotification {
|
||||
Unknown = 0,
|
||||
/// Trigger after creating a workspace
|
||||
DidCreateWorkspace = 1,
|
||||
// /// Trigger after updating a workspace
|
||||
/// Trigger after updating a workspace
|
||||
DidUpdateWorkspace = 2,
|
||||
|
||||
DidUpdateWorkspaceViews = 3,
|
||||
|
Loading…
Reference in New Issue
Block a user