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,7 +49,13 @@ 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(
onPopInvoked: (didPop) {
if (didPop) {
context.pop(_fieldOptionValues);
}
},
child: Scaffold(
appBar: AppBar( appBar: AppBar(
centerTitle: true, centerTitle: true,
title: FlowyText.medium( title: FlowyText.medium(
@ -64,39 +70,24 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
), ),
), ),
leading: AppBarBackButton( leading: AppBarBackButton(
onTap: () => context.pop(field), onTap: () => context.pop(_fieldOptionValues),
), ),
), ),
body: FieldOptionEditor( body: MobileFieldEditor(
mode: FieldOptionMode.edit, mode: FieldOptionMode.edit,
isPrimary: widget.field.isPrimary, isPrimary: widget.field.isPrimary,
defaultValues: field, defaultValues: _fieldOptionValues,
actions: [ actions: [
if (widget.field.fieldSettings?.visibility.isVisibleState() ?? true) widget.field.fieldSettings?.visibility.isVisibleState() ?? true
FieldOptionAction.hide ? FieldOptionAction.hide
else : FieldOptionAction.show,
FieldOptionAction.show,
FieldOptionAction.duplicate, FieldOptionAction.duplicate,
FieldOptionAction.delete, FieldOptionAction.delete,
], ],
onOptionValuesChanged: (newField) async { onOptionValuesChanged: (newFieldOptionValues) {
if (newField.name != field.name) { setState(() {
await fieldService.updateField(name: newField.name); _fieldOptionValues = newFieldOptionValues;
} });
if (newField.type != widget.field.fieldType) {
await fieldService.updateType(fieldType: newField.type);
}
final data = newField.getTypeOptionData();
if (data != null) {
await FieldBackendService.updateFieldTypeOption(
viewId: viewId,
fieldId: widget.field.id,
typeOptionData: data,
);
}
// setState(() => field = newField);
}, },
onAction: (action) { onAction: (action) {
final service = FieldServices( final service = FieldServices(
@ -105,10 +96,11 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
); );
switch (action) { switch (action) {
case FieldOptionAction.delete: case FieldOptionAction.delete:
service.delete(); fieldService.delete();
break; context.pop();
return;
case FieldOptionAction.duplicate: case FieldOptionAction.duplicate:
service.duplicate(); fieldService.duplicate();
break; break;
case FieldOptionAction.hide: case FieldOptionAction.hide:
service.hide(); service.hide();
@ -117,9 +109,10 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
service.show(); service.show();
break; break;
} }
context.pop(field); context.pop(_fieldOptionValues);
}, },
), ),
),
); );
} }
} }

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) { if (widget.mode == FieldOptionMode.add &&
!isFieldNameChanged) {
controller.text = type.i18n; controller.text = type.i18n;
_updateOptionValues(name: type.i18n, type: type); _updateOptionValues(name: type.i18n);
} else {
_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(