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:
ascarbek 2023-03-12 17:19:06 +06:00
commit a9c8bad599
244 changed files with 9303 additions and 2433 deletions

View File

@ -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",

View File

@ -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] = {};

View File

@ -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);
} }

View File

@ -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 {

View File

@ -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!;

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
} }
} }

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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,
)); ));
} }

View File

@ -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();
}
}

View File

@ -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))),

View File

@ -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),
),
);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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,
); );

View File

@ -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';

View File

@ -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);
},
);
result.fold(
(newRow) => _loadEvent(newRow.id),
(err) => Log.error(err),
);
} }
void _startListening() {
_databaseDataController.addListener(
onDatabaseChanged: (database) {
if (!isClosed) return;
add(CalendarEvent.didReceiveDatabaseUpdate(database));
},
onSettingsChanged: (CalendarSettingsPB settings) {
if (isClosed) return;
add(CalendarEvent.didReceiveCalendarSettings(settings));
},
onArrangeWithNewField: (field) {
if (isClosed) return;
_initializeEvents(field);
// add(CalendarEvent.)
},
onError: (err) {
Log.error(err);
}, },
); );
} }
void _initializeEvents(FieldPB dateField) { 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;
// final List<CalendarEventData<CalendarData>> events = rows.map((row) { DatabaseEventGetAllCalendarEvents(payload).send().then((result) {
// final event = CalendarEventData( result.fold(
// title: "", (events) {
// date: row -> dateField -> value, if (!isClosed) {
// event: row, final calendarEvents = <CalendarEventData<CalendarCardData>>[];
// ); for (final eventPB in events.items) {
final calendarEvent = _calendarEventDataFromEventPB(eventPB);
// return event; if (calendarEvent != null) {
// }).toList(); calendarEvents.add(calendarEvent);
calendarEventsController.addAll(events);
} }
} }
add(CalendarEvent.didLoadAllEvents(calendarEvents));
}
},
(r) => Log.error(r),
);
});
}
CalendarEventData<CalendarCardData>? _calendarEventDataFromEventPB(
CalendarEventPB eventPB) {
final fieldInfo = state.fieldInfoByFieldId[eventPB.titleFieldId];
if (fieldInfo != null) {
final cellId = CellIdentifier(
viewId: viewId,
rowId: eventPB.rowId,
fieldInfo: fieldInfo,
);
final eventData = CalendarCardData(
event: eventPB,
cellId: cellId,
);
final date = DateTime.fromMillisecondsSinceEpoch(
eventPB.timestamp.toInt() * 1000,
isUtc: true,
);
return CalendarEventData(
title: eventPB.title,
date: date,
event: eventData,
);
} else {
return null;
}
}
void _startListening() {
final onDatabaseChanged = DatabaseCallbacks(
onDatabaseChanged: (database) {
if (isClosed) return;
},
onFieldsChanged: (fieldInfos) {
if (isClosed) return;
final fieldInfoByFieldId = {
for (var fieldInfo in fieldInfos) fieldInfo.field.id: fieldInfo
};
add(CalendarEvent.didUpdateFieldInfos(fieldInfoByFieldId));
},
);
final onLayoutChanged = LayoutCallbacks(
onLayoutChanged: _didReceiveLayoutSetting,
onLoadLayout: _didReceiveLayoutSetting,
);
_databaseController.addListener(
onDatabaseChanged: onDatabaseChanged,
onLayoutChanged: onLayoutChanged,
);
}
void _didReceiveLayoutSetting(LayoutSettingPB layoutSetting) {
if (layoutSetting.hasCalendar()) {
if (isClosed) return;
add(CalendarEvent.didReceiveCalendarSettings(layoutSetting.calendar));
}
}
}
typedef Events = List<CalendarEventData<CalendarCardData>>;
@freezed @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});
} }

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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(
providers: [
BlocProvider<CalendarBloc>.value(
value: _calendarBloc,
)
],
child: BlocListener<CalendarBloc, CalendarState>(
listenWhen: (previous, current) => previous.events != current.events,
listener: (context, state) {
if (state.events.isNotEmpty) {
_eventController.removeWhere((element) => true);
_eventController.addAll(state.events);
}
},
child: BlocBuilder<CalendarBloc, CalendarState>(
builder: (context, state) {
return Column(
children: [ children: [
// const _ToolbarBlocAdaptor(), // const _ToolbarBlocAdaptor(),
_toolbar(), _toolbar(),
_buildCalendar(_eventController), _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;
}

View File

@ -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(

View File

@ -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();
}
}

View File

@ -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())) {

View File

@ -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();

View File

@ -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,
); );

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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) {

View File

@ -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, _) {

View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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()));
} }
} }

View File

@ -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),

View File

@ -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() ?? "",
); );
} }

View File

@ -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 ?? [],
); );
} }

View File

@ -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,
); );

View File

@ -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 ?? "",
); );

View File

@ -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,

View File

@ -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,
), ),
); );

View File

@ -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));

View File

@ -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,
); );

View File

@ -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);
}
} }
class EditableCellNotifier { renderHook[FieldType.SingleSelect] = selectOptionHook;
renderHook[FieldType.MultiSelect] = selectOptionHook;
}
}
abstract class CardCell<T> extends StatefulWidget {
final T? cardData;
const CardCell({super.key, this.cardData});
}
class EditableCardNotifier {
final ValueNotifier<bool> isCellEditing; 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 {

View File

@ -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()),
), ),
); );
}, },

View File

@ -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),
), ),

View File

@ -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,

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -1,3 +1,3 @@
class BoardSizes { class CardSizes {
static double get cardCellVPadding => 6; static double get cardCellVPadding => 6;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();
} }

View File

@ -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),
), ),

View File

@ -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),

View File

@ -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 {

View File

@ -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';

View File

@ -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';

View File

@ -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;
} }

View File

@ -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();
} }

View File

@ -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();
}); });
}, },

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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