fix: mobile field editing related bugs (#4534)

* fix: mobile field editing related bugs

* chore: review

* chore: restore podfile.lock
This commit is contained in:
Richard Shiue 2024-01-29 13:19:35 +08:00 committed by GitHub
parent 05a06980b9
commit 63c22feb8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 136 additions and 108 deletions

View File

@ -69,7 +69,7 @@ class _MobileNewPropertyScreenState extends State<MobileNewPropertyScreen> {
), ),
], ],
), ),
body: FieldOptionEditor( body: MobileFieldEditor(
mode: FieldOptionMode.add, mode: FieldOptionMode.add,
defaultValues: optionValues, defaultValues: optionValues,
onOptionValuesChanged: (optionValues) { onOptionValuesChanged: (optionValues) {

View File

@ -32,12 +32,12 @@ class MobileEditPropertyScreen extends StatefulWidget {
class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> { class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
late final FieldBackendService fieldService; late final FieldBackendService fieldService;
late FieldOptionValues field; late FieldOptionValues _fieldOptionValues;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
field = FieldOptionValues.fromField(field: widget.field.field); _fieldOptionValues = FieldOptionValues.fromField(field: widget.field.field);
fieldService = FieldBackendService( fieldService = FieldBackendService(
viewId: widget.viewId, viewId: widget.viewId,
fieldId: widget.field.id, fieldId: widget.field.id,
@ -49,76 +49,69 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
final viewId = widget.viewId; final viewId = widget.viewId;
final fieldId = widget.field.id; final fieldId = widget.field.id;
return Scaffold( return PopScope(
appBar: AppBar( onPopInvoked: (didPop) {
centerTitle: true, if (didPop) {
title: FlowyText.medium( context.pop(_fieldOptionValues);
LocaleKeys.grid_field_editProperty.tr(), }
), },
elevation: 0, child: Scaffold(
bottom: const PreferredSize( appBar: AppBar(
preferredSize: Size.fromHeight(1), centerTitle: true,
child: Divider( title: FlowyText.medium(
height: 1, LocaleKeys.grid_field_editProperty.tr(),
thickness: 1, ),
elevation: 0,
bottom: const PreferredSize(
preferredSize: Size.fromHeight(1),
child: Divider(
height: 1,
thickness: 1,
),
),
leading: AppBarBackButton(
onTap: () => context.pop(_fieldOptionValues),
), ),
), ),
leading: AppBarBackButton( body: MobileFieldEditor(
onTap: () => context.pop(field), mode: FieldOptionMode.edit,
), isPrimary: widget.field.isPrimary,
), defaultValues: _fieldOptionValues,
body: FieldOptionEditor( actions: [
mode: FieldOptionMode.edit, widget.field.fieldSettings?.visibility.isVisibleState() ?? true
isPrimary: widget.field.isPrimary, ? FieldOptionAction.hide
defaultValues: field, : FieldOptionAction.show,
actions: [ FieldOptionAction.duplicate,
if (widget.field.fieldSettings?.visibility.isVisibleState() ?? true) FieldOptionAction.delete,
FieldOptionAction.hide ],
else onOptionValuesChanged: (newFieldOptionValues) {
FieldOptionAction.show, setState(() {
FieldOptionAction.duplicate, _fieldOptionValues = newFieldOptionValues;
FieldOptionAction.delete, });
], },
onOptionValuesChanged: (newField) async { onAction: (action) {
if (newField.name != field.name) { final service = FieldServices(
await fieldService.updateField(name: newField.name);
}
if (newField.type != widget.field.fieldType) {
await fieldService.updateType(fieldType: newField.type);
}
final data = newField.getTypeOptionData();
if (data != null) {
await FieldBackendService.updateFieldTypeOption(
viewId: viewId, viewId: viewId,
fieldId: widget.field.id, fieldId: fieldId,
typeOptionData: data,
); );
} switch (action) {
// setState(() => field = newField); case FieldOptionAction.delete:
}, fieldService.delete();
onAction: (action) { context.pop();
final service = FieldServices( return;
viewId: viewId, case FieldOptionAction.duplicate:
fieldId: fieldId, fieldService.duplicate();
); break;
switch (action) { case FieldOptionAction.hide:
case FieldOptionAction.delete: service.hide();
service.delete(); break;
break; case FieldOptionAction.show:
case FieldOptionAction.duplicate: service.show();
service.duplicate(); break;
break; }
case FieldOptionAction.hide: context.pop(_fieldOptionValues);
service.hide(); },
break; ),
case FieldOptionAction.show:
service.show();
break;
}
context.pop(field);
},
), ),
); );
} }

View File

@ -12,6 +12,8 @@ import 'mobile_field_type_grid.dart';
import 'mobile_field_type_option_editor.dart'; import 'mobile_field_type_option_editor.dart';
import 'mobile_quick_field_editor.dart'; import 'mobile_quick_field_editor.dart';
/// Shows the field type grid and upon selection, allow users to edit the
/// field's properties and saving it when the user clicks save.
void showCreateFieldBottomSheet( void showCreateFieldBottomSheet(
BuildContext context, BuildContext context,
String viewId, { String viewId, {
@ -24,9 +26,10 @@ void showCreateFieldBottomSheet(
return DraggableScrollableSheet( return DraggableScrollableSheet(
expand: false, expand: false,
snap: true, snap: true,
initialChildSize: 0.7, initialChildSize: 0.97,
minChildSize: 0.7, minChildSize: 0.97,
builder: (context, controller) => FieldOptions( maxChildSize: 0.97,
builder: (context, controller) => MobileFieldTypeGrid(
scrollController: controller, scrollController: controller,
mode: FieldOptionMode.add, mode: FieldOptionMode.add,
onSelectFieldType: (type) async { onSelectFieldType: (type) async {
@ -52,22 +55,22 @@ void showCreateFieldBottomSheet(
); );
} }
/// Used to edit a field.
Future<FieldOptionValues?> showEditFieldScreen( Future<FieldOptionValues?> showEditFieldScreen(
BuildContext context, BuildContext context,
String viewId, String viewId,
FieldInfo field, FieldInfo field,
) async { ) {
final optionValues = await context.push<FieldOptionValues>( return context.push<FieldOptionValues>(
MobileEditPropertyScreen.routeName, MobileEditPropertyScreen.routeName,
extra: { extra: {
MobileEditPropertyScreen.argViewId: viewId, MobileEditPropertyScreen.argViewId: viewId,
MobileEditPropertyScreen.argField: field, MobileEditPropertyScreen.argField: field,
}, },
); );
return optionValues;
} }
/// Shows some quick field options in a bottom sheet.
void showQuickEditField( void showQuickEditField(
BuildContext context, BuildContext context,
String viewId, String viewId,
@ -89,6 +92,7 @@ void showQuickEditField(
); );
} }
/// Display a list of fields in the current database that users can choose from.
Future<String?> showFieldPicker( Future<String?> showFieldPicker(
BuildContext context, BuildContext context,
String? selectedFieldId, String? selectedFieldId,

View File

@ -23,8 +23,8 @@ const _supportedFieldTypes = [
FieldType.CreatedTime, FieldType.CreatedTime,
]; ];
class FieldOptions extends StatelessWidget { class MobileFieldTypeGrid extends StatelessWidget {
const FieldOptions({ const MobileFieldTypeGrid({
super.key, super.key,
required this.mode, required this.mode,
required this.onSelectFieldType, required this.onSelectFieldType,

View File

@ -159,8 +159,8 @@ enum FieldOptionAction {
delete, delete,
} }
class FieldOptionEditor extends StatefulWidget { class MobileFieldEditor extends StatefulWidget {
const FieldOptionEditor({ const MobileFieldEditor({
super.key, super.key,
required this.mode, required this.mode,
required this.defaultValues, required this.defaultValues,
@ -182,11 +182,12 @@ class FieldOptionEditor extends StatefulWidget {
final bool isPrimary; final bool isPrimary;
@override @override
State<FieldOptionEditor> createState() => _FieldOptionEditorState(); State<MobileFieldEditor> createState() => _MobileFieldEditorState();
} }
class _FieldOptionEditorState extends State<FieldOptionEditor> { class _MobileFieldEditorState extends State<MobileFieldEditor> {
final controller = TextEditingController(); final controller = TextEditingController();
bool isFieldNameChanged = false;
late FieldOptionValues values; late FieldOptionValues values;
@ -219,6 +220,7 @@ class _FieldOptionEditorState extends State<FieldOptionEditor> {
controller: controller, controller: controller,
type: values.type, type: values.type,
onTextChanged: (value) { onTextChanged: (value) {
isFieldNameChanged = true;
_updateOptionValues(name: value); _updateOptionValues(name: value);
}, },
), ),
@ -226,16 +228,18 @@ class _FieldOptionEditorState extends State<FieldOptionEditor> {
if (!widget.isPrimary) ...[ if (!widget.isPrimary) ...[
_PropertyType( _PropertyType(
type: values.type, type: values.type,
onSelected: (type) => setState( onSelected: (type) {
() { setState(
if (widget.mode == FieldOptionMode.add) { () {
controller.text = type.i18n; if (widget.mode == FieldOptionMode.add &&
_updateOptionValues(name: type.i18n, type: type); !isFieldNameChanged) {
} else { controller.text = type.i18n;
_updateOptionValues(name: type.i18n);
}
_updateOptionValues(type: type); _updateOptionValues(type: type);
} },
}, );
), },
), ),
const _Divider(), const _Divider(),
if (option.isNotEmpty) ...[ if (option.isNotEmpty) ...[
@ -446,9 +450,10 @@ class _PropertyType extends StatelessWidget {
return DraggableScrollableSheet( return DraggableScrollableSheet(
expand: false, expand: false,
snap: true, snap: true,
initialChildSize: 0.7, initialChildSize: 0.97,
minChildSize: 0.7, minChildSize: 0.97,
builder: (context, controller) => FieldOptions( maxChildSize: 0.97,
builder: (context, controller) => MobileFieldTypeGrid(
scrollController: controller, scrollController: controller,
mode: FieldOptionMode.edit, mode: FieldOptionMode.edit,
onSelectFieldType: (type) { onSelectFieldType: (type) {
@ -894,10 +899,10 @@ class _SelectOptionTile extends StatefulWidget {
final void Function(SelectOptionPB option) onUpdateOption; final void Function(SelectOptionPB option) onUpdateOption;
@override @override
State<_SelectOptionTile> createState() => __SelectOptionTileState(); State<_SelectOptionTile> createState() => _SelectOptionTileState();
} }
class __SelectOptionTileState extends State<_SelectOptionTile> { class _SelectOptionTileState extends State<_SelectOptionTile> {
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
late SelectOptionPB option; late SelectOptionPB option;

View File

@ -3,9 +3,11 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart';
import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_sheets.dart'; import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_sheets.dart';
import 'package:appflowy/mobile/presentation/database/field/mobile_field_type_option_editor.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/plugins/database/application/field/field_backend_service.dart'; import 'package:appflowy/plugins/database/application/field/field_backend_service.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart'; import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy/plugins/database/application/field/field_service.dart';
import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart'; import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -36,14 +38,15 @@ class _QuickEditFieldState extends State<QuickEditField> {
fieldId: widget.fieldInfo.field.id, fieldId: widget.fieldInfo.field.id,
); );
late FieldType fieldType;
late FieldVisibility fieldVisibility; late FieldVisibility fieldVisibility;
late FieldOptionValues _fieldOptionValues;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
fieldType = widget.fieldInfo.fieldType; _fieldOptionValues =
FieldOptionValues.fromField(field: widget.fieldInfo.field);
fieldVisibility = widget.fieldInfo.fieldSettings?.visibility ?? fieldVisibility = widget.fieldInfo.fieldSettings?.visibility ??
FieldVisibility.AlwaysShown; FieldVisibility.AlwaysShown;
controller.text = widget.fieldInfo.field.name; controller.text = widget.fieldInfo.field.name;
@ -52,7 +55,6 @@ class _QuickEditFieldState extends State<QuickEditField> {
@override @override
void dispose() { void dispose() {
controller.dispose(); controller.dispose();
super.dispose(); super.dispose();
} }
@ -64,7 +66,7 @@ class _QuickEditFieldState extends State<QuickEditField> {
const AppBarCloseButton(), const AppBarCloseButton(),
OptionTextField( OptionTextField(
controller: controller, controller: controller,
type: fieldType, type: _fieldOptionValues.type,
onTextChanged: (text) async { onTextChanged: (text) async {
await service.updateName(text); await service.updateName(text);
}, },
@ -77,18 +79,44 @@ class _QuickEditFieldState extends State<QuickEditField> {
widget.fieldInfo.field.freeze(); widget.fieldInfo.field.freeze();
final field = widget.fieldInfo.field.rebuild((field) { final field = widget.fieldInfo.field.rebuild((field) {
field.name = controller.text; field.name = controller.text;
field.fieldType = fieldType; field.fieldType = _fieldOptionValues.type;
field.typeOptionData =
_fieldOptionValues.getTypeOptionData() ?? [];
}); });
final optionValues = await showEditFieldScreen( final fieldOptionValues = await showEditFieldScreen(
context, context,
widget.viewId, widget.viewId,
widget.fieldInfo.copyWith(field: field), widget.fieldInfo.copyWith(field: field),
); );
if (optionValues != null) { if (fieldOptionValues != null) {
if (fieldOptionValues.name != _fieldOptionValues.name) {
await service.updateName(fieldOptionValues.name);
}
if (fieldOptionValues.type != _fieldOptionValues.type) {
await FieldBackendService.updateFieldType(
viewId: widget.viewId,
fieldId: widget.fieldInfo.id,
fieldType: fieldOptionValues.type,
);
}
final data = fieldOptionValues.getTypeOptionData();
if (data != null) {
await FieldBackendService.updateFieldTypeOption(
viewId: widget.viewId,
fieldId: widget.fieldInfo.id,
typeOptionData: data,
);
}
setState(() { setState(() {
fieldType = optionValues.type; _fieldOptionValues = fieldOptionValues;
controller.text = optionValues.name; controller.text = fieldOptionValues.name;
}); });
} else {
if (mounted) {
context.pop();
}
} }
}, },
), ),

View File

@ -220,9 +220,7 @@ class DatabaseFieldListTile extends StatelessWidget {
size: const Size.square(20), size: const Size.square(20),
), ),
showTopBorder: showTopBorder, showTopBorder: showTopBorder,
onTap: () { onTap: () => showEditFieldScreen(context, viewId, fieldInfo),
showEditFieldScreen(context, viewId, fieldInfo);
},
onValueChanged: (value) { onValueChanged: (value) {
final newVisibility = fieldInfo.visibility!.toggle(); final newVisibility = fieldInfo.visibility!.toggle();
context.read<DatabasePropertyBloc>().add( context.read<DatabasePropertyBloc>().add(