chore: calendar plugin backend database data (#1884)

* chore: implement CalendarBloc

* chore: enable save and read the calendar setting

* style: more precise variable name

* chore: backend calendar settings

* chore: protobuf for layout settings

* chore: update test

* chore: Enumerate the LayoutTypePB enum type to get the supported layout types

* fix: deserialize object type is not the same as serialize object type

* chore: add set/get calendar settings event

* ci: fix wanrings

---------

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: vedon <vedon.fu@gmail.com>
This commit is contained in:
Richard Shiue 2023-02-24 16:59:14 +08:00 committed by GitHub
parent 01a388c1c4
commit 6877607c5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 568 additions and 32 deletions

View File

@ -0,0 +1,159 @@
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'package:calendar_view/calendar_view.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'calendar_data_controller.dart';
part 'calendar_bloc.freezed.dart';
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
final CalendarDataController _databaseDataController;
final EventController calendarEventsController = EventController();
GridFieldController get fieldController =>
_databaseDataController.fieldController;
String get databaseId => _databaseDataController.databaseId;
CalendarBloc({required ViewPB view})
: _databaseDataController = CalendarDataController(view: view),
super(CalendarState.initial(view.id)) {
on<CalendarEvent>(
(event, emit) async {
await event.when(
initial: () async {
_startListening();
await _openDatabase(emit);
},
didReceiveCalendarSettings: (CalendarSettingsPB settings) {
emit(state.copyWith(settings: Some(settings)));
},
didReceiveDatabaseUpdate: (DatabasePB database) {
emit(state.copyWith(database: Some(database)));
},
didReceiveError: (FlowyError error) {
emit(state.copyWith(noneOrError: Some(error)));
},
);
},
);
}
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
final result = await _databaseDataController.openDatabase();
result.fold(
(database) => emit(
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
),
(err) => emit(
state.copyWith(loadingState: DatabaseLoadingState.finish(right(err))),
),
);
}
GridRowCache? getRowCache(String blockId) {
return _databaseDataController.rowCache;
}
void _startListening() {
_databaseDataController.addListener(
onDatabaseChanged: (database) {
if (!isClosed) return;
add(CalendarEvent.didReceiveDatabaseUpdate(database));
},
onSettingsChanged: (CalendarSettingsPB settings) {
if (isClosed) return;
add(CalendarEvent.didReceiveCalendarSettings(settings));
},
onArrangeWithNewField: (field) {
if (isClosed) return;
_initializeEvents(field);
// add(CalendarEvent.)
},
onError: (err) {
Log.error(err);
},
);
}
void _initializeEvents(FieldPB dateField) {
calendarEventsController.removeWhere((element) => true);
const events = <CalendarEventData<CalendarData>>[];
// final List<CalendarEventData<CalendarData>> events = rows.map((row) {
// final event = CalendarEventData(
// title: "",
// date: row -> dateField -> value,
// event: row,
// );
// return event;
// }).toList();
calendarEventsController.addAll(events);
}
}
@freezed
class CalendarEvent with _$CalendarEvent {
const factory CalendarEvent.initial() = _InitialCalendar;
const factory CalendarEvent.didReceiveCalendarSettings(
CalendarSettingsPB settings) = _DidReceiveCalendarSettings;
const factory CalendarEvent.didReceiveError(FlowyError error) =
_DidReceiveError;
const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
_DidReceiveDatabaseUpdate;
}
@freezed
class CalendarState with _$CalendarState {
const factory CalendarState({
required String databaseId,
required Option<DatabasePB> database,
required Option<FieldPB> dateField,
required Option<List<RowInfo>> unscheduledRows,
required Option<CalendarSettingsPB> settings,
required DatabaseLoadingState loadingState,
required Option<FlowyError> noneOrError,
}) = _CalendarState;
factory CalendarState.initial(String databaseId) => CalendarState(
database: none(),
databaseId: databaseId,
dateField: none(),
unscheduledRows: none(),
settings: none(),
noneOrError: none(),
loadingState: const _Loading(),
);
}
@freezed
class DatabaseLoadingState with _$DatabaseLoadingState {
const factory DatabaseLoadingState.loading() = _Loading;
const factory DatabaseLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish;
}
class CalendarEditingRow {
RowPB row;
int? index;
CalendarEditingRow({
required this.row,
required this.index,
});
}
class CalendarData {
final RowInfo rowInfo;
CalendarData(this.rowInfo);
}

View File

