fix: show calendar layout (#2752)

This commit is contained in:
Nathan.fooo
2023-06-10 13:23:29 +08:00
committed by GitHub
parent ab5a3dae3c
commit d4e39389d2
12 changed files with 268 additions and 335 deletions

View File

@ -80,6 +80,7 @@ class DatabaseController {
final DatabaseViewBackendService _databaseViewBackendSvc;
final FieldController fieldController;
DatabaseLayoutPB? databaseLayout;
DatabaseLayoutSettingPB? databaseLayoutSetting;
late DatabaseViewCache _viewCache;
// Callbacks
@ -92,17 +93,17 @@ class DatabaseController {
RowCache get rowCache => _viewCache.rowCache;
// Listener
final DatabaseGroupListener groupListener;
final DatabaseLayoutSettingListener layoutListener;
final DatabaseCalendarLayoutListener calendarLayoutListener;
final DatabaseGroupListener _groupListener;
final DatabaseLayoutSettingListener _layoutListener;
final DatabaseCalendarLayoutListener _calendarLayoutListener;
DatabaseController({required ViewPB view})
: viewId = view.id,
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
fieldController = FieldController(viewId: view.id),
groupListener = DatabaseGroupListener(view.id),
layoutListener = DatabaseLayoutSettingListener(view.id),
calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
_groupListener = DatabaseGroupListener(view.id),
_layoutListener = DatabaseLayoutSettingListener(view.id),
_calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
_viewCache = DatabaseViewCache(
viewId: viewId,
fieldController: fieldController,
@ -224,7 +225,7 @@ class DatabaseController {
Future<void> dispose() async {
await _databaseViewBackendSvc.closeView();
await fieldController.dispose();
await groupListener.stop();
await _groupListener.stop();
await _viewCache.dispose();
_databaseCallbacks = null;
_groupCallbacks = null;
@ -248,7 +249,12 @@ class DatabaseController {
if (databaseLayout != null) {
_databaseViewBackendSvc.getLayoutSetting(databaseLayout!).then((result) {
result.fold(
(l) => _layoutCallbacks?.onLoadLayout(l),
(newDatabaseLayoutSetting) {
databaseLayoutSetting = newDatabaseLayoutSetting;
databaseLayoutSetting?.freeze();
_layoutCallbacks?.onLoadLayout(newDatabaseLayoutSetting);
},
(r) => Log.error(r),
);
});
@ -285,7 +291,7 @@ class DatabaseController {
}
void _listenOnGroupChanged() {
groupListener.start(
_groupListener.start(
onNumOfGroupsChanged: (result) {
result.fold(
(changeset) {
@ -316,11 +322,14 @@ class DatabaseController {
}
void _listenOnLayoutChanged() {
layoutListener.start(
_layoutListener.start(
onLayoutChanged: (result) {
result.fold(
(l) {
_layoutCallbacks?.onLayoutChanged(l);
(newDatabaseLayoutSetting) {
databaseLayoutSetting = newDatabaseLayoutSetting;
databaseLayoutSetting?.freeze();
_layoutCallbacks?.onLayoutChanged(newDatabaseLayoutSetting);
},
(r) => Log.error(r),
);
@ -329,7 +338,7 @@ class DatabaseController {
}
void _listenOnCalendarLayoutChanged() {
calendarLayoutListener.start(
_calendarLayoutListener.start(
onCalendarLayoutChanged: (result) {
result.fold(
(l) {

View File

@ -1,4 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -45,6 +46,7 @@ enum DatabaseSettingAction {
showProperties,
showLayout,
showGroup,
showCalendarLayout,
}
extension DatabaseSettingActionExtension on DatabaseSettingAction {
@ -56,6 +58,8 @@ extension DatabaseSettingActionExtension on DatabaseSettingAction {
return 'grid/setting/database_layout';
case DatabaseSettingAction.showGroup:
return 'grid/setting/group';
case DatabaseSettingAction.showCalendarLayout:
return 'grid/setting/calendar_layout';
}
}
@ -67,6 +71,33 @@ extension DatabaseSettingActionExtension on DatabaseSettingAction {
return LocaleKeys.grid_settings_databaseLayout.tr();
case DatabaseSettingAction.showGroup:
return LocaleKeys.grid_settings_group.tr();
case DatabaseSettingAction.showCalendarLayout:
return LocaleKeys.calendar_settings_name.tr();
}
}
}
/// Returns the list of actions that should be shown for the given database layout.
List<DatabaseSettingAction> actionsForDatabaseLayout(DatabaseLayoutPB? layout) {
switch (layout) {
case DatabaseLayoutPB.Board:
return [
DatabaseSettingAction.showProperties,
DatabaseSettingAction.showLayout,
DatabaseSettingAction.showGroup,
];
case DatabaseLayoutPB.Calendar:
return [
DatabaseSettingAction.showProperties,
DatabaseSettingAction.showLayout,
DatabaseSettingAction.showCalendarLayout,
];
case DatabaseLayoutPB.Grid:
return [
DatabaseSettingAction.showProperties,
DatabaseSettingAction.showLayout,
];
default:
return [];
}
}

View File

@ -1,3 +1,5 @@
import 'package:appflowy/plugins/database_view/application/layout/layout_setting_listener.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
@ -9,10 +11,19 @@ typedef DayOfWeek = int;
class CalendarSettingBloc
extends Bloc<CalendarSettingEvent, CalendarSettingState> {
CalendarSettingBloc({required CalendarLayoutSettingPB? layoutSettings})
: super(CalendarSettingState.initial(layoutSettings)) {
final String viewId;
final DatabaseLayoutSettingListener _listener;
CalendarSettingBloc({
required this.viewId,
required CalendarLayoutSettingPB? layoutSettings,
}) : _listener = DatabaseLayoutSettingListener(viewId),
super(CalendarSettingState.initial(layoutSettings)) {
on<CalendarSettingEvent>((event, emit) {
event.when(
init: () {
_startListening();
},
performAction: (action) {
emit(state.copyWith(selectedAction: Some(action)));
},
@ -22,6 +33,44 @@ class CalendarSettingBloc
);
});
}
void _startListening() {
_listener.start(
onLayoutChanged: (result) {
if (isClosed) {
return;
}
result.fold(
(setting) =>
add(CalendarSettingEvent.updateLayoutSetting(setting.calendar)),
(r) => Log.error(r),
);
},
);
}
@override
Future<void> close() async {
await _listener.stop();
return super.close();
}
}
@freezed
class CalendarSettingEvent with _$CalendarSettingEvent {
const factory CalendarSettingEvent.init() = _Init;
const factory CalendarSettingEvent.performAction(
CalendarSettingAction action,
) = _PerformAction;
const factory CalendarSettingEvent.updateLayoutSetting(
CalendarLayoutSettingPB setting,
) = _UpdateLayoutSetting;
}
enum CalendarSettingAction {
properties,
layout,
}
@freezed
@ -39,18 +88,3 @@ class CalendarSettingState with _$CalendarSettingState {
layoutSetting: layoutSettings == null ? none() : Some(layoutSettings),
);
}
@freezed
class CalendarSettingEvent with _$CalendarSettingEvent {
const factory CalendarSettingEvent.performAction(
CalendarSettingAction action,
) = _PerformAction;
const factory CalendarSettingEvent.updateLayoutSetting(
CalendarLayoutSettingPB setting,
) = _UpdateLayoutSetting;
}
enum CalendarSettingAction {
properties,
layout,
}

View File

@ -15,17 +15,25 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:protobuf/protobuf.dart';
import 'calendar_setting.dart';
abstract class ICalendarSetting {
/// Returns the current layout settings for the calendar view.
CalendarLayoutSettingPB? getLayoutSetting();
/// Updates the layout settings for the calendar view.
void updateLayoutSettings(CalendarLayoutSettingPB layoutSettings);
}
/// Widget that displays a list of settings that alters the appearance of the
/// calendar
class CalendarLayoutSetting extends StatefulWidget {
final CalendarSettingContext settingContext;
final Function(CalendarLayoutSettingPB? layoutSettings) onUpdated;
final String viewId;
final FieldController fieldController;
final ICalendarSetting calendarSettingController;
const CalendarLayoutSetting({
required this.onUpdated,
required this.settingContext,
required this.viewId,
required this.fieldController,
required this.calendarSettingController,
super.key,
});
@ -44,85 +52,90 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
@override
Widget build(BuildContext context) {
return BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
builder: (context, state) {
final CalendarLayoutSettingPB? settings = state.layoutSetting
.foldLeft(null, (previous, settings) => settings);
if (settings == null) {
return const CircularProgressIndicator();
}
final availableSettings = _availableCalendarSettings(settings);
final items = availableSettings.map((setting) {
switch (setting) {
case CalendarLayoutSettingAction.showWeekNumber:
return ShowWeekNumber(
showWeekNumbers: settings.showWeekNumbers,
onUpdated: (showWeekNumbers) {
_updateLayoutSettings(
context,
showWeekNumbers: showWeekNumbers,
onUpdated: widget.onUpdated,
);
},
);
case CalendarLayoutSettingAction.showWeekends:
return ShowWeekends(
showWeekends: settings.showWeekends,
onUpdated: (showWeekends) {
_updateLayoutSettings(
context,
showWeekends: showWeekends,
onUpdated: widget.onUpdated,
);
},
);
case CalendarLayoutSettingAction.firstDayOfWeek:
return FirstDayOfWeek(
firstDayOfWeek: settings.firstDayOfWeek,
popoverMutex: popoverMutex,
onUpdated: (firstDayOfWeek) {
_updateLayoutSettings(
context,
onUpdated: widget.onUpdated,
firstDayOfWeek: firstDayOfWeek,
);
},
);
case CalendarLayoutSettingAction.layoutField:
return LayoutDateField(
fieldController: widget.settingContext.fieldController,
viewId: widget.settingContext.viewId,
fieldId: settings.fieldId,
popoverMutex: popoverMutex,
onUpdated: (fieldId) {
_updateLayoutSettings(
context,
onUpdated: widget.onUpdated,
layoutFieldId: fieldId,
);
},
);
default:
return const SizedBox();
}
}).toList();
return SizedBox(
width: 200,
child: ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
itemCount: items.length,
separatorBuilder: (context, index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) => items[index],
padding: const EdgeInsets.all(6.0),
),
);
return BlocProvider(
create: (context) {
return CalendarSettingBloc(
viewId: widget.viewId,
layoutSettings: widget.calendarSettingController.getLayoutSetting(),
)..add(
const CalendarSettingEvent.init(),
);
},
child: BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
builder: (context, state) {
final CalendarLayoutSettingPB? settings = state.layoutSetting
.foldLeft(null, (previous, settings) => settings);
if (settings == null) {
return const CircularProgressIndicator();
}
final availableSettings = _availableCalendarSettings(settings);
final items = availableSettings.map((setting) {
switch (setting) {
case CalendarLayoutSettingAction.showWeekNumber:
return ShowWeekNumber(
showWeekNumbers: settings.showWeekNumbers,
onUpdated: (showWeekNumbers) {
_updateLayoutSettings(
context,
showWeekNumbers: showWeekNumbers,
);
},
);
case CalendarLayoutSettingAction.showWeekends:
return ShowWeekends(
showWeekends: settings.showWeekends,
onUpdated: (showWeekends) {
_updateLayoutSettings(
context,
showWeekends: showWeekends,
);
},
);
case CalendarLayoutSettingAction.firstDayOfWeek:
return FirstDayOfWeek(
firstDayOfWeek: settings.firstDayOfWeek,
popoverMutex: popoverMutex,
onUpdated: (firstDayOfWeek) {
_updateLayoutSettings(
context,
firstDayOfWeek: firstDayOfWeek,
);
},
);
case CalendarLayoutSettingAction.layoutField:
return LayoutDateField(
fieldController: widget.fieldController,
viewId: widget.viewId,
fieldId: settings.fieldId,
popoverMutex: popoverMutex,
onUpdated: (fieldId) {
_updateLayoutSettings(
context,
layoutFieldId: fieldId,
);
},
);
default:
return const SizedBox();
}
}).toList();
return SizedBox(
width: 200,
child: ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
itemCount: items.length,
separatorBuilder: (context, index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) => items[index],
padding: const EdgeInsets.all(6.0),
),
);
},
),
);
}
@ -161,7 +174,6 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
void _updateLayoutSettings(
BuildContext context, {
required Function(CalendarLayoutSettingPB? layoutSettings) onUpdated,
bool? showWeekends,
bool? showWeekNumbers,
int? firstDayOfWeek,
@ -190,7 +202,8 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
context
.read<CalendarSettingBloc>()
.add(CalendarSettingEvent.updateLayoutSetting(setting));
onUpdated(setting);
widget.calendarSettingController.updateLayoutSettings(setting);
}
}

View File

@ -1,137 +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/calendar/application/calendar_setting_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.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';
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 'calendar_layout_setting.dart';
/// The highest-level widget shown in the popover triggered by clicking the
/// "Settings" button. Shows [AllCalendarSettings] by default, but replaces its
/// contents with the submenu when a category is selected.
class CalendarSetting extends StatelessWidget {
final CalendarSettingContext settingContext;
final CalendarLayoutSettingPB? layoutSettings;
final Function(CalendarLayoutSettingPB? layoutSettings) onUpdated;
const CalendarSetting({
required this.onUpdated,
required this.layoutSettings,
required this.settingContext,
super.key,
});
@override
Widget build(BuildContext context) {
return BlocProvider<CalendarSettingBloc>(
create: (context) => CalendarSettingBloc(layoutSettings: layoutSettings),
child: BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
builder: (context, state) {
final CalendarSettingAction? action =
state.selectedAction.foldLeft(null, (previous, action) => action);
switch (action) {
case CalendarSettingAction.properties:
return DatabasePropertyList(
viewId: settingContext.viewId,
fieldController: settingContext.fieldController,
);
case CalendarSettingAction.layout:
return CalendarLayoutSetting(
onUpdated: onUpdated,
settingContext: settingContext,
);
default:
return const AllCalendarSettings().padding(all: 6.0);
}
},
),
);
}
}
/// Shows all of the available categories of settings that can be set here.
/// For now, this only includes the Layout category.
class AllCalendarSettings extends StatelessWidget {
const AllCalendarSettings({super.key});
@override
Widget build(BuildContext context) {
final items = CalendarSettingAction.values
.map((e) => _settingItem(context, e))
.toList();
return SizedBox(
width: 140,
child: ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
itemCount: items.length,
separatorBuilder: (context, index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) => items[index],
),
);
}
Widget _settingItem(BuildContext context, CalendarSettingAction action) {
Widget? icon;
if (action.iconName() != null) {
icon = FlowySvg(
name: action.iconName()!,
);
}
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
leftIcon: icon,
text: FlowyText.medium(action.title()),
onTap: () {
context
.read<CalendarSettingBloc>()
.add(CalendarSettingEvent.performAction(action));
},
),
);
}
}
extension _SettingExtension on CalendarSettingAction {
String? iconName() {
switch (this) {
case CalendarSettingAction.properties:
return 'grid/setting/properties';
case CalendarSettingAction.layout:
return null;
}
}
String title() {
switch (this) {
case CalendarSettingAction.properties:
return LocaleKeys.grid_settings_Properties.tr();
case CalendarSettingAction.layout:
return LocaleKeys.grid_settings_layout.tr();
}
}
}
class CalendarSettingContext {
final String viewId;
final FieldController fieldController;
CalendarSettingContext({
required this.viewId,
required this.fieldController,
});
}

View File

@ -11,7 +11,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../application/calendar_bloc.dart';
import 'calendar_setting.dart';
class CalendarToolbar extends StatelessWidget {
const CalendarToolbar({super.key});
@ -33,52 +32,6 @@ class CalendarToolbar extends StatelessWidget {
}
}
class _SettingButton extends StatefulWidget {
const _SettingButton({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _SettingButtonState();
}
class _SettingButtonState extends State<_SettingButton> {
@override
Widget build(BuildContext context) {
return AppFlowyPopover(
direction: PopoverDirection.bottomWithRightAligned,
constraints: BoxConstraints.loose(const Size(300, 400)),
margin: EdgeInsets.zero,
child: FlowyTextButton(
LocaleKeys.settings_title.tr(),
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets,
),
popupBuilder: (BuildContext popoverContext) {
final bloc = context.watch<CalendarBloc>();
final settingContext = CalendarSettingContext(
viewId: bloc.viewId,
fieldController: bloc.fieldController,
);
return CalendarSetting(
settingContext: settingContext,
layoutSettings: bloc.state.settings.fold(
() => null,
(settings) => settings,
),
onUpdated: (layoutSettings) {
if (layoutSettings == null) {
return;
}
context
.read<CalendarBloc>()
.add(CalendarEvent.updateCalendarLayoutSetting(layoutSettings));
},
);
}, // use blocbuilder
);
}
}
class _UnscheduleEventsButton extends StatefulWidget {
const _UnscheduleEventsButton({Key? key}) : super(key: key);

View File

@ -1,6 +1,5 @@
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.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,7 +8,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import '../../layout/sizes.dart';
import '../../grid/presentation/layout/sizes.dart';
class DatabaseSettingList extends StatelessWidget {
final DatabaseController databaseContoller;
@ -22,33 +21,25 @@ class DatabaseSettingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cells = DatabaseSettingAction.values.where((element) {
if (element == DatabaseSettingAction.showGroup) {
return databaseContoller.databaseLayout == DatabaseLayoutPB.Board;
} else {
return true;
}
}).map((action) {
final cells = actionsForDatabaseLayout(databaseContoller.databaseLayout)
.map((action) {
return _SettingItem(
action: action,
onAction: (action) => onAction(action, databaseContoller),
);
}).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];
},
),
return 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];
},
);
}
}

View File

@ -1,7 +1,9 @@
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/calendar/presentation/toolbar/calendar_layout_setting.dart';
import 'package:appflowy/plugins/database_view/widgets/group/database_group.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
@ -12,7 +14,7 @@ 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';
import 'database_setting.dart';
class SettingButton extends StatefulWidget {
final DatabaseController databaseController;
@ -110,7 +112,31 @@ class _DatabaseSettingListPopoverState
viewId: widget.databaseController.viewId,
fieldController: widget.databaseController.fieldController,
);
case DatabaseSettingAction.showCalendarLayout:
return CalendarLayoutSetting(
viewId: widget.databaseController.viewId,
fieldController: widget.databaseController.fieldController,
calendarSettingController: ICalendarSettingImpl(
widget.databaseController,
),
);
}
}
}
}
class ICalendarSettingImpl extends ICalendarSetting {
final DatabaseController _databaseController;
ICalendarSettingImpl(this._databaseController);
@override
void updateLayoutSettings(CalendarLayoutSettingPB layoutSettings) {
_databaseController.updateCalenderLayoutSetting(layoutSettings);
}
@override
CalendarLayoutSettingPB? getLayoutSetting() {
return _databaseController.databaseLayoutSetting?.calendar;
}
}