fix: mobile launch review (#4214)

* fix: text for changing field type

* fix: field name text field autofocus

* fix: don't allow insert left on primary field

* fix: edit field from view setting

* fix: edit non-current database view

* fix: row detail toggle hidden fields button

* fix: database view name autofocus

* fix: mobile date picker

* fix: deleting select option not deleting column

* fix: duplicate and delete view

* fix: mobile tab bar header left  padding
This commit is contained in:
Richard Shiue 2023-12-27 11:56:06 +08:00 committed by GitHub
parent df8409178b
commit 1cde5a0df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 169 additions and 84 deletions

View File

@ -402,7 +402,6 @@ class MobileRowDetailPageContentState
children: [
if (rowDetailState.numHiddenFields != 0) ...[
const ToggleHiddenFieldsVisibilityButton(),
const VSpace(12),
],
MobileRowDetailCreateFieldButton(
viewId: viewId,

View File

@ -20,6 +20,7 @@ class OptionTextField extends StatelessWidget {
Widget build(BuildContext context) {
return FlowyOptionTile.textField(
controller: controller,
autofocus: true,
textFieldPadding: const EdgeInsets.symmetric(horizontal: 12.0),
onTextChanged: onTextChanged,
leftIcon: FlowySvg(

View File

@ -72,10 +72,8 @@ class _MobileDateCellEditScreenState extends State<MobileDateCellEditScreen> {
child: const Center(child: DragHandler()),
),
_buildHeader(),
Expanded(
child: _DateCellEditBody(
dateCellController: widget.controller,
),
_DateCellEditBody(
dateCellController: widget.controller,
),
],
),

View File

@ -24,7 +24,8 @@ void showCreateFieldBottomSheet(BuildContext context, String viewId) {
minChildSize: 0.7,
builder: (context, controller) => FieldOptions(
scrollController: controller,
onAddField: (type) async {
mode: FieldOptionMode.add,
onSelectFieldType: (type) async {
final optionValues = await context.push<FieldOptionValues>(
Uri(
path: MobileNewPropertyScreen.routeName,

View File

@ -8,6 +8,8 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'mobile_field_type_option_editor.dart';
const _supportedFieldTypes = [
FieldType.RichText,
FieldType.Number,
@ -22,18 +24,20 @@ const _supportedFieldTypes = [
class FieldOptions extends StatelessWidget {
const FieldOptions({
super.key,
required this.onAddField,
required this.mode,
required this.onSelectFieldType,
this.scrollController,
});
final void Function(FieldType) onAddField;
final FieldOptionMode mode;
final void Function(FieldType) onSelectFieldType;
final ScrollController? scrollController;
@override
Widget build(BuildContext context) {
return Column(
children: [
const _FieldHeader(),
_FieldHeader(mode: mode),
const VSpace(12.0),
Expanded(
child: SingleChildScrollView(
@ -46,7 +50,7 @@ class FieldOptions extends StatelessWidget {
.map(
(e) => _Field(
type: e,
onTap: () => onAddField(e),
onTap: () => onSelectFieldType(e),
),
)
.toList(),
@ -59,7 +63,9 @@ class FieldOptions extends StatelessWidget {
}
class _FieldHeader extends StatelessWidget {
const _FieldHeader();
const _FieldHeader({required this.mode});
final FieldOptionMode mode;
@override
Widget build(BuildContext context) {
@ -76,7 +82,10 @@ class _FieldHeader extends StatelessWidget {
),
),
FlowyText.medium(
LocaleKeys.titleBar_addField.tr(),
switch (mode) {
FieldOptionMode.add => LocaleKeys.grid_field_newProperty.tr(),
FieldOptionMode.edit => LocaleKeys.grid_field_editProperty.tr(),
},
fontSize: 17.0,
),
const HSpace(120),

View File

@ -378,7 +378,8 @@ class _PropertyType extends StatelessWidget {
minChildSize: 0.7,
builder: (context, controller) => FieldOptions(
scrollController: controller,
onAddField: (type) {
mode: FieldOptionMode.edit,
onSelectFieldType: (type) {
onSelected(type);
context.pop();
},

View File

@ -92,15 +92,16 @@ class _QuickEditFieldState extends State<QuickEditField> {
await service.hide();
},
),
FlowyOptionTile.text(
showTopBorder: false,
text: LocaleKeys.grid_field_insertLeft.tr(),
leftIcon: const FlowySvg(FlowySvgs.insert_left_s),
onTap: () async {
context.pop();
await service.insertLeft();
},
),
if (!widget.fieldInfo.isPrimary)
FlowyOptionTile.text(
showTopBorder: false,
text: LocaleKeys.grid_field_insertLeft.tr(),
leftIcon: const FlowySvg(FlowySvgs.insert_left_s),
onTap: () async {
context.pop();
await service.insertLeft();
},
),
FlowyOptionTile.text(
showTopBorder: false,
text: LocaleKeys.grid_field_insertRight.tr(),

View File

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

View File

@ -22,9 +22,7 @@ import 'database_view_quick_actions.dart';
/// [MobileDatabaseViewList] shows a list of all the views in the database and
/// adds a button to create a new database view.
class MobileDatabaseViewList extends StatelessWidget {
const MobileDatabaseViewList({super.key, required this.databaseController});
final DatabaseController databaseController;
const MobileDatabaseViewList({super.key});
@override
Widget build(BuildContext context) {
@ -37,7 +35,6 @@ class MobileDatabaseViewList extends StatelessWidget {
...views.mapIndexed(
(index, view) => MobileDatabaseViewListButton(
view: view,
databaseController: databaseController,
showTopBorder: index == 0,
),
),
@ -93,12 +90,10 @@ class MobileDatabaseViewListButton extends StatelessWidget {
const MobileDatabaseViewListButton({
super.key,
required this.view,
required this.databaseController,
required this.showTopBorder,
});
final ViewPB view;
final DatabaseController databaseController;
final bool showTopBorder;
@override
@ -116,7 +111,11 @@ class MobileDatabaseViewListButton extends StatelessWidget {
.add(DatabaseTabBarEvent.selectView(view.id));
},
leftIcon: _buildViewIconButton(context, view),
trailing: _trailing(context, isSelected),
trailing: _trailing(
context,
state.tabBarControllerByViewId[view.id]!.controller,
isSelected,
),
showTopBorder: showTopBorder,
);
},
@ -130,7 +129,11 @@ class MobileDatabaseViewListButton extends StatelessWidget {
);
}
Widget _trailing(BuildContext context, bool isSelected) {
Widget _trailing(
BuildContext context,
DatabaseController databaseController,
bool isSelected,
) {
final more = FlowyIconButton(
icon: FlowySvg(
FlowySvgs.three_dots_s,
@ -240,6 +243,7 @@ class _MobileCreateDatabaseViewState extends State<MobileCreateDatabaseView> {
selectedLayout: layoutType,
),
FlowyOptionTile.textField(
autofocus: true,
controller: controller,
),
const VSpace(20),

View File

@ -2,9 +2,12 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'edit_database_view_screen.dart';
@ -35,34 +38,49 @@ class _MobileDatabaseViewQuickActionsState
? MobileEditDatabaseViewScreen(
databaseController: widget.databaseController,
)
: Padding(
padding: const EdgeInsets.only(top: 8, bottom: 38),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_actionButton(context, _Action.edit),
_divider(),
_actionButton(context, _Action.duplicate),
_divider(),
_actionButton(context, _Action.delete),
_divider(),
],
),
);
: _quickActions(context, widget.view);
}
Widget _actionButton(BuildContext context, _Action action) {
Widget _quickActions(BuildContext context, ViewPB view) {
final isInline = view.childViews.isNotEmpty;
return Padding(
padding: const EdgeInsets.only(top: 8, bottom: 38),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_actionButton(context, _Action.edit, () {
setState(() => isEditing = true);
}),
if (!isInline) ...[
_divider(),
_actionButton(context, _Action.duplicate, () {
context.read<ViewBloc>().add(const ViewEvent.duplicate());
context.pop();
}),
_divider(),
_actionButton(context, _Action.delete, () {
context.read<ViewBloc>().add(const ViewEvent.delete());
context.pop();
}),
_divider(),
],
],
),
);
}
Widget _actionButton(
BuildContext context,
_Action action,
VoidCallback onTap,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: MobileQuickActionButton(
icon: action.icon,
text: action.label,
color: action.color(context),
onTap: () {
if (action == _Action.edit) {
setState(() => isEditing = true);
}
},
onTap: onTap,
),
);
}

View File

@ -209,6 +209,7 @@ class _NameAndIconState extends State<_NameAndIcon> {
@override
Widget build(BuildContext context) {
return FlowyOptionTile.textField(
autofocus: true,
controller: textEditingController,
onTextChanged: (text) {
context.read<ViewBloc>().add(ViewEvent.rename(text));

View File

@ -27,9 +27,11 @@ class FlowyOptionTile extends StatelessWidget {
vertical: 2.0,
),
this.isSelected = false,
this.onValueChanged,
this.textFieldHintText,
this.onTextChanged,
this.onTextSubmitted,
this.autofocus,
});
factory FlowyOptionTile.text({
@ -67,6 +69,7 @@ class FlowyOptionTile extends StatelessWidget {
Widget? leftIcon,
Widget? trailing,
String? textFieldHintText,
bool autofocus = false,
}) {
return FlowyOptionTile._(
type: FlowyOptionTileType.textField,
@ -81,6 +84,7 @@ class FlowyOptionTile extends StatelessWidget {
textFieldHintText: textFieldHintText,
onTextChanged: onTextChanged,
onTextSubmitted: onTextSubmitted,
autofocus: autofocus,
);
}
@ -114,6 +118,7 @@ class FlowyOptionTile extends StatelessWidget {
required String text,
required bool isSelected,
required void Function(bool value) onValueChanged,
void Function()? onTap,
bool showTopBorder = true,
bool showBottomBorder = true,
Widget? leftIcon,
@ -122,7 +127,8 @@ class FlowyOptionTile extends StatelessWidget {
type: FlowyOptionTileType.toggle,
text: text,
controller: null,
onTap: () => onValueChanged(!isSelected),
onTap: onTap ?? () => onValueChanged(!isSelected),
onValueChanged: onValueChanged,
showTopBorder: showTopBorder,
showBottomBorder: showBottomBorder,
leading: leftIcon,
@ -143,10 +149,14 @@ class FlowyOptionTile extends StatelessWidget {
// only used in checkbox or switcher
final bool isSelected;
// only used in switcher
final void Function(bool value)? onValueChanged;
// only used in textfield
final String? textFieldHintText;
final void Function(String value)? onTextChanged;
final void Function(String value)? onTextSubmitted;
final bool? autofocus;
final FlowyOptionTileType type;
@ -229,6 +239,7 @@ class FlowyOptionTile extends StatelessWidget {
alignment: Alignment.center,
child: TextField(
controller: controller,
autofocus: autofocus ?? false,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
border: InputBorder.none,

View File

@ -1,5 +1,6 @@
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
import 'package:appflowy/plugins/database_view/application/tab_bar_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/mobile_database_controls.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
@ -65,7 +66,7 @@ class _DatabaseViewList extends StatelessWidget {
return const SizedBox.shrink();
}
final children = state.tabBars.mapIndexed((index, tabBar) {
final children = state.tabBars.mapIndexed<Widget>((index, tabBar) {
return Padding(
padding: EdgeInsetsDirectional.only(
start: index == 0 ? 0 : 2,
@ -78,6 +79,8 @@ class _DatabaseViewList extends StatelessWidget {
);
}).toList();
children.insert(0, HSpace(GridSize.leadingHeaderPadding));
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: children),

View File

@ -110,12 +110,9 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
child: const TabBarHeader(),
);
} else {
return Padding(
padding: EdgeInsets.only(
left: GridSize.leadingHeaderPadding,
right: 8,
),
child: const MobileTabBarHeader(),
return const Padding(
padding: EdgeInsets.only(right: 8),
child: MobileTabBarHeader(),
);
}
},

View File

@ -12,6 +12,7 @@ import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_button.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
@ -314,24 +315,63 @@ class ToggleHiddenFieldsVisibilityButton extends StatelessWidget {
),
};
return SizedBox(
height: 30,
child: FlowyButton(
text: FlowyText.medium(text, color: Theme.of(context).hintColor),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
leftIcon: RotatedBox(
quarterTurns: state.showHiddenFields ? 1 : 3,
child: FlowySvg(
FlowySvgs.arrow_left_s,
if (PlatformExtension.isDesktop) {
return SizedBox(
height: 30,
child: FlowyButton(
text: FlowyText.medium(text, color: Theme.of(context).hintColor),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
leftIcon: RotatedBox(
quarterTurns: state.showHiddenFields ? 1 : 3,
child: FlowySvg(
FlowySvgs.arrow_left_s,
color: Theme.of(context).hintColor,
),
),
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
onTap: () => context.read<RowDetailBloc>().add(
const RowDetailEvent.toggleHiddenFieldVisibility(),
),
),
);
} else {
return ConstrainedBox(
constraints: const BoxConstraints(minWidth: double.infinity),
child: TextButton.icon(
style: Theme.of(context).textButtonTheme.style?.copyWith(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
side: BorderSide.none,
),
),
overlayColor: MaterialStateProperty.all<Color>(
Theme.of(context).hoverColor,
),
alignment: AlignmentDirectional.centerStart,
splashFactory: NoSplash.splashFactory,
padding: const MaterialStatePropertyAll(
EdgeInsets.symmetric(vertical: 14, horizontal: 6),
),
),
label: FlowyText.medium(
text,
fontSize: 15,
color: Theme.of(context).hintColor,
),
),
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
onTap: () => context.read<RowDetailBloc>().add(
const RowDetailEvent.toggleHiddenFieldVisibility(),
onPressed: () => context
.read<RowDetailBloc>()
.add(const RowDetailEvent.toggleHiddenFieldVisibility()),
icon: RotatedBox(
quarterTurns: state.showHiddenFields ? 1 : 3,
child: FlowySvg(
FlowySvgs.arrow_left_s,
color: Theme.of(context).hintColor,
),
),
);
),
),
);
}
},
);
}

View File

@ -99,9 +99,7 @@ class MobileDatabaseControls extends StatelessWidget {
value: context.read<DatabaseTabBarBloc>(),
),
],
child: MobileDatabaseViewList(
databaseController: controller,
),
child: const MobileDatabaseViewList(),
);
},
);

View File

@ -842,14 +842,14 @@ impl DatabaseEditor {
type_option.delete_option(&option.id);
}
notify_did_update_database_field(&self.database, field_id)?;
self
.database
.lock()
.fields
.update_field(field_id, |update| {
update.set_type_option(field.field_type, Some(type_option.to_type_option_data()));
});
let view_editor = self.database_views.get_view_editor(view_id).await?;
update_field_type_option_fn(
&self.database,
&view_editor,
type_option.to_type_option_data(),
field.clone(),
)
.await?;
self
.update_cell_with_changeset(view_id, row_id, field_id, cell_changeset)