@ -0,0 +1,115 @@
import 'dart:async';
import 'dart:collection';
import 'package:app_flowy/plugins/grid/application/view/grid_view_cache.dart';
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'calendar_listener.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
typedef OnDatabaseChanged = void Function(DatabasePB);
typedef OnSettingsChanged = void Function(CalendarSettingsPB);
typedef OnArrangeWithNewField = void Function(FieldPB);
typedef OnRowsChanged = void Function(List<RowInfo>, RowsChangedReason);
typedef OnError = void Function(FlowyError);
class CalendarDataController {
final String databaseId;
final DatabaseFFIService _databaseFFIService;
final GridFieldController fieldController;
final CalendarListener _listener;
late DatabaseViewCache _viewCache;
OnFieldsChanged? _onFieldsChanged;
OnDatabaseChanged? _onDatabaseChanged;
OnRowsChanged? _onRowsChanged;
OnSettingsChanged? _onSettingsChanged;
OnArrangeWithNewField? _onArrangeWithNewField;
OnError? _onError;
List<RowInfo> get rowInfos => _viewCache.rowInfos;
GridRowCache get rowCache => _viewCache.rowCache;
CalendarDataController({required ViewPB view})
: databaseId = view.id,
_listener = CalendarListener(view.id),
_databaseFFIService = DatabaseFFIService(viewId: view.id),
fieldController = GridFieldController(databaseId: view.id) {
_viewCache = DatabaseViewCache(
databaseId: view.id,
fieldController: fieldController,
);
_viewCache.addListener(onRowsChanged: (reason) {
_onRowsChanged?.call(rowInfos, reason);
});
}
void addListener({
required OnDatabaseChanged onDatabaseChanged,
OnFieldsChanged? onFieldsChanged,
OnRowsChanged? onRowsChanged,
required OnSettingsChanged? onSettingsChanged,
required OnArrangeWithNewField? onArrangeWithNewField,
required OnError? onError,
}) {
_onDatabaseChanged = onDatabaseChanged;
_onFieldsChanged = onFieldsChanged;
_onRowsChanged = onRowsChanged;
_onSettingsChanged = onSettingsChanged;
_onArrangeWithNewField = onArrangeWithNewField;
_onError = onError;
fieldController.addListener(onFields: (fields) {
_onFieldsChanged?.call(UnmodifiableListView(fields));
});
_listener.start(
onCalendarSettingsChanged: (result) {
result.fold(
(settings) => _onSettingsChanged?.call(settings),
(e) => _onError?.call(e),
);
},
onArrangeWithNewField: (result) {
result.fold(
(settings) => _onArrangeWithNewField?.call(settings),
(e) => _onError?.call(e),
);
},
);
}
Future<Either<Unit, FlowyError>> openDatabase() async {
final result = await _databaseFFIService.openGrid();
return result.fold(
(database) async {
_onDatabaseChanged?.call(database);
return fieldController
.loadFields(fieldIds: database.fields)
.then((result) {
return result.fold(
(l) => Future(() async {
_viewCache.rowCache.initializeRows(database.rows);
return left(l);
}),
(err) => right(err),
);
});
},
(err) => right(err),
);
}
Future<void> dispose() async {
await _viewCache.dispose();
await _databaseFFIService.closeGrid();
await fieldController.dispose();
}
}

View File

