Merge branch 'AppFlowy-IO:main' into side_context_menu

This commit is contained in:
Aryaman Arora 2022-08-10 14:51:30 +05:30 committed by GitHub
commit a71c5c0ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
397 changed files with 54285 additions and 2517 deletions

View File

@ -34,7 +34,7 @@ jobs:
with:
channel: 'stable'
cache: true
flutter-version: '3.0.0'
flutter-version: '3.0.5'
- name: Cache Cargo
uses: actions/cache@v2

View File

@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
flutter-version: '3.0.0'
flutter-version: '3.0.5'
channel: "stable"
- uses: actions-rs/toolchain@v1
with:

View File

@ -26,7 +26,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.0'
flutter-version: '3.0.5'
cache: true
- name: Cache Cargo

View File

@ -50,7 +50,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.0'
flutter-version: '3.0.5'
- name: Pre build
working-directory: frontend
@ -99,7 +99,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.0'
flutter-version: '3.0.5'
- name: Pre build
working-directory: frontend

View File

@ -22,7 +22,7 @@ jobs:
override: true
- uses: subosito/flutter-action@v1
with:
flutter-version: '3.0.0'
flutter-version: '3.0.5'
channel: "stable"
- name: Rust Deps

View File

@ -94,7 +94,8 @@
},
"tooltip": {
"lightMode": "Switch to Light mode",
"darkMode": "Switch to Dark mode"
"darkMode": "Switch to Dark mode",
"openAsPage": "Open as a Page"
},
"notifications": {
"export": {
@ -215,4 +216,4 @@
"timeHintTextInTwentyFourHour": "12:00"
}
}
}
}

View File

@ -4,7 +4,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
class BlankPluginBuilder extends PluginBuilder {
@override
@ -42,7 +42,8 @@ class BlankPagePlugin extends Plugin {
class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
@override
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12);
Widget get leftBarItem =>
FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12);
@override
Widget buildWidget() => const BlankPage();

View File

@ -1,10 +1,10 @@
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:flutter/material.dart';
import 'src/board_page.dart';
import 'presentation/board_page.dart';
class BoardPluginBuilder implements PluginBuilder {
@override
@ -28,7 +28,7 @@ class BoardPluginBuilder implements PluginBuilder {
class BoardPluginConfig implements PluginConfig {
@override
bool get creatable => false;
bool get creatable => true;
}
class BoardPlugin extends Plugin {

View File

@ -0,0 +1,161 @@
// ignore_for_file: unused_field
import 'package:appflowy_board/appflowy_board.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
class BoardPage extends StatefulWidget {
final ViewPB _view;
const BoardPage({required ViewPB view, Key? key})
: _view = view,
super(key: key);
@override
State<BoardPage> createState() => _BoardPageState();
}
class _BoardPageState extends State<BoardPage> {
final BoardDataController boardDataController = BoardDataController(
onMoveColumn: (fromIndex, toIndex) {
debugPrint('Move column from $fromIndex to $toIndex');
},
onMoveColumnItem: (columnId, fromIndex, toIndex) {
debugPrint('Move $columnId:$fromIndex to $columnId:$toIndex');
},
onMoveColumnItemToColumn: (fromColumnId, fromIndex, toColumnId, toIndex) {
debugPrint('Move $fromColumnId:$fromIndex to $toColumnId:$toIndex');
},
);
@override
void initState() {
final column1 = BoardColumnData(id: "To Do", items: [
TextItem("Card 1"),
TextItem("Card 2"),
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 4"),
]);
final column2 = BoardColumnData(id: "In Progress", items: [
RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 6"),
]);
final column3 = BoardColumnData(id: "Done", items: []);
boardDataController.addColumn(column1);
boardDataController.addColumn(column2);
boardDataController.addColumn(column3);
super.initState();
}
@override
Widget build(BuildContext context) {
final config = BoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
);
return Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Board(
dataController: boardDataController,
footBuilder: (context, columnData) {
return AppFlowyColumnFooter(
icon: const Icon(Icons.add, size: 20),
title: const Text('New'),
height: 50,
margin: config.columnItemPadding,
);
},
headerBuilder: (context, columnData) {
return AppFlowyColumnHeader(
icon: const Icon(Icons.lightbulb_circle),
title: Text(columnData.id),
addIcon: const Icon(Icons.add, size: 20),
moreIcon: const Icon(Icons.more_horiz, size: 20),
height: 50,
margin: config.columnItemPadding,
);
},
cardBuilder: (context, item) {
return AppFlowyColumnItemCard(
key: ObjectKey(item),
child: _buildCard(item),
);
},
columnConstraints: const BoxConstraints.tightFor(width: 240),
config: BoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
),
),
),
);
}
Widget _buildCard(ColumnItem item) {
if (item is TextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(item.s),
),
);
}
if (item is RichTextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
Text(
item.subtitle,
style: const TextStyle(fontSize: 12, color: Colors.grey),
)
],
),
),
);
}
throw UnimplementedError();
}
}
class TextItem extends ColumnItem {
final String s;
TextItem(this.s);
@override
String get id => s;
}
class RichTextItem extends ColumnItem {
final String title;
final String subtitle;
RichTextItem({required this.title, required this.subtitle});
@override
String get id => title;
}
extension HexColor on Color {
static Color fromHex(String hexString) {
final buffer = StringBuffer();
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
buffer.write(hexString.replaceFirst('#', ''));
return Color(int.parse(buffer.toString(), radix: 16));
}
}

View File

