mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: revamp database setting action sheets (#4570)
* chore: revamp database setting action sheets * chore: field picker improvement * chore: fix comments
This commit is contained in:
parent
9d28360887
commit
20391bfec4
@ -85,6 +85,39 @@ class AppBarDoneButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AppBarFilledDoneButton extends StatelessWidget {
|
||||||
|
const AppBarFilledDoneButton({super.key, required this.onTap});
|
||||||
|
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 4, 8, 8),
|
||||||
|
child: TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
enableFeedback: true,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: onTap,
|
||||||
|
child: FlowyText.medium(
|
||||||
|
LocaleKeys.button_done.tr(),
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AppBarMoreButton extends StatelessWidget {
|
class AppBarMoreButton extends StatelessWidget {
|
||||||
const AppBarMoreButton({
|
const AppBarMoreButton({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -4,7 +4,6 @@ export 'bottom_sheet_drag_handler.dart';
|
|||||||
export 'bottom_sheet_rename_widget.dart';
|
export 'bottom_sheet_rename_widget.dart';
|
||||||
export 'bottom_sheet_view_item.dart';
|
export 'bottom_sheet_view_item.dart';
|
||||||
export 'bottom_sheet_view_item_body.dart';
|
export 'bottom_sheet_view_item_body.dart';
|
||||||
export 'bottom_sheet_view_item_header.dart';
|
|
||||||
export 'bottom_sheet_view_page.dart';
|
export 'bottom_sheet_view_page.dart';
|
||||||
export 'default_mobile_action_pane.dart';
|
export 'default_mobile_action_pane.dart';
|
||||||
export 'show_mobile_bottom_sheet.dart';
|
export 'show_mobile_bottom_sheet.dart';
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
|
||||||
const MobileViewItemBottomSheetHeader({
|
|
||||||
super.key,
|
|
||||||
required this.view,
|
|
||||||
required this.showBackButton,
|
|
||||||
required this.onBack,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ViewPB view;
|
|
||||||
final bool showBackButton;
|
|
||||||
final VoidCallback onBack;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// back button,
|
|
||||||
showBackButton
|
|
||||||
? InkWell(
|
|
||||||
onTap: onBack,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_back_ios_new_rounded,
|
|
||||||
size: 24.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: FlowyButton(
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
text: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
),
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
onTap: () {
|
|
||||||
context.pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// title
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.6,
|
|
||||||
),
|
|
||||||
child: FlowyText.medium(
|
|
||||||
view.name,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const HSpace(24.0),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,10 +7,12 @@ import 'package:flutter/material.dart';
|
|||||||
Future<T?> showMobileBottomSheet<T>(
|
Future<T?> showMobileBottomSheet<T>(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required WidgetBuilder builder,
|
required WidgetBuilder builder,
|
||||||
|
bool useSafeArea = true,
|
||||||
bool isDragEnabled = true,
|
bool isDragEnabled = true,
|
||||||
bool showDragHandle = false,
|
bool showDragHandle = false,
|
||||||
bool showHeader = false,
|
bool showHeader = false,
|
||||||
// this field is only used if showHeader is true
|
// this field is only used if showHeader is true
|
||||||
|
bool showBackButton = false,
|
||||||
bool showCloseButton = false,
|
bool showCloseButton = false,
|
||||||
// this field is only used if showHeader is true
|
// this field is only used if showHeader is true
|
||||||
String title = '',
|
String title = '',
|
||||||
@ -20,7 +22,7 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
bool useRootNavigator = false,
|
bool useRootNavigator = false,
|
||||||
ShapeBorder? shape,
|
ShapeBorder? shape,
|
||||||
// the padding of the content, the padding of the header area is fixed
|
// the padding of the content, the padding of the header area is fixed
|
||||||
EdgeInsets padding = const EdgeInsets.all(0.0),
|
EdgeInsets padding = EdgeInsets.zero,
|
||||||
Color? backgroundColor,
|
Color? backgroundColor,
|
||||||
BoxConstraints? constraints,
|
BoxConstraints? constraints,
|
||||||
Color? barrierColor,
|
Color? barrierColor,
|
||||||
@ -32,10 +34,11 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
double maxChildSize = 0.8,
|
double maxChildSize = 0.8,
|
||||||
double initialChildSize = 0.51,
|
double initialChildSize = 0.51,
|
||||||
}) async {
|
}) async {
|
||||||
assert(() {
|
assert(
|
||||||
if (showCloseButton || title.isNotEmpty) assert(showHeader);
|
showHeader ||
|
||||||
return true;
|
title.isEmpty && !showCloseButton && !showBackButton && !showDoneButton,
|
||||||
}());
|
);
|
||||||
|
assert(!(showCloseButton && showBackButton));
|
||||||
|
|
||||||
shape ??= const RoundedRectangleBorder(
|
shape ??= const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.vertical(
|
borderRadius: BorderRadius.vertical(
|
||||||
@ -83,6 +86,7 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
children.add(
|
children.add(
|
||||||
_Header(
|
_Header(
|
||||||
showCloseButton: showCloseButton,
|
showCloseButton: showCloseButton,
|
||||||
|
showBackButton: showBackButton,
|
||||||
showDoneButton: showDoneButton,
|
showDoneButton: showDoneButton,
|
||||||
title: title,
|
title: title,
|
||||||
),
|
),
|
||||||
@ -147,23 +151,30 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
VSpace(MediaQuery.of(context).padding.bottom == 0 ? 28.0 : 16.0),
|
VSpace(MediaQuery.of(context).padding.bottom == 0 ? 28.0 : 16.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
return SafeArea(
|
return useSafeArea
|
||||||
child: Column(
|
? SafeArea(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: children,
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: children,
|
||||||
);
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Header extends StatelessWidget {
|
class _Header extends StatelessWidget {
|
||||||
const _Header({
|
const _Header({
|
||||||
|
required this.showBackButton,
|
||||||
required this.showCloseButton,
|
required this.showCloseButton,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.showDoneButton,
|
required this.showDoneButton,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool showBackButton;
|
||||||
final bool showCloseButton;
|
final bool showCloseButton;
|
||||||
final String title;
|
final String title;
|
||||||
final bool showDoneButton;
|
final bool showDoneButton;
|
||||||
@ -176,6 +187,11 @@ class _Header extends StatelessWidget {
|
|||||||
height: 44.0, // the height of the header area is fixed
|
height: 44.0, // the height of the header area is fixed
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
if (showBackButton)
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: AppBarBackButton(),
|
||||||
|
),
|
||||||
if (showCloseButton)
|
if (showCloseButton)
|
||||||
const Align(
|
const Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
@ -128,14 +128,17 @@ void showQuickEditField(
|
|||||||
/// Display a list of fields in the current database that users can choose from.
|
/// 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 title,
|
||||||
String? selectedFieldId,
|
String? selectedFieldId,
|
||||||
FieldController fieldController,
|
FieldController fieldController,
|
||||||
bool Function(FieldInfo fieldInfo) filterBy,
|
bool Function(FieldInfo fieldInfo) filterBy,
|
||||||
) {
|
) {
|
||||||
return showMobileBottomSheet<String>(
|
return showMobileBottomSheet<String>(
|
||||||
context,
|
context,
|
||||||
|
showDivider: false,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MobileFieldPickerList(
|
return MobileFieldPickerList(
|
||||||
|
title: title,
|
||||||
selectedFieldId: selectedFieldId,
|
selectedFieldId: selectedFieldId,
|
||||||
fieldController: fieldController,
|
fieldController: fieldController,
|
||||||
filterBy: filterBy,
|
filterBy: filterBy,
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||||
import 'package:appflowy/util/field_type_extension.dart';
|
import 'package:appflowy/util/field_type_extension.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -13,11 +12,13 @@ import 'package:go_router/go_router.dart';
|
|||||||
class MobileFieldPickerList extends StatefulWidget {
|
class MobileFieldPickerList extends StatefulWidget {
|
||||||
MobileFieldPickerList({
|
MobileFieldPickerList({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.title,
|
||||||
required this.selectedFieldId,
|
required this.selectedFieldId,
|
||||||
required FieldController fieldController,
|
required FieldController fieldController,
|
||||||
required bool Function(FieldInfo fieldInfo) filterBy,
|
required bool Function(FieldInfo fieldInfo) filterBy,
|
||||||
}) : fields = fieldController.fieldInfos.where(filterBy).toList();
|
}) : fields = fieldController.fieldInfos.where(filterBy).toList();
|
||||||
|
|
||||||
|
final String title;
|
||||||
final String? selectedFieldId;
|
final String? selectedFieldId;
|
||||||
final List<FieldInfo> fields;
|
final List<FieldInfo> fields;
|
||||||
|
|
||||||
@ -36,87 +37,79 @@ class _MobileFieldPickerListState extends State<MobileFieldPickerList> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return DraggableScrollableSheet(
|
||||||
children: [
|
expand: false,
|
||||||
const Center(child: DragHandler()),
|
snap: true,
|
||||||
_Header(newFieldId: newFieldId),
|
initialChildSize: 0.98,
|
||||||
Expanded(
|
minChildSize: 0.98,
|
||||||
child: ListView.builder(
|
maxChildSize: 0.98,
|
||||||
itemCount: widget.fields.length,
|
builder: (context, scrollController) {
|
||||||
itemBuilder: (context, index) => _FieldButton(
|
return Column(
|
||||||
field: widget.fields[index],
|
mainAxisSize: MainAxisSize.min,
|
||||||
showTopBorder: index == 0,
|
|
||||||
isSelected: widget.fields[index].id == newFieldId,
|
|
||||||
onSelect: (fieldId) => setState(() => newFieldId = fieldId),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Header extends StatelessWidget {
|
|
||||||
const _Header({required this.newFieldId});
|
|
||||||
|
|
||||||
final String? newFieldId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
SizedBox.square(
|
const DragHandler(),
|
||||||
dimension: 36,
|
_Header(
|
||||||
child: IconButton(
|
title: widget.title,
|
||||||
highlightColor: Colors.transparent,
|
onDone: (context) => context.pop(newFieldId),
|
||||||
splashColor: Colors.transparent,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
onPressed: () => context.pop(),
|
|
||||||
icon: const FlowySvg(
|
|
||||||
FlowySvgs.arrow_left_s,
|
|
||||||
size: Size.square(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
SingleChildScrollView(
|
||||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 8),
|
controller: scrollController,
|
||||||
child: TextButton(
|
child: ListView.builder(
|
||||||
style: TextButton.styleFrom(
|
shrinkWrap: true,
|
||||||
padding: const EdgeInsets.symmetric(
|
itemCount: widget.fields.length,
|
||||||
horizontal: 12,
|
itemBuilder: (context, index) => _FieldButton(
|
||||||
vertical: 5,
|
field: widget.fields[index],
|
||||||
),
|
showTopBorder: index == 0,
|
||||||
shape: RoundedRectangleBorder(
|
isSelected: widget.fields[index].id == newFieldId,
|
||||||
borderRadius: BorderRadius.circular(10),
|
onSelect: (fieldId) => setState(() => newFieldId = fieldId),
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
enableFeedback: true,
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () => context.pop(newFieldId),
|
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.button_done.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same header as the one in showMobileBottomSheet, but allows popping the
|
||||||
|
/// sheet with a value.
|
||||||
|
class _Header extends StatelessWidget {
|
||||||
|
const _Header({
|
||||||
|
required this.title,
|
||||||
|
required this.onDone,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final void Function(BuildContext context) onDone;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 44.0,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: AppBarBackButton(),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
child: FlowyText.medium(
|
||||||
|
title,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: AppBarDoneButton(
|
||||||
|
onTap: () => onDone(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Center(
|
),
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.calendar_settings_changeLayoutDateField.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import 'dart:ui';
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
|
||||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||||
@ -11,7 +10,6 @@ import 'package:appflowy/plugins/database/application/setting/property_bloc.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/util/field_type_extension.dart';
|
import 'package:appflowy/util/field_type_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
@ -30,61 +28,9 @@ class MobileDatabaseFieldList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DraggableScrollableSheet(
|
return _MobileDatabaseFieldListBody(
|
||||||
expand: false,
|
databaseController: databaseController,
|
||||||
snap: true,
|
viewId: context.read<ViewBloc>().state.view.id,
|
||||||
initialChildSize: 1.0,
|
|
||||||
minChildSize: 0.0,
|
|
||||||
builder: (context, controller) {
|
|
||||||
return Material(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Center(child: DragHandler()),
|
|
||||||
const _MobileDatabaseFieldListHeader(),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: _MobileDatabaseFieldListBody(
|
|
||||||
databaseController: databaseController,
|
|
||||||
view: context.read<ViewBloc>().state.view,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MobileDatabaseFieldListHeader extends StatelessWidget {
|
|
||||||
const _MobileDatabaseFieldListHeader();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const iconWidth = 30.0;
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 12),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: FlowyIconButton(
|
|
||||||
icon: const FlowySvg(
|
|
||||||
FlowySvgs.arrow_left_m,
|
|
||||||
size: Size.square(iconWidth),
|
|
||||||
),
|
|
||||||
onPressed: () => Navigator.of(context).maybePop(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.grid_settings_properties.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,17 +38,17 @@ class _MobileDatabaseFieldListHeader extends StatelessWidget {
|
|||||||
class _MobileDatabaseFieldListBody extends StatelessWidget {
|
class _MobileDatabaseFieldListBody extends StatelessWidget {
|
||||||
const _MobileDatabaseFieldListBody({
|
const _MobileDatabaseFieldListBody({
|
||||||
required this.databaseController,
|
required this.databaseController,
|
||||||
required this.view,
|
required this.viewId,
|
||||||
});
|
});
|
||||||
|
|
||||||
final DatabaseController databaseController;
|
final DatabaseController databaseController;
|
||||||
final ViewPB view;
|
final String viewId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<DatabasePropertyBloc>(
|
return BlocProvider<DatabasePropertyBloc>(
|
||||||
create: (_) => DatabasePropertyBloc(
|
create: (_) => DatabasePropertyBloc(
|
||||||
viewId: view.id,
|
viewId: viewId,
|
||||||
fieldController: databaseController.fieldController,
|
fieldController: databaseController.fieldController,
|
||||||
)..add(const DatabasePropertyEvent.initial()),
|
)..add(const DatabasePropertyEvent.initial()),
|
||||||
child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>(
|
child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>(
|
||||||
@ -114,7 +60,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
final firstField = fields.removeAt(0);
|
final firstField = fields.removeAt(0);
|
||||||
final firstCell = DatabaseFieldListTile(
|
final firstCell = DatabaseFieldListTile(
|
||||||
key: ValueKey(firstField.id),
|
key: ValueKey(firstField.id),
|
||||||
viewId: view.id,
|
viewId: viewId,
|
||||||
fieldController: databaseController.fieldController,
|
fieldController: databaseController.fieldController,
|
||||||
fieldInfo: firstField,
|
fieldInfo: firstField,
|
||||||
showTopBorder: true,
|
showTopBorder: true,
|
||||||
@ -123,7 +69,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
.mapIndexed(
|
.mapIndexed(
|
||||||
(index, field) => DatabaseFieldListTile(
|
(index, field) => DatabaseFieldListTile(
|
||||||
key: ValueKey(field.id),
|
key: ValueKey(field.id),
|
||||||
viewId: view.id,
|
viewId: viewId,
|
||||||
fieldController: databaseController.fieldController,
|
fieldController: databaseController.fieldController,
|
||||||
fieldInfo: field,
|
fieldInfo: field,
|
||||||
index: index,
|
index: index,
|
||||||
@ -133,6 +79,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return ReorderableListView.builder(
|
return ReorderableListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
proxyDecorator: (_, index, anim) {
|
proxyDecorator: (_, index, anim) {
|
||||||
final field = fields[index];
|
final field = fields[index];
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
@ -146,7 +93,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
child: Material(
|
child: Material(
|
||||||
child: DatabaseFieldListTile(
|
child: DatabaseFieldListTile(
|
||||||
key: ValueKey(field.id),
|
key: ValueKey(field.id),
|
||||||
viewId: view.id,
|
viewId: viewId,
|
||||||
fieldController: databaseController.fieldController,
|
fieldController: databaseController.fieldController,
|
||||||
fieldInfo: field,
|
fieldInfo: field,
|
||||||
index: index,
|
index: index,
|
||||||
@ -170,7 +117,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_divider(),
|
_divider(),
|
||||||
_NewDatabaseFieldTile(viewId: view.id),
|
_NewDatabaseFieldTile(viewId: viewId),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
itemCount: cells.length,
|
itemCount: cells.length,
|
||||||
|
@ -168,6 +168,7 @@ class _CalendarLayoutField extends StatelessWidget {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final newFieldId = await showFieldPicker(
|
final newFieldId = await showFieldPicker(
|
||||||
context,
|
context,
|
||||||
|
LocaleKeys.calendar_settings_changeLayoutDateField.tr(),
|
||||||
selectedFieldId,
|
selectedFieldId,
|
||||||
databaseController.fieldController,
|
databaseController.fieldController,
|
||||||
(field) => field.fieldType == FieldType.DateTime,
|
(field) => field.fieldType == FieldType.DateTime,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||||
@ -26,28 +27,110 @@ class MobileDatabaseViewList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ViewBloc, ViewState>(
|
return DraggableScrollableSheet(
|
||||||
builder: (context, state) {
|
expand: false,
|
||||||
final views = [state.view, ...state.view.childViews];
|
snap: true,
|
||||||
final children = [
|
initialChildSize: 0.98,
|
||||||
...views.mapIndexed(
|
minChildSize: 0.98,
|
||||||
(index, view) => MobileDatabaseViewListButton(
|
maxChildSize: 0.98,
|
||||||
view: view,
|
builder: (context, scrollController) {
|
||||||
showTopBorder: index == 0,
|
return BlocBuilder<ViewBloc, ViewState>(
|
||||||
),
|
builder: (context, state) {
|
||||||
),
|
final views = [state.view, ...state.view.childViews];
|
||||||
const VSpace(20),
|
|
||||||
const MobileNewDatabaseViewButton(),
|
|
||||||
];
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: children,
|
children: [
|
||||||
|
const DragHandler(),
|
||||||
|
_Header(
|
||||||
|
title: LocaleKeys.grid_settings_viewList.plural(
|
||||||
|
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
|
||||||
|
namedArgs: {
|
||||||
|
'count':
|
||||||
|
'${context.watch<DatabaseTabBarBloc>().state.tabBars.length}',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
showBackButton: false,
|
||||||
|
useFilledDoneButton: false,
|
||||||
|
onDone: (context) => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
SingleChildScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...views.mapIndexed(
|
||||||
|
(index, view) => MobileDatabaseViewListButton(
|
||||||
|
view: view,
|
||||||
|
showTopBorder: index == 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const VSpace(20),
|
||||||
|
const MobileNewDatabaseViewButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same header as the one in showMobileBottomSheet, but allows popping the
|
||||||
|
/// sheet with a value.
|
||||||
|
class _Header extends StatelessWidget {
|
||||||
|
const _Header({
|
||||||
|
required this.title,
|
||||||
|
required this.showBackButton,
|
||||||
|
required this.useFilledDoneButton,
|
||||||
|
required this.onDone,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final bool showBackButton;
|
||||||
|
final bool useFilledDoneButton;
|
||||||
|
final void Function(BuildContext context) onDone;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 44.0,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
if (showBackButton)
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: AppBarBackButton(),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
child: FlowyText.medium(
|
||||||
|
title,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
useFilledDoneButton
|
||||||
|
? Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: AppBarFilledDoneButton(
|
||||||
|
onTap: () => onDone(context),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: AppBarDoneButton(
|
||||||
|
onTap: () => onDone(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
class MobileDatabaseViewListButton extends StatelessWidget {
|
class MobileDatabaseViewListButton extends StatelessWidget {
|
||||||
const MobileDatabaseViewListButton({
|
const MobileDatabaseViewListButton({
|
||||||
@ -155,6 +238,7 @@ class MobileNewDatabaseViewButton extends StatelessWidget {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final result = await showMobileBottomSheet<(DatabaseLayoutPB, String)>(
|
final result = await showMobileBottomSheet<(DatabaseLayoutPB, String)>(
|
||||||
context,
|
context,
|
||||||
|
showDragHandle: true,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return const MobileCreateDatabaseView();
|
return const MobileCreateDatabaseView();
|
||||||
},
|
},
|
||||||
@ -199,12 +283,13 @@ class _MobileCreateDatabaseViewState extends State<MobileCreateDatabaseView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const Center(child: DragHandler()),
|
_Header(
|
||||||
_CreateViewHeader(
|
title: LocaleKeys.grid_settings_createView.tr(),
|
||||||
textController: controller,
|
showBackButton: true,
|
||||||
selectedLayout: layoutType,
|
useFilledDoneButton: true,
|
||||||
|
onDone: (context) =>
|
||||||
|
context.pop((layoutType, controller.text.trim())),
|
||||||
),
|
),
|
||||||
const VSpace(4.0),
|
|
||||||
FlowyOptionTile.textField(
|
FlowyOptionTile.textField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
@ -220,74 +305,3 @@ class _MobileCreateDatabaseViewState extends State<MobileCreateDatabaseView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CreateViewHeader extends StatelessWidget {
|
|
||||||
const _CreateViewHeader({
|
|
||||||
required this.textController,
|
|
||||||
required this.selectedLayout,
|
|
||||||
});
|
|
||||||
|
|
||||||
final TextEditingController textController;
|
|
||||||
final DatabaseLayoutPB selectedLayout;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
SizedBox.square(
|
|
||||||
dimension: 36,
|
|
||||||
child: IconButton(
|
|
||||||
highlightColor: Colors.transparent,
|
|
||||||
splashColor: Colors.transparent,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
onPressed: () => context.pop(),
|
|
||||||
icon: const FlowySvg(
|
|
||||||
FlowySvgs.arrow_left_s,
|
|
||||||
size: Size.square(24),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 8),
|
|
||||||
child: TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 5,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
enableFeedback: true,
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.pop((selectedLayout, textController.text.trim()));
|
|
||||||
},
|
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.button_done.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.grid_settings_createView.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
@ -13,7 +14,7 @@ import 'edit_database_view_screen.dart';
|
|||||||
|
|
||||||
/// [MobileDatabaseViewQuickActions] is gives users to quickly edit a database
|
/// [MobileDatabaseViewQuickActions] is gives users to quickly edit a database
|
||||||
/// view from the [MobileDatabaseViewList]
|
/// view from the [MobileDatabaseViewList]
|
||||||
class MobileDatabaseViewQuickActions extends StatefulWidget {
|
class MobileDatabaseViewQuickActions extends StatelessWidget {
|
||||||
const MobileDatabaseViewQuickActions({
|
const MobileDatabaseViewQuickActions({
|
||||||
super.key,
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
@ -23,49 +24,46 @@ class MobileDatabaseViewQuickActions extends StatefulWidget {
|
|||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
final DatabaseController databaseController;
|
final DatabaseController databaseController;
|
||||||
|
|
||||||
@override
|
|
||||||
State<MobileDatabaseViewQuickActions> createState() =>
|
|
||||||
_MobileDatabaseViewQuickActionsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MobileDatabaseViewQuickActionsState
|
|
||||||
extends State<MobileDatabaseViewQuickActions> {
|
|
||||||
bool isEditing = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return isEditing
|
|
||||||
? MobileEditDatabaseViewScreen(
|
|
||||||
databaseController: widget.databaseController,
|
|
||||||
)
|
|
||||||
: _quickActions(context, widget.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _quickActions(BuildContext context, ViewPB view) {
|
|
||||||
final isInline = view.childViews.isNotEmpty;
|
final isInline = view.childViews.isNotEmpty;
|
||||||
return Padding(
|
return Column(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Column(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
_actionButton(context, _Action.edit, () {
|
||||||
children: [
|
final bloc = context.read<ViewBloc>();
|
||||||
_actionButton(context, _Action.edit, () {
|
context.pop();
|
||||||
setState(() => isEditing = true);
|
showMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showDoneButton: true,
|
||||||
|
title: LocaleKeys.grid_settings_editView.tr(),
|
||||||
|
enableDraggableScrollable: true,
|
||||||
|
initialChildSize: 0.98,
|
||||||
|
minChildSize: 0.98,
|
||||||
|
maxChildSize: 0.98,
|
||||||
|
builder: (_) => BlocProvider.value(
|
||||||
|
value: bloc,
|
||||||
|
child: MobileEditDatabaseViewScreen(
|
||||||
|
databaseController: databaseController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
if (!isInline) ...[
|
||||||
|
_divider(),
|
||||||
|
_actionButton(context, _Action.duplicate, () {
|
||||||
|
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||||
|
context.pop();
|
||||||
}),
|
}),
|
||||||
if (!isInline) ...[
|
_divider(),
|
||||||
_divider(),
|
_actionButton(context, _Action.delete, () {
|
||||||
_actionButton(context, _Action.duplicate, () {
|
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
context.pop();
|
||||||
context.pop();
|
}),
|
||||||
}),
|
_divider(),
|
||||||
_divider(),
|
|
||||||
_actionButton(context, _Action.delete, () {
|
|
||||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
|
||||||
context.pop();
|
|
||||||
}),
|
|
||||||
_divider(),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'edit_database_view_cubit.freezed.dart';
|
|
||||||
|
|
||||||
class MobileEditDatabaseViewCubit extends Cubit<MobileDatabaseViewEditorState> {
|
|
||||||
MobileEditDatabaseViewCubit()
|
|
||||||
: super(
|
|
||||||
MobileDatabaseViewEditorState.initial(),
|
|
||||||
);
|
|
||||||
|
|
||||||
void changePage(MobileEditDatabaseViewPageEnum newPage) {
|
|
||||||
emit(MobileDatabaseViewEditorState(currentPage: newPage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class MobileDatabaseViewEditorState with _$MobileDatabaseViewEditorState {
|
|
||||||
factory MobileDatabaseViewEditorState({
|
|
||||||
required MobileEditDatabaseViewPageEnum currentPage,
|
|
||||||
}) = _MobileDatabaseViewEditorState;
|
|
||||||
|
|
||||||
factory MobileDatabaseViewEditorState.initial() =>
|
|
||||||
MobileDatabaseViewEditorState(
|
|
||||||
currentPage: MobileEditDatabaseViewPageEnum.main,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MobileEditDatabaseViewPageEnum {
|
|
||||||
main,
|
|
||||||
fields,
|
|
||||||
filter,
|
|
||||||
sort,
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
|
||||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/database_view_service.dart';
|
import 'package:appflowy/plugins/database/application/database_view_service.dart';
|
||||||
import 'package:appflowy/plugins/database/application/layout/layout_service.dart';
|
import 'package:appflowy/plugins/database/application/layout/layout_service.dart';
|
||||||
@ -18,12 +17,11 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import 'database_field_list.dart';
|
import 'database_field_list.dart';
|
||||||
import 'database_view_layout.dart';
|
import 'database_view_layout.dart';
|
||||||
import 'edit_database_view_cubit.dart';
|
|
||||||
|
|
||||||
/// [MobileEditDatabaseViewScreen] is the main widget used to edit a database
|
/// [MobileEditDatabaseViewScreen] is the main widget used to edit a database
|
||||||
/// view. It contains multiple sub-pages, and the current page is managed by
|
/// view. It contains multiple sub-pages, and the current page is managed by
|
||||||
/// [MobileEditDatabaseViewCubit]
|
/// [MobileEditDatabaseViewCubit]
|
||||||
class MobileEditDatabaseViewScreen extends StatefulWidget {
|
class MobileEditDatabaseViewScreen extends StatelessWidget {
|
||||||
const MobileEditDatabaseViewScreen({
|
const MobileEditDatabaseViewScreen({
|
||||||
super.key,
|
super.key,
|
||||||
required this.databaseController,
|
required this.databaseController,
|
||||||
@ -31,130 +29,12 @@ class MobileEditDatabaseViewScreen extends StatefulWidget {
|
|||||||
|
|
||||||
final DatabaseController databaseController;
|
final DatabaseController databaseController;
|
||||||
|
|
||||||
@override
|
|
||||||
State<MobileEditDatabaseViewScreen> createState() =>
|
|
||||||
_MobileEditDatabaseViewScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MobileEditDatabaseViewScreenState
|
|
||||||
extends State<MobileEditDatabaseViewScreen> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocProvider<MobileEditDatabaseViewCubit>(
|
|
||||||
create: (context) => MobileEditDatabaseViewCubit(),
|
|
||||||
child: BlocBuilder<MobileEditDatabaseViewCubit,
|
|
||||||
MobileDatabaseViewEditorState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return switch (state.currentPage) {
|
|
||||||
MobileEditDatabaseViewPageEnum.main => _EditDatabaseViewMainPage(
|
|
||||||
databaseController: widget.databaseController,
|
|
||||||
),
|
|
||||||
MobileEditDatabaseViewPageEnum.fields => _wrapSubPage(
|
|
||||||
context,
|
|
||||||
MobileDatabaseFieldList(
|
|
||||||
databaseController: widget.databaseController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_ => const SizedBox.shrink(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _wrapSubPage(BuildContext context, Widget child) {
|
|
||||||
return PopScope(
|
|
||||||
canPop: false,
|
|
||||||
child: child,
|
|
||||||
onPopInvoked: (_) {
|
|
||||||
context
|
|
||||||
.read<MobileEditDatabaseViewCubit>()
|
|
||||||
.changePage(MobileEditDatabaseViewPageEnum.main);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EditDatabaseViewMainPage extends StatelessWidget {
|
|
||||||
const _EditDatabaseViewMainPage({
|
|
||||||
required this.databaseController,
|
|
||||||
});
|
|
||||||
|
|
||||||
final DatabaseController databaseController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DraggableScrollableSheet(
|
|
||||||
expand: false,
|
|
||||||
snap: true,
|
|
||||||
initialChildSize: 1.0,
|
|
||||||
minChildSize: 0.0,
|
|
||||||
builder: (context, controller) {
|
|
||||||
return Material(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Center(child: DragHandler()),
|
|
||||||
const _EditDatabaseViewHeader(),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: _EditDatabaseViewBody(
|
|
||||||
databaseController: databaseController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EditDatabaseViewHeader extends StatelessWidget {
|
|
||||||
const _EditDatabaseViewHeader();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const iconWidth = 30.0;
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 12),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: FlowyIconButton(
|
|
||||||
icon: const FlowySvg(
|
|
||||||
FlowySvgs.close_s,
|
|
||||||
size: Size.square(iconWidth),
|
|
||||||
),
|
|
||||||
onPressed: () => context.pop(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
child: FlowyText.medium(
|
|
||||||
LocaleKeys.grid_settings_editView.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EditDatabaseViewBody extends StatelessWidget {
|
|
||||||
const _EditDatabaseViewBody({
|
|
||||||
required this.databaseController,
|
|
||||||
});
|
|
||||||
|
|
||||||
final DatabaseController databaseController;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ViewBloc, ViewState>(
|
return BlocBuilder<ViewBloc, ViewState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Column(
|
return ListView(
|
||||||
mainAxisSize: MainAxisSize.min,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
_NameAndIcon(view: state.view),
|
_NameAndIcon(view: state.view),
|
||||||
_divider(),
|
_divider(),
|
||||||
@ -207,6 +87,7 @@ class _NameAndIconState extends State<_NameAndIcon> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlowyOptionTile.textField(
|
return FlowyOptionTile.textField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
showTopBorder: false,
|
||||||
controller: textEditingController,
|
controller: textEditingController,
|
||||||
onTextChanged: (text) {
|
onTextChanged: (text) {
|
||||||
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
||||||
@ -250,15 +131,6 @@ enum DatabaseViewSettings {
|
|||||||
delete => FlowySvgs.delete_s,
|
delete => FlowySvgs.delete_s,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
MobileEditDatabaseViewPageEnum? get subPage {
|
|
||||||
return switch (this) {
|
|
||||||
fields => MobileEditDatabaseViewPageEnum.fields,
|
|
||||||
filter => MobileEditDatabaseViewPageEnum.filter,
|
|
||||||
sort => MobileEditDatabaseViewPageEnum.sort,
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DatabaseViewSettingTile extends StatelessWidget {
|
class DatabaseViewSettingTile extends StatelessWidget {
|
||||||
@ -325,25 +197,18 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onTap(BuildContext context) async {
|
void _onTap(BuildContext context) async {
|
||||||
final subPage = setting.subPage;
|
|
||||||
|
|
||||||
if (subPage != null) {
|
|
||||||
context.read<MobileEditDatabaseViewCubit>().changePage(subPage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setting == DatabaseViewSettings.layout) {
|
if (setting == DatabaseViewSettings.layout) {
|
||||||
final databaseLayout = databaseLayoutFromViewLayout(view.layout);
|
final databaseLayout = databaseLayoutFromViewLayout(view.layout);
|
||||||
final newLayout = await showMobileBottomSheet<DatabaseLayoutPB>(
|
final newLayout = await showMobileBottomSheet<DatabaseLayoutPB>(
|
||||||
context,
|
context,
|
||||||
resizeToAvoidBottomInset: false,
|
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
|
showHeader: true,
|
||||||
|
showDivider: false,
|
||||||
|
title: LocaleKeys.grid_settings_layout.tr(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return DatabaseViewLayoutPicker(
|
return DatabaseViewLayoutPicker(
|
||||||
selectedLayout: databaseLayout,
|
selectedLayout: databaseLayout,
|
||||||
onSelect: (layout) {
|
onSelect: (layout) => Navigator.of(context).pop(layout),
|
||||||
Navigator.of(context).pop(layout);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -356,6 +221,32 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting == DatabaseViewSettings.fields) {
|
||||||
|
await showMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
useSafeArea: false,
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
showDragHandle: true,
|
||||||
|
showHeader: true,
|
||||||
|
showBackButton: true,
|
||||||
|
title: LocaleKeys.grid_settings_properties.tr(),
|
||||||
|
showDivider: true,
|
||||||
|
enableDraggableScrollable: true,
|
||||||
|
initialChildSize: 0.98,
|
||||||
|
minChildSize: 0.98,
|
||||||
|
maxChildSize: 0.98,
|
||||||
|
builder: (_) {
|
||||||
|
return BlocProvider.value(
|
||||||
|
value: context.read<ViewBloc>(),
|
||||||
|
child: MobileDatabaseFieldList(
|
||||||
|
databaseController: databaseController,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (setting == DatabaseViewSettings.board) {
|
if (setting == DatabaseViewSettings.board) {
|
||||||
await showMobileBottomSheet<DatabaseLayoutPB>(
|
await showMobileBottomSheet<DatabaseLayoutPB>(
|
||||||
context,
|
context,
|
||||||
@ -375,13 +266,13 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
|||||||
if (setting == DatabaseViewSettings.calendar) {
|
if (setting == DatabaseViewSettings.calendar) {
|
||||||
await showMobileBottomSheet<DatabaseLayoutPB>(
|
await showMobileBottomSheet<DatabaseLayoutPB>(
|
||||||
context,
|
context,
|
||||||
resizeToAvoidBottomInset: false,
|
showDragHandle: true,
|
||||||
|
showHeader: true,
|
||||||
|
showDivider: false,
|
||||||
|
title: LocaleKeys.calendar_settings_name.tr(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Padding(
|
return MobileCalendarViewLayoutSettings(
|
||||||
padding: const EdgeInsets.only(top: 24, bottom: 46),
|
databaseController: databaseController,
|
||||||
child: MobileCalendarViewLayoutSettings(
|
|
||||||
databaseController: databaseController,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -65,7 +65,13 @@ class MobileDatabaseControls extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
padding: EdgeInsets.zero,
|
showHeader: true,
|
||||||
|
showDoneButton: true,
|
||||||
|
title: LocaleKeys.grid_settings_editView.tr(),
|
||||||
|
enableDraggableScrollable: true,
|
||||||
|
initialChildSize: 0.98,
|
||||||
|
minChildSize: 0.98,
|
||||||
|
maxChildSize: 0.98,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider<ViewBloc>(
|
return BlocProvider<ViewBloc>(
|
||||||
create: (_) {
|
create: (_) {
|
||||||
@ -90,11 +96,7 @@ class MobileDatabaseControls extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showHeader: true,
|
|
||||||
showCloseButton: true,
|
|
||||||
showDragHandle: true,
|
|
||||||
showDivider: false,
|
showDivider: false,
|
||||||
title: LocaleKeys.grid_settings_viewList.tr(),
|
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -480,7 +480,11 @@
|
|||||||
"typeAValue": "Type a value...",
|
"typeAValue": "Type a value...",
|
||||||
"layout": "Layout",
|
"layout": "Layout",
|
||||||
"databaseLayout": "Layout",
|
"databaseLayout": "Layout",
|
||||||
"viewList": "Database Views",
|
"viewList": {
|
||||||
|
"zero": "0 views",
|
||||||
|
"one": "{count} view",
|
||||||
|
"other": "{count} views"
|
||||||
|
},
|
||||||
"editView": "Edit View",
|
"editView": "Edit View",
|
||||||
"boardSettings": "Board settings",
|
"boardSettings": "Board settings",
|
||||||
"calendarSettings": "Calendar settings",
|
"calendarSettings": "Calendar settings",
|
||||||
@ -1250,4 +1254,4 @@
|
|||||||
"userIcon": "User icon"
|
"userIcon": "User icon"
|
||||||
},
|
},
|
||||||
"noLogFiles": "There're no log files"
|
"noLogFiles": "There're no log files"
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user