@ -0,0 +1,65 @@
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'package:dartz/dartz.dart';
typedef CalendarSettingsValue = Either<CalendarSettingsPB, FlowyError>;
typedef ArrangeWithNewField = Either<FieldPB, FlowyError>;
class CalendarListener {
final String viewId;
PublishNotifier<CalendarSettingsValue>? _calendarSettingsNotifier =
PublishNotifier();
PublishNotifier<ArrangeWithNewField>? _arrangeWithNewFieldNotifier =
PublishNotifier();
DatabaseNotificationListener? _listener;
CalendarListener(this.viewId);
void start({
required void Function(CalendarSettingsValue) onCalendarSettingsChanged,
required void Function(ArrangeWithNewField) onArrangeWithNewField,
}) {
_calendarSettingsNotifier?.addPublishListener(onCalendarSettingsChanged);
_arrangeWithNewFieldNotifier?.addPublishListener(onArrangeWithNewField);
_listener = DatabaseNotificationListener(
objectId: viewId,
handler: _handler,
);
}
void _handler(
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidUpdateCalendarSettings:
result.fold(
(payload) => _calendarSettingsNotifier?.value =
left(CalendarSettingsPB.fromBuffer(payload)),
(error) => _calendarSettingsNotifier?.value = right(error),
);
break;
case DatabaseNotification.DidArrangeCalendarWithNewField:
result.fold(
(payload) => _arrangeWithNewFieldNotifier?.value =
left(FieldPB.fromBuffer(payload)),
(error) => _arrangeWithNewFieldNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_calendarSettingsNotifier?.dispose();
_calendarSettingsNotifier = null;
_arrangeWithNewFieldNotifier?.dispose();
_arrangeWithNewFieldNotifier = null;
}
}

View File

@ -294,6 +294,33 @@ impl DatabaseViewRevisionPad {
})
}
/// Returns the settings for the given layout. If it's not exists then will return the
/// default settings for the given layout.
/// Each [database view](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/database-view) has its own settings.
pub fn get_layout_setting<T>(&self, layout: &LayoutRevision) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
let settings_str = self.view.layout_settings.get(layout)?;
serde_json::from_str::<T>(settings_str).ok()
}
/// updates the settings for the given layout type
pub fn update_layout_setting<T>(
&mut self,
layout: &LayoutRevision,
settings: &T,
) -> SyncResult<Option<GridViewRevisionChangeset>>
where
T: serde::Serialize,
{
let settings_str = serde_json::to_string(settings).map_err(internal_sync_error)?;
self.modify(|view| {
view.layout_settings.insert(layout.clone(), settings_str);
Ok(Some(()))
})
}
pub fn json_str(&self) -> SyncResult<String> {
make_grid_view_rev_json_str(&self.view)
}

View File

