mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: open a row as a full page (#5111)
* feat: open a row as a full page * chore: don't set latest open view * chore: fix calendar open * chore: disable in relation * chore: code cleanup * chore: fix merge conflicts
This commit is contained in:
parent
3c446d5e78
commit
969726ef73
@ -77,7 +77,7 @@ class RecentViewBloc extends Bloc<RecentViewEvent, RecentViewState> {
|
|||||||
final ViewListener _viewListener;
|
final ViewListener _viewListener;
|
||||||
|
|
||||||
Future<(CoverType, String?)> getCover() async {
|
Future<(CoverType, String?)> getCover() async {
|
||||||
final result = await _service.getDocument(viewId: view.id);
|
final result = await _service.getDocument(documentId: view.id);
|
||||||
final document = result.fold((s) => s.toDocument(), (f) => null);
|
final document = result.fold((s) => s.toDocument(), (f) => null);
|
||||||
if (document != null) {
|
if (document != null) {
|
||||||
final coverType = CoverType.fromString(
|
final coverType = CoverType.fromString(
|
||||||
|
@ -28,6 +28,7 @@ import 'package:flutter/material.dart' hide Card;
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import '../../../../workspace/application/view/view_bloc.dart';
|
||||||
import '../../widgets/card/card.dart';
|
import '../../widgets/card/card.dart';
|
||||||
import '../../widgets/cell/card_cell_builder.dart';
|
import '../../widgets/cell/card_cell_builder.dart';
|
||||||
import '../application/board_bloc.dart';
|
import '../application/board_bloc.dart';
|
||||||
@ -345,9 +346,12 @@ class _DesktopBoardContentState extends State<DesktopBoardContent> {
|
|||||||
|
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => RowDetailPage(
|
builder: (_) => BlocProvider.value(
|
||||||
databaseController: databaseController,
|
value: context.read<ViewBloc>(),
|
||||||
rowController: rowController,
|
child: RowDetailPage(
|
||||||
|
databaseController: databaseController,
|
||||||
|
rowController: rowController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
|||||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_skeleton/text_card_cell.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/card_cell_skeleton/text_card_cell.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
@ -399,9 +400,12 @@ class HiddenGroupPopupItemList extends StatelessWidget {
|
|||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return RowDetailPage(
|
return BlocProvider.value(
|
||||||
databaseController: databaseController,
|
value: context.read<ViewBloc>(),
|
||||||
rowController: rowController,
|
child: RowDetailPage(
|
||||||
|
databaseController: databaseController,
|
||||||
|
rowController: rowController,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
|
||||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/calendar_card_cell_style.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/calendar_card_cell_style.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@ -151,8 +151,15 @@ class _EventCardState extends State<EventCard> {
|
|||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return BlocProvider.value(
|
return MultiBlocProvider(
|
||||||
value: context.read<CalendarBloc>(),
|
providers: [
|
||||||
|
BlocProvider.value(
|
||||||
|
value: context.read<CalendarBloc>(),
|
||||||
|
),
|
||||||
|
BlocProvider.value(
|
||||||
|
value: context.read<ViewBloc>(),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: CalendarEventEditor(
|
child: CalendarEventEditor(
|
||||||
databaseController: widget.databaseController,
|
databaseController: widget.databaseController,
|
||||||
rowMeta: widget.event.event.rowMeta,
|
rowMeta: widget.event.event.rowMeta,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
@ -125,9 +126,12 @@ class EventEditorControls extends StatelessWidget {
|
|||||||
PopoverContainer.of(context).close();
|
PopoverContainer.of(context).close();
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => RowDetailPage(
|
builder: (_) => BlocProvider.value(
|
||||||
databaseController: databaseController,
|
value: context.read<ViewBloc>(),
|
||||||
rowController: rowController,
|
child: RowDetailPage(
|
||||||
|
databaseController: databaseController,
|
||||||
|
rowController: rowController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@ import 'package:appflowy/plugins/database/calendar/application/calendar_bloc.dar
|
|||||||
import 'package:appflowy/plugins/database/calendar/application/unschedule_event_bloc.dart';
|
import 'package:appflowy/plugins/database/calendar/application/unschedule_event_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||||
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
@ -353,9 +354,12 @@ void showEventDetails({
|
|||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext overlayContext) {
|
builder: (BuildContext overlayContext) {
|
||||||
return RowDetailPage(
|
return BlocProvider.value(
|
||||||
rowController: rowController,
|
value: context.read<ViewBloc>(),
|
||||||
databaseController: databaseController,
|
child: RowDetailPage(
|
||||||
|
rowController: rowController,
|
||||||
|
databaseController: databaseController,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -424,10 +428,13 @@ class _UnscheduledEventsButtonState extends State<UnscheduledEventsButton> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
popupBuilder: (context) {
|
popupBuilder: (_) {
|
||||||
return UnscheduleEventsList(
|
return BlocProvider.value(
|
||||||
databaseController: widget.databaseController,
|
value: context.read<ViewBloc>(),
|
||||||
unscheduleEvents: state.unscheduleEvents,
|
child: UnscheduleEventsList(
|
||||||
|
databaseController: widget.databaseController,
|
||||||
|
unscheduleEvents: state.unscheduleEvents,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
@ -186,9 +187,12 @@ class _GridPageState extends State<GridPage> {
|
|||||||
|
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => RowDetailPage(
|
builder: (_) => BlocProvider.value(
|
||||||
databaseController: context.read<GridBloc>().databaseController,
|
value: context.read<ViewBloc>(),
|
||||||
rowController: rowController,
|
child: RowDetailPage(
|
||||||
|
databaseController: context.read<GridBloc>().databaseController,
|
||||||
|
rowController: rowController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -415,12 +419,15 @@ class _GridRowsState extends State<_GridRows> {
|
|||||||
isDraggable: isDraggable,
|
isDraggable: isDraggable,
|
||||||
rowController: rowController,
|
rowController: rowController,
|
||||||
cellBuilder: EditableCellBuilder(databaseController: databaseController),
|
cellBuilder: EditableCellBuilder(databaseController: databaseController),
|
||||||
openDetailPage: (context, cellBuilder) {
|
openDetailPage: (rowDetailContext) {
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: rowDetailContext,
|
||||||
builder: (_) => RowDetailPage(
|
builder: (_) => BlocProvider.value(
|
||||||
rowController: rowController,
|
value: context.read<ViewBloc>(),
|
||||||
databaseController: databaseController,
|
child: RowDetailPage(
|
||||||
|
rowController: rowController,
|
||||||
|
databaseController: databaseController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -39,7 +39,7 @@ class GridRow extends StatefulWidget {
|
|||||||
final RowId rowId;
|
final RowId rowId;
|
||||||
final RowController rowController;
|
final RowController rowController;
|
||||||
final EditableCellBuilder cellBuilder;
|
final EditableCellBuilder cellBuilder;
|
||||||
final void Function(BuildContext, EditableCellBuilder) openDetailPage;
|
final void Function(BuildContext context) openDetailPage;
|
||||||
final int? index;
|
final int? index;
|
||||||
final bool isDraggable;
|
final bool isDraggable;
|
||||||
|
|
||||||
@ -68,10 +68,7 @@ class _GridRowState extends State<GridRow> {
|
|||||||
child: RowContent(
|
child: RowContent(
|
||||||
fieldController: widget.fieldController,
|
fieldController: widget.fieldController,
|
||||||
cellBuilder: widget.cellBuilder,
|
cellBuilder: widget.cellBuilder,
|
||||||
onExpand: () => widget.openDetailPage(
|
onExpand: () => widget.openDetailPage(context),
|
||||||
context,
|
|
||||||
widget.cellBuilder,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -5,6 +5,7 @@ import 'package:appflowy/plugins/shared/sync_indicator.dart';
|
|||||||
import 'package:appflowy/plugins/util.dart';
|
import 'package:appflowy/plugins/util.dart';
|
||||||
import 'package:appflowy/shared/feature_flags.dart';
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
|
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/favorite_button.dart';
|
import 'package:appflowy/workspace/presentation/widgets/favorite_button.dart';
|
||||||
@ -84,9 +85,17 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<DatabaseTabBarBloc>(
|
return MultiBlocProvider(
|
||||||
create: (context) => DatabaseTabBarBloc(view: widget.view)
|
providers: [
|
||||||
..add(const DatabaseTabBarEvent.initial()),
|
BlocProvider<DatabaseTabBarBloc>(
|
||||||
|
create: (context) => DatabaseTabBarBloc(view: widget.view)
|
||||||
|
..add(const DatabaseTabBarEvent.initial()),
|
||||||
|
),
|
||||||
|
BlocProvider<ViewBloc>(
|
||||||
|
create: (context) =>
|
||||||
|
ViewBloc(view: widget.view)..add(const ViewEvent.initial()),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: MultiBlocListener(
|
child: MultiBlocListener(
|
||||||
listeners: [
|
listeners: [
|
||||||
BlocListener<DatabaseTabBarBloc, DatabaseTabBarState>(
|
BlocListener<DatabaseTabBarBloc, DatabaseTabBarState>(
|
||||||
|
@ -29,6 +29,7 @@ class RelatedRowDetailPage extends StatelessWidget {
|
|||||||
return RowDetailPage(
|
return RowDetailPage(
|
||||||
databaseController: databaseController,
|
databaseController: databaseController,
|
||||||
rowController: rowController,
|
rowController: rowController,
|
||||||
|
allowOpenAsFullPage: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -4,11 +4,17 @@ import 'package:appflowy/plugins/database/application/cell/cell_controller.dart'
|
|||||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/row/row_banner_bloc.dart';
|
import 'package:appflowy/plugins/database/application/row/row_banner_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
||||||
|
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||||
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/row/row_action.dart';
|
import 'package:appflowy/plugins/database/widgets/row/row_action.dart';
|
||||||
|
import 'package:appflowy/plugins/database_document/database_document_plugin.dart';
|
||||||
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -25,11 +31,13 @@ class RowBanner extends StatefulWidget {
|
|||||||
required this.fieldController,
|
required this.fieldController,
|
||||||
required this.rowController,
|
required this.rowController,
|
||||||
required this.cellBuilder,
|
required this.cellBuilder,
|
||||||
|
this.allowOpenAsFullPage = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final FieldController fieldController;
|
final FieldController fieldController;
|
||||||
final RowController rowController;
|
final RowController rowController;
|
||||||
final EditableCellBuilder cellBuilder;
|
final EditableCellBuilder cellBuilder;
|
||||||
|
final bool allowOpenAsFullPage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RowBanner> createState() => _RowBannerState();
|
State<RowBanner> createState() => _RowBannerState();
|
||||||
@ -84,6 +92,42 @@ class _RowBannerState extends State<RowBanner> {
|
|||||||
right: 12,
|
right: 12,
|
||||||
child: RowActionButton(rowController: widget.rowController),
|
child: RowActionButton(rowController: widget.rowController),
|
||||||
),
|
),
|
||||||
|
if (widget.allowOpenAsFullPage)
|
||||||
|
Positioned(
|
||||||
|
top: 12,
|
||||||
|
left: 12,
|
||||||
|
child: FlowyIconButton(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
icon: const FlowySvg(FlowySvgs.full_view_s),
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
final viewBloc = context.read<ViewBloc>();
|
||||||
|
final databaseId = await DatabaseViewBackendService(
|
||||||
|
viewId: widget.cellBuilder.databaseController.viewId,
|
||||||
|
)
|
||||||
|
.getDatabaseId()
|
||||||
|
.then((value) => value.fold((s) => s, (f) => null));
|
||||||
|
final documentId = widget.rowController.rowMeta.documentId;
|
||||||
|
if (databaseId != null) {
|
||||||
|
getIt<TabsBloc>().add(
|
||||||
|
TabsEvent.openPlugin(
|
||||||
|
plugin: DatabaseDocumentPlugin(
|
||||||
|
data: DatabaseDocumentContext(
|
||||||
|
view: viewBloc.state.view,
|
||||||
|
databaseId: databaseId,
|
||||||
|
rowId: widget.rowController.rowId,
|
||||||
|
documentId: documentId,
|
||||||
|
),
|
||||||
|
pluginType: PluginType.databaseDocument,
|
||||||
|
),
|
||||||
|
setLatest: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -19,10 +19,12 @@ class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.rowController,
|
required this.rowController,
|
||||||
required this.databaseController,
|
required this.databaseController,
|
||||||
|
this.allowOpenAsFullPage = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final RowController rowController;
|
final RowController rowController;
|
||||||
final DatabaseController databaseController;
|
final DatabaseController databaseController;
|
||||||
|
final bool allowOpenAsFullPage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RowDetailPage> createState() => _RowDetailPageState();
|
State<RowDetailPage> createState() => _RowDetailPageState();
|
||||||
@ -60,6 +62,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
|||||||
fieldController: widget.databaseController.fieldController,
|
fieldController: widget.databaseController.fieldController,
|
||||||
rowController: widget.rowController,
|
rowController: widget.rowController,
|
||||||
cellBuilder: cellBuilder,
|
cellBuilder: cellBuilder,
|
||||||
|
allowOpenAsFullPage: widget.allowOpenAsFullPage,
|
||||||
),
|
),
|
||||||
const VSpace(16),
|
const VSpace(16),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -69,7 +69,7 @@ class _RowEditorState extends State<RowEditor> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
documentBloc = DocumentBloc(view: widget.viewPB)
|
documentBloc = DocumentBloc(documentId: widget.viewPB.id)
|
||||||
..add(const DocumentEvent.initial());
|
..add(const DocumentEvent.initial());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/database/application/row/related_row_detail_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database/grid/application/row/row_detail_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||||
|
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/banner.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_notification.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||||
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/action_navigation/navigation_action.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
// This widget is largely copied from `plugins/document/document_page.dart` intentionally instead of opting for an abstraction. We can make an abstraction after the view refactor is done and there's more clarity in that department.
|
||||||
|
|
||||||
|
class DatabaseDocumentPage extends StatefulWidget {
|
||||||
|
const DatabaseDocumentPage({
|
||||||
|
super.key,
|
||||||
|
required this.view,
|
||||||
|
required this.databaseId,
|
||||||
|
required this.rowId,
|
||||||
|
required this.documentId,
|
||||||
|
this.initialSelection,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final String databaseId;
|
||||||
|
final String rowId;
|
||||||
|
final String documentId;
|
||||||
|
final Selection? initialSelection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DatabaseDocumentPage> createState() => _DatabaseDocumentPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DatabaseDocumentPageState extends State<DatabaseDocumentPage> {
|
||||||
|
EditorState? editorState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
EditorNotification.addListener(_onEditorNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
EditorNotification.removeListener(_onEditorNotification);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider.value(
|
||||||
|
value: getIt<ActionNavigationBloc>(),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (_) => DocumentBloc(
|
||||||
|
databaseViewId: widget.databaseId,
|
||||||
|
rowId: widget.rowId,
|
||||||
|
documentId: widget.documentId,
|
||||||
|
)..add(const DocumentEvent.initial()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: BlocBuilder<DocumentBloc, DocumentState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state.isLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator.adaptive());
|
||||||
|
}
|
||||||
|
|
||||||
|
final editorState = state.editorState;
|
||||||
|
this.editorState = editorState;
|
||||||
|
final error = state.error;
|
||||||
|
if (error != null || editorState == null) {
|
||||||
|
Log.error(error);
|
||||||
|
return FlowyErrorPage.message(
|
||||||
|
error.toString(),
|
||||||
|
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.forceClose) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlocListener<ActionNavigationBloc, ActionNavigationState>(
|
||||||
|
listener: _onNotificationAction,
|
||||||
|
listenWhen: (_, curr) => curr.action != null,
|
||||||
|
child: _buildEditorPage(context, state),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEditorPage(BuildContext context, DocumentState state) {
|
||||||
|
final appflowyEditorPage = AppFlowyEditorPage(
|
||||||
|
editorState: state.editorState!,
|
||||||
|
styleCustomizer: EditorStyleCustomizer(
|
||||||
|
context: context,
|
||||||
|
// the 44 is the width of the left action list
|
||||||
|
padding: EditorStyleCustomizer.documentPadding,
|
||||||
|
),
|
||||||
|
header: _buildDatabaseDataContent(context, state.editorState!),
|
||||||
|
initialSelection: widget.initialSelection,
|
||||||
|
useViewInfoBloc: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
// Only show the indicator in integration test mode
|
||||||
|
// if (FlowyRunner.currentMode.isIntegrationTest)
|
||||||
|
// const DocumentSyncIndicator(),
|
||||||
|
|
||||||
|
if (state.isDeleted) _buildBanner(context),
|
||||||
|
Expanded(child: appflowyEditorPage),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDatabaseDataContent(
|
||||||
|
BuildContext context,
|
||||||
|
EditorState editorState,
|
||||||
|
) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => RelatedRowDetailPageBloc(
|
||||||
|
databaseId: widget.databaseId,
|
||||||
|
initialRowId: widget.rowId,
|
||||||
|
),
|
||||||
|
child: BlocBuilder<RelatedRowDetailPageBloc, RelatedRowDetailPageState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.when(
|
||||||
|
loading: () => const SizedBox.shrink(),
|
||||||
|
ready: (databaseController, rowController) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => RowDetailBloc(
|
||||||
|
fieldController: databaseController.fieldController,
|
||||||
|
rowController: rowController,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: 24,
|
||||||
|
left: EditorStyleCustomizer.documentPadding.left + 16 + 6,
|
||||||
|
right: EditorStyleCustomizer.documentPadding.right,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
RowPropertyList(
|
||||||
|
viewId: databaseController.viewId,
|
||||||
|
fieldController: databaseController.fieldController,
|
||||||
|
cellBuilder: EditableCellBuilder(
|
||||||
|
databaseController: databaseController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const TypeOptionSeparator(spacing: 24.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBanner(BuildContext context) {
|
||||||
|
return DocumentBanner(
|
||||||
|
onRestore: () => context.read<DocumentBloc>().add(
|
||||||
|
const DocumentEvent.restorePage(),
|
||||||
|
),
|
||||||
|
onDelete: () => context.read<DocumentBloc>().add(
|
||||||
|
const DocumentEvent.deletePermanently(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onEditorNotification(EditorNotificationType type) {
|
||||||
|
final editorState = this.editorState;
|
||||||
|
if (editorState == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == EditorNotificationType.undo) {
|
||||||
|
undoCommand.execute(editorState);
|
||||||
|
} else if (type == EditorNotificationType.redo) {
|
||||||
|
redoCommand.execute(editorState);
|
||||||
|
} else if (type == EditorNotificationType.exitEditing) {
|
||||||
|
editorState.selection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onNotificationAction(
|
||||||
|
BuildContext context,
|
||||||
|
ActionNavigationState state,
|
||||||
|
) {
|
||||||
|
if (state.action != null && state.action!.type == ActionType.jumpToBlock) {
|
||||||
|
final path = state.action?.arguments?[ActionArgumentKeys.nodePath];
|
||||||
|
|
||||||
|
final editorState = context.read<DocumentBloc>().state.editorState;
|
||||||
|
if (editorState != null && widget.documentId == state.action?.objectId) {
|
||||||
|
editorState.updateSelectionWithReason(
|
||||||
|
Selection.collapsed(Position(path: [path])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
library document_plugin;
|
||||||
|
|
||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||||
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import 'database_document_page.dart';
|
||||||
|
import 'presentation/database_document_title.dart';
|
||||||
|
|
||||||
|
// This widget is largely copied from `plugins/document/document_plugin.dart` intentionally instead of opting for an abstraction. We can make an abstraction after the view refactor is done and there's more clarity in that department.
|
||||||
|
|
||||||
|
class DatabaseDocumentContext {
|
||||||
|
DatabaseDocumentContext({
|
||||||
|
required this.view,
|
||||||
|
required this.databaseId,
|
||||||
|
required this.rowId,
|
||||||
|
required this.documentId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final String databaseId;
|
||||||
|
final String rowId;
|
||||||
|
final String documentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseDocumentPluginBuilder extends PluginBuilder {
|
||||||
|
@override
|
||||||
|
Plugin build(dynamic data) {
|
||||||
|
if (data is DatabaseDocumentContext) {
|
||||||
|
return DatabaseDocumentPlugin(pluginType: pluginType, data: data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FlowyPluginException.invalidData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get menuName => LocaleKeys.document_menuName.tr();
|
||||||
|
|
||||||
|
@override
|
||||||
|
FlowySvgData get icon => FlowySvgs.document_s;
|
||||||
|
|
||||||
|
@override
|
||||||
|
PluginType get pluginType => PluginType.databaseDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseDocumentPlugin extends Plugin {
|
||||||
|
DatabaseDocumentPlugin({
|
||||||
|
required this.data,
|
||||||
|
required PluginType pluginType,
|
||||||
|
this.initialSelection,
|
||||||
|
}) : _pluginType = pluginType;
|
||||||
|
|
||||||
|
final DatabaseDocumentContext data;
|
||||||
|
final PluginType _pluginType;
|
||||||
|
|
||||||
|
final Selection? initialSelection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
PluginWidgetBuilder get widgetBuilder => DatabaseDocumentPluginWidgetBuilder(
|
||||||
|
view: data.view,
|
||||||
|
databaseId: data.databaseId,
|
||||||
|
rowId: data.rowId,
|
||||||
|
documentId: data.documentId,
|
||||||
|
initialSelection: initialSelection,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
PluginType get pluginType => _pluginType;
|
||||||
|
|
||||||
|
@override
|
||||||
|
PluginId get id => data.rowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseDocumentPluginWidgetBuilder extends PluginWidgetBuilder
|
||||||
|
with NavigationItem {
|
||||||
|
DatabaseDocumentPluginWidgetBuilder({
|
||||||
|
required this.view,
|
||||||
|
required this.databaseId,
|
||||||
|
required this.rowId,
|
||||||
|
required this.documentId,
|
||||||
|
this.initialSelection,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final String databaseId;
|
||||||
|
final String rowId;
|
||||||
|
final String documentId;
|
||||||
|
final Selection? initialSelection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
EdgeInsets get contentPadding => EdgeInsets.zero;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildWidget({PluginContext? context, required bool shrinkWrap}) {
|
||||||
|
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
|
||||||
|
builder: (_, state) => DatabaseDocumentPage(
|
||||||
|
key: ValueKey(documentId),
|
||||||
|
view: view,
|
||||||
|
databaseId: databaseId,
|
||||||
|
documentId: documentId,
|
||||||
|
rowId: rowId,
|
||||||
|
initialSelection: initialSelection,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget get leftBarItem =>
|
||||||
|
ViewTitleBarWithRow(view: view, databaseId: databaseId, rowId: rowId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget tabBarItem(String pluginId) => const SizedBox.shrink();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? get rightBarItem => const SizedBox.shrink();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<NavigationItem> get navigationItems => [this];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseDocumentPluginConfig implements PluginConfig {
|
||||||
|
@override
|
||||||
|
bool get creatable => false;
|
||||||
|
}
|
@ -0,0 +1,380 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||||
|
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
|
||||||
|
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
||||||
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import 'database_document_title_bloc.dart';
|
||||||
|
|
||||||
|
// This widget is largely copied from `workspace/presentation/widgets/view_title_bar.dart` intentionally instead of opting for an abstraction. We can make an abstraction after the view refactor is done and there's more clarity in that department.
|
||||||
|
|
||||||
|
// workspaces / ... / database view name / row name
|
||||||
|
class ViewTitleBarWithRow extends StatelessWidget {
|
||||||
|
const ViewTitleBarWithRow({
|
||||||
|
super.key,
|
||||||
|
required this.view,
|
||||||
|
required this.databaseId,
|
||||||
|
required this.rowId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final String databaseId;
|
||||||
|
final String rowId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => DatabaseDocumentTitleBloc(
|
||||||
|
view: view,
|
||||||
|
rowId: rowId,
|
||||||
|
),
|
||||||
|
child: BlocBuilder<DatabaseDocumentTitleBloc, DatabaseDocumentTitleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state.ancestors.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
const maxWidth = WindowSizeManager.minWindowWidth - 200;
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return Visibility(
|
||||||
|
visible: maxWidth < constraints.maxWidth,
|
||||||
|
// if the width is too small, only show one view title bar without the ancestors
|
||||||
|
replacement: _ViewTitle(
|
||||||
|
key: ValueKey(state.ancestors.last),
|
||||||
|
view: state.ancestors.last,
|
||||||
|
maxTitleWidth: constraints.maxWidth - 50.0,
|
||||||
|
onUpdated: () {},
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
// refresh the view title bar when the ancestors changed
|
||||||
|
key: ValueKey(state.ancestors.hashCode),
|
||||||
|
children: _buildViewTitles(state.ancestors),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildViewTitles(List<ViewPB> views) {
|
||||||
|
// if the level is too deep, only show the root view, the database view and the row
|
||||||
|
return views.length > 2
|
||||||
|
? [
|
||||||
|
_buildViewButton(views.first),
|
||||||
|
const FlowyText.regular('/'),
|
||||||
|
const FlowyText.regular(' ... /'),
|
||||||
|
_buildViewButton(views.last),
|
||||||
|
const FlowyText.regular('/'),
|
||||||
|
_buildRowName(),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
...views
|
||||||
|
.map((e) => [_buildViewButton(e), const FlowyText.regular('/')])
|
||||||
|
.flattened,
|
||||||
|
_buildRowName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildViewButton(ViewPB view) {
|
||||||
|
return FlowyTooltip(
|
||||||
|
message: view.name,
|
||||||
|
child: _ViewTitle(
|
||||||
|
view: view,
|
||||||
|
behavior: _ViewTitleBehavior.uneditable,
|
||||||
|
onUpdated: () {},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRowName() {
|
||||||
|
return BlocBuilder<DatabaseDocumentTitleBloc, DatabaseDocumentTitleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state.databaseController == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return _RowName(
|
||||||
|
cellBuilder: EditableCellBuilder(
|
||||||
|
databaseController: state.databaseController!,
|
||||||
|
),
|
||||||
|
primaryFieldId: state.fieldId!,
|
||||||
|
rowId: rowId,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RowName extends StatelessWidget {
|
||||||
|
const _RowName({
|
||||||
|
required this.cellBuilder,
|
||||||
|
required this.primaryFieldId,
|
||||||
|
required this.rowId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final EditableCellBuilder cellBuilder;
|
||||||
|
final String primaryFieldId;
|
||||||
|
final String rowId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return cellBuilder.buildCustom(
|
||||||
|
CellContext(
|
||||||
|
fieldId: primaryFieldId,
|
||||||
|
rowId: rowId,
|
||||||
|
),
|
||||||
|
skinMap: EditableCellSkinMap(textSkin: _TitleSkin()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TitleSkin extends IEditableTextCellSkin {
|
||||||
|
@override
|
||||||
|
Widget build(
|
||||||
|
BuildContext context,
|
||||||
|
CellContainerNotifier cellContainerNotifier,
|
||||||
|
TextCellBloc bloc,
|
||||||
|
FocusNode focusNode,
|
||||||
|
TextEditingController textEditingController,
|
||||||
|
) {
|
||||||
|
return BlocSelector<TextCellBloc, TextCellState, String>(
|
||||||
|
selector: (state) => state.content,
|
||||||
|
builder: (context, content) {
|
||||||
|
final name = content.isEmpty
|
||||||
|
? LocaleKeys.grid_row_titlePlaceholder.tr()
|
||||||
|
: content;
|
||||||
|
return BlocBuilder<DatabaseDocumentTitleBloc,
|
||||||
|
DatabaseDocumentTitleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return FlowyTooltip(
|
||||||
|
message: name,
|
||||||
|
child: AppFlowyPopover(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 300,
|
||||||
|
maxHeight: 44,
|
||||||
|
),
|
||||||
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
offset: const Offset(0, 18),
|
||||||
|
popupBuilder: (_) {
|
||||||
|
return RenameRowPopover(
|
||||||
|
textController: textEditingController,
|
||||||
|
icon: state.icon ?? "",
|
||||||
|
onUpdateIcon: (String icon) {
|
||||||
|
context
|
||||||
|
.read<DatabaseDocumentTitleBloc>()
|
||||||
|
.add(DatabaseDocumentTitleEvent.updateIcon(icon));
|
||||||
|
},
|
||||||
|
onUpdateName: (text) =>
|
||||||
|
bloc.add(TextCellEvent.updateText(text)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
onTap: () {},
|
||||||
|
text: Row(
|
||||||
|
children: [
|
||||||
|
EmojiText(
|
||||||
|
emoji: state.icon ?? "",
|
||||||
|
fontSize: 18.0,
|
||||||
|
),
|
||||||
|
const HSpace(2.0),
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 180),
|
||||||
|
child: FlowyText.regular(
|
||||||
|
name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _ViewTitleBehavior {
|
||||||
|
editable,
|
||||||
|
uneditable,
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ViewTitle extends StatefulWidget {
|
||||||
|
const _ViewTitle({
|
||||||
|
super.key,
|
||||||
|
required this.view,
|
||||||
|
this.behavior = _ViewTitleBehavior.editable,
|
||||||
|
this.maxTitleWidth = 180,
|
||||||
|
required this.onUpdated,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final _ViewTitleBehavior behavior;
|
||||||
|
final double maxTitleWidth;
|
||||||
|
final VoidCallback onUpdated;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ViewTitle> createState() => _ViewTitleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ViewTitleState extends State<_ViewTitle> {
|
||||||
|
late final viewListener = ViewListener(viewId: widget.view.id);
|
||||||
|
|
||||||
|
String name = '';
|
||||||
|
String icon = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
name = widget.view.name.isEmpty
|
||||||
|
? LocaleKeys.document_title_placeholder.tr()
|
||||||
|
: widget.view.name;
|
||||||
|
icon = widget.view.icon.value;
|
||||||
|
|
||||||
|
viewListener.start(
|
||||||
|
onViewUpdated: (view) {
|
||||||
|
if (name != view.name || icon != view.icon.value) {
|
||||||
|
widget.onUpdated();
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
name = view.name.isEmpty
|
||||||
|
? LocaleKeys.document_title_placeholder.tr()
|
||||||
|
: view.name;
|
||||||
|
icon = view.icon.value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
viewListener.stop();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// root view
|
||||||
|
if (widget.view.parentViewId.isEmpty) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
FlowyText.regular(name),
|
||||||
|
const HSpace(4.0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final child = Row(
|
||||||
|
children: [
|
||||||
|
EmojiText(
|
||||||
|
emoji: icon,
|
||||||
|
fontSize: 18.0,
|
||||||
|
),
|
||||||
|
const HSpace(2.0),
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: widget.maxTitleWidth,
|
||||||
|
),
|
||||||
|
child: FlowyText.regular(
|
||||||
|
name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return Listener(
|
||||||
|
onPointerDown: (_) => context.read<TabsBloc>().openPlugin(widget.view),
|
||||||
|
child: FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
onTap: () {},
|
||||||
|
text: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenameRowPopover extends StatefulWidget {
|
||||||
|
const RenameRowPopover({
|
||||||
|
super.key,
|
||||||
|
required this.textController,
|
||||||
|
required this.onUpdateName,
|
||||||
|
required this.onUpdateIcon,
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextEditingController textController;
|
||||||
|
final String icon;
|
||||||
|
|
||||||
|
final void Function(String name) onUpdateName;
|
||||||
|
final void Function(String icon) onUpdateIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RenameRowPopover> createState() => _RenameRowPopoverState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RenameRowPopoverState extends State<RenameRowPopover> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
widget.textController.selection = TextSelection(
|
||||||
|
baseOffset: 0,
|
||||||
|
extentOffset: widget.textController.value.text.characters.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
EmojiPickerButton(
|
||||||
|
emoji: widget.icon,
|
||||||
|
direction: PopoverDirection.bottomWithCenterAligned,
|
||||||
|
offset: const Offset(0, 18),
|
||||||
|
defaultIcon: const FlowySvg(FlowySvgs.document_s),
|
||||||
|
onSubmitted: (emoji, _) {
|
||||||
|
widget.onUpdateIcon(emoji);
|
||||||
|
PopoverContainer.of(context).close();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const HSpace(6),
|
||||||
|
SizedBox(
|
||||||
|
height: 36.0,
|
||||||
|
width: 220,
|
||||||
|
child: FlowyTextField(
|
||||||
|
controller: widget.textController,
|
||||||
|
maxLength: 256,
|
||||||
|
onSubmitted: (text) {
|
||||||
|
widget.onUpdateName(text);
|
||||||
|
PopoverContainer.of(context).close();
|
||||||
|
},
|
||||||
|
onCanceled: () => widget.onUpdateName(widget.textController.text),
|
||||||
|
showCounter: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
|
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
||||||
|
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||||
|
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||||
|
import 'package:appflowy/plugins/database/domain/row_meta_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'database_document_title_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class DatabaseDocumentTitleBloc
|
||||||
|
extends Bloc<DatabaseDocumentTitleEvent, DatabaseDocumentTitleState> {
|
||||||
|
DatabaseDocumentTitleBloc({
|
||||||
|
required this.view,
|
||||||
|
required this.rowId,
|
||||||
|
}) : _metaListener = RowMetaListener(rowId),
|
||||||
|
super(DatabaseDocumentTitleState.initial()) {
|
||||||
|
_dispatch();
|
||||||
|
_startListening();
|
||||||
|
_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final String rowId;
|
||||||
|
final RowMetaListener _metaListener;
|
||||||
|
|
||||||
|
void _dispatch() {
|
||||||
|
on<DatabaseDocumentTitleEvent>((event, emit) async {
|
||||||
|
event.when(
|
||||||
|
didUpdateAncestors: (ancestors) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
ancestors: ancestors,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
didUpdateRowTitleInfo: (databaseController, rowController, fieldId) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
databaseController: databaseController,
|
||||||
|
rowController: rowController,
|
||||||
|
fieldId: fieldId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
didUpdateRowIcon: (icon) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
icon: icon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateIcon: (icon) {
|
||||||
|
_updateMeta(icon);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startListening() {
|
||||||
|
_metaListener.start(
|
||||||
|
callback: (rowMeta) {
|
||||||
|
if (!isClosed) {
|
||||||
|
add(DatabaseDocumentTitleEvent.didUpdateRowIcon(rowMeta.icon));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _init() async {
|
||||||
|
// get the database controller, row controller and primary field id
|
||||||
|
final databaseController = DatabaseController(view: view);
|
||||||
|
await databaseController.open().fold(
|
||||||
|
(s) => databaseController.setIsLoading(false),
|
||||||
|
(f) => null,
|
||||||
|
);
|
||||||
|
final rowInfo = databaseController.rowCache.getRow(rowId);
|
||||||
|
if (rowInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final rowController = RowController(
|
||||||
|
rowMeta: rowInfo.rowMeta,
|
||||||
|
viewId: view.id,
|
||||||
|
rowCache: databaseController.rowCache,
|
||||||
|
);
|
||||||
|
final primaryFieldId =
|
||||||
|
await FieldBackendService.getPrimaryField(viewId: view.id).fold(
|
||||||
|
(primaryField) => primaryField.id,
|
||||||
|
(r) {
|
||||||
|
Log.error(r);
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (primaryFieldId != null) {
|
||||||
|
add(
|
||||||
|
DatabaseDocumentTitleEvent.didUpdateRowTitleInfo(
|
||||||
|
databaseController,
|
||||||
|
rowController,
|
||||||
|
primaryFieldId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load ancestors
|
||||||
|
final ancestors = await ViewBackendService.getViewAncestors(view.id)
|
||||||
|
.fold((s) => s.items, (f) => <ViewPB>[]);
|
||||||
|
add(DatabaseDocumentTitleEvent.didUpdateAncestors(ancestors));
|
||||||
|
|
||||||
|
// initialize icon
|
||||||
|
if (rowInfo.rowMeta.icon.isNotEmpty) {
|
||||||
|
add(DatabaseDocumentTitleEvent.didUpdateRowIcon(rowInfo.rowMeta.icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the meta of the row and the view
|
||||||
|
void _updateMeta(String iconURL) {
|
||||||
|
RowBackendService(viewId: view.id)
|
||||||
|
.updateMeta(
|
||||||
|
iconURL: iconURL,
|
||||||
|
rowId: rowId,
|
||||||
|
)
|
||||||
|
.fold((l) => null, (err) => Log.error(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class DatabaseDocumentTitleEvent with _$DatabaseDocumentTitleEvent {
|
||||||
|
const factory DatabaseDocumentTitleEvent.didUpdateAncestors(
|
||||||
|
List<ViewPB> ancestors,
|
||||||
|
) = _DidUpdateAncestors;
|
||||||
|
const factory DatabaseDocumentTitleEvent.didUpdateRowTitleInfo(
|
||||||
|
DatabaseController databaseController,
|
||||||
|
RowController rowController,
|
||||||
|
String fieldId,
|
||||||
|
) = _DidUpdateRowTitleInfo;
|
||||||
|
const factory DatabaseDocumentTitleEvent.didUpdateRowIcon(
|
||||||
|
String icon,
|
||||||
|
) = _DidUpdateRowIcon;
|
||||||
|
const factory DatabaseDocumentTitleEvent.updateIcon(
|
||||||
|
String icon,
|
||||||
|
) = _UpdateIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class DatabaseDocumentTitleState with _$DatabaseDocumentTitleState {
|
||||||
|
const factory DatabaseDocumentTitleState({
|
||||||
|
required List<ViewPB> ancestors,
|
||||||
|
required DatabaseController? databaseController,
|
||||||
|
required RowController? rowController,
|
||||||
|
required String? fieldId,
|
||||||
|
required String? icon,
|
||||||
|
}) = _DatabaseDocumentTitleState;
|
||||||
|
|
||||||
|
factory DatabaseDocumentTitleState.initial() =>
|
||||||
|
const DatabaseDocumentTitleState(
|
||||||
|
ancestors: [],
|
||||||
|
databaseController: null,
|
||||||
|
rowController: null,
|
||||||
|
fieldId: null,
|
||||||
|
icon: null,
|
||||||
|
);
|
||||||
|
}
|
@ -22,7 +22,6 @@ import 'package:appflowy_backend/log.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||||
show
|
show
|
||||||
@ -41,19 +40,27 @@ part 'document_bloc.freezed.dart';
|
|||||||
|
|
||||||
class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||||
DocumentBloc({
|
DocumentBloc({
|
||||||
required this.view,
|
required this.documentId,
|
||||||
}) : _documentListener = DocumentListener(id: view.id),
|
this.databaseViewId,
|
||||||
_syncStateListener = DocumentSyncStateListener(id: view.id),
|
this.rowId,
|
||||||
_viewListener = ViewListener(viewId: view.id),
|
}) : _documentListener = DocumentListener(id: documentId),
|
||||||
|
_syncStateListener = DocumentSyncStateListener(id: documentId),
|
||||||
super(DocumentState.initial()) {
|
super(DocumentState.initial()) {
|
||||||
|
_viewListener = databaseViewId == null && rowId == null
|
||||||
|
? ViewListener(viewId: documentId)
|
||||||
|
: null;
|
||||||
on<DocumentEvent>(_onDocumentEvent);
|
on<DocumentEvent>(_onDocumentEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ViewPB view;
|
/// For a normal document, the document id is the same as the view id
|
||||||
|
final String documentId;
|
||||||
|
|
||||||
|
final String? databaseViewId;
|
||||||
|
final String? rowId;
|
||||||
|
|
||||||
final DocumentListener _documentListener;
|
final DocumentListener _documentListener;
|
||||||
final DocumentSyncStateListener _syncStateListener;
|
final DocumentSyncStateListener _syncStateListener;
|
||||||
final ViewListener _viewListener;
|
late final ViewListener? _viewListener;
|
||||||
|
|
||||||
final DocumentService _documentService = DocumentService();
|
final DocumentService _documentService = DocumentService();
|
||||||
final TrashService _trashService = TrashService();
|
final TrashService _trashService = TrashService();
|
||||||
@ -61,7 +68,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
late DocumentCollabAdapter _documentCollabAdapter;
|
late DocumentCollabAdapter _documentCollabAdapter;
|
||||||
|
|
||||||
late final TransactionAdapter _transactionAdapter = TransactionAdapter(
|
late final TransactionAdapter _transactionAdapter = TransactionAdapter(
|
||||||
documentId: view.id,
|
documentId: documentId,
|
||||||
documentService: _documentService,
|
documentService: _documentService,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,9 +92,9 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await _documentListener.stop();
|
await _documentListener.stop();
|
||||||
await _syncStateListener.stop();
|
await _syncStateListener.stop();
|
||||||
await _viewListener.stop();
|
await _viewListener?.stop();
|
||||||
await _transactionSubscription?.cancel();
|
await _transactionSubscription?.cancel();
|
||||||
await _documentService.closeDocument(view: view);
|
await _documentService.closeDocument(viewId: documentId);
|
||||||
_syncTimer?.cancel();
|
_syncTimer?.cancel();
|
||||||
_syncTimer = null;
|
_syncTimer = null;
|
||||||
state.editorState?.service.keyboardService?.closeKeyboard();
|
state.editorState?.service.keyboardService?.closeKeyboard();
|
||||||
@ -134,14 +141,18 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
emit(state.copyWith(isDeleted: false));
|
emit(state.copyWith(isDeleted: false));
|
||||||
},
|
},
|
||||||
deletePermanently: () async {
|
deletePermanently: () async {
|
||||||
final result = await _trashService.deleteViews([view.id]);
|
if (databaseViewId == null && rowId == null) {
|
||||||
final forceClose = result.fold((l) => true, (r) => false);
|
final result = await _trashService.deleteViews([documentId]);
|
||||||
emit(state.copyWith(forceClose: forceClose));
|
final forceClose = result.fold((l) => true, (r) => false);
|
||||||
|
emit(state.copyWith(forceClose: forceClose));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
restorePage: () async {
|
restorePage: () async {
|
||||||
final result = await _trashService.putback(view.id);
|
if (databaseViewId == null && rowId == null) {
|
||||||
final isDeleted = result.fold((l) => false, (r) => true);
|
final result = await _trashService.putback(documentId);
|
||||||
emit(state.copyWith(isDeleted: isDeleted));
|
final isDeleted = result.fold((l) => false, (r) => true);
|
||||||
|
emit(state.copyWith(isDeleted: isDeleted));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
syncStateChanged: (syncState) {
|
syncStateChanged: (syncState) {
|
||||||
emit(state.copyWith(syncState: syncState.value));
|
emit(state.copyWith(syncState: syncState.value));
|
||||||
@ -149,7 +160,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
clearAwarenessStates: () async {
|
clearAwarenessStates: () async {
|
||||||
// sync a null selection and a null meta to clear the awareness states
|
// sync a null selection and a null meta to clear the awareness states
|
||||||
await _documentService.syncAwarenessStates(
|
await _documentService.syncAwarenessStates(
|
||||||
documentId: view.id,
|
documentId: documentId,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
syncAwarenessStates: () async {
|
syncAwarenessStates: () async {
|
||||||
@ -160,7 +171,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
|
|
||||||
/// subscribe to the view(document page) change
|
/// subscribe to the view(document page) change
|
||||||
void _onViewChanged() {
|
void _onViewChanged() {
|
||||||
_viewListener.start(
|
_viewListener?.start(
|
||||||
onViewMoveToTrash: (r) {
|
onViewMoveToTrash: (r) {
|
||||||
r.map((r) => add(const DocumentEvent.moveToTrash()));
|
r.map((r) => add(const DocumentEvent.moveToTrash()));
|
||||||
},
|
},
|
||||||
@ -202,7 +213,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
|
|
||||||
/// Fetch document
|
/// Fetch document
|
||||||
Future<FlowyResult<EditorState?, FlowyError>> _fetchDocumentState() async {
|
Future<FlowyResult<EditorState?, FlowyError>> _fetchDocumentState() async {
|
||||||
final result = await _documentService.openDocument(viewId: view.id);
|
final result = await _documentService.openDocument(documentId: documentId);
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(s) async => FlowyResult.success(await _initAppFlowyEditorState(s)),
|
(s) async => FlowyResult.success(await _initAppFlowyEditorState(s)),
|
||||||
(e) => FlowyResult.failure(e),
|
(e) => FlowyResult.failure(e),
|
||||||
@ -218,7 +229,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
|
|
||||||
final editorState = EditorState(document: document);
|
final editorState = EditorState(document: document);
|
||||||
|
|
||||||
_documentCollabAdapter = DocumentCollabAdapter(editorState, view.id);
|
_documentCollabAdapter = DocumentCollabAdapter(editorState, documentId);
|
||||||
|
|
||||||
// subscribe to the document change from the editor
|
// subscribe to the document change from the editor
|
||||||
_transactionSubscription = editorState.transactionStream.listen(
|
_transactionSubscription = editorState.transactionStream.listen(
|
||||||
@ -358,7 +369,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
userAvatar: user.iconUrl,
|
userAvatar: user.iconUrl,
|
||||||
);
|
);
|
||||||
await _documentService.syncAwarenessStates(
|
await _documentService.syncAwarenessStates(
|
||||||
documentId: view.id,
|
documentId: documentId,
|
||||||
selection: selection,
|
selection: selection,
|
||||||
metadata: jsonEncode(metadata.toJson()),
|
metadata: jsonEncode(metadata.toJson()),
|
||||||
);
|
);
|
||||||
@ -381,7 +392,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
userAvatar: user.iconUrl,
|
userAvatar: user.iconUrl,
|
||||||
);
|
);
|
||||||
await _documentService.syncAwarenessStates(
|
await _documentService.syncAwarenessStates(
|
||||||
documentId: view.id,
|
documentId: documentId,
|
||||||
metadata: jsonEncode(metadata.toJson()),
|
metadata: jsonEncode(metadata.toJson()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class DocumentCollabAdapter {
|
|||||||
///
|
///
|
||||||
/// Only use in development
|
/// Only use in development
|
||||||
Future<EditorState?> syncV1() async {
|
Future<EditorState?> syncV1() async {
|
||||||
final result = await _service.getDocument(viewId: docId);
|
final result = await _service.getDocument(documentId: docId);
|
||||||
final document = result.fold((s) => s.toDocument(), (f) => null);
|
final document = result.fold((s) => s.toDocument(), (f) => null);
|
||||||
if (document == null) {
|
if (document == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -69,7 +69,7 @@ class DocumentCollabAdapter {
|
|||||||
///
|
///
|
||||||
/// Diff the local document with the remote document and apply the changes
|
/// Diff the local document with the remote document and apply the changes
|
||||||
Future<void> syncV3({DocEventPB? docEvent}) async {
|
Future<void> syncV3({DocEventPB? docEvent}) async {
|
||||||
final result = await _service.getDocument(viewId: docId);
|
final result = await _service.getDocument(documentId: docId);
|
||||||
final document = result.fold((s) => s.toDocument(), (f) => null);
|
final document = result.fold((s) => s.toDocument(), (f) => null);
|
||||||
if (document == null) {
|
if (document == null) {
|
||||||
return;
|
return;
|
||||||
@ -104,7 +104,7 @@ class DocumentCollabAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> forceReload() async {
|
Future<void> forceReload() async {
|
||||||
final result = await _service.getDocument(viewId: docId);
|
final result = await _service.getDocument(documentId: docId);
|
||||||
final document = result.fold((s) => s.toDocument(), (f) => null);
|
final document = result.fold((s) => s.toDocument(), (f) => null);
|
||||||
if (document == null) {
|
if (document == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -11,7 +11,7 @@ class DocumentService {
|
|||||||
Future<FlowyResult<void, FlowyError>> createDocument({
|
Future<FlowyResult<void, FlowyError>> createDocument({
|
||||||
required ViewPB view,
|
required ViewPB view,
|
||||||
}) async {
|
}) async {
|
||||||
final canOpen = await openDocument(viewId: view.id);
|
final canOpen = await openDocument(documentId: view.id);
|
||||||
if (canOpen.isSuccess) {
|
if (canOpen.isSuccess) {
|
||||||
return FlowyResult.success(null);
|
return FlowyResult.success(null);
|
||||||
}
|
}
|
||||||
@ -21,17 +21,17 @@ class DocumentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<FlowyResult<DocumentDataPB, FlowyError>> openDocument({
|
Future<FlowyResult<DocumentDataPB, FlowyError>> openDocument({
|
||||||
required String viewId,
|
required String documentId,
|
||||||
}) async {
|
}) async {
|
||||||
final payload = OpenDocumentPayloadPB()..documentId = viewId;
|
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
||||||
final result = await DocumentEventOpenDocument(payload).send();
|
final result = await DocumentEventOpenDocument(payload).send();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<FlowyResult<DocumentDataPB, FlowyError>> getDocument({
|
Future<FlowyResult<DocumentDataPB, FlowyError>> getDocument({
|
||||||
required String viewId,
|
required String documentId,
|
||||||
}) async {
|
}) async {
|
||||||
final payload = OpenDocumentPayloadPB()..documentId = viewId;
|
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
||||||
final result = await DocumentEventGetDocumentData(payload).send();
|
final result = await DocumentEventGetDocumentData(payload).send();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -54,9 +54,9 @@ class DocumentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<FlowyResult<void, FlowyError>> closeDocument({
|
Future<FlowyResult<void, FlowyError>> closeDocument({
|
||||||
required ViewPB view,
|
required String viewId,
|
||||||
}) async {
|
}) async {
|
||||||
final payload = ViewIdPB()..value = view.id;
|
final payload = ViewIdPB()..value = viewId;
|
||||||
final result = await FolderEventCloseView(payload).send();
|
final result = await FolderEventCloseView(payload).send();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,13 @@ class DocumentPluginBuilder extends PluginBuilder {
|
|||||||
FlowySvgData get icon => FlowySvgs.document_s;
|
FlowySvgData get icon => FlowySvgs.document_s;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PluginType get pluginType => PluginType.editor;
|
PluginType get pluginType => PluginType.document;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ViewLayoutPB? get layoutType => ViewLayoutPB.Document;
|
ViewLayoutPB? get layoutType => ViewLayoutPB.Document;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentPlugin extends Plugin<int> {
|
class DocumentPlugin extends Plugin {
|
||||||
DocumentPlugin({
|
DocumentPlugin({
|
||||||
required ViewPB view,
|
required ViewPB view,
|
||||||
required PluginType pluginType,
|
required PluginType pluginType,
|
||||||
|
@ -36,7 +36,7 @@ class DocumentPage extends StatefulWidget {
|
|||||||
class _DocumentPageState extends State<DocumentPage>
|
class _DocumentPageState extends State<DocumentPage>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
EditorState? editorState;
|
EditorState? editorState;
|
||||||
late final documentBloc = DocumentBloc(view: widget.view)
|
late final documentBloc = DocumentBloc(documentId: widget.view.id)
|
||||||
..add(const DocumentEvent.initial());
|
..add(const DocumentEvent.initial());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -76,6 +76,7 @@ class AppFlowyEditorPage extends StatefulWidget {
|
|||||||
this.showParagraphPlaceholder,
|
this.showParagraphPlaceholder,
|
||||||
this.placeholderText,
|
this.placeholderText,
|
||||||
this.initialSelection,
|
this.initialSelection,
|
||||||
|
this.useViewInfoBloc = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget? header;
|
final Widget? header;
|
||||||
@ -91,6 +92,8 @@ class AppFlowyEditorPage extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
final Selection? initialSelection;
|
final Selection? initialSelection;
|
||||||
|
|
||||||
|
final bool useViewInfoBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AppFlowyEditorPage> createState() => _AppFlowyEditorPageState();
|
State<AppFlowyEditorPage> createState() => _AppFlowyEditorPageState();
|
||||||
}
|
}
|
||||||
@ -101,7 +104,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
|||||||
late final InlineActionsService inlineActionsService = InlineActionsService(
|
late final InlineActionsService inlineActionsService = InlineActionsService(
|
||||||
context: context,
|
context: context,
|
||||||
handlers: [
|
handlers: [
|
||||||
InlinePageReferenceService(currentViewId: documentBloc.view.id),
|
InlinePageReferenceService(currentViewId: documentBloc.documentId),
|
||||||
DateReferenceService(context),
|
DateReferenceService(context),
|
||||||
ReminderReferenceService(context),
|
ReminderReferenceService(context),
|
||||||
],
|
],
|
||||||
@ -186,14 +189,14 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
|||||||
/// - Using `[[`
|
/// - Using `[[`
|
||||||
pageReferenceShortcutBrackets(
|
pageReferenceShortcutBrackets(
|
||||||
context,
|
context,
|
||||||
documentBloc.view.id,
|
documentBloc.documentId,
|
||||||
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// - Using `+`
|
/// - Using `+`
|
||||||
pageReferenceShortcutPlusSign(
|
pageReferenceShortcutPlusSign(
|
||||||
context,
|
context,
|
||||||
documentBloc.view.id,
|
documentBloc.documentId,
|
||||||
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@ -215,11 +218,13 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
viewInfoBloc.add(
|
if (widget.useViewInfoBloc) {
|
||||||
ViewInfoEvent.registerEditorState(
|
viewInfoBloc.add(
|
||||||
editorState: widget.editorState,
|
ViewInfoEvent.registerEditorState(
|
||||||
),
|
editorState: widget.editorState,
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_initEditorL10n();
|
_initEditorL10n();
|
||||||
_initializeShortcuts();
|
_initializeShortcuts();
|
||||||
@ -262,7 +267,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (!viewInfoBloc.isClosed) {
|
if (widget.useViewInfoBloc && !viewInfoBloc.isClosed) {
|
||||||
viewInfoBloc.add(const ViewInfoEvent.unregisterEditorState());
|
viewInfoBloc.add(const ViewInfoEvent.unregisterEditorState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ SelectionMenuItem inlineGridMenuItem(DocumentBloc documentBloc) =>
|
|||||||
keywords: ['grid', 'database'],
|
keywords: ['grid', 'database'],
|
||||||
handler: (editorState, menuService, context) async {
|
handler: (editorState, menuService, context) async {
|
||||||
// create the view inside current page
|
// create the view inside current page
|
||||||
final parentViewId = documentBloc.view.id;
|
final parentViewId = documentBloc.documentId;
|
||||||
final value = await ViewBackendService.createView(
|
final value = await ViewBackendService.createView(
|
||||||
parentViewId: parentViewId,
|
parentViewId: parentViewId,
|
||||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
@ -40,7 +40,7 @@ SelectionMenuItem inlineBoardMenuItem(DocumentBloc documentBloc) =>
|
|||||||
keywords: ['board', 'kanban', 'database'],
|
keywords: ['board', 'kanban', 'database'],
|
||||||
handler: (editorState, menuService, context) async {
|
handler: (editorState, menuService, context) async {
|
||||||
// create the view inside current page
|
// create the view inside current page
|
||||||
final parentViewId = documentBloc.view.id;
|
final parentViewId = documentBloc.documentId;
|
||||||
final value = await ViewBackendService.createView(
|
final value = await ViewBackendService.createView(
|
||||||
parentViewId: parentViewId,
|
parentViewId: parentViewId,
|
||||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
@ -61,7 +61,7 @@ SelectionMenuItem inlineCalendarMenuItem(DocumentBloc documentBloc) =>
|
|||||||
keywords: ['calendar', 'database'],
|
keywords: ['calendar', 'database'],
|
||||||
handler: (editorState, menuService, context) async {
|
handler: (editorState, menuService, context) async {
|
||||||
// create the view inside current page
|
// create the view inside current page
|
||||||
final parentViewId = documentBloc.view.id;
|
final parentViewId = documentBloc.documentId;
|
||||||
final value = await ViewBackendService.createView(
|
final value = await ViewBackendService.createView(
|
||||||
parentViewId: parentViewId,
|
parentViewId: parentViewId,
|
||||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
|
@ -357,7 +357,7 @@ class _MentionDateBlockState extends State<MentionDateBlock> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Add new reminder
|
// Add new reminder
|
||||||
final viewId = rootContext.read<DocumentBloc>().view.id;
|
final viewId = rootContext.read<DocumentBloc>().documentId;
|
||||||
return rootContext.read<ReminderBloc>().add(
|
return rootContext.read<ReminderBloc>().add(
|
||||||
ReminderEvent.add(
|
ReminderEvent.add(
|
||||||
reminder: ReminderPB(
|
reminder: ReminderPB(
|
||||||
|
@ -139,7 +139,7 @@ class ReminderReferenceService extends InlineActionsDelegate {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final viewId = context.read<DocumentBloc>().view.id;
|
final viewId = context.read<DocumentBloc>().documentId;
|
||||||
final reminder = _reminderFromDate(date, viewId, node);
|
final reminder = _reminderFromDate(date, viewId, node);
|
||||||
|
|
||||||
final transaction = editorState.transaction
|
final transaction = editorState.transaction
|
||||||
|
@ -11,17 +11,18 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|||||||
export "./src/sandbox.dart";
|
export "./src/sandbox.dart";
|
||||||
|
|
||||||
enum PluginType {
|
enum PluginType {
|
||||||
editor,
|
document,
|
||||||
blank,
|
blank,
|
||||||
trash,
|
trash,
|
||||||
grid,
|
grid,
|
||||||
board,
|
board,
|
||||||
calendar,
|
calendar,
|
||||||
|
databaseDocument,
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef PluginId = String;
|
typedef PluginId = String;
|
||||||
|
|
||||||
abstract class Plugin<T> {
|
abstract class Plugin {
|
||||||
PluginId get id;
|
PluginId get id;
|
||||||
|
|
||||||
PluginWidgetBuilder get widgetBuilder;
|
PluginWidgetBuilder get widgetBuilder;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/plugins/database/calendar/calendar.dart';
|
import 'package:appflowy/plugins/database/calendar/calendar.dart';
|
||||||
import 'package:appflowy/plugins/database/board/board.dart';
|
import 'package:appflowy/plugins/database/board/board.dart';
|
||||||
import 'package:appflowy/plugins/database/grid/grid.dart';
|
import 'package:appflowy/plugins/database/grid/grid.dart';
|
||||||
|
import 'package:appflowy/plugins/database_document/database_document_plugin.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/plugins/blank/blank.dart';
|
import 'package:appflowy/plugins/blank/blank.dart';
|
||||||
@ -24,6 +25,10 @@ class PluginLoadTask extends LaunchTask {
|
|||||||
builder: CalendarPluginBuilder(),
|
builder: CalendarPluginBuilder(),
|
||||||
config: CalendarPluginConfig(),
|
config: CalendarPluginConfig(),
|
||||||
);
|
);
|
||||||
|
registerPlugin(
|
||||||
|
builder: DatabaseDocumentPluginBuilder(),
|
||||||
|
config: DatabaseDocumentPluginConfig(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,7 +35,7 @@ class DocumentExporter {
|
|||||||
DocumentExportType type,
|
DocumentExportType type,
|
||||||
) async {
|
) async {
|
||||||
final documentService = DocumentService();
|
final documentService = DocumentService();
|
||||||
final result = await documentService.openDocument(viewId: view.id);
|
final result = await documentService.openDocument(documentId: view.id);
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(r) {
|
(r) {
|
||||||
final document = r.toDocument();
|
final document = r.toDocument();
|
||||||
|
@ -54,9 +54,11 @@ class TabsBloc extends Bloc<TabsEvent, TabsState> {
|
|||||||
emit(state.openView(plugin, view));
|
emit(state.openView(plugin, view));
|
||||||
_setLatestOpenView(view);
|
_setLatestOpenView(view);
|
||||||
},
|
},
|
||||||
openPlugin: (Plugin plugin, ViewPB? view) {
|
openPlugin: (Plugin plugin, ViewPB? view, bool setLatest) {
|
||||||
emit(state.openPlugin(plugin: plugin));
|
emit(state.openPlugin(plugin: plugin, setLatest: setLatest));
|
||||||
_setLatestOpenView(view);
|
if (setLatest) {
|
||||||
|
_setLatestOpenView(view);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,9 @@ class TabsEvent with _$TabsEvent {
|
|||||||
required Plugin plugin,
|
required Plugin plugin,
|
||||||
required ViewPB view,
|
required ViewPB view,
|
||||||
}) = _OpenTab;
|
}) = _OpenTab;
|
||||||
const factory TabsEvent.openPlugin({required Plugin plugin, ViewPB? view}) =
|
const factory TabsEvent.openPlugin({
|
||||||
_OpenPlugin;
|
required Plugin plugin,
|
||||||
|
ViewPB? view,
|
||||||
|
@Default(true) bool setLatest,
|
||||||
|
}) = _OpenPlugin;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class TabsState {
|
|||||||
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
||||||
|
|
||||||
if (selectExistingPlugin == null) {
|
if (selectExistingPlugin == null) {
|
||||||
_pageManagers.add(PageManager()..setPlugin(plugin));
|
_pageManagers.add(PageManager()..setPlugin(plugin, true));
|
||||||
|
|
||||||
return copyWith(newIndex: pages - 1, pageManagers: [..._pageManagers]);
|
return copyWith(newIndex: pages - 1, pageManagers: [..._pageManagers]);
|
||||||
}
|
}
|
||||||
@ -58,12 +58,12 @@ class TabsState {
|
|||||||
/// If the plugin is already open in a tab, then that tab
|
/// If the plugin is already open in a tab, then that tab
|
||||||
/// will become selected.
|
/// will become selected.
|
||||||
///
|
///
|
||||||
TabsState openPlugin({required Plugin plugin}) {
|
TabsState openPlugin({required Plugin plugin, bool setLatest = true}) {
|
||||||
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
||||||
|
|
||||||
if (selectExistingPlugin == null) {
|
if (selectExistingPlugin == null) {
|
||||||
final pageManagers = [..._pageManagers];
|
final pageManagers = [..._pageManagers];
|
||||||
pageManagers[currentIndex].setPlugin(plugin);
|
pageManagers[currentIndex].setPlugin(plugin, setLatest);
|
||||||
|
|
||||||
return copyWith(pageManagers: pageManagers);
|
return copyWith(pageManagers: pageManagers);
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
enum FlowyPlugin {
|
|
||||||
editor,
|
|
||||||
kanban,
|
|
||||||
}
|
|
||||||
|
|
||||||
class PluginArgumentKeys {
|
class PluginArgumentKeys {
|
||||||
static String selection = "selection";
|
static String selection = "selection";
|
||||||
static String rowId = "row_id";
|
static String rowId = "row_id";
|
||||||
@ -34,7 +29,7 @@ extension ViewExtension on ViewPB {
|
|||||||
PluginType get pluginType => switch (layout) {
|
PluginType get pluginType => switch (layout) {
|
||||||
ViewLayoutPB.Board => PluginType.board,
|
ViewLayoutPB.Board => PluginType.board,
|
||||||
ViewLayoutPB.Calendar => PluginType.calendar,
|
ViewLayoutPB.Calendar => PluginType.calendar,
|
||||||
ViewLayoutPB.Document => PluginType.editor,
|
ViewLayoutPB.Document => PluginType.document,
|
||||||
ViewLayoutPB.Grid => PluginType.grid,
|
ViewLayoutPB.Grid => PluginType.grid,
|
||||||
_ => throw UnimplementedError(),
|
_ => throw UnimplementedError(),
|
||||||
};
|
};
|
||||||
|
@ -95,7 +95,7 @@ class ViewBackendService {
|
|||||||
required ViewLayoutPB layoutType,
|
required ViewLayoutPB layoutType,
|
||||||
required String name,
|
required String name,
|
||||||
}) {
|
}) {
|
||||||
return ViewBackendService.createView(
|
return createView(
|
||||||
layoutType: layoutType,
|
layoutType: layoutType,
|
||||||
parentViewId: parentViewId,
|
parentViewId: parentViewId,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -174,12 +174,14 @@ class PageNotifier extends ChangeNotifier {
|
|||||||
|
|
||||||
/// This is the only place where the plugin is set.
|
/// This is the only place where the plugin is set.
|
||||||
/// No need compare the old plugin with the new plugin. Just set it.
|
/// No need compare the old plugin with the new plugin. Just set it.
|
||||||
set plugin(Plugin newPlugin) {
|
void setPlugin(Plugin newPlugin, bool setLatest) {
|
||||||
_plugin.dispose();
|
_plugin.dispose();
|
||||||
newPlugin.init();
|
newPlugin.init();
|
||||||
|
|
||||||
/// Set the plugin view as the latest view.
|
// Set the plugin view as the latest view.
|
||||||
FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send();
|
if (setLatest) {
|
||||||
|
FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send();
|
||||||
|
}
|
||||||
|
|
||||||
_plugin = newPlugin;
|
_plugin = newPlugin;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -202,8 +204,8 @@ class PageManager {
|
|||||||
|
|
||||||
Plugin get plugin => _notifier.plugin;
|
Plugin get plugin => _notifier.plugin;
|
||||||
|
|
||||||
void setPlugin(Plugin newPlugin) {
|
void setPlugin(Plugin newPlugin, bool setLatest) {
|
||||||
_notifier.plugin = newPlugin;
|
_notifier.setPlugin(newPlugin, setLatest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStackWithId(String id) {
|
void setStackWithId(String id) {
|
||||||
|
@ -65,7 +65,7 @@ class NotificationsView extends StatelessWidget {
|
|||||||
|
|
||||||
final documentService = DocumentService();
|
final documentService = DocumentService();
|
||||||
final documentFuture = documentService.openDocument(
|
final documentFuture = documentService.openDocument(
|
||||||
viewId: reminder.objectId,
|
documentId: reminder.objectId,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<Node?>? nodeBuilder;
|
Future<Node?>? nodeBuilder;
|
||||||
|
@ -49,7 +49,7 @@ void main() {
|
|||||||
|
|
||||||
assert(appBloc.state.lastCreatedView != null);
|
assert(appBloc.state.lastCreatedView != null);
|
||||||
final latestView = appBloc.state.lastCreatedView!;
|
final latestView = appBloc.state.lastCreatedView!;
|
||||||
final _ = DocumentBloc(view: latestView)
|
final _ = DocumentBloc(documentId: latestView.id)
|
||||||
..add(const DocumentEvent.initial());
|
..add(const DocumentEvent.initial());
|
||||||
|
|
||||||
await FolderEventSetLatestView(ViewIdPB(value: latestView.id)).send();
|
await FolderEventSetLatestView(ViewIdPB(value: latestView.id)).send();
|
||||||
|
@ -192,7 +192,7 @@ void main() {
|
|||||||
workspaceSetting.latestView.id == viewBloc.state.lastCreatedView!.id;
|
workspaceSetting.latestView.id == viewBloc.state.lastCreatedView!.id;
|
||||||
|
|
||||||
// ignore: unused_local_variable
|
// ignore: unused_local_variable
|
||||||
final documentBloc = DocumentBloc(view: document)
|
final documentBloc = DocumentBloc(documentId: document.id)
|
||||||
..add(
|
..add(
|
||||||
const DocumentEvent.initial(),
|
const DocumentEvent.initial(),
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user