mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-09-11 19:17:43 +03: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;
|
||||
|
||||
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);
|
||||
if (document != null) {
|
||||
final coverType = CoverType.fromString(
|
||||
|
@ -28,6 +28,7 @@ import 'package:flutter/material.dart' hide Card;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../workspace/application/view/view_bloc.dart';
|
||||
import '../../widgets/card/card.dart';
|
||||
import '../../widgets/cell/card_cell_builder.dart';
|
||||
import '../application/board_bloc.dart';
|
||||
@ -345,9 +346,12 @@ class _DesktopBoardContentState extends State<DesktopBoardContent> {
|
||||
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (_) => RowDetailPage(
|
||||
databaseController: databaseController,
|
||||
rowController: rowController,
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
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_skeleton/text_card_cell.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_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@ -399,9 +400,12 @@ class HiddenGroupPopupItemList extends StatelessWidget {
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return RowDetailPage(
|
||||
databaseController: databaseController,
|
||||
rowController: rowController,
|
||||
return BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
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/plugins/database/application/database_controller.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/cell/card_cell_builder.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_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
@ -151,8 +151,15 @@ class _EventCardState extends State<EventCard> {
|
||||
if (settings == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return BlocProvider.value(
|
||||
value: context.read<CalendarBloc>(),
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(
|
||||
value: context.read<CalendarBloc>(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
),
|
||||
],
|
||||
child: CalendarEventEditor(
|
||||
databaseController: widget.databaseController,
|
||||
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:appflowy/generated/flowy_svgs.g.dart';
|
||||
@ -125,9 +126,12 @@ class EventEditorControls extends StatelessWidget {
|
||||
PopoverContainer.of(context).close();
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (_) => RowDetailPage(
|
||||
databaseController: databaseController,
|
||||
rowController: rowController,
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
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/grid/presentation/layout/sizes.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-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
@ -353,9 +354,12 @@ void showEventDetails({
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (BuildContext overlayContext) {
|
||||
return RowDetailPage(
|
||||
rowController: rowController,
|
||||
databaseController: databaseController,
|
||||
return BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
child: RowDetailPage(
|
||||
rowController: rowController,
|
||||
databaseController: databaseController,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -424,10 +428,13 @@ class _UnscheduledEventsButtonState extends State<UnscheduledEventsButton> {
|
||||
),
|
||||
),
|
||||
),
|
||||
popupBuilder: (context) {
|
||||
return UnscheduleEventsList(
|
||||
databaseController: widget.databaseController,
|
||||
unscheduleEvents: state.unscheduleEvents,
|
||||
popupBuilder: (_) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
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:appflowy/generated/locale_keys.g.dart';
|
||||
@ -186,9 +187,12 @@ class _GridPageState extends State<GridPage> {
|
||||
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (_) => RowDetailPage(
|
||||
databaseController: context.read<GridBloc>().databaseController,
|
||||
rowController: rowController,
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
child: RowDetailPage(
|
||||
databaseController: context.read<GridBloc>().databaseController,
|
||||
rowController: rowController,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
@ -415,12 +419,15 @@ class _GridRowsState extends State<_GridRows> {
|
||||
isDraggable: isDraggable,
|
||||
rowController: rowController,
|
||||
cellBuilder: EditableCellBuilder(databaseController: databaseController),
|
||||
openDetailPage: (context, cellBuilder) {
|
||||
openDetailPage: (rowDetailContext) {
|
||||
FlowyOverlay.show(
|
||||
context: context,
|
||||
builder: (_) => RowDetailPage(
|
||||
rowController: rowController,
|
||||
databaseController: databaseController,
|
||||
context: rowDetailContext,
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
child: RowDetailPage(
|
||||
rowController: rowController,
|
||||
databaseController: databaseController,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -39,7 +39,7 @@ class GridRow extends StatefulWidget {
|
||||
final RowId rowId;
|
||||
final RowController rowController;
|
||||
final EditableCellBuilder cellBuilder;
|
||||
final void Function(BuildContext, EditableCellBuilder) openDetailPage;
|
||||
final void Function(BuildContext context) openDetailPage;
|
||||
final int? index;
|
||||
final bool isDraggable;
|
||||
|
||||
@ -68,10 +68,7 @@ class _GridRowState extends State<GridRow> {
|
||||
child: RowContent(
|
||||
fieldController: widget.fieldController,
|
||||
cellBuilder: widget.cellBuilder,
|
||||
onExpand: () => widget.openDetailPage(
|
||||
context,
|
||||
widget.cellBuilder,
|
||||
),
|
||||
onExpand: () => widget.openDetailPage(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -5,6 +5,7 @@ import 'package:appflowy/plugins/shared/sync_indicator.dart';
|
||||
import 'package:appflowy/plugins/util.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/presentation/home/home_stack.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/favorite_button.dart';
|
||||
@ -84,9 +85,17 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<DatabaseTabBarBloc>(
|
||||
create: (context) => DatabaseTabBarBloc(view: widget.view)
|
||||
..add(const DatabaseTabBarEvent.initial()),
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
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(
|
||||
listeners: [
|
||||
BlocListener<DatabaseTabBarBloc, DatabaseTabBarState>(
|
||||
|
@ -29,6 +29,7 @@ class RelatedRowDetailPage extends StatelessWidget {
|
||||
return RowDetailPage(
|
||||
databaseController: databaseController,
|
||||
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/row/row_banner_bloc.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_skeleton/text.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/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_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -25,11 +31,13 @@ class RowBanner extends StatefulWidget {
|
||||
required this.fieldController,
|
||||
required this.rowController,
|
||||
required this.cellBuilder,
|
||||
this.allowOpenAsFullPage = true,
|
||||
});
|
||||
|
||||
final FieldController fieldController;
|
||||
final RowController rowController;
|
||||
final EditableCellBuilder cellBuilder;
|
||||
final bool allowOpenAsFullPage;
|
||||
|
||||
@override
|
||||
State<RowBanner> createState() => _RowBannerState();
|
||||
@ -84,6 +92,42 @@ class _RowBannerState extends State<RowBanner> {
|
||||
right: 12,
|
||||
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,
|
||||
required this.rowController,
|
||||
required this.databaseController,
|
||||
this.allowOpenAsFullPage = true,
|
||||
});
|
||||
|
||||
final RowController rowController;
|
||||
final DatabaseController databaseController;
|
||||
final bool allowOpenAsFullPage;
|
||||
|
||||
@override
|
||||
State<RowDetailPage> createState() => _RowDetailPageState();
|
||||
@ -60,6 +62,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
||||
fieldController: widget.databaseController.fieldController,
|
||||
rowController: widget.rowController,
|
||||
cellBuilder: cellBuilder,
|
||||
allowOpenAsFullPage: widget.allowOpenAsFullPage,
|
||||
),
|
||||
const VSpace(16),
|
||||
Padding(
|
||||
|
@ -69,7 +69,7 @@ class _RowEditorState extends State<RowEditor> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
documentBloc = DocumentBloc(view: widget.viewPB)
|
||||
documentBloc = DocumentBloc(documentId: widget.viewPB.id)
|
||||
..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/protobuf.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_editor/appflowy_editor.dart'
|
||||
show
|
||||
@ -41,19 +40,27 @@ part 'document_bloc.freezed.dart';
|
||||
|
||||
class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
DocumentBloc({
|
||||
required this.view,
|
||||
}) : _documentListener = DocumentListener(id: view.id),
|
||||
_syncStateListener = DocumentSyncStateListener(id: view.id),
|
||||
_viewListener = ViewListener(viewId: view.id),
|
||||
required this.documentId,
|
||||
this.databaseViewId,
|
||||
this.rowId,
|
||||
}) : _documentListener = DocumentListener(id: documentId),
|
||||
_syncStateListener = DocumentSyncStateListener(id: documentId),
|
||||
super(DocumentState.initial()) {
|
||||
_viewListener = databaseViewId == null && rowId == null
|
||||
? ViewListener(viewId: documentId)
|
||||
: null;
|
||||
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 DocumentSyncStateListener _syncStateListener;
|
||||
final ViewListener _viewListener;
|
||||
late final ViewListener? _viewListener;
|
||||
|
||||
final DocumentService _documentService = DocumentService();
|
||||
final TrashService _trashService = TrashService();
|
||||
@ -61,7 +68,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
late DocumentCollabAdapter _documentCollabAdapter;
|
||||
|
||||
late final TransactionAdapter _transactionAdapter = TransactionAdapter(
|
||||
documentId: view.id,
|
||||
documentId: documentId,
|
||||
documentService: _documentService,
|
||||
);
|
||||
|
||||
@ -85,9 +92,9 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
Future<void> close() async {
|
||||
await _documentListener.stop();
|
||||
await _syncStateListener.stop();
|
||||
await _viewListener.stop();
|
||||
await _viewListener?.stop();
|
||||
await _transactionSubscription?.cancel();
|
||||
await _documentService.closeDocument(view: view);
|
||||
await _documentService.closeDocument(viewId: documentId);
|
||||
_syncTimer?.cancel();
|
||||
_syncTimer = null;
|
||||
state.editorState?.service.keyboardService?.closeKeyboard();
|
||||
@ -134,14 +141,18 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
emit(state.copyWith(isDeleted: false));
|
||||
},
|
||||
deletePermanently: () async {
|
||||
final result = await _trashService.deleteViews([view.id]);
|
||||
final forceClose = result.fold((l) => true, (r) => false);
|
||||
emit(state.copyWith(forceClose: forceClose));
|
||||
if (databaseViewId == null && rowId == null) {
|
||||
final result = await _trashService.deleteViews([documentId]);
|
||||
final forceClose = result.fold((l) => true, (r) => false);
|
||||
emit(state.copyWith(forceClose: forceClose));
|
||||
}
|
||||
},
|
||||
restorePage: () async {
|
||||
final result = await _trashService.putback(view.id);
|
||||
final isDeleted = result.fold((l) => false, (r) => true);
|
||||
emit(state.copyWith(isDeleted: isDeleted));
|
||||
if (databaseViewId == null && rowId == null) {
|
||||
final result = await _trashService.putback(documentId);
|
||||
final isDeleted = result.fold((l) => false, (r) => true);
|
||||
emit(state.copyWith(isDeleted: isDeleted));
|
||||
}
|
||||
},
|
||||
syncStateChanged: (syncState) {
|
||||
emit(state.copyWith(syncState: syncState.value));
|
||||
@ -149,7 +160,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
clearAwarenessStates: () async {
|
||||
// sync a null selection and a null meta to clear the awareness states
|
||||
await _documentService.syncAwarenessStates(
|
||||
documentId: view.id,
|
||||
documentId: documentId,
|
||||
);
|
||||
},
|
||||
syncAwarenessStates: () async {
|
||||
@ -160,7 +171,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
|
||||
/// subscribe to the view(document page) change
|
||||
void _onViewChanged() {
|
||||
_viewListener.start(
|
||||
_viewListener?.start(
|
||||
onViewMoveToTrash: (r) {
|
||||
r.map((r) => add(const DocumentEvent.moveToTrash()));
|
||||
},
|
||||
@ -202,7 +213,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
|
||||
/// Fetch document
|
||||
Future<FlowyResult<EditorState?, FlowyError>> _fetchDocumentState() async {
|
||||
final result = await _documentService.openDocument(viewId: view.id);
|
||||
final result = await _documentService.openDocument(documentId: documentId);
|
||||
return result.fold(
|
||||
(s) async => FlowyResult.success(await _initAppFlowyEditorState(s)),
|
||||
(e) => FlowyResult.failure(e),
|
||||
@ -218,7 +229,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
|
||||
final editorState = EditorState(document: document);
|
||||
|
||||
_documentCollabAdapter = DocumentCollabAdapter(editorState, view.id);
|
||||
_documentCollabAdapter = DocumentCollabAdapter(editorState, documentId);
|
||||
|
||||
// subscribe to the document change from the editor
|
||||
_transactionSubscription = editorState.transactionStream.listen(
|
||||
@ -358,7 +369,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
userAvatar: user.iconUrl,
|
||||
);
|
||||
await _documentService.syncAwarenessStates(
|
||||
documentId: view.id,
|
||||
documentId: documentId,
|
||||
selection: selection,
|
||||
metadata: jsonEncode(metadata.toJson()),
|
||||
);
|
||||
@ -381,7 +392,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
userAvatar: user.iconUrl,
|
||||
);
|
||||
await _documentService.syncAwarenessStates(
|
||||
documentId: view.id,
|
||||
documentId: documentId,
|
||||
metadata: jsonEncode(metadata.toJson()),
|
||||
);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class DocumentCollabAdapter {
|
||||
///
|
||||
/// Only use in development
|
||||
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);
|
||||
if (document == null) {
|
||||
return null;
|
||||
@ -69,7 +69,7 @@ class DocumentCollabAdapter {
|
||||
///
|
||||
/// Diff the local document with the remote document and apply the changes
|
||||
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);
|
||||
if (document == null) {
|
||||
return;
|
||||
@ -104,7 +104,7 @@ class DocumentCollabAdapter {
|
||||
}
|
||||
|
||||
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);
|
||||
if (document == null) {
|
||||
return;
|
||||
|
@ -11,7 +11,7 @@ class DocumentService {
|
||||
Future<FlowyResult<void, FlowyError>> createDocument({
|
||||
required ViewPB view,
|
||||
}) async {
|
||||
final canOpen = await openDocument(viewId: view.id);
|
||||
final canOpen = await openDocument(documentId: view.id);
|
||||
if (canOpen.isSuccess) {
|
||||
return FlowyResult.success(null);
|
||||
}
|
||||
@ -21,17 +21,17 @@ class DocumentService {
|
||||
}
|
||||
|
||||
Future<FlowyResult<DocumentDataPB, FlowyError>> openDocument({
|
||||
required String viewId,
|
||||
required String documentId,
|
||||
}) async {
|
||||
final payload = OpenDocumentPayloadPB()..documentId = viewId;
|
||||
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
||||
final result = await DocumentEventOpenDocument(payload).send();
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<FlowyResult<DocumentDataPB, FlowyError>> getDocument({
|
||||
required String viewId,
|
||||
required String documentId,
|
||||
}) async {
|
||||
final payload = OpenDocumentPayloadPB()..documentId = viewId;
|
||||
final payload = OpenDocumentPayloadPB()..documentId = documentId;
|
||||
final result = await DocumentEventGetDocumentData(payload).send();
|
||||
return result;
|
||||
}
|
||||
@ -54,9 +54,9 @@ class DocumentService {
|
||||
}
|
||||
|
||||
Future<FlowyResult<void, FlowyError>> closeDocument({
|
||||
required ViewPB view,
|
||||
required String viewId,
|
||||
}) async {
|
||||
final payload = ViewIdPB()..value = view.id;
|
||||
final payload = ViewIdPB()..value = viewId;
|
||||
final result = await FolderEventCloseView(payload).send();
|
||||
return result;
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ class DocumentPluginBuilder extends PluginBuilder {
|
||||
FlowySvgData get icon => FlowySvgs.document_s;
|
||||
|
||||
@override
|
||||
PluginType get pluginType => PluginType.editor;
|
||||
PluginType get pluginType => PluginType.document;
|
||||
|
||||
@override
|
||||
ViewLayoutPB? get layoutType => ViewLayoutPB.Document;
|
||||
}
|
||||
|
||||
class DocumentPlugin extends Plugin<int> {
|
||||
class DocumentPlugin extends Plugin {
|
||||
DocumentPlugin({
|
||||
required ViewPB view,
|
||||
required PluginType pluginType,
|
||||
|
@ -36,7 +36,7 @@ class DocumentPage extends StatefulWidget {
|
||||
class _DocumentPageState extends State<DocumentPage>
|
||||
with WidgetsBindingObserver {
|
||||
EditorState? editorState;
|
||||
late final documentBloc = DocumentBloc(view: widget.view)
|
||||
late final documentBloc = DocumentBloc(documentId: widget.view.id)
|
||||
..add(const DocumentEvent.initial());
|
||||
|
||||
@override
|
||||
|
@ -76,6 +76,7 @@ class AppFlowyEditorPage extends StatefulWidget {
|
||||
this.showParagraphPlaceholder,
|
||||
this.placeholderText,
|
||||
this.initialSelection,
|
||||
this.useViewInfoBloc = true,
|
||||
});
|
||||
|
||||
final Widget? header;
|
||||
@ -91,6 +92,8 @@ class AppFlowyEditorPage extends StatefulWidget {
|
||||
///
|
||||
final Selection? initialSelection;
|
||||
|
||||
final bool useViewInfoBloc;
|
||||
|
||||
@override
|
||||
State<AppFlowyEditorPage> createState() => _AppFlowyEditorPageState();
|
||||
}
|
||||
@ -101,7 +104,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
late final InlineActionsService inlineActionsService = InlineActionsService(
|
||||
context: context,
|
||||
handlers: [
|
||||
InlinePageReferenceService(currentViewId: documentBloc.view.id),
|
||||
InlinePageReferenceService(currentViewId: documentBloc.documentId),
|
||||
DateReferenceService(context),
|
||||
ReminderReferenceService(context),
|
||||
],
|
||||
@ -186,14 +189,14 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
/// - Using `[[`
|
||||
pageReferenceShortcutBrackets(
|
||||
context,
|
||||
documentBloc.view.id,
|
||||
documentBloc.documentId,
|
||||
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
||||
),
|
||||
|
||||
/// - Using `+`
|
||||
pageReferenceShortcutPlusSign(
|
||||
context,
|
||||
documentBloc.view.id,
|
||||
documentBloc.documentId,
|
||||
styleCustomizer.inlineActionsMenuStyleBuilder(),
|
||||
),
|
||||
];
|
||||
@ -215,11 +218,13 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
viewInfoBloc.add(
|
||||
ViewInfoEvent.registerEditorState(
|
||||
editorState: widget.editorState,
|
||||
),
|
||||
);
|
||||
if (widget.useViewInfoBloc) {
|
||||
viewInfoBloc.add(
|
||||
ViewInfoEvent.registerEditorState(
|
||||
editorState: widget.editorState,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_initEditorL10n();
|
||||
_initializeShortcuts();
|
||||
@ -262,7 +267,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!viewInfoBloc.isClosed) {
|
||||
if (widget.useViewInfoBloc && !viewInfoBloc.isClosed) {
|
||||
viewInfoBloc.add(const ViewInfoEvent.unregisterEditorState());
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ SelectionMenuItem inlineGridMenuItem(DocumentBloc documentBloc) =>
|
||||
keywords: ['grid', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
// create the view inside current page
|
||||
final parentViewId = documentBloc.view.id;
|
||||
final parentViewId = documentBloc.documentId;
|
||||
final value = await ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
@ -40,7 +40,7 @@ SelectionMenuItem inlineBoardMenuItem(DocumentBloc documentBloc) =>
|
||||
keywords: ['board', 'kanban', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
// create the view inside current page
|
||||
final parentViewId = documentBloc.view.id;
|
||||
final parentViewId = documentBloc.documentId;
|
||||
final value = await ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
@ -61,7 +61,7 @@ SelectionMenuItem inlineCalendarMenuItem(DocumentBloc documentBloc) =>
|
||||
keywords: ['calendar', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
// create the view inside current page
|
||||
final parentViewId = documentBloc.view.id;
|
||||
final parentViewId = documentBloc.documentId;
|
||||
final value = await ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
|
@ -357,7 +357,7 @@ class _MentionDateBlockState extends State<MentionDateBlock> {
|
||||
);
|
||||
|
||||
// Add new reminder
|
||||
final viewId = rootContext.read<DocumentBloc>().view.id;
|
||||
final viewId = rootContext.read<DocumentBloc>().documentId;
|
||||
return rootContext.read<ReminderBloc>().add(
|
||||
ReminderEvent.add(
|
||||
reminder: ReminderPB(
|
||||
|
@ -139,7 +139,7 @@ class ReminderReferenceService extends InlineActionsDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
final viewId = context.read<DocumentBloc>().view.id;
|
||||
final viewId = context.read<DocumentBloc>().documentId;
|
||||
final reminder = _reminderFromDate(date, viewId, node);
|
||||
|
||||
final transaction = editorState.transaction
|
||||
|
@ -11,17 +11,18 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
export "./src/sandbox.dart";
|
||||
|
||||
enum PluginType {
|
||||
editor,
|
||||
document,
|
||||
blank,
|
||||
trash,
|
||||
grid,
|
||||
board,
|
||||
calendar,
|
||||
databaseDocument,
|
||||
}
|
||||
|
||||
typedef PluginId = String;
|
||||
|
||||
abstract class Plugin<T> {
|
||||
abstract class Plugin {
|
||||
PluginId get id;
|
||||
|
||||
PluginWidgetBuilder get widgetBuilder;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/plugins/database/calendar/calendar.dart';
|
||||
import 'package:appflowy/plugins/database/board/board.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/startup.dart';
|
||||
import 'package:appflowy/plugins/blank/blank.dart';
|
||||
@ -24,6 +25,10 @@ class PluginLoadTask extends LaunchTask {
|
||||
builder: CalendarPluginBuilder(),
|
||||
config: CalendarPluginConfig(),
|
||||
);
|
||||
registerPlugin(
|
||||
builder: DatabaseDocumentPluginBuilder(),
|
||||
config: DatabaseDocumentPluginConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -35,7 +35,7 @@ class DocumentExporter {
|
||||
DocumentExportType type,
|
||||
) async {
|
||||
final documentService = DocumentService();
|
||||
final result = await documentService.openDocument(viewId: view.id);
|
||||
final result = await documentService.openDocument(documentId: view.id);
|
||||
return result.fold(
|
||||
(r) {
|
||||
final document = r.toDocument();
|
||||
|
@ -54,9 +54,11 @@ class TabsBloc extends Bloc<TabsEvent, TabsState> {
|
||||
emit(state.openView(plugin, view));
|
||||
_setLatestOpenView(view);
|
||||
},
|
||||
openPlugin: (Plugin plugin, ViewPB? view) {
|
||||
emit(state.openPlugin(plugin: plugin));
|
||||
_setLatestOpenView(view);
|
||||
openPlugin: (Plugin plugin, ViewPB? view, bool setLatest) {
|
||||
emit(state.openPlugin(plugin: plugin, setLatest: setLatest));
|
||||
if (setLatest) {
|
||||
_setLatestOpenView(view);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -10,6 +10,9 @@ class TabsEvent with _$TabsEvent {
|
||||
required Plugin plugin,
|
||||
required ViewPB view,
|
||||
}) = _OpenTab;
|
||||
const factory TabsEvent.openPlugin({required Plugin plugin, ViewPB? view}) =
|
||||
_OpenPlugin;
|
||||
const factory TabsEvent.openPlugin({
|
||||
required Plugin plugin,
|
||||
ViewPB? view,
|
||||
@Default(true) bool setLatest,
|
||||
}) = _OpenPlugin;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class TabsState {
|
||||
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
||||
|
||||
if (selectExistingPlugin == null) {
|
||||
_pageManagers.add(PageManager()..setPlugin(plugin));
|
||||
_pageManagers.add(PageManager()..setPlugin(plugin, true));
|
||||
|
||||
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
|
||||
/// will become selected.
|
||||
///
|
||||
TabsState openPlugin({required Plugin plugin}) {
|
||||
TabsState openPlugin({required Plugin plugin, bool setLatest = true}) {
|
||||
final selectExistingPlugin = _selectPluginIfOpen(plugin.id);
|
||||
|
||||
if (selectExistingPlugin == null) {
|
||||
final pageManagers = [..._pageManagers];
|
||||
pageManagers[currentIndex].setPlugin(plugin);
|
||||
pageManagers[currentIndex].setPlugin(plugin, setLatest);
|
||||
|
||||
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:flutter/material.dart';
|
||||
|
||||
enum FlowyPlugin {
|
||||
editor,
|
||||
kanban,
|
||||
}
|
||||
|
||||
class PluginArgumentKeys {
|
||||
static String selection = "selection";
|
||||
static String rowId = "row_id";
|
||||
@ -34,7 +29,7 @@ extension ViewExtension on ViewPB {
|
||||
PluginType get pluginType => switch (layout) {
|
||||
ViewLayoutPB.Board => PluginType.board,
|
||||
ViewLayoutPB.Calendar => PluginType.calendar,
|
||||
ViewLayoutPB.Document => PluginType.editor,
|
||||
ViewLayoutPB.Document => PluginType.document,
|
||||
ViewLayoutPB.Grid => PluginType.grid,
|
||||
_ => throw UnimplementedError(),
|
||||
};
|
||||
|
@ -95,7 +95,7 @@ class ViewBackendService {
|
||||
required ViewLayoutPB layoutType,
|
||||
required String name,
|
||||
}) {
|
||||
return ViewBackendService.createView(
|
||||
return createView(
|
||||
layoutType: layoutType,
|
||||
parentViewId: parentViewId,
|
||||
name: name,
|
||||
|
@ -174,12 +174,14 @@ class PageNotifier extends ChangeNotifier {
|
||||
|
||||
/// This is the only place where the plugin is set.
|
||||
/// 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();
|
||||
newPlugin.init();
|
||||
|
||||
/// Set the plugin view as the latest view.
|
||||
FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send();
|
||||
// Set the plugin view as the latest view.
|
||||
if (setLatest) {
|
||||
FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send();
|
||||
}
|
||||
|
||||
_plugin = newPlugin;
|
||||
notifyListeners();
|
||||
@ -202,8 +204,8 @@ class PageManager {
|
||||
|
||||
Plugin get plugin => _notifier.plugin;
|
||||
|
||||
void setPlugin(Plugin newPlugin) {
|
||||
_notifier.plugin = newPlugin;
|
||||
void setPlugin(Plugin newPlugin, bool setLatest) {
|
||||
_notifier.setPlugin(newPlugin, setLatest);
|
||||
}
|
||||
|
||||
void setStackWithId(String id) {
|
||||
|
@ -65,7 +65,7 @@ class NotificationsView extends StatelessWidget {
|
||||
|
||||
final documentService = DocumentService();
|
||||
final documentFuture = documentService.openDocument(
|
||||
viewId: reminder.objectId,
|
||||
documentId: reminder.objectId,
|
||||
);
|
||||
|
||||
Future<Node?>? nodeBuilder;
|
||||
|
@ -49,7 +49,7 @@ void main() {
|
||||
|
||||
assert(appBloc.state.lastCreatedView != null);
|
||||
final latestView = appBloc.state.lastCreatedView!;
|
||||
final _ = DocumentBloc(view: latestView)
|
||||
final _ = DocumentBloc(documentId: latestView.id)
|
||||
..add(const DocumentEvent.initial());
|
||||
|
||||
await FolderEventSetLatestView(ViewIdPB(value: latestView.id)).send();
|
||||
|
@ -192,7 +192,7 @@ void main() {
|
||||
workspaceSetting.latestView.id == viewBloc.state.lastCreatedView!.id;
|
||||
|
||||
// ignore: unused_local_variable
|
||||
final documentBloc = DocumentBloc(view: document)
|
||||
final documentBloc = DocumentBloc(documentId: document.id)
|
||||
..add(
|
||||
const DocumentEvent.initial(),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user