mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
df8409178b
commit
1cde5a0df6
@ -402,7 +402,6 @@ class MobileRowDetailPageContentState
|
||||
children: [
|
||||
if (rowDetailState.numHiddenFields != 0) ...[
|
||||
const ToggleHiddenFieldsVisibilityButton(),
|
||||
const VSpace(12),
|
||||
],
|
||||
MobileRowDetailCreateFieldButton(
|
||||
viewId: viewId,
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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(),
|
||||
|
@ -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(
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -99,9 +99,7 @@ class MobileDatabaseControls extends StatelessWidget {
|
||||
value: context.read<DatabaseTabBarBloc>(),
|
||||
),
|
||||
],
|
||||
child: MobileDatabaseViewList(
|
||||
databaseController: controller,
|
||||
),
|
||||
child: const MobileDatabaseViewList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user