mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'AppFlowy-IO:main' into side_context_menu
This commit is contained in:
commit
a71c5c0ee5
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -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
|
||||
|
2
.github/workflows/dart_lint.yml
vendored
2
.github/workflows/dart_lint.yml
vendored
@ -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:
|
||||
|
2
.github/workflows/dart_test.yml
vendored
2
.github/workflows/dart_test.yml
vendored
@ -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
|
||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -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
|
||||
|
2
.github/workflows/rust_lint.yml
vendored
2
.github/workflows/rust_lint.yml
vendored
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
@ -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 {
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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(
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
@ -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),
|
||||
],
|
||||
),
|
@ -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));
|
@ -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';
|
||||
|
@ -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];
|
@ -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';
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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(
|
@ -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 ?? [],
|
@ -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) {
|
@ -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(),
|
@ -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);
|
||||
}
|
@ -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);
|
@ -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,
|
||||
);
|
||||
}
|
@ -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);
|
@ -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);
|
161
frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
Normal file
161
frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
Normal 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);
|
||||
}
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
@ -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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
@ -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!);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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,
|
||||
);
|
@ -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
|
@ -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;
|
||||
}
|
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
@ -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,
|
@ -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) {
|
@ -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;
|
@ -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,
|
@ -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(),
|
@ -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);
|
||||
},
|
@ -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));
|
||||
}
|
||||
});
|
@ -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';
|
@ -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));
|
||||
},
|
||||
),
|
||||
],
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
});
|
@ -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));
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user