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