mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'upstream-main' into feat/appflowy_tauri_3
# Conflicts: # frontend/appflowy_tauri/package.json # frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx # frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx # frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.hooks.ts # frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx # frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PageItem.hooks.ts # frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PageItem.tsx # frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx
This commit is contained in:
commit
a9c8bad599
@ -372,6 +372,7 @@
|
||||
},
|
||||
"calendar": {
|
||||
"menuName": "Calendar",
|
||||
"defaultNewCalendarTitle": "Untitled",
|
||||
"navigation": {
|
||||
"today": "Today",
|
||||
"jumpToday": "Jump to Today",
|
||||
|
@ -2,9 +2,9 @@ part of 'cell_service.dart';
|
||||
|
||||
typedef CellByFieldId = LinkedHashMap<String, CellIdentifier>;
|
||||
|
||||
class GridBaseCell {
|
||||
class DatabaseCell {
|
||||
dynamic object;
|
||||
GridBaseCell({
|
||||
DatabaseCell({
|
||||
required this.object,
|
||||
});
|
||||
}
|
||||
@ -44,7 +44,7 @@ class CellCache {
|
||||
}
|
||||
}
|
||||
|
||||
void insert<T extends GridBaseCell>(CellCacheKey key, T value) {
|
||||
void insert<T extends DatabaseCell>(CellCacheKey key, T value) {
|
||||
var map = _cellDataByFieldId[key.fieldId];
|
||||
if (map == null) {
|
||||
_cellDataByFieldId[key.fieldId] = {};
|
||||
|
@ -170,7 +170,7 @@ class CellController<T, D> extends Equatable {
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_cellDataLoader.loadData().then((data) {
|
||||
if (data != null) {
|
||||
_cellCache.insert(_cacheKey, GridBaseCell(object: data));
|
||||
_cellCache.insert(_cacheKey, DatabaseCell(object: data));
|
||||
} else {
|
||||
_cellCache.remove(_cacheKey);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ typedef SelectOptionCellController
|
||||
= CellController<SelectOptionCellDataPB, String>;
|
||||
typedef ChecklistCellController
|
||||
= CellController<SelectOptionCellDataPB, String>;
|
||||
typedef DateCellController = CellController<DateCellDataPB, CalendarData>;
|
||||
typedef DateCellController = CellController<DateCellDataPB, DateCellData>;
|
||||
typedef URLCellController = CellController<URLCellDataPB, String>;
|
||||
|
||||
class CellControllerBuilder {
|
||||
|
@ -27,24 +27,28 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CalendarData with _$CalendarData {
|
||||
const factory CalendarData({required DateTime date, String? time}) =
|
||||
_CalendarData;
|
||||
class DateCellData with _$DateCellData {
|
||||
const factory DateCellData({
|
||||
required DateTime date,
|
||||
String? time,
|
||||
required bool includeTime,
|
||||
}) = _DateCellData;
|
||||
}
|
||||
|
||||
class DateCellDataPersistence implements CellDataPersistence<CalendarData> {
|
||||
class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
|
||||
final CellIdentifier cellId;
|
||||
DateCellDataPersistence({
|
||||
required this.cellId,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Option<FlowyError>> save(CalendarData data) {
|
||||
Future<Option<FlowyError>> save(DateCellData data) {
|
||||
var payload = DateChangesetPB.create()..cellPath = _makeCellPath(cellId);
|
||||
|
||||
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
|
||||
payload.date = date;
|
||||
payload.isUtc = data.date.isUtc;
|
||||
payload.includeTime = data.includeTime;
|
||||
|
||||
if (data.time != null) {
|
||||
payload.time = data.time!;
|
||||
|
@ -0,0 +1,290 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'database_service.dart';
|
||||
import 'defines.dart';
|
||||
import 'layout/layout_setting_listener.dart';
|
||||
import 'row/row_cache.dart';
|
||||
import 'group/group_listener.dart';
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo> rowInfos,
|
||||
RowsChangedReason,
|
||||
);
|
||||
|
||||
typedef OnGroupByField = void Function(List<GroupPB>);
|
||||
typedef OnUpdateGroup = void Function(List<GroupPB>);
|
||||
typedef OnDeleteGroup = void Function(List<String>);
|
||||
typedef OnInsertGroup = void Function(InsertedGroupPB);
|
||||
|
||||
class GroupCallbacks {
|
||||
final OnGroupByField? onGroupByField;
|
||||
final OnUpdateGroup? onUpdateGroup;
|
||||
final OnDeleteGroup? onDeleteGroup;
|
||||
final OnInsertGroup? onInsertGroup;
|
||||
|
||||
GroupCallbacks({
|
||||
this.onGroupByField,
|
||||
this.onUpdateGroup,
|
||||
this.onDeleteGroup,
|
||||
this.onInsertGroup,
|
||||
});
|
||||
}
|
||||
|
||||
class LayoutCallbacks {
|
||||
final void Function(LayoutSettingPB) onLayoutChanged;
|
||||
final void Function(LayoutSettingPB) onLoadLayout;
|
||||
|
||||
LayoutCallbacks({
|
||||
required this.onLayoutChanged,
|
||||
required this.onLoadLayout,
|
||||
});
|
||||
}
|
||||
|
||||
class DatabaseCallbacks {
|
||||
OnDatabaseChanged? onDatabaseChanged;
|
||||
OnRowsChanged? onRowsChanged;
|
||||
OnFieldsChanged? onFieldsChanged;
|
||||
OnFiltersChanged? onFiltersChanged;
|
||||
DatabaseCallbacks({
|
||||
this.onDatabaseChanged,
|
||||
this.onRowsChanged,
|
||||
this.onFieldsChanged,
|
||||
this.onFiltersChanged,
|
||||
});
|
||||
}
|
||||
|
||||
class DatabaseController {
|
||||
final String viewId;
|
||||
final DatabaseBackendService _databaseBackendSvc;
|
||||
final FieldController fieldController;
|
||||
late DatabaseViewCache _viewCache;
|
||||
final LayoutTypePB layoutType;
|
||||
|
||||
// Callbacks
|
||||
DatabaseCallbacks? _databaseCallbacks;
|
||||
GroupCallbacks? _groupCallbacks;
|
||||
LayoutCallbacks? _layoutCallbacks;
|
||||
|
||||
// Getters
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
RowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
// Listener
|
||||
final DatabaseGroupListener groupListener;
|
||||
final DatabaseLayoutListener layoutListener;
|
||||
|
||||
DatabaseController({required ViewPB view, required this.layoutType})
|
||||
: viewId = view.id,
|
||||
_databaseBackendSvc = DatabaseBackendService(viewId: view.id),
|
||||
fieldController = FieldController(viewId: view.id),
|
||||
groupListener = DatabaseGroupListener(view.id),
|
||||
layoutListener = DatabaseLayoutListener(view.id) {
|
||||
_viewCache = DatabaseViewCache(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_listenOnRowsChanged();
|
||||
_listenOnFieldsChanged();
|
||||
_listenOnGroupChanged();
|
||||
_listenOnLayoutChanged();
|
||||
}
|
||||
|
||||
void addListener({
|
||||
DatabaseCallbacks? onDatabaseChanged,
|
||||
LayoutCallbacks? onLayoutChanged,
|
||||
GroupCallbacks? onGroupChanged,
|
||||
}) {
|
||||
_layoutCallbacks = onLayoutChanged;
|
||||
_databaseCallbacks = onDatabaseChanged;
|
||||
_groupCallbacks = onGroupChanged;
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> open() async {
|
||||
return _databaseBackendSvc.openGrid().then((result) {
|
||||
return result.fold(
|
||||
(database) async {
|
||||
_databaseCallbacks?.onDatabaseChanged?.call(database);
|
||||
_viewCache.rowCache.setInitialRows(database.rows);
|
||||
return await fieldController
|
||||
.loadFields(
|
||||
fieldIds: database.fields,
|
||||
)
|
||||
.then(
|
||||
(result) {
|
||||
return result.fold(
|
||||
(l) => Future(() async {
|
||||
await _loadGroups();
|
||||
await _loadLayoutSetting();
|
||||
return left(l);
|
||||
}),
|
||||
(err) => right(err),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow({
|
||||
String? startRowId,
|
||||
String? groupId,
|
||||
void Function(RowDataBuilder builder)? withCells,
|
||||
}) {
|
||||
Map<String, String>? cellDataByFieldId;
|
||||
|
||||
if (withCells != null) {
|
||||
final rowBuilder = RowDataBuilder();
|
||||
withCells(rowBuilder);
|
||||
cellDataByFieldId = rowBuilder.build();
|
||||
}
|
||||
|
||||
return _databaseBackendSvc.createRow(
|
||||
startRowId: startRowId,
|
||||
groupId: groupId,
|
||||
cellDataByFieldId: cellDataByFieldId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow(RowPB fromRow,
|
||||
{RowPB? toRow, String? groupId}) {
|
||||
return _databaseBackendSvc.moveRow(
|
||||
fromRowId: fromRow.id,
|
||||
toGroupId: groupId,
|
||||
toRowId: toRow?.id,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroup(
|
||||
{required String fromGroupId, required String toGroupId}) {
|
||||
return _databaseBackendSvc.moveGroup(
|
||||
fromGroupId: fromGroupId,
|
||||
toGroupId: toGroupId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateCalenderLayoutSetting(
|
||||
CalendarLayoutSettingsPB layoutSetting) async {
|
||||
await _databaseBackendSvc
|
||||
.updateLayoutSetting(calendarLayoutSetting: layoutSetting)
|
||||
.then((result) {
|
||||
result.fold((l) => null, (r) => Log.error(r));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _databaseBackendSvc.closeView();
|
||||
await fieldController.dispose();
|
||||
await groupListener.stop();
|
||||
}
|
||||
|
||||
Future<void> _loadGroups() async {
|
||||
final result = await _databaseBackendSvc.loadGroups();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(groups) {
|
||||
_groupCallbacks?.onGroupByField?.call(groups.items);
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadLayoutSetting() async {
|
||||
_databaseBackendSvc.getLayoutSetting(layoutType).then((result) {
|
||||
result.fold(
|
||||
(l) {
|
||||
_layoutCallbacks?.onLoadLayout(l);
|
||||
},
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _listenOnRowsChanged() {
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_databaseCallbacks?.onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void _listenOnFieldsChanged() {
|
||||
fieldController.addListener(
|
||||
onReceiveFields: (fields) {
|
||||
_databaseCallbacks?.onFieldsChanged?.call(UnmodifiableListView(fields));
|
||||
},
|
||||
onFilters: (filters) {
|
||||
_databaseCallbacks?.onFiltersChanged?.call(filters);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _listenOnGroupChanged() {
|
||||
groupListener.start(
|
||||
onNumOfGroupsChanged: (result) {
|
||||
result.fold((changeset) {
|
||||
if (changeset.updateGroups.isNotEmpty) {
|
||||
_groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups);
|
||||
}
|
||||
|
||||
if (changeset.deletedGroups.isNotEmpty) {
|
||||
_groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups);
|
||||
}
|
||||
|
||||
for (final insertedGroup in changeset.insertedGroups) {
|
||||
_groupCallbacks?.onInsertGroup?.call(insertedGroup);
|
||||
}
|
||||
}, (r) => Log.error(r));
|
||||
},
|
||||
onGroupByNewField: (result) {
|
||||
result.fold((groups) {
|
||||
_groupCallbacks?.onGroupByField?.call(groups);
|
||||
}, (r) => Log.error(r));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _listenOnLayoutChanged() {
|
||||
layoutListener.start(onLayoutChanged: (result) {
|
||||
result.fold((l) {
|
||||
_layoutCallbacks?.onLayoutChanged(l);
|
||||
}, (r) => Log.error(r));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class RowDataBuilder {
|
||||
final _cellDataByFieldId = <String, String>{};
|
||||
|
||||
void insertText(FieldInfo fieldInfo, String text) {
|
||||
assert(fieldInfo.fieldType == FieldType.RichText);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = text;
|
||||
}
|
||||
|
||||
void insertNumber(FieldInfo fieldInfo, int num) {
|
||||
assert(fieldInfo.fieldType == FieldType.Number);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = num.toString();
|
||||
}
|
||||
|
||||
void insertDate(FieldInfo fieldInfo, DateTime date) {
|
||||
assert(fieldInfo.fieldType == FieldType.DateTime);
|
||||
final timestamp = (date.millisecondsSinceEpoch ~/ 1000);
|
||||
_cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
|
||||
}
|
||||
|
||||
Map<String, String> build() {
|
||||
return _cellDataByFieldId;
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
@ -20,25 +23,56 @@ class DatabaseBackendService {
|
||||
return DatabaseEventGetDatabase(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) {
|
||||
var payload = CreateRowPayloadPB.create()..viewId = viewId;
|
||||
startRowId?.fold(() => null, (id) => payload.startRowId = id);
|
||||
return DatabaseEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(
|
||||
String groupId,
|
||||
Future<Either<RowPB, FlowyError>> createRow({
|
||||
String? startRowId,
|
||||
) {
|
||||
CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..groupId = groupId;
|
||||
|
||||
String? groupId,
|
||||
Map<String, String>? cellDataByFieldId,
|
||||
}) {
|
||||
var payload = CreateRowPayloadPB.create()..viewId = viewId;
|
||||
if (startRowId != null) {
|
||||
payload.startRowId = startRowId;
|
||||
}
|
||||
|
||||
return DatabaseEventCreateBoardCard(payload).send();
|
||||
if (groupId != null) {
|
||||
payload.groupId = groupId;
|
||||
}
|
||||
|
||||
if (cellDataByFieldId != null && cellDataByFieldId.isNotEmpty) {
|
||||
payload.data = RowDataPB(cellDataByFieldId: cellDataByFieldId);
|
||||
}
|
||||
|
||||
return DatabaseEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
required String fromRowId,
|
||||
required String? toGroupId,
|
||||
required String? toRowId,
|
||||
}) {
|
||||
var payload = MoveGroupRowPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fromRowId = fromRowId;
|
||||
if (toGroupId != null) {
|
||||
payload.toGroupId = toGroupId;
|
||||
}
|
||||
|
||||
if (toRowId != null) {
|
||||
payload.toRowId = toRowId;
|
||||
}
|
||||
|
||||
return DatabaseEventMoveGroupRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroup({
|
||||
required String fromGroupId,
|
||||
required String toGroupId,
|
||||
}) {
|
||||
final payload = MoveGroupPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fromGroupId = fromGroupId
|
||||
..toGroupId = toGroupId;
|
||||
|
||||
return DatabaseEventMoveGroup(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<List<FieldPB>, FlowyError>> getFields(
|
||||
@ -53,6 +87,28 @@ class DatabaseBackendService {
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
|
||||
LayoutTypePB layoutType) {
|
||||
final payload = DatabaseLayoutIdPB.create()
|
||||
..viewId = viewId
|
||||
..layout = layoutType;
|
||||
return DatabaseEventGetLayoutSetting(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateLayoutSetting(
|
||||
{CalendarLayoutSettingsPB? calendarLayoutSetting}) {
|
||||
final layoutSetting = LayoutSettingPB.create();
|
||||
if (calendarLayoutSetting != null) {
|
||||
layoutSetting.calendar = calendarLayoutSetting;
|
||||
}
|
||||
|
||||
final payload = UpdateLayoutSettingPB.create()
|
||||
..viewId = viewId
|
||||
..layoutSetting = layoutSetting;
|
||||
|
||||
return DatabaseEventSetLayoutSetting(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> closeView() {
|
||||
final request = ViewIdPB(value: viewId);
|
||||
return FolderEventCloseView(request).send();
|
||||
|
@ -11,20 +11,20 @@ import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart
|
||||
typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
|
||||
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
|
||||
|
||||
class BoardListener {
|
||||
class DatabaseGroupListener {
|
||||
final String viewId;
|
||||
PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
|
||||
PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
|
||||
PublishNotifier<GroupUpdateValue>? _numOfGroupsNotifier = PublishNotifier();
|
||||
PublishNotifier<GroupByNewFieldValue>? _groupByFieldNotifier =
|
||||
PublishNotifier();
|
||||
DatabaseNotificationListener? _listener;
|
||||
BoardListener(this.viewId);
|
||||
DatabaseGroupListener(this.viewId);
|
||||
|
||||
void start({
|
||||
required void Function(GroupUpdateValue) onBoardChanged,
|
||||
required void Function(GroupUpdateValue) onNumOfGroupsChanged,
|
||||
required void Function(GroupByNewFieldValue) onGroupByNewField,
|
||||
}) {
|
||||
_groupUpdateNotifier?.addPublishListener(onBoardChanged);
|
||||
_groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
|
||||
_numOfGroupsNotifier?.addPublishListener(onNumOfGroupsChanged);
|
||||
_groupByFieldNotifier?.addPublishListener(onGroupByNewField);
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
@ -38,16 +38,16 @@ class BoardListener {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateGroups:
|
||||
result.fold(
|
||||
(payload) => _groupUpdateNotifier?.value =
|
||||
(payload) => _numOfGroupsNotifier?.value =
|
||||
left(GroupChangesetPB.fromBuffer(payload)),
|
||||
(error) => _groupUpdateNotifier?.value = right(error),
|
||||
(error) => _numOfGroupsNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.DidGroupByField:
|
||||
result.fold(
|
||||
(payload) => _groupByNewFieldNotifier?.value =
|
||||
(payload) => _groupByFieldNotifier?.value =
|
||||
left(GroupChangesetPB.fromBuffer(payload).initialGroups),
|
||||
(error) => _groupByNewFieldNotifier?.value = right(error),
|
||||
(error) => _groupByFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@ -57,10 +57,10 @@ class BoardListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_groupUpdateNotifier?.dispose();
|
||||
_groupUpdateNotifier = null;
|
||||
_numOfGroupsNotifier?.dispose();
|
||||
_numOfGroupsNotifier = null;
|
||||
|
||||
_groupByNewFieldNotifier?.dispose();
|
||||
_groupByNewFieldNotifier = null;
|
||||
_groupByFieldNotifier?.dispose();
|
||||
_groupByFieldNotifier = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
typedef LayoutSettingsValue<T> = Either<T, FlowyError>;
|
||||
|
||||
class DatabaseLayoutListener {
|
||||
final String viewId;
|
||||
PublishNotifier<LayoutSettingsValue<LayoutSettingPB>>? _settingNotifier =
|
||||
PublishNotifier();
|
||||
DatabaseNotificationListener? _listener;
|
||||
DatabaseLayoutListener(this.viewId);
|
||||
|
||||
void start({
|
||||
required void Function(LayoutSettingsValue<LayoutSettingPB>)
|
||||
onLayoutChanged,
|
||||
}) {
|
||||
_settingNotifier?.addPublishListener(onLayoutChanged);
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateLayoutSettings:
|
||||
result.fold(
|
||||
(payload) => _settingNotifier?.value =
|
||||
left(LayoutSettingPB.fromBuffer(payload)),
|
||||
(error) => _settingNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_settingNotifier?.dispose();
|
||||
_settingNotifier = null;
|
||||
}
|
||||
}
|
@ -61,7 +61,7 @@ class RowCache {
|
||||
});
|
||||
}
|
||||
|
||||
void initializeRows(List<RowPB> rows) {
|
||||
void setInitialRows(List<RowPB> rows) {
|
||||
for (final row in rows) {
|
||||
final rowInfo = buildGridRow(row);
|
||||
_rowList.add(rowInfo);
|
||||
|
@ -4,25 +4,27 @@ import 'row_cache.dart';
|
||||
|
||||
typedef OnRowChanged = void Function(CellByFieldId, RowsChangedReason);
|
||||
|
||||
class RowDataController {
|
||||
final RowInfo rowInfo;
|
||||
class RowController {
|
||||
final String rowId;
|
||||
final String viewId;
|
||||
final List<VoidCallback> _onRowChangedListeners = [];
|
||||
final RowCache _rowCache;
|
||||
|
||||
get cellCache => _rowCache.cellCache;
|
||||
|
||||
RowDataController({
|
||||
required this.rowInfo,
|
||||
RowController({
|
||||
required this.rowId,
|
||||
required this.viewId,
|
||||
required RowCache rowCache,
|
||||
}) : _rowCache = rowCache;
|
||||
|
||||
CellByFieldId loadData() {
|
||||
return _rowCache.loadGridCells(rowInfo.rowPB.id);
|
||||
return _rowCache.loadGridCells(rowId);
|
||||
}
|
||||
|
||||
void addListener({OnRowChanged? onRowChanged}) {
|
||||
_onRowChangedListeners.add(_rowCache.addListener(
|
||||
rowId: rowInfo.rowPB.id,
|
||||
rowId: rowId,
|
||||
onCellUpdated: onRowChanged,
|
||||
));
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.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-database/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
|
||||
class RowBackendService {
|
||||
@ -44,52 +42,3 @@ class RowBackendService {
|
||||
return DatabaseEventDuplicateRow(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
class GroupBackendService {
|
||||
final String viewId;
|
||||
|
||||
GroupBackendService({
|
||||
required this.viewId,
|
||||
});
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
required String fromRowId,
|
||||
required String toRowId,
|
||||
}) {
|
||||
var payload = MoveRowPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fromRowId = fromRowId
|
||||
..toRowId = toRowId;
|
||||
|
||||
return DatabaseEventMoveRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroupRow({
|
||||
required String fromRowId,
|
||||
required String toGroupId,
|
||||
required String? toRowId,
|
||||
}) {
|
||||
var payload = MoveGroupRowPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fromRowId = fromRowId
|
||||
..toGroupId = toGroupId;
|
||||
|
||||
if (toRowId != null) {
|
||||
payload.toRowId = toRowId;
|
||||
}
|
||||
|
||||
return DatabaseEventMoveGroupRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroup({
|
||||
required String fromGroupId,
|
||||
required String toGroupId,
|
||||
}) {
|
||||
final payload = MoveGroupPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fromGroupId = fromGroupId
|
||||
..toGroupId = toGroupId;
|
||||
|
||||
return DatabaseEventMoveGroup(payload).send();
|
||||
}
|
||||
}
|
||||
|
@ -13,25 +13,25 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../application/field/field_controller.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
import '../../application/row/row_service.dart';
|
||||
import 'board_data_controller.dart';
|
||||
import '../../application/database_controller.dart';
|
||||
import 'group_controller.dart';
|
||||
|
||||
part 'board_bloc.freezed.dart';
|
||||
|
||||
class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
final BoardDataController _boardDataController;
|
||||
final DatabaseController _databaseController;
|
||||
late final AppFlowyBoardController boardController;
|
||||
final GroupBackendService _groupBackendSvc;
|
||||
final LinkedHashMap<String, GroupController> groupControllers =
|
||||
LinkedHashMap();
|
||||
|
||||
FieldController get fieldController => _boardDataController.fieldController;
|
||||
String get viewId => _boardDataController.viewId;
|
||||
FieldController get fieldController => _databaseController.fieldController;
|
||||
String get viewId => _databaseController.viewId;
|
||||
|
||||
BoardBloc({required ViewPB view})
|
||||
: _groupBackendSvc = GroupBackendService(viewId: view.id),
|
||||
_boardDataController = BoardDataController(view: view),
|
||||
: _databaseController = DatabaseController(
|
||||
view: view,
|
||||
layoutType: LayoutTypePB.Board,
|
||||
),
|
||||
super(BoardState.initial(view.id)) {
|
||||
boardController = AppFlowyBoardController(
|
||||
onMoveGroup: (
|
||||
@ -40,7 +40,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
toGroupId,
|
||||
toIndex,
|
||||
) {
|
||||
_moveGroup(fromGroupId, toGroupId);
|
||||
_databaseController.moveGroup(
|
||||
fromGroupId: fromGroupId,
|
||||
toGroupId: toGroupId,
|
||||
);
|
||||
},
|
||||
onMoveGroupItem: (
|
||||
groupId,
|
||||
@ -49,7 +52,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
) {
|
||||
final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
|
||||
_moveRow(fromRow, groupId, toRow);
|
||||
if (fromRow != null) {
|
||||
_databaseController.moveRow(
|
||||
fromRow,
|
||||
toRow: toRow,
|
||||
groupId: groupId,
|
||||
);
|
||||
}
|
||||
},
|
||||
onMoveGroupItemToGroup: (
|
||||
fromGroupId,
|
||||
@ -59,7 +68,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
) {
|
||||
final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
|
||||
_moveRow(fromRow, toGroupId, toRow);
|
||||
if (fromRow != null) {
|
||||
_databaseController.moveRow(
|
||||
fromRow,
|
||||
toRow: toRow,
|
||||
groupId: toGroupId,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -72,8 +87,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
},
|
||||
createBottomRow: (groupId) async {
|
||||
final startRowId = groupControllers[groupId]?.lastRow()?.id;
|
||||
final result = await _boardDataController.createBoardCard(
|
||||
groupId,
|
||||
final result = await _databaseController.createRow(
|
||||
groupId: groupId,
|
||||
startRowId: startRowId,
|
||||
);
|
||||
result.fold(
|
||||
@ -82,7 +97,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
);
|
||||
},
|
||||
createHeaderRow: (String groupId) async {
|
||||
final result = await _boardDataController.createBoardCard(groupId);
|
||||
final result =
|
||||
await _databaseController.createRow(groupId: groupId);
|
||||
result.fold(
|
||||
(_) {},
|
||||
(err) => Log.error(err),
|
||||
@ -141,44 +157,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
}
|
||||
|
||||
boardController.enableGroupDragging(!isEdit);
|
||||
// boardController.updateGroupItem(
|
||||
// group.groupId,
|
||||
// GroupItem(
|
||||
// row: row,
|
||||
// fieldInfo: fieldInfo,
|
||||
// isDraggable: !isEdit,
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
|
||||
void _moveRow(RowPB? fromRow, String columnId, RowPB? toRow) {
|
||||
if (fromRow != null) {
|
||||
_groupBackendSvc
|
||||
.moveGroupRow(
|
||||
fromRowId: fromRow.id,
|
||||
toGroupId: columnId,
|
||||
toRowId: toRow?.id,
|
||||
)
|
||||
.then((result) {
|
||||
result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _moveGroup(String fromGroupId, String toGroupId) {
|
||||
_groupBackendSvc
|
||||
.moveGroup(
|
||||
fromGroupId: fromGroupId,
|
||||
toGroupId: toGroupId,
|
||||
)
|
||||
.then((result) {
|
||||
result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _boardDataController.dispose();
|
||||
await _databaseController.dispose();
|
||||
for (final controller in groupControllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
@ -204,34 +187,36 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
}
|
||||
|
||||
RowCache? getRowCache(String blockId) {
|
||||
return _boardDataController.rowCache;
|
||||
return _databaseController.rowCache;
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_boardDataController.addListener(
|
||||
onDatabaseChanged: (grid) {
|
||||
final onDatabaseChanged = DatabaseCallbacks(
|
||||
onDatabaseChanged: (database) {
|
||||
if (!isClosed) {
|
||||
add(BoardEvent.didReceiveGridUpdate(grid));
|
||||
add(BoardEvent.didReceiveGridUpdate(database));
|
||||
}
|
||||
},
|
||||
didLoadGroups: (groups) {
|
||||
);
|
||||
final onGroupChanged = GroupCallbacks(
|
||||
onGroupByField: (groups) {
|
||||
if (isClosed) return;
|
||||
initializeGroups(groups);
|
||||
add(BoardEvent.didReceiveGroups(groups));
|
||||
},
|
||||
onDeletedGroup: (groupIds) {
|
||||
onDeleteGroup: (groupIds) {
|
||||
if (isClosed) return;
|
||||
boardController.removeGroups(groupIds);
|
||||
},
|
||||
onInsertedGroup: (insertedGroup) {
|
||||
onInsertGroup: (insertGroups) {
|
||||
if (isClosed) return;
|
||||
final group = insertedGroup.group;
|
||||
final group = insertGroups.group;
|
||||
final newGroup = initializeGroupData(group);
|
||||
final controller = initializeGroupController(group);
|
||||
groupControllers[controller.group.groupId] = (controller);
|
||||
boardController.addGroup(newGroup);
|
||||
},
|
||||
onUpdatedGroup: (updatedGroups) {
|
||||
onUpdateGroup: (updatedGroups) {
|
||||
if (isClosed) return;
|
||||
for (final group in updatedGroups) {
|
||||
final columnController =
|
||||
@ -239,15 +224,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
columnController?.updateGroupName(group.desc);
|
||||
}
|
||||
},
|
||||
onError: (err) {
|
||||
Log.error(err);
|
||||
},
|
||||
onResetGroups: (groups) {
|
||||
if (isClosed) return;
|
||||
);
|
||||
|
||||
initializeGroups(groups);
|
||||
add(BoardEvent.didReceiveGroups(groups));
|
||||
},
|
||||
_databaseController.addListener(
|
||||
onDatabaseChanged: onDatabaseChanged,
|
||||
onGroupChanged: onGroupChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@ -264,7 +245,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
}
|
||||
|
||||
Future<void> _openGrid(Emitter<BoardState> emit) async {
|
||||
final result = await _boardDataController.openGrid();
|
||||
final result = await _databaseController.open();
|
||||
result.fold(
|
||||
(grid) => emit(
|
||||
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
|
||||
|
@ -1,144 +0,0 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
|
||||
import '../../application/database_service.dart';
|
||||
import '../../application/defines.dart';
|
||||
import '../../application/field/field_controller.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
import '../../application/view/view_cache.dart';
|
||||
import 'board_listener.dart';
|
||||
|
||||
typedef DidLoadGroups = void Function(List<GroupPB>);
|
||||
typedef OnUpdatedGroup = void Function(List<GroupPB>);
|
||||
typedef OnDeletedGroup = void Function(List<String>);
|
||||
typedef OnInsertedGroup = void Function(InsertedGroupPB);
|
||||
typedef OnResetGroups = void Function(List<GroupPB>);
|
||||
|
||||
class BoardDataController {
|
||||
final String viewId;
|
||||
final DatabaseBackendService _databaseSvc;
|
||||
final FieldController fieldController;
|
||||
final BoardListener _listener;
|
||||
late DatabaseViewCache _viewCache;
|
||||
|
||||
OnFieldsChanged? _onFieldsChanged;
|
||||
OnDatabaseChanged? _onDatabaseChanged;
|
||||
DidLoadGroups? _didLoadGroup;
|
||||
OnRowsChanged? _onRowsChanged;
|
||||
OnError? _onError;
|
||||
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
RowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
BoardDataController({required ViewPB view})
|
||||
: viewId = view.id,
|
||||
_listener = BoardListener(view.id),
|
||||
_databaseSvc = DatabaseBackendService(viewId: view.id),
|
||||
fieldController = FieldController(viewId: view.id) {
|
||||
//
|
||||
_viewCache = DatabaseViewCache(
|
||||
viewId: view.id,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required OnDatabaseChanged onDatabaseChanged,
|
||||
OnFieldsChanged? onFieldsChanged,
|
||||
required DidLoadGroups didLoadGroups,
|
||||
OnRowsChanged? onRowsChanged,
|
||||
required OnUpdatedGroup onUpdatedGroup,
|
||||
required OnDeletedGroup onDeletedGroup,
|
||||
required OnInsertedGroup onInsertedGroup,
|
||||
required OnResetGroups onResetGroups,
|
||||
required OnError? onError,
|
||||
}) {
|
||||
_onDatabaseChanged = onDatabaseChanged;
|
||||
_onFieldsChanged = onFieldsChanged;
|
||||
_didLoadGroup = didLoadGroups;
|
||||
_onRowsChanged = onRowsChanged;
|
||||
_onError = onError;
|
||||
|
||||
fieldController.addListener(onReceiveFields: (fields) {
|
||||
_onFieldsChanged?.call(UnmodifiableListView(fields));
|
||||
});
|
||||
|
||||
_listener.start(
|
||||
onBoardChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
if (changeset.updateGroups.isNotEmpty) {
|
||||
onUpdatedGroup.call(changeset.updateGroups);
|
||||
}
|
||||
|
||||
if (changeset.deletedGroups.isNotEmpty) {
|
||||
onDeletedGroup.call(changeset.deletedGroups);
|
||||
}
|
||||
|
||||
for (final insertedGroup in changeset.insertedGroups) {
|
||||
onInsertedGroup.call(insertedGroup);
|
||||
}
|
||||
},
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
onGroupByNewField: (result) {
|
||||
result.fold(
|
||||
(groups) => onResetGroups(groups),
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> openGrid() async {
|
||||
final result = await _databaseSvc.openGrid();
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_onDatabaseChanged?.call(grid);
|
||||
return fieldController.loadFields(fieldIds: grid.fields).then((result) {
|
||||
return result.fold(
|
||||
(l) => Future(() async {
|
||||
await _loadGroups();
|
||||
_viewCache.rowCache.initializeRows(grid.rows);
|
||||
return left(l);
|
||||
}),
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
|
||||
{String? startRowId}) {
|
||||
return _databaseSvc.createBoardCard(groupId, startRowId);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _viewCache.dispose();
|
||||
await _databaseSvc.closeView();
|
||||
await fieldController.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadGroups() async {
|
||||
final result = await _databaseSvc.loadGroups();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(groups) {
|
||||
_didLoadGroup?.call(groups.items);
|
||||
},
|
||||
(err) => _onError?.call(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../../../application/cell/cell_service.dart';
|
||||
import '../../../application/row/row_cache.dart';
|
||||
import '../../presentation/card/card_cell_builder.dart';
|
||||
|
||||
typedef OnCardChanged = void Function(CellByFieldId, RowsChangedReason);
|
||||
|
||||
class CardDataController extends BoardCellBuilderDelegate {
|
||||
final RowPB rowPB;
|
||||
final RowCache _rowCache;
|
||||
final List<VoidCallback> _onCardChangedListeners = [];
|
||||
|
||||
CardDataController({
|
||||
required this.rowPB,
|
||||
required RowCache rowCache,
|
||||
}) : _rowCache = rowCache;
|
||||
|
||||
CellByFieldId loadData() {
|
||||
return _rowCache.loadGridCells(rowPB.id);
|
||||
}
|
||||
|
||||
void addListener({OnCardChanged? onRowChanged}) {
|
||||
_onCardChangedListeners.add(_rowCache.addListener(
|
||||
rowId: rowPB.id,
|
||||
onCellUpdated: onRowChanged,
|
||||
));
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
for (final fn in _onCardChangedListeners) {
|
||||
_rowCache.removeRowListener(fn);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
CellCache get cellCache => _rowCache.cellCache;
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'group_listener.dart';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
typedef OnGroupError = void Function(FlowyError);
|
||||
|
||||
@ -14,14 +18,14 @@ abstract class GroupControllerDelegate {
|
||||
|
||||
class GroupController {
|
||||
final GroupPB group;
|
||||
final GroupListener _listener;
|
||||
final SingleGroupListener _listener;
|
||||
final GroupControllerDelegate delegate;
|
||||
|
||||
GroupController({
|
||||
required String viewId,
|
||||
required this.group,
|
||||
required this.delegate,
|
||||
}) : _listener = GroupListener(group);
|
||||
}) : _listener = SingleGroupListener(group);
|
||||
|
||||
RowPB? rowAtIndex(int index) {
|
||||
if (index < group.rows.length) {
|
||||
@ -81,3 +85,45 @@ class GroupController {
|
||||
_listener.stop();
|
||||
}
|
||||
}
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class SingleGroupListener {
|
||||
final GroupPB group;
|
||||
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
|
||||
DatabaseNotificationListener? _listener;
|
||||
SingleGroupListener(this.group);
|
||||
|
||||
void start({
|
||||
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
|
||||
}) {
|
||||
_groupNotifier?.addPublishListener(onGroupChanged);
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: group.groupId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateGroupRow:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_groupNotifier?.dispose();
|
||||
_groupNotifier = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class GroupListener {
|
||||
final GroupPB group;
|
||||
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
|
||||
DatabaseNotificationListener? _listener;
|
||||
GroupListener(this.group);
|
||||
|
||||
void start({
|
||||
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
|
||||
}) {
|
||||
_groupNotifier?.addPublishListener(onGroupChanged);
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: group.groupId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateGroupRow:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_groupNotifier?.dispose();
|
||||
_groupNotifier = null;
|
||||
}
|
||||
}
|
@ -6,8 +6,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/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
@ -17,13 +16,14 @@ import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide Card;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../widgets/card/cells/card_cell.dart';
|
||||
import '../../widgets/card/card_cell_builder.dart';
|
||||
import '../../widgets/row/cell_builder.dart';
|
||||
import '../application/board_bloc.dart';
|
||||
import '../application/card/card_data_controller.dart';
|
||||
import 'card/card.dart';
|
||||
import 'card/card_cell_builder.dart';
|
||||
import '../../widgets/card/card.dart';
|
||||
import 'toolbar/board_toolbar.dart';
|
||||
|
||||
class BoardPage extends StatelessWidget {
|
||||
@ -78,6 +78,7 @@ class BoardContent extends StatefulWidget {
|
||||
|
||||
class _BoardContentState extends State<BoardContent> {
|
||||
late AppFlowyBoardScrollController scrollManager;
|
||||
final cardConfiguration = CardConfiguration<String>();
|
||||
|
||||
final config = AppFlowyBoardConfig(
|
||||
groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
|
||||
@ -86,6 +87,16 @@ class _BoardContentState extends State<BoardContent> {
|
||||
@override
|
||||
void initState() {
|
||||
scrollManager = AppFlowyBoardScrollController();
|
||||
cardConfiguration.addSelectOptionHook((options, groupId) {
|
||||
// The cell should hide if the option id is equal to the groupId.
|
||||
final isInGroup =
|
||||
options.where((element) => element.id == groupId).isNotEmpty;
|
||||
if (isInGroup || options.isEmpty) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -225,15 +236,11 @@ class _BoardContentState extends State<BoardContent> {
|
||||
|
||||
/// Return placeholder widget if the rowCache is null.
|
||||
if (rowCache == null) return SizedBox(key: ObjectKey(groupItem));
|
||||
|
||||
final cellCache = rowCache.cellCache;
|
||||
final fieldController = context.read<BoardBloc>().fieldController;
|
||||
final viewId = context.read<BoardBloc>().viewId;
|
||||
final cardController = CardDataController(
|
||||
rowCache: rowCache,
|
||||
rowPB: rowPB,
|
||||
);
|
||||
|
||||
final cellBuilder = BoardCellBuilder(cardController);
|
||||
final cellBuilder = CardCellBuilder<String>(cellCache);
|
||||
bool isEditing = false;
|
||||
context.read<BoardBloc>().state.editingRow.fold(
|
||||
() => null,
|
||||
@ -247,13 +254,15 @@ class _BoardContentState extends State<BoardContent> {
|
||||
key: ValueKey(groupItemId),
|
||||
margin: config.cardPadding,
|
||||
decoration: _makeBoxDecoration(context),
|
||||
child: BoardCard(
|
||||
child: Card<String>(
|
||||
row: rowPB,
|
||||
viewId: viewId,
|
||||
groupId: groupData.group.groupId,
|
||||
rowCache: rowCache,
|
||||
cardData: groupData.group.groupId,
|
||||
fieldId: groupItem.fieldInfo.id,
|
||||
isEditing: isEditing,
|
||||
cellBuilder: cellBuilder,
|
||||
dataController: cardController,
|
||||
configuration: cardConfiguration,
|
||||
openCard: (context) => _openCard(
|
||||
viewId,
|
||||
fieldController,
|
||||
@ -303,8 +312,9 @@ class _BoardContentState extends State<BoardContent> {
|
||||
rowPB: rowPB,
|
||||
);
|
||||
|
||||
final dataController = RowDataController(
|
||||
rowInfo: rowInfo,
|
||||
final dataController = RowController(
|
||||
rowId: rowInfo.rowPB.id,
|
||||
viewId: rowInfo.viewId,
|
||||
rowCache: rowCache,
|
||||
);
|
||||
|
||||
|
@ -5,7 +5,6 @@ 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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'board_setting.dart';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
@ -9,20 +10,24 @@ import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'calendar_data_controller.dart';
|
||||
import '../../application/database_controller.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
|
||||
part 'calendar_bloc.freezed.dart';
|
||||
|
||||
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
final CalendarDataController _databaseDataController;
|
||||
final EventController calendarEventsController = EventController();
|
||||
final DatabaseController _databaseController;
|
||||
|
||||
FieldController get fieldController =>
|
||||
_databaseDataController.fieldController;
|
||||
String get databaseId => _databaseDataController.databaseId;
|
||||
// Getters
|
||||
String get viewId => _databaseController.viewId;
|
||||
CellCache get cellCache => _databaseController.rowCache.cellCache;
|
||||
RowCache get rowCache => _databaseController.rowCache;
|
||||
|
||||
CalendarBloc({required ViewPB view})
|
||||
: _databaseDataController = CalendarDataController(view: view),
|
||||
: _databaseController = DatabaseController(
|
||||
view: view,
|
||||
layoutType: LayoutTypePB.Calendar,
|
||||
),
|
||||
super(CalendarState.initial(view.id)) {
|
||||
on<CalendarEvent>(
|
||||
(event, emit) async {
|
||||
@ -30,23 +35,57 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
initial: () async {
|
||||
_startListening();
|
||||
await _openDatabase(emit);
|
||||
_loadAllEvents();
|
||||
},
|
||||
didReceiveCalendarSettings: (CalendarSettingsPB settings) {
|
||||
didReceiveCalendarSettings: (CalendarLayoutSettingsPB settings) {
|
||||
emit(state.copyWith(settings: Some(settings)));
|
||||
},
|
||||
didReceiveDatabaseUpdate: (DatabasePB database) {
|
||||
emit(state.copyWith(database: Some(database)));
|
||||
},
|
||||
didReceiveError: (FlowyError error) {
|
||||
emit(state.copyWith(noneOrError: Some(error)));
|
||||
didLoadAllEvents: (events) {
|
||||
emit(state.copyWith(events: events));
|
||||
},
|
||||
createEvent: (DateTime date, String title) async {
|
||||
await _createEvent(date, title);
|
||||
},
|
||||
didReceiveEvent: (CalendarEventData<CalendarCardData> newEvent) {
|
||||
emit(state.copyWith(events: [...state.events, newEvent]));
|
||||
},
|
||||
didUpdateFieldInfos: (Map<String, FieldInfo> fieldInfoByFieldId) {
|
||||
emit(state.copyWith(fieldInfoByFieldId: fieldInfoByFieldId));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
FieldInfo? _getCalendarFieldInfo(String fieldId) {
|
||||
final fieldInfos = _databaseController.fieldController.fieldInfos;
|
||||
final index = fieldInfos.indexWhere(
|
||||
(element) => element.field.id == fieldId,
|
||||
);
|
||||
if (index != -1) {
|
||||
return fieldInfos[index];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo? _getTitleFieldInfo() {
|
||||
final fieldInfos = _databaseController.fieldController.fieldInfos;
|
||||
final index = fieldInfos.indexWhere(
|
||||
(element) => element.field.isPrimary,
|
||||
);
|
||||
if (index != -1) {
|
||||
return fieldInfos[index];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
|
||||
final result = await _databaseDataController.openDatabase();
|
||||
final result = await _databaseController.open();
|
||||
result.fold(
|
||||
(database) => emit(
|
||||
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
|
||||
@ -57,60 +96,145 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
);
|
||||
}
|
||||
|
||||
RowCache? getRowCache(String blockId) {
|
||||
return _databaseDataController.rowCache;
|
||||
Future<void> _createEvent(DateTime date, String title) async {
|
||||
state.settings.fold(
|
||||
() => null,
|
||||
(settings) async {
|
||||
final dateField = _getCalendarFieldInfo(settings.layoutFieldId);
|
||||
final titleField = _getTitleFieldInfo();
|
||||
if (dateField != null && titleField != null) {
|
||||
final result = await _databaseController.createRow(
|
||||
withCells: (builder) {
|
||||
builder.insertDate(dateField, date);
|
||||
builder.insertText(titleField, title);
|
||||
},
|
||||
);
|
||||
|
||||
result.fold(
|
||||
(newRow) => _loadEvent(newRow.id),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_databaseDataController.addListener(
|
||||
onDatabaseChanged: (database) {
|
||||
if (!isClosed) return;
|
||||
|
||||
add(CalendarEvent.didReceiveDatabaseUpdate(database));
|
||||
},
|
||||
onSettingsChanged: (CalendarSettingsPB settings) {
|
||||
if (isClosed) return;
|
||||
add(CalendarEvent.didReceiveCalendarSettings(settings));
|
||||
},
|
||||
onArrangeWithNewField: (field) {
|
||||
if (isClosed) return;
|
||||
_initializeEvents(field);
|
||||
// add(CalendarEvent.)
|
||||
},
|
||||
onError: (err) {
|
||||
Log.error(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _initializeEvents(FieldPB dateField) {
|
||||
calendarEventsController.removeWhere((element) => true);
|
||||
Future<void> _loadEvent(String rowId) async {
|
||||
final payload = RowIdPB(viewId: viewId, rowId: rowId);
|
||||
DatabaseEventGetCalendarEvent(payload).send().then((result) {
|
||||
result.fold(
|
||||
(eventPB) {
|
||||
final calendarEvent = _calendarEventDataFromEventPB(eventPB);
|
||||
if (calendarEvent != null) {
|
||||
add(CalendarEvent.didReceiveEvent(calendarEvent));
|
||||
}
|
||||
},
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const events = <CalendarEventData<CalendarData>>[];
|
||||
|
||||
// final List<CalendarEventData<CalendarData>> events = rows.map((row) {
|
||||
// final event = CalendarEventData(
|
||||
// title: "",
|
||||
// date: row -> dateField -> value,
|
||||
// event: row,
|
||||
// );
|
||||
|
||||
// return event;
|
||||
// }).toList();
|
||||
|
||||
calendarEventsController.addAll(events);
|
||||
Future<void> _loadAllEvents() async {
|
||||
final payload = CalendarEventRequestPB.create()..viewId = viewId;
|
||||
DatabaseEventGetAllCalendarEvents(payload).send().then((result) {
|
||||
result.fold(
|
||||
(events) {
|
||||
if (!isClosed) {
|
||||
final calendarEvents = <CalendarEventData<CalendarCardData>>[];
|
||||
for (final eventPB in events.items) {
|
||||
final calendarEvent = _calendarEventDataFromEventPB(eventPB);
|
||||
if (calendarEvent != null) {
|
||||
calendarEvents.add(calendarEvent);
|
||||
}
|
||||
}
|
||||
|
||||
add(CalendarEvent.didLoadAllEvents(calendarEvents));
|
||||
}
|
||||
},
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
CalendarEventData<CalendarCardData>? _calendarEventDataFromEventPB(
|
||||
CalendarEventPB eventPB) {
|
||||
final fieldInfo = state.fieldInfoByFieldId[eventPB.titleFieldId];
|
||||
if (fieldInfo != null) {
|
||||
final cellId = CellIdentifier(
|
||||
viewId: viewId,
|
||||
rowId: eventPB.rowId,
|
||||
fieldInfo: fieldInfo,
|
||||
);
|
||||
|
||||
final eventData = CalendarCardData(
|
||||
event: eventPB,
|
||||
cellId: cellId,
|
||||
);
|
||||
|
||||
final date = DateTime.fromMillisecondsSinceEpoch(
|
||||
eventPB.timestamp.toInt() * 1000,
|
||||
isUtc: true,
|
||||
);
|
||||
return CalendarEventData(
|
||||
title: eventPB.title,
|
||||
date: date,
|
||||
event: eventData,
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
final onDatabaseChanged = DatabaseCallbacks(
|
||||
onDatabaseChanged: (database) {
|
||||
if (isClosed) return;
|
||||
},
|
||||
onFieldsChanged: (fieldInfos) {
|
||||
if (isClosed) return;
|
||||
final fieldInfoByFieldId = {
|
||||
for (var fieldInfo in fieldInfos) fieldInfo.field.id: fieldInfo
|
||||
};
|
||||
add(CalendarEvent.didUpdateFieldInfos(fieldInfoByFieldId));
|
||||
},
|
||||
);
|
||||
|
||||
final onLayoutChanged = LayoutCallbacks(
|
||||
onLayoutChanged: _didReceiveLayoutSetting,
|
||||
onLoadLayout: _didReceiveLayoutSetting,
|
||||
);
|
||||
|
||||
_databaseController.addListener(
|
||||
onDatabaseChanged: onDatabaseChanged,
|
||||
onLayoutChanged: onLayoutChanged,
|
||||
);
|
||||
}
|
||||
|
||||
void _didReceiveLayoutSetting(LayoutSettingPB layoutSetting) {
|
||||
if (layoutSetting.hasCalendar()) {
|
||||
if (isClosed) return;
|
||||
add(CalendarEvent.didReceiveCalendarSettings(layoutSetting.calendar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef Events = List<CalendarEventData<CalendarCardData>>;
|
||||
|
||||
@freezed
|
||||
class CalendarEvent with _$CalendarEvent {
|
||||
const factory CalendarEvent.initial() = _InitialCalendar;
|
||||
const factory CalendarEvent.didReceiveCalendarSettings(
|
||||
CalendarSettingsPB settings) = _DidReceiveCalendarSettings;
|
||||
const factory CalendarEvent.didReceiveError(FlowyError error) =
|
||||
_DidReceiveError;
|
||||
CalendarLayoutSettingsPB settings) = _ReceiveCalendarSettings;
|
||||
const factory CalendarEvent.didLoadAllEvents(Events events) =
|
||||
_ReceiveCalendarEvents;
|
||||
const factory CalendarEvent.didReceiveEvent(
|
||||
CalendarEventData<CalendarCardData> event) = _ReceiveEvent;
|
||||
const factory CalendarEvent.didUpdateFieldInfos(
|
||||
Map<String, FieldInfo> fieldInfoByFieldId) = _DidUpdateFieldInfos;
|
||||
const factory CalendarEvent.createEvent(DateTime date, String title) =
|
||||
_CreateEvent;
|
||||
const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
|
||||
_DidReceiveDatabaseUpdate;
|
||||
_ReceiveDatabaseUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -118,9 +242,9 @@ class CalendarState with _$CalendarState {
|
||||
const factory CalendarState({
|
||||
required String databaseId,
|
||||
required Option<DatabasePB> database,
|
||||
required Option<FieldPB> dateField,
|
||||
required Option<List<RowInfo>> unscheduledRows,
|
||||
required Option<CalendarSettingsPB> settings,
|
||||
required Events events,
|
||||
required Map<String, FieldInfo> fieldInfoByFieldId,
|
||||
required Option<CalendarLayoutSettingsPB> settings,
|
||||
required DatabaseLoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _CalendarState;
|
||||
@ -128,8 +252,8 @@ class CalendarState with _$CalendarState {
|
||||
factory CalendarState.initial(String databaseId) => CalendarState(
|
||||
database: none(),
|
||||
databaseId: databaseId,
|
||||
dateField: none(),
|
||||
unscheduledRows: none(),
|
||||
fieldInfoByFieldId: {},
|
||||
events: [],
|
||||
settings: none(),
|
||||
noneOrError: none(),
|
||||
loadingState: const _Loading(),
|
||||
@ -153,7 +277,8 @@ class CalendarEditingRow {
|
||||
});
|
||||
}
|
||||
|
||||
class CalendarData {
|
||||
final RowInfo rowInfo;
|
||||
CalendarData(this.rowInfo);
|
||||
class CalendarCardData {
|
||||
final CalendarEventPB event;
|
||||
final CellIdentifier cellId;
|
||||
CalendarCardData({required this.cellId, required this.event});
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/plugins/database_view/application/database_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import 'calendar_listener.dart';
|
||||
|
||||
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
|
||||
typedef OnDatabaseChanged = void Function(DatabasePB);
|
||||
typedef OnSettingsChanged = void Function(CalendarSettingsPB);
|
||||
typedef OnArrangeWithNewField = void Function(FieldPB);
|
||||
|
||||
typedef OnRowsChanged = void Function(List<RowInfo>, RowsChangedReason);
|
||||
typedef OnError = void Function(FlowyError);
|
||||
|
||||
class CalendarDataController {
|
||||
final String databaseId;
|
||||
final DatabaseBackendService _databaseBackendSvc;
|
||||
final FieldController fieldController;
|
||||
final CalendarListener _listener;
|
||||
late DatabaseViewCache _viewCache;
|
||||
|
||||
OnFieldsChanged? _onFieldsChanged;
|
||||
OnDatabaseChanged? _onDatabaseChanged;
|
||||
OnRowsChanged? _onRowsChanged;
|
||||
OnSettingsChanged? _onSettingsChanged;
|
||||
OnArrangeWithNewField? _onArrangeWithNewField;
|
||||
OnError? _onError;
|
||||
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
RowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
CalendarDataController({required ViewPB view})
|
||||
: databaseId = view.id,
|
||||
_listener = CalendarListener(view.id),
|
||||
_databaseBackendSvc = DatabaseBackendService(viewId: view.id),
|
||||
fieldController = FieldController(viewId: view.id) {
|
||||
_viewCache = DatabaseViewCache(
|
||||
viewId: view.id,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required OnDatabaseChanged onDatabaseChanged,
|
||||
OnFieldsChanged? onFieldsChanged,
|
||||
OnRowsChanged? onRowsChanged,
|
||||
required OnSettingsChanged? onSettingsChanged,
|
||||
required OnArrangeWithNewField? onArrangeWithNewField,
|
||||
required OnError? onError,
|
||||
}) {
|
||||
_onDatabaseChanged = onDatabaseChanged;
|
||||
_onFieldsChanged = onFieldsChanged;
|
||||
_onRowsChanged = onRowsChanged;
|
||||
_onSettingsChanged = onSettingsChanged;
|
||||
_onArrangeWithNewField = onArrangeWithNewField;
|
||||
_onError = onError;
|
||||
|
||||
fieldController.addListener(onReceiveFields: (fields) {
|
||||
_onFieldsChanged?.call(UnmodifiableListView(fields));
|
||||
});
|
||||
|
||||
_listener.start(
|
||||
onCalendarSettingsChanged: (result) {
|
||||
result.fold(
|
||||
(settings) => _onSettingsChanged?.call(settings),
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
onArrangeWithNewField: (result) {
|
||||
result.fold(
|
||||
(settings) => _onArrangeWithNewField?.call(settings),
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> openDatabase() async {
|
||||
final result = await _databaseBackendSvc.openGrid();
|
||||
return result.fold(
|
||||
(database) async {
|
||||
_onDatabaseChanged?.call(database);
|
||||
return fieldController
|
||||
.loadFields(fieldIds: database.fields)
|
||||
.then((result) {
|
||||
return result.fold(
|
||||
(l) => Future(() async {
|
||||
_viewCache.rowCache.initializeRows(database.rows);
|
||||
return left(l);
|
||||
}),
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _viewCache.dispose();
|
||||
await _databaseBackendSvc.closeView();
|
||||
await fieldController.dispose();
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
typedef CalendarSettingsValue = Either<CalendarSettingsPB, FlowyError>;
|
||||
typedef ArrangeWithNewField = Either<FieldPB, FlowyError>;
|
||||
|
||||
class CalendarListener {
|
||||
final String viewId;
|
||||
PublishNotifier<CalendarSettingsValue>? _calendarSettingsNotifier =
|
||||
PublishNotifier();
|
||||
PublishNotifier<ArrangeWithNewField>? _arrangeWithNewFieldNotifier =
|
||||
PublishNotifier();
|
||||
DatabaseNotificationListener? _listener;
|
||||
CalendarListener(this.viewId);
|
||||
|
||||
void start({
|
||||
required void Function(CalendarSettingsValue) onCalendarSettingsChanged,
|
||||
required void Function(ArrangeWithNewField) onArrangeWithNewField,
|
||||
}) {
|
||||
_calendarSettingsNotifier?.addPublishListener(onCalendarSettingsChanged);
|
||||
_arrangeWithNewFieldNotifier?.addPublishListener(onArrangeWithNewField);
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateCalendarSettings:
|
||||
result.fold(
|
||||
(payload) => _calendarSettingsNotifier?.value =
|
||||
left(CalendarSettingsPB.fromBuffer(payload)),
|
||||
(error) => _calendarSettingsNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.DidArrangeCalendarWithNewField:
|
||||
result.fold(
|
||||
(payload) => _arrangeWithNewFieldNotifier?.value =
|
||||
left(FieldPB.fromBuffer(payload)),
|
||||
(error) => _arrangeWithNewFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_calendarSettingsNotifier?.dispose();
|
||||
_calendarSettingsNotifier = null;
|
||||
|
||||
_arrangeWithNewFieldNotifier?.dispose();
|
||||
_arrangeWithNewFieldNotifier = null;
|
||||
}
|
||||
}
|
@ -77,8 +77,7 @@ class CalendarPluginDisplay extends PluginDisplay {
|
||||
});
|
||||
});
|
||||
|
||||
return CalendarPage(key: ValueKey(view.id));
|
||||
// return CalendarPage(key: ValueKey(view.id), view: view);
|
||||
return CalendarPage(key: ValueKey(view.id), view: view);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1,56 +1,83 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:calendar_view/calendar_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../grid/presentation/layout/sizes.dart';
|
||||
import '../../widgets/row/cell_builder.dart';
|
||||
import '../../widgets/row/row_detail.dart';
|
||||
import 'layout/sizes.dart';
|
||||
import 'toolbar/calendar_toolbar.dart';
|
||||
|
||||
class CalendarPage extends StatelessWidget {
|
||||
const CalendarPage({super.key});
|
||||
class CalendarPage extends StatefulWidget {
|
||||
final ViewPB view;
|
||||
const CalendarPage({required this.view, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const CalendarContent();
|
||||
}
|
||||
State<CalendarPage> createState() => _CalendarPageState();
|
||||
}
|
||||
|
||||
class CalendarContent extends StatefulWidget {
|
||||
const CalendarContent({super.key});
|
||||
|
||||
@override
|
||||
State<CalendarContent> createState() => _CalendarContentState();
|
||||
}
|
||||
|
||||
class _CalendarContentState extends State<CalendarContent> {
|
||||
late EventController _eventController;
|
||||
class _CalendarPageState extends State<CalendarPage> {
|
||||
final _eventController = EventController<CalendarCardData>();
|
||||
GlobalKey<MonthViewState>? _calendarState;
|
||||
late CalendarBloc _calendarBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_eventController = EventController();
|
||||
_calendarState = GlobalKey<MonthViewState>();
|
||||
_calendarBloc = CalendarBloc(view: widget.view)
|
||||
..add(const CalendarEvent.initial());
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_calendarBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CalendarControllerProvider(
|
||||
controller: _eventController,
|
||||
child: Column(
|
||||
child: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<CalendarBloc>.value(
|
||||
value: _calendarBloc,
|
||||
)
|
||||
],
|
||||
child: BlocListener<CalendarBloc, CalendarState>(
|
||||
listenWhen: (previous, current) => previous.events != current.events,
|
||||
listener: (context, state) {
|
||||
if (state.events.isNotEmpty) {
|
||||
_eventController.removeWhere((element) => true);
|
||||
_eventController.addAll(state.events);
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<CalendarBloc, CalendarState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
// const _ToolbarBlocAdaptor(),
|
||||
_toolbar(),
|
||||
_buildCalendar(_eventController),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -125,9 +152,190 @@ class _CalendarContentState extends State<CalendarContent> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _calendarDayBuilder(date, event, isToday, isInMonth) {
|
||||
Widget _calendarDayBuilder(
|
||||
DateTime date,
|
||||
List<CalendarEventData<CalendarCardData>> calenderEvents,
|
||||
isToday,
|
||||
isInMonth,
|
||||
) {
|
||||
final builder = CardCellBuilder(_calendarBloc.cellCache);
|
||||
final cells = calenderEvents.map((value) => value.event!).map((event) {
|
||||
final child = builder.buildCell(cellId: event.cellId);
|
||||
|
||||
return FlowyHover(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final dataController = RowController(
|
||||
rowId: event.cellId.rowId,
|
||||
viewId: widget.view.id,
|
||||
rowCache: _calendarBloc.rowCache,
|
||||
);
|
||||
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return RowDetailPage(
|
||||
cellBuilder:
|
||||
GridCellBuilder(cellCache: _calendarBloc.cellCache),
|
||||
dataController: dataController,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return _CalendarCard(
|
||||
isToday: isToday,
|
||||
isInMonth: isInMonth,
|
||||
date: date,
|
||||
children: cells,
|
||||
onCreateEvent: (date) {
|
||||
_calendarBloc.add(
|
||||
CalendarEvent.createEvent(
|
||||
date,
|
||||
LocaleKeys.calendar_defaultNewCalendarTitle.tr(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CalendarCard extends StatelessWidget {
|
||||
final bool isToday;
|
||||
final bool isInMonth;
|
||||
final DateTime date;
|
||||
final List<Widget> children;
|
||||
final void Function(DateTime) onCreateEvent;
|
||||
|
||||
const _CalendarCard({
|
||||
required this.isToday,
|
||||
required this.isInMonth,
|
||||
required this.date,
|
||||
required this.children,
|
||||
required this.onCreateEvent,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color backgroundColor = Theme.of(context).colorScheme.surface;
|
||||
if (!isInMonth) {
|
||||
backgroundColor = AFThemeExtension.of(context).lightGreyHover;
|
||||
}
|
||||
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => _CardEnterNotifier(),
|
||||
builder: ((context, child) {
|
||||
return Container(
|
||||
color: backgroundColor,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (p) => notifyEnter(context, true),
|
||||
onExit: (p) => notifyEnter(context, false),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
_Header(
|
||||
date: date,
|
||||
isInMonth: isInMonth,
|
||||
isToday: isToday,
|
||||
onCreate: () => onCreateEvent(date),
|
||||
),
|
||||
...children
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
notifyEnter(BuildContext context, bool isEnter) {
|
||||
Provider.of<_CardEnterNotifier>(
|
||||
context,
|
||||
listen: false,
|
||||
).onEnter = isEnter;
|
||||
}
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
final bool isToday;
|
||||
final bool isInMonth;
|
||||
final DateTime date;
|
||||
final VoidCallback onCreate;
|
||||
const _Header({
|
||||
required this.isToday,
|
||||
required this.isInMonth,
|
||||
required this.date,
|
||||
required this.onCreate,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<_CardEnterNotifier>(
|
||||
builder: (context, notifier, _) {
|
||||
final badge = _DayBadge(
|
||||
isToday: isToday,
|
||||
isInMonth: isInMonth,
|
||||
date: date,
|
||||
);
|
||||
return Row(
|
||||
children: [
|
||||
if (notifier.onEnter) _NewEventButton(onClick: onCreate),
|
||||
const Spacer(),
|
||||
badge,
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NewEventButton extends StatelessWidget {
|
||||
final VoidCallback onClick;
|
||||
const _NewEventButton({
|
||||
required this.onClick,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
onPressed: onClick,
|
||||
iconPadding: EdgeInsets.zero,
|
||||
icon: svgWidget(
|
||||
"home/add",
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
width: 22,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DayBadge extends StatelessWidget {
|
||||
final bool isToday;
|
||||
final bool isInMonth;
|
||||
final DateTime date;
|
||||
const _DayBadge({
|
||||
required this.isToday,
|
||||
required this.isInMonth,
|
||||
required this.date,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color dayTextColor = Theme.of(context).colorScheme.onSurface;
|
||||
Color cellBackgroundColor = Theme.of(context).colorScheme.surface;
|
||||
String dayString = date.day == 1
|
||||
? DateFormat('MMM d', context.locale.toLanguageTag()).format(date)
|
||||
: date.day.toString();
|
||||
@ -137,8 +345,8 @@ class _CalendarContentState extends State<CalendarContent> {
|
||||
}
|
||||
if (!isInMonth) {
|
||||
dayTextColor = Theme.of(context).disabledColor;
|
||||
cellBackgroundColor = AFThemeExtension.of(context).lightGreyHover;
|
||||
}
|
||||
|
||||
Widget day = Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isToday ? Theme.of(context).colorScheme.primary : null,
|
||||
@ -151,12 +359,21 @@ class _CalendarContentState extends State<CalendarContent> {
|
||||
),
|
||||
);
|
||||
|
||||
return Container(
|
||||
color: cellBackgroundColor,
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: day.padding(all: 6.0),
|
||||
),
|
||||
);
|
||||
return day;
|
||||
}
|
||||
}
|
||||
|
||||
class _CardEnterNotifier extends ChangeNotifier {
|
||||
bool _onEnter = false;
|
||||
|
||||
_CardEnterNotifier();
|
||||
|
||||
set onEnter(bool value) {
|
||||
if (_onEnter != value) {
|
||||
_onEnter = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool get onEnter => _onEnter;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import '../../application/field/field_controller.dart';
|
||||
import 'grid_data_controller.dart';
|
||||
import '../../application/database_controller.dart';
|
||||
import 'dart:collection';
|
||||
|
||||
part 'grid_bloc.freezed.dart';
|
||||
@ -66,10 +66,10 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
databaseController.addListener(
|
||||
onGridChanged: (grid) {
|
||||
final onDatabaseChanged = DatabaseCallbacks(
|
||||
onDatabaseChanged: (database) {
|
||||
if (!isClosed) {
|
||||
add(GridEvent.didReceiveGridUpdate(grid));
|
||||
add(GridEvent.didReceiveGridUpdate(database));
|
||||
}
|
||||
},
|
||||
onRowsChanged: (rowInfos, reason) {
|
||||
@ -83,10 +83,11 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
}
|
||||
},
|
||||
);
|
||||
databaseController.addListener(onDatabaseChanged: onDatabaseChanged);
|
||||
}
|
||||
|
||||
Future<void> _openGrid(Emitter<GridState> emit) async {
|
||||
final result = await databaseController.openGrid();
|
||||
final result = await databaseController.open();
|
||||
result.fold(
|
||||
(grid) {
|
||||
emit(
|
||||
|
@ -1,83 +0,0 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../application/database_service.dart';
|
||||
import '../../application/defines.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo> rowInfos,
|
||||
RowsChangedReason,
|
||||
);
|
||||
typedef ListenOnRowChangedCondition = bool Function();
|
||||
|
||||
class DatabaseController {
|
||||
final String viewId;
|
||||
final DatabaseBackendService _databaseBackendSvc;
|
||||
final FieldController fieldController;
|
||||
late DatabaseViewCache _viewCache;
|
||||
|
||||
OnRowsChanged? _onRowChanged;
|
||||
OnDatabaseChanged? _onGridChanged;
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
RowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
DatabaseController({required ViewPB view})
|
||||
: viewId = view.id,
|
||||
_databaseBackendSvc = DatabaseBackendService(viewId: view.id),
|
||||
fieldController = FieldController(viewId: view.id) {
|
||||
_viewCache = DatabaseViewCache(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
OnDatabaseChanged? onGridChanged,
|
||||
OnRowsChanged? onRowsChanged,
|
||||
OnFieldsChanged? onFieldsChanged,
|
||||
OnFiltersChanged? onFiltersChanged,
|
||||
}) {
|
||||
_onGridChanged = onGridChanged;
|
||||
_onRowChanged = onRowsChanged;
|
||||
|
||||
fieldController.addListener(
|
||||
onReceiveFields: (fields) {
|
||||
onFieldsChanged?.call(UnmodifiableListView(fields));
|
||||
},
|
||||
onFilters: onFiltersChanged,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> openGrid() async {
|
||||
return _databaseBackendSvc.openGrid().then((result) {
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
_viewCache.rowCache.initializeRows(grid.rows);
|
||||
final result = await fieldController.loadFields(
|
||||
fieldIds: grid.fields,
|
||||
);
|
||||
return result;
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createRow() async {
|
||||
await _databaseBackendSvc.createRow();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _databaseBackendSvc.closeView();
|
||||
await fieldController.dispose();
|
||||
}
|
||||
}
|
@ -14,11 +14,11 @@ part 'row_bloc.freezed.dart';
|
||||
|
||||
class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
final RowBackendService _rowBackendSvc;
|
||||
final RowDataController _dataController;
|
||||
final RowController _dataController;
|
||||
|
||||
RowBloc({
|
||||
required RowInfo rowInfo,
|
||||
required RowDataController dataController,
|
||||
required RowController dataController,
|
||||
}) : _rowBackendSvc = RowBackendService(viewId: rowInfo.viewId),
|
||||
_dataController = dataController,
|
||||
super(RowState.initial(rowInfo, dataController.loadData())) {
|
||||
|
@ -7,7 +7,7 @@ import '../../../application/row/row_data_controller.dart';
|
||||
part 'row_detail_bloc.freezed.dart';
|
||||
|
||||
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
final RowDataController dataController;
|
||||
final RowController dataController;
|
||||
|
||||
RowDetailBloc({
|
||||
required this.dataController,
|
||||
@ -27,7 +27,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
},
|
||||
deleteField: (_DeleteField value) {
|
||||
final fieldService = FieldBackendService(
|
||||
viewId: dataController.rowInfo.viewId,
|
||||
viewId: dataController.viewId,
|
||||
fieldId: value.fieldId,
|
||||
);
|
||||
fieldService.deleteField();
|
||||
|
@ -1,4 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pbenum.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
@ -16,17 +18,16 @@ import '../../application/row/row_data_controller.dart';
|
||||
import '../../application/setting/setting_bloc.dart';
|
||||
import '../application/filter/filter_menu_bloc.dart';
|
||||
import '../application/grid_bloc.dart';
|
||||
import '../application/grid_data_controller.dart';
|
||||
import '../../application/database_controller.dart';
|
||||
import '../application/sort/sort_menu_bloc.dart';
|
||||
import 'grid_scroll.dart';
|
||||
import 'layout/layout.dart';
|
||||
import 'layout/sizes.dart';
|
||||
import 'widgets/accessory_menu.dart';
|
||||
import 'widgets/cell/cell_builder.dart';
|
||||
import 'widgets/row/grid_row.dart';
|
||||
import 'widgets/row/row.dart';
|
||||
import 'widgets/footer/grid_footer.dart';
|
||||
import 'widgets/header/grid_header.dart';
|
||||
import 'widgets/row/row_detail.dart';
|
||||
import '../../widgets/row/row_detail.dart';
|
||||
import 'widgets/shortcuts.dart';
|
||||
import 'widgets/toolbar/grid_toolbar.dart';
|
||||
|
||||
@ -35,7 +36,10 @@ class GridPage extends StatefulWidget {
|
||||
required this.view,
|
||||
this.onDeleted,
|
||||
Key? key,
|
||||
}) : databaseController = DatabaseController(view: view),
|
||||
}) : databaseController = DatabaseController(
|
||||
view: view,
|
||||
layoutType: LayoutTypePB.Grid,
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
final ViewPB view;
|
||||
@ -275,14 +279,15 @@ class _GridRowsState extends State<_GridRows> {
|
||||
|
||||
final fieldController =
|
||||
context.read<GridBloc>().databaseController.fieldController;
|
||||
final dataController = RowDataController(
|
||||
rowInfo: rowInfo,
|
||||
final dataController = RowController(
|
||||
rowId: rowInfo.rowPB.id,
|
||||
viewId: rowInfo.viewId,
|
||||
rowCache: rowCache,
|
||||
);
|
||||
|
||||
return SizeTransition(
|
||||
sizeFactor: animation,
|
||||
child: GridRowWidget(
|
||||
child: GridRow(
|
||||
rowInfo: rowInfo,
|
||||
dataController: dataController,
|
||||
cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
|
||||
@ -307,8 +312,9 @@ class _GridRowsState extends State<_GridRows> {
|
||||
RowCache rowCache,
|
||||
GridCellBuilder cellBuilder,
|
||||
) {
|
||||
final dataController = RowDataController(
|
||||
rowInfo: rowInfo,
|
||||
final dataController = RowController(
|
||||
viewId: rowInfo.viewId,
|
||||
rowId: rowInfo.rowPB.id,
|
||||
rowCache: rowCache,
|
||||
);
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
export 'cell_builder.dart';
|
||||
export 'text_cell.dart';
|
||||
export 'number_cell.dart';
|
||||
export 'date_cell/date_cell.dart';
|
||||
export 'checkbox_cell.dart';
|
||||
export 'select_option_cell/select_option_cell.dart';
|
||||
export 'url_cell/url_cell.dart';
|
@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/checkbox_filter.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -4,7 +4,6 @@ import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/checklist_filter.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -8,7 +8,7 @@ import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.d
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../cell/select_option_cell/extension.dart';
|
||||
import '../../../../../../widgets/row/cells/select_option_cell/extension.dart';
|
||||
import '../../filter_info.dart';
|
||||
import 'select_option_loader.dart';
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
import 'package:appflowy/plugins/database_view/grid/application/filter/select_option_filter_bloc.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_option_filter.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/text_filter.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -5,7 +5,6 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
|
@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -1,9 +1,6 @@
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.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/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
|
@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:dartz/dartz.dart' show Either;
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -9,7 +9,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -6,8 +6,6 @@ import 'package:easy_localization/easy_localization.dart' hide DateFormat;
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -4,8 +4,6 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/typ
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
@ -2,8 +2,6 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/sel
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -11,7 +9,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../../cell/select_option_cell/extension.dart';
|
||||
import '../../../../../widgets/row/cells/select_option_cell/extension.dart';
|
||||
import '../../common/type_option_separator.dart';
|
||||
import 'select_option_editor.dart';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/edit_select_option_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
|
@ -12,9 +12,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
|
||||
class GridRowActionSheet extends StatelessWidget {
|
||||
class RowActions extends StatelessWidget {
|
||||
final RowInfo rowData;
|
||||
const GridRowActionSheet({required this.rowData, Key? key}) : super(key: key);
|
||||
const RowActions({required this.rowData, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -24,9 +24,7 @@ class GridRowActionSheet extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final cells = _RowAction.values
|
||||
.where((value) => value.enable())
|
||||
.map(
|
||||
(action) => _RowActionCell(action: action),
|
||||
)
|
||||
.map((action) => _ActionCell(action: action))
|
||||
.toList();
|
||||
|
||||
//
|
||||
@ -49,9 +47,9 @@ class GridRowActionSheet extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _RowActionCell extends StatelessWidget {
|
||||
class _ActionCell extends StatelessWidget {
|
||||
final _RowAction action;
|
||||
const _RowActionCell({required this.action, Key? key}) : super(key: key);
|
||||
const _ActionCell({required this.action, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
@ -2,30 +2,29 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_service.dar
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../../widgets/row/accessory/cell_accessory.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
import '../cell/cell_accessory.dart';
|
||||
import '../cell/cell_container.dart';
|
||||
import '../cell/prelude.dart';
|
||||
import 'row_action_sheet.dart';
|
||||
import '../../../../widgets/row/cells/cell_container.dart';
|
||||
import 'action.dart';
|
||||
import "package:appflowy/generated/locale_keys.g.dart";
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class GridRowWidget extends StatefulWidget {
|
||||
class GridRow extends StatefulWidget {
|
||||
final RowInfo rowInfo;
|
||||
final RowDataController dataController;
|
||||
final RowController dataController;
|
||||
final GridCellBuilder cellBuilder;
|
||||
final void Function(BuildContext, GridCellBuilder) openDetailPage;
|
||||
|
||||
const GridRowWidget({
|
||||
const GridRow({
|
||||
required this.rowInfo,
|
||||
required this.dataController,
|
||||
required this.cellBuilder,
|
||||
@ -34,10 +33,10 @@ class GridRowWidget extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<GridRowWidget> createState() => _GridRowWidgetState();
|
||||
State<GridRow> createState() => _GridRowState();
|
||||
}
|
||||
|
||||
class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
class _GridRowState extends State<GridRow> {
|
||||
late RowBloc _rowBloc;
|
||||
|
||||
@override
|
||||
@ -111,8 +110,7 @@ class _RowLeadingState extends State<_RowLeading> {
|
||||
direction: PopoverDirection.rightWithCenterAligned,
|
||||
margin: const EdgeInsets.all(6),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return GridRowActionSheet(
|
||||
rowData: context.read<RowBloc>().state.rowInfo);
|
||||
return RowActions(rowData: context.read<RowBloc>().state.rowInfo);
|
||||
},
|
||||
child: Consumer<RegionStateNotifier>(
|
||||
builder: (context, state, _) {
|
@ -9,9 +9,6 @@ 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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'dart:math' as math;
|
||||
|
@ -4,7 +4,6 @@ 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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
|
@ -6,9 +6,6 @@ import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.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';
|
||||
|
@ -5,7 +5,6 @@ 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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
@ -4,7 +4,6 @@ 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:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
|
@ -3,16 +3,16 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import '../../../application/cell/cell_controller_builder.dart';
|
||||
|
||||
part 'board_checkbox_cell_bloc.freezed.dart';
|
||||
part 'checkbox_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardCheckboxCellBloc
|
||||
extends Bloc<BoardCheckboxCellEvent, BoardCheckboxCellState> {
|
||||
class CheckboxCardCellBloc
|
||||
extends Bloc<CheckboxCardCellEvent, CheckboxCardCellState> {
|
||||
final CheckboxCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
BoardCheckboxCellBloc({
|
||||
CheckboxCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : super(BoardCheckboxCellState.initial(cellController)) {
|
||||
on<BoardCheckboxCellEvent>(
|
||||
}) : super(CheckboxCardCellState.initial(cellController)) {
|
||||
on<CheckboxCardCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
@ -43,7 +43,7 @@ class BoardCheckboxCellBloc
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((cellContent) {
|
||||
if (!isClosed) {
|
||||
add(BoardCheckboxCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
add(CheckboxCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -51,21 +51,21 @@ class BoardCheckboxCellBloc
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardCheckboxCellEvent with _$BoardCheckboxCellEvent {
|
||||
const factory BoardCheckboxCellEvent.initial() = _InitialCell;
|
||||
const factory BoardCheckboxCellEvent.select() = _Selected;
|
||||
const factory BoardCheckboxCellEvent.didReceiveCellUpdate(
|
||||
String cellContent) = _DidReceiveCellUpdate;
|
||||
class CheckboxCardCellEvent with _$CheckboxCardCellEvent {
|
||||
const factory CheckboxCardCellEvent.initial() = _InitialCell;
|
||||
const factory CheckboxCardCellEvent.select() = _Selected;
|
||||
const factory CheckboxCardCellEvent.didReceiveCellUpdate(String cellContent) =
|
||||
_DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardCheckboxCellState with _$BoardCheckboxCellState {
|
||||
const factory BoardCheckboxCellState({
|
||||
class CheckboxCardCellState with _$CheckboxCardCellState {
|
||||
const factory CheckboxCardCellState({
|
||||
required bool isSelected,
|
||||
}) = _CheckboxCellState;
|
||||
|
||||
factory BoardCheckboxCellState.initial(TextCellController context) {
|
||||
return BoardCheckboxCellState(
|
||||
factory CheckboxCardCellState.initial(TextCellController context) {
|
||||
return CheckboxCardCellState(
|
||||
isSelected: _isSelected(context.getCellData()));
|
||||
}
|
||||
}
|
@ -5,15 +5,15 @@ import 'dart:async';
|
||||
|
||||
import '../../../application/cell/cell_controller_builder.dart';
|
||||
import '../../../application/field/field_controller.dart';
|
||||
part 'board_date_cell_bloc.freezed.dart';
|
||||
part 'date_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
|
||||
class DateCardCellBloc extends Bloc<DateCardCellEvent, DateCardCellState> {
|
||||
final DateCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
BoardDateCellBloc({required this.cellController})
|
||||
: super(BoardDateCellState.initial(cellController)) {
|
||||
on<BoardDateCellEvent>(
|
||||
DateCardCellBloc({required this.cellController})
|
||||
: super(DateCardCellState.initial(cellController)) {
|
||||
on<DateCardCellEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () => _startListening(),
|
||||
@ -40,7 +40,7 @@ class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((data) {
|
||||
if (!isClosed) {
|
||||
add(BoardDateCellEvent.didReceiveCellUpdate(data));
|
||||
add(DateCardCellEvent.didReceiveCellUpdate(data));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -48,24 +48,24 @@ class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardDateCellEvent with _$BoardDateCellEvent {
|
||||
const factory BoardDateCellEvent.initial() = _InitialCell;
|
||||
const factory BoardDateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
|
||||
class DateCardCellEvent with _$DateCardCellEvent {
|
||||
const factory DateCardCellEvent.initial() = _InitialCell;
|
||||
const factory DateCardCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
|
||||
_DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardDateCellState with _$BoardDateCellState {
|
||||
const factory BoardDateCellState({
|
||||
class DateCardCellState with _$DateCardCellState {
|
||||
const factory DateCardCellState({
|
||||
required DateCellDataPB? data,
|
||||
required String dateStr,
|
||||
required FieldInfo fieldInfo,
|
||||
}) = _BoardDateCellState;
|
||||
}) = _DateCardCellState;
|
||||
|
||||
factory BoardDateCellState.initial(DateCellController context) {
|
||||
factory DateCardCellState.initial(DateCellController context) {
|
||||
final cellData = context.getCellData();
|
||||
|
||||
return BoardDateCellState(
|
||||
return DateCardCellState(
|
||||
fieldInfo: context.fieldInfo,
|
||||
data: cellData,
|
||||
dateStr: _dateStrFromCellData(cellData),
|
@ -4,16 +4,16 @@ import 'dart:async';
|
||||
|
||||
import '../../../application/cell/cell_controller_builder.dart';
|
||||
|
||||
part 'board_number_cell_bloc.freezed.dart';
|
||||
part 'number_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardNumberCellBloc
|
||||
extends Bloc<BoardNumberCellEvent, BoardNumberCellState> {
|
||||
class NumberCardCellBloc
|
||||
extends Bloc<NumberCardCellEvent, NumberCardCellState> {
|
||||
final NumberCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
BoardNumberCellBloc({
|
||||
NumberCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : super(BoardNumberCellState.initial(cellController)) {
|
||||
on<BoardNumberCellEvent>(
|
||||
}) : super(NumberCardCellState.initial(cellController)) {
|
||||
on<NumberCardCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
@ -41,7 +41,7 @@ class BoardNumberCellBloc
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((cellContent) {
|
||||
if (!isClosed) {
|
||||
add(BoardNumberCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
add(NumberCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -49,20 +49,20 @@ class BoardNumberCellBloc
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardNumberCellEvent with _$BoardNumberCellEvent {
|
||||
const factory BoardNumberCellEvent.initial() = _InitialCell;
|
||||
const factory BoardNumberCellEvent.didReceiveCellUpdate(String cellContent) =
|
||||
class NumberCardCellEvent with _$NumberCardCellEvent {
|
||||
const factory NumberCardCellEvent.initial() = _InitialCell;
|
||||
const factory NumberCardCellEvent.didReceiveCellUpdate(String cellContent) =
|
||||
_DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardNumberCellState with _$BoardNumberCellState {
|
||||
const factory BoardNumberCellState({
|
||||
class NumberCardCellState with _$NumberCardCellState {
|
||||
const factory NumberCardCellState({
|
||||
required String content,
|
||||
}) = _BoardNumberCellState;
|
||||
}) = _NumberCardCellState;
|
||||
|
||||
factory BoardNumberCellState.initial(TextCellController context) =>
|
||||
BoardNumberCellState(
|
||||
factory NumberCardCellState.initial(TextCellController context) =>
|
||||
NumberCardCellState(
|
||||
content: context.getCellData() ?? "",
|
||||
);
|
||||
}
|
@ -4,17 +4,17 @@ import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.d
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'board_select_option_cell_bloc.freezed.dart';
|
||||
part 'select_option_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardSelectOptionCellBloc
|
||||
extends Bloc<BoardSelectOptionCellEvent, BoardSelectOptionCellState> {
|
||||
class SelectOptionCardCellBloc
|
||||
extends Bloc<SelectOptionCardCellEvent, SelectOptionCardCellState> {
|
||||
final SelectOptionCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
BoardSelectOptionCellBloc({
|
||||
SelectOptionCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : super(BoardSelectOptionCellState.initial(cellController)) {
|
||||
on<BoardSelectOptionCellEvent>(
|
||||
}) : super(SelectOptionCardCellState.initial(cellController)) {
|
||||
on<SelectOptionCardCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
@ -42,7 +42,7 @@ class BoardSelectOptionCellBloc
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
add(BoardSelectOptionCellEvent.didReceiveOptions(
|
||||
add(SelectOptionCardCellEvent.didReceiveOptions(
|
||||
selectOptionContext?.selectOptions ?? [],
|
||||
));
|
||||
}
|
||||
@ -52,23 +52,23 @@ class BoardSelectOptionCellBloc
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardSelectOptionCellEvent with _$BoardSelectOptionCellEvent {
|
||||
const factory BoardSelectOptionCellEvent.initial() = _InitialCell;
|
||||
const factory BoardSelectOptionCellEvent.didReceiveOptions(
|
||||
class SelectOptionCardCellEvent with _$SelectOptionCardCellEvent {
|
||||
const factory SelectOptionCardCellEvent.initial() = _InitialCell;
|
||||
const factory SelectOptionCardCellEvent.didReceiveOptions(
|
||||
List<SelectOptionPB> selectedOptions,
|
||||
) = _DidReceiveOptions;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardSelectOptionCellState with _$BoardSelectOptionCellState {
|
||||
const factory BoardSelectOptionCellState({
|
||||
class SelectOptionCardCellState with _$SelectOptionCardCellState {
|
||||
const factory SelectOptionCardCellState({
|
||||
required List<SelectOptionPB> selectedOptions,
|
||||
}) = _BoardSelectOptionCellState;
|
||||
}) = _SelectOptionCardCellState;
|
||||
|
||||
factory BoardSelectOptionCellState.initial(
|
||||
factory SelectOptionCardCellState.initial(
|
||||
SelectOptionCellController context) {
|
||||
final data = context.getCellData();
|
||||
return BoardSelectOptionCellState(
|
||||
return SelectOptionCardCellState(
|
||||
selectedOptions: data?.selectOptions ?? [],
|
||||
);
|
||||
}
|
@ -3,15 +3,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
part 'board_text_cell_bloc.freezed.dart';
|
||||
part 'text_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
|
||||
class TextCardCellBloc extends Bloc<TextCardCellEvent, TextCardCellState> {
|
||||
final TextCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
BoardTextCellBloc({
|
||||
TextCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : super(BoardTextCellState.initial(cellController)) {
|
||||
on<BoardTextCellEvent>(
|
||||
}) : super(TextCardCellState.initial(cellController)) {
|
||||
on<TextCardCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
@ -48,7 +48,7 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((cellContent) {
|
||||
if (!isClosed) {
|
||||
add(BoardTextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
add(TextCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -56,23 +56,23 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardTextCellEvent with _$BoardTextCellEvent {
|
||||
const factory BoardTextCellEvent.initial() = _InitialCell;
|
||||
const factory BoardTextCellEvent.updateText(String text) = _UpdateContent;
|
||||
const factory BoardTextCellEvent.enableEdit(bool enabled) = _EnableEdit;
|
||||
const factory BoardTextCellEvent.didReceiveCellUpdate(String cellContent) =
|
||||
class TextCardCellEvent with _$TextCardCellEvent {
|
||||
const factory TextCardCellEvent.initial() = _InitialCell;
|
||||
const factory TextCardCellEvent.updateText(String text) = _UpdateContent;
|
||||
const factory TextCardCellEvent.enableEdit(bool enabled) = _EnableEdit;
|
||||
const factory TextCardCellEvent.didReceiveCellUpdate(String cellContent) =
|
||||
_DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardTextCellState with _$BoardTextCellState {
|
||||
const factory BoardTextCellState({
|
||||
class TextCardCellState with _$TextCardCellState {
|
||||
const factory TextCardCellState({
|
||||
required String content,
|
||||
required bool enableEdit,
|
||||
}) = _BoardTextCellState;
|
||||
}) = _TextCardCellState;
|
||||
|
||||
factory BoardTextCellState.initial(TextCellController context) =>
|
||||
BoardTextCellState(
|
||||
factory TextCardCellState.initial(TextCellController context) =>
|
||||
TextCardCellState(
|
||||
content: context.getCellData() ?? "",
|
||||
enableEdit: false,
|
||||
);
|
@ -4,15 +4,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
part 'board_url_cell_bloc.freezed.dart';
|
||||
part 'url_card_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
|
||||
class URLCardCellBloc extends Bloc<URLCardCellEvent, URLCardCellState> {
|
||||
final URLCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
BoardURLCellBloc({
|
||||
URLCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : super(BoardURLCellState.initial(cellController)) {
|
||||
on<BoardURLCellEvent>(
|
||||
}) : super(URLCardCellState.initial(cellController)) {
|
||||
on<URLCardCellEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () {
|
||||
@ -46,7 +46,7 @@ class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((cellData) {
|
||||
if (!isClosed) {
|
||||
add(BoardURLCellEvent.didReceiveCellUpdate(cellData));
|
||||
add(URLCardCellEvent.didReceiveCellUpdate(cellData));
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -54,23 +54,23 @@ class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardURLCellEvent with _$BoardURLCellEvent {
|
||||
const factory BoardURLCellEvent.initial() = _InitialCell;
|
||||
const factory BoardURLCellEvent.updateURL(String url) = _UpdateURL;
|
||||
const factory BoardURLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
|
||||
class URLCardCellEvent with _$URLCardCellEvent {
|
||||
const factory URLCardCellEvent.initial() = _InitialCell;
|
||||
const factory URLCardCellEvent.updateURL(String url) = _UpdateURL;
|
||||
const factory URLCardCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
|
||||
_DidReceiveCellUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class BoardURLCellState with _$BoardURLCellState {
|
||||
const factory BoardURLCellState({
|
||||
class URLCardCellState with _$URLCardCellState {
|
||||
const factory URLCardCellState({
|
||||
required String content,
|
||||
required String url,
|
||||
}) = _BoardURLCellState;
|
||||
}) = _URLCardCellState;
|
||||
|
||||
factory BoardURLCellState.initial(URLCellController context) {
|
||||
factory URLCardCellState.initial(URLCellController context) {
|
||||
final cellData = context.getCellData();
|
||||
return BoardURLCellState(
|
||||
return URLCardCellState(
|
||||
content: cellData?.content ?? "",
|
||||
url: cellData?.url ?? "",
|
||||
);
|
@ -2,7 +2,7 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../application/card/board_number_cell_bloc.dart';
|
||||
import 'bloc/number_card_cell_bloc.dart';
|
||||
import 'define.dart';
|
||||
|
||||
class BoardNumberCell extends StatefulWidget {
|
||||
@ -16,19 +16,19 @@ class BoardNumberCell extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardNumberCell> createState() => _BoardNumberCellState();
|
||||
State<BoardNumberCell> createState() => _NumberCardCellState();
|
||||
}
|
||||
|
||||
class _BoardNumberCellState extends State<BoardNumberCell> {
|
||||
late BoardNumberCellBloc _cellBloc;
|
||||
class _NumberCardCellState extends State<BoardNumberCell> {
|
||||
late NumberCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as NumberCellController;
|
||||
|
||||
_cellBloc = BoardNumberCellBloc(cellController: cellController)
|
||||
..add(const BoardNumberCellEvent.initial());
|
||||
_cellBloc = NumberCardCellBloc(cellController: cellController)
|
||||
..add(const NumberCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ class _BoardNumberCellState extends State<BoardNumberCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<BoardNumberCellBloc, BoardNumberCellState>(
|
||||
child: BlocBuilder<NumberCardCellBloc, NumberCardCellState>(
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
builder: (context, state) {
|
||||
if (state.content.isEmpty) {
|
||||
@ -46,7 +46,7 @@ class _BoardNumberCellState extends State<BoardNumberCell> {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: BoardSizes.cardCellVPadding,
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
state.content,
|
@ -1,47 +1,52 @@
|
||||
import 'package:appflowy/plugins/database_view/board/application/card/card_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/application/card/card_data_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row_action_sheet.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'board_cell.dart';
|
||||
import 'card_bloc.dart';
|
||||
import 'cells/card_cell.dart';
|
||||
import 'card_cell_builder.dart';
|
||||
import 'container/accessory.dart';
|
||||
import 'container/card_container.dart';
|
||||
|
||||
class BoardCard extends StatefulWidget {
|
||||
class Card<CustomCardData> extends StatefulWidget {
|
||||
final RowPB row;
|
||||
final String viewId;
|
||||
final String groupId;
|
||||
final String fieldId;
|
||||
final CustomCardData? cardData;
|
||||
final bool isEditing;
|
||||
final CardDataController dataController;
|
||||
final BoardCellBuilder cellBuilder;
|
||||
final RowCache rowCache;
|
||||
final CardCellBuilder<CustomCardData> cellBuilder;
|
||||
final void Function(BuildContext) openCard;
|
||||
final VoidCallback onStartEditing;
|
||||
final VoidCallback onEndEditing;
|
||||
final CardConfiguration<CustomCardData>? configuration;
|
||||
|
||||
const BoardCard({
|
||||
const Card({
|
||||
required this.row,
|
||||
required this.viewId,
|
||||
required this.groupId,
|
||||
required this.fieldId,
|
||||
required this.isEditing,
|
||||
required this.dataController,
|
||||
required this.rowCache,
|
||||
required this.cellBuilder,
|
||||
required this.openCard,
|
||||
required this.onStartEditing,
|
||||
required this.onEndEditing,
|
||||
this.cardData,
|
||||
this.configuration,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardCard> createState() => _BoardCardState();
|
||||
State<Card<CustomCardData>> createState() => _CardState<CustomCardData>();
|
||||
}
|
||||
|
||||
class _BoardCardState extends State<BoardCard> {
|
||||
late BoardCardBloc _cardBloc;
|
||||
class _CardState<T> extends State<Card<T>> {
|
||||
late CardBloc _cardBloc;
|
||||
late EditableRowNotifier rowNotifier;
|
||||
late PopoverController popoverController;
|
||||
AccessoryType? accessoryType;
|
||||
@ -49,11 +54,12 @@ class _BoardCardState extends State<BoardCard> {
|
||||
@override
|
||||
void initState() {
|
||||
rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
|
||||
_cardBloc = BoardCardBloc(
|
||||
_cardBloc = CardBloc(
|
||||
viewId: widget.viewId,
|
||||
groupFieldId: widget.fieldId,
|
||||
dataController: widget.dataController,
|
||||
isEditing: widget.isEditing,
|
||||
row: widget.row,
|
||||
rowCache: widget.rowCache,
|
||||
)..add(const BoardCardEvent.initial());
|
||||
|
||||
rowNotifier.isEditing.addListener(() {
|
||||
@ -75,7 +81,7 @@ class _BoardCardState extends State<BoardCard> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cardBloc,
|
||||
child: BlocBuilder<BoardCardBloc, BoardCardState>(
|
||||
child: BlocBuilder<CardBloc, BoardCardState>(
|
||||
buildWhen: (previous, current) {
|
||||
// Rebuild when:
|
||||
// 1.If the length of the cells is not the same
|
||||
@ -110,11 +116,12 @@ class _BoardCardState extends State<BoardCard> {
|
||||
},
|
||||
openAccessory: _handleOpenAccessory,
|
||||
openCard: (context) => widget.openCard(context),
|
||||
child: _CellColumn(
|
||||
groupId: widget.groupId,
|
||||
child: _CardContent<T>(
|
||||
rowNotifier: rowNotifier,
|
||||
cellBuilder: widget.cellBuilder,
|
||||
cells: state.cells,
|
||||
cardConfiguration: widget.configuration,
|
||||
cardData: widget.cardData,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -142,8 +149,8 @@ class _BoardCardState extends State<BoardCard> {
|
||||
case AccessoryType.edit:
|
||||
throw UnimplementedError();
|
||||
case AccessoryType.more:
|
||||
return GridRowActionSheet(
|
||||
rowData: context.read<BoardCardBloc>().rowInfo(),
|
||||
return RowActions(
|
||||
rowData: context.read<CardBloc>().rowInfo(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -156,16 +163,18 @@ class _BoardCardState extends State<BoardCard> {
|
||||
}
|
||||
}
|
||||
|
||||
class _CellColumn extends StatelessWidget {
|
||||
final String groupId;
|
||||
final BoardCellBuilder cellBuilder;
|
||||
class _CardContent<CustomCardData> extends StatelessWidget {
|
||||
final CardCellBuilder<CustomCardData> cellBuilder;
|
||||
final EditableRowNotifier rowNotifier;
|
||||
final List<BoardCellEquatable> cells;
|
||||
const _CellColumn({
|
||||
required this.groupId,
|
||||
final CardConfiguration<CustomCardData>? cardConfiguration;
|
||||
final CustomCardData? cardData;
|
||||
const _CardContent({
|
||||
required this.rowNotifier,
|
||||
required this.cellBuilder,
|
||||
required this.cells,
|
||||
required this.cardData,
|
||||
this.cardConfiguration,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -188,7 +197,7 @@ class _CellColumn extends StatelessWidget {
|
||||
cells.asMap().forEach(
|
||||
(int index, BoardCellEquatable cell) {
|
||||
final isEditing = index == 0 ? rowNotifier.isEditing.value : false;
|
||||
final cellNotifier = EditableCellNotifier(isEditing: isEditing);
|
||||
final cellNotifier = EditableCardNotifier(isEditing: isEditing);
|
||||
|
||||
if (index == 0) {
|
||||
// Only use the first cell to receive user's input when click the edit
|
||||
@ -200,9 +209,10 @@ class _CellColumn extends StatelessWidget {
|
||||
key: cell.identifier.key(),
|
||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||
child: cellBuilder.buildCell(
|
||||
groupId,
|
||||
cell.identifier,
|
||||
cellNotifier,
|
||||
cellId: cell.identifier,
|
||||
cellNotifier: cellNotifier,
|
||||
cardConfiguration: cardConfiguration,
|
||||
cardData: cardData,
|
||||
),
|
||||
);
|
||||
|
@ -1,34 +1,36 @@
|
||||
import 'dart:collection';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import '../../../application/cell/cell_service.dart';
|
||||
import '../../../application/row/row_cache.dart';
|
||||
import '../../../application/row/row_service.dart';
|
||||
import 'card_data_controller.dart';
|
||||
|
||||
import '../../application/cell/cell_service.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
import '../../application/row/row_service.dart';
|
||||
|
||||
part 'card_bloc.freezed.dart';
|
||||
|
||||
class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
class CardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
final RowPB row;
|
||||
final String groupFieldId;
|
||||
final RowBackendService _rowBackendSvc;
|
||||
final CardDataController _dataController;
|
||||
final RowCache _rowCache;
|
||||
VoidCallback? _rowCallback;
|
||||
|
||||
BoardCardBloc({
|
||||
CardBloc({
|
||||
required this.row,
|
||||
required this.groupFieldId,
|
||||
required String viewId,
|
||||
required CardDataController dataController,
|
||||
required RowCache rowCache,
|
||||
required bool isEditing,
|
||||
}) : _rowBackendSvc = RowBackendService(
|
||||
viewId: viewId,
|
||||
),
|
||||
_dataController = dataController,
|
||||
}) : _rowBackendSvc = RowBackendService(viewId: viewId),
|
||||
_rowCache = rowCache,
|
||||
super(
|
||||
BoardCardState.initial(
|
||||
dataController.rowPB,
|
||||
_makeCells(groupFieldId, dataController.loadData()),
|
||||
row,
|
||||
_makeCells(groupFieldId, rowCache.loadGridCells(row.id)),
|
||||
isEditing,
|
||||
),
|
||||
) {
|
||||
@ -54,7 +56,10 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
_dataController.dispose();
|
||||
if (_rowCallback != null) {
|
||||
_rowCache.removeRowListener(_rowCallback!);
|
||||
_rowCallback = null;
|
||||
}
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@ -69,8 +74,9 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
}
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_dataController.addListener(
|
||||
onRowChanged: (cellMap, reason) {
|
||||
_rowCallback = _rowCache.addListener(
|
||||
rowId: row.id,
|
||||
onCellUpdated: (cellMap, reason) {
|
||||
if (!isClosed) {
|
||||
final cells = _makeCells(groupFieldId, cellMap);
|
||||
add(BoardCardEvent.didReceiveCells(cells, reason));
|
@ -2,83 +2,78 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../application/cell/cell_service.dart';
|
||||
import 'board_cell.dart';
|
||||
import 'board_checkbox_cell.dart';
|
||||
import 'board_checklist_cell.dart';
|
||||
import 'board_date_cell.dart';
|
||||
import 'board_number_cell.dart';
|
||||
import 'board_select_option_cell.dart';
|
||||
import 'board_text_cell.dart';
|
||||
import 'board_url_cell.dart';
|
||||
import '../../application/cell/cell_service.dart';
|
||||
import 'cells/card_cell.dart';
|
||||
import 'cells/checkbox_card_cell.dart';
|
||||
import 'cells/checklist_card_cell.dart';
|
||||
import 'cells/date_card_cell.dart';
|
||||
import 'cells/number_card_cell.dart';
|
||||
import 'cells/select_option_card_cell.dart';
|
||||
import 'cells/text_card_cell.dart';
|
||||
import 'cells/url_card_cell.dart';
|
||||
|
||||
abstract class BoardCellBuilderDelegate {
|
||||
CellCache get cellCache;
|
||||
}
|
||||
// T represents as the Generic card data
|
||||
class CardCellBuilder<CustomCardData> {
|
||||
final CellCache cellCache;
|
||||
|
||||
class BoardCellBuilder {
|
||||
final BoardCellBuilderDelegate delegate;
|
||||
CardCellBuilder(this.cellCache);
|
||||
|
||||
BoardCellBuilder(this.delegate);
|
||||
|
||||
Widget buildCell(
|
||||
String groupId,
|
||||
CellIdentifier cellId,
|
||||
EditableCellNotifier cellNotifier,
|
||||
) {
|
||||
Widget buildCell({
|
||||
CustomCardData? cardData,
|
||||
required CellIdentifier cellId,
|
||||
EditableCardNotifier? cellNotifier,
|
||||
CardConfiguration<CustomCardData>? cardConfiguration,
|
||||
}) {
|
||||
final cellControllerBuilder = CellControllerBuilder(
|
||||
cellId: cellId,
|
||||
cellCache: delegate.cellCache,
|
||||
cellCache: cellCache,
|
||||
);
|
||||
|
||||
final key = cellId.key();
|
||||
switch (cellId.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return BoardCheckboxCell(
|
||||
groupId: groupId,
|
||||
return CheckboxCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
return BoardDateCell(
|
||||
groupId: groupId,
|
||||
return DateCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.SingleSelect:
|
||||
return BoardSelectOptionCell(
|
||||
groupId: groupId,
|
||||
return SelectOptionCardCell<CustomCardData>(
|
||||
renderHook: cardConfiguration?.renderHook[FieldType.SingleSelect],
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
cardData: cardData,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
return BoardSelectOptionCell(
|
||||
groupId: groupId,
|
||||
return SelectOptionCardCell<CustomCardData>(
|
||||
renderHook: cardConfiguration?.renderHook[FieldType.MultiSelect],
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
cardData: cardData,
|
||||
editableNotifier: cellNotifier,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.Checklist:
|
||||
return BoardChecklistCell(
|
||||
return ChecklistCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.Number:
|
||||
return BoardNumberCell(
|
||||
groupId: groupId,
|
||||
return NumberCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.RichText:
|
||||
return BoardTextCell(
|
||||
groupId: groupId,
|
||||
return TextCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
editableNotifier: cellNotifier,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.URL:
|
||||
return BoardUrlCell(
|
||||
groupId: groupId,
|
||||
return URLCardCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
@ -1,14 +1,39 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class FocusableBoardCell {
|
||||
set becomeFocus(bool isFocus);
|
||||
typedef CellRenderHook<C, T> = Widget? Function(C cellData, T cardData);
|
||||
typedef RenderHookByFieldType<C> = Map<FieldType, CellRenderHook<dynamic, C>>;
|
||||
|
||||
class CardConfiguration<CustomCardData> {
|
||||
final RenderHookByFieldType<CustomCardData> renderHook = {};
|
||||
CardConfiguration();
|
||||
|
||||
void addSelectOptionHook(
|
||||
CellRenderHook<List<SelectOptionPB>, CustomCardData> hook,
|
||||
) {
|
||||
selectOptionHook(cellData, cardData) {
|
||||
if (cellData is List<SelectOptionPB>) {
|
||||
hook(cellData, cardData);
|
||||
}
|
||||
}
|
||||
|
||||
class EditableCellNotifier {
|
||||
renderHook[FieldType.SingleSelect] = selectOptionHook;
|
||||
renderHook[FieldType.MultiSelect] = selectOptionHook;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CardCell<T> extends StatefulWidget {
|
||||
final T? cardData;
|
||||
|
||||
const CardCell({super.key, this.cardData});
|
||||
}
|
||||
|
||||
class EditableCardNotifier {
|
||||
final ValueNotifier<bool> isCellEditing;
|
||||
|
||||
EditableCellNotifier({bool isEditing = false})
|
||||
EditableCardNotifier({bool isEditing = false})
|
||||
: isCellEditing = ValueNotifier(isEditing);
|
||||
|
||||
void dispose() {
|
||||
@ -17,7 +42,7 @@ class EditableCellNotifier {
|
||||
}
|
||||
|
||||
class EditableRowNotifier {
|
||||
final Map<EditableCellId, EditableCellNotifier> _cells = {};
|
||||
final Map<EditableCellId, EditableCardNotifier> _cells = {};
|
||||
final ValueNotifier<bool> isEditing;
|
||||
|
||||
EditableRowNotifier({required bool isEditing})
|
||||
@ -25,7 +50,7 @@ class EditableRowNotifier {
|
||||
|
||||
void bindCell(
|
||||
CellIdentifier cellIdentifier,
|
||||
EditableCellNotifier notifier,
|
||||
EditableCardNotifier notifier,
|
||||
) {
|
||||
assert(
|
||||
_cells.values.isEmpty,
|
||||
@ -80,7 +105,7 @@ abstract class EditableCell {
|
||||
// the row notifier receive its cells event. For example: begin editing the
|
||||
// cell or end editing the cell.
|
||||
//
|
||||
EditableCellNotifier? get editableNotifier;
|
||||
EditableCardNotifier? get editableNotifier;
|
||||
}
|
||||
|
||||
class EditableCellId {
|
@ -1,33 +1,33 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class BoardCheckboxCell extends StatefulWidget {
|
||||
final String groupId;
|
||||
import '../bloc/checkbox_card_cell_bloc.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class CheckboxCardCell extends CardCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
|
||||
const BoardCheckboxCell({
|
||||
required this.groupId,
|
||||
const CheckboxCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardCheckboxCell> createState() => _BoardCheckboxCellState();
|
||||
State<CheckboxCardCell> createState() => _CheckboxCardCellState();
|
||||
}
|
||||
|
||||
class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
|
||||
late BoardCheckboxCellBloc _cellBloc;
|
||||
class _CheckboxCardCellState extends State<CheckboxCardCell> {
|
||||
late CheckboxCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as CheckboxCellController;
|
||||
_cellBloc = BoardCheckboxCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const BoardCheckboxCellEvent.initial());
|
||||
_cellBloc = CheckboxCardCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const CheckboxCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<BoardCheckboxCellBloc, BoardCheckboxCellState>(
|
||||
child: BlocBuilder<CheckboxCardCellBloc, CheckboxCardCellState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.isSelected != current.isSelected,
|
||||
builder: (context, state) {
|
||||
@ -49,8 +49,8 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
|
||||
icon: icon,
|
||||
width: 20,
|
||||
onPressed: () => context
|
||||
.read<BoardCheckboxCellBloc>()
|
||||
.add(const BoardCheckboxCellEvent.select()),
|
||||
.read<CheckboxCardCellBloc>()
|
||||
.add(const CheckboxCardCellEvent.select()),
|
||||
),
|
||||
);
|
||||
},
|
@ -1,27 +1,28 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../grid/application/cell/checklist_cell_bloc.dart';
|
||||
import '../../../grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart';
|
||||
import '../../row/cells/checklist_cell/checklist_cell_bloc.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class BoardChecklistCell extends StatefulWidget {
|
||||
class ChecklistCardCell extends CardCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
const BoardChecklistCell({required this.cellControllerBuilder, Key? key})
|
||||
const ChecklistCardCell({required this.cellControllerBuilder, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardChecklistCell> createState() => _BoardChecklistCellState();
|
||||
State<ChecklistCardCell> createState() => _ChecklistCardCellState();
|
||||
}
|
||||
|
||||
class _BoardChecklistCellState extends State<BoardChecklistCell> {
|
||||
late ChecklistCellBloc _cellBloc;
|
||||
class _ChecklistCardCellState extends State<ChecklistCardCell> {
|
||||
late ChecklistCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as ChecklistCellController;
|
||||
_cellBloc = ChecklistCellBloc(cellController: cellController);
|
||||
_cellBloc = ChecklistCardCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const ChecklistCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
@ -30,7 +31,7 @@ class _BoardChecklistCellState extends State<BoardChecklistCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
|
||||
child: BlocBuilder<ChecklistCardCellBloc, ChecklistCellState>(
|
||||
builder: (context, state) =>
|
||||
ChecklistProgressBar(percent: state.percent),
|
||||
),
|
@ -1,35 +1,34 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/application/card/board_date_cell_bloc.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'define.dart';
|
||||
import '../bloc/date_card_cell_bloc.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class BoardDateCell extends StatefulWidget {
|
||||
final String groupId;
|
||||
class DateCardCell extends CardCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
|
||||
const BoardDateCell({
|
||||
required this.groupId,
|
||||
const DateCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardDateCell> createState() => _BoardDateCellState();
|
||||
State<DateCardCell> createState() => _DateCardCellState();
|
||||
}
|
||||
|
||||
class _BoardDateCellState extends State<BoardDateCell> {
|
||||
late BoardDateCellBloc _cellBloc;
|
||||
class _DateCardCellState extends State<DateCardCell> {
|
||||
late DateCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as DateCellController;
|
||||
|
||||
_cellBloc = BoardDateCellBloc(cellController: cellController)
|
||||
..add(const BoardDateCellEvent.initial());
|
||||
_cellBloc = DateCardCellBloc(cellController: cellController)
|
||||
..add(const DateCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -37,7 +36,7 @@ class _BoardDateCellState extends State<BoardDateCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<BoardDateCellBloc, BoardDateCellState>(
|
||||
child: BlocBuilder<DateCardCellBloc, DateCardCellState>(
|
||||
buildWhen: (previous, current) => previous.dateStr != current.dateStr,
|
||||
builder: (context, state) {
|
||||
if (state.dateStr.isEmpty) {
|
||||
@ -47,7 +46,7 @@ class _BoardDateCellState extends State<BoardDateCell> {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: BoardSizes.cardCellVPadding,
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.regular(
|
||||
state.dateStr,
|
@ -0,0 +1,68 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../bloc/number_card_cell_bloc.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class NumberCardCell extends CardCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
|
||||
const NumberCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<NumberCardCell> createState() => _NumberCardCellState();
|
||||
}
|
||||
|
||||
class _NumberCardCellState extends State<NumberCardCell> {
|
||||
late NumberCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as NumberCellController;
|
||||
|
||||
_cellBloc = NumberCardCellBloc(cellController: cellController)
|
||||
..add(const NumberCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<NumberCardCellBloc, NumberCardCellState>(
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
builder: (context, state) {
|
||||
if (state.content.isEmpty) {
|
||||
return const SizedBox();
|
||||
} else {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
state.content,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_cellBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -1,32 +1,35 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../grid/presentation/widgets/cell/select_option_cell/extension.dart';
|
||||
import '../../../grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart';
|
||||
import '../../application/card/board_select_option_cell_bloc.dart';
|
||||
import 'board_cell.dart';
|
||||
import '../bloc/select_option_card_cell_bloc.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class BoardSelectOptionCell extends StatefulWidget with EditableCell {
|
||||
final String groupId;
|
||||
class SelectOptionCardCell<T> extends CardCell<T> with EditableCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
@override
|
||||
final EditableCellNotifier? editableNotifier;
|
||||
final CellRenderHook<List<SelectOptionPB>, T>? renderHook;
|
||||
|
||||
const BoardSelectOptionCell({
|
||||
required this.groupId,
|
||||
@override
|
||||
final EditableCardNotifier? editableNotifier;
|
||||
|
||||
SelectOptionCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
required T? cardData,
|
||||
this.renderHook,
|
||||
this.editableNotifier,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
}) : super(key: key, cardData: cardData);
|
||||
|
||||
@override
|
||||
State<BoardSelectOptionCell> createState() => _BoardSelectOptionCellState();
|
||||
State<SelectOptionCardCell> createState() => _SelectOptionCardCellState();
|
||||
}
|
||||
|
||||
class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
||||
late BoardSelectOptionCellBloc _cellBloc;
|
||||
class _SelectOptionCardCellState extends State<SelectOptionCardCell> {
|
||||
late SelectOptionCardCellBloc _cellBloc;
|
||||
late PopoverController _popover;
|
||||
|
||||
@override
|
||||
@ -34,8 +37,8 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
||||
_popover = PopoverController();
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as SelectOptionCellController;
|
||||
_cellBloc = BoardSelectOptionCellBloc(cellController: cellController)
|
||||
..add(const BoardSelectOptionCellEvent.initial());
|
||||
_cellBloc = SelectOptionCardCellBloc(cellController: cellController)
|
||||
..add(const SelectOptionCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -43,12 +46,17 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<BoardSelectOptionCellBloc, BoardSelectOptionCellState>(
|
||||
child: BlocBuilder<SelectOptionCardCellBloc, SelectOptionCardCellState>(
|
||||
buildWhen: (previous, current) {
|
||||
return previous.selectedOptions != current.selectedOptions;
|
||||
}, builder: (context, state) {
|
||||
// Returns SizedBox if the content of the cell is empty
|
||||
if (_isEmpty(state)) return const SizedBox();
|
||||
Widget? custom = widget.renderHook?.call(
|
||||
state.selectedOptions,
|
||||
widget.cardData,
|
||||
);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
final children = state.selectedOptions.map(
|
||||
(option) {
|
||||
@ -73,14 +81,6 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
||||
);
|
||||
}
|
||||
|
||||
bool _isEmpty(BoardSelectOptionCellState state) {
|
||||
// The cell should hide if the option id is equal to the groupId.
|
||||
final isInGroup = state.selectedOptions
|
||||
.where((element) => element.id == widget.groupId)
|
||||
.isNotEmpty;
|
||||
return isInGroup || state.selectedOptions.isEmpty;
|
||||
}
|
||||
|
||||
Widget _wrapPopover(Widget child) {
|
||||
final constraints = BoxConstraints.loose(Size(
|
||||
SelectOptionCellEditor.editorPanelWidth,
|
@ -1,33 +1,31 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
import '../../application/card/board_text_cell_bloc.dart';
|
||||
import 'board_cell.dart';
|
||||
import 'define.dart';
|
||||
import '../../row/cell_builder.dart';
|
||||
import '../bloc/text_card_cell_bloc.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class BoardTextCell extends StatefulWidget with EditableCell {
|
||||
final String groupId;
|
||||
class TextCardCell extends CardCell with EditableCell {
|
||||
@override
|
||||
final EditableCellNotifier? editableNotifier;
|
||||
final EditableCardNotifier? editableNotifier;
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
|
||||
const BoardTextCell({
|
||||
required this.groupId,
|
||||
const TextCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
this.editableNotifier,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardTextCell> createState() => _BoardTextCellState();
|
||||
State<TextCardCell> createState() => _TextCardCellState();
|
||||
}
|
||||
|
||||
class _BoardTextCellState extends State<BoardTextCell> {
|
||||
late BoardTextCellBloc _cellBloc;
|
||||
class _TextCardCellState extends State<TextCardCell> {
|
||||
late TextCardCellBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
bool focusWhenInit = false;
|
||||
final focusNode = SingleListenerFocusNode();
|
||||
@ -36,8 +34,8 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as TextCellController;
|
||||
_cellBloc = BoardTextCellBloc(cellController: cellController)
|
||||
..add(const BoardTextCellEvent.initial());
|
||||
_cellBloc = TextCardCellBloc(cellController: cellController)
|
||||
..add(const TextCardCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
focusWhenInit = widget.editableNotifier?.isCellEditing.value ?? false;
|
||||
if (focusWhenInit) {
|
||||
@ -51,7 +49,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
if (!focusNode.hasFocus) {
|
||||
focusWhenInit = false;
|
||||
widget.editableNotifier?.isCellEditing.value = false;
|
||||
_cellBloc.add(const BoardTextCellEvent.enableEdit(false));
|
||||
_cellBloc.add(const TextCardCellEvent.enableEdit(false));
|
||||
}
|
||||
});
|
||||
_bindEditableNotifier();
|
||||
@ -68,12 +66,12 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
_cellBloc.add(BoardTextCellEvent.enableEdit(isEditing));
|
||||
_cellBloc.add(TextCardCellEvent.enableEdit(isEditing));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant BoardTextCell oldWidget) {
|
||||
void didUpdateWidget(covariant TextCardCell oldWidget) {
|
||||
_bindEditableNotifier();
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
@ -82,13 +80,13 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocListener<BoardTextCellBloc, BoardTextCellState>(
|
||||
child: BlocListener<TextCardCellBloc, TextCardCellState>(
|
||||
listener: (context, state) {
|
||||
if (_controller.text != state.content) {
|
||||
_controller.text = state.content;
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<BoardTextCellBloc, BoardTextCellState>(
|
||||
child: BlocBuilder<TextCardCellBloc, TextCardCellState>(
|
||||
buildWhen: (previous, current) {
|
||||
if (previous.content != current.content &&
|
||||
_controller.text == current.content &&
|
||||
@ -120,7 +118,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
}
|
||||
|
||||
Future<void> focusChanged() async {
|
||||
_cellBloc.add(BoardTextCellEvent.updateText(_controller.text));
|
||||
_cellBloc.add(TextCardCellEvent.updateText(_controller.text));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -131,10 +129,10 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildText(BoardTextCellState state) {
|
||||
Widget _buildText(TextCardCellState state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: BoardSizes.cardCellVPadding,
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
state.content,
|
||||
@ -156,7 +154,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
decoration: InputDecoration(
|
||||
// Magic number 4 makes the textField take up the same space as FlowyText
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: BoardSizes.cardCellVPadding + 4,
|
||||
vertical: CardSizes.cardCellVPadding + 4,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
@ -4,32 +4,31 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
|
||||
import '../../application/card/board_url_cell_bloc.dart';
|
||||
import 'define.dart';
|
||||
import '../bloc/url_card_cell_bloc.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class BoardUrlCell extends StatefulWidget {
|
||||
final String groupId;
|
||||
class URLCardCell extends CardCell {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
|
||||
const BoardUrlCell({
|
||||
required this.groupId,
|
||||
const URLCardCell({
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardUrlCell> createState() => _BoardUrlCellState();
|
||||
State<URLCardCell> createState() => _URLCardCellState();
|
||||
}
|
||||
|
||||
class _BoardUrlCellState extends State<BoardUrlCell> {
|
||||
late BoardURLCellBloc _cellBloc;
|
||||
class _URLCardCellState extends State<URLCardCell> {
|
||||
late URLCardCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as URLCellController;
|
||||
_cellBloc = BoardURLCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const BoardURLCellEvent.initial());
|
||||
_cellBloc = URLCardCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const URLCardCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -37,7 +36,7 @@ class _BoardUrlCellState extends State<BoardUrlCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<BoardURLCellBloc, BoardURLCellState>(
|
||||
child: BlocBuilder<URLCardCellBloc, URLCardCellState>(
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
builder: (context, state) {
|
||||
if (state.content.isEmpty) {
|
||||
@ -47,7 +46,7 @@ class _BoardUrlCellState extends State<BoardUrlCell> {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: BoardSizes.cardCellVPadding,
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
@ -1,3 +1,3 @@
|
||||
class BoardSizes {
|
||||
class CardSizes {
|
||||
static double get cardCellVPadding => 6;
|
||||
}
|
@ -9,7 +9,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
|
||||
import 'cell_builder.dart';
|
||||
import '../cell_builder.dart';
|
||||
|
||||
class GridCellAccessoryBuildContext {
|
||||
final BuildContext anchorContext;
|
@ -3,16 +3,16 @@ import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart'
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../application/cell/cell_service.dart';
|
||||
import 'cell_accessory.dart';
|
||||
import 'cell_shortcuts.dart';
|
||||
import 'checkbox_cell.dart';
|
||||
import 'checklist_cell/checklist_cell.dart';
|
||||
import 'date_cell/date_cell.dart';
|
||||
import 'number_cell.dart';
|
||||
import 'select_option_cell/select_option_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
import 'url_cell/url_cell.dart';
|
||||
import '../../application/cell/cell_service.dart';
|
||||
import 'accessory/cell_accessory.dart';
|
||||
import 'accessory/cell_shortcuts.dart';
|
||||
import 'cells/checkbox_cell/checkbox_cell.dart';
|
||||
import 'cells/checklist_cell/checklist_cell.dart';
|
||||
import 'cells/date_cell/date_cell.dart';
|
||||
import 'cells/number_cell/number_cell.dart';
|
||||
import 'cells/select_option_cell/select_option_cell.dart';
|
||||
import 'cells/text_cell/text_cell.dart';
|
||||
import 'cells/url_cell/url_cell.dart';
|
||||
|
||||
class GridCellBuilder {
|
||||
final CellCache cellCache;
|
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
import '../row/grid_row.dart';
|
||||
import 'cell_accessory.dart';
|
||||
import 'cell_builder.dart';
|
||||
import 'cell_shortcuts.dart';
|
||||
import '../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../../grid/presentation/widgets/row/row.dart';
|
||||
import '../accessory/cell_accessory.dart';
|
||||
import '../accessory/cell_shortcuts.dart';
|
||||
import '../cell_builder.dart';
|
||||
|
||||
class CellContainer extends StatelessWidget {
|
||||
final GridCellWidget child;
|
@ -1,12 +1,12 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../application/cell/checkbox_cell_bloc.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
import 'cell_builder.dart';
|
||||
import 'checkbox_cell_bloc.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../cell_builder.dart';
|
||||
|
||||
class GridCheckboxCell extends GridCellWidget {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
@ -26,7 +26,8 @@ class _CheckboxCellState extends GridCellState<GridCheckboxCell> {
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as CheckboxCellController;
|
||||
_cellBloc = getIt<CheckboxCellBloc>(param1: cellController)
|
||||
_cellBloc = CheckboxCellBloc(
|
||||
service: CellBackendService(), cellController: cellController)
|
||||
..add(const CheckboxCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../cell_builder.dart';
|
||||
import '../../cell_builder.dart';
|
||||
import 'checklist_cell_bloc.dart';
|
||||
import 'checklist_cell_editor.dart';
|
||||
import 'checklist_progress_bar.dart';
|
||||
|
||||
@ -20,7 +20,7 @@ class GridChecklistCell extends GridCellWidget {
|
||||
}
|
||||
|
||||
class GridChecklistCellState extends GridCellState<GridChecklistCell> {
|
||||
late ChecklistCellBloc _cellBloc;
|
||||
late ChecklistCardCellBloc _cellBloc;
|
||||
late final PopoverController _popover;
|
||||
|
||||
@override
|
||||
@ -28,7 +28,7 @@ class GridChecklistCellState extends GridCellState<GridChecklistCell> {
|
||||
_popover = PopoverController();
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as ChecklistCellController;
|
||||
_cellBloc = ChecklistCellBloc(cellController: cellController);
|
||||
_cellBloc = ChecklistCardCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const ChecklistCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
@ -54,7 +54,7 @@ class GridChecklistCellState extends GridCellState<GridChecklistCell> {
|
||||
onClose: () => widget.onCellEditing.value = false,
|
||||
child: Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
|
||||
child: BlocBuilder<ChecklistCardCellBloc, ChecklistCellState>(
|
||||
builder: (context, state) =>
|
||||
ChecklistProgressBar(percent: state.percent),
|
||||
),
|
@ -1,18 +1,19 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'checklist_cell_editor_bloc.dart';
|
||||
import 'select_option_service.dart';
|
||||
part 'checklist_cell_bloc.freezed.dart';
|
||||
|
||||
class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
|
||||
class ChecklistCardCellBloc
|
||||
extends Bloc<ChecklistCellEvent, ChecklistCellState> {
|
||||
final ChecklistCellController cellController;
|
||||
final SelectOptionBackendService _selectOptionSvc;
|
||||
void Function()? _onCellChangedFn;
|
||||
ChecklistCellBloc({
|
||||
ChecklistCardCellBloc({
|
||||
required this.cellController,
|
||||
}) : _selectOptionSvc =
|
||||
SelectOptionBackendService(cellId: cellController.cellId),
|
@ -2,16 +2,12 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/cell/checklist_cell_editor_bloc.dart';
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../../header/type_option/select_option_editor.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../../../grid/presentation/widgets/header/type_option/select_option_editor.dart';
|
||||
import 'checklist_cell_editor_bloc.dart';
|
||||
import 'checklist_progress_bar.dart';
|
||||
|
||||
class GridChecklistCellEditor extends StatefulWidget {
|
@ -1,12 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'select_option_service.dart';
|
||||
|
||||
part 'checklist_cell_editor_bloc.freezed.dart';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
@ -15,19 +15,20 @@ import 'package:table_calendar/table_calendar.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
|
||||
part 'date_cal_bloc.freezed.dart';
|
||||
|
||||
class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
class DateCellCalendarBloc
|
||||
extends Bloc<DateCellCalendarEvent, DateCellCalendarState> {
|
||||
final DateCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
DateCalBloc({
|
||||
DateCellCalendarBloc({
|
||||
required DateTypeOptionPB dateTypeOptionPB,
|
||||
required DateCellDataPB? cellData,
|
||||
required this.cellController,
|
||||
}) : super(DateCalState.initial(dateTypeOptionPB, cellData)) {
|
||||
on<DateCalEvent>(
|
||||
}) : super(DateCellCalendarState.initial(dateTypeOptionPB, cellData)) {
|
||||
on<DateCellCalendarEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async => _startListening(),
|
||||
@ -41,13 +42,13 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
emit(state.copyWith(focusedDay: focusedDay));
|
||||
},
|
||||
didReceiveCellUpdate: (DateCellDataPB? cellData) {
|
||||
final calData = calDataFromCellData(cellData);
|
||||
final time = calData.foldRight(
|
||||
final dateCellData = calDataFromCellData(cellData);
|
||||
final time = dateCellData.foldRight(
|
||||
"", (dateData, previous) => dateData.time ?? '');
|
||||
emit(state.copyWith(calData: calData, time: time));
|
||||
emit(state.copyWith(dateCellData: dateCellData, time: time));
|
||||
},
|
||||
setIncludeTime: (includeTime) async {
|
||||
await _updateTypeOption(emit, includeTime: includeTime);
|
||||
await _updateDateData(emit, includeTime: includeTime);
|
||||
},
|
||||
setDateFormat: (dateFormat) async {
|
||||
await _updateTypeOption(emit, dateFormat: dateFormat);
|
||||
@ -56,24 +57,28 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
await _updateTypeOption(emit, timeFormat: timeFormat);
|
||||
},
|
||||
setTime: (time) async {
|
||||
if (state.calData.isSome()) {
|
||||
if (state.dateCellData.isSome()) {
|
||||
await _updateDateData(emit, time: time);
|
||||
}
|
||||
},
|
||||
didUpdateCalData:
|
||||
(Option<CalendarData> data, Option<String> timeFormatError) {
|
||||
(Option<DateCellData> data, Option<String> timeFormatError) {
|
||||
emit(state.copyWith(
|
||||
calData: data, timeFormatError: timeFormatError));
|
||||
dateCellData: data, timeFormatError: timeFormatError));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateDateData(Emitter<DateCalState> emit,
|
||||
{DateTime? date, String? time}) {
|
||||
final CalendarData newDateData = state.calData.fold(
|
||||
() => CalendarData(date: date ?? DateTime.now(), time: time),
|
||||
Future<void> _updateDateData(Emitter<DateCellCalendarState> emit,
|
||||
{DateTime? date, String? time, bool? includeTime}) {
|
||||
final DateCellData newDateData = state.dateCellData.fold(
|
||||
() => DateCellData(
|
||||
date: date ?? DateTime.now(),
|
||||
time: time,
|
||||
includeTime: includeTime ?? false,
|
||||
),
|
||||
(dateData) {
|
||||
var newDateData = dateData;
|
||||
if (date != null && !isSameDay(newDateData.date, date)) {
|
||||
@ -83,6 +88,11 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
if (newDateData.time != time) {
|
||||
newDateData = newDateData.copyWith(time: time);
|
||||
}
|
||||
|
||||
if (includeTime != null && newDateData.includeTime != includeTime) {
|
||||
newDateData = newDateData.copyWith(includeTime: includeTime);
|
||||
}
|
||||
|
||||
return newDateData;
|
||||
},
|
||||
);
|
||||
@ -91,15 +101,16 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
}
|
||||
|
||||
Future<void> _saveDateData(
|
||||
Emitter<DateCalState> emit, CalendarData newCalData) async {
|
||||
if (state.calData == Some(newCalData)) {
|
||||
Emitter<DateCellCalendarState> emit, DateCellData newCalData) async {
|
||||
if (state.dateCellData == Some(newCalData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateCalData(
|
||||
Option<CalendarData> calData, Option<String> timeFormatError) {
|
||||
Option<DateCellData> dateCellData, Option<String> timeFormatError) {
|
||||
if (!isClosed) {
|
||||
add(DateCalEvent.didUpdateCalData(calData, timeFormatError));
|
||||
add(DateCellCalendarEvent.didUpdateCalData(
|
||||
dateCellData, timeFormatError));
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +120,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
(err) {
|
||||
switch (ErrorCode.valueOf(err.code)!) {
|
||||
case ErrorCode.InvalidDateTimeFormat:
|
||||
updateCalData(state.calData, Some(timeFormatPrompt(err)));
|
||||
updateCalData(state.dateCellData, Some(timeFormatPrompt(err)));
|
||||
break;
|
||||
default:
|
||||
Log.error(err);
|
||||
@ -148,17 +159,16 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
_onCellChangedFn = cellController.startListening(
|
||||
onCellChanged: ((cell) {
|
||||
if (!isClosed) {
|
||||
add(DateCalEvent.didReceiveCellUpdate(cell));
|
||||
add(DateCellCalendarEvent.didReceiveCellUpdate(cell));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void>? _updateTypeOption(
|
||||
Emitter<DateCalState> emit, {
|
||||
Emitter<DateCellCalendarState> emit, {
|
||||
DateFormat? dateFormat,
|
||||
TimeFormat? timeFormat,
|
||||
bool? includeTime,
|
||||
}) async {
|
||||
state.dateTypeOptionPB.freeze();
|
||||
final newDateTypeOption = state.dateTypeOptionPB.rebuild((typeOption) {
|
||||
@ -169,10 +179,6 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
if (timeFormat != null) {
|
||||
typeOption.timeFormat = timeFormat;
|
||||
}
|
||||
|
||||
if (includeTime != null) {
|
||||
typeOption.includeTime = includeTime;
|
||||
}
|
||||
});
|
||||
|
||||
final result = await FieldBackendService.updateFieldTypeOption(
|
||||
@ -191,48 +197,51 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DateCalEvent with _$DateCalEvent {
|
||||
const factory DateCalEvent.initial() = _Initial;
|
||||
const factory DateCalEvent.selectDay(DateTime day) = _SelectDay;
|
||||
const factory DateCalEvent.setCalFormat(CalendarFormat format) =
|
||||
class DateCellCalendarEvent with _$DateCellCalendarEvent {
|
||||
const factory DateCellCalendarEvent.initial() = _Initial;
|
||||
const factory DateCellCalendarEvent.selectDay(DateTime day) = _SelectDay;
|
||||
const factory DateCellCalendarEvent.setCalFormat(CalendarFormat format) =
|
||||
_CalendarFormat;
|
||||
const factory DateCalEvent.setFocusedDay(DateTime day) = _FocusedDay;
|
||||
const factory DateCalEvent.setTimeFormat(TimeFormat timeFormat) = _TimeFormat;
|
||||
const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
|
||||
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
|
||||
const factory DateCalEvent.setTime(String time) = _Time;
|
||||
const factory DateCalEvent.didReceiveCellUpdate(DateCellDataPB? data) =
|
||||
_DidReceiveCellUpdate;
|
||||
const factory DateCalEvent.didUpdateCalData(
|
||||
Option<CalendarData> data, Option<String> timeFormatError) =
|
||||
const factory DateCellCalendarEvent.setFocusedDay(DateTime day) = _FocusedDay;
|
||||
const factory DateCellCalendarEvent.setTimeFormat(TimeFormat timeFormat) =
|
||||
_TimeFormat;
|
||||
const factory DateCellCalendarEvent.setDateFormat(DateFormat dateFormat) =
|
||||
_DateFormat;
|
||||
const factory DateCellCalendarEvent.setIncludeTime(bool includeTime) =
|
||||
_IncludeTime;
|
||||
const factory DateCellCalendarEvent.setTime(String time) = _Time;
|
||||
const factory DateCellCalendarEvent.didReceiveCellUpdate(
|
||||
DateCellDataPB? data) = _DidReceiveCellUpdate;
|
||||
const factory DateCellCalendarEvent.didUpdateCalData(
|
||||
Option<DateCellData> data, Option<String> timeFormatError) =
|
||||
_DidUpdateCalData;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DateCalState with _$DateCalState {
|
||||
const factory DateCalState({
|
||||
class DateCellCalendarState with _$DateCellCalendarState {
|
||||
const factory DateCellCalendarState({
|
||||
required DateTypeOptionPB dateTypeOptionPB,
|
||||
required CalendarFormat format,
|
||||
required DateTime focusedDay,
|
||||
required Option<String> timeFormatError,
|
||||
required Option<CalendarData> calData,
|
||||
required Option<DateCellData> dateCellData,
|
||||
required String? time,
|
||||
required String timeHintText,
|
||||
}) = _DateCalState;
|
||||
}) = _DateCellCalendarState;
|
||||
|
||||
factory DateCalState.initial(
|
||||
factory DateCellCalendarState.initial(
|
||||
DateTypeOptionPB dateTypeOptionPB,
|
||||
DateCellDataPB? cellData,
|
||||
) {
|
||||
Option<CalendarData> calData = calDataFromCellData(cellData);
|
||||
Option<DateCellData> dateCellData = calDataFromCellData(cellData);
|
||||
final time =
|
||||
calData.foldRight("", (dateData, previous) => dateData.time ?? '');
|
||||
return DateCalState(
|
||||
dateCellData.foldRight("", (dateData, previous) => dateData.time ?? '');
|
||||
return DateCellCalendarState(
|
||||
dateTypeOptionPB: dateTypeOptionPB,
|
||||
format: CalendarFormat.month,
|
||||
focusedDay: DateTime.now(),
|
||||
time: time,
|
||||
calData: calData,
|
||||
dateCellData: dateCellData,
|
||||
timeFormatError: none(),
|
||||
timeHintText: _timeHintText(dateTypeOptionPB),
|
||||
);
|
||||
@ -245,30 +254,33 @@ String _timeHintText(DateTypeOptionPB typeOption) {
|
||||
return LocaleKeys.document_date_timeHintTextInTwelveHour.tr();
|
||||
case TimeFormat.TwentyFourHour:
|
||||
return LocaleKeys.document_date_timeHintTextInTwentyFourHour.tr();
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Option<CalendarData> calDataFromCellData(DateCellDataPB? cellData) {
|
||||
Option<DateCellData> calDataFromCellData(DateCellDataPB? cellData) {
|
||||
String? time = timeFromCellData(cellData);
|
||||
Option<CalendarData> calData = none();
|
||||
Option<DateCellData> dateData = none();
|
||||
if (cellData != null) {
|
||||
final timestamp = cellData.timestamp * 1000;
|
||||
final date = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt());
|
||||
calData = Some(CalendarData(date: date, time: time));
|
||||
final date = DateTime.fromMillisecondsSinceEpoch(
|
||||
timestamp.toInt(),
|
||||
isUtc: true,
|
||||
);
|
||||
dateData = Some(DateCellData(
|
||||
date: date,
|
||||
time: time,
|
||||
includeTime: cellData.includeTime,
|
||||
));
|
||||
}
|
||||
return calData;
|
||||
}
|
||||
|
||||
$fixnum.Int64 timestampFromDateTime(DateTime dateTime) {
|
||||
final timestamp = (dateTime.millisecondsSinceEpoch ~/ 1000);
|
||||
return $fixnum.Int64(timestamp);
|
||||
return dateData;
|
||||
}
|
||||
|
||||
String? timeFromCellData(DateCellDataPB? cellData) {
|
||||
String? time;
|
||||
if (cellData?.hasTime() ?? false) {
|
||||
time = cellData?.time;
|
||||
if (cellData == null || !cellData.hasTime()) {
|
||||
return null;
|
||||
}
|
||||
return time;
|
||||
|
||||
return cellData.time;
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/date_cell_bloc.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../cell_builder.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../cell_builder.dart';
|
||||
import 'date_cell_bloc.dart';
|
||||
import 'date_editor.dart';
|
||||
|
||||
class DateCellStyle extends GridCellStyle {
|
||||
@ -50,7 +49,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
|
||||
_popover = PopoverController();
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as DateCellController;
|
||||
_cellBloc = getIt<DateCellBloc>(param1: cellController)
|
||||
_cellBloc = DateCellBloc(cellController: cellController)
|
||||
..add(const DateCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/date_cal_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
@ -11,9 +10,7 @@ import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option.pb.dart';
|
||||
@ -21,9 +18,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../../common/type_option_separator.dart';
|
||||
import '../../header/type_option/date.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../../../grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import '../../../../grid/presentation/widgets/header/type_option/date.dart';
|
||||
import 'date_cal_bloc.dart';
|
||||
|
||||
final kToday = DateTime.now();
|
||||
final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
|
||||
@ -92,17 +90,17 @@ class _CellCalendarWidget extends StatefulWidget {
|
||||
|
||||
class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
|
||||
late PopoverMutex popoverMutex;
|
||||
late DateCalBloc bloc;
|
||||
late DateCellCalendarBloc bloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverMutex = PopoverMutex();
|
||||
|
||||
bloc = DateCalBloc(
|
||||
bloc = DateCellCalendarBloc(
|
||||
dateTypeOptionPB: widget.dateTypeOptionPB,
|
||||
cellData: widget.cellContext.getCellData(),
|
||||
cellController: widget.cellContext,
|
||||
)..add(const DateCalEvent.initial());
|
||||
)..add(const DateCellCalendarEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -110,18 +108,20 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: BlocBuilder<DateCalBloc, DateCalState>(
|
||||
child: BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
|
||||
buildWhen: (p, c) => p != c,
|
||||
builder: (context, state) {
|
||||
bool includeTime = state.dateCellData
|
||||
.fold(() => false, (dateData) => dateData.includeTime);
|
||||
List<Widget> children = [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: _buildCalendar(context),
|
||||
),
|
||||
if (state.dateTypeOptionPB.includeTime) ...[
|
||||
if (includeTime) ...[
|
||||
const VSpace(12.0),
|
||||
_TimeTextField(
|
||||
bloc: context.read<DateCalBloc>(),
|
||||
bloc: context.read<DateCellCalendarBloc>(),
|
||||
popoverMutex: popoverMutex,
|
||||
),
|
||||
],
|
||||
@ -151,7 +151,7 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
|
||||
}
|
||||
|
||||
Widget _buildCalendar(BuildContext context) {
|
||||
return BlocBuilder<DateCalBloc, DateCalState>(
|
||||
return BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
|
||||
builder: (context, state) {
|
||||
final textStyle = Theme.of(context).textTheme.bodyMedium!;
|
||||
final boxDecoration = BoxDecoration(
|
||||
@ -208,23 +208,25 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
|
||||
textStyle.textColor(Theme.of(context).disabledColor),
|
||||
),
|
||||
selectedDayPredicate: (day) {
|
||||
return state.calData.fold(
|
||||
return state.dateCellData.fold(
|
||||
() => false,
|
||||
(dateData) => isSameDay(dateData.date, day),
|
||||
);
|
||||
},
|
||||
onDaySelected: (selectedDay, focusedDay) {
|
||||
context
|
||||
.read<DateCalBloc>()
|
||||
.add(DateCalEvent.selectDay(selectedDay));
|
||||
.read<DateCellCalendarBloc>()
|
||||
.add(DateCellCalendarEvent.selectDay(selectedDay));
|
||||
},
|
||||
onFormatChanged: (format) {
|
||||
context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
|
||||
context
|
||||
.read<DateCellCalendarBloc>()
|
||||
.add(DateCellCalendarEvent.setCalFormat(format));
|
||||
},
|
||||
onPageChanged: (focusedDay) {
|
||||
context
|
||||
.read<DateCalBloc>()
|
||||
.add(DateCalEvent.setFocusedDay(focusedDay));
|
||||
.read<DateCellCalendarBloc>()
|
||||
.add(DateCellCalendarEvent.setFocusedDay(focusedDay));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -237,8 +239,11 @@ class _IncludeTimeButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<DateCalBloc, DateCalState, bool>(
|
||||
selector: (state) => state.dateTypeOptionPB.includeTime,
|
||||
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
|
||||
selector: (state) => state.dateCellData.fold(
|
||||
() => false,
|
||||
(dateData) => dateData.includeTime,
|
||||
),
|
||||
builder: (context, includeTime) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
@ -258,8 +263,8 @@ class _IncludeTimeButton extends StatelessWidget {
|
||||
Toggle(
|
||||
value: includeTime,
|
||||
onChanged: (value) => context
|
||||
.read<DateCalBloc>()
|
||||
.add(DateCalEvent.setIncludeTime(!value)),
|
||||
.read<DateCellCalendarBloc>()
|
||||
.add(DateCellCalendarEvent.setIncludeTime(!value)),
|
||||
style: ToggleStyle.big,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
@ -274,7 +279,7 @@ class _IncludeTimeButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _TimeTextField extends StatefulWidget {
|
||||
final DateCalBloc bloc;
|
||||
final DateCellCalendarBloc bloc;
|
||||
final PopoverMutex popoverMutex;
|
||||
|
||||
const _TimeTextField({
|
||||
@ -298,7 +303,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
|
||||
|
||||
_focusNode.addListener(() {
|
||||
if (mounted) {
|
||||
widget.bloc.add(DateCalEvent.setTime(_controller.text));
|
||||
widget.bloc.add(DateCellCalendarEvent.setTime(_controller.text));
|
||||
}
|
||||
});
|
||||
|
||||
@ -340,7 +345,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
|
||||
errorText: widget.bloc.state.timeFormatError
|
||||
.fold(() => "", (error) => error),
|
||||
onEditingComplete: (value) =>
|
||||
widget.bloc.add(DateCalEvent.setTime(value)),
|
||||
widget.bloc.add(DateCellCalendarEvent.setTime(value)),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -364,7 +369,8 @@ class _DateTypeOptionButton extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final title =
|
||||
"${LocaleKeys.grid_field_dateFormat.tr()} & ${LocaleKeys.grid_field_timeFormat.tr()}";
|
||||
return BlocSelector<DateCalBloc, DateCalState, DateTypeOptionPB>(
|
||||
return BlocSelector<DateCellCalendarBloc, DateCellCalendarState,
|
||||
DateTypeOptionPB>(
|
||||
selector: (state) => state.dateTypeOptionPB,
|
||||
builder: (context, dateTypeOptionPB) {
|
||||
return AppFlowyPopover(
|
||||
@ -391,7 +397,7 @@ class _DateTypeOptionButton extends StatelessWidget {
|
||||
return _CalDateTimeSetting(
|
||||
dateTypeOptionPB: dateTypeOptionPB,
|
||||
onEvent: (event) {
|
||||
context.read<DateCalBloc>().add(event);
|
||||
context.read<DateCellCalendarBloc>().add(event);
|
||||
popoverMutex.close();
|
||||
},
|
||||
);
|
||||
@ -404,7 +410,7 @@ class _DateTypeOptionButton extends StatelessWidget {
|
||||
|
||||
class _CalDateTimeSetting extends StatefulWidget {
|
||||
final DateTypeOptionPB dateTypeOptionPB;
|
||||
final Function(DateCalEvent) onEvent;
|
||||
final Function(DateCellCalendarEvent) onEvent;
|
||||
const _CalDateTimeSetting({
|
||||
required this.dateTypeOptionPB,
|
||||
required this.onEvent,
|
||||
@ -430,7 +436,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
|
||||
return DateFormatList(
|
||||
selectedFormat: widget.dateTypeOptionPB.dateFormat,
|
||||
onSelected: (format) {
|
||||
widget.onEvent(DateCalEvent.setDateFormat(format));
|
||||
widget.onEvent(DateCellCalendarEvent.setDateFormat(format));
|
||||
timeSettingPopoverMutex.close();
|
||||
},
|
||||
);
|
||||
@ -448,7 +454,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
|
||||
return TimeFormatList(
|
||||
selectedFormat: widget.dateTypeOptionPB.timeFormat,
|
||||
onSelected: (format) {
|
||||
widget.onEvent(DateCalEvent.setTimeFormat(format));
|
||||
widget.onEvent(DateCellCalendarEvent.setTimeFormat(format));
|
||||
timeSettingPopoverMutex.close();
|
||||
});
|
||||
},
|
@ -1,12 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../application/cell/number_cell_bloc.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
import 'cell_builder.dart';
|
||||
import 'number_cell_bloc.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../cell_builder.dart';
|
||||
|
||||
class GridNumberCell extends GridCellWidget {
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
@ -26,8 +25,9 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController = widget.cellControllerBuilder.build();
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: cellController)
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as NumberCellController;
|
||||
_cellBloc = NumberCellBloc(cellController: cellController)
|
||||
..add(const NumberCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.cellContent);
|
||||
super.initState();
|
@ -1,15 +1,14 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/cell/select_option_cell_bloc.dart';
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../cell_builder.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../cell_builder.dart';
|
||||
import 'extension.dart';
|
||||
import 'select_option_cell_bloc.dart';
|
||||
import 'select_option_editor.dart';
|
||||
|
||||
class SelectOptionCellStyle extends GridCellStyle {
|
||||
@ -48,7 +47,7 @@ class _SingleSelectCellState extends GridCellState<GridSingleSelectCell> {
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as SelectOptionCellController;
|
||||
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellController)
|
||||
_cellBloc = SelectOptionCellBloc(cellController: cellController)
|
||||
..add(const SelectOptionCellEvent.initial());
|
||||
_popover = PopoverController();
|
||||
super.initState();
|
||||
@ -111,7 +110,7 @@ class _MultiSelectCellState extends GridCellState<GridMultiSelectCell> {
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as SelectOptionCellController;
|
||||
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellController)
|
||||
_cellBloc = SelectOptionCellBloc(cellController: cellController)
|
||||
..add(const SelectOptionCellEvent.initial());
|
||||
_popover = PopoverController();
|
||||
super.initState();
|
@ -1,14 +1,10 @@
|
||||
import 'dart:collection';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -16,10 +12,11 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
|
||||
import '../../../layout/sizes.dart';
|
||||
import '../../common/type_option_separator.dart';
|
||||
import '../../header/type_option/select_option_editor.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../../../grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import '../../../../grid/presentation/widgets/header/type_option/select_option_editor.dart';
|
||||
import 'extension.dart';
|
||||
import 'select_option_editor_bloc.dart';
|
||||
import 'text_field.dart';
|
||||
|
||||
const double _editorPanelWidth = 300;
|
@ -1,11 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/cell/text_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
import 'cell_builder.dart';
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
import '../../cell_builder.dart';
|
||||
|
||||
class GridTextCellStyle extends GridCellStyle {
|
||||
String? placeholder;
|
||||
@ -40,8 +39,9 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController = widget.cellControllerBuilder.build();
|
||||
_cellBloc = getIt<TextCellBloc>(param1: cellController);
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as TextCellController;
|
||||
_cellBloc = TextCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const TextCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
super.initState();
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/cell/url_cell_editor_bloc.dart';
|
||||
import 'url_cell_editor_bloc.dart';
|
||||
|
||||
class URLCellEditor extends StatefulWidget {
|
||||
final VoidCallback onExit;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user