mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-25 07:07:32 -04:00
* feat: implement folder indexer * feat: sqlite search views using fts5 * feat: add view indexing to user manager * feat: implement folder indexer * feat: add sqlite search documents * feat: add document indexing to user manager * feat: add document indexing to folder indexer * chore: update collab rev * feat: search frontend integration * refactor: search index * test: add event test * chore: fix ci * feat: initial command palette overlay impl (#4619) * chore: test search engine * chore: initial structure * chore: replace old search request * chore: enable log for lib-dispatch * chore: move search manager to core * feat: move traits and responsibility to search crate * feat: move search to search crate * feat: replace sqlite with tantivy * feat: deserialize tantivy documents * chore: fixes after rebase * chore: clean code * feat: fetch and sort results * fix: code review + cleaning * feat: support custom icons * feat: support view layout icons * feat: rename bloc and fix indexing * fix: prettify dialog * feat: score results * chore: update collab rev * feat: add recent view history to command palette * test: add integration_tests * fix: clippy changes * fix: focus traversal in cmd palette * fix: remove file after merging main * chore: code review and panic-safe * feat: index all views if index does not exist * chore: improve logic with conditional * chore: add is_empty check * chore: abstract logic from folder manager init * chore: update collab rev * chore: code review * chore: fixes after merge + update lock file * chore: revert cargo lock * fix: set icon type when removing icon * fix: code review + dependency inversion * fix: remove icon fix for not persisting icon type * test: simple tests manipulating views * test: create 100 views * fix: tauri build * chore: create 1000 views * chore: create util methods * chore: test * chore: test * chore: remove logs * chore: fix build.rs * chore: export models * chore: enable clear cache on Rust-CI * fix: navigate to newly created views * fix: force disable setting workspace listener on rebuilds * fix: remove late final * fix: missing returns * fix: localization and minor fixes * test: add index assert to large test * fix: missing section param after merging main * chore: try fix unzip file error * chore: lower the test * feat: show hint when result is in trash * feat: one index_writer per index * fix: minor changes after merge * fix: make create_log_filter public after merge * chore: fix test * chore: fix test * chore: flutter analyze * chore: flutter analyze * chore: fix tauri build --------- Co-authored-by: nathan <nathan@appflowy.io> Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io> Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
173 lines
5.5 KiB
Dart
173 lines
5.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:appflowy/generated/locale_keys.g.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_plugins/plugins.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/workspace/application/view/prelude.dart';
|
|
import 'package:appflowy_backend/log.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.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_bloc/flutter_bloc.dart';
|
|
|
|
class DocumentPage extends StatefulWidget {
|
|
const DocumentPage({
|
|
super.key,
|
|
required this.view,
|
|
required this.onDeleted,
|
|
this.initialSelection,
|
|
});
|
|
|
|
final ViewPB view;
|
|
final VoidCallback onDeleted;
|
|
final Selection? initialSelection;
|
|
|
|
@override
|
|
State<DocumentPage> createState() => _DocumentPageState();
|
|
}
|
|
|
|
class _DocumentPageState extends State<DocumentPage> {
|
|
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(view: widget.view)
|
|
..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) {
|
|
widget.onDeleted();
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
return BlocListener<ActionNavigationBloc, ActionNavigationState>(
|
|
listenWhen: (_, curr) => curr.action != null,
|
|
listener: _onNotificationAction,
|
|
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: _buildCoverAndIcon(context, state.editorState!),
|
|
initialSelection: widget.initialSelection,
|
|
);
|
|
|
|
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 _buildBanner(BuildContext context) {
|
|
return DocumentBanner(
|
|
onRestore: () => context.read<DocumentBloc>().add(
|
|
const DocumentEvent.restorePage(),
|
|
),
|
|
onDelete: () => context.read<DocumentBloc>().add(
|
|
const DocumentEvent.deletePermanently(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildCoverAndIcon(BuildContext context, EditorState editorState) {
|
|
final page = editorState.document.root;
|
|
return DocumentHeaderNodeWidget(
|
|
node: page,
|
|
editorState: editorState,
|
|
view: widget.view,
|
|
onIconChanged: (icon) async {
|
|
await ViewBackendService.updateViewIcon(
|
|
viewId: widget.view.id,
|
|
viewIcon: icon,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
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.view.id == state.action?.objectId) {
|
|
editorState.updateSelectionWithReason(
|
|
Selection.collapsed(Position(path: [path])),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|