2023-11-25 09:18:31 +00:00
|
|
|
import 'dart:async';
|
2022-02-19 15:19:33 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
2023-11-29 20:55:13 +00:00
|
|
|
import 'package:appflowy/env/cloud_env.dart';
|
2024-03-07 01:28:58 +00:00
|
|
|
import 'package:appflowy/startup/tasks/feature_flag_task.dart';
|
2023-07-02 15:37:30 +00:00
|
|
|
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
2023-01-08 04:10:53 +00:00
|
|
|
import 'package:appflowy_backend/appflowy_backend.dart';
|
2024-05-10 02:09:25 +00:00
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
2021-06-19 15:41:19 +00:00
|
|
|
import 'package:get_it/get_it.dart';
|
2024-01-03 03:41:29 +00:00
|
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
2022-12-20 03:14:42 +00:00
|
|
|
|
|
|
|
import 'deps_resolver.dart';
|
2023-08-17 15:46:39 +00:00
|
|
|
import 'entry_point.dart';
|
2022-12-20 03:14:42 +00:00
|
|
|
import 'launch_configuration.dart';
|
|
|
|
import 'plugin/plugin.dart';
|
|
|
|
import 'tasks/prelude.dart';
|
2021-06-19 15:41:19 +00:00
|
|
|
|
|
|
|
final getIt = GetIt.instance;
|
|
|
|
|
2021-09-12 14:19:59 +00:00
|
|
|
abstract class EntryPoint {
|
2022-12-20 03:14:42 +00:00
|
|
|
Widget create(LaunchConfiguration config);
|
2021-06-19 15:41:19 +00:00
|
|
|
}
|
|
|
|
|
2023-07-02 15:37:30 +00:00
|
|
|
class FlowyRunnerContext {
|
|
|
|
FlowyRunnerContext({required this.applicationDataDirectory});
|
2024-01-25 15:37:36 +00:00
|
|
|
|
|
|
|
final Directory applicationDataDirectory;
|
2023-07-02 15:37:30 +00:00
|
|
|
}
|
|
|
|
|
2023-12-25 18:03:42 +00:00
|
|
|
Future<void> runAppFlowy({bool isAnon = false}) async {
|
|
|
|
if (kReleaseMode) {
|
|
|
|
await FlowyRunner.run(
|
|
|
|
AppFlowyApplication(),
|
|
|
|
integrationMode(),
|
|
|
|
isAnon: isAnon,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// When running the app in integration test mode, we need to
|
|
|
|
// specify the mode to run the app again.
|
|
|
|
await FlowyRunner.run(
|
|
|
|
AppFlowyApplication(),
|
|
|
|
FlowyRunner.currentMode,
|
|
|
|
didInitGetItCallback: IntegrationTestHelper.didInitGetItCallback,
|
|
|
|
rustEnvsBuilder: IntegrationTestHelper.rustEnvsBuilder,
|
|
|
|
isAnon: isAnon,
|
|
|
|
);
|
|
|
|
}
|
2023-08-17 15:46:39 +00:00
|
|
|
}
|
|
|
|
|
2022-02-20 08:10:50 +00:00
|
|
|
class FlowyRunner {
|
2023-12-25 18:03:42 +00:00
|
|
|
// This variable specifies the initial mode of the app when it is launched for the first time.
|
|
|
|
// The same mode will be automatically applied in subsequent executions when the runAppFlowy()
|
|
|
|
// method is called.
|
2023-11-28 02:54:31 +00:00
|
|
|
static var currentMode = integrationMode();
|
|
|
|
|
2023-07-02 15:37:30 +00:00
|
|
|
static Future<FlowyRunnerContext> run(
|
2023-06-15 14:43:07 +00:00
|
|
|
EntryPoint f,
|
|
|
|
IntegrationMode mode, {
|
2023-11-28 02:54:31 +00:00
|
|
|
// This callback is triggered after the initialization of 'getIt',
|
|
|
|
// which is used for dependency injection throughout the app.
|
|
|
|
// If your functionality depends on 'getIt', ensure to register
|
|
|
|
// your callback here to execute any necessary actions post-initialization.
|
2023-11-28 23:49:47 +00:00
|
|
|
Future Function()? didInitGetItCallback,
|
2023-11-28 02:54:31 +00:00
|
|
|
// Passing the envs to the backend
|
|
|
|
Map<String, String> Function()? rustEnvsBuilder,
|
|
|
|
// Indicate whether the app is running in anonymous mode.
|
|
|
|
// Note: when the app is running in anonymous mode, the user no need to
|
|
|
|
// sign in, and the app will only save the data in the local storage.
|
|
|
|
bool isAnon = false,
|
2022-12-20 03:14:42 +00:00
|
|
|
}) async {
|
2023-11-28 02:54:31 +00:00
|
|
|
currentMode = mode;
|
2023-12-25 18:03:42 +00:00
|
|
|
|
|
|
|
// Only set the mode when it's not release mode
|
|
|
|
if (!kReleaseMode) {
|
|
|
|
IntegrationTestHelper.didInitGetItCallback = didInitGetItCallback;
|
|
|
|
IntegrationTestHelper.rustEnvsBuilder = rustEnvsBuilder;
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:32:01 +00:00
|
|
|
// Clear and dispose tasks from previous AppLaunch
|
|
|
|
if (getIt.isRegistered(instance: AppLauncher)) {
|
|
|
|
await getIt<AppLauncher>().dispose();
|
|
|
|
}
|
|
|
|
|
2022-12-20 03:14:42 +00:00
|
|
|
// Clear all the states in case of rebuilding.
|
|
|
|
await getIt.reset();
|
|
|
|
|
2023-11-28 02:54:31 +00:00
|
|
|
final config = LaunchConfiguration(
|
|
|
|
isAnon: isAnon,
|
2024-01-30 01:33:34 +00:00
|
|
|
// Unit test can't use the package_info_plus plugin
|
|
|
|
version: mode.isUnitTest
|
|
|
|
? '1.0.0'
|
|
|
|
: await PackageInfo.fromPlatform().then((value) => value.version),
|
2023-11-28 02:54:31 +00:00
|
|
|
rustEnvs: rustEnvsBuilder?.call() ?? {},
|
|
|
|
);
|
|
|
|
|
2021-11-19 07:23:58 +00:00
|
|
|
// Specify the env
|
2023-11-24 03:54:47 +00:00
|
|
|
await initGetIt(getIt, mode, f, config);
|
2023-11-28 23:49:47 +00:00
|
|
|
await didInitGetItCallback?.call();
|
2023-11-25 09:18:31 +00:00
|
|
|
|
2023-07-02 15:37:30 +00:00
|
|
|
final applicationDataDirectory =
|
|
|
|
await getIt<ApplicationDataStorage>().getPath().then(
|
|
|
|
(value) => Directory(value),
|
|
|
|
);
|
2023-05-21 10:53:59 +00:00
|
|
|
|
2021-06-19 15:41:19 +00:00
|
|
|
// add task
|
2023-05-17 03:03:33 +00:00
|
|
|
final launcher = getIt<AppLauncher>();
|
|
|
|
launcher.addTasks(
|
|
|
|
[
|
2023-07-09 03:03:22 +00:00
|
|
|
// this task should be first task, for handling platform errors.
|
|
|
|
// don't catch errors in test mode
|
|
|
|
if (!mode.isUnitTest) const PlatformErrorCatcherTask(),
|
2023-11-26 07:10:48 +00:00
|
|
|
// this task should be second task, for handling memory leak.
|
|
|
|
// there's a flag named _enable in memory_leak_detector.dart. If it's false, the task will be ignored.
|
|
|
|
MemoryLeakDetectorTask(),
|
2023-12-27 03:11:14 +00:00
|
|
|
const DebugTask(),
|
2024-03-07 01:28:58 +00:00
|
|
|
const FeatureFlagTask(),
|
2024-01-11 06:26:25 +00:00
|
|
|
|
2023-05-17 03:03:33 +00:00
|
|
|
// localization
|
|
|
|
const InitLocalizationTask(),
|
|
|
|
// init the app window
|
2024-05-10 02:09:25 +00:00
|
|
|
InitAppWindowTask(),
|
2023-05-17 03:03:33 +00:00
|
|
|
// Init Rust SDK
|
2023-11-20 12:54:47 +00:00
|
|
|
InitRustSDKTask(customApplicationPath: applicationDataDirectory),
|
2023-05-17 03:03:33 +00:00
|
|
|
// Load Plugins, like document, grid ...
|
|
|
|
const PluginLoadTask(),
|
|
|
|
|
|
|
|
// init the app widget
|
|
|
|
// ignore in test mode
|
2023-07-02 15:37:30 +00:00
|
|
|
if (!mode.isUnitTest) ...[
|
2024-01-11 06:26:25 +00:00
|
|
|
// The DeviceOrApplicationInfoTask should be placed before the AppWidgetTask to fetch the app information.
|
|
|
|
// It is unable to get the device information from the test environment.
|
2024-03-28 09:46:31 +00:00
|
|
|
const ApplicationInfoTask(),
|
2023-05-17 03:03:33 +00:00
|
|
|
const HotKeyTask(),
|
2023-11-29 20:55:13 +00:00
|
|
|
if (isSupabaseEnabled) InitSupabaseTask(),
|
|
|
|
if (isAppFlowyCloudEnabled) InitAppFlowyCloudTask(),
|
2023-05-17 03:03:33 +00:00
|
|
|
const InitAppWidgetTask(),
|
2023-11-14 14:33:07 +00:00
|
|
|
const InitPlatformServiceTask(),
|
2024-04-23 13:46:57 +00:00
|
|
|
const RecentServiceTask(),
|
2023-05-17 03:03:33 +00:00
|
|
|
],
|
|
|
|
],
|
|
|
|
);
|
|
|
|
await launcher.launch(); // execute the tasks
|
2023-07-02 15:37:30 +00:00
|
|
|
|
|
|
|
return FlowyRunnerContext(
|
|
|
|
applicationDataDirectory: applicationDataDirectory,
|
|
|
|
);
|
2021-06-19 15:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-11 05:15:41 +00:00
|
|
|
|
|
|
|
Future<void> initGetIt(
|
|
|
|
GetIt getIt,
|
2023-08-22 07:40:22 +00:00
|
|
|
IntegrationMode mode,
|
2022-02-20 00:35:52 +00:00
|
|
|
EntryPoint f,
|
2022-12-20 03:14:42 +00:00
|
|
|
LaunchConfiguration config,
|
2021-10-11 05:15:41 +00:00
|
|
|
) async {
|
2022-02-20 00:35:52 +00:00
|
|
|
getIt.registerFactory<EntryPoint>(() => f);
|
2023-12-27 03:42:39 +00:00
|
|
|
getIt.registerLazySingleton<FlowySDK>(
|
|
|
|
() {
|
|
|
|
return FlowySDK();
|
|
|
|
},
|
|
|
|
dispose: (sdk) async {
|
|
|
|
await sdk.dispose();
|
|
|
|
},
|
|
|
|
);
|
2022-12-20 03:14:42 +00:00
|
|
|
getIt.registerLazySingleton<AppLauncher>(
|
|
|
|
() => AppLauncher(
|
|
|
|
context: LaunchContext(
|
|
|
|
getIt,
|
2023-08-22 07:40:22 +00:00
|
|
|
mode,
|
2022-12-20 03:14:42 +00:00
|
|
|
config,
|
|
|
|
),
|
|
|
|
),
|
2023-10-24 15:13:51 +00:00
|
|
|
dispose: (launcher) async {
|
|
|
|
await launcher.dispose();
|
|
|
|
},
|
2022-12-20 03:14:42 +00:00
|
|
|
);
|
2022-02-28 14:38:53 +00:00
|
|
|
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
|
2021-10-11 05:15:41 +00:00
|
|
|
|
2023-08-22 07:40:22 +00:00
|
|
|
await DependencyResolver.resolve(getIt, mode);
|
2021-10-11 05:15:41 +00:00
|
|
|
}
|
2022-02-19 05:52:52 +00:00
|
|
|
|
|
|
|
class LaunchContext {
|
2024-01-25 15:37:36 +00:00
|
|
|
LaunchContext(this.getIt, this.env, this.config);
|
|
|
|
|
2022-02-19 05:52:52 +00:00
|
|
|
GetIt getIt;
|
2022-02-20 08:34:15 +00:00
|
|
|
IntegrationMode env;
|
2022-12-20 03:14:42 +00:00
|
|
|
LaunchConfiguration config;
|
2022-02-19 05:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum LaunchTaskType {
|
|
|
|
dataProcessing,
|
|
|
|
appLauncher,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The interface of an app launch task, which will trigger
|
|
|
|
/// some nonresident indispensable task in app launching task.
|
|
|
|
abstract class LaunchTask {
|
2023-05-17 03:03:33 +00:00
|
|
|
const LaunchTask();
|
|
|
|
|
2022-02-19 05:52:52 +00:00
|
|
|
LaunchTaskType get type => LaunchTaskType.dataProcessing;
|
2023-05-17 03:03:33 +00:00
|
|
|
|
2022-02-19 05:52:52 +00:00
|
|
|
Future<void> initialize(LaunchContext context);
|
2023-10-24 15:13:51 +00:00
|
|
|
Future<void> dispose();
|
2022-02-19 05:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class AppLauncher {
|
2023-05-17 03:03:33 +00:00
|
|
|
AppLauncher({
|
|
|
|
required this.context,
|
|
|
|
});
|
2022-02-19 05:52:52 +00:00
|
|
|
|
2022-12-20 03:14:42 +00:00
|
|
|
final LaunchContext context;
|
2023-05-17 03:03:33 +00:00
|
|
|
final List<LaunchTask> tasks = [];
|
2022-02-19 05:52:52 +00:00
|
|
|
|
|
|
|
void addTask(LaunchTask task) {
|
|
|
|
tasks.add(task);
|
|
|
|
}
|
|
|
|
|
2023-05-17 03:03:33 +00:00
|
|
|
void addTasks(Iterable<LaunchTask> tasks) {
|
|
|
|
this.tasks.addAll(tasks);
|
|
|
|
}
|
|
|
|
|
2022-02-19 09:12:44 +00:00
|
|
|
Future<void> launch() async {
|
2023-05-17 03:03:33 +00:00
|
|
|
for (final task in tasks) {
|
2022-02-19 05:52:52 +00:00
|
|
|
await task.initialize(context);
|
|
|
|
}
|
|
|
|
}
|
2023-10-24 15:13:51 +00:00
|
|
|
|
|
|
|
Future<void> dispose() async {
|
|
|
|
for (final task in tasks) {
|
|
|
|
await task.dispose();
|
|
|
|
}
|
|
|
|
tasks.clear();
|
|
|
|
}
|
2022-02-19 05:52:52 +00:00
|
|
|
}
|
2022-02-19 15:19:33 +00:00
|
|
|
|
2022-02-20 08:34:15 +00:00
|
|
|
enum IntegrationMode {
|
2022-02-19 15:19:33 +00:00
|
|
|
develop,
|
|
|
|
release,
|
2023-07-02 15:37:30 +00:00
|
|
|
unitTest,
|
|
|
|
integrationTest;
|
2022-02-19 15:19:33 +00:00
|
|
|
|
2023-07-02 15:37:30 +00:00
|
|
|
// test mode
|
|
|
|
bool get isTest => isUnitTest || isIntegrationTest;
|
|
|
|
bool get isUnitTest => this == IntegrationMode.unitTest;
|
|
|
|
bool get isIntegrationTest => this == IntegrationMode.integrationTest;
|
2023-06-15 14:43:07 +00:00
|
|
|
|
2023-07-02 15:37:30 +00:00
|
|
|
// release mode
|
|
|
|
bool get isRelease => this == IntegrationMode.release;
|
|
|
|
|
|
|
|
// develop mode
|
|
|
|
bool get isDevelop => this == IntegrationMode.develop;
|
2022-02-19 15:19:33 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 07:40:22 +00:00
|
|
|
IntegrationMode integrationMode() {
|
2022-02-19 15:19:33 +00:00
|
|
|
if (Platform.environment.containsKey('FLUTTER_TEST')) {
|
2023-07-02 15:37:30 +00:00
|
|
|
return IntegrationMode.unitTest;
|
2022-02-19 15:19:33 +00:00
|
|
|
}
|
2022-02-20 01:58:25 +00:00
|
|
|
|
|
|
|
if (kReleaseMode) {
|
2022-02-20 08:34:15 +00:00
|
|
|
return IntegrationMode.release;
|
2022-02-19 15:19:33 +00:00
|
|
|
}
|
|
|
|
|
2022-02-20 08:34:15 +00:00
|
|
|
return IntegrationMode.develop;
|
2022-02-19 15:19:33 +00:00
|
|
|
}
|
2023-12-25 18:03:42 +00:00
|
|
|
|
|
|
|
/// Only used for integration test
|
|
|
|
class IntegrationTestHelper {
|
|
|
|
static Future Function()? didInitGetItCallback;
|
|
|
|
static Map<String, String> Function()? rustEnvsBuilder;
|
|
|
|
}
|