feat: switch database layout (#2677)

* chore: rename update at and create at

* chore: support switching view layout

* chore: implement ui

* chore: update layout type

* refactor: board/calendar/grid setting button

* chore: update UI after switch to other layout type

* fix: no date display in calendar

* chore: update patch

* chore: fix create ref view in document

* chore: fix flutter analyze

* ci: warnings

* chore: rename board and grid keys

* fix: calendar row event update

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo
2023-06-01 20:23:27 +08:00
committed by GitHub
parent 2ef72f3203
commit 33e0f8d26d
98 changed files with 1600 additions and 1122 deletions

View File

@ -39,18 +39,18 @@ class GroupCallbacks {
});
}
class LayoutCallbacks {
final void Function(LayoutSettingPB) onLayoutChanged;
final void Function(LayoutSettingPB) onLoadLayout;
class DatabaseLayoutSettingCallbacks {
final void Function(DatabaseLayoutSettingPB) onLayoutChanged;
final void Function(DatabaseLayoutSettingPB) onLoadLayout;
LayoutCallbacks({
DatabaseLayoutSettingCallbacks({
required this.onLayoutChanged,
required this.onLoadLayout,
});
}
class CalendarLayoutCallbacks {
final void Function(LayoutSettingPB) onCalendarLayoutChanged;
final void Function(DatabaseLayoutSettingPB) onCalendarLayoutChanged;
CalendarLayoutCallbacks({required this.onCalendarLayoutChanged});
}
@ -59,14 +59,14 @@ class DatabaseCallbacks {
OnDatabaseChanged? onDatabaseChanged;
OnFieldsChanged? onFieldsChanged;
OnFiltersChanged? onFiltersChanged;
OnRowsChanged? onRowsChanged;
OnNumOfRowsChanged? onNumOfRowsChanged;
OnRowsDeleted? onRowsDeleted;
OnRowsUpdated? onRowsUpdated;
OnRowsCreated? onRowsCreated;
DatabaseCallbacks({
this.onDatabaseChanged,
this.onRowsChanged,
this.onNumOfRowsChanged,
this.onFieldsChanged,
this.onFiltersChanged,
this.onRowsUpdated,
@ -79,13 +79,13 @@ class DatabaseController {
final String viewId;
final DatabaseViewBackendService _databaseViewBackendSvc;
final FieldController fieldController;
DatabaseLayoutPB? databaseLayout;
late DatabaseViewCache _viewCache;
final DatabaseLayoutPB layoutType;
// Callbacks
DatabaseCallbacks? _databaseCallbacks;
GroupCallbacks? _groupCallbacks;
LayoutCallbacks? _layoutCallbacks;
DatabaseLayoutSettingCallbacks? _layoutCallbacks;
CalendarLayoutCallbacks? _calendarLayoutCallbacks;
// Getters
@ -93,15 +93,15 @@ class DatabaseController {
// Listener
final DatabaseGroupListener groupListener;
final DatabaseLayoutListener layoutListener;
final DatabaseLayoutSettingListener layoutListener;
final DatabaseCalendarLayoutListener calendarLayoutListener;
DatabaseController({required ViewPB view, required this.layoutType})
DatabaseController({required ViewPB view})
: viewId = view.id,
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
fieldController = FieldController(viewId: view.id),
groupListener = DatabaseGroupListener(view.id),
layoutListener = DatabaseLayoutListener(view.id),
layoutListener = DatabaseLayoutSettingListener(view.id),
calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
_viewCache = DatabaseViewCache(
viewId: viewId,
@ -111,14 +111,11 @@ class DatabaseController {
_listenOnFieldsChanged();
_listenOnGroupChanged();
_listenOnLayoutChanged();
if (layoutType == DatabaseLayoutPB.Calendar) {
_listenOnCalendarLayoutChanged();
}
}
void setListener({
DatabaseCallbacks? onDatabaseChanged,
LayoutCallbacks? onLayoutChanged,
DatabaseLayoutSettingCallbacks? onLayoutChanged,
GroupCallbacks? onGroupChanged,
CalendarLayoutCallbacks? onCalendarLayoutChanged,
}) {
@ -132,6 +129,12 @@ class DatabaseController {
return _databaseViewBackendSvc.openGrid().then((result) {
return result.fold(
(database) async {
databaseLayout = database.layoutType;
if (databaseLayout == DatabaseLayoutPB.Calendar) {
_listenOnCalendarLayoutChanged();
}
_databaseCallbacks?.onDatabaseChanged?.call(database);
_viewCache.rowCache.setInitialRows(database.rows);
return await fieldController
@ -242,20 +245,20 @@ class DatabaseController {
}
Future<void> _loadLayoutSetting() async {
_databaseViewBackendSvc.getLayoutSetting(layoutType).then((result) {
result.fold(
(l) {
_layoutCallbacks?.onLoadLayout(l);
},
(r) => Log.error(r),
);
});
if (databaseLayout != null) {
_databaseViewBackendSvc.getLayoutSetting(databaseLayout!).then((result) {
result.fold(
(l) => _layoutCallbacks?.onLoadLayout(l),
(r) => Log.error(r),
);
});
}
}
void _listenOnRowsChanged() {
final callbacks = DatabaseViewCallbacks(
onRowsChanged: (rows, rowByRowId, reason) {
_databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason);
onNumOfRowsChanged: (rows, rowByRowId, reason) {
_databaseCallbacks?.onNumOfRowsChanged?.call(rows, rowByRowId, reason);
},
onRowsDeleted: (ids) {
_databaseCallbacks?.onRowsDeleted?.call(ids);

View File

@ -97,10 +97,10 @@ class DatabaseViewBackendService {
});
}
Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
Future<Either<DatabaseLayoutSettingPB, FlowyError>> getLayoutSetting(
DatabaseLayoutPB layoutType,
) {
final payload = DatabaseLayoutIdPB.create()
final payload = DatabaseLayoutMetaPB.create()
..viewId = viewId
..layout = layoutType;
return DatabaseEventGetLayoutSetting(payload).send();

View File

@ -15,7 +15,7 @@ typedef OnDatabaseChanged = void Function(DatabasePB);
typedef OnRowsCreated = void Function(List<RowId> ids);
typedef OnRowsUpdated = void Function(List<RowId> ids);
typedef OnRowsDeleted = void Function(List<RowId> ids);
typedef OnRowsChanged = void Function(
typedef OnNumOfRowsChanged = void Function(
UnmodifiableListView<RowInfo> rows,
UnmodifiableMapView<RowId, RowInfo> rowByRowId,
RowsChangedReason reason,

View File

@ -6,7 +6,7 @@ import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:dartz/dartz.dart';
typedef NewLayoutFieldValue = Either<LayoutSettingPB, FlowyError>;
typedef NewLayoutFieldValue = Either<DatabaseLayoutSettingPB, FlowyError>;
class DatabaseCalendarLayoutListener {
final String viewId;
@ -33,7 +33,7 @@ class DatabaseCalendarLayoutListener {
case DatabaseNotification.DidSetNewLayoutField:
result.fold(
(payload) => _newLayoutFieldNotifier?.value =
left(LayoutSettingPB.fromBuffer(payload)),
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
(error) => _newLayoutFieldNotifier?.value = right(error),
);
break;

View File

@ -0,0 +1,56 @@
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'layout_service.dart';
part 'layout_bloc.freezed.dart';
class DatabaseLayoutBloc
extends Bloc<DatabaseLayoutEvent, DatabaseLayoutState> {
final DatabaseLayoutBackendService layoutService;
DatabaseLayoutBloc({
required String viewId,
required DatabaseLayoutPB databaseLayout,
}) : layoutService = DatabaseLayoutBackendService(viewId),
super(DatabaseLayoutState.initial(viewId, databaseLayout)) {
on<DatabaseLayoutEvent>(
(event, emit) async {
event.when(
initial: () {},
updateLayout: (DatabaseLayoutPB layout) {
layoutService.updateLayout(
fieldId: viewId,
layout: layout,
);
emit(state.copyWith(databaseLayout: layout));
},
);
},
);
}
}
@freezed
class DatabaseLayoutEvent with _$DatabaseLayoutEvent {
const factory DatabaseLayoutEvent.initial() = _Initial;
const factory DatabaseLayoutEvent.updateLayout(DatabaseLayoutPB layout) =
_UpdateLayout;
}
@freezed
class DatabaseLayoutState with _$DatabaseLayoutState {
const factory DatabaseLayoutState({
required String viewId,
required DatabaseLayoutPB databaseLayout,
}) = _DatabaseLayoutState;
factory DatabaseLayoutState.initial(
String viewId,
DatabaseLayoutPB databaseLayout,
) =>
DatabaseLayoutState(
viewId: viewId,
databaseLayout: databaseLayout,
);
}

View File

@ -0,0 +1,50 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:dartz/dartz.dart';
/// Listener for database layout changes.
class DatabaseLayoutListener {
final String viewId;
PublishNotifier<Either<DatabaseLayoutPB, FlowyError>>? _layoutNotifier =
PublishNotifier();
DatabaseNotificationListener? _listener;
DatabaseLayoutListener(this.viewId);
void start({
required void Function(Either<DatabaseLayoutPB, FlowyError>)
onLayoutChanged,
}) {
_layoutNotifier?.addPublishListener(onLayoutChanged);
_listener = DatabaseNotificationListener(
objectId: viewId,
handler: _handler,
);
}
void _handler(
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidUpdateDatabaseLayout:
result.fold(
(payload) => _layoutNotifier?.value =
left(DatabaseLayoutMetaPB.fromBuffer(payload).layout),
(error) => _layoutNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_layoutNotifier?.dispose();
_layoutNotifier = null;
}
}

View File

@ -0,0 +1,35 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:dartz/dartz.dart';
class DatabaseLayoutBackendService {
final String viewId;
DatabaseLayoutBackendService(this.viewId);
Future<Either<ViewPB, FlowyError>> updateLayout({
required String fieldId,
required DatabaseLayoutPB layout,
}) {
var payload = UpdateViewPayloadPB.create()
..viewId = viewId
..layout = _viewLayoutFromDatabaseLayout(layout);
return FolderEventUpdateView(payload).send();
}
}
ViewLayoutPB _viewLayoutFromDatabaseLayout(DatabaseLayoutPB databaseLayout) {
switch (databaseLayout) {
case DatabaseLayoutPB.Board:
return ViewLayoutPB.Board;
case DatabaseLayoutPB.Calendar:
return ViewLayoutPB.Calendar;
case DatabaseLayoutPB.Grid:
return ViewLayoutPB.Grid;
default:
throw UnimplementedError;
}
}

View File

@ -8,15 +8,15 @@ import 'package:dartz/dartz.dart';
typedef LayoutSettingsValue<T> = Either<T, FlowyError>;
class DatabaseLayoutListener {
class DatabaseLayoutSettingListener {
final String viewId;
PublishNotifier<LayoutSettingsValue<LayoutSettingPB>>? _settingNotifier =
PublishNotifier();
PublishNotifier<LayoutSettingsValue<DatabaseLayoutSettingPB>>?
_settingNotifier = PublishNotifier();
DatabaseNotificationListener? _listener;
DatabaseLayoutListener(this.viewId);
DatabaseLayoutSettingListener(this.viewId);
void start({
required void Function(LayoutSettingsValue<LayoutSettingPB>)
required void Function(LayoutSettingsValue<DatabaseLayoutSettingPB>)
onLayoutChanged,
}) {
_settingNotifier?.addPublishListener(onLayoutChanged);
@ -34,7 +34,7 @@ class DatabaseLayoutListener {
case DatabaseNotification.DidUpdateLayoutSettings:
result.fold(
(payload) => _settingNotifier?.value =
left(LayoutSettingPB.fromBuffer(payload)),
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
(error) => _settingNotifier?.value = right(error),
);
break;

View File

@ -1,3 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:dartz/dartz.dart';
@ -40,7 +42,31 @@ class DatabaseSettingState with _$DatabaseSettingState {
}
enum DatabaseSettingAction {
showFilters,
sortBy,
showProperties,
showLayout,
showGroup,
}
extension DatabaseSettingActionExtension on DatabaseSettingAction {
String iconName() {
switch (this) {
case DatabaseSettingAction.showProperties:
return 'grid/setting/properties';
case DatabaseSettingAction.showLayout:
return 'grid/setting/database_layout';
case DatabaseSettingAction.showGroup:
return 'grid/setting/group';
}
}
String title() {
switch (this) {
case DatabaseSettingAction.showProperties:
return LocaleKeys.grid_settings_Properties.tr();
case DatabaseSettingAction.showLayout:
return LocaleKeys.grid_settings_databaseLayout.tr();
case DatabaseSettingAction.showGroup:
return LocaleKeys.grid_settings_group.tr();
}
}
}

View File

@ -9,21 +9,21 @@ import 'view_listener.dart';
class DatabaseViewCallbacks {
/// Will get called when number of rows were changed that includes
/// update/delete/insert rows. The [onRowsChanged] will return all
/// update/delete/insert rows. The [onNumOfRowsChanged] will return all
/// the rows of the current database
final OnRowsChanged? onRowsChanged;
final OnNumOfRowsChanged? onNumOfRowsChanged;
// Will get called when creating new rows
final OnRowsCreated? onRowsCreated;
/// Will get called when number of rows were updated
/// Will get called when rows were updated
final OnRowsUpdated? onRowsUpdated;
/// Will get called when number of rows were deleted
final OnRowsDeleted? onRowsDeleted;
const DatabaseViewCallbacks({
this.onRowsChanged,
this.onNumOfRowsChanged,
this.onRowsCreated,
this.onRowsUpdated,
this.onRowsDeleted,
@ -101,7 +101,7 @@ class DatabaseViewCache {
);
_rowCache.onRowsChanged(
(reason) => _callbacks?.onRowsChanged?.call(
(reason) => _callbacks?.onNumOfRowsChanged?.call(
rowInfos,
_rowCache.rowByRowId,
reason,

View File

@ -20,18 +20,17 @@ import 'group_controller.dart';
part 'board_bloc.freezed.dart';
class BoardBloc extends Bloc<BoardEvent, BoardState> {
final DatabaseController _databaseController;
final DatabaseController databaseController;
late final AppFlowyBoardController boardController;
final LinkedHashMap<String, GroupController> groupControllers =
LinkedHashMap();
FieldController get fieldController => _databaseController.fieldController;
String get viewId => _databaseController.viewId;
FieldController get fieldController => databaseController.fieldController;
String get viewId => databaseController.viewId;
BoardBloc({required ViewPB view})
: _databaseController = DatabaseController(
: databaseController = DatabaseController(
view: view,
layoutType: DatabaseLayoutPB.Board,
),
super(BoardState.initial(view.id)) {
boardController = AppFlowyBoardController(
@ -41,7 +40,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
toGroupId,
toIndex,
) {
_databaseController.moveGroup(
databaseController.moveGroup(
fromGroupId: fromGroupId,
toGroupId: toGroupId,
);
@ -54,7 +53,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
if (fromRow != null) {
_databaseController.moveGroupRow(
databaseController.moveGroupRow(
fromRow: fromRow,
toRow: toRow,
groupId: groupId,
@ -70,7 +69,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
if (fromRow != null) {
_databaseController.moveGroupRow(
databaseController.moveGroupRow(
fromRow: fromRow,
toRow: toRow,
groupId: toGroupId,
@ -88,7 +87,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
},
createBottomRow: (groupId) async {
final startRowId = groupControllers[groupId]?.lastRow()?.id;
final result = await _databaseController.createRow(
final result = await databaseController.createRow(
groupId: groupId,
startRowId: startRowId,
);
@ -98,8 +97,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
);
},
createHeaderRow: (String groupId) async {
final result =
await _databaseController.createRow(groupId: groupId);
final result = await databaseController.createRow(groupId: groupId);
result.fold(
(_) {},
(err) => Log.error(err),
@ -170,7 +168,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
@override
Future<void> close() async {
await _databaseController.dispose();
await databaseController.dispose();
for (final controller in groupControllers.values) {
controller.dispose();
}
@ -198,7 +196,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
}
RowCache? getRowCache() {
return _databaseController.rowCache;
return databaseController.rowCache;
}
void _startListening() {
@ -237,7 +235,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
},
);
_databaseController.setListener(
databaseController.setListener(
onDatabaseChanged: onDatabaseChanged,
onGroupChanged: onGroupChanged,
);
@ -256,7 +254,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
}
Future<void> _openGrid(Emitter<BoardState> emit) async {
final result = await _databaseController.open();
final result = await databaseController.open();
result.fold(
(grid) => emit(
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),

View File

@ -49,18 +49,19 @@ class BoardPlugin extends Plugin {
notifier = ViewPluginNotifier(view: view);
@override
PluginDisplay get display => GridPluginDisplay(notifier: notifier);
PluginWidgetBuilder get widgetBuilder =>
BoardPluginWidgetBuilder(notifier: notifier);
@override
PluginId get id => notifier.view.id;
@override
PluginType get ty => _pluginType;
PluginType get pluginType => _pluginType;
}
class GridPluginDisplay extends PluginDisplay {
class BoardPluginWidgetBuilder extends PluginWidgetBuilder {
final ViewPluginNotifier notifier;
GridPluginDisplay({required this.notifier, Key? key});
BoardPluginWidgetBuilder({required this.notifier, Key? key});
ViewPB get view => notifier.view;

View File

@ -340,15 +340,7 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<BoardBloc, BoardState>(
builder: (context, state) {
final bloc = context.read<BoardBloc>();
final toolbarContext = BoardToolbarContext(
viewId: bloc.viewId,
fieldController: bloc.fieldController,
);
return BoardToolbar(toolbarContext: toolbarContext);
},
builder: (context, state) => const BoardToolbar(),
);
}
}

View File

@ -1,192 +0,0 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/board/application/toolbar/board_setting_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_group.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'board_toolbar.dart';
class BoardSettingContext {
final String viewId;
final FieldController fieldController;
BoardSettingContext({
required this.viewId,
required this.fieldController,
});
factory BoardSettingContext.from(BoardToolbarContext toolbarContext) =>
BoardSettingContext(
viewId: toolbarContext.viewId,
fieldController: toolbarContext.fieldController,
);
}
class BoardSettingList extends StatelessWidget {
final BoardSettingContext settingContext;
final Function(BoardSettingAction, BoardSettingContext) onAction;
const BoardSettingList({
required this.settingContext,
required this.onAction,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => BoardSettingBloc(viewId: settingContext.viewId),
child: BlocListener<BoardSettingBloc, BoardSettingState>(
listenWhen: (previous, current) =>
previous.selectedAction != current.selectedAction,
listener: (context, state) {
state.selectedAction.foldLeft(null, (_, action) {
onAction(action, settingContext);
});
},
child: BlocBuilder<BoardSettingBloc, BoardSettingState>(
builder: (context, state) {
return _renderList();
},
),
),
);
}
Widget _renderList() {
final cells = BoardSettingAction.values.map((action) {
return _SettingItem(action: action);
}).toList();
return SizedBox(
width: 140,
child: ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
itemCount: cells.length,
separatorBuilder: (context, index) {
return VSpace(GridSize.typeOptionSeparatorHeight);
},
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return cells[index];
},
),
);
}
}
class _SettingItem extends StatelessWidget {
final BoardSettingAction action;
const _SettingItem({
required this.action,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final isSelected = context
.read<BoardSettingBloc>()
.state
.selectedAction
.foldLeft(false, (_, selectedAction) => selectedAction == action);
return SizedBox(
height: 30,
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
isSelected: isSelected,
text: FlowyText.medium(
action.title(),
color: AFThemeExtension.of(context).textColor,
),
onTap: () {
context
.read<BoardSettingBloc>()
.add(BoardSettingEvent.performAction(action));
},
leftIcon: svgWidget(
action.iconName(),
color: Theme.of(context).iconTheme.color,
),
),
);
}
}
extension _GridSettingExtension on BoardSettingAction {
String iconName() {
switch (this) {
case BoardSettingAction.properties:
return 'grid/setting/properties';
case BoardSettingAction.groups:
return 'grid/setting/group';
}
}
String title() {
switch (this) {
case BoardSettingAction.properties:
return LocaleKeys.grid_settings_Properties.tr();
case BoardSettingAction.groups:
return LocaleKeys.grid_settings_group.tr();
}
}
}
class BoardSettingListPopover extends StatefulWidget {
final PopoverController popoverController;
final BoardSettingContext settingContext;
const BoardSettingListPopover({
Key? key,
required this.popoverController,
required this.settingContext,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _BoardSettingListPopoverState();
}
class _BoardSettingListPopoverState extends State<BoardSettingListPopover> {
BoardSettingAction? _action;
@override
Widget build(BuildContext context) {
if (_action != null) {
switch (_action!) {
case BoardSettingAction.groups:
return GridGroupList(
viewId: widget.settingContext.viewId,
fieldController: widget.settingContext.fieldController,
onDismissed: () {
widget.popoverController.close();
},
);
case BoardSettingAction.properties:
return GridPropertyList(
viewId: widget.settingContext.viewId,
fieldController: widget.settingContext.fieldController,
);
}
}
return BoardSettingList(
settingContext: widget.settingContext,
onAction: (action, settingContext) {
setState(() => _action = action);
},
).padding(all: 6.0);
}
}

View File

@ -1,28 +1,10 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
import 'package:flutter/material.dart';
import 'board_setting.dart';
class BoardToolbarContext {
final String viewId;
final FieldController fieldController;
BoardToolbarContext({
required this.viewId,
required this.fieldController,
});
}
import 'package:flutter_bloc/flutter_bloc.dart';
class BoardToolbar extends StatelessWidget {
final BoardToolbarContext toolbarContext;
const BoardToolbar({
required this.toolbarContext,
Key? key,
}) : super(key: key);
@ -33,58 +15,11 @@ class BoardToolbar extends StatelessWidget {
child: Row(
children: [
const Spacer(),
_SettingButton(
settingContext: BoardSettingContext.from(toolbarContext),
SettingButton(
databaseController: context.read<BoardBloc>().databaseController,
),
],
),
);
}
}
class _SettingButton extends StatefulWidget {
final BoardSettingContext settingContext;
const _SettingButton({required this.settingContext, Key? key})
: super(key: key);
@override
State<_SettingButton> createState() => _SettingButtonState();
}
class _SettingButtonState extends State<_SettingButton> {
late PopoverController popoverController;
@override
void initState() {
popoverController = PopoverController();
super.initState();
}
@override
Widget build(BuildContext context) {
return AppFlowyPopover(
controller: popoverController,
direction: PopoverDirection.leftWithTopAligned,
offset: const Offset(-8, 0),
triggerActions: PopoverTriggerFlags.none,
constraints: BoxConstraints.loose(const Size(260, 400)),
margin: EdgeInsets.zero,
child: FlowyTextButton(
LocaleKeys.settings_title.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets,
onPressed: () {
popoverController.show();
},
),
popupBuilder: (BuildContext popoverContext) {
return BoardSettingListPopover(
settingContext: widget.settingContext,
popoverController: popoverController,
);
},
);
}
}

View File

@ -18,20 +18,17 @@ import '../../application/row/row_cache.dart';
part 'calendar_bloc.freezed.dart';
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
final DatabaseController _databaseController;
final DatabaseController databaseController;
Map<String, FieldInfo> fieldInfoByFieldId = {};
// Getters
String get viewId => _databaseController.viewId;
FieldController get fieldController => _databaseController.fieldController;
CellCache get cellCache => _databaseController.rowCache.cellCache;
RowCache get rowCache => _databaseController.rowCache;
String get viewId => databaseController.viewId;
FieldController get fieldController => databaseController.fieldController;
CellCache get cellCache => databaseController.rowCache.cellCache;
RowCache get rowCache => databaseController.rowCache;
CalendarBloc({required ViewPB view})
: _databaseController = DatabaseController(
view: view,
layoutType: DatabaseLayoutPB.Calendar,
),
: databaseController = DatabaseController(view: view),
super(CalendarState.initial()) {
on<CalendarEvent>(
(event, emit) async {
@ -110,12 +107,12 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
@override
Future<void> close() async {
await _databaseController.dispose();
await databaseController.dispose();
return super.close();
}
FieldInfo? _getCalendarFieldInfo(String fieldId) {
final fieldInfos = _databaseController.fieldController.fieldInfos;
final fieldInfos = databaseController.fieldController.fieldInfos;
final index = fieldInfos.indexWhere(
(element) => element.field.id == fieldId,
);
@ -127,7 +124,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
}
FieldInfo? _getTitleFieldInfo() {
final fieldInfos = _databaseController.fieldController.fieldInfos;
final fieldInfos = databaseController.fieldController.fieldInfos;
final index = fieldInfos.indexWhere(
(element) => element.field.isPrimary,
);
@ -139,7 +136,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
}
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
final result = await _databaseController.open();
final result = await databaseController.open();
result.fold(
(database) => emit(
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
@ -157,7 +154,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
final dateField = _getCalendarFieldInfo(settings.fieldId);
final titleField = _getTitleFieldInfo();
if (dateField != null && titleField != null) {
final newRow = await _databaseController.createRow(
final newRow = await databaseController.createRow(
withCells: (builder) {
builder.insertDate(dateField, date);
builder.insertText(titleField, title);
@ -210,7 +207,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
Future<void> _updateCalendarLayoutSetting(
CalendarLayoutSettingPB layoutSetting,
) async {
return _databaseController.updateCalenderLayoutSetting(layoutSetting);
return databaseController.updateCalenderLayoutSetting(layoutSetting);
}
Future<CalendarEventData<CalendarDayEvent>?> _loadEvent(RowId rowId) async {
@ -331,7 +328,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
},
);
final onLayoutChanged = LayoutCallbacks(
final onLayoutChanged = DatabaseLayoutSettingCallbacks(
onLayoutChanged: _didReceiveLayoutSetting,
onLoadLayout: _didReceiveLayoutSetting,
);
@ -340,14 +337,14 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
onCalendarLayoutChanged: _didReceiveNewLayoutField,
);
_databaseController.setListener(
databaseController.setListener(
onDatabaseChanged: onDatabaseChanged,
onLayoutChanged: onLayoutChanged,
onCalendarLayoutChanged: onCalendarLayoutFieldChanged,
);
}
void _didReceiveLayoutSetting(LayoutSettingPB layoutSetting) {
void _didReceiveLayoutSetting(DatabaseLayoutSettingPB layoutSetting) {
if (layoutSetting.hasCalendar()) {
if (isClosed) {
return;
@ -356,7 +353,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
}
}
void _didReceiveNewLayoutField(LayoutSettingPB layoutSetting) {
void _didReceiveNewLayoutField(DatabaseLayoutSettingPB layoutSetting) {
if (layoutSetting.hasCalendar()) {
if (isClosed) return;
add(CalendarEvent.didReceiveNewLayoutField(layoutSetting.calendar));

View File

@ -49,18 +49,19 @@ class CalendarPlugin extends Plugin {
notifier = ViewPluginNotifier(view: view);
@override
PluginDisplay get display => CalendarPluginDisplay(notifier: notifier);
PluginWidgetBuilder get widgetBuilder =>
CalendarPluginWidgetBuilder(notifier: notifier);
@override
PluginId get id => notifier.view.id;
@override
PluginType get ty => _pluginType;
PluginType get pluginType => _pluginType;
}
class CalendarPluginDisplay extends PluginDisplay {
class CalendarPluginWidgetBuilder extends PluginWidgetBuilder {
final ViewPluginNotifier notifier;
CalendarPluginDisplay({required this.notifier, Key? key});
CalendarPluginWidgetBuilder({required this.notifier, Key? key});
ViewPB get view => notifier.view;

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/calendar/application/calendar_setting_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart';
import 'package:appflowy/plugins/database_view/widgets/field/grid_property.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
@ -41,7 +41,7 @@ class CalendarSetting extends StatelessWidget {
state.selectedAction.foldLeft(null, (previous, action) => action);
switch (action) {
case CalendarSettingAction.properties:
return GridPropertyList(
return DatabasePropertyList(
viewId: settingContext.viewId,
fieldController: settingContext.fieldController,
);

View File

@ -1,6 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:calendar_view/calendar_view.dart';
import 'package:easy_localization/easy_localization.dart';
@ -17,13 +18,15 @@ class CalendarToolbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const SizedBox(
return SizedBox(
height: 40,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_UnscheduleEventsButton(),
_SettingButton(),
const _UnscheduleEventsButton(),
SettingButton(
databaseController: context.read<CalendarBloc>().databaseController,
),
],
),
);
@ -115,12 +118,16 @@ class _UnscheduleEventsButtonState extends State<_UnscheduleEventsButton> {
),
popupBuilder: (context) {
final cells = <Widget>[
FlowyText.medium(
LocaleKeys.calendar_settings_noDateHint.tr(),
color: Theme.of(context).hintColor,
overflow: TextOverflow.ellipsis,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
child: FlowyText.medium(
// LocaleKeys.calendar_settings_noDateHint.tr(),
LocaleKeys.calendar_settings_clickToAdd.tr(),
color: Theme.of(context).hintColor,
overflow: TextOverflow.ellipsis,
),
),
const VSpace(10),
const VSpace(6),
...unscheduledEvents.map(
(e) => _UnscheduledEventItem(
event: e,
@ -164,7 +171,11 @@ class _UnscheduledEventItem extends StatelessWidget {
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
text: FlowyText.medium(event.title),
text: FlowyText.medium(
event.title.isEmpty
? LocaleKeys.calendar_defaultNewCalendarTitle.tr()
: event.title,
),
onTap: onPressed,
),
);

View File

@ -0,0 +1,54 @@
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import '../../workspace/presentation/home/home_stack.dart';
/// [DatabaseViewPlugin] is used to build the grid, calendar, and board.
/// It is a wrapper of the [Plugin] class. The underlying [Plugin] is
/// determined by the [ViewPB.pluginType] field.
///
class DatabaseViewPlugin extends Plugin {
final ViewListener _viewListener;
ViewPB _view;
Plugin _innerPlugin;
DatabaseViewPlugin({
required ViewPB view,
}) : _view = view,
_innerPlugin = _makeInnerPlugin(view),
_viewListener = ViewListener(view: view) {
_listenOnLayoutChanged();
}
@override
PluginId get id => _innerPlugin.id;
@override
PluginType get pluginType => _innerPlugin.pluginType;
@override
PluginWidgetBuilder get widgetBuilder => _innerPlugin.widgetBuilder;
void _listenOnLayoutChanged() {
_viewListener.start(
onViewUpdated: (result) {
result.fold(
(updatedView) {
if (_view.layout != updatedView.layout) {
_innerPlugin = _makeInnerPlugin(updatedView);
getIt<HomeStackManager>().setPlugin(_innerPlugin);
}
_view = updatedView;
},
(r) => null,
);
},
);
}
}
Plugin _makeInnerPlugin(ViewPB view) {
return makePlugin(pluginType: view.pluginType, data: view);
}

View File

@ -87,7 +87,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
add(GridEvent.didReceiveGridUpdate(database));
}
},
onRowsChanged: (rowInfos, _, reason) {
onNumOfRowsChanged: (rowInfos, _, reason) {
if (!isClosed) {
add(GridEvent.didReceiveRowUpdate(rowInfos, reason));
}

View File

@ -49,20 +49,21 @@ class GridPlugin extends Plugin {
notifier = ViewPluginNotifier(view: view);
@override
PluginDisplay get display => GridPluginDisplay(notifier: notifier);
PluginWidgetBuilder get widgetBuilder =>
GridPluginWidgetBuilder(notifier: notifier);
@override
PluginId get id => notifier.view.id;
@override
PluginType get ty => _pluginType;
PluginType get pluginType => _pluginType;
}
class GridPluginDisplay extends PluginDisplay {
class GridPluginWidgetBuilder extends PluginWidgetBuilder {
final ViewPluginNotifier notifier;
ViewPB get view => notifier.view;
GridPluginDisplay({required this.notifier, Key? key});
GridPluginWidgetBuilder({required this.notifier, Key? key});
@override
Widget get leftBarItem => ViewLeftBarItem(view: view);

View File

@ -1,6 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
@ -36,10 +35,7 @@ class GridPage extends StatefulWidget {
required this.view,
this.onDeleted,
Key? key,
}) : databaseController = DatabaseController(
view: view,
layoutType: DatabaseLayoutPB.Grid,
),
}) : databaseController = DatabaseController(view: view),
super(key: key);
final ViewPB view;

View File

@ -0,0 +1,133 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/layout/layout_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../layout/sizes.dart';
class DatabaseLayoutList extends StatefulWidget {
final String viewId;
final DatabaseLayoutPB currentLayout;
const DatabaseLayoutList({
required this.viewId,
required this.currentLayout,
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _DatabaseLayoutListState();
}
class _DatabaseLayoutListState extends State<DatabaseLayoutList> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => DatabaseLayoutBloc(
viewId: widget.viewId,
databaseLayout: widget.currentLayout,
)..add(const DatabaseLayoutEvent.initial()),
child: BlocBuilder<DatabaseLayoutBloc, DatabaseLayoutState>(
builder: (context, state) {
final cells = DatabaseLayoutPB.values.map((layout) {
final isSelected = state.databaseLayout == layout;
return DatabaseViewLayoutCell(
databaseLayout: layout,
isSelected: isSelected,
onTap: (selectedLayout) {
context
.read<DatabaseLayoutBloc>()
.add(DatabaseLayoutEvent.updateLayout(selectedLayout));
},
);
}).toList();
return ListView.separated(
controller: ScrollController(),
shrinkWrap: true,
itemCount: cells.length,
itemBuilder: (BuildContext context, int index) => cells[index],
separatorBuilder: (BuildContext context, int index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
padding: const EdgeInsets.symmetric(vertical: 6.0),
);
},
),
);
}
}
extension DatabaseLayoutExtension on DatabaseLayoutPB {
String layoutName() {
switch (this) {
case DatabaseLayoutPB.Board:
return LocaleKeys.board_menuName.tr();
case DatabaseLayoutPB.Calendar:
return LocaleKeys.calendar_menuName.tr();
case DatabaseLayoutPB.Grid:
return LocaleKeys.grid_menuName.tr();
default:
return "";
}
}
String iconName() {
switch (this) {
case DatabaseLayoutPB.Board:
return 'editor/board';
case DatabaseLayoutPB.Calendar:
return "editor/grid";
case DatabaseLayoutPB.Grid:
return "editor/grid";
default:
return "";
}
}
}
class DatabaseViewLayoutCell extends StatelessWidget {
final bool isSelected;
final DatabaseLayoutPB databaseLayout;
final void Function(DatabaseLayoutPB) onTap;
const DatabaseViewLayoutCell({
required this.databaseLayout,
required this.isSelected,
required this.onTap,
super.key,
});
@override
Widget build(BuildContext context) {
Widget? checkmark;
if (isSelected) {
checkmark = svgWidget("grid/checkmark");
}
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
databaseLayout.layoutName(),
color: AFThemeExtension.of(context).textColor,
),
leftIcon: svgWidget(
databaseLayout.iconName(),
color: Theme.of(context).iconTheme.color,
),
rightIcon: checkmark,
onTap: () => onTap(databaseLayout),
).padding(horizontal: 6.0),
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
@ -9,36 +9,29 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import '../../layout/sizes.dart';
class GridSettingContext {
final String viewId;
final FieldController fieldController;
GridSettingContext({
required this.viewId,
required this.fieldController,
});
}
class GridSettingList extends StatelessWidget {
final GridSettingContext settingContext;
final Function(DatabaseSettingAction, GridSettingContext) onAction;
const GridSettingList({
required this.settingContext,
class DatabaseSettingList extends StatelessWidget {
final DatabaseController databaseContoller;
final Function(DatabaseSettingAction, DatabaseController) onAction;
const DatabaseSettingList({
required this.databaseContoller,
required this.onAction,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final cells = DatabaseSettingAction.values
.where((value) => value.enable())
.map((action) {
final cells = DatabaseSettingAction.values.where((element) {
if (element == DatabaseSettingAction.showGroup) {
return databaseContoller.databaseLayout == DatabaseLayoutPB.Board;
} else {
return true;
}
}).map((action) {
return _SettingItem(
action: action,
onAction: (action) => onAction(action, settingContext),
onAction: (action) => onAction(action, databaseContoller),
);
}).toList();
@ -78,9 +71,7 @@ class _SettingItem extends StatelessWidget {
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
action.title(),
color: action.enable()
? AFThemeExtension.of(context).textColor
: Theme.of(context).disabledColor,
color: AFThemeExtension.of(context).textColor,
),
onTap: () => onAction(action),
leftIcon: svgWidget(
@ -91,36 +82,3 @@ class _SettingItem extends StatelessWidget {
);
}
}
extension _GridSettingExtension on DatabaseSettingAction {
String iconName() {
switch (this) {
case DatabaseSettingAction.showFilters:
return 'grid/setting/filter';
case DatabaseSettingAction.sortBy:
return 'grid/setting/sort';
case DatabaseSettingAction.showProperties:
return 'grid/setting/properties';
}
}
String title() {
switch (this) {
case DatabaseSettingAction.showFilters:
return LocaleKeys.grid_settings_filter.tr();
case DatabaseSettingAction.sortBy:
return LocaleKeys.grid_settings_sortBy.tr();
case DatabaseSettingAction.showProperties:
return LocaleKeys.grid_settings_Properties.tr();
}
}
bool enable() {
switch (this) {
case DatabaseSettingAction.showProperties:
return true;
default:
return false;
}
}
}

View File

@ -1,9 +1,11 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../layout/sizes.dart';
import 'filter_button.dart';
import 'setting_button.dart';
import '../../../../widgets/setting/setting_button.dart';
import 'sort_button.dart';
class GridToolbarContext {
@ -29,7 +31,9 @@ class GridToolbar extends StatelessWidget {
const Spacer(),
const FilterButton(),
const SortButton(),
const SettingButton(),
SettingButton(
databaseController: context.read<GridBloc>().databaseController,
),
],
),
);

View File

@ -1,102 +0,0 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../layout/sizes.dart';
import 'grid_property.dart';
import 'grid_setting.dart';
class SettingButton extends StatefulWidget {
const SettingButton({Key? key}) : super(key: key);
@override
State<SettingButton> createState() => _SettingButtonState();
}
class _SettingButtonState extends State<SettingButton> {
late PopoverController _popoverController;
@override
void initState() {
_popoverController = PopoverController();
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocSelector<GridBloc, GridState, GridSettingContext>(
selector: (state) {
final fieldController =
context.read<GridBloc>().databaseController.fieldController;
return GridSettingContext(
viewId: state.viewId,
fieldController: fieldController,
);
},
builder: (context, settingContext) {
return SizedBox(
height: 26,
child: AppFlowyPopover(
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(260, 400)),
direction: PopoverDirection.bottomWithLeftAligned,
offset: const Offset(0, 8),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
child: FlowyTextButton(
LocaleKeys.settings_title.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets,
onPressed: () => _popoverController.show(),
),
popupBuilder: (BuildContext context) {
return _GridSettingListPopover(settingContext: settingContext);
},
),
);
},
);
}
}
class _GridSettingListPopover extends StatefulWidget {
final GridSettingContext settingContext;
const _GridSettingListPopover({Key? key, required this.settingContext})
: super(key: key);
@override
State<StatefulWidget> createState() => _GridSettingListPopoverState();
}
class _GridSettingListPopoverState extends State<_GridSettingListPopover> {
DatabaseSettingAction? _action;
@override
Widget build(BuildContext context) {
if (_action == DatabaseSettingAction.showProperties) {
return GridPropertyList(
viewId: widget.settingContext.viewId,
fieldController: widget.settingContext.fieldController,
);
}
return GridSettingList(
settingContext: widget.settingContext,
onAction: (action, settingContext) {
setState(() {
_action = action;
});
},
).padding(all: 6.0);
}
}

View File

@ -11,23 +11,23 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../layout/sizes.dart';
import '../header/field_editor.dart';
import '../../grid/presentation/layout/sizes.dart';
import '../../grid/presentation/widgets/header/field_editor.dart';
class GridPropertyList extends StatefulWidget {
class DatabasePropertyList extends StatefulWidget {
final String viewId;
final FieldController fieldController;
const GridPropertyList({
const DatabasePropertyList({
required this.viewId,
required this.fieldController,
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _GridPropertyListState();
State<StatefulWidget> createState() => _DatabasePropertyListState();
}
class _GridPropertyListState extends State<GridPropertyList> {
class _DatabasePropertyListState extends State<DatabasePropertyList> {
late PopoverMutex _popoverMutex;
@override

View File

@ -11,11 +11,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class GridGroupList extends StatelessWidget {
class DatabaseGroupList extends StatelessWidget {
final String viewId;
final FieldController fieldController;
final VoidCallback onDismissed;
const GridGroupList({
const DatabaseGroupList({
required this.viewId,
required this.fieldController,
required this.onDismissed,

View File

@ -0,0 +1,116 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
import 'package:appflowy/plugins/database_view/widgets/group/database_group.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../grid/presentation/layout/sizes.dart';
import '../../grid/presentation/widgets/toolbar/grid_layout.dart';
import '../field/grid_property.dart';
import '../../grid/presentation/widgets/toolbar/grid_setting.dart';
class SettingButton extends StatefulWidget {
final DatabaseController databaseController;
const SettingButton({
required this.databaseController,
Key? key,
}) : super(key: key);
@override
State<SettingButton> createState() => _SettingButtonState();
}
class _SettingButtonState extends State<SettingButton> {
late PopoverController _popoverController;
@override
void initState() {
_popoverController = PopoverController();
super.initState();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 26,
child: AppFlowyPopover(
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(200, 400)),
direction: PopoverDirection.bottomWithLeftAligned,
offset: const Offset(0, 8),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
child: FlowyTextButton(
LocaleKeys.settings_title.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets,
onPressed: () => _popoverController.show(),
),
popupBuilder: (BuildContext context) {
return _DatabaseSettingListPopover(
databaseController: widget.databaseController,
);
},
),
);
}
}
class _DatabaseSettingListPopover extends StatefulWidget {
final DatabaseController databaseController;
const _DatabaseSettingListPopover({
required this.databaseController,
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _DatabaseSettingListPopoverState();
}
class _DatabaseSettingListPopoverState
extends State<_DatabaseSettingListPopover> {
DatabaseSettingAction? _action;
@override
Widget build(BuildContext context) {
if (_action == null) {
return DatabaseSettingList(
databaseContoller: widget.databaseController,
onAction: (action, settingContext) {
setState(() {
_action = action;
});
},
).padding(all: 6.0);
} else {
switch (_action!) {
case DatabaseSettingAction.showLayout:
return DatabaseLayoutList(
viewId: widget.databaseController.viewId,
currentLayout: widget.databaseController.databaseLayout!,
);
case DatabaseSettingAction.showGroup:
return DatabaseGroupList(
viewId: widget.databaseController.viewId,
fieldController: widget.databaseController.fieldController,
onDismissed: () {
// widget.popoverController.close();
},
);
case DatabaseSettingAction.showProperties:
return DatabasePropertyList(
viewId: widget.databaseController.viewId,
fieldController: widget.databaseController.fieldController,
);
}
}
}
}