@ -1,7 +1,7 @@
import 'dart:convert';
import 'package:app_flowy/workspace/application/doc/doc_service.dart';
import 'package:app_flowy/workspace/application/trash/trash_service.dart';
import 'package:app_flowy/plugins/trash/application/trash_service.dart';
import 'package:app_flowy/workspace/application/view/view_listener.dart';
import 'package:app_flowy/plugins/doc/application/doc_service.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
@ -43,14 +43,17 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
emit(state.copyWith(isDeleted: false));
},
deletePermanently: (DeletePermanently value) async {
final result = await trashService.deleteViews([Tuple2(view.id, TrashType.TrashView)]);
final result = await trashService
.deleteViews([Tuple2(view.id, TrashType.TrashView)]);
final newState = result.fold((l) => state.copyWith(forceClose: true), (r) => state);
final newState = result.fold(
(l) => state.copyWith(forceClose: true), (r) => state);
emit(newState);
},
restorePage: (RestorePage value) async {
final result = await trashService.putback(view.id);
final newState = result.fold((l) => state.copyWith(isDeleted: false), (r) => state);
final newState = result.fold(
(l) => state.copyWith(isDeleted: false), (r) => state);
emit(newState);
},
);
@ -93,10 +96,12 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
final documentDelta = document.toDelta();
_composeDelta(delta, documentDelta);
});
emit(state.copyWith(loadingState: DocumentLoadingState.finish(left(unit))));
emit(state.copyWith(
loadingState: DocumentLoadingState.finish(left(unit))));
},
(err) {
emit(state.copyWith(loadingState: DocumentLoadingState.finish(right(err))));
emit(state.copyWith(
loadingState: DocumentLoadingState.finish(right(err))));
},
);
}
@ -156,5 +161,6 @@ class DocumentState with _$DocumentState {
@freezed
class DocumentLoadingState with _$DocumentLoadingState {
const factory DocumentLoadingState.loading() = _Loading;
const factory DocumentLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
const factory DocumentLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish;
}

View File

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:io';
import 'package:app_flowy/startup/tasks/rust_sdk.dart';
import 'package:app_flowy/workspace/application/doc/share_service.dart';
import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart';
import 'package:app_flowy/plugins/doc/application/share_service.dart';
import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@ -14,13 +14,15 @@ part 'share_bloc.freezed.dart';
class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
ShareService service;
ViewPB view;
DocShareBloc({required this.view, required this.service}) : super(const DocShareState.initial()) {
DocShareBloc({required this.view, required this.service})
: super(const DocShareState.initial()) {
on<DocShareEvent>((event, emit) async {
await event.map(
shareMarkdown: (ShareMarkdown value) async {
await service.exportMarkdown(view.id).then((result) {
result.fold(
(value) => emit(DocShareState.finish(left(_convertDeltaToMarkdown(value)))),
(value) => emit(
DocShareState.finish(left(_convertDeltaToMarkdown(value)))),
(error) => emit(DocShareState.finish(right(error))),
);
});
@ -73,5 +75,6 @@ class DocShareEvent with _$DocShareEvent {
class DocShareState with _$DocShareState {
const factory DocShareState.initial() = _Initial;
const factory DocShareState.loading() = _Loading;
const factory DocShareState.finish(Either<ExportDataPB, FlowyError> successOrFail) = _Finish;
const factory DocShareState.finish(
Either<ExportDataPB, FlowyError> successOrFail) = _Finish;
}

View File

@ -1,14 +1,14 @@
library docuemnt_plugin;
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/appearance.dart';
import 'package:app_flowy/workspace/application/doc/share_bloc.dart';
import 'package:app_flowy/workspace/application/view/view_listener.dart';
import 'package:app_flowy/plugins/doc/application/share_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/toast.dart';
import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:clipboard/clipboard.dart';
@ -26,12 +26,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'src/document_page.dart';
export './src/document_page.dart';
export './src/widget/toolbar/history_button.dart';
export './src/widget/toolbar/tool_bar.dart';
export './src/widget/toolbar/toolbar_icon_button.dart';
import 'document_page.dart';
class DocumentPluginBuilder extends PluginBuilder {
@override
@ -58,7 +53,9 @@ class DocumentPlugin implements Plugin {
ViewListener? _listener;
late PluginType _pluginType;
DocumentPlugin({required PluginType pluginType, required ViewPB view, Key? key}) : _view = view {
DocumentPlugin(
{required PluginType pluginType, required ViewPB view, Key? key})
: _view = view {
_pluginType = pluginType;
_listener = getIt<ViewListener>(param1: view);
_listener?.start(onViewUpdated: (result) {
@ -112,7 +109,8 @@ class DocumentPluginDisplay extends PluginDisplay<int> with NavigationItem {
class DocumentShareButton extends StatelessWidget {
final ViewPB view;
DocumentShareButton({Key? key, required this.view}) : super(key: ValueKey(view.hashCode));
DocumentShareButton({Key? key, required this.view})
: super(key: ValueKey(view.hashCode));
@override
Widget build(BuildContext context) {
@ -149,7 +147,8 @@ class DocumentShareButton extends StatelessWidget {
fontSize: 12,
borderRadius: Corners.s6Border,
color: Colors.lightBlue,
onPressed: () => _showActionList(context, Offset(-(buttonWidth / 2), 10)),
onPressed: () => _showActionList(
context, Offset(-(buttonWidth / 2), 10)),
),
),
),
@ -165,7 +164,8 @@ class DocumentShareButton extends StatelessWidget {
case ExportType.Link:
break;
case ExportType.Markdown:
FlutterClipboard.copy(exportData.data).then((value) => Log.info('copied to clipboard'));
FlutterClipboard.copy(exportData.data)
.then((value) => Log.info('copied to clipboard'));
break;
case ExportType.Text:
break;
@ -179,11 +179,15 @@ class DocumentShareButton extends StatelessWidget {
result.fold(() {}, (action) {
switch (action) {
case ShareAction.markdown:
context.read<DocShareBloc>().add(const DocShareEvent.shareMarkdown());
showMessageToast('Exported to: ${LocaleKeys.notifications_export_path.tr()}');
context
.read<DocShareBloc>()
.add(const DocShareEvent.shareMarkdown());
showMessageToast(
'Exported to: ${LocaleKeys.notifications_export_path.tr()}');
break;
case ShareAction.copyLink:
FlowyAlertDialog(title: LocaleKeys.shareAction_workInProgress.tr()).show(context);
FlowyAlertDialog(title: LocaleKeys.shareAction_workInProgress.tr())
.show(context);
break;
}
});
@ -198,7 +202,8 @@ class DocumentShareButton extends StatelessWidget {
class ShareActions with ActionList<ShareActionWrapper>, FlowyOverlayDelegate {
final Function(dartz.Option<ShareAction>) onSelected;
final _items = ShareAction.values.map((action) => ShareActionWrapper(action)).toList();
final _items =
ShareAction.values.map((action) => ShareActionWrapper(action)).toList();
ShareActions({required this.onSelected});
@ -212,7 +217,8 @@ class ShareActions with ActionList<ShareActionWrapper>, FlowyOverlayDelegate {
List<ShareActionWrapper> get items => _items;
@override
void Function(dartz.Option<ShareActionWrapper> p1) get selectCallback => (result) {
void Function(dartz.Option<ShareActionWrapper> p1) get selectCallback =>
(result) {
result.fold(
() => onSelected(dartz.none()),
(wrapper) => onSelected(

View File

@ -1,7 +1,7 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/appearance.dart';
import 'package:app_flowy/workspace/application/doc/doc_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/doc/document.dart';
import 'package:app_flowy/plugins/doc/presentation/banner.dart';
import 'package:app_flowy/plugins/doc/presentation/toolbar/tool_bar.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter_quill/flutter_quill.dart' as quill;
@ -10,8 +10,8 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'application/doc_bloc.dart';
import 'styles.dart';
import 'widget/banner.dart';
class DocumentPage extends StatefulWidget {
final ViewPB view;
@ -29,7 +29,8 @@ class _DocumentPageState extends State<DocumentPage> {
@override
void initState() {
documentBloc = getIt<DocumentBloc>(param1: super.widget.view)..add(const DocumentEvent.initial());
documentBloc = getIt<DocumentBloc>(param1: super.widget.view)
..add(const DocumentEvent.initial());
super.initState();
}
@ -39,10 +40,12 @@ class _DocumentPageState extends State<DocumentPage> {
providers: [
BlocProvider<DocumentBloc>.value(value: documentBloc),
],
child: BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
child:
BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
return state.loadingState.map(
// loading: (_) => const FlowyProgressIndicator(),
loading: (_) => SizedBox.expand(child: Container(color: Colors.transparent)),
loading: (_) =>
SizedBox.expand(child: Container(color: Colors.transparent)),
finish: (result) => result.successOrFail.fold(
(_) {
if (state.forceClose) {
@ -90,8 +93,11 @@ class _DocumentPageState extends State<DocumentPage> {
Widget _renderBanner(BuildContext context) {
return DocumentBanner(
onRestore: () => context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
onDelete: () => context.read<DocumentBloc>().add(const DocumentEvent.deletePermanently()),
onRestore: () =>
context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
onDelete: () => context
.read<DocumentBloc>()
.add(const DocumentEvent.deletePermanently()),
);
}

View File

@ -11,7 +11,9 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
class DocumentBanner extends StatelessWidget {
final void Function() onRestore;
final void Function() onDelete;
const DocumentBanner({required this.onRestore, required this.onDelete, Key? key}) : super(key: key);
const DocumentBanner(
{required this.onRestore, required this.onDelete, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -26,7 +28,8 @@ class DocumentBanner extends StatelessWidget {
fit: BoxFit.scaleDown,
child: Row(
children: [
FlowyText.medium(LocaleKeys.deletePagePrompt_text.tr(), color: Colors.white),
FlowyText.medium(LocaleKeys.deletePagePrompt_text.tr(),
color: Colors.white),
const HSpace(20),
BaseStyledButton(
minWidth: 160,
@ -37,7 +40,10 @@ class DocumentBanner extends StatelessWidget {
downColor: theme.main1,
outlineColor: Colors.white,
borderRadius: Corners.s8Border,
child: FlowyText.medium(LocaleKeys.deletePagePrompt_restore.tr(), color: Colors.white, fontSize: 14),
child: FlowyText.medium(
LocaleKeys.deletePagePrompt_restore.tr(),
color: Colors.white,
fontSize: 14),
onPressed: onRestore),
const HSpace(20),
BaseStyledButton(
@ -49,8 +55,10 @@ class DocumentBanner extends StatelessWidget {
downColor: theme.main1,
outlineColor: Colors.white,
borderRadius: Corners.s8Border,
child: FlowyText.medium(LocaleKeys.deletePagePrompt_deletePermanent.tr(),
color: Colors.white, fontSize: 14),
child: FlowyText.medium(
LocaleKeys.deletePagePrompt_deletePermanent.tr(),
color: Colors.white,
fontSize: 14),
onPressed: onDelete),
],
),

View File

@ -1,11 +1,10 @@
import 'package:app_flowy/plugins/doc/presentation/style_widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:flowy_infra/theme.dart';
import 'widget/style_widgets.dart';
DefaultStyles customStyles(BuildContext context) {
const baseSpacing = Tuple2<double, double>(6, 0);
@ -53,7 +52,8 @@ DefaultStyles customStyles(BuildContext context) {
const Tuple2(8, 0),
const Tuple2(0, 0),
null),
paragraph: DefaultTextBlockStyle(baseStyle, const Tuple2(10, 0), const Tuple2(0, 0), null),
paragraph: DefaultTextBlockStyle(
baseStyle, const Tuple2(10, 0), const Tuple2(0, 0), null),
bold: const TextStyle(fontWeight: FontWeight.bold),
italic: const TextStyle(fontStyle: FontStyle.italic),
small: const TextStyle(fontSize: 12, color: Colors.black45),
@ -78,8 +78,8 @@ DefaultStyles customStyles(BuildContext context) {
const Tuple2(0, 0),
const Tuple2(0, 0),
null),
lists:
DefaultListBlockStyle(baseStyle, baseSpacing, const Tuple2(0, 6), null, StyleWidgetBuilder.checkbox(theme)),
lists: DefaultListBlockStyle(baseStyle, baseSpacing, const Tuple2(0, 6),
null, StyleWidgetBuilder.checkbox(theme)),
quote: DefaultTextBlockStyle(
TextStyle(color: baseStyle.color!.withOpacity(0.6)),
baseSpacing,
@ -102,9 +102,12 @@ DefaultStyles customStyles(BuildContext context) {
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(2),
)),
indent: DefaultTextBlockStyle(baseStyle, baseSpacing, const Tuple2(0, 6), null),
align: DefaultTextBlockStyle(baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
leading: DefaultTextBlockStyle(baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
indent: DefaultTextBlockStyle(
baseStyle, baseSpacing, const Tuple2(0, 6), null),
align: DefaultTextBlockStyle(
baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
leading: DefaultTextBlockStyle(
baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
sizeSmall: const TextStyle(fontSize: 10),
sizeLarge: const TextStyle(fontSize: 18),
sizeHuge: const TextStyle(fontSize: 22));

View File

@ -1,6 +1,6 @@
import 'dart:async';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';

View File

@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
import 'cell_service.dart';
abstract class GridFieldChangedNotifier {
abstract class IGridFieldChangedNotifier {
void onFieldChanged(void Function(GridFieldPB) callback);
void dispose();
}
@ -12,9 +12,10 @@ abstract class GridFieldChangedNotifier {
/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
class GridCellFieldNotifier {
/// fieldId: {objectId: callback}
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId =
{};
GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) {
GridCellFieldNotifier({required IGridFieldChangedNotifier notifier}) {
notifier.onFieldChanged(
(field) {
final map = _fieldListenerByFieldId[field.id];

View File

@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
@ -14,8 +14,8 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_listener.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'dart:convert' show utf8;
import '../../field/type_option/type_option_service.dart';

View File

@ -1,8 +1,10 @@
part of 'cell_service.dart';
typedef GridCellController = IGridCellController<String, String>;
typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellDataPB, String>;
typedef GridDateCellController = IGridCellController<DateCellDataPB, CalendarData>;
typedef GridSelectOptionCellController
= IGridCellController<SelectOptionCellDataPB, String>;
typedef GridDateCellController
= IGridCellController<DateCellDataPB, CalendarData>;
typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
class GridCellControllerBuilder {
@ -19,7 +21,8 @@ class GridCellControllerBuilder {
_cellId = cellId;
IGridCellController build() {
final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
final cellFieldNotifier = GridCellFieldNotifier(
notifier: _GridFieldChangedNotifierImpl(_fieldCache));
switch (_cellId.fieldType) {
case FieldType.Checkbox:
@ -142,8 +145,10 @@ class IGridCellController<T, D> extends Equatable {
_cellDataLoader = cellDataLoader,
_cellDataPersistence = cellDataPersistence,
_fieldNotifier = fieldNotifier,
_fieldService = FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
_cacheKey = GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
_fieldService =
FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
_cacheKey =
GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
IGridCellController<T, D> clone() {
return IGridCellController(
@ -164,7 +169,9 @@ class IGridCellController<T, D> extends Equatable {
FieldType get fieldType => cellId.field.fieldType;
VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
VoidCallback? startListening(
{required void Function(T?) onCellChanged,
VoidCallback? onCellFieldChanged}) {
if (isListening) {
Log.error("Already started. It seems like you should call clone first");
return null;
@ -226,8 +233,11 @@ class IGridCellController<T, D> extends Equatable {
/// Return the FieldTypeOptionDataPB that can be parsed into corresponding class using the [parser].
/// [PD] is the type that the parser return.
Future<Either<PD, FlowyError>> getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
return _fieldService.getFieldTypeOptionData(fieldType: fieldType).then((result) {
Future<Either<PD, FlowyError>>
getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
return _fieldService
.getFieldTypeOptionData(fieldType: fieldType)
.then((result) {
return result.fold(
(data) => parser.fromBuffer(data.typeOptionData),
(err) => right(err),
@ -236,10 +246,12 @@ class IGridCellController<T, D> extends Equatable {
}
/// Save the cell data to disk
/// You can set [dedeplicate] to true (default is false) to reduce the save operation.
/// You can set [deduplicate] to true (default is false) to reduce the save operation.
/// It's useful when you call this method when user editing the [TextField].
/// The default debounce interval is 300 milliseconds.
void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
void saveCellData(D data,
{bool deduplicate = false,
void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) {
_loadDataOperation?.cancel();
@ -288,10 +300,11 @@ class IGridCellController<T, D> extends Equatable {
}
@override
List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
List<Object> get props =>
[_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
}
class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
class _GridFieldChangedNotifierImpl extends IGridFieldChangedNotifier {
final GridFieldCache _cache;
FieldChangesetCallback? _onChangesetFn;
@ -300,7 +313,7 @@ class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
@override
void dispose() {
if (_onChangesetFn != null) {
_cache.removeListener(onChangsetListener: _onChangesetFn!);
_cache.removeListener(onChangesetListener: _onChangesetFn!);
_onChangesetFn = null;
}
}

View File

@ -1,6 +1,7 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension;
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:easy_localization/easy_localization.dart'
show StringTranslateExtension;
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@ -40,7 +41,8 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
},
didReceiveCellUpdate: (DateCellDataPB? cellData) {
final calData = calDataFromCellData(cellData);
final time = calData.foldRight("", (dateData, previous) => dateData.time);
final time =
calData.foldRight("", (dateData, previous) => dateData.time);
emit(state.copyWith(calData: calData, time: time));
},
setIncludeTime: (includeTime) async {
@ -57,15 +59,18 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
await _updateDateData(emit, time: time);
}
},
didUpdateCalData: (Option<CalendarData> data, Option<String> timeFormatError) {
emit(state.copyWith(calData: data, timeFormatError: timeFormatError));
didUpdateCalData:
(Option<CalendarData> data, Option<String> timeFormatError) {
emit(state.copyWith(
calData: data, timeFormatError: timeFormatError));
},
);
},
);
}
Future<void> _updateDateData(Emitter<DateCalState> emit, {DateTime? date, String? time}) {
Future<void> _updateDateData(Emitter<DateCalState> emit,
{DateTime? date, String? time}) {
final CalendarData newDateData = state.calData.fold(
() => CalendarData(date: date ?? DateTime.now(), time: time),
(dateData) {
@ -84,13 +89,17 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
return _saveDateData(emit, newDateData);
}
Future<void> _saveDateData(Emitter<DateCalState> emit, CalendarData newCalData) async {
Future<void> _saveDateData(
Emitter<DateCalState> emit, CalendarData newCalData) async {
if (state.calData == Some(newCalData)) {
return;
}
updateCalData(Option<CalendarData> calData, Option<String> timeFormatError) {
if (!isClosed) add(DateCalEvent.didUpdateCalData(calData, timeFormatError));
updateCalData(
Option<CalendarData> calData, Option<String> timeFormatError) {
if (!isClosed) {
add(DateCalEvent.didUpdateCalData(calData, timeFormatError));
}
}
cellContext.saveCellData(newCalData, resultCallback: (result) {
@ -172,7 +181,9 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
);
result.fold(
(l) => emit(state.copyWith(dateTypeOption: newDateTypeOption, timeHintText: _timeHintText(newDateTypeOption))),
(l) => emit(state.copyWith(
dateTypeOption: newDateTypeOption,
timeHintText: _timeHintText(newDateTypeOption))),
(err) => Log.error(err),
);
}
@ -182,14 +193,17 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
class DateCalEvent with _$DateCalEvent {
const factory DateCalEvent.initial() = _Initial;
const factory DateCalEvent.selectDay(DateTime day) = _SelectDay;
const factory DateCalEvent.setCalFormat(CalendarFormat format) = _CalendarFormat;
const factory DateCalEvent.setCalFormat(CalendarFormat format) =
_CalendarFormat;
const factory DateCalEvent.setFocusedDay(DateTime day) = _FocusedDay;
const factory DateCalEvent.setTimeFormat(TimeFormat timeFormat) = _TimeFormat;
const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
const factory DateCalEvent.setTime(String time) = _Time;
const factory DateCalEvent.didReceiveCellUpdate(DateCellDataPB? data) = _DidReceiveCellUpdate;
const factory DateCalEvent.didUpdateCalData(Option<CalendarData> data, Option<String> timeFormatError) =
const factory DateCalEvent.didReceiveCellUpdate(DateCellDataPB? data) =
_DidReceiveCellUpdate;
const factory DateCalEvent.didUpdateCalData(
Option<CalendarData> data, Option<String> timeFormatError) =
_DidUpdateCalData;
}

View File

@ -2,11 +2,12 @@ import 'dart:async';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
part 'select_option_cell_bloc.freezed.dart';
class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
class SelectOptionCellBloc
extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
final GridSelectOptionCellController cellContext;
void Function()? _onCellChangedFn;
@ -66,7 +67,8 @@ class SelectOptionCellState with _$SelectOptionCellState {
required List<SelectOptionPB> selectedOptions,
}) = _SelectOptionCellState;
factory SelectOptionCellState.initial(GridSelectOptionCellController context) {
factory SelectOptionCellState.initial(
GridSelectOptionCellController context) {
final data = context.getCellData();
return SelectOptionCellState(

View File

@ -4,20 +4,22 @@ import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'select_option_service.dart';
import 'package:collection/collection.dart';
part 'select_option_editor_bloc.freezed.dart';
class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
class SelectOptionCellEditorBloc
extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
final SelectOptionService _selectOptionService;
final GridSelectOptionCellController cellController;
Timer? _delayOperation;
SelectOptionCellEditorBloc({
required this.cellController,
}) : _selectOptionService = SelectOptionService(cellId: cellController.cellId),
}) : _selectOptionService =
SelectOptionService(cellId: cellController.cellId),
super(SelectOptionEditorState.initial(cellController)) {
on<SelectOptionEditorEvent>(
(event, emit) async {
@ -87,7 +89,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
}
void _onSelectOption(String optionId) {
final hasSelected = state.selectedOptions.firstWhereOrNull((option) => option.id == optionId);
final hasSelected = state.selectedOptions
.firstWhereOrNull((option) => option.id == optionId);
if (hasSelected != null) {
_selectOptionService.unSelect(optionId: optionId);
} else {
@ -96,7 +99,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
}
void _filterOption(String optionName, Emitter<SelectOptionEditorState> emit) {
final _MakeOptionResult result = _makeOptions(Some(optionName), state.allOptions);
final _MakeOptionResult result =
_makeOptions(Some(optionName), state.allOptions);
emit(state.copyWith(
filter: Some(optionName),
options: result.options,
@ -112,7 +116,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
return;
}
return result.fold(
(data) => add(SelectOptionEditorEvent.didReceiveOptions(data.options, data.selectOptions)),
(data) => add(SelectOptionEditorEvent.didReceiveOptions(
data.options, data.selectOptions)),
(err) {
Log.error(err);
return null;
@ -122,7 +127,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
});
}
_MakeOptionResult _makeOptions(Option<String> filter, List<SelectOptionPB> allOptions) {
_MakeOptionResult _makeOptions(
Option<String> filter, List<SelectOptionPB> allOptions) {
final List<SelectOptionPB> options = List.from(allOptions);
Option<String> createOption = filter;
@ -165,12 +171,18 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
class SelectOptionEditorEvent with _$SelectOptionEditorEvent {
const factory SelectOptionEditorEvent.initial() = _Initial;
const factory SelectOptionEditorEvent.didReceiveOptions(
List<SelectOptionPB> options, List<SelectOptionPB> selectedOptions) = _DidReceiveOptions;
const factory SelectOptionEditorEvent.newOption(String optionName) = _NewOption;
const factory SelectOptionEditorEvent.selectOption(String optionId) = _SelectOption;
const factory SelectOptionEditorEvent.updateOption(SelectOptionPB option) = _UpdateOption;
const factory SelectOptionEditorEvent.deleteOption(SelectOptionPB option) = _DeleteOption;
const factory SelectOptionEditorEvent.filterOption(String optionName) = _SelectOptionFilter;
List<SelectOptionPB> options, List<SelectOptionPB> selectedOptions) =
_DidReceiveOptions;
const factory SelectOptionEditorEvent.newOption(String optionName) =
_NewOption;
const factory SelectOptionEditorEvent.selectOption(String optionId) =
_SelectOption;
const factory SelectOptionEditorEvent.updateOption(SelectOptionPB option) =
_UpdateOption;
const factory SelectOptionEditorEvent.deleteOption(SelectOptionPB option) =
_DeleteOption;
const factory SelectOptionEditorEvent.filterOption(String optionName) =
_SelectOptionFilter;
}
@freezed
@ -183,7 +195,8 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
required Option<String> filter,
}) = _SelectOptionEditorState;
factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
factory SelectOptionEditorState.initial(
GridSelectOptionCellController context) {
final data = context.getCellData(loadIfNotExist: false);
return SelectOptionEditorState(
options: data?.options ?? [],

View File

@ -2,7 +2,7 @@ import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'cell_service/cell_service.dart';
@ -15,7 +15,9 @@ class SelectOptionService {
String get rowId => cellId.rowId;
Future<Either<Unit, FlowyError>> create({required String name}) {
return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then(
return TypeOptionService(gridId: gridId, fieldId: fieldId)
.newOption(name: name)
.then(
(result) {
return result.fold(
(option) {

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_listener.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -15,7 +15,8 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
FieldCellBloc({
required GridFieldCellContext cellContext,
}) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
_fieldService = FieldService(gridId: cellContext.gridId, fieldId: cellContext.field.id),
_fieldService = FieldService(
gridId: cellContext.gridId, fieldId: cellContext.field.id),
super(FieldCellState.initial(cellContext)) {
on<FieldCellEvent>(
(event, emit) async {
@ -62,8 +63,10 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
@freezed
class FieldCellEvent with _$FieldCellEvent {
const factory FieldCellEvent.initial() = _InitialCell;
const factory FieldCellEvent.didReceiveFieldUpdate(GridFieldPB field) = _DidReceiveFieldUpdate;
const factory FieldCellEvent.startUpdateWidth(double offset) = _StartUpdateWidth;
const factory FieldCellEvent.didReceiveFieldUpdate(GridFieldPB field) =
_DidReceiveFieldUpdate;
const factory FieldCellEvent.startUpdateWidth(double offset) =
_StartUpdateWidth;
const factory FieldCellEvent.endUpdateWidth() = _EndUpdateWidth;
}
@ -75,7 +78,8 @@ class FieldCellState with _$FieldCellState {
required double width,
}) = _FieldCellState;
factory FieldCellState.initial(GridFieldCellContext cellContext) => FieldCellState(
factory FieldCellState.initial(GridFieldCellContext cellContext) =>
FieldCellState(
gridId: cellContext.gridId,
field: cellContext.field,
width: cellContext.field.width.toDouble(),

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -16,20 +16,24 @@ class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
}
}
class DateTypeOptionBloc extends Bloc<DateTypeOptionEvent, DateTypeOptionState> {
class DateTypeOptionBloc
extends Bloc<DateTypeOptionEvent, DateTypeOptionState> {
DateTypeOptionBloc({required DateTypeOptionContext typeOptionContext})
: super(DateTypeOptionState.initial(typeOptionContext.typeOption)) {
on<DateTypeOptionEvent>(
(event, emit) async {
event.map(
didSelectDateFormat: (_DidSelectDateFormat value) {
emit(state.copyWith(typeOption: _updateTypeOption(dateFormat: value.format)));
emit(state.copyWith(
typeOption: _updateTypeOption(dateFormat: value.format)));
},
didSelectTimeFormat: (_DidSelectTimeFormat value) {
emit(state.copyWith(typeOption: _updateTypeOption(timeFormat: value.format)));
emit(state.copyWith(
typeOption: _updateTypeOption(timeFormat: value.format)));
},
includeTime: (_IncludeTime value) {
emit(state.copyWith(typeOption: _updateTypeOption(includeTime: value.includeTime)));
emit(state.copyWith(
typeOption: _updateTypeOption(includeTime: value.includeTime)));
},
);
},
@ -65,9 +69,12 @@ class DateTypeOptionBloc extends Bloc<DateTypeOptionEvent, DateTypeOptionState>
@freezed
class DateTypeOptionEvent with _$DateTypeOptionEvent {
const factory DateTypeOptionEvent.didSelectDateFormat(DateFormat format) = _DidSelectDateFormat;
const factory DateTypeOptionEvent.didSelectTimeFormat(TimeFormat format) = _DidSelectTimeFormat;
const factory DateTypeOptionEvent.includeTime(bool includeTime) = _IncludeTime;
const factory DateTypeOptionEvent.didSelectDateFormat(DateFormat format) =
_DidSelectDateFormat;
const factory DateTypeOptionEvent.didSelectTimeFormat(TimeFormat format) =
_DidSelectTimeFormat;
const factory DateTypeOptionEvent.includeTime(bool includeTime) =
_IncludeTime;
}
@freezed
@ -76,5 +83,6 @@ class DateTypeOptionState with _$DateTypeOptionState {
required DateTypeOption typeOption,
}) = _DateTypeOptionState;
factory DateTypeOptionState.initial(DateTypeOption typeOption) => DateTypeOptionState(typeOption: typeOption);
factory DateTypeOptionState.initial(DateTypeOption typeOption) =>
DateTypeOptionState(typeOption: typeOption);
}

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
@ -7,7 +7,8 @@ import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTypeOption>
class MultiSelectTypeOptionContext
extends TypeOptionWidgetContext<MultiSelectTypeOption>
with SelectOptionTypeOptionAction {
final TypeOptionService service;
@ -25,7 +26,8 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
final index =
typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options.removeAt(index);
}
@ -61,7 +63,8 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
final index =
typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options[index] = option;
}
@ -71,7 +74,8 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
}
}
class MultiSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<MultiSelectTypeOption> {
class MultiSelectTypeOptionWidgetDataParser
extends TypeOptionDataParser<MultiSelectTypeOption> {
@override
MultiSelectTypeOption fromBuffer(List<int> buffer) {
return MultiSelectTypeOption.fromBuffer(buffer);

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -10,14 +10,16 @@ part 'number_bloc.freezed.dart';
typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
class NumberTypeOptionWidgetDataParser extends TypeOptionDataParser<NumberTypeOption> {
class NumberTypeOptionWidgetDataParser
extends TypeOptionDataParser<NumberTypeOption> {
@override
NumberTypeOption fromBuffer(List<int> buffer) {
return NumberTypeOption.fromBuffer(buffer);
}
}
class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionState> {
class NumberTypeOptionBloc
extends Bloc<NumberTypeOptionEvent, NumberTypeOptionState> {
NumberTypeOptionBloc({required NumberTypeOptionContext typeOptionContext})
: super(NumberTypeOptionState.initial(typeOptionContext.typeOption)) {
on<NumberTypeOptionEvent>(
@ -46,7 +48,8 @@ class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionS
@freezed
class NumberTypeOptionEvent with _$NumberTypeOptionEvent {
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormat format) = _DidSelectFormat;
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormat format) =
_DidSelectFormat;
}
@freezed
@ -55,7 +58,8 @@ class NumberTypeOptionState with _$NumberTypeOptionState {
required NumberTypeOption typeOption,
}) = _NumberTypeOptionState;
factory NumberTypeOptionState.initial(NumberTypeOption typeOption) => NumberTypeOptionState(
factory NumberTypeOptionState.initial(NumberTypeOption typeOption) =>
NumberTypeOptionState(
typeOption: typeOption,
);
}

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart';
@ -7,7 +7,8 @@ import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelectTypeOptionPB>
class SingleSelectTypeOptionContext
extends TypeOptionWidgetContext<SingleSelectTypeOptionPB>
with SelectOptionTypeOptionAction {
final TypeOptionService service;
@ -25,7 +26,8 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
final index =
typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options.removeAt(index);
}
@ -61,7 +63,8 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
final index =
typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options[index] = option;
}
@ -71,7 +74,8 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
}
}
class SingleSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<SingleSelectTypeOptionPB> {
class SingleSelectTypeOptionWidgetDataParser
extends TypeOptionDataParser<SingleSelectTypeOptionPB> {
@override
SingleSelectTypeOptionPB fromBuffer(List<int> buffer) {
return SingleSelectTypeOptionPB.fromBuffer(buffer);

View File

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@ -92,7 +92,9 @@ class TypeOptionContext2<T> {
return Future(() => left(_data!));
}
return _fieldService.getFieldTypeOptionData(fieldType: field.fieldType).then((result) {
return _fieldService
.getFieldTypeOptionData(fieldType: field.fieldType)
.then((result) {
return result.fold(
(data) {
_data = dataBuilder.fromBuffer(data.typeOptionData);

View File

@ -0,0 +1,161 @@
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'block/block_cache.dart';
import 'grid_data_controller.dart';
import 'row/row_service.dart';
import 'dart:collection';
part 'grid_bloc.freezed.dart';
class GridBloc extends Bloc<GridEvent, GridState> {
final GridDataController dataController;
GridBloc({required ViewPB view})
: dataController = GridDataController(view: view),
super(GridState.initial(view.id)) {
on<GridEvent>(
(event, emit) async {
await event.when(
initial: () async {
_startListening();
await _loadGrid(emit);
},
createRow: () {
dataController.createRow();
},
didReceiveGridUpdate: (grid) {
emit(state.copyWith(grid: Some(grid)));
},
didReceiveFieldUpdate: (fields) {
emit(state.copyWith(
fields: GridFieldEquatable(fields),
));
},
didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith(
rowInfos: newRowInfos,
reason: reason,
));
},
);
},
);
}
@override
Future<void> close() async {
await dataController.dispose();
return super.close();
}
GridRowCache? getRowCache(String blockId, String rowId) {
final GridBlockCache? blockCache = dataController.blocks[blockId];
return blockCache?.rowCache;
}
void _startListening() {
dataController.addListener(
onGridChanged: (grid) {
if (!isClosed) {
add(GridEvent.didReceiveGridUpdate(grid));
}
},
onRowsChanged: (rowInfos, reason) {
if (!isClosed) {
add(GridEvent.didReceiveRowUpdate(rowInfos, reason));
}
},
onFieldsChanged: (fields) {
if (!isClosed) {
add(GridEvent.didReceiveFieldUpdate(fields));
}
},
);
}
Future<void> _loadGrid(Emitter<GridState> emit) async {
final result = await dataController.loadData();
result.fold(
(grid) => emit(
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
),
(err) => emit(
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
),
);
}
}
@freezed
class GridEvent with _$GridEvent {
const factory GridEvent.initial() = InitialGrid;
const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.didReceiveRowUpdate(
List<GridRowInfo> rows,
GridRowChangeReason listState,
) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(
UnmodifiableListView<GridFieldPB> fields,
) = _DidReceiveFieldUpdate;
const factory GridEvent.didReceiveGridUpdate(
GridPB grid,
) = _DidReceiveGridUpdate;
}
@freezed
class GridState with _$GridState {
const factory GridState({
required String gridId,
required Option<GridPB> grid,
required GridFieldEquatable fields,
required List<GridRowInfo> rowInfos,
required GridLoadingState loadingState,
required GridRowChangeReason reason,
}) = _GridState;
factory GridState.initial(String gridId) => GridState(
fields: GridFieldEquatable(UnmodifiableListView([])),
rowInfos: [],
grid: none(),
gridId: gridId,
loadingState: const _Loading(),
reason: const InitialListState(),
);
}
@freezed
class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish;
}
class GridFieldEquatable extends Equatable {
final UnmodifiableListView<GridFieldPB> _fields;
const GridFieldEquatable(
UnmodifiableListView<GridFieldPB> fields,
) : _fields = fields;
@override
List<Object?> get props {
if (_fields.isEmpty) {
return [];
}
return [
_fields.length,
_fields
.map((field) => field.width)
.reduce((value, element) => value + element),
];
}
UnmodifiableListView<GridFieldPB> get value => UnmodifiableListView(_fields);
}

View File

@ -0,0 +1,128 @@
import 'dart:collection';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'block/block_cache.dart';
import 'prelude.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<GridFieldPB>);
typedef OnGridChanged = void Function(GridPB);
typedef OnRowsChanged = void Function(
List<GridRowInfo> rowInfos,
GridRowChangeReason,
);
typedef ListenONRowChangedCondition = bool Function();
class GridDataController {
final String gridId;
final GridService _gridFFIService;
final GridFieldCache fieldCache;
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
UnmodifiableMapView<String, GridBlockCache> get blocks =>
UnmodifiableMapView(_blocks);
OnRowsChanged? _onRowChanged;
OnFieldsChanged? _onFieldsChanged;
OnGridChanged? _onGridChanged;
List<GridRowInfo> get rowInfos {
final List<GridRowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
GridDataController({required ViewPB view})
: gridId = view.id,
_blocks = LinkedHashMap.identity(),
_gridFFIService = GridService(gridId: view.id),
fieldCache = GridFieldCache(gridId: view.id);
void addListener({
required OnGridChanged onGridChanged,
required OnRowsChanged onRowsChanged,
required OnFieldsChanged onFieldsChanged,
}) {
_onGridChanged = onGridChanged;
_onRowChanged = onRowsChanged;
_onFieldsChanged = onFieldsChanged;
fieldCache.addListener(onFields: (fields) {
_onFieldsChanged?.call(UnmodifiableListView(fields));
});
}
Future<Either<Unit, FlowyError>> loadData() async {
final result = await _gridFFIService.loadGrid();
return Future(
() => result.fold(
(grid) async {
_initialBlocks(grid.blocks);
_onGridChanged?.call(grid);
return await _loadFields(grid);
},
(err) => right(err),
),
);
}
void createRow() {
_gridFFIService.createRow();
}
Future<void> dispose() async {
await _gridFFIService.closeGrid();
await fieldCache.dispose();
for (final blockCache in _blocks.values) {
blockCache.dispose();
}
}
void _initialBlocks(List<GridBlockPB> blocks) {
for (final block in blocks) {
if (_blocks[block.id] != null) {
Log.warn("Initial duplicate block's cache: ${block.id}");
return;
}
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldCache: fieldCache,
);
cache.addListener(
onChangeReason: (reason) {
_onRowChanged?.call(rowInfos, reason);
},
);
_blocks[block.id] = cache;
}
}
Future<Either<Unit, FlowyError>> _loadFields(GridPB grid) async {
final result = await _gridFFIService.getFields(fieldIds: grid.fields);
return Future(
() => result.fold(
(fields) {
fieldCache.fields = fields.items;
_onFieldsChanged?.call(UnmodifiableListView(fieldCache.fields));
return left(unit);
},
(err) => right(err),
),
);
}
}

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -33,7 +33,8 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
);
}
Future<void> _moveField(_MoveField value, Emitter<GridHeaderState> emit) async {
Future<void> _moveField(
_MoveField value, Emitter<GridHeaderState> emit) async {
final fields = List<GridFieldPB>.from(state.fields);
fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
emit(state.copyWith(fields: fields));
@ -62,13 +63,16 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
@freezed
class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.initial() = _InitialHeader;
const factory GridHeaderEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField(GridFieldPB field, int fromIndex, int toIndex) = _MoveField;
const factory GridHeaderEvent.didReceiveFieldUpdate(
List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField(
GridFieldPB field, int fromIndex, int toIndex) = _MoveField;
}
@freezed
class GridHeaderState with _$GridHeaderState {
const factory GridHeaderState({required List<GridFieldPB> fields}) = _GridHeaderState;
const factory GridHeaderState({required List<GridFieldPB> fields}) =
_GridHeaderState;
factory GridHeaderState.initial(List<GridFieldPB> fields) {
// final List<GridFieldPB> newFields = List.from(fields);

View File

@ -1,6 +1,6 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
@ -26,13 +26,15 @@ class GridService {
return GridEventGetGrid(payload).send();
}
Future<Either<GridRowPB, FlowyError>> createRow({Option<String>? startRowId}) {
Future<Either<GridRowPB, FlowyError>> createRow(
{Option<String>? startRowId}) {
CreateRowPayloadPB payload = CreateRowPayloadPB.create()..gridId = gridId;
startRowId?.fold(() => null, (id) => payload.startRowId = id);
return GridEventCreateRow(payload).send();
}
Future<Either<RepeatedGridFieldPB, FlowyError>> getFields({required List<GridFieldIdPB> fieldIds}) {
Future<Either<RepeatedGridFieldPB, FlowyError>> getFields(
{required List<GridFieldIdPB> fieldIds}) {
final payload = QueryFieldPayloadPB.create()
..gridId = gridId
..fieldIds = RepeatedGridFieldIdPB(items: fieldIds);
@ -64,9 +66,11 @@ class GridFieldCache {
final GridFieldsListener _fieldListener;
FieldsNotifier? _fieldNotifier = FieldsNotifier();
final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
final Map<FieldChangesetCallback, FieldChangesetCallback> _changesetCallbackMap = {};
final Map<FieldChangesetCallback, FieldChangesetCallback>
_changesetCallbackMap = {};
GridFieldCache({required this.gridId}) : _fieldListener = GridFieldsListener(gridId: gridId) {
GridFieldCache({required this.gridId})
: _fieldListener = GridFieldsListener(gridId: gridId) {
_fieldListener.start(onFieldsChanged: (result) {
result.fold(
(changeset) {
@ -88,7 +92,8 @@ class GridFieldCache {
_fieldNotifier = null;
}
UnmodifiableListView<GridFieldPB> get unmodifiableFields => UnmodifiableListView(_fieldNotifier?.fields ?? []);
UnmodifiableListView<GridFieldPB> get unmodifiableFields =>
UnmodifiableListView(_fieldNotifier?.fields ?? []);
List<GridFieldPB> get fields => [..._fieldNotifier?.fields ?? []];
@ -127,7 +132,7 @@ class GridFieldCache {
void removeListener({
FieldsCallback? onFieldsListener,
FieldChangesetCallback? onChangsetListener,
FieldChangesetCallback? onChangesetListener,
}) {
if (onFieldsListener != null) {
final fn = _fieldsCallbackMap.remove(onFieldsListener);
@ -136,8 +141,8 @@ class GridFieldCache {
}
}
if (onChangsetListener != null) {
_changesetCallbackMap.remove(onChangsetListener);
if (onChangesetListener != null) {
_changesetCallbackMap.remove(onChangesetListener);
}
}
@ -175,7 +180,8 @@ class GridFieldCache {
}
final List<GridFieldPB> newFields = fields;
for (final updatedField in updatedFields) {
final index = newFields.indexWhere((field) => field.id == updatedField.id);
final index =
newFields.indexWhere((field) => field.id == updatedField.id);
if (index != -1) {
newFields.removeAt(index);
newFields.insert(index, updatedField);
@ -219,7 +225,7 @@ class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
}
if (_onChangesetFn != null) {
_cache.removeListener(onChangsetListener: _onChangesetFn!);
_cache.removeListener(onChangesetListener: _onChangesetFn!);
_onChangesetFn = null;
}
}

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -8,7 +8,8 @@ import 'package:dartz/dartz.dart';
part 'row_action_sheet_bloc.freezed.dart';
class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState> {
class RowActionSheetBloc
extends Bloc<RowActionSheetEvent, RowActionSheetState> {
final RowService _rowService;
RowActionSheetBloc({required GridRowInfo rowData})
@ -56,7 +57,8 @@ class RowActionSheetState with _$RowActionSheetState {
required GridRowInfo rowData,
}) = _RowActionSheetState;
factory RowActionSheetState.initial(GridRowInfo rowData) => RowActionSheetState(
factory RowActionSheetState.initial(GridRowInfo rowData) =>
RowActionSheetState(
rowData: rowData,
);
}

View File

@ -1,29 +1,29 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'row_data_controller.dart';
import 'row_service.dart';
part 'row_bloc.freezed.dart';
class RowBloc extends Bloc<RowEvent, RowState> {
final RowService _rowService;
final GridRowCache _rowCache;
void Function()? _rowListenFn;
final GridRowDataController _dataController;
RowBloc({
required GridRowInfo rowInfo,
required GridRowCache rowCache,
required GridRowDataController dataController,
}) : _rowService = RowService(
gridId: rowInfo.gridId,
blockId: rowInfo.blockId,
rowId: rowInfo.id,
),
_rowCache = rowCache,
super(RowState.initial(rowInfo, rowCache.loadGridCells(rowInfo.id))) {
_dataController = dataController,
super(RowState.initial(rowInfo, dataController.loadData())) {
on<RowEvent>(
(event, emit) async {
await event.map(
@ -33,8 +33,10 @@ class RowBloc extends Bloc<RowEvent, RowState> {
createRow: (_CreateRow value) {
_rowService.createRow();
},
didReceiveCellDatas: (_DidReceiveCellDatas value) async {
final fields = value.gridCellMap.values.map((e) => GridCellEquatable(e.field)).toList();
didReceiveCells: (_DidReceiveCells value) async {
final fields = value.gridCellMap.values
.map((e) => GridCellEquatable(e.field))
.toList();
final snapshots = UnmodifiableListView(fields);
emit(state.copyWith(
gridCellMap: value.gridCellMap,
@ -49,18 +51,17 @@ class RowBloc extends Bloc<RowEvent, RowState> {
@override
Future<void> close() async {
if (_rowListenFn != null) {
_rowCache.removeRowListener(_rowListenFn!);
}
_dataController.dispose();
return super.close();
}
Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener(
rowId: state.rowInfo.id,
onCellUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
listenWhen: () => !isClosed,
_dataController.addListener(
onRowChanged: (cells, reason) {
if (!isClosed) {
add(RowEvent.didReceiveCells(cells, reason));
}
},
);
}
}
@ -69,8 +70,8 @@ class RowBloc extends Bloc<RowEvent, RowState> {
class RowEvent with _$RowEvent {
const factory RowEvent.initial() = _InitialRow;
const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.didReceiveCellDatas(GridCellMap gridCellMap, GridRowChangeReason reason) =
_DidReceiveCellDatas;
const factory RowEvent.didReceiveCells(
GridCellMap gridCellMap, GridRowChangeReason reason) = _DidReceiveCells;
}
@freezed
@ -82,10 +83,12 @@ class RowState with _$RowState {
GridRowChangeReason? changeReason,
}) = _RowState;
factory RowState.initial(GridRowInfo rowInfo, GridCellMap cellDataMap) => RowState(
factory RowState.initial(GridRowInfo rowInfo, GridCellMap cellDataMap) =>
RowState(
rowInfo: rowInfo,
gridCellMap: cellDataMap,
snapshots: UnmodifiableListView(cellDataMap.values.map((e) => GridCellEquatable(e.field)).toList()),
snapshots: UnmodifiableListView(
cellDataMap.values.map((e) => GridCellEquatable(e.field)).toList()),
);
}

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import '../cell/cell_service/cell_service.dart';
import '../grid_service.dart';
import 'row_service.dart';
typedef OnRowChanged = void Function(GridCellMap, GridRowChangeReason);
class GridRowDataController {
final String rowId;
VoidCallback? _onRowChangedListener;
final GridFieldCache _fieldCache;
final GridRowCache _rowCache;
GridFieldCache get fieldCache => _fieldCache;
GridRowCache get rowCache => _rowCache;
GridRowDataController({
required this.rowId,
required GridFieldCache fieldCache,
required GridRowCache rowCache,
}) : _fieldCache = fieldCache,
_rowCache = rowCache;
GridCellMap loadData() {
return _rowCache.loadGridCells(rowId);
}
void addListener({OnRowChanged? onRowChanged}) {
_onRowChangedListener = _rowCache.addListener(
rowId: rowId,
onCellUpdated: onRowChanged,
);
}
void dispose() {
if (_onRowChangedListener != null) {
_rowCache.removeRowListener(_onRowChangedListener!);
}
}
}

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -42,7 +42,8 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener(
rowId: rowInfo.id,
onCellUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
onCellUpdated: (cellDatas, reason) =>
add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
listenWhen: () => !isClosed,
);
}
@ -58,7 +59,8 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
@freezed
class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial;
const factory RowDetailEvent.didReceiveCellDatas(List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
const factory RowDetailEvent.didReceiveCellDatas(
List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
}
@freezed

View File

@ -1,5 +1,5 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
@ -53,9 +53,12 @@ class GridRowCache {
_rowChangeReasonNotifier = _GridRowChangesetNotifier(),
_fieldNotifier = notifier {
//
notifier.onFieldsChanged(() => _rowChangeReasonNotifier.receive(const GridRowChangeReason.fieldDidChange()));
notifier.onFieldsChanged(() => _rowChangeReasonNotifier
.receive(const GridRowChangeReason.fieldDidChange()));
notifier.onFieldChanged((field) => _cellCache.remove(field.id));
_rowInfos = block.rows.map((rowInfo) => buildGridRow(rowInfo.id, rowInfo.height.toDouble())).toList();
_rowInfos = block.rows
.map((rowInfo) => buildGridRow(rowInfo.id, rowInfo.height.toDouble()))
.toList();
}
Future<void> dispose() async {
@ -81,7 +84,9 @@ class GridRowCache {
final List<GridRowInfo> newRows = [];
final DeletedIndexs deletedIndex = [];
final Map<String, String> deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId};
final Map<String, String> deletedRowByRowId = {
for (var rowId in deletedRows) rowId: rowId
};
_rowInfos.asMap().forEach((index, row) {
if (deletedRowByRowId[row.id] == null) {
@ -107,7 +112,8 @@ class GridRowCache {
rowId: insertRow.rowId,
);
insertIndexs.add(insertIndex);
_rowInfos.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
_rowInfos.insert(insertRow.index,
(buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
}
_rowChangeReasonNotifier.receive(GridRowChangeReason.insert(insertIndexs));
@ -126,7 +132,8 @@ class GridRowCache {
_rowByRowId[rowId] = updatedRow.row;
_rowInfos.removeAt(index);
_rowInfos.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
_rowInfos.insert(
index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
}
}
@ -151,7 +158,7 @@ class GridRowCache {
void Function(GridCellMap, GridRowChangeReason)? onCellUpdated,
bool Function()? listenWhen,
}) {
listenrHandler() async {
listenerHandler() async {
if (listenWhen != null && listenWhen() == false) {
return;
}
@ -174,8 +181,8 @@ class GridRowCache {
);
}
_rowChangeReasonNotifier.addListener(listenrHandler);
return listenrHandler;
_rowChangeReasonNotifier.addListener(listenerHandler);
return listenerHandler;
}
void removeRowListener(VoidCallback callback) {
@ -225,7 +232,8 @@ class GridRowCache {
updatedRow.freeze();
_rowByRowId[updatedRow.id] = updatedRow;
final index = _rowInfos.indexWhere((gridRow) => gridRow.id == updatedRow.id);
final index =
_rowInfos.indexWhere((gridRow) => gridRow.id == updatedRow.id);
if (index != -1) {
// update the corresponding row in _rows if they are not the same
if (_rowInfos[index].rawRow != updatedRow) {
@ -237,7 +245,8 @@ class GridRowCache {
updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id);
//
_rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
_rowChangeReasonNotifier
.receive(GridRowChangeReason.update(updatedIndexs));
}
}
}
@ -275,7 +284,8 @@ class RowService {
final String blockId;
final String rowId;
RowService({required this.gridId, required this.blockId, required this.rowId});
RowService(
{required this.gridId, required this.blockId, required this.rowId});
Future<Either<GridRowPB, FlowyError>> createRow() {
CreateRowPayloadPB payload = CreateRowPayloadPB.create()
@ -285,7 +295,8 @@ class RowService {
return GridEventCreateRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveRow(String rowId, int fromIndex, int toIndex) {
Future<Either<Unit, FlowyError>> moveRow(
String rowId, int fromIndex, int toIndex) {
final payload = MoveItemPayloadPB.create()
..gridId = gridId
..itemId = rowId

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -22,8 +22,10 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
_startListening();
},
setFieldVisibility: (_SetFieldVisibility value) async {
final fieldService = FieldService(gridId: gridId, fieldId: value.fieldId);
final result = await fieldService.updateField(visibility: value.visibility);
final fieldService =
FieldService(gridId: gridId, fieldId: value.fieldId);
final result =
await fieldService.updateField(visibility: value.visibility);
result.fold(
(l) => null,
(err) => Log.error(err),
@ -50,7 +52,8 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
}
void _startListening() {
_onFieldsFn = (fields) => add(GridPropertyEvent.didReceiveFieldUpdate(fields));
_onFieldsFn =
(fields) => add(GridPropertyEvent.didReceiveFieldUpdate(fields));
_fieldCache.addListener(
onFields: _onFieldsFn,
listenWhen: () => !isClosed,
@ -61,9 +64,12 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
@freezed
class GridPropertyEvent with _$GridPropertyEvent {
const factory GridPropertyEvent.initial() = _Initial;
const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility;
const factory GridPropertyEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) = _MoveField;
const factory GridPropertyEvent.setFieldVisibility(
String fieldId, bool visibility) = _SetFieldVisibility;
const factory GridPropertyEvent.didReceiveFieldUpdate(
List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) =
_MoveField;
}
@freezed
@ -73,7 +79,8 @@ class GridPropertyState with _$GridPropertyState {
required List<GridFieldPB> fields,
}) = _GridPropertyState;
factory GridPropertyState.initial(String gridId, List<GridFieldPB> fields) => GridPropertyState(
factory GridPropertyState.initial(String gridId, List<GridFieldPB> fields) =>
GridPropertyState(
gridId: gridId,
fields: fields,
);

View File

@ -1,12 +1,12 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugin/plugin.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'src/grid_page.dart';
import 'presentation/grid_page.dart';
class GridPluginBuilder implements PluginBuilder {
@override

View File

@ -2,19 +2,20 @@ import 'package:flutter/material.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
class GridScrollController {
final LinkedScrollControllerGroup _scrollGroupContorller;
final LinkedScrollControllerGroup _scrollGroupController;
final ScrollController verticalController;
final ScrollController horizontalController;
final List<ScrollController> _linkHorizontalControllers = [];
GridScrollController({required LinkedScrollControllerGroup scrollGroupContorller})
: _scrollGroupContorller = scrollGroupContorller,
GridScrollController(
{required LinkedScrollControllerGroup scrollGroupController})
: _scrollGroupController = scrollGroupController,
verticalController = ScrollController(),
horizontalController = scrollGroupContorller.addAndGet();
horizontalController = scrollGroupController.addAndGet();
ScrollController linkHorizontalController() {
final controller = _scrollGroupContorller.addAndGet();
final controller = _scrollGroupController.addAndGet();
_linkHorizontalControllers.add(controller);
return controller;
}

View File

@ -1,6 +1,7 @@
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_bloc.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
@ -35,13 +36,15 @@ class _GridPageState extends State<GridPage> {
return MultiBlocProvider(
providers: [
BlocProvider<GridBloc>(
create: (context) => getIt<GridBloc>(param1: widget.view)..add(const GridEvent.initial()),
create: (context) => getIt<GridBloc>(param1: widget.view)
..add(const GridEvent.initial()),
),
],
child: BlocBuilder<GridBloc, GridState>(
builder: (context, state) {
return state.loadingState.map(
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
loading: (_) =>
const Center(child: CircularProgressIndicator.adaptive()),
finish: (result) => result.successOrFail.fold(
(_) => const GridShortcuts(child: FlowyGrid()),
(err) => FlowyErrorPage(err.toString()),
@ -76,7 +79,8 @@ class FlowyGrid extends StatefulWidget {
}
class _FlowyGridState extends State<FlowyGrid> {
final _scrollController = GridScrollController(scrollGroupContorller: LinkedScrollControllerGroup());
final _scrollController = GridScrollController(
scrollGroupController: LinkedScrollControllerGroup());
late ScrollController headerScrollController;
@override
@ -150,7 +154,7 @@ class _FlowyGridState extends State<FlowyGrid> {
}
Widget _gridHeader(BuildContext context, String gridId) {
final fieldCache = context.read<GridBloc>().fieldCache;
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
return GridHeaderSliverAdaptor(
gridId: gridId,
fieldCache: fieldCache,
@ -166,7 +170,7 @@ class _GridToolbarAdaptor extends StatelessWidget {
Widget build(BuildContext context) {
return BlocSelector<GridBloc, GridState, GridToolbarContext>(
selector: (state) {
final fieldCache = context.read<GridBloc>().fieldCache;
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
return GridToolbarContext(
gridId: state.gridId,
fieldCache: fieldCache,
@ -204,7 +208,8 @@ class _GridRowsState extends State<_GridRows> {
for (final item in value.items) {
_key.currentState?.removeItem(
item.index,
(context, animation) => _renderRow(context, item.row, animation),
(context, animation) =>
_renderRow(context, item.row, animation),
);
}
},
@ -215,8 +220,10 @@ class _GridRowsState extends State<_GridRows> {
return SliverAnimatedList(
key: _key,
initialItemCount: context.read<GridBloc>().state.rowInfos.length,
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
final GridRowInfo rowInfo = context.read<GridBloc>().state.rowInfos[index];
itemBuilder:
(BuildContext context, int index, Animation<double> animation) {
final GridRowInfo rowInfo =
context.read<GridBloc>().state.rowInfos[index];
return _renderRow(context, rowInfo, animation);
},
);
@ -229,15 +236,22 @@ class _GridRowsState extends State<_GridRows> {
GridRowInfo rowInfo,
Animation<double> animation,
) {
final rowCache = context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id);
final fieldCache = context.read<GridBloc>().fieldCache;
final rowCache =
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id);
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
if (rowCache != null) {
final dataController = GridRowDataController(
rowId: rowInfo.id,
fieldCache: fieldCache,
rowCache: rowCache,
);
return SizeTransition(
sizeFactor: animation,
child: GridRowWidget(
rowData: rowInfo,
rowCache: rowCache,
fieldCache: fieldCache,
dataController: dataController,
key: ValueKey(rowInfo.id),
),
);
@ -266,10 +280,10 @@ class _GridFooter extends StatelessWidget {
SizedBox(width: GridSize.leadingHeaderPadding),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
children: [
const SizedBox(width: 120, child: GridAddRowButton()),
const SizedBox(height: 30),
_rowCountTextWidget(theme: theme,count: rowCount)
_rowCountTextWidget(theme: theme, count: rowCount)
],
),
],
@ -280,16 +294,18 @@ class _GridFooter extends StatelessWidget {
);
}
Widget _rowCountTextWidget({required AppTheme theme, required int count}){
Widget _rowCountTextWidget({required AppTheme theme, required int count}) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
FlowyText.regular('Count : ',
fontSize: 13,
color: theme.shader3,
FlowyText.regular(
'Count : ',
fontSize: 13,
color: theme.shader3,
),
FlowyText.regular(count.toString(),
fontSize: 13,
FlowyText.regular(
count.toString(),
fontSize: 13,
),
],
);

View File

@ -1,11 +1,12 @@
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flutter/widgets.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flowy_infra/size.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
class GridCellAccessoryBuildContext {
final BuildContext anchorContext;
@ -39,7 +40,13 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
return const SizedBox();
} else {
final theme = context.watch<AppTheme>();
return svgWidget("grid/expander", color: theme.main1);
return Tooltip(
message: LocaleKeys.tooltip_openAsPage.tr(),
child: svgWidget(
"grid/expander",
color: theme.main1,
),
);
}
}
@ -50,7 +57,8 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
bool enable() => !isCellEditing;
}
typedef AccessoryBuilder = List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext);
typedef AccessoryBuilder = List<GridCellAccessory> Function(
GridCellAccessoryBuildContext buildContext);
abstract class CellAccessory extends Widget {
const CellAccessory({Key? key}) : super(key: key);
@ -81,7 +89,8 @@ class _AccessoryHoverState extends State<AccessoryHover> {
@override
void initState() {
_hoverState = AccessoryHoverState();
_listenerFn = () => _hoverState.onHover = widget.child.onAccessoryHover?.value ?? false;
_listenerFn = () =>
_hoverState.onHover = widget.child.onAccessoryHover?.value ?? false;
widget.child.onAccessoryHover?.addListener(_listenerFn!);
super.initState();
@ -159,7 +168,8 @@ class _Background extends StatelessWidget {
builder: (context, state, child) {
if (state.onHover) {
return FlowyHoverContainer(
style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6),
style: HoverStyle(
borderRadius: Corners.s6Border, hoverColor: theme.shader6),
);
} else {
return const SizedBox();
@ -171,14 +181,17 @@ class _Background extends StatelessWidget {
class CellAccessoryContainer extends StatelessWidget {
final List<GridCellAccessory> accessories;
const CellAccessoryContainer({required this.accessories, Key? key}) : super(key: key);
const CellAccessoryContainer({required this.accessories, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final children = accessories.where((accessory) => accessory.enable()).map((accessory) {
final children =
accessories.where((accessory) => accessory.enable()).map((accessory) {
final hover = FlowyHover(
style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface),
style:
HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface),
builder: (_, onHover) => Container(
width: 26,
height: 26,

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@ -27,22 +27,49 @@ class GridCellBuilder {
cellCache: cellCache,
fieldCache: fieldCache,
);
final key = cell.key();
switch (cell.fieldType) {
case FieldType.Checkbox:
return GridCheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);
return GridCheckboxCell(
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.DateTime:
return GridDateCell(cellControllerBuilder: cellControllerBuilder, key: key, style: style);
return GridDateCell(
cellControllerBuilder: cellControllerBuilder,
key: key,
style: style,
);
case FieldType.SingleSelect:
return GridSingleSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
return GridSingleSelectCell(
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.MultiSelect:
return GridMultiSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
return GridMultiSelectCell(
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.Number:
return GridNumberCell(cellContorllerBuilder: cellControllerBuilder, key: key);
return GridNumberCell(
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.RichText:
return GridTextCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
return GridTextCell(
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.URL:
return GridURLCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
return GridURLCell(
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
}
throw UnimplementedError;
}
@ -65,7 +92,8 @@ abstract class CellEditable {
ValueNotifier<bool> get onCellEditing;
}
abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellEditable, CellShortcuts {
abstract class GridCellWidget extends StatefulWidget
implements CellAccessory, CellEditable, CellShortcuts {
GridCellWidget({Key? key}) : super(key: key) {
onCellEditing.addListener(() {
onCellFocus.value = onCellEditing.value;
@ -75,7 +103,7 @@ abstract class GridCellWidget extends StatefulWidget implements CellAccessory, C
@override
final ValueNotifier<bool> onCellFocus = ValueNotifier<bool>(false);
// When the cell is focused, we assume that the accessory alse be hovered.
// When the cell is focused, we assume that the accessory also be hovered.
@override
ValueNotifier<bool> get onAccessoryHover => onCellFocus;
@ -83,7 +111,8 @@ abstract class GridCellWidget extends StatefulWidget implements CellAccessory, C
final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
@override
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)?
get accessoryBuilder => null;
@override
final GridCellFocusListener beginFocus = GridCellFocusListener();
@ -129,12 +158,14 @@ abstract class GridCellState<T extends GridCellWidget> extends State<T> {
void onInsert(String value) {}
}
abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCellState<T> {
SingleListenrFocusNode focusNode = SingleListenrFocusNode();
abstract class GridFocusNodeCellState<T extends GridCellWidget>
extends GridCellState<T> {
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
@override
void initState() {
widget.shortcutHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus();
widget.shortcutHandlers[CellKeyboardKey.onEnter] =
() => focusNode.unfocus();
_listenOnFocusNodeChanged();
super.initState();
}
@ -198,7 +229,7 @@ class GridCellFocusListener extends ChangeNotifier {
abstract class GridCellStyle {}
class SingleListenrFocusNode extends FocusNode {
class SingleListenerFocusNode extends FocusNode {
VoidCallback? _listener;
void setListener(VoidCallback listener) {

View File

@ -1,10 +1,10 @@
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../layout/sizes.dart';
import '../row/grid_row.dart';
import 'cell_accessory.dart';
import 'cell_builder.dart';
import 'cell_shortcuts.dart';
@ -24,9 +24,11 @@ class CellContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<RegionStateNotifier, CellContainerNotifier>(
return ChangeNotifierProxyProvider<RegionStateNotifier,
CellContainerNotifier>(
create: (_) => CellContainerNotifier(child),
update: (_, rowStateNotifier, cellStateNotifier) => cellStateNotifier!..onEnter = rowStateNotifier.onEnter,
update: (_, rowStateNotifier, cellStateNotifier) =>
cellStateNotifier!..onEnter = rowStateNotifier.onEnter,
child: Selector<CellContainerNotifier, bool>(
selector: (context, notifier) => notifier.isFocus,
builder: (context, isFocus, _) {
@ -39,7 +41,8 @@ class CellContainer extends StatelessWidget {
));
if (accessories.isNotEmpty) {
container = CellEnterRegion(child: container, accessories: accessories);
container =
CellEnterRegion(child: container, accessories: accessories);
}
}
@ -65,7 +68,8 @@ class CellContainer extends StatelessWidget {
return BoxDecoration(border: Border.fromBorderSide(borderSide));
} else {
final borderSide = BorderSide(color: theme.shader5, width: 1.0);
return BoxDecoration(border: Border(right: borderSide, bottom: borderSide));
return BoxDecoration(
border: Border(right: borderSide, bottom: borderSide));
}
}
}
@ -73,7 +77,9 @@ class CellContainer extends StatelessWidget {
class CellEnterRegion extends StatelessWidget {
final Widget child;
final List<GridCellAccessory> accessories;
const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key);
const CellEnterRegion(
{required this.child, required this.accessories, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -82,13 +88,18 @@ class CellEnterRegion extends StatelessWidget {
builder: (context, onEnter, _) {
List<Widget> children = [child];
if (onEnter) {
children.add(CellAccessoryContainer(accessories: accessories).positioned(right: 0));
children.add(CellAccessoryContainer(accessories: accessories)
.positioned(right: 0));
}
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (p) => Provider.of<CellContainerNotifier>(context, listen: false).onEnter = true,
onExit: (p) => Provider.of<CellContainerNotifier>(context, listen: false).onEnter = false,
onEnter: (p) =>
Provider.of<CellContainerNotifier>(context, listen: false)
.onEnter = true,
onExit: (p) =>
Provider.of<CellContainerNotifier>(context, listen: false)
.onEnter = false,
child: Stack(
alignment: AlignmentDirectional.center,
fit: StackFit.expand,
@ -102,7 +113,6 @@ class CellEnterRegion extends StatelessWidget {
class CellContainerNotifier extends ChangeNotifier {
final CellEditable cellEditable;
bool mouted = false;
VoidCallback? _onCellFocusListener;
bool _isFocus = false;
bool _onEnter = false;

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/widgets.dart';
@ -23,7 +23,8 @@ class _CheckboxCellState extends GridCellState<GridCheckboxCell> {
@override
void initState() {
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)
..add(const CheckboxCellEvent.initial());
super.initState();
}
@ -33,11 +34,15 @@ class _CheckboxCellState extends GridCellState<GridCheckboxCell> {
value: _cellBloc,
child: BlocBuilder<CheckboxCellBloc, CheckboxCellState>(
builder: (context, state) {
final icon = state.isSelected ? svgWidget('editor/editor_check') : svgWidget('editor/editor_uncheck');
final icon = state.isSelected
? svgWidget('editor/editor_check')
: svgWidget('editor/editor_uncheck');
return Align(
alignment: Alignment.centerLeft,
child: FlowyIconButton(
onPressed: () => context.read<CheckboxCellBloc>().add(const CheckboxCellEvent.select()),
onPressed: () => context
.read<CheckboxCellBloc>()
.add(const CheckboxCellEvent.select()),
iconPadding: EdgeInsets.zero,
icon: icon,
width: 20,

View File

@ -2,7 +2,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import '../cell_builder.dart';
import 'date_editor.dart';
@ -44,13 +44,16 @@ class _DateCellState extends GridCellState<GridDateCell> {
@override
void initState() {
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
_cellBloc = getIt<DateCellBloc>(param1: cellContext)
..add(const DateCellEvent.initial());
super.initState();
}
@override
Widget build(BuildContext context) {
final alignment = widget.cellStyle != null ? widget.cellStyle!.alignment : Alignment.center;
final alignment = widget.cellStyle != null
? widget.cellStyle!.alignment
: Alignment.center;
return BlocProvider.value(
value: _cellBloc,
child: BlocBuilder<DateCellBloc, DateCellState>(
@ -77,7 +80,8 @@ class _DateCellState extends GridCellState<GridDateCell> {
void _showCalendar(BuildContext context) {
final bloc = context.read<DateCellBloc>();
widget.onCellEditing.value = true;
final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false);
final calendar =
DateCellEditor(onDismissed: () => widget.onCellEditing.value = false);
calendar.show(
context,
cellController: bloc.cellContext.clone(),

View File

@ -1,7 +1,5 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/application/grid/cell/date_cal_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart';
import 'package:app_flowy/plugins/grid/application/cell/date_cal_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
@ -15,7 +13,10 @@ import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import '../../../layout/sizes.dart';
import '../../header/type_option/date.dart';
final kToday = DateTime.now();
final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
@ -35,7 +36,8 @@ class DateCellEditor with FlowyOverlayDelegate {
}) async {
DateCellEditor.remove(context);
final result = await cellController.getFieldTypeOption(DateTypeOptionDataParser());
final result =
await cellController.getFieldTypeOption(DateTypeOptionDataParser());
result.fold(
(dateTypeOption) {
final calendar = _CellCalendarWidget(
@ -167,7 +169,9 @@ class _CellCalendarWidget extends StatelessWidget {
},
onDaySelected: (selectedDay, focusedDay) {
_CalDateTimeSetting.hide(context);
context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
context
.read<DateCalBloc>()
.add(DateCalEvent.selectDay(selectedDay));
},
onFormatChanged: (format) {
_CalDateTimeSetting.hide(context);
@ -175,7 +179,9 @@ class _CellCalendarWidget extends StatelessWidget {
},
onPageChanged: (focusedDay) {
_CalDateTimeSetting.hide(context);
context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay));
context
.read<DateCalBloc>()
.add(DateCalEvent.setFocusedDay(focusedDay));
},
);
},
@ -200,11 +206,14 @@ class _IncludeTimeButton extends StatelessWidget {
children: [
svgWidget("grid/clock", color: theme.iconColor),
const HSpace(4),
FlowyText.medium(LocaleKeys.grid_field_includeTime.tr(), fontSize: 14),
FlowyText.medium(LocaleKeys.grid_field_includeTime.tr(),
fontSize: 14),
const Spacer(),
Switch(
value: includeTime,
onChanged: (newValue) => context.read<DateCalBloc>().add(DateCalEvent.setIncludeTime(newValue)),
onChanged: (newValue) => context
.read<DateCalBloc>()
.add(DateCalEvent.setIncludeTime(newValue)),
),
],
),
@ -294,7 +303,9 @@ class _DateTypeOptionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final title = LocaleKeys.grid_field_dateFormat.tr() + " &" + LocaleKeys.grid_field_timeFormat.tr();
final title = LocaleKeys.grid_field_dateFormat.tr() +
" &" +
LocaleKeys.grid_field_timeFormat.tr();
return BlocSelector<DateCalBloc, DateCalState, DateTypeOption>(
selector: (state) => state.dateTypeOption,
builder: (context, dateTypeOption) {
@ -321,7 +332,9 @@ class _DateTypeOptionButton extends StatelessWidget {
class _CalDateTimeSetting extends StatefulWidget {
final DateTypeOption dateTypeOption;
final Function(DateCalEvent) onEvent;
const _CalDateTimeSetting({required this.dateTypeOption, required this.onEvent, Key? key}) : super(key: key);
const _CalDateTimeSetting(
{required this.dateTypeOption, required this.onEvent, Key? key})
: super(key: key);
@override
State<_CalDateTimeSetting> createState() => _CalDateTimeSettingState();
@ -358,7 +371,8 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
DateFormatButton(onTap: () {
final list = DateFormatList(
selectedFormat: widget.dateTypeOption.dateFormat,
onSelected: (format) => widget.onEvent(DateCalEvent.setDateFormat(format)),
onSelected: (format) =>
widget.onEvent(DateCalEvent.setDateFormat(format)),
);
_showOverlay(context, list);
}),
@ -367,7 +381,8 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
onTap: () {
final list = TimeFormatList(
selectedFormat: widget.dateTypeOption.timeFormat,
onSelected: (format) => widget.onEvent(DateCalEvent.setTimeFormat(format)),
onSelected: (format) =>
widget.onEvent(DateCalEvent.setTimeFormat(format)),
);
_showOverlay(context, list);
},

View File

@ -1,16 +1,16 @@
import 'dart:async';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
class GridNumberCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
GridNumberCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
Key? key,
}) : super(key: key);
@ -25,9 +25,11 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build();
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)..add(const NumberCellEvent.initial());
_controller = TextEditingController(text: contentFromState(_cellBloc.state));
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)
..add(const NumberCellEvent.initial());
_controller =
TextEditingController(text: contentFromState(_cellBloc.state));
super.initState();
}
@ -39,7 +41,8 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
listeners: [
BlocListener<NumberCellBloc, NumberCellState>(
listenWhen: (p, c) => p.content != c.content,
listener: (context, state) => _controller.text = contentFromState(state),
listener: (context, state) =>
_controller.text = contentFromState(state),
),
],
child: TextField(
@ -70,7 +73,8 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
if (mounted) {
_delayOperation?.cancel();
_delayOperation = Timer(const Duration(milliseconds: 300), () {
if (_cellBloc.isClosed == false && _controller.text != contentFromState(_cellBloc.state)) {
if (_cellBloc.isClosed == false &&
_controller.text != contentFromState(_cellBloc.state)) {
_cellBloc.add(NumberCellEvent.updateCell(_controller.text));
}
});

View File

@ -4,3 +4,4 @@ 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

@ -1,6 +1,6 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
// ignore: unused_import
@ -9,6 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../cell_builder.dart';
import 'extension.dart';
import 'select_option_editor.dart';
@ -21,11 +22,11 @@ class SelectOptionCellStyle extends GridCellStyle {
}
class GridSingleSelectCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final SelectOptionCellStyle? cellStyle;
GridSingleSelectCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -45,8 +46,10 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
final cellContext =
widget.cellControllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)
..add(const SelectOptionCellEvent.initial());
super.initState();
}
@ -60,7 +63,7 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
selectOptions: state.selectedOptions,
cellStyle: widget.cellStyle,
onFocus: (value) => widget.onCellEditing.value = value,
cellContorllerBuilder: widget.cellContorllerBuilder);
cellControllerBuilder: widget.cellControllerBuilder);
},
),
);
@ -75,11 +78,11 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
//----------------------------------------------------------------
class GridMultiSelectCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final SelectOptionCellStyle? cellStyle;
GridMultiSelectCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -99,8 +102,10 @@ class _MultiSelectCellState extends State<GridMultiSelectCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
final cellContext =
widget.cellControllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)
..add(const SelectOptionCellEvent.initial());
super.initState();
}
@ -114,7 +119,7 @@ class _MultiSelectCellState extends State<GridMultiSelectCell> {
selectOptions: state.selectedOptions,
cellStyle: widget.cellStyle,
onFocus: (value) => widget.onCellEditing.value = value,
cellContorllerBuilder: widget.cellContorllerBuilder);
cellControllerBuilder: widget.cellControllerBuilder);
},
),
);
@ -131,12 +136,12 @@ class _SelectOptionCell extends StatelessWidget {
final List<SelectOptionPB> selectOptions;
final void Function(bool) onFocus;
final SelectOptionCellStyle? cellStyle;
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
const _SelectOptionCell({
required this.selectOptions,
required this.onFocus,
required this.cellStyle,
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
Key? key,
}) : super(key: key);
@ -147,7 +152,8 @@ class _SelectOptionCell extends StatelessWidget {
if (selectOptions.isEmpty && cellStyle != null) {
child = Align(
alignment: Alignment.centerLeft,
child: FlowyText.medium(cellStyle!.placeholder, fontSize: 14, color: theme.shader3),
child: FlowyText.medium(cellStyle!.placeholder,
fontSize: 14, color: theme.shader3),
);
} else {
final tags = selectOptions
@ -172,8 +178,10 @@ class _SelectOptionCell extends StatelessWidget {
InkWell(
onTap: () {
onFocus(true);
final cellContext = cellContorllerBuilder.build() as GridSelectOptionCellController;
SelectOptionCellEditor.show(context, cellContext, () => onFocus(false));
final cellContext =
cellControllerBuilder.build() as GridSelectOptionCellController;
SelectOptionCellEditor.show(
context, cellContext, () => onFocus(false));
},
),
],

View File

@ -1,9 +1,7 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/workspace/application/grid/cell/select_option_editor_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/select_option_editor_bloc.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -18,6 +16,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:textfield_tags/textfield_tags.dart';
import '../../../layout/sizes.dart';
import '../../common/text_field.dart';
import '../../header/type_option/select_option_editor.dart';
import 'extension.dart';
import 'text_field.dart';
@ -105,7 +106,8 @@ class _OptionList extends StatelessWidget {
builder: (context, state) {
List<Widget> cells = [];
cells.addAll(state.options.map((option) {
return _SelectOptionCell(option, state.selectedOptions.contains(option));
return _SelectOptionCell(
option, state.selectedOptions.contains(option));
}).toList());
state.createOption.fold(
@ -146,8 +148,10 @@ class _TextField extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<SelectOptionCellEditorBloc, SelectOptionEditorState>(
builder: (context, state) {
final optionMap = LinkedHashMap<String, SelectOptionPB>.fromIterable(state.selectedOptions,
key: (option) => option.name, value: (option) => option);
final optionMap = LinkedHashMap<String, SelectOptionPB>.fromIterable(
state.selectedOptions,
key: (option) => option.name,
value: (option) => option);
return SizedBox(
height: 42,
@ -156,12 +160,17 @@ class _TextField extends StatelessWidget {
selectedOptionMap: optionMap,
distanceToText: _editorPannelWidth * 0.7,
tagController: _tagController,
onClick: () => FlowyOverlay.of(context).remove(SelectOptionTypeOptionEditor.identifier),
onClick: () => FlowyOverlay.of(context)
.remove(SelectOptionTypeOptionEditor.identifier),
newText: (text) {
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.filterOption(text));
context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.filterOption(text));
},
onNewTag: (tagName) {
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.newOption(tagName));
context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.newOption(tagName));
},
),
);
@ -208,7 +217,9 @@ class _CreateOptionCell extends StatelessWidget {
SelectOptionTag(
name: name,
color: theme.shader6,
onSelected: () => context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.newOption(name)),
onSelected: () => context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.newOption(name)),
),
],
);
@ -218,7 +229,8 @@ class _CreateOptionCell extends StatelessWidget {
class _SelectOptionCell extends StatelessWidget {
final SelectOptionPB option;
final bool isSelected;
const _SelectOptionCell(this.option, this.isSelected, {Key? key}) : super(key: key);
const _SelectOptionCell(this.option, this.isSelected, {Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -232,7 +244,9 @@ class _SelectOptionCell extends StatelessWidget {
child: SelectOptionTagCell(
option: option,
onSelected: (option) {
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.selectOption(option.id));
},
children: [
if (isSelected)
@ -258,12 +272,17 @@ class _SelectOptionCell extends StatelessWidget {
final pannel = SelectOptionTypeOptionEditor(
option: option,
onDeleted: () {
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.deleteOption(option));
context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.deleteOption(option));
},
onUpdated: (updatedOption) {
context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.updateOption(updatedOption));
context
.read<SelectOptionCellEditorBloc>()
.add(SelectOptionEditorEvent.updateOption(updatedOption));
},
key: ValueKey(option.id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
key: ValueKey(option
.id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
);
final overlayIdentifier = (SelectOptionTypeOptionEditor).toString();

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'cell_builder.dart';
class GridTextCellStyle extends GridCellStyle {
@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle {
}
class GridTextCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final GridTextCellStyle? cellStyle;
GridTextCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -39,7 +39,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build();
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<TextCellBloc>(param1: cellContext);
_cellBloc.add(const TextCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
@ -86,7 +86,8 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
if (mounted) {
_delayOperation?.cancel();
_delayOperation = Timer(const Duration(milliseconds: 300), () {
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
if (_cellBloc.isClosed == false &&
_controller.text != _cellBloc.state.content) {
_cellBloc.add(TextCellEvent.updateText(_controller.text));
}
});

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/workspace/application/grid/cell/url_cell_editor_bloc.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/url_cell_editor_bloc.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'dart:async';
@ -9,7 +9,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
final GridURLCellController cellController;
final VoidCallback completed;
const URLCellEditor({required this.cellController, required this.completed, Key? key}) : super(key: key);
const URLCellEditor(
{required this.cellController, required this.completed, Key? key})
: super(key: key);
@override
State<URLCellEditor> createState() => _URLCellEditorState();
@ -105,7 +107,8 @@ class _URLCellEditorState extends State<URLCellEditor> {
Future<void> focusChanged() async {
if (mounted) {
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
if (_cellBloc.isClosed == false &&
_controller.text != _cellBloc.state.content) {
_cellBloc.add(URLCellEditorEvent.updateText(_controller.text));
}
}

View File

@ -1,16 +1,16 @@
import 'dart:async';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart';
import 'package:app_flowy/plugins/grid/application/cell/url_cell_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/toast.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:url_launcher/url_launcher.dart';
import '../cell_accessory.dart';
import '../cell_builder.dart';
import 'cell_editor.dart';
@ -31,10 +31,10 @@ enum GridURLCellAccessoryType {
}
class GridURLCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final GridURLCellStyle? cellStyle;
GridURLCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -48,34 +48,41 @@ class GridURLCell extends GridCellWidget {
@override
GridCellState<GridURLCell> createState() => _GridURLCellState();
GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
GridCellAccessory accessoryFromType(
GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
switch (ty) {
case GridURLCellAccessoryType.edit:
final cellContext = cellContorllerBuilder.build() as GridURLCellController;
return _EditURLAccessory(cellContext: cellContext, anchorContext: buildContext.anchorContext);
final cellContext =
cellControllerBuilder.build() as GridURLCellController;
return _EditURLAccessory(
cellContext: cellContext,
anchorContext: buildContext.anchorContext);
case GridURLCellAccessoryType.copyURL:
final cellContext = cellContorllerBuilder.build() as GridURLCellController;
final cellContext =
cellControllerBuilder.build() as GridURLCellController;
return _CopyURLAccessory(cellContext: cellContext);
}
}
@override
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext) get accessoryBuilder => (buildContext) {
final List<GridCellAccessory> accessories = [];
if (cellStyle != null) {
accessories.addAll(cellStyle!.accessoryTypes.map((ty) {
return accessoryFromType(ty, buildContext);
}));
}
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)
get accessoryBuilder => (buildContext) {
final List<GridCellAccessory> accessories = [];
if (cellStyle != null) {
accessories.addAll(cellStyle!.accessoryTypes.map((ty) {
return accessoryFromType(ty, buildContext);
}));
}
// If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit
if (accessories.isEmpty) {
accessories.add(accessoryFromType(GridURLCellAccessoryType.edit, buildContext));
}
// If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit
if (accessories.isEmpty) {
accessories.add(accessoryFromType(
GridURLCellAccessoryType.edit, buildContext));
}
return accessories;
};
return accessories;
};
}
class _GridURLCellState extends GridCellState<GridURLCell> {
@ -83,7 +90,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
final cellContext =
widget.cellControllerBuilder.build() as GridURLCellController;
_cellBloc = URLCellBloc(cellContext: cellContext);
_cellBloc.add(const URLCellEvent.initial());
super.initState();
@ -132,7 +140,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
if (url.isNotEmpty && await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
final cellContext =
widget.cellControllerBuilder.build() as GridURLCellController;
widget.onCellEditing.value = true;
URLCellEditor.show(context, cellContext, () {
widget.onCellEditing.value = false;
@ -177,7 +186,8 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
final GridURLCellController cellContext;
const _CopyURLAccessory({required this.cellContext, Key? key}) : super(key: key);
const _CopyURLAccessory({required this.cellContext, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -187,7 +197,8 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
@override
void onTap() {
final content = cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
final content =
cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
Clipboard.setData(ClipboardData(text: content));
showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
}

Some files were not shown because too many files have changed in this diff Show More