mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
2ef72f3203
commit
33e0f8d26d
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 6C3 3.79086 4.79086 2 7 2H17C19.2091 2 21 3.79086 21 6C21 8.20914 19.2091 10 17 10H7C4.79086 10 3 8.20914 3 6Z" stroke="#000000" stroke-width="2"/>
|
||||||
|
<path d="M3 16C3 14.8954 3.89543 14 5 14H8C9.10457 14 10 14.8954 10 16V19C10 20.1046 9.10457 21 8 21H5C3.89543 21 3 20.1046 3 19V16Z" stroke="#000000" stroke-width="2"/>
|
||||||
|
<path d="M14 17.5C14 15.567 15.567 14 17.5 14C19.433 14 21 15.567 21 17.5C21 19.433 19.433 21 17.5 21C15.567 21 14 19.433 14 17.5Z" stroke="#000000" stroke-width="2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 725 B |
@ -232,7 +232,8 @@
|
|||||||
"deleteFilter": "Delete filter",
|
"deleteFilter": "Delete filter",
|
||||||
"filterBy": "Filter by...",
|
"filterBy": "Filter by...",
|
||||||
"typeAValue": "Type a value...",
|
"typeAValue": "Type a value...",
|
||||||
"layout": "Layout"
|
"layout": "Layout",
|
||||||
|
"databaseLayout": "Layout"
|
||||||
},
|
},
|
||||||
"textFilter": {
|
"textFilter": {
|
||||||
"contains": "Contains",
|
"contains": "Contains",
|
||||||
@ -439,7 +440,8 @@
|
|||||||
"firstDayOfWeek": "Start week on",
|
"firstDayOfWeek": "Start week on",
|
||||||
"layoutDateField": "Layout calendar by",
|
"layoutDateField": "Layout calendar by",
|
||||||
"noDateTitle": "No Date",
|
"noDateTitle": "No Date",
|
||||||
"noDateHint": "Unscheduled events will show up here"
|
"noDateHint": "Unscheduled events will show up here",
|
||||||
|
"clickToAdd": "Click to add to the calendar"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,16 +28,17 @@ class BlankPluginConfig implements PluginConfig {
|
|||||||
|
|
||||||
class BlankPagePlugin extends Plugin {
|
class BlankPagePlugin extends Plugin {
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display => BlankPagePluginDisplay();
|
PluginWidgetBuilder get widgetBuilder => BlankPagePluginWidgetBuilder();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => "BlankStack";
|
PluginId get id => "BlankStack";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => PluginType.blank;
|
PluginType get pluginType => PluginType.blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
|
class BlankPagePluginWidgetBuilder extends PluginWidgetBuilder
|
||||||
|
with NavigationItem {
|
||||||
@override
|
@override
|
||||||
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr());
|
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr());
|
||||||
|
|
||||||
|
@ -39,18 +39,18 @@ class GroupCallbacks {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class LayoutCallbacks {
|
class DatabaseLayoutSettingCallbacks {
|
||||||
final void Function(LayoutSettingPB) onLayoutChanged;
|
final void Function(DatabaseLayoutSettingPB) onLayoutChanged;
|
||||||
final void Function(LayoutSettingPB) onLoadLayout;
|
final void Function(DatabaseLayoutSettingPB) onLoadLayout;
|
||||||
|
|
||||||
LayoutCallbacks({
|
DatabaseLayoutSettingCallbacks({
|
||||||
required this.onLayoutChanged,
|
required this.onLayoutChanged,
|
||||||
required this.onLoadLayout,
|
required this.onLoadLayout,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class CalendarLayoutCallbacks {
|
class CalendarLayoutCallbacks {
|
||||||
final void Function(LayoutSettingPB) onCalendarLayoutChanged;
|
final void Function(DatabaseLayoutSettingPB) onCalendarLayoutChanged;
|
||||||
|
|
||||||
CalendarLayoutCallbacks({required this.onCalendarLayoutChanged});
|
CalendarLayoutCallbacks({required this.onCalendarLayoutChanged});
|
||||||
}
|
}
|
||||||
@ -59,14 +59,14 @@ class DatabaseCallbacks {
|
|||||||
OnDatabaseChanged? onDatabaseChanged;
|
OnDatabaseChanged? onDatabaseChanged;
|
||||||
OnFieldsChanged? onFieldsChanged;
|
OnFieldsChanged? onFieldsChanged;
|
||||||
OnFiltersChanged? onFiltersChanged;
|
OnFiltersChanged? onFiltersChanged;
|
||||||
OnRowsChanged? onRowsChanged;
|
OnNumOfRowsChanged? onNumOfRowsChanged;
|
||||||
OnRowsDeleted? onRowsDeleted;
|
OnRowsDeleted? onRowsDeleted;
|
||||||
OnRowsUpdated? onRowsUpdated;
|
OnRowsUpdated? onRowsUpdated;
|
||||||
OnRowsCreated? onRowsCreated;
|
OnRowsCreated? onRowsCreated;
|
||||||
|
|
||||||
DatabaseCallbacks({
|
DatabaseCallbacks({
|
||||||
this.onDatabaseChanged,
|
this.onDatabaseChanged,
|
||||||
this.onRowsChanged,
|
this.onNumOfRowsChanged,
|
||||||
this.onFieldsChanged,
|
this.onFieldsChanged,
|
||||||
this.onFiltersChanged,
|
this.onFiltersChanged,
|
||||||
this.onRowsUpdated,
|
this.onRowsUpdated,
|
||||||
@ -79,13 +79,13 @@ class DatabaseController {
|
|||||||
final String viewId;
|
final String viewId;
|
||||||
final DatabaseViewBackendService _databaseViewBackendSvc;
|
final DatabaseViewBackendService _databaseViewBackendSvc;
|
||||||
final FieldController fieldController;
|
final FieldController fieldController;
|
||||||
|
DatabaseLayoutPB? databaseLayout;
|
||||||
late DatabaseViewCache _viewCache;
|
late DatabaseViewCache _viewCache;
|
||||||
final DatabaseLayoutPB layoutType;
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
DatabaseCallbacks? _databaseCallbacks;
|
DatabaseCallbacks? _databaseCallbacks;
|
||||||
GroupCallbacks? _groupCallbacks;
|
GroupCallbacks? _groupCallbacks;
|
||||||
LayoutCallbacks? _layoutCallbacks;
|
DatabaseLayoutSettingCallbacks? _layoutCallbacks;
|
||||||
CalendarLayoutCallbacks? _calendarLayoutCallbacks;
|
CalendarLayoutCallbacks? _calendarLayoutCallbacks;
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
@ -93,15 +93,15 @@ class DatabaseController {
|
|||||||
|
|
||||||
// Listener
|
// Listener
|
||||||
final DatabaseGroupListener groupListener;
|
final DatabaseGroupListener groupListener;
|
||||||
final DatabaseLayoutListener layoutListener;
|
final DatabaseLayoutSettingListener layoutListener;
|
||||||
final DatabaseCalendarLayoutListener calendarLayoutListener;
|
final DatabaseCalendarLayoutListener calendarLayoutListener;
|
||||||
|
|
||||||
DatabaseController({required ViewPB view, required this.layoutType})
|
DatabaseController({required ViewPB view})
|
||||||
: viewId = view.id,
|
: viewId = view.id,
|
||||||
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
|
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
|
||||||
fieldController = FieldController(viewId: view.id),
|
fieldController = FieldController(viewId: view.id),
|
||||||
groupListener = DatabaseGroupListener(view.id),
|
groupListener = DatabaseGroupListener(view.id),
|
||||||
layoutListener = DatabaseLayoutListener(view.id),
|
layoutListener = DatabaseLayoutSettingListener(view.id),
|
||||||
calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
|
calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
|
||||||
_viewCache = DatabaseViewCache(
|
_viewCache = DatabaseViewCache(
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
@ -111,14 +111,11 @@ class DatabaseController {
|
|||||||
_listenOnFieldsChanged();
|
_listenOnFieldsChanged();
|
||||||
_listenOnGroupChanged();
|
_listenOnGroupChanged();
|
||||||
_listenOnLayoutChanged();
|
_listenOnLayoutChanged();
|
||||||
if (layoutType == DatabaseLayoutPB.Calendar) {
|
|
||||||
_listenOnCalendarLayoutChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setListener({
|
void setListener({
|
||||||
DatabaseCallbacks? onDatabaseChanged,
|
DatabaseCallbacks? onDatabaseChanged,
|
||||||
LayoutCallbacks? onLayoutChanged,
|
DatabaseLayoutSettingCallbacks? onLayoutChanged,
|
||||||
GroupCallbacks? onGroupChanged,
|
GroupCallbacks? onGroupChanged,
|
||||||
CalendarLayoutCallbacks? onCalendarLayoutChanged,
|
CalendarLayoutCallbacks? onCalendarLayoutChanged,
|
||||||
}) {
|
}) {
|
||||||
@ -132,6 +129,12 @@ class DatabaseController {
|
|||||||
return _databaseViewBackendSvc.openGrid().then((result) {
|
return _databaseViewBackendSvc.openGrid().then((result) {
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(database) async {
|
(database) async {
|
||||||
|
databaseLayout = database.layoutType;
|
||||||
|
|
||||||
|
if (databaseLayout == DatabaseLayoutPB.Calendar) {
|
||||||
|
_listenOnCalendarLayoutChanged();
|
||||||
|
}
|
||||||
|
|
||||||
_databaseCallbacks?.onDatabaseChanged?.call(database);
|
_databaseCallbacks?.onDatabaseChanged?.call(database);
|
||||||
_viewCache.rowCache.setInitialRows(database.rows);
|
_viewCache.rowCache.setInitialRows(database.rows);
|
||||||
return await fieldController
|
return await fieldController
|
||||||
@ -242,20 +245,20 @@ class DatabaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadLayoutSetting() async {
|
Future<void> _loadLayoutSetting() async {
|
||||||
_databaseViewBackendSvc.getLayoutSetting(layoutType).then((result) {
|
if (databaseLayout != null) {
|
||||||
result.fold(
|
_databaseViewBackendSvc.getLayoutSetting(databaseLayout!).then((result) {
|
||||||
(l) {
|
result.fold(
|
||||||
_layoutCallbacks?.onLoadLayout(l);
|
(l) => _layoutCallbacks?.onLoadLayout(l),
|
||||||
},
|
(r) => Log.error(r),
|
||||||
(r) => Log.error(r),
|
);
|
||||||
);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenOnRowsChanged() {
|
void _listenOnRowsChanged() {
|
||||||
final callbacks = DatabaseViewCallbacks(
|
final callbacks = DatabaseViewCallbacks(
|
||||||
onRowsChanged: (rows, rowByRowId, reason) {
|
onNumOfRowsChanged: (rows, rowByRowId, reason) {
|
||||||
_databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason);
|
_databaseCallbacks?.onNumOfRowsChanged?.call(rows, rowByRowId, reason);
|
||||||
},
|
},
|
||||||
onRowsDeleted: (ids) {
|
onRowsDeleted: (ids) {
|
||||||
_databaseCallbacks?.onRowsDeleted?.call(ids);
|
_databaseCallbacks?.onRowsDeleted?.call(ids);
|
||||||
|
@ -97,10 +97,10 @@ class DatabaseViewBackendService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
|
Future<Either<DatabaseLayoutSettingPB, FlowyError>> getLayoutSetting(
|
||||||
DatabaseLayoutPB layoutType,
|
DatabaseLayoutPB layoutType,
|
||||||
) {
|
) {
|
||||||
final payload = DatabaseLayoutIdPB.create()
|
final payload = DatabaseLayoutMetaPB.create()
|
||||||
..viewId = viewId
|
..viewId = viewId
|
||||||
..layout = layoutType;
|
..layout = layoutType;
|
||||||
return DatabaseEventGetLayoutSetting(payload).send();
|
return DatabaseEventGetLayoutSetting(payload).send();
|
||||||
|
@ -15,7 +15,7 @@ typedef OnDatabaseChanged = void Function(DatabasePB);
|
|||||||
typedef OnRowsCreated = void Function(List<RowId> ids);
|
typedef OnRowsCreated = void Function(List<RowId> ids);
|
||||||
typedef OnRowsUpdated = void Function(List<RowId> ids);
|
typedef OnRowsUpdated = void Function(List<RowId> ids);
|
||||||
typedef OnRowsDeleted = void Function(List<RowId> ids);
|
typedef OnRowsDeleted = void Function(List<RowId> ids);
|
||||||
typedef OnRowsChanged = void Function(
|
typedef OnNumOfRowsChanged = void Function(
|
||||||
UnmodifiableListView<RowInfo> rows,
|
UnmodifiableListView<RowInfo> rows,
|
||||||
UnmodifiableMapView<RowId, RowInfo> rowByRowId,
|
UnmodifiableMapView<RowId, RowInfo> rowByRowId,
|
||||||
RowsChangedReason reason,
|
RowsChangedReason reason,
|
||||||
|
@ -6,7 +6,7 @@ import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
|
|
||||||
typedef NewLayoutFieldValue = Either<LayoutSettingPB, FlowyError>;
|
typedef NewLayoutFieldValue = Either<DatabaseLayoutSettingPB, FlowyError>;
|
||||||
|
|
||||||
class DatabaseCalendarLayoutListener {
|
class DatabaseCalendarLayoutListener {
|
||||||
final String viewId;
|
final String viewId;
|
||||||
@ -33,7 +33,7 @@ class DatabaseCalendarLayoutListener {
|
|||||||
case DatabaseNotification.DidSetNewLayoutField:
|
case DatabaseNotification.DidSetNewLayoutField:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _newLayoutFieldNotifier?.value =
|
(payload) => _newLayoutFieldNotifier?.value =
|
||||||
left(LayoutSettingPB.fromBuffer(payload)),
|
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
|
||||||
(error) => _newLayoutFieldNotifier?.value = right(error),
|
(error) => _newLayoutFieldNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -8,15 +8,15 @@ import 'package:dartz/dartz.dart';
|
|||||||
|
|
||||||
typedef LayoutSettingsValue<T> = Either<T, FlowyError>;
|
typedef LayoutSettingsValue<T> = Either<T, FlowyError>;
|
||||||
|
|
||||||
class DatabaseLayoutListener {
|
class DatabaseLayoutSettingListener {
|
||||||
final String viewId;
|
final String viewId;
|
||||||
PublishNotifier<LayoutSettingsValue<LayoutSettingPB>>? _settingNotifier =
|
PublishNotifier<LayoutSettingsValue<DatabaseLayoutSettingPB>>?
|
||||||
PublishNotifier();
|
_settingNotifier = PublishNotifier();
|
||||||
DatabaseNotificationListener? _listener;
|
DatabaseNotificationListener? _listener;
|
||||||
DatabaseLayoutListener(this.viewId);
|
DatabaseLayoutSettingListener(this.viewId);
|
||||||
|
|
||||||
void start({
|
void start({
|
||||||
required void Function(LayoutSettingsValue<LayoutSettingPB>)
|
required void Function(LayoutSettingsValue<DatabaseLayoutSettingPB>)
|
||||||
onLayoutChanged,
|
onLayoutChanged,
|
||||||
}) {
|
}) {
|
||||||
_settingNotifier?.addPublishListener(onLayoutChanged);
|
_settingNotifier?.addPublishListener(onLayoutChanged);
|
||||||
@ -34,7 +34,7 @@ class DatabaseLayoutListener {
|
|||||||
case DatabaseNotification.DidUpdateLayoutSettings:
|
case DatabaseNotification.DidUpdateLayoutSettings:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _settingNotifier?.value =
|
(payload) => _settingNotifier?.value =
|
||||||
left(LayoutSettingPB.fromBuffer(payload)),
|
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
|
||||||
(error) => _settingNotifier?.value = right(error),
|
(error) => _settingNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -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:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
@ -40,7 +42,31 @@ class DatabaseSettingState with _$DatabaseSettingState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum DatabaseSettingAction {
|
enum DatabaseSettingAction {
|
||||||
showFilters,
|
|
||||||
sortBy,
|
|
||||||
showProperties,
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,21 @@ import 'view_listener.dart';
|
|||||||
|
|
||||||
class DatabaseViewCallbacks {
|
class DatabaseViewCallbacks {
|
||||||
/// Will get called when number of rows were changed that includes
|
/// 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
|
/// the rows of the current database
|
||||||
final OnRowsChanged? onRowsChanged;
|
final OnNumOfRowsChanged? onNumOfRowsChanged;
|
||||||
|
|
||||||
// Will get called when creating new rows
|
// Will get called when creating new rows
|
||||||
final OnRowsCreated? onRowsCreated;
|
final OnRowsCreated? onRowsCreated;
|
||||||
|
|
||||||
/// Will get called when number of rows were updated
|
/// Will get called when rows were updated
|
||||||
final OnRowsUpdated? onRowsUpdated;
|
final OnRowsUpdated? onRowsUpdated;
|
||||||
|
|
||||||
/// Will get called when number of rows were deleted
|
/// Will get called when number of rows were deleted
|
||||||
final OnRowsDeleted? onRowsDeleted;
|
final OnRowsDeleted? onRowsDeleted;
|
||||||
|
|
||||||
const DatabaseViewCallbacks({
|
const DatabaseViewCallbacks({
|
||||||
this.onRowsChanged,
|
this.onNumOfRowsChanged,
|
||||||
this.onRowsCreated,
|
this.onRowsCreated,
|
||||||
this.onRowsUpdated,
|
this.onRowsUpdated,
|
||||||
this.onRowsDeleted,
|
this.onRowsDeleted,
|
||||||
@ -101,7 +101,7 @@ class DatabaseViewCache {
|
|||||||
);
|
);
|
||||||
|
|
||||||
_rowCache.onRowsChanged(
|
_rowCache.onRowsChanged(
|
||||||
(reason) => _callbacks?.onRowsChanged?.call(
|
(reason) => _callbacks?.onNumOfRowsChanged?.call(
|
||||||
rowInfos,
|
rowInfos,
|
||||||
_rowCache.rowByRowId,
|
_rowCache.rowByRowId,
|
||||||
reason,
|
reason,
|
||||||
|
@ -20,18 +20,17 @@ import 'group_controller.dart';
|
|||||||
part 'board_bloc.freezed.dart';
|
part 'board_bloc.freezed.dart';
|
||||||
|
|
||||||
class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||||
final DatabaseController _databaseController;
|
final DatabaseController databaseController;
|
||||||
late final AppFlowyBoardController boardController;
|
late final AppFlowyBoardController boardController;
|
||||||
final LinkedHashMap<String, GroupController> groupControllers =
|
final LinkedHashMap<String, GroupController> groupControllers =
|
||||||
LinkedHashMap();
|
LinkedHashMap();
|
||||||
|
|
||||||
FieldController get fieldController => _databaseController.fieldController;
|
FieldController get fieldController => databaseController.fieldController;
|
||||||
String get viewId => _databaseController.viewId;
|
String get viewId => databaseController.viewId;
|
||||||
|
|
||||||
BoardBloc({required ViewPB view})
|
BoardBloc({required ViewPB view})
|
||||||
: _databaseController = DatabaseController(
|
: databaseController = DatabaseController(
|
||||||
view: view,
|
view: view,
|
||||||
layoutType: DatabaseLayoutPB.Board,
|
|
||||||
),
|
),
|
||||||
super(BoardState.initial(view.id)) {
|
super(BoardState.initial(view.id)) {
|
||||||
boardController = AppFlowyBoardController(
|
boardController = AppFlowyBoardController(
|
||||||
@ -41,7 +40,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
toGroupId,
|
toGroupId,
|
||||||
toIndex,
|
toIndex,
|
||||||
) {
|
) {
|
||||||
_databaseController.moveGroup(
|
databaseController.moveGroup(
|
||||||
fromGroupId: fromGroupId,
|
fromGroupId: fromGroupId,
|
||||||
toGroupId: toGroupId,
|
toGroupId: toGroupId,
|
||||||
);
|
);
|
||||||
@ -54,7 +53,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
|
final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
|
||||||
final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
|
final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
|
||||||
if (fromRow != null) {
|
if (fromRow != null) {
|
||||||
_databaseController.moveGroupRow(
|
databaseController.moveGroupRow(
|
||||||
fromRow: fromRow,
|
fromRow: fromRow,
|
||||||
toRow: toRow,
|
toRow: toRow,
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
@ -70,7 +69,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
|
final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
|
||||||
final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
|
final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
|
||||||
if (fromRow != null) {
|
if (fromRow != null) {
|
||||||
_databaseController.moveGroupRow(
|
databaseController.moveGroupRow(
|
||||||
fromRow: fromRow,
|
fromRow: fromRow,
|
||||||
toRow: toRow,
|
toRow: toRow,
|
||||||
groupId: toGroupId,
|
groupId: toGroupId,
|
||||||
@ -88,7 +87,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
},
|
},
|
||||||
createBottomRow: (groupId) async {
|
createBottomRow: (groupId) async {
|
||||||
final startRowId = groupControllers[groupId]?.lastRow()?.id;
|
final startRowId = groupControllers[groupId]?.lastRow()?.id;
|
||||||
final result = await _databaseController.createRow(
|
final result = await databaseController.createRow(
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
startRowId: startRowId,
|
startRowId: startRowId,
|
||||||
);
|
);
|
||||||
@ -98,8 +97,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
createHeaderRow: (String groupId) async {
|
createHeaderRow: (String groupId) async {
|
||||||
final result =
|
final result = await databaseController.createRow(groupId: groupId);
|
||||||
await _databaseController.createRow(groupId: groupId);
|
|
||||||
result.fold(
|
result.fold(
|
||||||
(_) {},
|
(_) {},
|
||||||
(err) => Log.error(err),
|
(err) => Log.error(err),
|
||||||
@ -170,7 +168,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await _databaseController.dispose();
|
await databaseController.dispose();
|
||||||
for (final controller in groupControllers.values) {
|
for (final controller in groupControllers.values) {
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
}
|
}
|
||||||
@ -198,7 +196,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RowCache? getRowCache() {
|
RowCache? getRowCache() {
|
||||||
return _databaseController.rowCache;
|
return databaseController.rowCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
@ -237,7 +235,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
_databaseController.setListener(
|
databaseController.setListener(
|
||||||
onDatabaseChanged: onDatabaseChanged,
|
onDatabaseChanged: onDatabaseChanged,
|
||||||
onGroupChanged: onGroupChanged,
|
onGroupChanged: onGroupChanged,
|
||||||
);
|
);
|
||||||
@ -256,7 +254,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openGrid(Emitter<BoardState> emit) async {
|
Future<void> _openGrid(Emitter<BoardState> emit) async {
|
||||||
final result = await _databaseController.open();
|
final result = await databaseController.open();
|
||||||
result.fold(
|
result.fold(
|
||||||
(grid) => emit(
|
(grid) => emit(
|
||||||
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
|
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
|
||||||
|
@ -49,18 +49,19 @@ class BoardPlugin extends Plugin {
|
|||||||
notifier = ViewPluginNotifier(view: view);
|
notifier = ViewPluginNotifier(view: view);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display => GridPluginDisplay(notifier: notifier);
|
PluginWidgetBuilder get widgetBuilder =>
|
||||||
|
BoardPluginWidgetBuilder(notifier: notifier);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => notifier.view.id;
|
PluginId get id => notifier.view.id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => _pluginType;
|
PluginType get pluginType => _pluginType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridPluginDisplay extends PluginDisplay {
|
class BoardPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||||
final ViewPluginNotifier notifier;
|
final ViewPluginNotifier notifier;
|
||||||
GridPluginDisplay({required this.notifier, Key? key});
|
BoardPluginWidgetBuilder({required this.notifier, Key? key});
|
||||||
|
|
||||||
ViewPB get view => notifier.view;
|
ViewPB get view => notifier.view;
|
||||||
|
|
||||||
|
@ -340,15 +340,7 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<BoardBloc, BoardState>(
|
return BlocBuilder<BoardBloc, BoardState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) => const BoardToolbar(),
|
||||||
final bloc = context.read<BoardBloc>();
|
|
||||||
final toolbarContext = BoardToolbarContext(
|
|
||||||
viewId: bloc.viewId,
|
|
||||||
fieldController: bloc.fieldController,
|
|
||||||
);
|
|
||||||
|
|
||||||
return BoardToolbar(toolbarContext: toolbarContext);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +1,10 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'board_setting.dart';
|
|
||||||
|
|
||||||
class BoardToolbarContext {
|
|
||||||
final String viewId;
|
|
||||||
final FieldController fieldController;
|
|
||||||
|
|
||||||
BoardToolbarContext({
|
|
||||||
required this.viewId,
|
|
||||||
required this.fieldController,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class BoardToolbar extends StatelessWidget {
|
class BoardToolbar extends StatelessWidget {
|
||||||
final BoardToolbarContext toolbarContext;
|
|
||||||
const BoardToolbar({
|
const BoardToolbar({
|
||||||
required this.toolbarContext,
|
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -33,58 +15,11 @@ class BoardToolbar extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
_SettingButton(
|
SettingButton(
|
||||||
settingContext: BoardSettingContext.from(toolbarContext),
|
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,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -18,20 +18,17 @@ import '../../application/row/row_cache.dart';
|
|||||||
part 'calendar_bloc.freezed.dart';
|
part 'calendar_bloc.freezed.dart';
|
||||||
|
|
||||||
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||||
final DatabaseController _databaseController;
|
final DatabaseController databaseController;
|
||||||
Map<String, FieldInfo> fieldInfoByFieldId = {};
|
Map<String, FieldInfo> fieldInfoByFieldId = {};
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
String get viewId => _databaseController.viewId;
|
String get viewId => databaseController.viewId;
|
||||||
FieldController get fieldController => _databaseController.fieldController;
|
FieldController get fieldController => databaseController.fieldController;
|
||||||
CellCache get cellCache => _databaseController.rowCache.cellCache;
|
CellCache get cellCache => databaseController.rowCache.cellCache;
|
||||||
RowCache get rowCache => _databaseController.rowCache;
|
RowCache get rowCache => databaseController.rowCache;
|
||||||
|
|
||||||
CalendarBloc({required ViewPB view})
|
CalendarBloc({required ViewPB view})
|
||||||
: _databaseController = DatabaseController(
|
: databaseController = DatabaseController(view: view),
|
||||||
view: view,
|
|
||||||
layoutType: DatabaseLayoutPB.Calendar,
|
|
||||||
),
|
|
||||||
super(CalendarState.initial()) {
|
super(CalendarState.initial()) {
|
||||||
on<CalendarEvent>(
|
on<CalendarEvent>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
@ -110,12 +107,12 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await _databaseController.dispose();
|
await databaseController.dispose();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldInfo? _getCalendarFieldInfo(String fieldId) {
|
FieldInfo? _getCalendarFieldInfo(String fieldId) {
|
||||||
final fieldInfos = _databaseController.fieldController.fieldInfos;
|
final fieldInfos = databaseController.fieldController.fieldInfos;
|
||||||
final index = fieldInfos.indexWhere(
|
final index = fieldInfos.indexWhere(
|
||||||
(element) => element.field.id == fieldId,
|
(element) => element.field.id == fieldId,
|
||||||
);
|
);
|
||||||
@ -127,7 +124,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FieldInfo? _getTitleFieldInfo() {
|
FieldInfo? _getTitleFieldInfo() {
|
||||||
final fieldInfos = _databaseController.fieldController.fieldInfos;
|
final fieldInfos = databaseController.fieldController.fieldInfos;
|
||||||
final index = fieldInfos.indexWhere(
|
final index = fieldInfos.indexWhere(
|
||||||
(element) => element.field.isPrimary,
|
(element) => element.field.isPrimary,
|
||||||
);
|
);
|
||||||
@ -139,7 +136,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
|
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
|
||||||
final result = await _databaseController.open();
|
final result = await databaseController.open();
|
||||||
result.fold(
|
result.fold(
|
||||||
(database) => emit(
|
(database) => emit(
|
||||||
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
|
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
|
||||||
@ -157,7 +154,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
final dateField = _getCalendarFieldInfo(settings.fieldId);
|
final dateField = _getCalendarFieldInfo(settings.fieldId);
|
||||||
final titleField = _getTitleFieldInfo();
|
final titleField = _getTitleFieldInfo();
|
||||||
if (dateField != null && titleField != null) {
|
if (dateField != null && titleField != null) {
|
||||||
final newRow = await _databaseController.createRow(
|
final newRow = await databaseController.createRow(
|
||||||
withCells: (builder) {
|
withCells: (builder) {
|
||||||
builder.insertDate(dateField, date);
|
builder.insertDate(dateField, date);
|
||||||
builder.insertText(titleField, title);
|
builder.insertText(titleField, title);
|
||||||
@ -210,7 +207,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
Future<void> _updateCalendarLayoutSetting(
|
Future<void> _updateCalendarLayoutSetting(
|
||||||
CalendarLayoutSettingPB layoutSetting,
|
CalendarLayoutSettingPB layoutSetting,
|
||||||
) async {
|
) async {
|
||||||
return _databaseController.updateCalenderLayoutSetting(layoutSetting);
|
return databaseController.updateCalenderLayoutSetting(layoutSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CalendarEventData<CalendarDayEvent>?> _loadEvent(RowId rowId) async {
|
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,
|
onLayoutChanged: _didReceiveLayoutSetting,
|
||||||
onLoadLayout: _didReceiveLayoutSetting,
|
onLoadLayout: _didReceiveLayoutSetting,
|
||||||
);
|
);
|
||||||
@ -340,14 +337,14 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
onCalendarLayoutChanged: _didReceiveNewLayoutField,
|
onCalendarLayoutChanged: _didReceiveNewLayoutField,
|
||||||
);
|
);
|
||||||
|
|
||||||
_databaseController.setListener(
|
databaseController.setListener(
|
||||||
onDatabaseChanged: onDatabaseChanged,
|
onDatabaseChanged: onDatabaseChanged,
|
||||||
onLayoutChanged: onLayoutChanged,
|
onLayoutChanged: onLayoutChanged,
|
||||||
onCalendarLayoutChanged: onCalendarLayoutFieldChanged,
|
onCalendarLayoutChanged: onCalendarLayoutFieldChanged,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _didReceiveLayoutSetting(LayoutSettingPB layoutSetting) {
|
void _didReceiveLayoutSetting(DatabaseLayoutSettingPB layoutSetting) {
|
||||||
if (layoutSetting.hasCalendar()) {
|
if (layoutSetting.hasCalendar()) {
|
||||||
if (isClosed) {
|
if (isClosed) {
|
||||||
return;
|
return;
|
||||||
@ -356,7 +353,7 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _didReceiveNewLayoutField(LayoutSettingPB layoutSetting) {
|
void _didReceiveNewLayoutField(DatabaseLayoutSettingPB layoutSetting) {
|
||||||
if (layoutSetting.hasCalendar()) {
|
if (layoutSetting.hasCalendar()) {
|
||||||
if (isClosed) return;
|
if (isClosed) return;
|
||||||
add(CalendarEvent.didReceiveNewLayoutField(layoutSetting.calendar));
|
add(CalendarEvent.didReceiveNewLayoutField(layoutSetting.calendar));
|
||||||
|
@ -49,18 +49,19 @@ class CalendarPlugin extends Plugin {
|
|||||||
notifier = ViewPluginNotifier(view: view);
|
notifier = ViewPluginNotifier(view: view);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display => CalendarPluginDisplay(notifier: notifier);
|
PluginWidgetBuilder get widgetBuilder =>
|
||||||
|
CalendarPluginWidgetBuilder(notifier: notifier);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => notifier.view.id;
|
PluginId get id => notifier.view.id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => _pluginType;
|
PluginType get pluginType => _pluginType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CalendarPluginDisplay extends PluginDisplay {
|
class CalendarPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||||
final ViewPluginNotifier notifier;
|
final ViewPluginNotifier notifier;
|
||||||
CalendarPluginDisplay({required this.notifier, Key? key});
|
CalendarPluginWidgetBuilder({required this.notifier, Key? key});
|
||||||
|
|
||||||
ViewPB get view => notifier.view;
|
ViewPB get view => notifier.view;
|
||||||
|
|
||||||
|
@ -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/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/calendar/application/calendar_setting_bloc.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/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:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
@ -41,7 +41,7 @@ class CalendarSetting extends StatelessWidget {
|
|||||||
state.selectedAction.foldLeft(null, (previous, action) => action);
|
state.selectedAction.foldLeft(null, (previous, action) => action);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case CalendarSettingAction.properties:
|
case CalendarSettingAction.properties:
|
||||||
return GridPropertyList(
|
return DatabasePropertyList(
|
||||||
viewId: settingContext.viewId,
|
viewId: settingContext.viewId,
|
||||||
fieldController: settingContext.fieldController,
|
fieldController: settingContext.fieldController,
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
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/calendar/presentation/calendar_page.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.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:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:calendar_view/calendar_view.dart';
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -17,13 +18,15 @@ class CalendarToolbar extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const SizedBox(
|
return SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
_UnscheduleEventsButton(),
|
const _UnscheduleEventsButton(),
|
||||||
_SettingButton(),
|
SettingButton(
|
||||||
|
databaseController: context.read<CalendarBloc>().databaseController,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -115,12 +118,16 @@ class _UnscheduleEventsButtonState extends State<_UnscheduleEventsButton> {
|
|||||||
),
|
),
|
||||||
popupBuilder: (context) {
|
popupBuilder: (context) {
|
||||||
final cells = <Widget>[
|
final cells = <Widget>[
|
||||||
FlowyText.medium(
|
Padding(
|
||||||
LocaleKeys.calendar_settings_noDateHint.tr(),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||||
color: Theme.of(context).hintColor,
|
child: FlowyText.medium(
|
||||||
overflow: TextOverflow.ellipsis,
|
// 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(
|
...unscheduledEvents.map(
|
||||||
(e) => _UnscheduledEventItem(
|
(e) => _UnscheduledEventItem(
|
||||||
event: e,
|
event: e,
|
||||||
@ -164,7 +171,11 @@ class _UnscheduledEventItem extends StatelessWidget {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: GridSize.popoverItemHeight,
|
height: GridSize.popoverItemHeight,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
text: FlowyText.medium(event.title),
|
text: FlowyText.medium(
|
||||||
|
event.title.isEmpty
|
||||||
|
? LocaleKeys.calendar_defaultNewCalendarTitle.tr()
|
||||||
|
: event.title,
|
||||||
|
),
|
||||||
onTap: onPressed,
|
onTap: onPressed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -87,7 +87,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
|||||||
add(GridEvent.didReceiveGridUpdate(database));
|
add(GridEvent.didReceiveGridUpdate(database));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRowsChanged: (rowInfos, _, reason) {
|
onNumOfRowsChanged: (rowInfos, _, reason) {
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
add(GridEvent.didReceiveRowUpdate(rowInfos, reason));
|
add(GridEvent.didReceiveRowUpdate(rowInfos, reason));
|
||||||
}
|
}
|
||||||
|
@ -49,20 +49,21 @@ class GridPlugin extends Plugin {
|
|||||||
notifier = ViewPluginNotifier(view: view);
|
notifier = ViewPluginNotifier(view: view);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display => GridPluginDisplay(notifier: notifier);
|
PluginWidgetBuilder get widgetBuilder =>
|
||||||
|
GridPluginWidgetBuilder(notifier: notifier);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => notifier.view.id;
|
PluginId get id => notifier.view.id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => _pluginType;
|
PluginType get pluginType => _pluginType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridPluginDisplay extends PluginDisplay {
|
class GridPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||||
final ViewPluginNotifier notifier;
|
final ViewPluginNotifier notifier;
|
||||||
ViewPB get view => notifier.view;
|
ViewPB get view => notifier.view;
|
||||||
|
|
||||||
GridPluginDisplay({required this.notifier, Key? key});
|
GridPluginWidgetBuilder({required this.notifier, Key? key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||||
@ -36,10 +35,7 @@ class GridPage extends StatefulWidget {
|
|||||||
required this.view,
|
required this.view,
|
||||||
this.onDeleted,
|
this.onDeleted,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : databaseController = DatabaseController(
|
}) : databaseController = DatabaseController(view: view),
|
||||||
view: view,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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: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/image.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.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:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import '../../layout/sizes.dart';
|
import '../../layout/sizes.dart';
|
||||||
|
|
||||||
class GridSettingContext {
|
class DatabaseSettingList extends StatelessWidget {
|
||||||
final String viewId;
|
final DatabaseController databaseContoller;
|
||||||
final FieldController fieldController;
|
final Function(DatabaseSettingAction, DatabaseController) onAction;
|
||||||
|
const DatabaseSettingList({
|
||||||
GridSettingContext({
|
required this.databaseContoller,
|
||||||
required this.viewId,
|
|
||||||
required this.fieldController,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridSettingList extends StatelessWidget {
|
|
||||||
final GridSettingContext settingContext;
|
|
||||||
final Function(DatabaseSettingAction, GridSettingContext) onAction;
|
|
||||||
const GridSettingList({
|
|
||||||
required this.settingContext,
|
|
||||||
required this.onAction,
|
required this.onAction,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cells = DatabaseSettingAction.values
|
final cells = DatabaseSettingAction.values.where((element) {
|
||||||
.where((value) => value.enable())
|
if (element == DatabaseSettingAction.showGroup) {
|
||||||
.map((action) {
|
return databaseContoller.databaseLayout == DatabaseLayoutPB.Board;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).map((action) {
|
||||||
return _SettingItem(
|
return _SettingItem(
|
||||||
action: action,
|
action: action,
|
||||||
onAction: (action) => onAction(action, settingContext),
|
onAction: (action) => onAction(action, databaseContoller),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
@ -78,9 +71,7 @@ class _SettingItem extends StatelessWidget {
|
|||||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||||
text: FlowyText.medium(
|
text: FlowyText.medium(
|
||||||
action.title(),
|
action.title(),
|
||||||
color: action.enable()
|
color: AFThemeExtension.of(context).textColor,
|
||||||
? AFThemeExtension.of(context).textColor
|
|
||||||
: Theme.of(context).disabledColor,
|
|
||||||
),
|
),
|
||||||
onTap: () => onAction(action),
|
onTap: () => onAction(action),
|
||||||
leftIcon: svgWidget(
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
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/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
import '../../layout/sizes.dart';
|
import '../../layout/sizes.dart';
|
||||||
import 'filter_button.dart';
|
import 'filter_button.dart';
|
||||||
import 'setting_button.dart';
|
import '../../../../widgets/setting/setting_button.dart';
|
||||||
import 'sort_button.dart';
|
import 'sort_button.dart';
|
||||||
|
|
||||||
class GridToolbarContext {
|
class GridToolbarContext {
|
||||||
@ -29,7 +31,9 @@ class GridToolbar extends StatelessWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
const FilterButton(),
|
const FilterButton(),
|
||||||
const SortButton(),
|
const SortButton(),
|
||||||
const SettingButton(),
|
SettingButton(
|
||||||
|
databaseController: context.read<GridBloc>().databaseController,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,23 +11,23 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
import '../../layout/sizes.dart';
|
import '../../grid/presentation/layout/sizes.dart';
|
||||||
import '../header/field_editor.dart';
|
import '../../grid/presentation/widgets/header/field_editor.dart';
|
||||||
|
|
||||||
class GridPropertyList extends StatefulWidget {
|
class DatabasePropertyList extends StatefulWidget {
|
||||||
final String viewId;
|
final String viewId;
|
||||||
final FieldController fieldController;
|
final FieldController fieldController;
|
||||||
const GridPropertyList({
|
const DatabasePropertyList({
|
||||||
required this.viewId,
|
required this.viewId,
|
||||||
required this.fieldController,
|
required this.fieldController,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _GridPropertyListState();
|
State<StatefulWidget> createState() => _DatabasePropertyListState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GridPropertyListState extends State<GridPropertyList> {
|
class _DatabasePropertyListState extends State<DatabasePropertyList> {
|
||||||
late PopoverMutex _popoverMutex;
|
late PopoverMutex _popoverMutex;
|
||||||
|
|
||||||
@override
|
@override
|
@ -11,11 +11,11 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class GridGroupList extends StatelessWidget {
|
class DatabaseGroupList extends StatelessWidget {
|
||||||
final String viewId;
|
final String viewId;
|
||||||
final FieldController fieldController;
|
final FieldController fieldController;
|
||||||
final VoidCallback onDismissed;
|
final VoidCallback onDismissed;
|
||||||
const GridGroupList({
|
const DatabaseGroupList({
|
||||||
required this.viewId,
|
required this.viewId,
|
||||||
required this.fieldController,
|
required this.fieldController,
|
||||||
required this.onDismissed,
|
required this.onDismissed,
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -61,32 +61,36 @@ class DocumentPlugin extends Plugin<int> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display {
|
PluginWidgetBuilder get widgetBuilder {
|
||||||
return DocumentPluginDisplay(
|
return DocumentPluginWidgetBuilder(
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
documentAppearanceCubit: _documentAppearanceCubit,
|
documentAppearanceCubit: _documentAppearanceCubit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => _pluginType;
|
PluginType get pluginType => _pluginType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => notifier.view.id;
|
PluginId get id => notifier.view.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
|
class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
|
||||||
|
with NavigationItem {
|
||||||
final ViewPluginNotifier notifier;
|
final ViewPluginNotifier notifier;
|
||||||
ViewPB get view => notifier.view;
|
ViewPB get view => notifier.view;
|
||||||
int? deletedViewIndex;
|
int? deletedViewIndex;
|
||||||
DocumentAppearanceCubit documentAppearanceCubit;
|
DocumentAppearanceCubit documentAppearanceCubit;
|
||||||
|
|
||||||
DocumentPluginDisplay({
|
DocumentPluginWidgetBuilder({
|
||||||
required this.notifier,
|
required this.notifier,
|
||||||
required this.documentAppearanceCubit,
|
required this.documentAppearanceCubit,
|
||||||
Key? key,
|
Key? key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
EdgeInsets get contentPadding => EdgeInsets.zero;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildWidget(PluginContext context) {
|
Widget buildWidget(PluginContext context) {
|
||||||
notifier.isDeleted.addListener(() {
|
notifier.isDeleted.addListener(() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
@ -38,13 +38,18 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
|
|||||||
late Future<dartz.Either<FlowyError, ViewPB>> future;
|
late Future<dartz.Either<FlowyError, ViewPB>> future;
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
|
|
||||||
String get appId => widget.node.attributes[DatabaseBlockKeys.kAppID];
|
String get parentViewId => widget.node.attributes[DatabaseBlockKeys.parentID];
|
||||||
String get viewId => widget.node.attributes[DatabaseBlockKeys.kViewID];
|
String get childViewId => widget.node.attributes[DatabaseBlockKeys.viewID];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
future = AppBackendService().getChildView(viewId, appId).then(
|
future = ViewBackendService()
|
||||||
|
.getChildView(
|
||||||
|
parentViewId: parentViewId,
|
||||||
|
childViewId: childViewId,
|
||||||
|
)
|
||||||
|
.then(
|
||||||
(value) => value.swap(),
|
(value) => value.swap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -153,6 +158,7 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
|
|||||||
switch (action.inner) {
|
switch (action.inner) {
|
||||||
case _ActionType.viewDatabase:
|
case _ActionType.viewDatabase:
|
||||||
getIt<MenuSharedState>().latestOpenView = viewPB;
|
getIt<MenuSharedState>().latestOpenView = viewPB;
|
||||||
|
|
||||||
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
|
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
|
||||||
break;
|
break;
|
||||||
case _ActionType.delete:
|
case _ActionType.delete:
|
||||||
|
@ -2,20 +2,20 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:appflowy/plugins/database_view/application/database_view_service.dart';
|
import 'package:appflowy/plugins/database_view/application/database_view_service.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/board/board_node_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/board/board_node_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/grid/grid_node_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/grid/grid_node_widget.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class DatabaseBlockKeys {
|
class DatabaseBlockKeys {
|
||||||
const DatabaseBlockKeys._();
|
const DatabaseBlockKeys._();
|
||||||
|
|
||||||
static const String kAppID = 'app_id';
|
static const String parentID = 'parent_id';
|
||||||
static const String kViewID = 'view_id';
|
static const String viewID = 'view_id';
|
||||||
}
|
}
|
||||||
|
|
||||||
extension InsertDatabase on EditorState {
|
extension InsertDatabase on EditorState {
|
||||||
Future<void> insertPage(ViewPB appPB, ViewPB viewPB) async {
|
Future<void> insertPage(ViewPB parentView, ViewPB childView) async {
|
||||||
final selection = this.selection;
|
final selection = this.selection;
|
||||||
if (selection == null || !selection.isCollapsed) {
|
if (selection == null || !selection.isCollapsed) {
|
||||||
return;
|
return;
|
||||||
@ -26,23 +26,22 @@ extension InsertDatabase on EditorState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the database that the view is associated with
|
// get the database that the view is associated with
|
||||||
final database = await DatabaseViewBackendService(viewId: viewPB.id)
|
final database = await DatabaseViewBackendService(viewId: childView.id)
|
||||||
.openGrid()
|
.openGrid()
|
||||||
.then((value) => value.swap().toOption().toNullable());
|
.then((value) => value.swap().toOption().toNullable());
|
||||||
|
|
||||||
if (database == null) {
|
if (database == null) {
|
||||||
throw StateError(
|
throw StateError(
|
||||||
'The database associated with ${viewPB.id} could not be found while attempting to create a referenced ${viewPB.layout.name}.',
|
'The database associated with ${childView.id} could not be found while attempting to create a referenced ${childView.layout.name}.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final prefix = _referencedDatabasePrefix(viewPB.layout);
|
final prefix = _referencedDatabasePrefix(childView.layout);
|
||||||
final ref = await AppBackendService().createView(
|
final ref = await ViewBackendService.createDatabaseReferenceView(
|
||||||
parentViewId: appPB.id,
|
parentViewId: parentView.id,
|
||||||
name: "$prefix ${viewPB.name}",
|
name: "$prefix ${childView.name}",
|
||||||
layoutType: viewPB.layout,
|
layoutType: childView.layout,
|
||||||
ext: {
|
databaseId: database.id,
|
||||||
'database_id': database.id,
|
|
||||||
},
|
|
||||||
).then((value) => value.swap().toOption().toNullable());
|
).then((value) => value.swap().toOption().toNullable());
|
||||||
|
|
||||||
// TODO(a-wallen): Show error dialog here.
|
// TODO(a-wallen): Show error dialog here.
|
||||||
@ -54,10 +53,10 @@ extension InsertDatabase on EditorState {
|
|||||||
transaction.insertNode(
|
transaction.insertNode(
|
||||||
selection.end.path,
|
selection.end.path,
|
||||||
Node(
|
Node(
|
||||||
type: _convertPageType(viewPB),
|
type: _convertPageType(childView),
|
||||||
attributes: {
|
attributes: {
|
||||||
DatabaseBlockKeys.kAppID: appPB.id,
|
DatabaseBlockKeys.parentID: parentView.id,
|
||||||
DatabaseBlockKeys.kViewID: ref.id,
|
DatabaseBlockKeys.viewID: ref.id,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
@ -69,7 +69,7 @@ class _LinkToPageMenuState extends State<LinkToPageMenu> {
|
|||||||
final Map<int, (ViewPB, ViewPB)> _items = {};
|
final Map<int, (ViewPB, ViewPB)> _items = {};
|
||||||
|
|
||||||
Future<List<(ViewPB, List<ViewPB>)>> fetchItems() async {
|
Future<List<(ViewPB, List<ViewPB>)>> fetchItems() async {
|
||||||
final items = await AppBackendService().fetchViews(widget.layoutType);
|
final items = await ViewBackendService().fetchViews(widget.layoutType);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (final (app, children) in items) {
|
for (final (app, children) in items) {
|
||||||
|
@ -37,8 +37,8 @@ class BoardBlockComponentBuilder extends BlockComponentBuilder {
|
|||||||
@override
|
@override
|
||||||
bool validate(Node node) =>
|
bool validate(Node node) =>
|
||||||
node.children.isEmpty &&
|
node.children.isEmpty &&
|
||||||
node.attributes[DatabaseBlockKeys.kAppID] is String &&
|
node.attributes[DatabaseBlockKeys.parentID] is String &&
|
||||||
node.attributes[DatabaseBlockKeys.kViewID] is String;
|
node.attributes[DatabaseBlockKeys.viewID] is String;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoardBlockComponentWidget extends BlockComponentStatefulWidget {
|
class BoardBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) =>
|
SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) =>
|
||||||
@ -22,9 +22,9 @@ SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
final appId = documentBloc.view.parentViewId;
|
final appId = documentBloc.view.parentViewId;
|
||||||
final service = AppBackendService();
|
final service = ViewBackendService();
|
||||||
|
|
||||||
final result = (await service.createView(
|
final result = (await ViewBackendService.createView(
|
||||||
parentViewId: appId,
|
parentViewId: appId,
|
||||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
layoutType: ViewLayoutPB.Board,
|
layoutType: ViewLayoutPB.Board,
|
||||||
@ -42,7 +42,10 @@ SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final view = (await service.getChildView(result.viewId, result.id))
|
final view = (await service.getChildView(
|
||||||
|
parentViewId: result.viewId,
|
||||||
|
childViewId: result.id,
|
||||||
|
))
|
||||||
.getLeftOrNull();
|
.getLeftOrNull();
|
||||||
// As this.
|
// As this.
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
|
@ -37,8 +37,8 @@ class GridBlockComponentBuilder extends BlockComponentBuilder {
|
|||||||
@override
|
@override
|
||||||
bool validate(Node node) =>
|
bool validate(Node node) =>
|
||||||
node.children.isEmpty &&
|
node.children.isEmpty &&
|
||||||
node.attributes[DatabaseBlockKeys.kAppID] is String &&
|
node.attributes[DatabaseBlockKeys.parentID] is String &&
|
||||||
node.attributes[DatabaseBlockKeys.kViewID] is String;
|
node.attributes[DatabaseBlockKeys.viewID] is String;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridBlockComponentWidget extends BlockComponentStatefulWidget {
|
class GridBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -22,9 +22,9 @@ SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
final appId = documentBloc.view.parentViewId;
|
final appId = documentBloc.view.parentViewId;
|
||||||
final service = AppBackendService();
|
final service = ViewBackendService();
|
||||||
|
|
||||||
final result = (await service.createView(
|
final result = (await ViewBackendService.createView(
|
||||||
parentViewId: appId,
|
parentViewId: appId,
|
||||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
layoutType: ViewLayoutPB.Grid,
|
layoutType: ViewLayoutPB.Grid,
|
||||||
@ -42,7 +42,10 @@ SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final view = (await service.getChildView(result.viewId, result.id))
|
final view = (await service.getChildView(
|
||||||
|
parentViewId: result.viewId,
|
||||||
|
childViewId: result.id,
|
||||||
|
))
|
||||||
.getLeftOrNull();
|
.getLeftOrNull();
|
||||||
// As this.
|
// As this.
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
|
@ -38,16 +38,16 @@ class TrashPlugin extends Plugin {
|
|||||||
TrashPlugin({required PluginType pluginType}) : _pluginType = pluginType;
|
TrashPlugin({required PluginType pluginType}) : _pluginType = pluginType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginDisplay get display => TrashPluginDisplay();
|
PluginWidgetBuilder get widgetBuilder => TrashPluginDisplay();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginId get id => "TrashStack";
|
PluginId get id => "TrashStack";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get ty => _pluginType;
|
PluginType get pluginType => _pluginType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrashPluginDisplay extends PluginDisplay {
|
class TrashPluginDisplay extends PluginWidgetBuilder {
|
||||||
@override
|
@override
|
||||||
Widget get leftBarItem => FlowyText.medium(LocaleKeys.trash_text.tr());
|
Widget get leftBarItem => FlowyText.medium(LocaleKeys.trash_text.tr());
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ typedef PluginId = String;
|
|||||||
abstract class Plugin<T> {
|
abstract class Plugin<T> {
|
||||||
PluginId get id;
|
PluginId get id;
|
||||||
|
|
||||||
PluginDisplay get display;
|
PluginWidgetBuilder get widgetBuilder;
|
||||||
|
|
||||||
PluginNotifier? get notifier => null;
|
PluginNotifier? get notifier => null;
|
||||||
|
|
||||||
PluginType get ty;
|
PluginType get pluginType;
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
notifier?.dispose();
|
notifier?.dispose();
|
||||||
@ -37,7 +37,7 @@ abstract class PluginNotifier<T> {
|
|||||||
/// Notify if the plugin get deleted
|
/// Notify if the plugin get deleted
|
||||||
ValueNotifier<T> get isDeleted;
|
ValueNotifier<T> get isDeleted;
|
||||||
|
|
||||||
/// Notify if the [PluginDisplay]'s content was changed
|
/// Notify if the [PluginWidgetBuilder]'s content was changed
|
||||||
ValueNotifier<int> get isDisplayChanged;
|
ValueNotifier<int> get isDisplayChanged;
|
||||||
|
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
@ -50,8 +50,11 @@ abstract class PluginBuilder {
|
|||||||
|
|
||||||
String get menuIcon;
|
String get menuIcon;
|
||||||
|
|
||||||
|
/// The type of this [Plugin]. Each [Plugin] should have a unique [PluginType]
|
||||||
PluginType get pluginType;
|
PluginType get pluginType;
|
||||||
|
|
||||||
|
/// The layoutType is used in the backend to determine the layout of the view.
|
||||||
|
/// Currrently, AppFlowy supports 4 layout types: Document, Grid, Board, Calendar.
|
||||||
ViewLayoutPB? get layoutType => ViewLayoutPB.Document;
|
ViewLayoutPB? get layoutType => ViewLayoutPB.Document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,9 +63,12 @@ abstract class PluginConfig {
|
|||||||
bool get creatable => true;
|
bool get creatable => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class PluginDisplay with NavigationItem {
|
abstract class PluginWidgetBuilder with NavigationItem {
|
||||||
List<NavigationItem> get navigationItems;
|
List<NavigationItem> get navigationItems;
|
||||||
|
|
||||||
|
EdgeInsets get contentPadding =>
|
||||||
|
const EdgeInsets.symmetric(horizontal: 40, vertical: 28);
|
||||||
|
|
||||||
Widget buildWidget(PluginContext context);
|
Widget buildWidget(PluginContext context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +84,8 @@ void registerPlugin({required PluginBuilder builder, PluginConfig? config}) {
|
|||||||
.registerPlugin(builder.pluginType, builder, config: config);
|
.registerPlugin(builder.pluginType, builder, config: config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make the correct plugin from the [pluginType] and [data]. If the plugin
|
||||||
|
/// is not registered, it will return a blank plugin.
|
||||||
Plugin makePlugin({required PluginType pluginType, dynamic data}) {
|
Plugin makePlugin({required PluginType pluginType, dynamic data}) {
|
||||||
final plugin = getIt<PluginSandbox>().buildPlugin(pluginType, data);
|
final plugin = getIt<PluginSandbox>().buildPlugin(pluginType, data);
|
||||||
return plugin;
|
return plugin;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:appflowy/plugins/blank/blank.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import '../plugin.dart';
|
import '../plugin.dart';
|
||||||
@ -28,9 +29,11 @@ class PluginSandbox {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a plugin from [data] with [pluginType]
|
||||||
|
/// If the [pluginType] is not registered, it will return a blank plugin
|
||||||
Plugin buildPlugin(PluginType pluginType, dynamic data) {
|
Plugin buildPlugin(PluginType pluginType, dynamic data) {
|
||||||
final plugin = _pluginBuilders[pluginType]!.build(data);
|
final builder = _pluginBuilders[pluginType] ?? BlankPluginBuilder();
|
||||||
return plugin;
|
return builder.build(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerPlugin(
|
void registerPlugin(
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:collection';
|
|||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_listener.dart';
|
import 'package:appflowy/workspace/application/app/app_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
|
||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
@ -17,11 +17,11 @@ import 'package:dartz/dartz.dart';
|
|||||||
part 'app_bloc.freezed.dart';
|
part 'app_bloc.freezed.dart';
|
||||||
|
|
||||||
class AppBloc extends Bloc<AppEvent, AppState> {
|
class AppBloc extends Bloc<AppEvent, AppState> {
|
||||||
final AppBackendService appService;
|
final ViewBackendService appService;
|
||||||
final AppListener appListener;
|
final AppListener appListener;
|
||||||
|
|
||||||
AppBloc({required ViewPB view})
|
AppBloc({required ViewPB view})
|
||||||
: appService = AppBackendService(),
|
: appService = ViewBackendService(),
|
||||||
appListener = AppListener(viewId: view.id),
|
appListener = AppListener(viewId: view.id),
|
||||||
super(AppState.initial(view)) {
|
super(AppState.initial(view)) {
|
||||||
on<AppEvent>((event, emit) async {
|
on<AppEvent>((event, emit) async {
|
||||||
@ -77,8 +77,10 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
|
Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
|
||||||
final result =
|
final result = await ViewBackendService.updateView(
|
||||||
await appService.updateApp(appId: state.view.id, name: e.newName);
|
viewId: state.view.id,
|
||||||
|
name: e.newName,
|
||||||
|
);
|
||||||
result.fold(
|
result.fold(
|
||||||
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
@ -87,7 +89,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
|
|
||||||
// Delete the current app
|
// Delete the current app
|
||||||
Future<void> _deleteApp(Emitter<AppState> emit) async {
|
Future<void> _deleteApp(Emitter<AppState> emit) async {
|
||||||
final result = await appService.delete(viewId: state.view.id);
|
final result = await ViewBackendService.delete(viewId: state.view.id);
|
||||||
result.fold(
|
result.fold(
|
||||||
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
@ -95,7 +97,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _deleteView(Emitter<AppState> emit, String viewId) async {
|
Future<void> _deleteView(Emitter<AppState> emit, String viewId) async {
|
||||||
final result = await appService.deleteView(viewId: viewId);
|
final result = await ViewBackendService.deleteView(viewId: viewId);
|
||||||
result.fold(
|
result.fold(
|
||||||
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
@ -103,7 +105,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
|
Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
|
||||||
final result = await appService.createView(
|
final result = await ViewBackendService.createView(
|
||||||
parentViewId: state.view.id,
|
parentViewId: state.view.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
desc: value.desc ?? "",
|
desc: value.desc ?? "",
|
||||||
@ -132,7 +134,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadViews(Emitter<AppState> emit) async {
|
Future<void> _loadViews(Emitter<AppState> emit) async {
|
||||||
final viewsOrFailed = await appService.getViews(viewId: state.view.id);
|
final viewsOrFailed =
|
||||||
|
await ViewBackendService.getViews(viewId: state.view.id);
|
||||||
viewsOrFailed.fold(
|
viewsOrFailed.fold(
|
||||||
(views) => emit(state.copyWith(views: views)),
|
(views) => emit(state.copyWith(views: views)),
|
||||||
(error) {
|
(error) {
|
||||||
|
@ -1,143 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
|
||||||
|
|
||||||
class AppBackendService {
|
|
||||||
Future<Either<ViewPB, FlowyError>> createView({
|
|
||||||
required String parentViewId,
|
|
||||||
required String name,
|
|
||||||
String? desc,
|
|
||||||
required ViewLayoutPB layoutType,
|
|
||||||
|
|
||||||
/// The initial data should be a JSON that represent the DocumentDataPB.
|
|
||||||
/// Currently, only support create document with initial data.
|
|
||||||
List<int>? initialDataBytes,
|
|
||||||
|
|
||||||
/// The [ext] is used to pass through the custom configuration
|
|
||||||
/// to the backend.
|
|
||||||
/// Linking the view to the existing database, it needs to pass
|
|
||||||
/// the database id. For example: "database_id": "xxx"
|
|
||||||
///
|
|
||||||
Map<String, String> ext = const {},
|
|
||||||
}) {
|
|
||||||
final payload = CreateViewPayloadPB.create()
|
|
||||||
..parentViewId = parentViewId
|
|
||||||
..name = name
|
|
||||||
..desc = desc ?? ""
|
|
||||||
..layout = layoutType
|
|
||||||
..initialData = initialDataBytes ?? [];
|
|
||||||
|
|
||||||
if (ext.isNotEmpty) {
|
|
||||||
payload.ext.addAll(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FolderEventCreateView(payload).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<List<ViewPB>, FlowyError>> getViews({required String viewId}) {
|
|
||||||
final payload = ViewIdPB.create()..value = viewId;
|
|
||||||
|
|
||||||
return FolderEventReadView(payload).send().then((result) {
|
|
||||||
return result.fold(
|
|
||||||
(app) => left(app.childViews),
|
|
||||||
(error) => right(error),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> delete({required String viewId}) {
|
|
||||||
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
|
||||||
return FolderEventDeleteView(request).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> deleteView({required String viewId}) {
|
|
||||||
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
|
||||||
return FolderEventDeleteView(request).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<ViewPB, FlowyError>> updateApp({
|
|
||||||
required String appId,
|
|
||||||
String? name,
|
|
||||||
}) {
|
|
||||||
var payload = UpdateViewPayloadPB.create()..viewId = appId;
|
|
||||||
|
|
||||||
if (name != null) {
|
|
||||||
payload.name = name;
|
|
||||||
}
|
|
||||||
return FolderEventUpdateView(payload).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> moveView({
|
|
||||||
required String viewId,
|
|
||||||
required int fromIndex,
|
|
||||||
required int toIndex,
|
|
||||||
}) {
|
|
||||||
final payload = MoveFolderItemPayloadPB.create()
|
|
||||||
..itemId = viewId
|
|
||||||
..from = fromIndex
|
|
||||||
..to = toIndex
|
|
||||||
..ty = MoveFolderItemType.MoveView;
|
|
||||||
|
|
||||||
return FolderEventMoveItem(payload).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<(ViewPB, List<ViewPB>)>> fetchViews(
|
|
||||||
ViewLayoutPB layoutType,
|
|
||||||
) async {
|
|
||||||
final result = <(ViewPB, List<ViewPB>)>[];
|
|
||||||
return FolderEventReadCurrentWorkspace().send().then((value) async {
|
|
||||||
final workspaces = value.getLeftOrNull<WorkspaceSettingPB>();
|
|
||||||
if (workspaces != null) {
|
|
||||||
final views = workspaces.workspace.views;
|
|
||||||
for (var view in views) {
|
|
||||||
final childViews = await getViews(viewId: view.id).then(
|
|
||||||
(value) => value
|
|
||||||
.getLeftOrNull<List<ViewPB>>()
|
|
||||||
?.where((e) => e.layout == layoutType)
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
if (childViews != null && childViews.isNotEmpty) {
|
|
||||||
result.add((view, childViews));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<ViewPB, FlowyError>> getView(
|
|
||||||
String viewID,
|
|
||||||
) async {
|
|
||||||
final payload = ViewIdPB.create()..value = viewID;
|
|
||||||
return FolderEventReadView(payload).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<ViewPB, FlowyError>> getChildView(
|
|
||||||
String viewID,
|
|
||||||
String childViewID,
|
|
||||||
) async {
|
|
||||||
final payload = ViewIdPB.create()..value = viewID;
|
|
||||||
return FolderEventReadView(payload).send().then((result) {
|
|
||||||
return result.fold(
|
|
||||||
(app) => left(
|
|
||||||
app.childViews.firstWhere((e) => e.id == childViewID),
|
|
||||||
),
|
|
||||||
(error) => right(error),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AppFlowy on Either {
|
|
||||||
T? getLeftOrNull<T>() {
|
|
||||||
if (isLeft()) {
|
|
||||||
final result = fold<T?>((l) => l, (r) => null);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,2 @@
|
|||||||
export 'app_bloc.dart';
|
export 'app_bloc.dart';
|
||||||
export 'app_listener.dart';
|
export 'app_listener.dart';
|
||||||
export 'app_service.dart';
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy/workspace/application/app/app_bloc.dart';
|
import 'package:appflowy/workspace/application/app/app_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -13,12 +13,10 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
|
|||||||
void Function()? _viewsListener;
|
void Function()? _viewsListener;
|
||||||
void Function()? _selectedViewlistener;
|
void Function()? _selectedViewlistener;
|
||||||
final AppViewDataContext _appViewData;
|
final AppViewDataContext _appViewData;
|
||||||
late final AppBackendService _appService;
|
|
||||||
|
|
||||||
ViewSectionBloc({
|
ViewSectionBloc({
|
||||||
required AppViewDataContext appViewData,
|
required AppViewDataContext appViewData,
|
||||||
}) : _appService = AppBackendService(),
|
}) : _appViewData = appViewData,
|
||||||
_appViewData = appViewData,
|
|
||||||
super(ViewSectionState.initial(appViewData)) {
|
super(ViewSectionState.initial(appViewData)) {
|
||||||
on<ViewSectionEvent>((event, emit) async {
|
on<ViewSectionEvent>((event, emit) async {
|
||||||
await event.map(
|
await event.map(
|
||||||
@ -69,7 +67,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
|
|||||||
views.insert(value.toIndex, views.removeAt(value.fromIndex));
|
views.insert(value.toIndex, views.removeAt(value.fromIndex));
|
||||||
emit(state.copyWith(views: views));
|
emit(state.copyWith(views: views));
|
||||||
|
|
||||||
final result = await _appService.moveView(
|
final result = await ViewBackendService.moveView(
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
fromIndex: value.fromIndex,
|
fromIndex: value.fromIndex,
|
||||||
toIndex: value.toIndex,
|
toIndex: value.toIndex,
|
||||||
|
@ -9,13 +9,13 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
|||||||
part 'view_bloc.freezed.dart';
|
part 'view_bloc.freezed.dart';
|
||||||
|
|
||||||
class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||||
final ViewService service;
|
final ViewBackendService viewBackendSvc;
|
||||||
final ViewListener listener;
|
final ViewListener listener;
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
|
||||||
ViewBloc({
|
ViewBloc({
|
||||||
required this.view,
|
required this.view,
|
||||||
}) : service = ViewService(),
|
}) : viewBackendSvc = ViewBackendService(),
|
||||||
listener = ViewListener(view: view),
|
listener = ViewListener(view: view),
|
||||||
super(ViewState.init(view)) {
|
super(ViewState.init(view)) {
|
||||||
on<ViewEvent>((event, emit) async {
|
on<ViewEvent>((event, emit) async {
|
||||||
@ -42,7 +42,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
rename: (e) async {
|
rename: (e) async {
|
||||||
final result = await service.updateView(
|
final result = await ViewBackendService.updateView(
|
||||||
viewId: view.id,
|
viewId: view.id,
|
||||||
name: e.newName,
|
name: e.newName,
|
||||||
);
|
);
|
||||||
@ -54,7 +54,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
delete: (e) async {
|
delete: (e) async {
|
||||||
final result = await service.delete(viewId: view.id);
|
final result = await ViewBackendService.delete(viewId: view.id);
|
||||||
emit(
|
emit(
|
||||||
result.fold(
|
result.fold(
|
||||||
(l) => state.copyWith(successOrFailure: left(unit)),
|
(l) => state.copyWith(successOrFailure: left(unit)),
|
||||||
@ -63,7 +63,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
duplicate: (e) async {
|
duplicate: (e) async {
|
||||||
final result = await service.duplicate(view: view);
|
final result = await ViewBackendService.duplicate(view: view);
|
||||||
emit(
|
emit(
|
||||||
result.fold(
|
result.fold(
|
||||||
(l) => state.copyWith(successOrFailure: left(unit)),
|
(l) => state.copyWith(successOrFailure: left(unit)),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/plugins/database_view/database_view.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
@ -56,7 +57,14 @@ extension ViewExtension on ViewPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Plugin plugin() {
|
Plugin plugin() {
|
||||||
final plugin = makePlugin(pluginType: pluginType, data: this);
|
switch (layout) {
|
||||||
return plugin;
|
case ViewLayoutPB.Board:
|
||||||
|
case ViewLayoutPB.Calendar:
|
||||||
|
case ViewLayoutPB.Grid:
|
||||||
|
return DatabaseViewPlugin(view: this);
|
||||||
|
case ViewLayoutPB.Document:
|
||||||
|
return makePlugin(pluginType: pluginType, data: this);
|
||||||
|
}
|
||||||
|
throw UnimplementedError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,165 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
|
||||||
class ViewService {
|
class ViewBackendService {
|
||||||
Future<Either<ViewPB, FlowyError>> updateView({
|
static Future<Either<ViewPB, FlowyError>> createView({
|
||||||
required String viewId,
|
required ViewLayoutPB layoutType,
|
||||||
String? name,
|
required String parentViewId,
|
||||||
|
required String name,
|
||||||
String? desc,
|
String? desc,
|
||||||
|
|
||||||
|
/// The initial data should be a JSON that represent the DocumentDataPB.
|
||||||
|
/// Currently, only support create document with initial data.
|
||||||
|
List<int>? initialDataBytes,
|
||||||
|
|
||||||
|
/// The [ext] is used to pass through the custom configuration
|
||||||
|
/// to the backend.
|
||||||
|
/// Linking the view to the existing database, it needs to pass
|
||||||
|
/// the database id. For example: "database_id": "xxx"
|
||||||
|
///
|
||||||
|
Map<String, String> ext = const {},
|
||||||
}) {
|
}) {
|
||||||
final request = UpdateViewPayloadPB.create()..viewId = viewId;
|
final payload = CreateViewPayloadPB.create()
|
||||||
|
..parentViewId = parentViewId
|
||||||
|
..name = name
|
||||||
|
..desc = desc ?? ""
|
||||||
|
..layout = layoutType
|
||||||
|
..initialData = initialDataBytes ?? [];
|
||||||
|
|
||||||
if (name != null) {
|
if (ext.isNotEmpty) {
|
||||||
request.name = name;
|
payload.ext.addAll(ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc != null) {
|
return FolderEventCreateView(payload).send();
|
||||||
request.desc = desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FolderEventUpdateView(request).send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> delete({required String viewId}) {
|
static Future<Either<ViewPB, FlowyError>> createDatabaseReferenceView({
|
||||||
|
required String parentViewId,
|
||||||
|
required String databaseId,
|
||||||
|
required ViewLayoutPB layoutType,
|
||||||
|
required String name,
|
||||||
|
}) {
|
||||||
|
return ViewBackendService.createView(
|
||||||
|
layoutType: layoutType,
|
||||||
|
parentViewId: parentViewId,
|
||||||
|
name: name,
|
||||||
|
ext: {
|
||||||
|
'database_id': databaseId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Either<List<ViewPB>, FlowyError>> getViews({
|
||||||
|
required String viewId,
|
||||||
|
}) {
|
||||||
|
final payload = ViewIdPB.create()..value = viewId;
|
||||||
|
|
||||||
|
return FolderEventReadView(payload).send().then((result) {
|
||||||
|
return result.fold(
|
||||||
|
(app) => left(app.childViews),
|
||||||
|
(error) => right(error),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Either<Unit, FlowyError>> delete({required String viewId}) {
|
||||||
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
||||||
return FolderEventDeleteView(request).send();
|
return FolderEventDeleteView(request).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> duplicate({required ViewPB view}) {
|
static Future<Either<Unit, FlowyError>> deleteView({required String viewId}) {
|
||||||
|
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
||||||
|
return FolderEventDeleteView(request).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Either<Unit, FlowyError>> duplicate({required ViewPB view}) {
|
||||||
return FolderEventDuplicateView(view).send();
|
return FolderEventDuplicateView(view).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Either<ViewPB, FlowyError>> updateView({
|
||||||
|
required String viewId,
|
||||||
|
String? name,
|
||||||
|
}) {
|
||||||
|
var payload = UpdateViewPayloadPB.create()..viewId = viewId;
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
payload.name = name;
|
||||||
|
}
|
||||||
|
return FolderEventUpdateView(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Either<Unit, FlowyError>> moveView({
|
||||||
|
required String viewId,
|
||||||
|
required int fromIndex,
|
||||||
|
required int toIndex,
|
||||||
|
}) {
|
||||||
|
final payload = MoveFolderItemPayloadPB.create()
|
||||||
|
..itemId = viewId
|
||||||
|
..from = fromIndex
|
||||||
|
..to = toIndex
|
||||||
|
..ty = MoveFolderItemType.MoveView;
|
||||||
|
|
||||||
|
return FolderEventMoveItem(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<(ViewPB, List<ViewPB>)>> fetchViews(
|
||||||
|
ViewLayoutPB layoutType,
|
||||||
|
) async {
|
||||||
|
final result = <(ViewPB, List<ViewPB>)>[];
|
||||||
|
return FolderEventReadCurrentWorkspace().send().then((value) async {
|
||||||
|
final workspaces = value.getLeftOrNull<WorkspaceSettingPB>();
|
||||||
|
if (workspaces != null) {
|
||||||
|
final views = workspaces.workspace.views;
|
||||||
|
for (var view in views) {
|
||||||
|
final childViews = await getViews(viewId: view.id).then(
|
||||||
|
(value) => value
|
||||||
|
.getLeftOrNull<List<ViewPB>>()
|
||||||
|
?.where((e) => e.layout == layoutType)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
if (childViews != null && childViews.isNotEmpty) {
|
||||||
|
result.add((view, childViews));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<ViewPB, FlowyError>> getView(
|
||||||
|
String viewID,
|
||||||
|
) async {
|
||||||
|
final payload = ViewIdPB.create()..value = viewID;
|
||||||
|
return FolderEventReadView(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<ViewPB, FlowyError>> getChildView({
|
||||||
|
required String parentViewId,
|
||||||
|
required String childViewId,
|
||||||
|
}) async {
|
||||||
|
final payload = ViewIdPB.create()..value = parentViewId;
|
||||||
|
return FolderEventReadView(payload).send().then((result) {
|
||||||
|
return result.fold(
|
||||||
|
(app) => left(
|
||||||
|
app.childViews.firstWhere((e) => e.id == childViewId),
|
||||||
|
),
|
||||||
|
(error) => right(error),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppFlowy on Either {
|
||||||
|
T? getLeftOrNull<T>() {
|
||||||
|
if (isLeft()) {
|
||||||
|
final result = fold<T?>((l) => l, (r) => null);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
if (view != null) {
|
if (view != null) {
|
||||||
// Only open the last opened view if the [HomeStackManager] current opened plugin is blank and the last opened view is not null.
|
// Only open the last opened view if the [HomeStackManager] current opened plugin is blank and the last opened view is not null.
|
||||||
// All opened widgets that display on the home screen are in the form of plugins. There is a list of built-in plugins defined in the [PluginType] enum, including board, grid and trash.
|
// All opened widgets that display on the home screen are in the form of plugins. There is a list of built-in plugins defined in the [PluginType] enum, including board, grid and trash.
|
||||||
if (getIt<HomeStackManager>().plugin.ty ==
|
if (getIt<HomeStackManager>().plugin.pluginType ==
|
||||||
PluginType.blank) {
|
PluginType.blank) {
|
||||||
final plugin = makePlugin(
|
final plugin = makePlugin(
|
||||||
pluginType: view.pluginType,
|
pluginType: view.pluginType,
|
||||||
|
@ -112,16 +112,14 @@ abstract mixin class NavigationItem {
|
|||||||
class HomeStackNotifier extends ChangeNotifier {
|
class HomeStackNotifier extends ChangeNotifier {
|
||||||
Plugin _plugin;
|
Plugin _plugin;
|
||||||
|
|
||||||
Widget get titleWidget => _plugin.display.leftBarItem;
|
Widget get titleWidget => _plugin.widgetBuilder.leftBarItem;
|
||||||
|
|
||||||
HomeStackNotifier({Plugin? plugin})
|
HomeStackNotifier({Plugin? plugin})
|
||||||
: _plugin = plugin ?? makePlugin(pluginType: PluginType.blank);
|
: _plugin = plugin ?? makePlugin(pluginType: PluginType.blank);
|
||||||
|
|
||||||
|
/// This is the only place where the plugin is set.
|
||||||
|
/// No need compare the old plugin with the new plugin. Just set it.
|
||||||
set plugin(Plugin newPlugin) {
|
set plugin(Plugin newPlugin) {
|
||||||
if (newPlugin.id == _plugin.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_plugin.notifier?.isDisplayChanged.addListener(notifyListeners);
|
_plugin.notifier?.isDisplayChanged.addListener(notifyListeners);
|
||||||
_plugin.dispose();
|
_plugin.dispose();
|
||||||
|
|
||||||
@ -139,7 +137,7 @@ class HomeStackManager {
|
|||||||
HomeStackManager();
|
HomeStackManager();
|
||||||
|
|
||||||
Widget title() {
|
Widget title() {
|
||||||
return _notifier.plugin.display.leftBarItem;
|
return _notifier.plugin.widgetBuilder.leftBarItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin get plugin => _notifier.plugin;
|
Plugin get plugin => _notifier.plugin;
|
||||||
@ -172,20 +170,22 @@ class HomeStackManager {
|
|||||||
child: Consumer(
|
child: Consumer(
|
||||||
builder: (_, HomeStackNotifier notifier, __) {
|
builder: (_, HomeStackNotifier notifier, __) {
|
||||||
return FadingIndexedStack(
|
return FadingIndexedStack(
|
||||||
index: getIt<PluginSandbox>().indexOf(notifier.plugin.ty),
|
index: getIt<PluginSandbox>().indexOf(notifier.plugin.pluginType),
|
||||||
children: getIt<PluginSandbox>().supportPluginTypes.map(
|
children: getIt<PluginSandbox>().supportPluginTypes.map(
|
||||||
(pluginType) {
|
(pluginType) {
|
||||||
if (pluginType == notifier.plugin.ty) {
|
if (pluginType == notifier.plugin.pluginType) {
|
||||||
final pluginWidget = notifier.plugin.display
|
final builder = notifier.plugin.widgetBuilder;
|
||||||
.buildWidget(PluginContext(onDeleted: onDeleted));
|
final pluginWidget = builder.buildWidget(
|
||||||
if (pluginType == PluginType.editor) {
|
PluginContext(onDeleted: onDeleted),
|
||||||
return pluginWidget;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return pluginWidget.padding(horizontal: 40, vertical: 28);
|
return Padding(
|
||||||
|
padding: builder.contentPadding,
|
||||||
|
child: pluginWidget,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const BlankPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
return const BlankPage();
|
|
||||||
},
|
},
|
||||||
).toList(),
|
).toList(),
|
||||||
);
|
);
|
||||||
@ -219,7 +219,7 @@ class HomeTopBar extends StatelessWidget {
|
|||||||
value: Provider.of<HomeStackNotifier>(context, listen: false),
|
value: Provider.of<HomeStackNotifier>(context, listen: false),
|
||||||
child: Consumer(
|
child: Consumer(
|
||||||
builder: (_, HomeStackNotifier notifier, __) =>
|
builder: (_, HomeStackNotifier notifier, __) =>
|
||||||
notifier.plugin.display.rightBarItem ??
|
notifier.plugin.widgetBuilder.rightBarItem ??
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -21,8 +21,8 @@ class NavigationNotifier with ChangeNotifier {
|
|||||||
|
|
||||||
void update(HomeStackNotifier notifier) {
|
void update(HomeStackNotifier notifier) {
|
||||||
bool shouldNotify = false;
|
bool shouldNotify = false;
|
||||||
if (navigationItems != notifier.plugin.display.navigationItems) {
|
if (navigationItems != notifier.plugin.widgetBuilder.navigationItems) {
|
||||||
navigationItems = notifier.plugin.display.navigationItems;
|
navigationItems = notifier.plugin.widgetBuilder.navigationItems;
|
||||||
shouldNotify = true;
|
shouldNotify = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class FlowyNavigation extends StatelessWidget {
|
|||||||
create: (_) {
|
create: (_) {
|
||||||
final notifier = Provider.of<HomeStackNotifier>(context, listen: false);
|
final notifier = Provider.of<HomeStackNotifier>(context, listen: false);
|
||||||
return NavigationNotifier(
|
return NavigationNotifier(
|
||||||
navigationItems: notifier.plugin.display.navigationItems,
|
navigationItems: notifier.plugin.widgetBuilder.navigationItems,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
update: (_, notifier, controller) => controller!..update(notifier),
|
update: (_, notifier, controller) => controller!..update(notifier),
|
||||||
|
@ -17,7 +17,6 @@ class ViewLeftBarItem extends StatefulWidget {
|
|||||||
class _ViewLeftBarItemState extends State<ViewLeftBarItem> {
|
class _ViewLeftBarItemState extends State<ViewLeftBarItem> {
|
||||||
final _controller = TextEditingController();
|
final _controller = TextEditingController();
|
||||||
final _focusNode = FocusNode();
|
final _focusNode = FocusNode();
|
||||||
late final ViewService _viewService;
|
|
||||||
late final ViewListener _viewListener;
|
late final ViewListener _viewListener;
|
||||||
late ViewPB view;
|
late ViewPB view;
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ class _ViewLeftBarItemState extends State<ViewLeftBarItem> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
view = widget.view;
|
view = widget.view;
|
||||||
_viewService = ViewService();
|
|
||||||
_focusNode.addListener(_handleFocusChanged);
|
_focusNode.addListener(_handleFocusChanged);
|
||||||
_viewListener = ViewListener(view: widget.view);
|
_viewListener = ViewListener(view: widget.view);
|
||||||
_viewListener.start(
|
_viewListener.start(
|
||||||
@ -86,7 +84,7 @@ class _ViewLeftBarItemState extends State<ViewLeftBarItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_controller.text != view.name) {
|
if (_controller.text != view.name) {
|
||||||
_viewService.updateView(viewId: view.id, name: _controller.text);
|
ViewBackendService.updateView(viewId: view.id, name: _controller.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@ import 'package:appflowy/plugins/database_view/application/row/row_data_controll
|
|||||||
import 'package:appflowy/plugins/database_view/board/board.dart';
|
import 'package:appflowy/plugins/database_view/board/board.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||||
|
|
||||||
@ -30,21 +29,16 @@ class AppFlowyBoardTest {
|
|||||||
Future<BoardTestContext> createTestBoard() async {
|
Future<BoardTestContext> createTestBoard() async {
|
||||||
final app = await unitTest.createTestApp();
|
final app = await unitTest.createTestApp();
|
||||||
final builder = BoardPluginBuilder();
|
final builder = BoardPluginBuilder();
|
||||||
return AppBackendService()
|
return ViewBackendService.createView(
|
||||||
.createView(
|
|
||||||
parentViewId: app.id,
|
parentViewId: app.id,
|
||||||
name: "Test Board",
|
name: "Test Board",
|
||||||
layoutType: builder.layoutType!,
|
layoutType: builder.layoutType!,
|
||||||
)
|
).then((result) {
|
||||||
.then((result) {
|
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(view) async {
|
(view) async {
|
||||||
final context = BoardTestContext(
|
final context = BoardTestContext(
|
||||||
view,
|
view,
|
||||||
DatabaseController(
|
DatabaseController(view: view),
|
||||||
view: view,
|
|
||||||
layoutType: DatabaseLayoutPB.Board,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
final result = await context._boardDataController.open();
|
final result = await context._boardDataController.open();
|
||||||
result.fold((l) => null, (r) => throw Exception(r));
|
result.fold((l) => null, (r) => throw Exception(r));
|
||||||
|
@ -2,7 +2,6 @@ import 'package:appflowy/plugins/database_view/application/filter/filter_service
|
|||||||
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pbenum.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/text_filter.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/text_filter.pb.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -55,7 +54,6 @@ void main() {
|
|||||||
final service = FilterBackendService(viewId: context.gridView.id);
|
final service = FilterBackendService(viewId: context.gridView.id);
|
||||||
final gridController = DatabaseController(
|
final gridController = DatabaseController(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
);
|
);
|
||||||
final gridBloc = GridBloc(
|
final gridBloc = GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
@ -80,7 +78,6 @@ void main() {
|
|||||||
final service = FilterBackendService(viewId: context.gridView.id);
|
final service = FilterBackendService(viewId: context.gridView.id);
|
||||||
final gridController = DatabaseController(
|
final gridController = DatabaseController(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
);
|
);
|
||||||
final gridBloc = GridBloc(
|
final gridBloc = GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
@ -126,7 +123,6 @@ void main() {
|
|||||||
final service = FilterBackendService(viewId: context.gridView.id);
|
final service = FilterBackendService(viewId: context.gridView.id);
|
||||||
final gridController = DatabaseController(
|
final gridController = DatabaseController(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
);
|
);
|
||||||
final gridBloc = GridBloc(
|
final gridBloc = GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
@ -148,7 +144,6 @@ void main() {
|
|||||||
final service = FilterBackendService(viewId: context.gridView.id);
|
final service = FilterBackendService(viewId: context.gridView.id);
|
||||||
final gridController = DatabaseController(
|
final gridController = DatabaseController(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
);
|
);
|
||||||
final gridBloc = GridBloc(
|
final gridBloc = GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
|
@ -1,28 +1,22 @@
|
|||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/grid.dart';
|
import 'package:appflowy/plugins/database_view/grid/grid.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
|
||||||
|
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
|
|
||||||
Future<GridTestContext> createTestFilterGrid(AppFlowyGridTest gridTest) async {
|
Future<GridTestContext> createTestFilterGrid(AppFlowyGridTest gridTest) async {
|
||||||
final app = await gridTest.unitTest.createTestApp();
|
final app = await gridTest.unitTest.createTestApp();
|
||||||
final builder = GridPluginBuilder();
|
final builder = GridPluginBuilder();
|
||||||
final context = await AppBackendService()
|
final context = await ViewBackendService.createView(
|
||||||
.createView(
|
|
||||||
parentViewId: app.id,
|
parentViewId: app.id,
|
||||||
name: "Filter Grid",
|
name: "Filter Grid",
|
||||||
layoutType: builder.layoutType!,
|
layoutType: builder.layoutType!,
|
||||||
)
|
).then((result) {
|
||||||
.then((result) {
|
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(view) async {
|
(view) async {
|
||||||
final context = GridTestContext(
|
final context = GridTestContext(
|
||||||
view,
|
view,
|
||||||
DatabaseController(
|
DatabaseController(view: view),
|
||||||
view: view,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
final result = await context.gridController.open();
|
final result = await context.gridController.open();
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bloc_test/bloc_test.dart';
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
@ -23,10 +22,7 @@ void main() {
|
|||||||
"create a row",
|
"create a row",
|
||||||
build: () => GridBloc(
|
build: () => GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
databaseController: DatabaseController(
|
databaseController: DatabaseController(view: context.gridView),
|
||||||
view: context.gridView,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
)..add(const GridEvent.initial()),
|
)..add(const GridEvent.initial()),
|
||||||
act: (bloc) => bloc.add(const GridEvent.createRow()),
|
act: (bloc) => bloc.add(const GridEvent.createRow()),
|
||||||
wait: const Duration(milliseconds: 300),
|
wait: const Duration(milliseconds: 300),
|
||||||
@ -39,10 +35,7 @@ void main() {
|
|||||||
"delete the last row",
|
"delete the last row",
|
||||||
build: () => GridBloc(
|
build: () => GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
databaseController: DatabaseController(
|
databaseController: DatabaseController(view: context.gridView),
|
||||||
view: context.gridView,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
)..add(const GridEvent.initial()),
|
)..add(const GridEvent.initial()),
|
||||||
act: (bloc) async {
|
act: (bloc) async {
|
||||||
await gridResponseFuture();
|
await gridResponseFuture();
|
||||||
@ -65,10 +58,7 @@ void main() {
|
|||||||
'reorder rows',
|
'reorder rows',
|
||||||
build: () => GridBloc(
|
build: () => GridBloc(
|
||||||
view: context.gridView,
|
view: context.gridView,
|
||||||
databaseController: DatabaseController(
|
databaseController: DatabaseController(view: context.gridView),
|
||||||
view: context.gridView,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
)..add(const GridEvent.initial()),
|
)..add(const GridEvent.initial()),
|
||||||
act: (bloc) async {
|
act: (bloc) async {
|
||||||
await gridResponseFuture();
|
await gridResponseFuture();
|
||||||
|
@ -9,9 +9,8 @@ import 'package:appflowy/plugins/database_view/application/row/row_data_controll
|
|||||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/grid.dart';
|
import 'package:appflowy/plugins/database_view/grid/grid.dart';
|
||||||
import 'package:appflowy/workspace/application/app/app_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||||
@ -170,21 +169,16 @@ class AppFlowyGridTest {
|
|||||||
Future<GridTestContext> createTestGrid() async {
|
Future<GridTestContext> createTestGrid() async {
|
||||||
final app = await unitTest.createTestApp();
|
final app = await unitTest.createTestApp();
|
||||||
final builder = GridPluginBuilder();
|
final builder = GridPluginBuilder();
|
||||||
final context = await AppBackendService()
|
final context = await ViewBackendService.createView(
|
||||||
.createView(
|
|
||||||
parentViewId: app.id,
|
parentViewId: app.id,
|
||||||
name: "Test Grid",
|
name: "Test Grid",
|
||||||
layoutType: builder.layoutType!,
|
layoutType: builder.layoutType!,
|
||||||
)
|
).then((result) {
|
||||||
.then((result) {
|
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(view) async {
|
(view) async {
|
||||||
final context = GridTestContext(
|
final context = GridTestContext(
|
||||||
view,
|
view,
|
||||||
DatabaseController(
|
DatabaseController(view: view),
|
||||||
view: view,
|
|
||||||
layoutType: DatabaseLayoutPB.Grid,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
final result = await context.gridController.open();
|
final result = await context.gridController.open();
|
||||||
result.fold((l) => null, (r) => throw Exception(r));
|
result.fold((l) => null, (r) => throw Exception(r));
|
||||||
|
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1024,7 +1024,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1042,7 +1042,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -1060,7 +1060,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1086,7 +1086,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1098,7 +1098,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1115,7 +1115,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1134,7 +1134,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1154,7 +1154,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1184,7 +1184,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bb6ab1#bb6ab1bada7b045e0edb2652017cd95795eb1309"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
|
@ -34,17 +34,17 @@ default = ["custom-protocol"]
|
|||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
|
|
||||||
#collab = { path = "../../AppFlowy-Collab/collab" }
|
#collab = { path = "../../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
||||||
#collab-document = { path = "../../AppFlowy-Collab/collab-document" }
|
#collab-document = { path = "../../AppFlowy-Collab/collab-document" }
|
||||||
#collab-database= { path = "../../AppFlowy-Collab/collab-database" }
|
#collab-database = { path = "../../AppFlowy-Collab/collab-database" }
|
||||||
#appflowy-integrate = { path = "../../AppFlowy-Collab/appflowy-integrate" }
|
#appflowy-integrate = { path = "../../AppFlowy-Collab/appflowy-integrate" }
|
||||||
|
|
||||||
|
|
||||||
|
20
frontend/rust-lib/Cargo.lock
generated
20
frontend/rust-lib/Cargo.lock
generated
@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -887,7 +887,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -905,7 +905,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -923,7 +923,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -949,7 +949,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -961,7 +961,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -978,7 +978,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -997,7 +997,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1017,7 +1017,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1047,7 +1047,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=26438c#26438c77de5d4cad723370380da19763da536ac1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
|
@ -33,11 +33,11 @@ opt-level = 3
|
|||||||
incremental = false
|
incremental = false
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "26438c" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" }
|
||||||
|
|
||||||
#collab = { path = "../AppFlowy-Collab/collab" }
|
#collab = { path = "../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
||||||
|
@ -18,7 +18,7 @@ use flowy_error::FlowyError;
|
|||||||
use flowy_folder2::deps::{FolderCloudService, FolderUser};
|
use flowy_folder2::deps::{FolderCloudService, FolderUser};
|
||||||
use flowy_folder2::entities::ViewLayoutPB;
|
use flowy_folder2::entities::ViewLayoutPB;
|
||||||
use flowy_folder2::manager::Folder2Manager;
|
use flowy_folder2::manager::Folder2Manager;
|
||||||
use flowy_folder2::view_ext::{FolderOperationHandler, FolderOperationHandlers};
|
use flowy_folder2::view_operation::{FolderOperationHandler, FolderOperationHandlers, View};
|
||||||
use flowy_folder2::ViewLayout;
|
use flowy_folder2::ViewLayout;
|
||||||
use flowy_user::services::UserSession;
|
use flowy_user::services::UserSession;
|
||||||
use lib_dispatch::prelude::ToBytes;
|
use lib_dispatch::prelude::ToBytes;
|
||||||
@ -111,7 +111,7 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
|||||||
_name: &str,
|
_name: &str,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
_ext: HashMap<String, String>,
|
_meta: HashMap<String, String>,
|
||||||
) -> FutureResult<(), FlowyError> {
|
) -> FutureResult<(), FlowyError> {
|
||||||
debug_assert_eq!(layout, ViewLayout::Document);
|
debug_assert_eq!(layout, ViewLayout::Document);
|
||||||
let view_id = view_id.to_string();
|
let view_id = view_id.to_string();
|
||||||
@ -130,7 +130,6 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
|||||||
view_id: &str,
|
view_id: &str,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
_ext: HashMap<String, String>,
|
|
||||||
) -> FutureResult<(), FlowyError> {
|
) -> FutureResult<(), FlowyError> {
|
||||||
debug_assert_eq!(layout, ViewLayout::Document);
|
debug_assert_eq!(layout, ViewLayout::Document);
|
||||||
|
|
||||||
@ -200,9 +199,9 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
|||||||
name: &str,
|
name: &str,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
ext: HashMap<String, String>,
|
meta: HashMap<String, String>,
|
||||||
) -> FutureResult<(), FlowyError> {
|
) -> FutureResult<(), FlowyError> {
|
||||||
match CreateDatabaseExtParams::from_map(ext) {
|
match CreateDatabaseExtParams::from_map(meta) {
|
||||||
None => {
|
None => {
|
||||||
let database_manager = self.0.clone();
|
let database_manager = self.0.clone();
|
||||||
let view_id = view_id.to_string();
|
let view_id = view_id.to_string();
|
||||||
@ -217,17 +216,11 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
|||||||
let database_manager = self.0.clone();
|
let database_manager = self.0.clone();
|
||||||
let layout = layout_type_from_view_layout(layout.into());
|
let layout = layout_type_from_view_layout(layout.into());
|
||||||
let name = name.to_string();
|
let name = name.to_string();
|
||||||
let target_view_id = view_id.to_string();
|
let database_view_id = view_id.to_string();
|
||||||
|
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
database_manager
|
database_manager
|
||||||
.create_linked_view(
|
.create_linked_view(name, layout, params.database_id, database_view_id)
|
||||||
name,
|
|
||||||
layout,
|
|
||||||
params.database_id,
|
|
||||||
target_view_id,
|
|
||||||
params.duplicated_view_id,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -245,7 +238,6 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
|||||||
view_id: &str,
|
view_id: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
_meta: HashMap<String, String>,
|
|
||||||
) -> FutureResult<(), FlowyError> {
|
) -> FutureResult<(), FlowyError> {
|
||||||
let name = name.to_string();
|
let name = name.to_string();
|
||||||
let database_manager = self.0.clone();
|
let database_manager = self.0.clone();
|
||||||
@ -296,12 +288,37 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn did_update_view(&self, old: &View, new: &View) -> FutureResult<(), FlowyError> {
|
||||||
|
let database_layout = match new.layout {
|
||||||
|
ViewLayout::Document => {
|
||||||
|
return FutureResult::new(async {
|
||||||
|
Err(FlowyError::internal().context("Can't handle document layout type"))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ViewLayout::Grid => DatabaseLayoutPB::Grid,
|
||||||
|
ViewLayout::Board => DatabaseLayoutPB::Board,
|
||||||
|
ViewLayout::Calendar => DatabaseLayoutPB::Calendar,
|
||||||
|
};
|
||||||
|
|
||||||
|
let database_manager = self.0.clone();
|
||||||
|
let view_id = new.id.clone();
|
||||||
|
if old.layout != new.layout {
|
||||||
|
FutureResult::new(async move {
|
||||||
|
database_manager
|
||||||
|
.update_database_layout(&view_id, database_layout)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
FutureResult::new(async move { Ok(()) })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
struct CreateDatabaseExtParams {
|
struct CreateDatabaseExtParams {
|
||||||
database_id: String,
|
database_id: String,
|
||||||
duplicated_view_id: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateDatabaseExtParams {
|
impl CreateDatabaseExtParams {
|
||||||
|
@ -134,3 +134,18 @@ pub struct MoveCalendarEventPB {
|
|||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||||
|
pub struct NoDateCalendarEventPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub row_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||||
|
pub struct RepeatedNoDateCalendarEventPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub items: Vec<NoDateCalendarEventPB>,
|
||||||
|
}
|
||||||
|
@ -3,10 +3,11 @@ use collab_database::user::DatabaseRecord;
|
|||||||
use collab_database::views::DatabaseLayout;
|
use collab_database::views::DatabaseLayout;
|
||||||
|
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use flowy_error::ErrorCode;
|
use flowy_error::{ErrorCode, FlowyError};
|
||||||
|
|
||||||
use crate::entities::parser::NotEmptyStr;
|
use crate::entities::parser::NotEmptyStr;
|
||||||
use crate::entities::{DatabaseLayoutPB, FieldIdPB, RowPB};
|
use crate::entities::{DatabaseLayoutPB, FieldIdPB, RowPB};
|
||||||
|
use crate::services::database::CreateDatabaseViewParams;
|
||||||
|
|
||||||
/// [DatabasePB] describes how many fields and blocks the grid has
|
/// [DatabasePB] describes how many fields and blocks the grid has
|
||||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||||
@ -19,12 +20,34 @@ pub struct DatabasePB {
|
|||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub rows: Vec<RowPB>,
|
pub rows: Vec<RowPB>,
|
||||||
|
|
||||||
|
#[pb(index = 4)]
|
||||||
|
pub layout_type: DatabaseLayoutPB,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
#[derive(ProtoBuf, Default)]
|
||||||
pub struct CreateDatabasePayloadPB {
|
pub struct CreateDatabaseViewPayloadPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub view_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
|
pub layout_type: DatabaseLayoutPB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<CreateDatabaseViewParams> for CreateDatabaseViewPayloadPB {
|
||||||
|
type Error = FlowyError;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<CreateDatabaseViewParams, Self::Error> {
|
||||||
|
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
|
||||||
|
Ok(CreateDatabaseViewParams {
|
||||||
|
name: self.name,
|
||||||
|
view_id: view_id.0,
|
||||||
|
layout_type: self.layout_type.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, ProtoBuf, Default, Debug)]
|
#[derive(Clone, ProtoBuf, Default, Debug)]
|
||||||
@ -198,7 +221,7 @@ impl TryInto<DatabaseGroupIdParams> for DatabaseGroupIdPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, ProtoBuf, Default, Debug)]
|
#[derive(Clone, ProtoBuf, Default, Debug)]
|
||||||
pub struct DatabaseLayoutIdPB {
|
pub struct DatabaseLayoutMetaPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
|
||||||
@ -207,18 +230,18 @@ pub struct DatabaseLayoutIdPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DatabaseLayoutId {
|
pub struct DatabaseLayoutMeta {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub layout: DatabaseLayout,
|
pub layout: DatabaseLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<DatabaseLayoutId> for DatabaseLayoutIdPB {
|
impl TryInto<DatabaseLayoutMeta> for DatabaseLayoutMetaPB {
|
||||||
type Error = ErrorCode;
|
type Error = ErrorCode;
|
||||||
|
|
||||||
fn try_into(self) -> Result<DatabaseLayoutId, Self::Error> {
|
fn try_into(self) -> Result<DatabaseLayoutMeta, Self::Error> {
|
||||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
|
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
|
||||||
let layout = self.layout.into();
|
let layout = self.layout.into();
|
||||||
Ok(DatabaseLayoutId {
|
Ok(DatabaseLayoutMeta {
|
||||||
view_id: view_id.0,
|
view_id: view_id.0,
|
||||||
layout,
|
layout,
|
||||||
})
|
})
|
||||||
|
@ -121,7 +121,7 @@ pub struct UpdatedRowPB {
|
|||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub row: RowPB,
|
pub row: RowPB,
|
||||||
|
|
||||||
// represents as the cells that were updated in this row.
|
// Indicates the field ids of the cells that were updated in this row.
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub field_ids: Vec<String>,
|
pub field_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ use crate::services::setting::CalendarLayoutSetting;
|
|||||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||||
pub struct DatabaseViewSettingPB {
|
pub struct DatabaseViewSettingPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub current_layout: DatabaseLayoutPB,
|
pub layout_type: DatabaseLayoutPB,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub layout_setting: LayoutSettingPB,
|
pub layout_setting: DatabaseLayoutSettingPB,
|
||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub filters: RepeatedFilterPB,
|
pub filters: RepeatedFilterPB,
|
||||||
@ -72,8 +72,8 @@ pub struct DatabaseSettingChangesetPB {
|
|||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2, one_of)]
|
||||||
pub layout_type: DatabaseLayoutPB,
|
pub layout_type: Option<DatabaseLayoutPB>,
|
||||||
|
|
||||||
#[pb(index = 3, one_of)]
|
#[pb(index = 3, one_of)]
|
||||||
pub update_filter: Option<UpdateFilterPayloadPB>,
|
pub update_filter: Option<UpdateFilterPayloadPB>,
|
||||||
@ -121,7 +121,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
|
|||||||
|
|
||||||
Ok(DatabaseSettingChangesetParams {
|
Ok(DatabaseSettingChangesetParams {
|
||||||
view_id,
|
view_id,
|
||||||
layout_type: self.layout_type.into(),
|
layout_type: self.layout_type.map(|ty| ty.into()),
|
||||||
insert_filter,
|
insert_filter,
|
||||||
delete_filter,
|
delete_filter,
|
||||||
alert_sort,
|
alert_sort,
|
||||||
@ -132,7 +132,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
|
|||||||
|
|
||||||
pub struct DatabaseSettingChangesetParams {
|
pub struct DatabaseSettingChangesetParams {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub layout_type: DatabaseLayout,
|
pub layout_type: Option<DatabaseLayout>,
|
||||||
pub insert_filter: Option<UpdateFilterParams>,
|
pub insert_filter: Option<UpdateFilterParams>,
|
||||||
pub delete_filter: Option<DeleteFilterParams>,
|
pub delete_filter: Option<DeleteFilterParams>,
|
||||||
pub alert_sort: Option<UpdateSortParams>,
|
pub alert_sort: Option<UpdateSortParams>,
|
||||||
@ -146,19 +146,24 @@ impl DatabaseSettingChangesetParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Default, ProtoBuf, Clone)]
|
#[derive(Debug, Eq, PartialEq, Default, ProtoBuf, Clone)]
|
||||||
pub struct LayoutSettingPB {
|
pub struct DatabaseLayoutSettingPB {
|
||||||
#[pb(index = 1, one_of)]
|
#[pb(index = 1)]
|
||||||
|
pub layout_type: DatabaseLayoutPB,
|
||||||
|
|
||||||
|
#[pb(index = 2, one_of)]
|
||||||
pub calendar: Option<CalendarLayoutSettingPB>,
|
pub calendar: Option<CalendarLayoutSettingPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct LayoutSettingParams {
|
pub struct LayoutSettingParams {
|
||||||
|
pub layout_type: DatabaseLayout,
|
||||||
pub calendar: Option<CalendarLayoutSetting>,
|
pub calendar: Option<CalendarLayoutSetting>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LayoutSettingParams> for LayoutSettingPB {
|
impl From<LayoutSettingParams> for DatabaseLayoutSettingPB {
|
||||||
fn from(data: LayoutSettingParams) -> Self {
|
fn from(data: LayoutSettingParams) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
layout_type: data.layout_type.into(),
|
||||||
calendar: data.calendar.map(|calendar| calendar.into()),
|
calendar: data.calendar.map(|calendar| calendar.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,13 +174,17 @@ pub struct LayoutSettingChangesetPB {
|
|||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
|
||||||
#[pb(index = 2, one_of)]
|
#[pb(index = 2)]
|
||||||
|
pub layout_type: DatabaseLayoutPB,
|
||||||
|
|
||||||
|
#[pb(index = 3, one_of)]
|
||||||
pub calendar: Option<CalendarLayoutSettingPB>,
|
pub calendar: Option<CalendarLayoutSettingPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LayoutSettingChangeset {
|
pub struct LayoutSettingChangeset {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
|
pub layout_type: DatabaseLayout,
|
||||||
pub calendar: Option<CalendarLayoutSetting>,
|
pub calendar: Option<CalendarLayoutSetting>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +198,7 @@ impl TryInto<LayoutSettingChangeset> for LayoutSettingChangesetPB {
|
|||||||
|
|
||||||
Ok(LayoutSettingChangeset {
|
Ok(LayoutSettingChangeset {
|
||||||
view_id,
|
view_id,
|
||||||
|
layout_type: self.layout_type.into(),
|
||||||
calendar: self.calendar.map(|calendar| calendar.into()),
|
calendar: self.calendar.map(|calendar| calendar.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ use collab_database::database::gen_row_id;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
use collab_database::views::DatabaseLayout;
|
|
||||||
use lib_infra::util::timestamp;
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
@ -25,7 +24,7 @@ pub(crate) async fn get_database_data_handler(
|
|||||||
) -> DataResult<DatabasePB, FlowyError> {
|
) -> DataResult<DatabasePB, FlowyError> {
|
||||||
let view_id: DatabaseViewIdPB = data.into_inner();
|
let view_id: DatabaseViewIdPB = data.into_inner();
|
||||||
let database_editor = manager.get_database_with_view_id(view_id.as_ref()).await?;
|
let database_editor = manager.get_database_with_view_id(view_id.as_ref()).await?;
|
||||||
let data = database_editor.get_database_data(view_id.as_ref()).await;
|
let data = database_editor.get_database_data(view_id.as_ref()).await?;
|
||||||
data_result_ok(data)
|
data_result_ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +63,12 @@ pub(crate) async fn update_database_setting_handler(
|
|||||||
if let Some(delete_sort) = params.delete_sort {
|
if let Some(delete_sort) = params.delete_sort {
|
||||||
editor.delete_sort(delete_sort).await?;
|
editor.delete_sort(delete_sort).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(layout_type) = params.layout_type {
|
||||||
|
editor
|
||||||
|
.update_view_layout(¶ms.view_id, layout_type)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,24 +631,25 @@ pub(crate) async fn set_layout_setting_handler(
|
|||||||
let params: LayoutSettingChangeset = data.into_inner().try_into()?;
|
let params: LayoutSettingChangeset = data.into_inner().try_into()?;
|
||||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
let layout_params = LayoutSettingParams {
|
let layout_params = LayoutSettingParams {
|
||||||
|
layout_type: params.layout_type,
|
||||||
calendar: params.calendar,
|
calendar: params.calendar,
|
||||||
};
|
};
|
||||||
database_editor
|
database_editor
|
||||||
.set_layout_setting(¶ms.view_id, DatabaseLayout::Calendar, layout_params)
|
.set_layout_setting(¶ms.view_id, layout_params)
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_layout_setting_handler(
|
pub(crate) async fn get_layout_setting_handler(
|
||||||
data: AFPluginData<DatabaseLayoutIdPB>,
|
data: AFPluginData<DatabaseLayoutMetaPB>,
|
||||||
manager: AFPluginState<Arc<DatabaseManager2>>,
|
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||||
) -> DataResult<LayoutSettingPB, FlowyError> {
|
) -> DataResult<DatabaseLayoutSettingPB, FlowyError> {
|
||||||
let params: DatabaseLayoutId = data.into_inner().try_into()?;
|
let params: DatabaseLayoutMeta = data.into_inner().try_into()?;
|
||||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
let layout_setting_pb = database_editor
|
let layout_setting_pb = database_editor
|
||||||
.get_layout_setting(¶ms.view_id, params.layout)
|
.get_layout_setting(¶ms.view_id, params.layout)
|
||||||
.await
|
.await
|
||||||
.map(LayoutSettingPB::from)
|
.map(DatabaseLayoutSettingPB::from)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
data_result_ok(layout_setting_pb)
|
data_result_ok(layout_setting_pb)
|
||||||
}
|
}
|
||||||
@ -661,6 +667,19 @@ pub(crate) async fn get_calendar_events_handler(
|
|||||||
data_result_ok(RepeatedCalendarEventPB { items: events })
|
data_result_ok(RepeatedCalendarEventPB { items: events })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
|
pub(crate) async fn get_no_date_calendar_events_handler(
|
||||||
|
data: AFPluginData<CalendarEventRequestPB>,
|
||||||
|
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||||
|
) -> DataResult<RepeatedNoDateCalendarEventPB, FlowyError> {
|
||||||
|
let params: CalendarEventRequestParams = data.into_inner().try_into()?;
|
||||||
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
|
let _events = database_editor
|
||||||
|
.get_all_no_date_calendar_events(¶ms.view_id)
|
||||||
|
.await;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
pub(crate) async fn get_calendar_event_handler(
|
pub(crate) async fn get_calendar_event_handler(
|
||||||
data: AFPluginData<RowIdPB>,
|
data: AFPluginData<RowIdPB>,
|
||||||
@ -699,3 +718,12 @@ pub(crate) async fn move_calendar_event_handler(
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
|
pub(crate) async fn create_database_view(
|
||||||
|
_data: AFPluginData<CreateDatabaseViewPayloadPB>,
|
||||||
|
_manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
// let data: CreateDatabaseViewParams = data.into_inner().try_into()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -60,12 +60,13 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
|
|||||||
.event(DatabaseEvent::GetDatabases, get_databases_handler)
|
.event(DatabaseEvent::GetDatabases, get_databases_handler)
|
||||||
// Calendar
|
// Calendar
|
||||||
.event(DatabaseEvent::GetAllCalendarEvents, get_calendar_events_handler)
|
.event(DatabaseEvent::GetAllCalendarEvents, get_calendar_events_handler)
|
||||||
|
.event(DatabaseEvent::GetNoDateCalendarEvents, get_no_date_calendar_events_handler)
|
||||||
.event(DatabaseEvent::GetCalendarEvent, get_calendar_event_handler)
|
.event(DatabaseEvent::GetCalendarEvent, get_calendar_event_handler)
|
||||||
.event(DatabaseEvent::MoveCalendarEvent, move_calendar_event_handler)
|
.event(DatabaseEvent::MoveCalendarEvent, move_calendar_event_handler)
|
||||||
// Layout setting
|
// Layout setting
|
||||||
.event(DatabaseEvent::SetLayoutSetting, set_layout_setting_handler)
|
.event(DatabaseEvent::SetLayoutSetting, set_layout_setting_handler)
|
||||||
.event(DatabaseEvent::GetLayoutSetting, get_layout_setting_handler)
|
.event(DatabaseEvent::GetLayoutSetting, get_layout_setting_handler)
|
||||||
// import
|
.event(DatabaseEvent::CreateDatabaseView, create_database_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [DatabaseEvent] defines events that are used to interact with the Grid. You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/protobuf)
|
/// [DatabaseEvent] defines events that are used to interact with the Grid. You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/protobuf)
|
||||||
@ -265,15 +266,24 @@ pub enum DatabaseEvent {
|
|||||||
#[event(input = "LayoutSettingChangesetPB")]
|
#[event(input = "LayoutSettingChangesetPB")]
|
||||||
SetLayoutSetting = 121,
|
SetLayoutSetting = 121,
|
||||||
|
|
||||||
#[event(input = "DatabaseLayoutIdPB", output = "LayoutSettingPB")]
|
#[event(input = "DatabaseLayoutMetaPB", output = "DatabaseLayoutSettingPB")]
|
||||||
GetLayoutSetting = 122,
|
GetLayoutSetting = 122,
|
||||||
|
|
||||||
#[event(input = "CalendarEventRequestPB", output = "RepeatedCalendarEventPB")]
|
#[event(input = "CalendarEventRequestPB", output = "RepeatedCalendarEventPB")]
|
||||||
GetAllCalendarEvents = 123,
|
GetAllCalendarEvents = 123,
|
||||||
|
|
||||||
|
#[event(
|
||||||
|
input = "CalendarEventRequestPB",
|
||||||
|
output = "RepeatedNoDateCalendarEventPB"
|
||||||
|
)]
|
||||||
|
GetNoDateCalendarEvents = 124,
|
||||||
|
|
||||||
#[event(input = "RowIdPB", output = "CalendarEventPB")]
|
#[event(input = "RowIdPB", output = "CalendarEventPB")]
|
||||||
GetCalendarEvent = 124,
|
GetCalendarEvent = 125,
|
||||||
|
|
||||||
#[event(input = "MoveCalendarEventPB")]
|
#[event(input = "MoveCalendarEventPB")]
|
||||||
MoveCalendarEvent = 125,
|
MoveCalendarEvent = 126,
|
||||||
|
|
||||||
|
#[event(input = "CreateDatabaseViewPayloadPB")]
|
||||||
|
CreateDatabaseView = 130,
|
||||||
}
|
}
|
||||||
|
@ -171,8 +171,7 @@ impl DatabaseManager2 {
|
|||||||
name: String,
|
name: String,
|
||||||
layout: DatabaseLayoutPB,
|
layout: DatabaseLayoutPB,
|
||||||
database_id: String,
|
database_id: String,
|
||||||
target_view_id: String,
|
database_view_id: String,
|
||||||
duplicated_view_id: Option<String>,
|
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
self.with_user_database(
|
self.with_user_database(
|
||||||
Err(FlowyError::internal().context("Create database view failed")),
|
Err(FlowyError::internal().context("Create database view failed")),
|
||||||
@ -180,15 +179,8 @@ impl DatabaseManager2 {
|
|||||||
let database = user_database
|
let database = user_database
|
||||||
.get_database(&database_id)
|
.get_database(&database_id)
|
||||||
.ok_or_else(FlowyError::record_not_found)?;
|
.ok_or_else(FlowyError::record_not_found)?;
|
||||||
match duplicated_view_id {
|
let params = CreateViewParams::new(database_id, database_view_id, name, layout.into());
|
||||||
None => {
|
database.create_linked_view(params)?;
|
||||||
let params = CreateViewParams::new(database_id, target_view_id, name, layout.into());
|
|
||||||
database.create_linked_view(params)?;
|
|
||||||
},
|
|
||||||
Some(duplicated_view_id) => {
|
|
||||||
database.duplicate_linked_view(&duplicated_view_id);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@ -228,6 +220,15 @@ impl DatabaseManager2 {
|
|||||||
database.export_csv(style).await
|
database.export_csv(style).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_database_layout(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
layout: DatabaseLayoutPB,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
let database = self.get_database_with_view_id(view_id).await?;
|
||||||
|
database.update_view_layout(view_id, layout.into()).await
|
||||||
|
}
|
||||||
|
|
||||||
fn with_user_database<F, Output>(&self, default_value: Output, f: F) -> Output
|
fn with_user_database<F, Output>(&self, default_value: Output, f: F) -> Output
|
||||||
where
|
where
|
||||||
F: FnOnce(&InnerUserDatabase) -> Output,
|
F: FnOnce(&InnerUserDatabase) -> Output,
|
||||||
|
@ -35,6 +35,8 @@ pub enum DatabaseNotification {
|
|||||||
DidUpdateLayoutSettings = 80,
|
DidUpdateLayoutSettings = 80,
|
||||||
// Trigger when the layout field of the database is changed
|
// Trigger when the layout field of the database is changed
|
||||||
DidSetNewLayoutField = 81,
|
DidSetNewLayoutField = 81,
|
||||||
|
// Trigger when the layout of the database is changed
|
||||||
|
DidUpdateDatabaseLayout = 82,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for DatabaseNotification {
|
impl std::default::Default for DatabaseNotification {
|
||||||
|
@ -18,8 +18,9 @@ use crate::entities::{
|
|||||||
CalendarEventPB, CellChangesetNotifyPB, CellPB, ChecklistCellDataPB, DatabaseFieldChangesetPB,
|
CalendarEventPB, CellChangesetNotifyPB, CellPB, ChecklistCellDataPB, DatabaseFieldChangesetPB,
|
||||||
DatabasePB, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
|
DatabasePB, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
|
||||||
FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
|
FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
|
||||||
LayoutSettingParams, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB,
|
LayoutSettingParams, NoDateCalendarEventPB, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB,
|
||||||
SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams, UpdateSortParams,
|
RowPB, RowsChangePB, SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams,
|
||||||
|
UpdateSortParams, UpdatedRowPB,
|
||||||
};
|
};
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
use crate::services::cell::{
|
use crate::services::cell::{
|
||||||
@ -38,6 +39,7 @@ use crate::services::filter::Filter;
|
|||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
default_group_setting, GroupSetting, GroupSettingChangeset, RowChangeset,
|
default_group_setting, GroupSetting, GroupSettingChangeset, RowChangeset,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::services::share::csv::{CSVExport, CSVFormat};
|
use crate::services::share::csv::{CSVExport, CSVFormat};
|
||||||
use crate::services::sort::Sort;
|
use crate::services::sort::Sort;
|
||||||
|
|
||||||
@ -76,6 +78,17 @@ impl DatabaseEditor {
|
|||||||
|
|
||||||
pub async fn close(&self) {}
|
pub async fn close(&self) {}
|
||||||
|
|
||||||
|
pub async fn update_view_layout(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
layout_type: DatabaseLayout,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
let view_editor = self.database_views.get_view_editor(view_id).await?;
|
||||||
|
view_editor.v_update_layout_type(layout_type).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn subscribe_view_changed(
|
pub async fn subscribe_view_changed(
|
||||||
&self,
|
&self,
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
@ -442,7 +455,7 @@ impl DatabaseEditor {
|
|||||||
let (field, cell) = {
|
let (field, cell) = {
|
||||||
let database = self.database.lock();
|
let database = self.database.lock();
|
||||||
let field = database.fields.get_field(field_id);
|
let field = database.fields.get_field(field_id);
|
||||||
let cell = database.get_cell(field_id, &row_id);
|
let cell = database.get_cell(field_id, &row_id).cell;
|
||||||
(field, cell)
|
(field, cell)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -484,12 +497,7 @@ impl DatabaseEditor {
|
|||||||
Err(FlowyError::internal().context(msg))
|
Err(FlowyError::internal().context(msg))
|
||||||
},
|
},
|
||||||
}?;
|
}?;
|
||||||
(
|
(field, database.get_cell(field_id, &row_id).cell)
|
||||||
field,
|
|
||||||
database
|
|
||||||
.get_cell(field_id, &row_id)
|
|
||||||
.map(|row_cell| row_cell.cell),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let new_cell =
|
let new_cell =
|
||||||
apply_cell_changeset(cell_changeset, cell, &field, Some(self.cell_cache.clone()))?;
|
apply_cell_changeset(cell_changeset, cell, &field, Some(self.cell_cache.clone()))?;
|
||||||
@ -529,6 +537,15 @@ impl DatabaseEditor {
|
|||||||
|
|
||||||
let option_row = self.database.lock().get_row(&row_id);
|
let option_row = self.database.lock().get_row(&row_id);
|
||||||
if let Some(new_row) = option_row {
|
if let Some(new_row) = option_row {
|
||||||
|
let updated_row = UpdatedRowPB {
|
||||||
|
row: RowPB::from(&new_row),
|
||||||
|
field_ids: vec![field_id.to_string()],
|
||||||
|
};
|
||||||
|
let changes = RowsChangePB::from_update(view_id.to_string(), updated_row);
|
||||||
|
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
|
||||||
|
.payload(changes)
|
||||||
|
.send();
|
||||||
|
|
||||||
for view in self.database_views.editors().await {
|
for view in self.database_views.editors().await {
|
||||||
view.v_did_update_row(&old_row, &new_row, field_id).await;
|
view.v_did_update_row(&old_row, &new_row, field_id).await;
|
||||||
}
|
}
|
||||||
@ -646,10 +663,10 @@ impl DatabaseEditor {
|
|||||||
match field {
|
match field {
|
||||||
None => SelectOptionCellDataPB::default(),
|
None => SelectOptionCellDataPB::default(),
|
||||||
Some(field) => {
|
Some(field) => {
|
||||||
let row_cell = self.database.lock().get_cell(field_id, &row_id);
|
let cell = self.database.lock().get_cell(field_id, &row_id).cell;
|
||||||
let ids = match row_cell {
|
let ids = match cell {
|
||||||
None => SelectOptionIds::new(),
|
None => SelectOptionIds::new(),
|
||||||
Some(row_cell) => SelectOptionIds::from(&row_cell.cell),
|
Some(cell) => SelectOptionIds::from(&cell),
|
||||||
};
|
};
|
||||||
match select_type_option_from_field(&field) {
|
match select_type_option_from_field(&field) {
|
||||||
Ok(type_option) => type_option.get_selected_options(ids).into(),
|
Ok(type_option) => type_option.get_selected_options(ids).into(),
|
||||||
@ -661,9 +678,9 @@ impl DatabaseEditor {
|
|||||||
|
|
||||||
pub async fn get_checklist_option(&self, row_id: RowId, field_id: &str) -> ChecklistCellDataPB {
|
pub async fn get_checklist_option(&self, row_id: RowId, field_id: &str) -> ChecklistCellDataPB {
|
||||||
let row_cell = self.database.lock().get_cell(field_id, &row_id);
|
let row_cell = self.database.lock().get_cell(field_id, &row_id);
|
||||||
let cell_data = match row_cell {
|
let cell_data = match row_cell.cell {
|
||||||
None => ChecklistCellData::default(),
|
None => ChecklistCellData::default(),
|
||||||
Some(row_cell) => ChecklistCellData::from(&row_cell.cell),
|
Some(cell) => ChecklistCellData::from(&cell),
|
||||||
};
|
};
|
||||||
ChecklistCellDataPB::from(cell_data)
|
ChecklistCellDataPB::from(cell_data)
|
||||||
}
|
}
|
||||||
@ -763,14 +780,9 @@ impl DatabaseEditor {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_layout_setting(
|
pub async fn set_layout_setting(&self, view_id: &str, layout_setting: LayoutSettingParams) {
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
layout_ty: DatabaseLayout,
|
|
||||||
layout_setting: LayoutSettingParams,
|
|
||||||
) {
|
|
||||||
if let Ok(view) = self.database_views.get_view_editor(view_id).await {
|
if let Ok(view) = self.database_views.get_view_editor(view_id).await {
|
||||||
let _ = view.v_set_layout_settings(&layout_ty, layout_setting).await;
|
let _ = view.v_set_layout_settings(layout_setting).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,6 +807,15 @@ impl DatabaseEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
|
pub async fn get_all_no_date_calendar_events(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
) -> FlowyResult<Vec<NoDateCalendarEventPB>> {
|
||||||
|
let _database_view = self.database_views.get_view_editor(view_id).await?;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub async fn get_calendar_event(&self, view_id: &str, row_id: RowId) -> Option<CalendarEventPB> {
|
pub async fn get_calendar_event(&self, view_id: &str, row_id: RowId) -> Option<CalendarEventPB> {
|
||||||
let view = self.database_views.get_view_editor(view_id).await.ok()?;
|
let view = self.database_views.get_view_editor(view_id).await.ok()?;
|
||||||
@ -858,8 +879,13 @@ impl DatabaseEditor {
|
|||||||
Ok(database_view_setting_pb_from_view(view))
|
Ok(database_view_setting_pb_from_view(view))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_database_data(&self, view_id: &str) -> DatabasePB {
|
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> {
|
||||||
let rows = self.get_rows(view_id).await.unwrap_or_default();
|
let database_view = self.database_views.get_view_editor(view_id).await?;
|
||||||
|
let view = database_view
|
||||||
|
.get_view()
|
||||||
|
.await
|
||||||
|
.ok_or(FlowyError::record_not_found())?;
|
||||||
|
let rows = database_view.v_get_rows().await;
|
||||||
let (database_id, fields) = {
|
let (database_id, fields) = {
|
||||||
let database = self.database.lock();
|
let database = self.database.lock();
|
||||||
let database_id = database.get_database_id();
|
let database_id = database.get_database_id();
|
||||||
@ -876,11 +902,12 @@ impl DatabaseEditor {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row| RowPB::from(row.as_ref()))
|
.map(|row| RowPB::from(row.as_ref()))
|
||||||
.collect::<Vec<RowPB>>();
|
.collect::<Vec<RowPB>>();
|
||||||
DatabasePB {
|
Ok(DatabasePB {
|
||||||
id: database_id,
|
id: database_id,
|
||||||
fields,
|
fields,
|
||||||
rows,
|
rows,
|
||||||
}
|
layout_type: view.layout.into(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn export_csv(&self, style: CSVFormat) -> FlowyResult<String> {
|
pub async fn export_csv(&self, style: CSVFormat) -> FlowyResult<String> {
|
||||||
@ -946,7 +973,7 @@ struct DatabaseViewDataImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseViewData for DatabaseViewDataImpl {
|
impl DatabaseViewData for DatabaseViewDataImpl {
|
||||||
fn get_view_setting(&self, view_id: &str) -> Fut<Option<DatabaseView>> {
|
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>> {
|
||||||
let view = self.database.lock().get_view(view_id);
|
let view = self.database.lock().get_view(view_id);
|
||||||
to_fut(async move { view })
|
to_fut(async move { view })
|
||||||
}
|
}
|
||||||
@ -966,6 +993,26 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
to_fut(async move { field })
|
to_fut(async move { field })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_field(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
name: &str,
|
||||||
|
field_type: FieldType,
|
||||||
|
type_option_data: TypeOptionData,
|
||||||
|
) -> Fut<Field> {
|
||||||
|
let (_, field) = self.database.lock().create_default_field(
|
||||||
|
view_id,
|
||||||
|
name.to_string(),
|
||||||
|
field_type.clone().into(),
|
||||||
|
|field| {
|
||||||
|
field
|
||||||
|
.type_options
|
||||||
|
.insert(field_type.to_string(), type_option_data);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
to_fut(async move { field })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>> {
|
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>> {
|
||||||
let field = self
|
let field = self
|
||||||
.database
|
.database
|
||||||
@ -1002,18 +1049,13 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
to_fut(async move { cells.into_iter().map(Arc::new).collect() })
|
to_fut(async move { cells.into_iter().map(Arc::new).collect() })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Option<Arc<RowCell>>> {
|
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>> {
|
||||||
let cell = self.database.lock().get_cell(field_id, row_id);
|
let cell = self.database.lock().get_cell(field_id, row_id);
|
||||||
to_fut(async move { cell.map(Arc::new) })
|
to_fut(async move { Arc::new(cell) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout {
|
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout {
|
||||||
self
|
self.database.lock().views.get_database_view_layout(view_id)
|
||||||
.database
|
|
||||||
.lock()
|
|
||||||
.views
|
|
||||||
.get_view_layout(view_id)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_group_setting(&self, view_id: &str) -> Vec<GroupSetting> {
|
fn get_group_setting(&self, view_id: &str) -> Vec<GroupSetting> {
|
||||||
@ -1077,11 +1119,7 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option<LayoutSetting> {
|
fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option<LayoutSetting> {
|
||||||
self
|
self.database.lock().get_layout_setting(view_id, layout_ty)
|
||||||
.database
|
|
||||||
.lock()
|
|
||||||
.views
|
|
||||||
.get_layout_setting(view_id, layout_ty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_layout_setting(
|
fn insert_layout_setting(
|
||||||
@ -1096,6 +1134,17 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
.insert_layout_setting(view_id, layout_ty, layout_setting);
|
.insert_layout_setting(view_id, layout_ty, layout_setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_layout_type(&self, view_id: &str) -> DatabaseLayout {
|
||||||
|
self.database.lock().views.get_database_view_layout(view_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout) {
|
||||||
|
self
|
||||||
|
.database
|
||||||
|
.lock()
|
||||||
|
.update_layout_type(view_id, layout_type);
|
||||||
|
}
|
||||||
|
|
||||||
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>> {
|
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>> {
|
||||||
self.task_scheduler.clone()
|
self.task_scheduler.clone()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
use collab_database::views::RowOrder;
|
use collab_database::views::{DatabaseLayout, RowOrder};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DatabaseRowEvent {
|
pub enum DatabaseRowEvent {
|
||||||
@ -25,3 +25,10 @@ pub struct UpdatedRow {
|
|||||||
// represents as the cells that were updated in this row.
|
// represents as the cells that were updated in this row.
|
||||||
pub field_ids: Vec<String>,
|
pub field_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CreateDatabaseViewParams {
|
||||||
|
pub name: String,
|
||||||
|
pub view_id: String,
|
||||||
|
pub layout_type: DatabaseLayout,
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseViewSettingPB, FilterPB, GroupSettingPB,
|
CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseLayoutSettingPB, DatabaseViewSettingPB,
|
||||||
LayoutSettingPB, SortPB,
|
FilterPB, GroupSettingPB, SortPB,
|
||||||
};
|
};
|
||||||
use crate::services::filter::Filter;
|
use crate::services::filter::Filter;
|
||||||
use crate::services::group::GroupSetting;
|
use crate::services::group::GroupSetting;
|
||||||
@ -9,17 +9,18 @@ use crate::services::sort::Sort;
|
|||||||
use collab_database::views::DatabaseView;
|
use collab_database::views::DatabaseView;
|
||||||
|
|
||||||
pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> DatabaseViewSettingPB {
|
pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> DatabaseViewSettingPB {
|
||||||
|
let layout_type: DatabaseLayoutPB = view.layout.clone().into();
|
||||||
let layout_setting = if let Some(layout_setting) = view.layout_settings.get(&view.layout) {
|
let layout_setting = if let Some(layout_setting) = view.layout_settings.get(&view.layout) {
|
||||||
let calendar_setting =
|
let calendar_setting =
|
||||||
CalendarLayoutSettingPB::from(CalendarLayoutSetting::from(layout_setting.clone()));
|
CalendarLayoutSettingPB::from(CalendarLayoutSetting::from(layout_setting.clone()));
|
||||||
LayoutSettingPB {
|
DatabaseLayoutSettingPB {
|
||||||
|
layout_type: layout_type.clone(),
|
||||||
calendar: Some(calendar_setting),
|
calendar: Some(calendar_setting),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LayoutSettingPB::default()
|
DatabaseLayoutSettingPB::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_layout: DatabaseLayoutPB = view.layout.into();
|
|
||||||
let filters = view
|
let filters = view
|
||||||
.filters
|
.filters
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -47,7 +48,7 @@ pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> Database
|
|||||||
.collect::<Vec<SortPB>>();
|
.collect::<Vec<SortPB>>();
|
||||||
|
|
||||||
DatabaseViewSettingPB {
|
DatabaseViewSettingPB {
|
||||||
current_layout,
|
layout_type,
|
||||||
filters: filters.into(),
|
filters: filters.into(),
|
||||||
group_settings: group_settings.into(),
|
group_settings: group_settings.into(),
|
||||||
sorts: sorts.into(),
|
sorts: sorts.into(),
|
||||||
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
|
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::{Field, TypeOptionData};
|
||||||
use collab_database::rows::{Cells, Row, RowCell, RowId};
|
use collab_database::rows::{Cells, Row, RowCell, RowId};
|
||||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
|
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
@ -13,9 +13,9 @@ use flowy_task::TaskDispatcher;
|
|||||||
use lib_infra::future::Fut;
|
use lib_infra::future::Fut;
|
||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
CalendarEventPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams, FieldType,
|
CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams,
|
||||||
GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedRowPB, LayoutSettingPB,
|
DeleteGroupParams, DeleteSortParams, FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB,
|
||||||
LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
|
InsertedRowPB, LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
|
||||||
UpdateFilterParams, UpdateSortParams,
|
UpdateFilterParams, UpdateSortParams,
|
||||||
};
|
};
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
@ -31,7 +31,7 @@ use crate::services::database_view::{
|
|||||||
notify_did_update_setting, notify_did_update_sort, DatabaseViewChangedNotifier,
|
notify_did_update_setting, notify_did_update_sort, DatabaseViewChangedNotifier,
|
||||||
DatabaseViewChangedReceiverRunner,
|
DatabaseViewChangedReceiverRunner,
|
||||||
};
|
};
|
||||||
use crate::services::field::TypeOptionCellDataHandler;
|
use crate::services::field::{DateTypeOption, TypeOptionCellDataHandler};
|
||||||
use crate::services::filter::{
|
use crate::services::filter::{
|
||||||
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
|
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
|
||||||
};
|
};
|
||||||
@ -42,13 +42,21 @@ use crate::services::setting::CalendarLayoutSetting;
|
|||||||
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
|
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
|
||||||
|
|
||||||
pub trait DatabaseViewData: Send + Sync + 'static {
|
pub trait DatabaseViewData: Send + Sync + 'static {
|
||||||
fn get_view_setting(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
|
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
|
||||||
/// If the field_ids is None, then it will return all the field revisions
|
/// If the field_ids is None, then it will return all the field revisions
|
||||||
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
||||||
|
|
||||||
/// Returns the field with the field_id
|
/// Returns the field with the field_id
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
|
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
|
||||||
|
|
||||||
|
fn create_field(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
name: &str,
|
||||||
|
field_type: FieldType,
|
||||||
|
type_option_data: TypeOptionData,
|
||||||
|
) -> Fut<Field>;
|
||||||
|
|
||||||
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>>;
|
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>>;
|
||||||
|
|
||||||
/// Returns the index of the row with row_id
|
/// Returns the index of the row with row_id
|
||||||
@ -62,7 +70,7 @@ pub trait DatabaseViewData: Send + Sync + 'static {
|
|||||||
|
|
||||||
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
|
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
|
||||||
|
|
||||||
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Option<Arc<RowCell>>>;
|
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>>;
|
||||||
|
|
||||||
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout;
|
fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout;
|
||||||
|
|
||||||
@ -99,6 +107,12 @@ pub trait DatabaseViewData: Send + Sync + 'static {
|
|||||||
layout_setting: LayoutSetting,
|
layout_setting: LayoutSetting,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Return the database layout type for the view with given view_id
|
||||||
|
/// The default layout type is [DatabaseLayout::Grid]
|
||||||
|
fn get_layout_type(&self, view_id: &str) -> DatabaseLayout;
|
||||||
|
|
||||||
|
fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout);
|
||||||
|
|
||||||
/// Returns a `TaskDispatcher` used to poll a `Task`
|
/// Returns a `TaskDispatcher` used to poll a `Task`
|
||||||
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
|
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
|
||||||
|
|
||||||
@ -167,6 +181,10 @@ impl DatabaseViewEditor {
|
|||||||
self.filter_controller.close().await;
|
self.filter_controller.close().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_view(&self) -> Option<DatabaseView> {
|
||||||
|
self.delegate.get_view(&self.view_id).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn v_will_create_row(&self, cells: &mut Cells, group_id: &Option<String>) {
|
pub async fn v_will_create_row(&self, cells: &mut Cells, group_id: &Option<String>) {
|
||||||
if group_id.is_none() {
|
if group_id.is_none() {
|
||||||
return;
|
return;
|
||||||
@ -398,7 +416,7 @@ impl DatabaseViewEditor {
|
|||||||
if !is_grouping_field {
|
if !is_grouping_field {
|
||||||
self.v_update_grouping_field(field_id).await?;
|
self.v_update_grouping_field(field_id).await?;
|
||||||
|
|
||||||
if let Some(view) = self.delegate.get_view_setting(&self.view_id).await {
|
if let Some(view) = self.delegate.get_view(&self.view_id).await {
|
||||||
let setting = database_view_setting_pb_from_view(view);
|
let setting = database_view_setting_pb_from_view(view);
|
||||||
notify_did_update_setting(&self.view_id, setting).await;
|
notify_did_update_setting(&self.view_id, setting).await;
|
||||||
}
|
}
|
||||||
@ -571,16 +589,11 @@ impl DatabaseViewEditor {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("{:?}", layout_setting);
|
|
||||||
layout_setting
|
layout_setting
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the calendar settings and send the notification to refresh the UI
|
/// Update the calendar settings and send the notification to refresh the UI
|
||||||
pub async fn v_set_layout_settings(
|
pub async fn v_set_layout_settings(&self, params: LayoutSettingParams) -> FlowyResult<()> {
|
||||||
&self,
|
|
||||||
_layout_ty: &DatabaseLayout,
|
|
||||||
params: LayoutSettingParams,
|
|
||||||
) -> FlowyResult<()> {
|
|
||||||
// Maybe it needs no send notification to refresh the UI
|
// Maybe it needs no send notification to refresh the UI
|
||||||
if let Some(new_calendar_setting) = params.calendar {
|
if let Some(new_calendar_setting) = params.calendar {
|
||||||
if let Some(field) = self
|
if let Some(field) = self
|
||||||
@ -593,16 +606,19 @@ impl DatabaseViewEditor {
|
|||||||
return Err(FlowyError::unexpect_calendar_field_type());
|
return Err(FlowyError::unexpect_calendar_field_type());
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout_ty = DatabaseLayout::Calendar;
|
let old_calender_setting = self
|
||||||
let old_calender_setting = self.v_get_layout_settings(&layout_ty).await.calendar;
|
.v_get_layout_settings(¶ms.layout_type)
|
||||||
|
.await
|
||||||
|
.calendar;
|
||||||
|
|
||||||
self.delegate.insert_layout_setting(
|
self.delegate.insert_layout_setting(
|
||||||
&self.view_id,
|
&self.view_id,
|
||||||
&layout_ty,
|
¶ms.layout_type,
|
||||||
new_calendar_setting.clone().into(),
|
new_calendar_setting.clone().into(),
|
||||||
);
|
);
|
||||||
let new_field_id = new_calendar_setting.field_id.clone();
|
let new_field_id = new_calendar_setting.field_id.clone();
|
||||||
let layout_setting_pb: LayoutSettingPB = LayoutSettingParams {
|
let layout_setting_pb: DatabaseLayoutSettingPB = LayoutSettingParams {
|
||||||
|
layout_type: params.layout_type,
|
||||||
calendar: Some(new_calendar_setting),
|
calendar: Some(new_calendar_setting),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
@ -620,8 +636,6 @@ impl DatabaseViewEditor {
|
|||||||
.payload(layout_setting_pb)
|
.payload(layout_setting_pb)
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tracing::warn!("Calendar setting should not be empty")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,6 +802,66 @@ impl DatabaseViewEditor {
|
|||||||
Some(events)
|
Some(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
|
pub async fn v_update_layout_type(&self, layout_type: DatabaseLayout) -> FlowyResult<()> {
|
||||||
|
self
|
||||||
|
.delegate
|
||||||
|
.update_layout_type(&self.view_id, &layout_type);
|
||||||
|
|
||||||
|
// Update the layout type in the database might add a new field to the database. If the new
|
||||||
|
// layout type is a calendar and there is not date field in the database, it will add a new
|
||||||
|
// date field to the database and create the corresponding layout setting.
|
||||||
|
//
|
||||||
|
let fields = self.delegate.get_fields(&self.view_id, None).await;
|
||||||
|
let date_field_id = match fields
|
||||||
|
.into_iter()
|
||||||
|
.find(|field| FieldType::from(field.field_type) == FieldType::DateTime)
|
||||||
|
{
|
||||||
|
None => {
|
||||||
|
tracing::trace!("Create a new date field after layout type change");
|
||||||
|
let default_date_type_option = DateTypeOption::default();
|
||||||
|
let field = self
|
||||||
|
.delegate
|
||||||
|
.create_field(
|
||||||
|
&self.view_id,
|
||||||
|
"Date",
|
||||||
|
FieldType::DateTime,
|
||||||
|
default_date_type_option.into(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
field.id
|
||||||
|
},
|
||||||
|
Some(date_field) => date_field.id.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout_setting = self.v_get_layout_settings(&layout_type).await;
|
||||||
|
match layout_type {
|
||||||
|
DatabaseLayout::Grid => {},
|
||||||
|
DatabaseLayout::Board => {},
|
||||||
|
DatabaseLayout::Calendar => {
|
||||||
|
if layout_setting.calendar.is_none() {
|
||||||
|
let layout_setting = CalendarLayoutSetting::new(date_field_id.clone());
|
||||||
|
self
|
||||||
|
.v_set_layout_settings(LayoutSettingParams {
|
||||||
|
layout_type,
|
||||||
|
calendar: Some(layout_setting),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = DatabaseLayoutMetaPB {
|
||||||
|
view_id: self.view_id.clone(),
|
||||||
|
layout: layout_type.into(),
|
||||||
|
};
|
||||||
|
send_notification(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout)
|
||||||
|
.payload(payload)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
|
pub async fn handle_row_event(&self, event: Cow<'_, DatabaseRowEvent>) {
|
||||||
let changeset = match event.into_owned() {
|
let changeset = match event.into_owned() {
|
||||||
DatabaseRowEvent::InsertRow(row) => {
|
DatabaseRowEvent::InsertRow(row) => {
|
||||||
|
@ -52,10 +52,8 @@ pub async fn new_group_controller(
|
|||||||
|
|
||||||
let layout = delegate.get_layout_for_view(&view_id);
|
let layout = delegate.get_layout_for_view(&view_id);
|
||||||
// If the view is a board and the grouping field is empty, we need to find a new grouping field
|
// If the view is a board and the grouping field is empty, we need to find a new grouping field
|
||||||
if layout.is_board() {
|
if layout.is_board() && grouping_field.is_none() {
|
||||||
if grouping_field.is_none() {
|
grouping_field = find_new_grouping_field(&fields, &layout);
|
||||||
grouping_field = find_new_grouping_field(&fields, &layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(grouping_field) = grouping_field {
|
if let Some(grouping_field) = grouping_field {
|
||||||
@ -104,21 +102,20 @@ pub(crate) async fn get_cell_for_row(
|
|||||||
row_id: &RowId,
|
row_id: &RowId,
|
||||||
) -> Option<RowSingleCellData> {
|
) -> Option<RowSingleCellData> {
|
||||||
let field = delegate.get_field(field_id).await?;
|
let field = delegate.get_field(field_id).await?;
|
||||||
let cell = delegate.get_cell_in_row(field_id, row_id).await?;
|
let row_cell = delegate.get_cell_in_row(field_id, row_id).await;
|
||||||
let field_type = FieldType::from(field.field_type);
|
let field_type = FieldType::from(field.field_type);
|
||||||
|
let handler = delegate.get_type_option_cell_handler(&field, &field_type)?;
|
||||||
|
|
||||||
if let Some(handler) = delegate.get_type_option_cell_handler(&field, &field_type) {
|
let cell_data = match &row_cell.cell {
|
||||||
return match handler.get_cell_data(&cell, &field_type, &field) {
|
None => None,
|
||||||
Ok(cell_data) => Some(RowSingleCellData {
|
Some(cell) => handler.get_cell_data(&cell, &field_type, &field).ok(),
|
||||||
row_id: cell.row_id.clone(),
|
};
|
||||||
field_id: field.id.clone(),
|
Some(RowSingleCellData {
|
||||||
field_type: field_type.clone(),
|
row_id: row_cell.row_id.clone(),
|
||||||
cell_data,
|
field_id: field.id.clone(),
|
||||||
}),
|
field_type: field_type.clone(),
|
||||||
Err(_) => None,
|
cell_data,
|
||||||
};
|
})
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the list of cells corresponding to the given field.
|
// Returns the list of cells corresponding to the given field.
|
||||||
@ -133,17 +130,18 @@ pub(crate) async fn get_cells_for_field(
|
|||||||
let cells = delegate.get_cells_for_field(view_id, field_id).await;
|
let cells = delegate.get_cells_for_field(view_id, field_id).await;
|
||||||
return cells
|
return cells
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(
|
.map(|row_cell| {
|
||||||
|cell| match handler.get_cell_data(cell, &field_type, &field) {
|
let cell_data = match &row_cell.cell {
|
||||||
Ok(cell_data) => Some(RowSingleCellData {
|
None => None,
|
||||||
row_id: cell.row_id.clone(),
|
Some(cell) => handler.get_cell_data(&cell, &field_type, &field).ok(),
|
||||||
field_id: field.id.clone(),
|
};
|
||||||
field_type: field_type.clone(),
|
RowSingleCellData {
|
||||||
cell_data,
|
row_id: row_cell.row_id.clone(),
|
||||||
}),
|
field_id: field.id.clone(),
|
||||||
Err(_) => None,
|
field_type: field_type.clone(),
|
||||||
},
|
cell_data,
|
||||||
)
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use std::str::FromStr;
|
|||||||
/// The [DateTypeOption] is used by [FieldType::Date], [FieldType::LastEditedTime], and [FieldType::CreatedTime].
|
/// The [DateTypeOption] is used by [FieldType::Date], [FieldType::LastEditedTime], and [FieldType::CreatedTime].
|
||||||
/// So, storing the field type is necessary to distinguish the field type.
|
/// So, storing the field type is necessary to distinguish the field type.
|
||||||
/// Most of the cases, each [FieldType] has its own [TypeOption] implementation.
|
/// Most of the cases, each [FieldType] has its own [TypeOption] implementation.
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct DateTypeOption {
|
pub struct DateTypeOption {
|
||||||
pub date_format: DateFormat,
|
pub date_format: DateFormat,
|
||||||
pub time_format: TimeFormat,
|
pub time_format: TimeFormat,
|
||||||
@ -27,6 +27,17 @@ pub struct DateTypeOption {
|
|||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for DateTypeOption {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
date_format: Default::default(),
|
||||||
|
time_format: Default::default(),
|
||||||
|
timezone_id: Default::default(),
|
||||||
|
field_type: FieldType::DateTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TypeOption for DateTypeOption {
|
impl TypeOption for DateTypeOption {
|
||||||
type CellData = DateCellData;
|
type CellData = DateCellData;
|
||||||
type CellChangeset = DateCellChangeset;
|
type CellChangeset = DateCellChangeset;
|
||||||
|
@ -498,14 +498,14 @@ pub struct RowSingleCellData {
|
|||||||
pub row_id: RowId,
|
pub row_id: RowId,
|
||||||
pub field_id: String,
|
pub field_id: String,
|
||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
pub cell_data: BoxCellData,
|
pub cell_data: Option<BoxCellData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! into_cell_data {
|
macro_rules! into_cell_data {
|
||||||
($func_name:ident,$return_ty:ty) => {
|
($func_name:ident,$return_ty:ty) => {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn $func_name(self) -> Option<$return_ty> {
|
pub fn $func_name(self) -> Option<$return_ty> {
|
||||||
self.cell_data.unbox_or_none()
|
self.cell_data?.unbox_or_none()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -254,6 +254,7 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.context.get_mut_no_status_group() {
|
match self.context.get_mut_no_status_group() {
|
||||||
None => {},
|
None => {},
|
||||||
Some(no_status_group) => no_status_group.add_row((*row).clone()),
|
Some(no_status_group) => no_status_group.add_row((*row).clone()),
|
||||||
@ -349,12 +350,12 @@ where
|
|||||||
deleted_group: None,
|
deleted_group: None,
|
||||||
row_changesets: vec![],
|
row_changesets: vec![],
|
||||||
};
|
};
|
||||||
let cell_rev = match context.row.cells.get(&self.grouping_field_id) {
|
let cell = match context.row.cells.get(&self.grouping_field_id) {
|
||||||
Some(cell_rev) => Some(cell_rev.clone()),
|
Some(cell) => Some(cell.clone()),
|
||||||
None => self.placeholder_cell(),
|
None => self.placeholder_cell(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cell) = cell_rev {
|
if let Some(cell) = cell {
|
||||||
let cell_bytes = get_cell_protobuf(&cell, context.field, None);
|
let cell_bytes = get_cell_protobuf(&cell, context.field, None);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
result.deleted_group = self.delete_group_when_move_row(context.row, &cell_data);
|
result.deleted_group = self.delete_group_when_move_row(context.row, &cell_data);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, SelectOptionCellDataPB};
|
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
|
||||||
use crate::services::cell::insert_select_option_cell;
|
use crate::services::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{MultiSelectTypeOption, SelectOptionCellDataParser};
|
use crate::services::field::{MultiSelectTypeOption, SelectOptionCellDataParser};
|
||||||
use crate::services::group::action::GroupCustomize;
|
use crate::services::group::action::GroupCustomize;
|
||||||
@ -10,7 +10,7 @@ use crate::services::group::{
|
|||||||
move_group_row, remove_select_option_row, GeneratedGroups, GroupContext,
|
move_group_row, remove_select_option_row, GeneratedGroups, GroupContext,
|
||||||
};
|
};
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::{Cells, Row};
|
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -39,6 +39,14 @@ impl GroupCustomize for MultiSelectGroupController {
|
|||||||
.any(|option| option.id == content)
|
.any(|option| option.id == content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
|
Some(
|
||||||
|
new_cell_builder(FieldType::MultiSelect)
|
||||||
|
.insert_str_value("data", "")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row: &Row,
|
row: &Row,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, SelectOptionCellDataPB};
|
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
|
||||||
use crate::services::cell::insert_select_option_cell;
|
use crate::services::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption};
|
use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption};
|
||||||
use crate::services::group::action::GroupCustomize;
|
use crate::services::group::action::GroupCustomize;
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::{Cells, Row};
|
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{
|
||||||
@ -39,6 +39,14 @@ impl GroupCustomize for SingleSelectGroupController {
|
|||||||
.any(|option| option.id == content)
|
.any(|option| option.id == content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
|
Some(
|
||||||
|
new_cell_builder(FieldType::SingleSelect)
|
||||||
|
.insert_str_value("data", "")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row: &Row,
|
row: &Row,
|
||||||
|
@ -3,10 +3,10 @@ mod configuration;
|
|||||||
mod controller;
|
mod controller;
|
||||||
mod controller_impls;
|
mod controller_impls;
|
||||||
mod entities;
|
mod entities;
|
||||||
mod group_util;
|
mod group_builder;
|
||||||
|
|
||||||
pub(crate) use configuration::*;
|
pub(crate) use configuration::*;
|
||||||
pub(crate) use controller::*;
|
pub(crate) use controller::*;
|
||||||
pub(crate) use controller_impls::*;
|
pub(crate) use controller_impls::*;
|
||||||
pub(crate) use entities::*;
|
pub(crate) use entities::*;
|
||||||
pub(crate) use group_util::*;
|
pub(crate) use group_builder::*;
|
||||||
|
@ -74,8 +74,8 @@ async fn text_cell_data_test() {
|
|||||||
.get_cells_for_field(&test.view_id, &text_field.id)
|
.get_cells_for_field(&test.view_id, &text_field.id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
for (i, cell) in cells.into_iter().enumerate() {
|
for (i, row_cell) in cells.into_iter().enumerate() {
|
||||||
let text = StrCellData::from(cell.as_ref());
|
let text = StrCellData::from(row_cell.cell.as_ref().unwrap());
|
||||||
match i {
|
match i {
|
||||||
0 => assert_eq!(text.as_str(), "A"),
|
0 => assert_eq!(text.as_str(), "A"),
|
||||||
1 => assert_eq!(text.as_str(), ""),
|
1 => assert_eq!(text.as_str(), ""),
|
||||||
@ -97,10 +97,12 @@ async fn url_cell_data_test() {
|
|||||||
.get_cells_for_field(&test.view_id, &url_field.id)
|
.get_cells_for_field(&test.view_id, &url_field.id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
for (i, cell) in cells.into_iter().enumerate() {
|
for (i, row_cell) in cells.into_iter().enumerate() {
|
||||||
let cell = URLCellData::from(cell.as_ref());
|
if let Some(cell) = row_cell.cell.as_ref() {
|
||||||
if i == 0 {
|
let cell = URLCellData::from(cell);
|
||||||
assert_eq!(cell.url.as_str(), "https://www.appflowy.io/");
|
if i == 0 {
|
||||||
|
assert_eq!(cell.url.as_str(), "https://www.appflowy.io/");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,8 +137,10 @@ async fn update_updated_at_field_on_other_cell_update() {
|
|||||||
.get_cells_for_field(&test.view_id, &updated_at_field.id)
|
.get_cells_for_field(&test.view_id, &updated_at_field.id)
|
||||||
.await;
|
.await;
|
||||||
assert!(!cells.is_empty());
|
assert!(!cells.is_empty());
|
||||||
for (i, cell) in cells.into_iter().enumerate() {
|
for (i, row_cell) in cells.into_iter().enumerate() {
|
||||||
let timestamp = DateCellData::from(cell.as_ref()).timestamp.unwrap();
|
let timestamp = DateCellData::from(row_cell.cell.as_ref().unwrap())
|
||||||
|
.timestamp
|
||||||
|
.unwrap();
|
||||||
println!(
|
println!(
|
||||||
"{}, bf: {}, af: {}",
|
"{}, bf: {}, af: {}",
|
||||||
timestamp, before_update_timestamp, after_update_timestamp
|
timestamp, before_update_timestamp, after_update_timestamp
|
||||||
|
@ -6,7 +6,7 @@ use collab_database::fields::Field;
|
|||||||
use collab_database::rows::{CreateRowParams, Row, RowId};
|
use collab_database::rows::{CreateRowParams, Row, RowId};
|
||||||
use strum::EnumCount;
|
use strum::EnumCount;
|
||||||
|
|
||||||
use flowy_database2::entities::{DatabaseLayoutPB, FieldType, FilterPB, RowPB, SelectOptionPB};
|
use flowy_database2::entities::{FieldType, FilterPB, RowPB, SelectOptionPB};
|
||||||
use flowy_database2::services::cell::{CellBuilder, ToCellChangeset};
|
use flowy_database2::services::cell::{CellBuilder, ToCellChangeset};
|
||||||
use flowy_database2::services::database::DatabaseEditor;
|
use flowy_database2::services::database::DatabaseEditor;
|
||||||
use flowy_database2::services::field::checklist_type_option::{
|
use flowy_database2::services::field::checklist_type_option::{
|
||||||
@ -21,7 +21,9 @@ use flowy_error::FlowyResult;
|
|||||||
use flowy_test::folder_event::ViewTest;
|
use flowy_test::folder_event::ViewTest;
|
||||||
use flowy_test::FlowyCoreTest;
|
use flowy_test::FlowyCoreTest;
|
||||||
|
|
||||||
use crate::database::mock_data::{make_test_board, make_test_calendar, make_test_grid};
|
use crate::database::mock_data::{
|
||||||
|
make_no_date_test_grid, make_test_board, make_test_calendar, make_test_grid,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DatabaseEditorTest {
|
pub struct DatabaseEditorTest {
|
||||||
pub sdk: FlowyCoreTest,
|
pub sdk: FlowyCoreTest,
|
||||||
@ -36,35 +38,42 @@ pub struct DatabaseEditorTest {
|
|||||||
|
|
||||||
impl DatabaseEditorTest {
|
impl DatabaseEditorTest {
|
||||||
pub async fn new_grid() -> Self {
|
pub async fn new_grid() -> Self {
|
||||||
Self::new(DatabaseLayoutPB::Grid).await
|
let sdk = FlowyCoreTest::new();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
|
||||||
|
let params = make_test_grid();
|
||||||
|
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
|
||||||
|
Self::new(sdk, view_test).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_no_date_grid() -> Self {
|
||||||
|
let sdk = FlowyCoreTest::new();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
|
||||||
|
let params = make_no_date_test_grid();
|
||||||
|
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
|
||||||
|
Self::new(sdk, view_test).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_board() -> Self {
|
pub async fn new_board() -> Self {
|
||||||
Self::new(DatabaseLayoutPB::Board).await
|
let sdk = FlowyCoreTest::new();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
|
||||||
|
let params = make_test_board();
|
||||||
|
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
|
||||||
|
Self::new(sdk, view_test).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_calendar() -> Self {
|
pub async fn new_calendar() -> Self {
|
||||||
Self::new(DatabaseLayoutPB::Calendar).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new(layout: DatabaseLayoutPB) -> Self {
|
|
||||||
let sdk = FlowyCoreTest::new();
|
let sdk = FlowyCoreTest::new();
|
||||||
let _ = sdk.init_user().await;
|
let _ = sdk.init_user().await;
|
||||||
let test = match layout {
|
|
||||||
DatabaseLayoutPB::Grid => {
|
|
||||||
let params = make_test_grid();
|
|
||||||
ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await
|
|
||||||
},
|
|
||||||
DatabaseLayoutPB::Board => {
|
|
||||||
let data = make_test_board();
|
|
||||||
ViewTest::new_board_view(&sdk, data.to_json_bytes().unwrap()).await
|
|
||||||
},
|
|
||||||
DatabaseLayoutPB::Calendar => {
|
|
||||||
let data = make_test_calendar();
|
|
||||||
ViewTest::new_calendar_view(&sdk, data.to_json_bytes().unwrap()).await
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let params = make_test_calendar();
|
||||||
|
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
|
||||||
|
Self::new(sdk, view_test).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new(sdk: FlowyCoreTest, test: ViewTest) -> Self {
|
||||||
let editor = sdk
|
let editor = sdk
|
||||||
.database_manager
|
.database_manager
|
||||||
.get_database_with_view_id(&test.child_view.id)
|
.get_database_with_view_id(&test.child_view.id)
|
||||||
|
@ -265,7 +265,7 @@ impl DatabaseFilterTest {
|
|||||||
assert_eq!(expected_setting, setting);
|
assert_eq!(expected_setting, setting);
|
||||||
}
|
}
|
||||||
FilterScript::AssertNumberOfVisibleRows { expected } => {
|
FilterScript::AssertNumberOfVisibleRows { expected } => {
|
||||||
let grid = self.editor.get_database_data(&self.view_id).await;
|
let grid = self.editor.get_database_data(&self.view_id).await.unwrap();
|
||||||
assert_eq!(grid.rows.len(), expected);
|
assert_eq!(grid.rows.len(), expected);
|
||||||
}
|
}
|
||||||
FilterScript::Wait { millisecond } => {
|
FilterScript::Wait { millisecond } => {
|
||||||
|
@ -8,7 +8,9 @@ use crate::database::database_editor::DatabaseEditorTest;
|
|||||||
|
|
||||||
pub enum LayoutScript {
|
pub enum LayoutScript {
|
||||||
AssertCalendarLayoutSetting { expected: CalendarLayoutSetting },
|
AssertCalendarLayoutSetting { expected: CalendarLayoutSetting },
|
||||||
GetCalendarEvents,
|
AssertDefaultAllCalendarEvents,
|
||||||
|
AssertAllCalendarEventsCount { expected: usize },
|
||||||
|
UpdateDatabaseLayout { layout: DatabaseLayout },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DatabaseLayoutTest {
|
pub struct DatabaseLayoutTest {
|
||||||
@ -16,6 +18,11 @@ pub struct DatabaseLayoutTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseLayoutTest {
|
impl DatabaseLayoutTest {
|
||||||
|
pub async fn new_no_date_grid() -> Self {
|
||||||
|
let database_test = DatabaseEditorTest::new_no_date_grid().await;
|
||||||
|
Self { database_test }
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn new_calendar() -> Self {
|
pub async fn new_calendar() -> Self {
|
||||||
let database_test = DatabaseEditorTest::new_calendar().await;
|
let database_test = DatabaseEditorTest::new_calendar().await;
|
||||||
Self { database_test }
|
Self { database_test }
|
||||||
@ -33,6 +40,22 @@ impl DatabaseLayoutTest {
|
|||||||
|
|
||||||
pub async fn run_script(&mut self, script: LayoutScript) {
|
pub async fn run_script(&mut self, script: LayoutScript) {
|
||||||
match script {
|
match script {
|
||||||
|
LayoutScript::UpdateDatabaseLayout { layout } => {
|
||||||
|
self
|
||||||
|
.database_test
|
||||||
|
.editor
|
||||||
|
.update_view_layout(&self.database_test.view_id, layout)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
},
|
||||||
|
LayoutScript::AssertAllCalendarEventsCount { expected } => {
|
||||||
|
let events = self
|
||||||
|
.database_test
|
||||||
|
.editor
|
||||||
|
.get_all_calendar_events(&self.database_test.view_id)
|
||||||
|
.await;
|
||||||
|
assert_eq!(events.len(), expected);
|
||||||
|
},
|
||||||
LayoutScript::AssertCalendarLayoutSetting { expected } => {
|
LayoutScript::AssertCalendarLayoutSetting { expected } => {
|
||||||
let view_id = self.database_test.view_id.clone();
|
let view_id = self.database_test.view_id.clone();
|
||||||
let layout_ty = DatabaseLayout::Calendar;
|
let layout_ty = DatabaseLayout::Calendar;
|
||||||
@ -53,7 +76,7 @@ impl DatabaseLayoutTest {
|
|||||||
);
|
);
|
||||||
assert_eq!(calendar_setting.show_weekends, expected.show_weekends);
|
assert_eq!(calendar_setting.show_weekends, expected.show_weekends);
|
||||||
},
|
},
|
||||||
LayoutScript::GetCalendarEvents => {
|
LayoutScript::AssertDefaultAllCalendarEvents => {
|
||||||
let events = self
|
let events = self
|
||||||
.database_test
|
.database_test
|
||||||
.editor
|
.editor
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use collab_database::views::DatabaseLayout;
|
||||||
use flowy_database2::services::setting::CalendarLayoutSetting;
|
use flowy_database2::services::setting::CalendarLayoutSetting;
|
||||||
|
|
||||||
use crate::database::layout_test::script::DatabaseLayoutTest;
|
use crate::database::layout_test::script::DatabaseLayoutTest;
|
||||||
@ -17,6 +18,18 @@ async fn calendar_initial_layout_setting_test() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn calendar_get_events_test() {
|
async fn calendar_get_events_test() {
|
||||||
let mut test = DatabaseLayoutTest::new_calendar().await;
|
let mut test = DatabaseLayoutTest::new_calendar().await;
|
||||||
let scripts = vec![GetCalendarEvents];
|
let scripts = vec![AssertDefaultAllCalendarEvents];
|
||||||
|
test.run_scripts(scripts).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn grid_to_calendar_layout_test() {
|
||||||
|
let mut test = DatabaseLayoutTest::new_no_date_grid().await;
|
||||||
|
let scripts = vec![
|
||||||
|
UpdateDatabaseLayout {
|
||||||
|
layout: DatabaseLayout::Calendar,
|
||||||
|
},
|
||||||
|
AssertAllCalendarEventsCount { expected: 3 },
|
||||||
|
];
|
||||||
test.run_scripts(scripts).await;
|
test.run_scripts(scripts).await;
|
||||||
}
|
}
|
||||||
|
@ -240,3 +240,80 @@ pub fn make_test_grid() -> DatabaseData {
|
|||||||
|
|
||||||
DatabaseData { view, fields, rows }
|
DatabaseData { view, fields, rows }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_no_date_test_grid() -> DatabaseData {
|
||||||
|
let mut fields = vec![];
|
||||||
|
let mut rows = vec![];
|
||||||
|
// Iterate through the FieldType to create the corresponding Field.
|
||||||
|
for field_type in FieldType::iter() {
|
||||||
|
match field_type {
|
||||||
|
FieldType::RichText => {
|
||||||
|
let text_field = FieldBuilder::from_field_type(field_type.clone())
|
||||||
|
.name("Name")
|
||||||
|
.visibility(true)
|
||||||
|
.primary(true)
|
||||||
|
.build();
|
||||||
|
fields.push(text_field);
|
||||||
|
},
|
||||||
|
FieldType::Number => {
|
||||||
|
// Number
|
||||||
|
let mut type_option = NumberTypeOption::default();
|
||||||
|
type_option.set_format(NumberFormat::USD);
|
||||||
|
|
||||||
|
let number_field = FieldBuilder::new(field_type.clone(), type_option)
|
||||||
|
.name("Price")
|
||||||
|
.visibility(true)
|
||||||
|
.build();
|
||||||
|
fields.push(number_field);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
let mut row_builder = TestRowBuilder::new(i.into(), &fields);
|
||||||
|
match i {
|
||||||
|
0 => {
|
||||||
|
for field_type in FieldType::iter() {
|
||||||
|
match field_type {
|
||||||
|
FieldType::RichText => row_builder.insert_text_cell("A"),
|
||||||
|
FieldType::Number => row_builder.insert_number_cell("1"),
|
||||||
|
_ => "".to_owned(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
for field_type in FieldType::iter() {
|
||||||
|
match field_type {
|
||||||
|
FieldType::RichText => row_builder.insert_text_cell(""),
|
||||||
|
FieldType::Number => row_builder.insert_number_cell("2"),
|
||||||
|
_ => "".to_owned(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
for field_type in FieldType::iter() {
|
||||||
|
match field_type {
|
||||||
|
FieldType::RichText => row_builder.insert_text_cell("C"),
|
||||||
|
FieldType::Number => row_builder.insert_number_cell("3"),
|
||||||
|
_ => "".to_owned(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = row_builder.build();
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = DatabaseView {
|
||||||
|
id: gen_database_view_id(),
|
||||||
|
database_id: gen_database_id(),
|
||||||
|
name: "".to_string(),
|
||||||
|
layout: DatabaseLayout::Grid,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
DatabaseData { view, fields, rows }
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::entities::parser::view::{ViewDesc, ViewIdentify, ViewName, ViewThumbnail};
|
use crate::entities::parser::view::{ViewDesc, ViewIdentify, ViewName, ViewThumbnail};
|
||||||
use crate::view_ext::gen_view_id;
|
use crate::view_operation::gen_view_id;
|
||||||
use collab_folder::core::{View, ViewLayout};
|
use collab_folder::core::{View, ViewLayout};
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use flowy_error::ErrorCode;
|
use flowy_error::ErrorCode;
|
||||||
@ -31,7 +31,7 @@ pub struct ViewPB {
|
|||||||
pub fn view_pb_without_child_views(view: View) -> ViewPB {
|
pub fn view_pb_without_child_views(view: View) -> ViewPB {
|
||||||
ViewPB {
|
ViewPB {
|
||||||
id: view.id,
|
id: view.id,
|
||||||
parent_view_id: view.bid,
|
parent_view_id: view.parent_view_id,
|
||||||
name: view.name,
|
name: view.name,
|
||||||
create_time: view.created_at,
|
create_time: view.created_at,
|
||||||
child_views: Default::default(),
|
child_views: Default::default(),
|
||||||
@ -43,7 +43,7 @@ pub fn view_pb_without_child_views(view: View) -> ViewPB {
|
|||||||
pub fn view_pb_with_child_views(view: View, child_views: Vec<View>) -> ViewPB {
|
pub fn view_pb_with_child_views(view: View, child_views: Vec<View>) -> ViewPB {
|
||||||
ViewPB {
|
ViewPB {
|
||||||
id: view.id,
|
id: view.id,
|
||||||
parent_view_id: view.bid,
|
parent_view_id: view.parent_view_id,
|
||||||
name: view.name,
|
name: view.name,
|
||||||
create_time: view.created_at,
|
create_time: view.created_at,
|
||||||
child_views: child_views
|
child_views: child_views
|
||||||
@ -219,6 +219,9 @@ pub struct UpdateViewPayloadPB {
|
|||||||
|
|
||||||
#[pb(index = 4, one_of)]
|
#[pb(index = 4, one_of)]
|
||||||
pub thumbnail: Option<String>,
|
pub thumbnail: Option<String>,
|
||||||
|
|
||||||
|
#[pb(index = 5, one_of)]
|
||||||
|
pub layout: Option<ViewLayoutPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -227,6 +230,7 @@ pub struct UpdateViewParams {
|
|||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub thumbnail: Option<String>,
|
pub thumbnail: Option<String>,
|
||||||
|
pub layout: Option<ViewLayout>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<UpdateViewParams> for UpdateViewPayloadPB {
|
impl TryInto<UpdateViewParams> for UpdateViewPayloadPB {
|
||||||
@ -255,6 +259,7 @@ impl TryInto<UpdateViewParams> for UpdateViewPayloadPB {
|
|||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
|
layout: self.layout.map(|ty| ty.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ pub mod manager;
|
|||||||
mod notification;
|
mod notification;
|
||||||
pub mod protobuf;
|
pub mod protobuf;
|
||||||
mod user_default;
|
mod user_default;
|
||||||
pub mod view_ext;
|
pub mod view_operation;
|
||||||
|
|
||||||
pub mod deps;
|
pub mod deps;
|
||||||
mod share;
|
mod share;
|
||||||
|
@ -29,7 +29,9 @@ use crate::notification::{
|
|||||||
};
|
};
|
||||||
use crate::share::ImportParams;
|
use crate::share::ImportParams;
|
||||||
use crate::user_default::DefaultFolderBuilder;
|
use crate::user_default::DefaultFolderBuilder;
|
||||||
use crate::view_ext::{create_view, gen_view_id, FolderOperationHandler, FolderOperationHandlers};
|
use crate::view_operation::{
|
||||||
|
create_view, gen_view_id, FolderOperationHandler, FolderOperationHandlers,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Folder2Manager {
|
pub struct Folder2Manager {
|
||||||
mutex_folder: Arc<MutexFolder>,
|
mutex_folder: Arc<MutexFolder>,
|
||||||
@ -200,18 +202,12 @@ impl Folder2Manager {
|
|||||||
let view_layout: ViewLayout = params.layout.clone().into();
|
let view_layout: ViewLayout = params.layout.clone().into();
|
||||||
let handler = self.get_handler(&view_layout)?;
|
let handler = self.get_handler(&view_layout)?;
|
||||||
let user_id = self.user.user_id()?;
|
let user_id = self.user.user_id()?;
|
||||||
let ext = params.meta.clone();
|
let meta = params.meta.clone();
|
||||||
match params.initial_data.is_empty() {
|
match params.initial_data.is_empty() {
|
||||||
true => {
|
true => {
|
||||||
tracing::trace!("Create view with build-in data");
|
tracing::trace!("Create view with build-in data");
|
||||||
handler
|
handler
|
||||||
.create_built_in_view(
|
.create_built_in_view(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone())
|
||||||
user_id,
|
|
||||||
¶ms.view_id,
|
|
||||||
¶ms.name,
|
|
||||||
view_layout.clone(),
|
|
||||||
ext,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
false => {
|
false => {
|
||||||
@ -223,7 +219,7 @@ impl Folder2Manager {
|
|||||||
¶ms.name,
|
¶ms.name,
|
||||||
params.initial_data.clone(),
|
params.initial_data.clone(),
|
||||||
view_layout.clone(),
|
view_layout.clone(),
|
||||||
ext,
|
meta,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
@ -233,7 +229,7 @@ impl Folder2Manager {
|
|||||||
folder.insert_view(view.clone());
|
folder.insert_view(view.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.bid.clone()]);
|
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]);
|
||||||
Ok(view)
|
Ok(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +332,7 @@ impl Folder2Manager {
|
|||||||
match view {
|
match view {
|
||||||
None => tracing::error!("Couldn't find the view. It should not be empty"),
|
None => tracing::error!("Couldn't find the view. It should not be empty"),
|
||||||
Some(view) => {
|
Some(view) => {
|
||||||
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.bid]);
|
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id]);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -350,18 +346,23 @@ impl Folder2Manager {
|
|||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||||
pub async fn update_view_with_params(&self, params: UpdateViewParams) -> FlowyResult<()> {
|
pub async fn update_view_with_params(&self, params: UpdateViewParams) -> FlowyResult<()> {
|
||||||
let _ = self
|
let value = self.with_folder(None, |folder| {
|
||||||
.mutex_folder
|
let old_view = folder.views.get_view(¶ms.view_id);
|
||||||
.lock()
|
let new_view = folder.views.update_view(¶ms.view_id, |update| {
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(folder_not_init_error)?
|
|
||||||
.views
|
|
||||||
.update_view(¶ms.view_id, |update| {
|
|
||||||
update
|
update
|
||||||
.set_name_if_not_none(params.name)
|
.set_name_if_not_none(params.name)
|
||||||
.set_desc_if_not_none(params.desc)
|
.set_desc_if_not_none(params.desc)
|
||||||
|
.set_layout_if_not_none(params.layout)
|
||||||
.done()
|
.done()
|
||||||
});
|
});
|
||||||
|
Some((old_view, new_view))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((Some(old_view), Some(new_view))) = value {
|
||||||
|
if let Ok(handler) = self.get_handler(&old_view.layout) {
|
||||||
|
handler.did_update_view(&old_view, &new_view).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(view_pb) = self.get_view(¶ms.view_id).await {
|
if let Ok(view_pb) = self.get_view(¶ms.view_id).await {
|
||||||
notify_parent_view_did_change(
|
notify_parent_view_did_change(
|
||||||
@ -388,7 +389,7 @@ impl Folder2Manager {
|
|||||||
// meta.insert("database_id".to_string(), database_id);
|
// meta.insert("database_id".to_string(), database_id);
|
||||||
// }
|
// }
|
||||||
let duplicate_params = CreateViewParams {
|
let duplicate_params = CreateViewParams {
|
||||||
parent_view_id: view.bid.clone(),
|
parent_view_id: view.parent_view_id.clone(),
|
||||||
name: format!("{} (copy)", &view.name),
|
name: format!("{} (copy)", &view.name),
|
||||||
desc: view.desc,
|
desc: view.desc,
|
||||||
layout: view.layout.into(),
|
layout: view.layout.into(),
|
||||||
@ -501,7 +502,7 @@ impl Folder2Manager {
|
|||||||
self.with_folder((), |folder| {
|
self.with_folder((), |folder| {
|
||||||
folder.insert_view(view.clone());
|
folder.insert_view(view.clone());
|
||||||
});
|
});
|
||||||
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.bid.clone()]);
|
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]);
|
||||||
Ok(view)
|
Ok(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,11 +530,11 @@ fn listen_on_view_change(mut rx: ViewChangeReceiver, weak_mutex_folder: &Weak<Mu
|
|||||||
tracing::trace!("Did receive view change: {:?}", value);
|
tracing::trace!("Did receive view change: {:?}", value);
|
||||||
match value {
|
match value {
|
||||||
ViewChange::DidCreateView { view } => {
|
ViewChange::DidCreateView { view } => {
|
||||||
notify_parent_view_did_change(folder.clone(), vec![view.bid]);
|
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
||||||
},
|
},
|
||||||
ViewChange::DidDeleteView { views: _ } => {},
|
ViewChange::DidDeleteView { views: _ } => {},
|
||||||
ViewChange::DidUpdate { view } => {
|
ViewChange::DidUpdate { view } => {
|
||||||
notify_parent_view_did_change(folder.clone(), vec![view.bid]);
|
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -580,7 +581,7 @@ fn listen_on_trash_change(mut rx: TrashChangeReceiver, weak_mutex_folder: &Weak<
|
|||||||
if let Some(folder) = folder.lock().as_ref() {
|
if let Some(folder) = folder.lock().as_ref() {
|
||||||
let views = folder.views.get_views(&ids);
|
let views = folder.views.get_views(&ids);
|
||||||
for view in views {
|
for view in views {
|
||||||
unique_ids.insert(view.bid);
|
unique_ids.insert(view.parent_view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let repeated_trash: RepeatedTrashPB = folder.trash.get_all_trash().into();
|
let repeated_trash: RepeatedTrashPB = folder.trash.get_all_trash().into();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::entities::{CreateViewParams, ViewLayoutPB};
|
use crate::entities::{CreateViewParams, ViewLayoutPB};
|
||||||
use crate::manager::Folder2Manager;
|
use crate::manager::Folder2Manager;
|
||||||
use crate::view_ext::gen_view_id;
|
use crate::view_operation::gen_view_id;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[cfg(feature = "test_helper")]
|
#[cfg(feature = "test_helper")]
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use collab_folder::core::{FolderData, RepeatedView, View, ViewIdentifier, ViewLayout, Workspace};
|
use collab_folder::core::{FolderData, RepeatedView, View, ViewIdentifier, ViewLayout, Workspace};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
|
|
||||||
use crate::entities::{view_pb_with_child_views, WorkspacePB};
|
use crate::entities::{view_pb_with_child_views, WorkspacePB};
|
||||||
use crate::view_ext::{gen_view_id, FolderOperationHandlers};
|
use crate::view_operation::{gen_view_id, FolderOperationHandlers};
|
||||||
|
|
||||||
pub struct DefaultFolderBuilder();
|
pub struct DefaultFolderBuilder();
|
||||||
impl DefaultFolderBuilder {
|
impl DefaultFolderBuilder {
|
||||||
@ -21,7 +19,7 @@ impl DefaultFolderBuilder {
|
|||||||
let child_view_layout = ViewLayout::Document;
|
let child_view_layout = ViewLayout::Document;
|
||||||
let child_view = View {
|
let child_view = View {
|
||||||
id: child_view_id.clone(),
|
id: child_view_id.clone(),
|
||||||
bid: view_id.clone(),
|
parent_view_id: view_id.clone(),
|
||||||
name: "Read me".to_string(),
|
name: "Read me".to_string(),
|
||||||
desc: "".to_string(),
|
desc: "".to_string(),
|
||||||
created_at: time,
|
created_at: time,
|
||||||
@ -39,14 +37,13 @@ impl DefaultFolderBuilder {
|
|||||||
&child_view.id,
|
&child_view.id,
|
||||||
&child_view.name,
|
&child_view.name,
|
||||||
child_view_layout.clone(),
|
child_view_layout.clone(),
|
||||||
HashMap::default(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let view = View {
|
let view = View {
|
||||||
id: view_id,
|
id: view_id,
|
||||||
bid: workspace_id.clone(),
|
parent_view_id: workspace_id.clone(),
|
||||||
name: "⭐️ Getting started".to_string(),
|
name: "⭐️ Getting started".to_string(),
|
||||||
desc: "".to_string(),
|
desc: "".to_string(),
|
||||||
children: RepeatedView::new(vec![ViewIdentifier {
|
children: RepeatedView::new(vec![ViewIdentifier {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::entities::{CreateViewParams, ViewLayoutPB};
|
use crate::entities::{CreateViewParams, ViewLayoutPB};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use collab_folder::core::{View, ViewLayout};
|
use collab_folder::core::ViewLayout;
|
||||||
use flowy_error::FlowyError;
|
use flowy_error::FlowyError;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
use lib_infra::util::timestamp;
|
use lib_infra::util::timestamp;
|
||||||
@ -9,6 +9,7 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type ViewData = Bytes;
|
pub type ViewData = Bytes;
|
||||||
|
pub use collab_folder::core::View;
|
||||||
|
|
||||||
/// The handler will be used to handler the folder operation for a specific
|
/// The handler will be used to handler the folder operation for a specific
|
||||||
/// view layout. Each [ViewLayout] will have a handler. So when creating a new
|
/// view layout. Each [ViewLayout] will have a handler. So when creating a new
|
||||||
@ -22,7 +23,18 @@ pub trait FolderOperationHandler {
|
|||||||
/// Returns the [ViewData] that can be used to create the same view.
|
/// Returns the [ViewData] that can be used to create the same view.
|
||||||
fn duplicate_view(&self, view_id: &str) -> FutureResult<ViewData, FlowyError>;
|
fn duplicate_view(&self, view_id: &str) -> FutureResult<ViewData, FlowyError>;
|
||||||
|
|
||||||
/// Create a view with custom data
|
/// Create a view with the data.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `user_id`: the user id
|
||||||
|
/// * `view_id`: the view id
|
||||||
|
/// * `name`: the name of the view
|
||||||
|
/// * `data`: initial data of the view. The data should be parsed by the [FolderOperationHandler]
|
||||||
|
/// implementation. For example, the data of the database will be [DatabaseData].
|
||||||
|
/// * `layout`: the layout of the view
|
||||||
|
/// * `meta`: use to carry extra information. For example, the database view will use this
|
||||||
|
/// to carry the reference database id.
|
||||||
fn create_view_with_view_data(
|
fn create_view_with_view_data(
|
||||||
&self,
|
&self,
|
||||||
user_id: i64,
|
user_id: i64,
|
||||||
@ -30,7 +42,7 @@ pub trait FolderOperationHandler {
|
|||||||
name: &str,
|
name: &str,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
ext: HashMap<String, String>,
|
meta: HashMap<String, String>,
|
||||||
) -> FutureResult<(), FlowyError>;
|
) -> FutureResult<(), FlowyError>;
|
||||||
|
|
||||||
/// Create a view with the pre-defined data.
|
/// Create a view with the pre-defined data.
|
||||||
@ -42,7 +54,6 @@ pub trait FolderOperationHandler {
|
|||||||
view_id: &str,
|
view_id: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
layout: ViewLayout,
|
layout: ViewLayout,
|
||||||
meta: HashMap<String, String>,
|
|
||||||
) -> FutureResult<(), FlowyError>;
|
) -> FutureResult<(), FlowyError>;
|
||||||
|
|
||||||
/// Create a view by importing data
|
/// Create a view by importing data
|
||||||
@ -60,6 +71,11 @@ pub trait FolderOperationHandler {
|
|||||||
name: &str,
|
name: &str,
|
||||||
path: String,
|
path: String,
|
||||||
) -> FutureResult<(), FlowyError>;
|
) -> FutureResult<(), FlowyError>;
|
||||||
|
|
||||||
|
/// Called when the view is updated. The handler is the `old` registered handler.
|
||||||
|
fn did_update_view(&self, _old: &View, _new: &View) -> FutureResult<(), FlowyError> {
|
||||||
|
FutureResult::new(async move { Ok(()) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FolderOperationHandlers =
|
pub type FolderOperationHandlers =
|
||||||
@ -80,7 +96,7 @@ pub(crate) fn create_view(params: CreateViewParams, layout: ViewLayout) -> View
|
|||||||
let time = timestamp();
|
let time = timestamp();
|
||||||
View {
|
View {
|
||||||
id: params.view_id,
|
id: params.view_id,
|
||||||
bid: params.parent_view_id,
|
parent_view_id: params.parent_view_id,
|
||||||
name: params.name,
|
name: params.name,
|
||||||
desc: params.desc,
|
desc: params.desc,
|
||||||
children: Default::default(),
|
children: Default::default(),
|
@ -274,6 +274,7 @@ pub async fn update_view(
|
|||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
thumbnail: None,
|
thumbnail: None,
|
||||||
|
layout: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(UpdateView)
|
.event(UpdateView)
|
||||||
|
Loading…
Reference in New Issue
Block a user