Mathias Mogensen c1006c18c3
feat: folder search mvp (#4665)
* 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

---------

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: Jiraffe7 <twajxjiraffe@gmail.com>
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-03-21 17:34:53 +01:00

271 lines
9.1 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/user/application/user_settings_service.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/notification/notification_service.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/notifications/notification_settings_cubit.dart';
import 'package:appflowy/workspace/application/sidebar/rename_view/rename_view_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/command_palette/command_palette.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'prelude.dart';
class InitAppWidgetTask extends LaunchTask {
const InitAppWidgetTask();
@override
LaunchTaskType get type => LaunchTaskType.appLauncher;
@override
Future<void> initialize(LaunchContext context) async {
WidgetsFlutterBinding.ensureInitialized();
await NotificationService.initialize();
final widget = context.getIt<EntryPoint>().create(context.config);
final appearanceSetting =
await UserSettingsBackendService().getAppearanceSetting();
final dateTimeSettings =
await UserSettingsBackendService().getDateTimeSettings();
// If the passed-in context is not the same as the context of the
// application widget, the application widget will be rebuilt.
final app = ApplicationWidget(
key: ValueKey(context),
appearanceSetting: appearanceSetting,
dateTimeSettings: dateTimeSettings,
appTheme: await appTheme(appearanceSetting.theme),
child: widget,
);
Bloc.observer = ApplicationBlocObserver();
runApp(
EasyLocalization(
supportedLocales: const [
// In alphabetical order
Locale('am', 'ET'),
Locale('ar', 'SA'),
Locale('ca', 'ES'),
Locale('cs', 'CZ'),
Locale('ckb', 'KU'),
Locale('de', 'DE'),
Locale('en'),
Locale('es', 'VE'),
Locale('eu', 'ES'),
Locale('el', 'GR'),
Locale('fr', 'FR'),
Locale('fr', 'CA'),
Locale('hu', 'HU'),
Locale('id', 'ID'),
Locale('it', 'IT'),
Locale('ja', 'JP'),
Locale('ko', 'KR'),
Locale('pl', 'PL'),
Locale('pt', 'BR'),
Locale('ru', 'RU'),
Locale('sv', 'SE'),
Locale('th', 'TH'),
Locale('tr', 'TR'),
Locale('uk', 'UA'),
Locale('ur'),
Locale('vi', 'VN'),
Locale('zh', 'CN'),
Locale('zh', 'TW'),
Locale('fa'),
Locale('hin'),
],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
useFallbackTranslations: true,
child: app,
),
);
return;
}
@override
Future<void> dispose() async {}
}
class ApplicationWidget extends StatefulWidget {
const ApplicationWidget({
super.key,
required this.child,
required this.appTheme,
required this.appearanceSetting,
required this.dateTimeSettings,
});
final Widget child;
final AppTheme appTheme;
final AppearanceSettingsPB appearanceSetting;
final DateTimeSettingsPB dateTimeSettings;
@override
State<ApplicationWidget> createState() => _ApplicationWidgetState();
}
class _ApplicationWidgetState extends State<ApplicationWidget> {
late final GoRouter routerConfig;
@override
void initState() {
super.initState();
// avoid rebuild routerConfig when the appTheme is changed.
routerConfig = generateRouter(widget.child);
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AppearanceSettingsCubit>(
create: (_) => AppearanceSettingsCubit(
widget.appearanceSetting,
widget.dateTimeSettings,
widget.appTheme,
)..readLocaleWhenAppLaunch(context),
),
BlocProvider<NotificationSettingsCubit>(
create: (_) => NotificationSettingsCubit(),
),
BlocProvider<DocumentAppearanceCubit>(
create: (_) => DocumentAppearanceCubit()..fetch(),
),
BlocProvider.value(value: getIt<RenameViewBloc>()),
BlocProvider.value(
value: getIt<ActionNavigationBloc>()
..add(const ActionNavigationEvent.initialize()),
),
BlocProvider.value(
value: getIt<ReminderBloc>()..add(const ReminderEvent.started()),
),
],
child: BlocListener<ActionNavigationBloc, ActionNavigationState>(
listenWhen: (_, curr) => curr.action != null,
listener: (context, state) {
final action = state.action;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (action?.type == ActionType.openView &&
PlatformExtension.isDesktop) {
final view = action!.arguments?[ActionArgumentKeys.view];
if (view != null) {
AppGlobals.rootNavKey.currentContext?.pushView(view);
}
} else if (action?.type == ActionType.openRow &&
PlatformExtension.isMobile) {
final view = action!.arguments?[ActionArgumentKeys.view];
if (view != null) {
final view = action.arguments?[ActionArgumentKeys.view];
final rowId = action.arguments?[ActionArgumentKeys.rowId];
AppGlobals.rootNavKey.currentContext?.pushView(view, {
PluginArgumentKeys.rowId: rowId,
});
}
}
});
},
child: BlocBuilder<AppearanceSettingsCubit, AppearanceSettingsState>(
builder: (context, state) {
_setSystemOverlayStyle(state);
return MaterialApp.router(
builder: (context, child) => MediaQuery(
// use the 1.0 as the textScaleFactor to avoid the text size
// affected by the system setting.
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(state.textScaleFactor),
),
child: overlayManagerBuilder(
context,
CommandPalette(
toggleNotifier: ValueNotifier<bool>(false),
child: child,
),
),
),
debugShowCheckedModeBanner: false,
theme: state.lightTheme,
darkTheme: state.darkTheme,
themeMode: state.themeMode,
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: state.locale,
routerConfig: routerConfig,
);
},
),
),
);
}
void _setSystemOverlayStyle(AppearanceSettingsState state) {
if (Platform.isAndroid) {
SystemUiOverlayStyle style = SystemUiOverlayStyle.dark;
final themeMode = state.themeMode;
if (themeMode == ThemeMode.dark) {
style = SystemUiOverlayStyle.light;
} else if (themeMode == ThemeMode.light) {
style = SystemUiOverlayStyle.dark;
} else {
final brightness = Theme.of(context).brightness;
// reverse the brightness of the system status bar.
style = brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;
}
SystemChrome.setSystemUIOverlayStyle(
style.copyWith(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
),
);
}
}
}
class AppGlobals {
// static GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey();
static GlobalKey<NavigatorState> rootNavKey = GlobalKey();
static NavigatorState get nav => rootNavKey.currentState!;
}
class ApplicationBlocObserver extends BlocObserver {
@override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
Log.debug(error);
super.onError(bloc, error, stackTrace);
}
}
Future<AppTheme> appTheme(String themeName) async {
if (themeName.isEmpty) {
return AppTheme.fallback;
} else {
try {
return await AppTheme.fromName(themeName);
} catch (e) {
return AppTheme.fallback;
}
}
}