@ -0,0 +1,85 @@
use crate::entities::parser::NotEmptyStr;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::convert::TryInto;
#[derive(Debug, Clone, Eq, PartialEq, Default, ProtoBuf)]
pub struct CalendarSettingsPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub layout_ty: CalendarLayout,
#[pb(index = 3)]
pub first_day_of_week: i32,
#[pb(index = 4)]
pub show_weekends: bool,
#[pb(index = 5)]
pub show_week_numbers: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalendarSettingsParams {
pub(crate) view_id: String,
layout_ty: CalendarLayout,
first_day_of_week: i32,
show_weekends: bool,
show_week_numbers: bool,
}
const DEFAULT_FIRST_DAY_OF_WEEK: i32 = 0;
const DEFAULT_SHOW_WEEKENDS: bool = true;
const DEFAULT_SHOW_WEEK_NUMBERS: bool = true;
impl CalendarSettingsParams {
pub fn default_with(view_id: String) -> Self {
CalendarSettingsParams {
view_id: view_id.to_string(),
layout_ty: CalendarLayout::default(),
first_day_of_week: DEFAULT_FIRST_DAY_OF_WEEK,
show_weekends: DEFAULT_SHOW_WEEKENDS,
show_week_numbers: DEFAULT_SHOW_WEEK_NUMBERS,
}
}
}
impl TryInto<CalendarSettingsParams> for CalendarSettingsPB {
type Error = ErrorCode;
fn try_into(self) -> Result<CalendarSettingsParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdInvalid)?;
Ok(CalendarSettingsParams {
view_id: view_id.0,
layout_ty: self.layout_ty,
first_day_of_week: self.first_day_of_week,
show_weekends: self.show_weekends,
show_week_numbers: self.show_week_numbers,
})
}
}
impl std::convert::From<CalendarSettingsParams> for CalendarSettingsPB {
fn from(params: CalendarSettingsParams) -> Self {
CalendarSettingsPB {
view_id: params.view_id,
layout_ty: params.layout_ty,
first_day_of_week: params.first_day_of_week,
show_weekends: params.show_weekends,
show_week_numbers: params.show_week_numbers,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default, ProtoBuf_Enum, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum CalendarLayout {
#[default]
MonthLayout = 0,
WeekLayout = 1,
DayLayout = 2,
}

View File

@ -1,3 +1,4 @@
mod calendar_entities;
mod cell_entities;
mod field_entities;
pub mod filter_entities;
@ -9,6 +10,7 @@ pub mod setting_entities;
mod sort_entities;
mod view_entities;
pub use calendar_entities::*;
pub use cell_entities::*;
pub use field_entities::*;
pub use filter_entities::*;

View File

@ -1,25 +1,24 @@
use crate::entities::parser::NotEmptyStr;
use crate::entities::{
AlterFilterParams, AlterFilterPayloadPB, AlterSortParams, AlterSortPayloadPB, DeleteFilterParams,
DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, DeleteSortParams,
DeleteSortPayloadPB, InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB,
AlterFilterParams, AlterFilterPayloadPB, AlterSortParams, AlterSortPayloadPB, CalendarSettingsPB,
DeleteFilterParams, DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB,
DeleteSortParams, DeleteSortPayloadPB, InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB,
RepeatedGroupConfigurationPB, RepeatedSortPB,
};
use database_model::LayoutRevision;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use std::convert::TryInto;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
/// [DatabaseViewSettingPB] defines the setting options for the grid. Such as the filter, group, and sort.
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct DatabaseViewSettingPB {
#[pb(index = 1)]
pub support_layouts: Vec<ViewLayoutPB>,
pub current_layout: LayoutTypePB,
#[pb(index = 2)]
pub current_layout: LayoutTypePB,
pub layout_setting: LayoutSettingPB,
#[pb(index = 3)]
pub filters: RepeatedFilterPB,
@ -31,23 +30,6 @@ pub struct DatabaseViewSettingPB {
pub sorts: RepeatedSortPB,
}
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct ViewLayoutPB {
#[pb(index = 1)]
ty: LayoutTypePB,
}
impl ViewLayoutPB {
pub fn all() -> Vec<ViewLayoutPB> {
let mut layouts = vec![];
for layout_ty in LayoutTypePB::iter() {
layouts.push(ViewLayoutPB { ty: layout_ty })
}
layouts
}
}
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)]
#[repr(u8)]
pub enum LayoutTypePB {
@ -176,3 +158,15 @@ impl DatabaseSettingChangesetParams {
self.insert_filter.is_some() || self.delete_filter.is_some()
}
}
#[derive(Debug, Eq, PartialEq, Default, ProtoBuf, Clone)]
pub struct LayoutSettingPB {
#[pb(index = 1, one_of)]
pub calendar: Option<CalendarSettingsPB>,
}
impl LayoutSettingPB {
pub fn new() -> Self {
Self::default()
}
}

View File

@ -560,3 +560,23 @@ pub(crate) async fn move_group_row_handler(
editor.move_group_row(params).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn set_calendar_setting_handler(
data: AFPluginData<CalendarSettingsPB>,
manager: AFPluginState<Arc<DatabaseManager>>,
) -> FlowyResult<()> {
let params: CalendarSettingsParams = data.into_inner().try_into()?;
let _ = manager.get_database_editor(params.view_id.as_ref()).await?;
todo!("nathan: depends on the main branch refactoring")
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn get_calendar_setting_handler(
data: AFPluginData<DatabaseViewIdPB>,
manager: AFPluginState<Arc<DatabaseManager>>,
) -> FlowyResult<()> {
let view_id = data.into_inner().value;
let _ = manager.get_database_editor(view_id.as_ref()).await?;
todo!("nathan: depends on the main branch refactoring")
}

View File

@ -47,7 +47,10 @@ pub fn init(database_manager: Arc<DatabaseManager>) -> AFPlugin {
.event(DatabaseEvent::CreateBoardCard, create_board_card_handler)
.event(DatabaseEvent::MoveGroup, move_group_handler)
.event(DatabaseEvent::MoveGroupRow, move_group_row_handler)
.event(DatabaseEvent::GetGroup, get_groups_handler);
.event(DatabaseEvent::GetGroup, get_groups_handler)
// Calendar
.event(DatabaseEvent::SetCalenderSetting, set_calendar_setting_handler)
.event(DatabaseEvent::GetCalendarSetting, get_calendar_setting_handler);
plugin
}
@ -229,4 +232,10 @@ pub enum DatabaseEvent {
#[event(input = "MoveGroupRowPayloadPB")]
GroupByField = 113,
#[event(input = "CalendarSettingsPB")]
SetCalenderSetting = 114,
#[event()]
GetCalendarSetting = 115,
}

View File

@ -31,6 +31,8 @@ pub enum DatabaseNotification {
DidReorderSingleRow = 66,
/// Trigger when the settings of the database are changed
DidUpdateSettings = 70,
DidUpdateCalendarSettings = 80,
DidArrangeCalendarWithNewField = 81,
}
impl std::default::Default for DatabaseNotification {

View File

@ -635,6 +635,26 @@ impl DatabaseViewRevisionEditor {
Ok(())
}
/// Returns the current calendar settings
pub async fn get_calendar_settings(&self) -> FlowyResult<CalendarSettingsParams> {
let settings = self
.pad
.read()
.await
.get_layout_setting(&LayoutRevision::Calendar)
.unwrap_or_else(|| CalendarSettingsParams::default_with(self.view_id.to_string()));
Ok(settings)
}
/// Update the calendar settings and send the notification to refresh the UI
pub async fn update_calendar_settings(&self, params: CalendarSettingsParams) -> FlowyResult<()> {
// Maybe it needs no send notification to refresh the UI
self
.modify(|pad| Ok(pad.update_layout_setting(&LayoutRevision::Calendar, &params)?))
.await?;
Ok(())
}
#[tracing::instrument(level = "trace", skip_all, err)]
pub async fn did_update_view_field_type_option(
&self,
@ -878,7 +898,7 @@ async fn new_group_controller(
.await
}
/// Returns a [GroupController]
/// Returns a [GroupController]
///
async fn new_group_controller_with_field_rev(
user_id: String,

View File

@ -1,4 +1,4 @@
use crate::entities::{DatabaseViewSettingPB, LayoutTypePB, ViewLayoutPB};
use crate::entities::{CalendarSettingsParams, DatabaseViewSettingPB, LayoutSettingPB};
use crate::services::database_view::{get_cells_for_field, DatabaseViewEditorDelegate};
use crate::services::field::RowSingleCellData;
use crate::services::filter::{FilterController, FilterDelegate, FilterType};
@ -7,8 +7,8 @@ use crate::services::row::DatabaseBlockRowRevision;
use crate::services::sort::{SortDelegate, SortType};
use bytes::Bytes;
use database_model::{
FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, RowRevision,
SortRevision,
FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, LayoutRevision,
RowRevision, SortRevision,
};
use flowy_client_sync::client_database::{DatabaseViewRevisionPad, GridViewRevisionChangeset};
use flowy_client_sync::make_operations_from_revisions;
@ -146,13 +146,24 @@ pub fn make_grid_setting(
view_pad: &DatabaseViewRevisionPad,
field_revs: &[Arc<FieldRevision>],
) -> DatabaseViewSettingPB {
let layout_type: LayoutTypePB = view_pad.layout.clone().into();
let layout_type: LayoutRevision = view_pad.layout.clone();
let mut layout_settings = LayoutSettingPB::new();
match layout_type {
LayoutRevision::Grid => {},
LayoutRevision::Board => {},
LayoutRevision::Calendar => {
layout_settings.calendar = view_pad
.get_layout_setting::<CalendarSettingsParams>(&layout_type)
.map(|params| params.into());
},
}
let filters = view_pad.get_all_filters(field_revs);
let group_configurations = view_pad.get_groups_by_field_revs(field_revs);
let sorts = view_pad.get_all_sorts(field_revs);
DatabaseViewSettingPB {
support_layouts: ViewLayoutPB::all(),
current_layout: layout_type,
current_layout: layout_type.into(),
layout_setting: layout_settings,
filters: filters.into(),
sorts: sorts.into(),
group_configurations: group_configurations.into(),

View File

@ -1,4 +1,5 @@
use crate::{FilterConfiguration, GroupConfiguration, SortConfiguration};
use indexmap::IndexMap;
use nanoid::nanoid;
use serde::{Deserialize, Serialize};
use serde_repr::*;
@ -38,6 +39,9 @@ pub struct DatabaseViewRevision {
pub layout: LayoutRevision,
#[serde(default)]
pub layout_settings: LayoutSettings,
#[serde(default)]
pub filters: FilterConfiguration,
@ -54,6 +58,7 @@ impl DatabaseViewRevision {
view_id,
database_id,
layout,
layout_settings: Default::default(),
filters: Default::default(),
groups: Default::default(),
sorts: Default::default(),
@ -65,6 +70,27 @@ impl DatabaseViewRevision {
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct LayoutSettings {
#[serde(with = "indexmap::serde_seq")]
inner: IndexMap<LayoutRevision, String>,
}
impl std::ops::Deref for LayoutSettings {
type Target = IndexMap<LayoutRevision, String>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for LayoutSettings {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RowOrderRevision {
pub row_id: String,
@ -80,6 +106,7 @@ mod tests {
view_id: "1".to_string(),
database_id: "1".to_string(),
layout: Default::default(),
layout_settings: Default::default(),
filters: Default::default(),
groups: Default::default(),
sorts: Default::default(),
@ -87,7 +114,7 @@ mod tests {
let s = serde_json::to_string(&grid_view_revision).unwrap();
assert_eq!(
s,
r#"{"view_id":"1","grid_id":"1","layout":0,"filters":[],"groups":[],"sorts":[]}"#
r#"{"view_id":"1","grid_id":"1","layout":0,"layout_settings":[],"filters":[],"groups":[],"sorts":[]}"#
);
}
}