From e2359cf047f30b23c47c621cd7d0232a334c37c7 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 13 Aug 2024 20:01:32 +0800 Subject: [PATCH 1/4] fix: row property align issue (#5950) * fix: row property align issue * fix: generate_freezed.sh path warning * Revert "fix: generate_freezed.sh path warning" This reverts commit 7c0a4a3177702d5b58858296133876a04ebe274d. * fix: generate_freezed.sh path warning * chore: improve chat page mobile UI --- frontend/appflowy_flutter/ios/Podfile.lock | 6 ----- .../widgets/mobile_row_property_list.dart | 1 + .../presentation/chat_input/chat_input.dart | 12 +++++---- .../lib/startup/tasks/generate_router.dart | 27 ++++++++++++++++--- frontend/appflowy_flutter/pubspec.lock | 4 +-- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../freezed/generate_freezed.sh | 3 ++- 7 files changed, 37 insertions(+), 18 deletions(-) diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index c54ae23ed6..d7647a9d4a 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -48,8 +48,6 @@ PODS: - fluttertoast (0.0.2): - Flutter - Toast - - image_gallery_saver (2.0.2): - - Flutter - image_picker_ios (0.0.1): - Flutter - integration_test (0.0.1): @@ -95,7 +93,6 @@ DEPENDENCIES: - flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`) @@ -136,8 +133,6 @@ EXTERNAL SOURCES: :path: Flutter fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" - image_gallery_saver: - :path: ".symlinks/plugins/image_gallery_saver/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: @@ -176,7 +171,6 @@ SPEC CHECKSUMS: flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c - image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425 integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9 diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart index 0498427547..7c26879a4f 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/mobile_row_property_list.dart @@ -87,6 +87,7 @@ class _PropertyCellState extends State<_PropertyCell> { fieldInfo.name, overflow: TextOverflow.ellipsis, fontSize: 14, + figmaLineHeight: 16.0, color: Theme.of(context).hintColor, ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input.dart index 60de3fd528..54c70d61cc 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input.dart @@ -12,6 +12,7 @@ import 'package:extended_text_field/extended_text_field.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/platform_extension.dart'; import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -21,8 +22,8 @@ import 'package:flutter_chat_ui/flutter_chat_ui.dart'; import 'chat_at_button.dart'; import 'chat_input_attachment.dart'; -import 'chat_send_button.dart'; import 'chat_input_span.dart'; +import 'chat_send_button.dart'; import 'layout_define.dart'; class ChatInput extends StatefulWidget { @@ -114,7 +115,7 @@ class _ChatInputState extends State { child: Container( decoration: BoxDecoration( border: Border.all( - color: _inputFocusNode.hasFocus && !isMobile + color: _inputFocusNode.hasFocus ? Theme.of(context).colorScheme.primary.withOpacity(0.6) : Theme.of(context).colorScheme.secondary, ), @@ -161,9 +162,9 @@ class _ChatInputState extends State { Expanded(child: _inputTextField(context, textPadding)), // mention button - // TODO(lucas): support mobile - if (PlatformExtension.isDesktop) - _mentionButton(buttonPadding), + _mentionButton(buttonPadding), + + if (PlatformExtension.isMobile) const HSpace(6.0), // send button _sendButton(buttonPadding), @@ -245,6 +246,7 @@ class _ChatInputState extends State { InputDecoration _buildInputDecoration(BuildContext context) { return InputDecoration( border: InputBorder.none, + enabledBorder: InputBorder.none, hintText: widget.hintText, focusedBorder: InputBorder.none, hintStyle: TextStyle( diff --git a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart index 127f365bf6..7e10166fe4 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart @@ -31,6 +31,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/m import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flowy_infra/time/duration.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sheet/route.dart'; @@ -558,10 +559,25 @@ GoRoute _mobileCardDetailScreenRoute() { parentNavigatorKey: AppGlobals.rootNavKey, path: MobileRowDetailPage.routeName, pageBuilder: (context, state) { - final args = state.extra as Map; + var extra = state.extra as Map?; + + if (kDebugMode && extra == null) { + extra = _dynamicValues; + } + + if (extra == null) { + return const MaterialExtendedPage( + child: SizedBox.shrink(), + ); + } + final databaseController = - args[MobileRowDetailPage.argDatabaseController]; - final rowId = args[MobileRowDetailPage.argRowId]!; + extra[MobileRowDetailPage.argDatabaseController]; + final rowId = extra[MobileRowDetailPage.argRowId]!; + + if (kDebugMode) { + _dynamicValues = extra; + } return MaterialExtendedPage( child: MobileRowDetailPage( @@ -629,3 +645,8 @@ Widget _buildFadeTransition( Duration _slowDuration = Duration( milliseconds: RouteDurations.slow.inMilliseconds.round(), ); + +// ONLY USE IN DEBUG MODE +// this is a workaround for the issue of GoRouter not supporting extra with complex types +// https://github.com/flutter/flutter/issues/137248 +Map _dynamicValues = {}; diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 183b341472..5d16830e41 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -890,10 +890,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "170c46e237d6eb0e6e9f0e8b3f56101e14fb64f787016e42edd74c39cf8b176a" + sha256: ddc16d34b0d74cb313986918c0f0885a7ba2fc24d8fb8419de75f0015144ccfe url: "https://pub.dev" source: hosted - version: "13.2.0" + version: "14.2.3" google_fonts: dependency: "direct main" description: diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 185ee51a4f..b8caf2c183 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -99,7 +99,7 @@ dependencies: url_protocol: hive_flutter: ^1.1.0 super_clipboard: ^0.8.4 - go_router: ^13.1.0 + go_router: ^14.2.0 string_validator: ^1.0.0 unsplash_client: ^2.1.1 flutter_emoji_mart: diff --git a/frontend/scripts/code_generation/freezed/generate_freezed.sh b/frontend/scripts/code_generation/freezed/generate_freezed.sh index 4b7ded87c8..391aea08b2 100755 --- a/frontend/scripts/code_generation/freezed/generate_freezed.sh +++ b/frontend/scripts/code_generation/freezed/generate_freezed.sh @@ -71,10 +71,11 @@ if [ "$exclude_packages" = false ]; then # Navigate back to the packages directory cd .. done + + cd .. fi # Navigate to the appflowy_flutter directory and generate files -cd .. echo "🧊 Start generating freezed files (AppFlowy)." if [ "$skip_pub_packages_get" = false ]; then From 463c8c7ee4d75658a46b55bd6dfd87ec0c2bcb97 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 13 Aug 2024 23:36:44 +0800 Subject: [PATCH 2/4] feat: enable local set (#5955) * chore: enable local set * chore: fix test * chore: clippy * chore: fix tauri build * chore: fix tauri build --- frontend/appflowy_tauri/src-tauri/src/init.rs | 15 +- .../appflowy_tauri/src-tauri/src/request.rs | 8 +- .../appflowy_web_app/src-tauri/src/init.rs | 40 +++-- .../appflowy_web_app/src-tauri/src/request.rs | 8 +- frontend/rust-lib/dart-ffi/Cargo.toml | 2 +- frontend/rust-lib/dart-ffi/src/lib.rs | 9 +- .../src/event_builder.rs | 13 +- .../event-integration-test/src/lib.rs | 11 +- .../af_cloud_test/file_upload_test.rs | 28 ++-- .../folder/local_test/subscription_test.rs | 63 +++----- .../user/af_cloud_test/workspace_test.rs | 22 ++- .../rust-lib/flowy-ai/src/event_handler.rs | 28 ++-- .../src/local_ai/local_llm_resource.rs | 2 +- frontend/rust-lib/flowy-core/src/lib.rs | 22 ++- frontend/rust-lib/lib-dispatch/Cargo.toml | 2 +- .../rust-lib/lib-dispatch/src/dispatcher.rs | 146 +++++++++--------- .../lib-dispatch/src/module/module.rs | 24 +-- frontend/rust-lib/lib-dispatch/src/runtime.rs | 58 +++---- .../lib-dispatch/src/service/boxed.rs | 8 +- .../rust-lib/lib-dispatch/tests/api/module.rs | 6 +- 20 files changed, 250 insertions(+), 265 deletions(-) diff --git a/frontend/appflowy_tauri/src-tauri/src/init.rs b/frontend/appflowy_tauri/src-tauri/src/init.rs index 7591ba37ff..636735e5f4 100644 --- a/frontend/appflowy_tauri/src-tauri/src/init.rs +++ b/frontend/appflowy_tauri/src-tauri/src/init.rs @@ -1,7 +1,7 @@ use flowy_core::config::AppFlowyCoreConfig; -use flowy_core::{AppFlowyCore, DEFAULT_NAME}; +use flowy_core::{AppFlowyCore, MutexAppFlowyCore, DEFAULT_NAME}; use lib_dispatch::runtime::AFPluginRuntime; -use std::sync::Arc; +use std::rc::Rc; use dotenv::dotenv; @@ -25,7 +25,7 @@ pub fn read_env() { } } -pub fn init_flowy_core() -> AppFlowyCore { +pub fn init_flowy_core() -> MutexAppFlowyCore { let config_json = include_str!("../tauri.conf.json"); let config: tauri_utils::config::Config = serde_json::from_str(config_json).unwrap(); @@ -35,7 +35,8 @@ pub fn init_flowy_core() -> AppFlowyCore { .clone() .map(|v| v.to_string()) .unwrap_or_else(|| "0.5.8".to_string()); - let app_version = semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); + let app_version = + semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); let mut data_path = tauri::api::path::app_local_data_dir(&config).unwrap(); if cfg!(debug_assertions) { data_path.push("data_dev"); @@ -60,7 +61,9 @@ pub fn init_flowy_core() -> AppFlowyCore { ) .log_filter("trace", vec!["appflowy_tauri".to_string()]); - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); + let runtime = Rc::new(AFPluginRuntime::new().unwrap()); let cloned_runtime = runtime.clone(); - runtime.block_on(async move { AppFlowyCore::new(config, cloned_runtime, None).await }) + runtime.block_on(async move { + MutexAppFlowyCore::new(AppFlowyCore::new(config, cloned_runtime, None).await) + }) } diff --git a/frontend/appflowy_tauri/src-tauri/src/request.rs b/frontend/appflowy_tauri/src-tauri/src/request.rs index 029e71c18c..6d2d01fb6e 100644 --- a/frontend/appflowy_tauri/src-tauri/src/request.rs +++ b/frontend/appflowy_tauri/src-tauri/src/request.rs @@ -1,4 +1,4 @@ -use flowy_core::AppFlowyCore; +use flowy_core::MutexAppFlowyCore; use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, }; @@ -38,8 +38,8 @@ pub async fn invoke_request( app_handler: AppHandle, ) -> AFTauriResponse { let request: AFPluginRequest = request.into(); - let state: State = app_handler.state(); - let dispatcher = state.inner().dispatcher(); - let response = AFPluginDispatcher::async_send(dispatcher.as_ref(), request).await; + let state: State = app_handler.state(); + let dispatcher = state.0.lock().dispatcher(); + let response = AFPluginDispatcher::sync_send(dispatcher, request); response.into() } diff --git a/frontend/appflowy_web_app/src-tauri/src/init.rs b/frontend/appflowy_web_app/src-tauri/src/init.rs index 42c857abdf..636735e5f4 100644 --- a/frontend/appflowy_web_app/src-tauri/src/init.rs +++ b/frontend/appflowy_web_app/src-tauri/src/init.rs @@ -1,7 +1,7 @@ use flowy_core::config::AppFlowyCoreConfig; -use flowy_core::{AppFlowyCore, DEFAULT_NAME}; +use flowy_core::{AppFlowyCore, MutexAppFlowyCore, DEFAULT_NAME}; use lib_dispatch::runtime::AFPluginRuntime; -use std::sync::Arc; +use std::rc::Rc; use dotenv::dotenv; @@ -9,28 +9,34 @@ pub fn read_env() { dotenv().ok(); let env = if cfg!(debug_assertions) { - include_str!("../env.development") + include_str!("../env.development") } else { - include_str!("../env.production") + include_str!("../env.production") }; for line in env.lines() { - if let Some((key, value)) = line.split_once('=') { - // Check if the environment variable is not already set in the system - let current_value = std::env::var(key).unwrap_or_default(); - if current_value.is_empty() { - std::env::set_var(key, value); - } + if let Some((key, value)) = line.split_once('=') { + // Check if the environment variable is not already set in the system + let current_value = std::env::var(key).unwrap_or_default(); + if current_value.is_empty() { + std::env::set_var(key, value); } + } } } -pub fn init_flowy_core() -> AppFlowyCore { +pub fn init_flowy_core() -> MutexAppFlowyCore { let config_json = include_str!("../tauri.conf.json"); let config: tauri_utils::config::Config = serde_json::from_str(config_json).unwrap(); - let app_version = config.package.version.clone().map(|v| v.to_string()).unwrap_or_else(|| "0.0.0".to_string()); - let app_version = semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); + let app_version = config + .package + .version + .clone() + .map(|v| v.to_string()) + .unwrap_or_else(|| "0.5.8".to_string()); + let app_version = + semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); let mut data_path = tauri::api::path::app_local_data_dir(&config).unwrap(); if cfg!(debug_assertions) { data_path.push("data_dev"); @@ -50,12 +56,14 @@ pub fn init_flowy_core() -> AppFlowyCore { custom_application_path, application_path, device_id, - "web".to_string(), + "tauri".to_string(), DEFAULT_NAME.to_string(), ) .log_filter("trace", vec!["appflowy_tauri".to_string()]); - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); + let runtime = Rc::new(AFPluginRuntime::new().unwrap()); let cloned_runtime = runtime.clone(); - runtime.block_on(async move { AppFlowyCore::new(config, cloned_runtime, None).await }) + runtime.block_on(async move { + MutexAppFlowyCore::new(AppFlowyCore::new(config, cloned_runtime, None).await) + }) } diff --git a/frontend/appflowy_web_app/src-tauri/src/request.rs b/frontend/appflowy_web_app/src-tauri/src/request.rs index 029e71c18c..0ec6a8dadc 100644 --- a/frontend/appflowy_web_app/src-tauri/src/request.rs +++ b/frontend/appflowy_web_app/src-tauri/src/request.rs @@ -1,4 +1,4 @@ -use flowy_core::AppFlowyCore; +use flowy_core::{AppFlowyCore, MutexAppFlowyCore}; use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, }; @@ -38,8 +38,8 @@ pub async fn invoke_request( app_handler: AppHandle, ) -> AFTauriResponse { let request: AFPluginRequest = request.into(); - let state: State = app_handler.state(); - let dispatcher = state.inner().dispatcher(); - let response = AFPluginDispatcher::async_send(dispatcher.as_ref(), request).await; + let state: State = app_handler.state(); + let dispatcher = state.0.lock().dispatcher(); + let response = AFPluginDispatcher::sync_send(dispatcher, request); response.into() } diff --git a/frontend/rust-lib/dart-ffi/Cargo.toml b/frontend/rust-lib/dart-ffi/Cargo.toml index bca0489e7b..22e07f3483 100644 --- a/frontend/rust-lib/dart-ffi/Cargo.toml +++ b/frontend/rust-lib/dart-ffi/Cargo.toml @@ -28,7 +28,7 @@ lib-log.workspace = true semver = "1.0.22" # workspace -lib-dispatch = { workspace = true } +lib-dispatch = { workspace = true, features = ["local_set"] } # Core #flowy-core = { workspace = true, features = ["profiling"] } diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs index aecd9bef28..14b5a13a24 100644 --- a/frontend/rust-lib/dart-ffi/src/lib.rs +++ b/frontend/rust-lib/dart-ffi/src/lib.rs @@ -4,6 +4,7 @@ use allo_isolate::Isolate; use lazy_static::lazy_static; use parking_lot::Mutex; use semver::Version; +use std::rc::Rc; use std::sync::Arc; use std::{ffi::CStr, os::raw::c_char}; use tracing::{debug, error, info, trace, warn}; @@ -37,14 +38,14 @@ lazy_static! { static ref LOG_STREAM_ISOLATE: Mutex> = Mutex::new(None); } -struct MutexAppFlowyCore(Arc>>); +struct MutexAppFlowyCore(Rc>>); impl MutexAppFlowyCore { fn new() -> Self { - Self(Arc::new(Mutex::new(None))) + Self(Rc::new(Mutex::new(None))) } - fn dispatcher(&self) -> Option> { + fn dispatcher(&self) -> Option> { let binding = self.0.lock(); let core = binding.as_ref(); core.map(|core| core.event_dispatcher.clone()) @@ -90,7 +91,7 @@ pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 { core.close_db(); } - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); + let runtime = Rc::new(AFPluginRuntime::new().unwrap()); let cloned_runtime = runtime.clone(); let log_stream = LOG_STREAM_ISOLATE diff --git a/frontend/rust-lib/event-integration-test/src/event_builder.rs b/frontend/rust-lib/event-integration-test/src/event_builder.rs index 0d083b1037..5168723981 100644 --- a/frontend/rust-lib/event-integration-test/src/event_builder.rs +++ b/frontend/rust-lib/event-integration-test/src/event_builder.rs @@ -1,13 +1,12 @@ +use flowy_user::errors::{internal_error, FlowyError}; +use lib_dispatch::prelude::{ + AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *, +}; +use std::rc::Rc; use std::{ convert::TryFrom, fmt::{Debug, Display}, hash::Hash, - sync::Arc, -}; - -use flowy_user::errors::{internal_error, FlowyError}; -use lib_dispatch::prelude::{ - AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *, }; use crate::EventIntegrationTest; @@ -86,7 +85,7 @@ impl EventBuilder { .map(|data| data.into_inner()) } - fn dispatch(&self) -> Arc { + fn dispatch(&self) -> Rc { self.context.sdk.dispatcher() } diff --git a/frontend/rust-lib/event-integration-test/src/lib.rs b/frontend/rust-lib/event-integration-test/src/lib.rs index cd2a01d84f..e368c4168c 100644 --- a/frontend/rust-lib/event-integration-test/src/lib.rs +++ b/frontend/rust-lib/event-integration-test/src/lib.rs @@ -5,6 +5,7 @@ use collab_document::document::Document; use collab_entity::CollabType; use std::env::temp_dir; use std::path::PathBuf; +use std::rc::Rc; use std::sync::Arc; use std::time::Duration; @@ -163,13 +164,9 @@ pub fn document_from_document_doc_state(doc_id: &str, doc_state: Vec) -> Doc } async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore { - std::thread::spawn(|| { - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); - let cloned_runtime = runtime.clone(); - runtime.block_on(async move { AppFlowyCore::new(config, cloned_runtime, None).await }) - }) - .join() - .unwrap() + let runtime = Rc::new(AFPluginRuntime::new().unwrap()); + let cloned_runtime = runtime.clone(); + AppFlowyCore::new(config, cloned_runtime, None).await } impl std::ops::Deref for EventIntegrationTest { diff --git a/frontend/rust-lib/event-integration-test/tests/document/af_cloud_test/file_upload_test.rs b/frontend/rust-lib/event-integration-test/tests/document/af_cloud_test/file_upload_test.rs index cf0050341e..a2ab2d1245 100644 --- a/frontend/rust-lib/event-integration-test/tests/document/af_cloud_test/file_upload_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/document/af_cloud_test/file_upload_test.rs @@ -94,30 +94,24 @@ async fn af_cloud_upload_6_files_test() { // Wait for all uploads to finish let uploads = Arc::new(Mutex::new(created_uploads)); let mut handles = vec![]; + for mut receiver in receivers { let cloned_uploads = uploads.clone(); - let cloned_test = test.clone(); + let state = test.storage_manager.get_file_state(&receiver.file_id).await; let handle = tokio::spawn(async move { - if let Some(state) = cloned_test - .storage_manager - .get_file_state(&receiver.file_id) - .await - { - if let FileUploadState::Finished { file_id } = state { + if let Some(FileUploadState::Finished { file_id }) = state { + cloned_uploads + .lock() + .await + .retain(|upload| upload.file_id != file_id); + } + while let Some(value) = receiver.recv().await { + if let FileUploadState::Finished { file_id } = value { cloned_uploads .lock() .await .retain(|upload| upload.file_id != file_id); - } - } else { - while let Some(value) = receiver.recv().await { - if let FileUploadState::Finished { file_id } = value { - cloned_uploads - .lock() - .await - .retain(|upload| upload.file_id != file_id); - break; - } + break; } } }); diff --git a/frontend/rust-lib/event-integration-test/tests/folder/local_test/subscription_test.rs b/frontend/rust-lib/event-integration-test/tests/folder/local_test/subscription_test.rs index 089bbae7ba..c9460d9db0 100644 --- a/frontend/rust-lib/event-integration-test/tests/folder/local_test/subscription_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/folder/local_test/subscription_test.rs @@ -24,11 +24,9 @@ async fn create_child_view_in_workspace_subscription_test() { let cloned_test = test.clone(); let cloned_workspace_id = workspace.id.clone(); - test.appflowy_core.dispatcher().spawn(async move { - cloned_test - .create_view(&cloned_workspace_id, "workspace child view".to_string()) - .await; - }); + cloned_test + .create_view(&cloned_workspace_id, "workspace child view".to_string()) + .await; let views = receive_with_timeout(rx, Duration::from_secs(30)) .await @@ -50,14 +48,17 @@ async fn create_child_view_in_view_subscription_test() { let cloned_test = test.clone(); let child_view_id = workspace_child_view.id.clone(); - test.appflowy_core.dispatcher().spawn(async move { - cloned_test - .create_view( - &child_view_id, - "workspace child view's child view".to_string(), - ) - .await; - }); + let local_set = tokio::task::LocalSet::new(); + local_set + .run_until(async move { + cloned_test + .create_view( + &child_view_id, + "workspace child view's child view".to_string(), + ) + .await; + }) + .await; let update = receive_with_timeout(rx, Duration::from_secs(30)) .await @@ -81,22 +82,11 @@ async fn delete_view_subscription_test() { let cloned_test = test.clone(); let delete_view_id = workspace.views.first().unwrap().id.clone(); let cloned_delete_view_id = delete_view_id.clone(); - test - .appflowy_core - .dispatcher() - .spawn(async move { - cloned_test.delete_view(&cloned_delete_view_id).await; - }) + + cloned_test.delete_view(&cloned_delete_view_id).await; + let update = receive_with_timeout(rx, Duration::from_secs(60)) .await .unwrap(); - - let update = test - .appflowy_core - .dispatcher() - .run_until(receive_with_timeout(rx, Duration::from_secs(60))) - .await - .unwrap(); - assert_eq!(update.delete_child_views.len(), 1); assert_eq!(update.delete_child_views[0], delete_view_id); } @@ -114,17 +104,14 @@ async fn update_view_subscription_test() { assert!(!view.is_favorite); let update_view_id = view.id.clone(); - test.appflowy_core.dispatcher().spawn(async move { - cloned_test - .update_view(UpdateViewPayloadPB { - view_id: update_view_id, - name: Some("hello world".to_string()), - is_favorite: Some(true), - ..Default::default() - }) - .await; - }); - + cloned_test + .update_view(UpdateViewPayloadPB { + view_id: update_view_id, + name: Some("hello world".to_string()), + is_favorite: Some(true), + ..Default::default() + }) + .await; let update = receive_with_timeout(rx, Duration::from_secs(30)) .await .unwrap(); diff --git a/frontend/rust-lib/event-integration-test/tests/user/af_cloud_test/workspace_test.rs b/frontend/rust-lib/event-integration-test/tests/user/af_cloud_test/workspace_test.rs index b72ceba33f..c35224ea99 100644 --- a/frontend/rust-lib/event-integration-test/tests/user/af_cloud_test/workspace_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/user/af_cloud_test/workspace_test.rs @@ -5,6 +5,7 @@ use collab_folder::Folder; use event_integration_test::user_event::user_localhost_af_cloud; use event_integration_test::EventIntegrationTest; use std::time::Duration; +use tokio::task::LocalSet; use tokio::time::sleep; use crate::user::af_cloud_test::util::get_synced_workspaces; @@ -158,9 +159,9 @@ async fn af_cloud_different_open_same_workspace_test() { user_localhost_af_cloud().await; // Set up the primary client and sign them up to the cloud. - let client_1 = EventIntegrationTest::new().await; - let owner_profile = client_1.af_cloud_sign_up().await; - let shared_workspace_id = client_1.get_current_workspace().await.id.clone(); + let test_runner = EventIntegrationTest::new().await; + let owner_profile = test_runner.af_cloud_sign_up().await; + let shared_workspace_id = test_runner.get_current_workspace().await.id.clone(); // Verify that the workspace ID from the profile matches the current session's workspace ID. assert_eq!(shared_workspace_id, owner_profile.workspace_id); @@ -181,7 +182,7 @@ async fn af_cloud_different_open_same_workspace_test() { client.delete_view(&view.id).await; } - client_1 + test_runner .add_workspace_member(&owner_profile.workspace_id, &client) .await; clients.push((client, client_profile)); @@ -195,9 +196,10 @@ async fn af_cloud_different_open_same_workspace_test() { // Simulate each client open different workspace 30 times let mut handles = vec![]; + let local_set = LocalSet::new(); for client in clients.clone() { let cloned_shared_workspace_id = shared_workspace_id.clone(); - let handle = tokio::spawn(async move { + let handle = local_set.spawn_local(async move { let (client, profile) = client; let all_workspaces = get_synced_workspaces(&client, profile.id).await; for i in 0..30 { @@ -216,10 +218,16 @@ async fn af_cloud_different_open_same_workspace_test() { }); handles.push(handle); } - futures::future::join_all(handles).await; + let results = local_set + .run_until(futures::future::join_all(handles)) + .await; + + for result in results { + assert!(result.is_ok()); + } // Retrieve and verify the collaborative document state for Client 1's workspace. - let doc_state = client_1 + let doc_state = test_runner .get_collab_doc_state(&shared_workspace_id, CollabType::Folder) .await .unwrap(); diff --git a/frontend/rust-lib/flowy-ai/src/event_handler.rs b/frontend/rust-lib/flowy-ai/src/event_handler.rs index 21f14070f4..3ec6eeb660 100644 --- a/frontend/rust-lib/flowy-ai/src/event_handler.rs +++ b/frontend/rust-lib/flowy-ai/src/event_handler.rs @@ -63,24 +63,18 @@ pub(crate) async fn stream_chat_message_handler( .collect::>(); trace!("Stream chat message with metadata: {:?}", metadata); - let (tx, rx) = oneshot::channel::>(); let ai_manager = upgrade_ai_manager(ai_manager)?; - tokio::spawn(async move { - let result = ai_manager - .stream_chat_message( - &data.chat_id, - &data.message, - message_type, - data.answer_stream_port, - data.question_stream_port, - metadata, - ) - .await; - let _ = tx.send(result); - }); - - let question = rx.await??; - data_result_ok(question) + let result = ai_manager + .stream_chat_message( + &data.chat_id, + &data.message, + message_type, + data.answer_stream_port, + data.question_stream_port, + metadata, + ) + .await?; + data_result_ok(result) } #[tracing::instrument(level = "debug", skip_all, err)] diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs index 5528a00ac5..457322a111 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs @@ -288,7 +288,7 @@ impl LocalAIResourceController { while let Ok(value) = rx.recv().await { let is_finish = value == DOWNLOAD_FINISH; if let Err(err) = progress_sink.send(value).await { - error!("Failed to send progress: {:?}", err); + warn!("Failed to send progress: {:?}", err); break; } diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index 761708bfe5..ae5b1d801d 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -2,6 +2,8 @@ use flowy_search::folder::indexer::FolderIndexManagerImpl; use flowy_search::services::manager::SearchManager; +use parking_lot::Mutex; +use std::rc::Rc; use std::sync::{Arc, Weak}; use std::time::Duration; use sysinfo::System; @@ -54,7 +56,7 @@ pub struct AppFlowyCore { pub document_manager: Arc, pub folder_manager: Arc, pub database_manager: Arc, - pub event_dispatcher: Arc, + pub event_dispatcher: Rc, pub server_provider: Arc, pub task_dispatcher: Arc>, pub store_preference: Arc, @@ -66,7 +68,7 @@ pub struct AppFlowyCore { impl AppFlowyCore { pub async fn new( config: AppFlowyCoreConfig, - runtime: Arc, + runtime: Rc, stream_log_sender: Option>, ) -> Self { let platform = OperatingSystem::from(&config.platform); @@ -102,7 +104,7 @@ impl AppFlowyCore { } #[instrument(skip(config, runtime))] - async fn init(config: AppFlowyCoreConfig, runtime: Arc) -> Self { + async fn init(config: AppFlowyCoreConfig, runtime: Rc) -> Self { // Init the key value database let store_preference = Arc::new(KVStorePreferences::new(&config.storage_path).unwrap()); info!("🔥{:?}", &config); @@ -261,7 +263,7 @@ impl AppFlowyCore { error!("Init user failed: {}", err) } } - let event_dispatcher = Arc::new(AFPluginDispatcher::new( + let event_dispatcher = Rc::new(AFPluginDispatcher::new( runtime, make_plugins( Arc::downgrade(&folder_manager), @@ -290,7 +292,7 @@ impl AppFlowyCore { } /// Only expose the dispatcher in test - pub fn dispatcher(&self) -> Arc { + pub fn dispatcher(&self) -> Rc { self.event_dispatcher.clone() } } @@ -321,3 +323,13 @@ impl ServerUser for ServerUserImpl { self.upgrade_user()?.workspace_id() } } + +pub struct MutexAppFlowyCore(pub Rc>); + +impl MutexAppFlowyCore { + pub fn new(appflowy_core: AppFlowyCore) -> Self { + Self(Rc::new(Mutex::new(appflowy_core))) + } +} +unsafe impl Sync for MutexAppFlowyCore {} +unsafe impl Send for MutexAppFlowyCore {} diff --git a/frontend/rust-lib/lib-dispatch/Cargo.toml b/frontend/rust-lib/lib-dispatch/Cargo.toml index f81eb2d084..0d835915c7 100644 --- a/frontend/rust-lib/lib-dispatch/Cargo.toml +++ b/frontend/rust-lib/lib-dispatch/Cargo.toml @@ -42,7 +42,7 @@ tokio = { workspace = true, features = ["rt"] } futures-util = "0.3.26" [features] -default = ["use_protobuf"] +default = ["local_set", "use_protobuf"] use_serde = ["bincode", "serde_json", "serde", "serde_repr"] use_protobuf = ["protobuf"] local_set = [] diff --git a/frontend/rust-lib/lib-dispatch/src/dispatcher.rs b/frontend/rust-lib/lib-dispatch/src/dispatcher.rs index eb55bfc4fa..e3e72ff2be 100644 --- a/frontend/rust-lib/lib-dispatch/src/dispatcher.rs +++ b/frontend/rust-lib/lib-dispatch/src/dispatcher.rs @@ -1,10 +1,10 @@ -use std::any::Any; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{future::Future, sync::Arc}; - use derivative::*; use pin_project::pin_project; +use std::any::Any; +use std::future::Future; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; use tracing::event; use crate::module::AFPluginStateMap; @@ -16,60 +16,50 @@ use crate::{ service::{AFPluginServiceFactory, Service}, }; -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub trait AFConcurrent {} -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] impl AFConcurrent for T where T: ?Sized {} -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub trait AFConcurrent: Send + Sync {} -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] impl AFConcurrent for T where T: Send + Sync {} -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub type AFBoxFuture<'a, T> = futures_core::future::LocalBoxFuture<'a, T>; -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub type AFBoxFuture<'a, T> = futures_core::future::BoxFuture<'a, T>; pub type AFStateMap = std::sync::Arc; -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub(crate) fn downcast_owned(boxed: AFBox) -> Option { boxed.downcast().ok().map(|boxed| *boxed) } -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub(crate) fn downcast_owned(boxed: AFBox) -> Option { boxed.downcast().ok().map(|boxed| *boxed) } -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub(crate) type AFBox = Box; -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub(crate) type AFBox = Box; -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub type BoxFutureCallback = Box AFBoxFuture<'static, ()> + 'static>; -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub type BoxFutureCallback = Box AFBoxFuture<'static, ()> + Send + Sync + 'static>; -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] -pub fn af_spawn(future: T) -> tokio::task::JoinHandle -where - T: Future + 'static, - T::Output: 'static, -{ - tokio::task::spawn_local(future) -} - -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] pub fn af_spawn(future: T) -> tokio::task::JoinHandle where T: Future + Send + 'static, @@ -80,11 +70,11 @@ where pub struct AFPluginDispatcher { plugins: AFPluginMap, - runtime: Arc, + runtime: Rc, } impl AFPluginDispatcher { - pub fn new(runtime: Arc, plugins: Vec) -> AFPluginDispatcher { + pub fn new(runtime: Rc, plugins: Vec) -> AFPluginDispatcher { tracing::trace!("{}", plugin_info(&plugins)); AFPluginDispatcher { plugins: plugin_map_or_crash(plugins), @@ -126,14 +116,37 @@ impl AFPluginDispatcher { // The provided future will start running in the background immediately // when `spawn` is called, even if you don't await the returned // `JoinHandle`. - let handle = dispatch.runtime.spawn(async move { - service.call(service_ctx).await.unwrap_or_else(|e| { - tracing::error!("Dispatch runtime error: {:?}", e); - InternalError::Other(format!("{:?}", e)).as_response() - }) - }); + let result: Result; + #[cfg(feature = "local_set")] + { + let handle = dispatch.runtime.local.spawn_local(async move { + service.call(service_ctx).await.unwrap_or_else(|e| { + tracing::error!("Dispatch runtime error: {:?}", e); + InternalError::Other(format!("{:?}", e)).as_response() + }) + }); + + result = dispatch + .runtime + .local + .run_until(handle) + .await + .map_err(|e| e.to_string().into()) + } + + #[cfg(not(feature = "local_set"))] + { + result = dispatch + .runtime + .spawn(async move { + service.call(service_ctx).await.unwrap_or_else(|e| { + tracing::error!("Dispatch runtime error: {:?}", e); + InternalError::Other(format!("{:?}", e)).as_response() + }) + }) + .await; + } - let result = dispatch.runtime.run_until(handle).await; result.unwrap_or_else(|e| { let msg = format!("EVENT_DISPATCH join error: {:?}", e); tracing::error!("{}", msg); @@ -170,16 +183,17 @@ impl AFPluginDispatcher { callback: Some(Box::new(callback)), }; - let handle = dispatch.runtime.spawn(async move { - service.call(service_ctx).await.unwrap_or_else(|e| { - tracing::error!("[dispatch]: runtime error: {:?}", e); - InternalError::Other(format!("{:?}", e)).as_response() - }) - }); - - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] + #[cfg(feature = "local_set")] { - let result = dispatch.runtime.block_on(handle); + let handle = dispatch.runtime.local.spawn_local(async move { + service.call(service_ctx).await.unwrap_or_else(|e| { + tracing::error!("Dispatch runtime error: {:?}", e); + InternalError::Other(format!("{:?}", e)).as_response() + }) + }); + + let fut = dispatch.runtime.local.run_until(handle); + let result = dispatch.runtime.block_on(fut); DispatchFuture { fut: Box::pin(async move { result.unwrap_or_else(|e| { @@ -192,8 +206,18 @@ impl AFPluginDispatcher { } } - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] + #[cfg(not(feature = "local_set"))] { + let handle = dispatch.runtime.spawn(async move { + service + .call(crate::service::service::Service) + .await + .unwrap_or_else(|e| { + tracing::error!("[dispatch]: runtime error: {:?}", e); + InternalError::Other(format!("{:?}", e)).as_response() + }) + }); + let runtime = dispatch.runtime.clone(); DispatchFuture { fut: Box::pin(async move { @@ -211,7 +235,7 @@ impl AFPluginDispatcher { #[cfg(not(target_arch = "wasm32"))] pub fn sync_send( - dispatch: Arc, + dispatch: Rc, request: AFPluginRequest, ) -> AFPluginEventResponse { futures::executor::block_on(AFPluginDispatcher::async_send_with_callback( @@ -221,16 +245,6 @@ impl AFPluginDispatcher { )) } - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] - #[track_caller] - pub fn spawn(&self, future: F) -> tokio::task::JoinHandle - where - F: Future + 'static, - { - self.runtime.spawn(future) - } - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] #[track_caller] pub fn spawn(&self, future: F) -> tokio::task::JoinHandle where @@ -239,24 +253,6 @@ impl AFPluginDispatcher { { self.runtime.spawn(future) } - - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] - pub async fn run_until(&self, future: F) -> F::Output - where - F: Future + 'static, - { - let handle = self.runtime.spawn(future); - self.runtime.run_until(handle).await.unwrap() - } - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] - pub async fn run_until<'a, F>(&self, future: F) -> F::Output - where - F: Future + Send + 'a, - ::Output: Send + 'a, - { - self.runtime.run_until(future).await - } } #[derive(Derivative)] diff --git a/frontend/rust-lib/lib-dispatch/src/module/module.rs b/frontend/rust-lib/lib-dispatch/src/module/module.rs index a5b2df234a..ae92cf9a0c 100644 --- a/frontend/rust-lib/lib-dispatch/src/module/module.rs +++ b/frontend/rust-lib/lib-dispatch/src/module/module.rs @@ -1,3 +1,7 @@ +use futures_core::ready; +use nanoid::nanoid; +use pin_project::pin_project; +use std::rc::Rc; use std::sync::Arc; use std::{ collections::HashMap, @@ -9,10 +13,6 @@ use std::{ task::{Context, Poll}, }; -use futures_core::ready; -use nanoid::nanoid; -use pin_project::pin_project; - use crate::dispatcher::AFConcurrent; use crate::prelude::{AFBoxFuture, AFStateMap}; use crate::service::AFPluginHandler; @@ -26,12 +26,12 @@ use crate::{ }, }; -pub type AFPluginMap = Arc>>; +pub type AFPluginMap = Rc>>; pub(crate) fn plugin_map_or_crash(plugins: Vec) -> AFPluginMap { - let mut plugin_map: HashMap> = HashMap::new(); + let mut plugin_map: HashMap> = HashMap::new(); plugins.into_iter().for_each(|m| { let events = m.events(); - let plugins = Arc::new(m); + let plugins = Rc::new(m); events.into_iter().for_each(|e| { if plugin_map.contains_key(&e) { let plugin_name = plugin_map.get(&e).map(|p| &p.name); @@ -40,7 +40,7 @@ pub(crate) fn plugin_map_or_crash(plugins: Vec) -> AFPluginMap { plugin_map.insert(e, plugins.clone()); }); }); - Arc::new(plugin_map) + Rc::new(plugin_map) } #[derive(PartialEq, Eq, Hash, Debug, Clone)] @@ -67,7 +67,7 @@ pub struct AFPlugin { /// Contains a list of factories that are used to generate the services used to handle the passed-in /// `ServiceRequest`. /// - event_service_factory: Arc< + event_service_factory: Rc< HashMap>, >, } @@ -77,7 +77,7 @@ impl std::default::Default for AFPlugin { Self { name: "".to_owned(), states: Default::default(), - event_service_factory: Arc::new(HashMap::new()), + event_service_factory: Rc::new(HashMap::new()), } } } @@ -113,7 +113,7 @@ impl AFPlugin { if self.event_service_factory.contains_key(&event) { panic!("Register duplicate Event: {:?}", &event); } else { - Arc::get_mut(&mut self.event_service_factory) + Rc::get_mut(&mut self.event_service_factory) .unwrap() .insert(event, factory(AFPluginHandlerService::new(handler))); } @@ -185,7 +185,7 @@ impl AFPluginServiceFactory for AFPlugin { } pub struct AFPluginService { - services: Arc< + services: Rc< HashMap>, >, states: AFStateMap, diff --git a/frontend/rust-lib/lib-dispatch/src/runtime.rs b/frontend/rust-lib/lib-dispatch/src/runtime.rs index fd3658517c..eaa3223a20 100644 --- a/frontend/rust-lib/lib-dispatch/src/runtime.rs +++ b/frontend/rust-lib/lib-dispatch/src/runtime.rs @@ -8,8 +8,8 @@ use tokio::task::JoinHandle; pub struct AFPluginRuntime { inner: Runtime, - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] - local: tokio::task::LocalSet, + #[cfg(feature = "local_set")] + pub(crate) local: tokio::task::LocalSet, } impl Display for AFPluginRuntime { @@ -27,21 +27,11 @@ impl AFPluginRuntime { let inner = default_tokio_runtime()?; Ok(Self { inner, - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] + #[cfg(feature = "local_set")] local: tokio::task::LocalSet::new(), }) } - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] - #[track_caller] - pub fn spawn(&self, future: F) -> JoinHandle - where - F: Future + 'static, - { - self.local.spawn_local(future) - } - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] #[track_caller] pub fn spawn(&self, future: F) -> JoinHandle where @@ -51,23 +41,7 @@ impl AFPluginRuntime { self.inner.spawn(future) } - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] - pub async fn run_until(&self, future: F) -> F::Output - where - F: Future, - { - self.local.run_until(future).await - } - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] - pub async fn run_until(&self, future: F) -> F::Output - where - F: Future, - { - future.await - } - - #[cfg(any(target_arch = "wasm32", feature = "local_set"))] + #[cfg(feature = "local_set")] #[track_caller] pub fn block_on(&self, f: F) -> F::Output where @@ -76,7 +50,7 @@ impl AFPluginRuntime { self.local.block_on(&self.inner, f) } - #[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] + #[cfg(not(feature = "local_set"))] #[track_caller] pub fn block_on(&self, f: F) -> F::Output where @@ -86,14 +60,26 @@ impl AFPluginRuntime { } } -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub fn default_tokio_runtime() -> io::Result { - runtime::Builder::new_current_thread() - .thread_name("dispatch-rt-st") - .build() + #[cfg(not(target_arch = "wasm32"))] + { + runtime::Builder::new_multi_thread() + .enable_io() + .enable_time() + .thread_name("dispatch-rt-st") + .build() + } + + #[cfg(target_arch = "wasm32")] + { + runtime::Builder::new_current_thread() + .thread_name("dispatch-rt-st") + .build() + } } -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub fn default_tokio_runtime() -> io::Result { runtime::Builder::new_multi_thread() .thread_name("dispatch-rt-mt") diff --git a/frontend/rust-lib/lib-dispatch/src/service/boxed.rs b/frontend/rust-lib/lib-dispatch/src/service/boxed.rs index 7ff7a7c116..811b995082 100644 --- a/frontend/rust-lib/lib-dispatch/src/service/boxed.rs +++ b/frontend/rust-lib/lib-dispatch/src/service/boxed.rs @@ -16,7 +16,7 @@ where BoxServiceFactory(Box::new(FactoryWrapper(factory))) } -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] type Inner = Box< dyn AFPluginServiceFactory< Req, @@ -27,7 +27,7 @@ type Inner = Box< Future = AFBoxFuture<'static, Result, Err>>, >, >; -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] type Inner = Box< dyn AFPluginServiceFactory< Req, @@ -58,12 +58,12 @@ where } } -#[cfg(any(target_arch = "wasm32", feature = "local_set"))] +#[cfg(feature = "local_set")] pub type BoxService = Box< dyn Service>>, >; -#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))] +#[cfg(not(feature = "local_set"))] pub type BoxService = Box< dyn Service>> + Sync diff --git a/frontend/rust-lib/lib-dispatch/tests/api/module.rs b/frontend/rust-lib/lib-dispatch/tests/api/module.rs index 2c4539bd7e..fed8d75720 100644 --- a/frontend/rust-lib/lib-dispatch/tests/api/module.rs +++ b/frontend/rust-lib/lib-dispatch/tests/api/module.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::rc::Rc; use lib_dispatch::prelude::*; use lib_dispatch::runtime::AFPluginRuntime; @@ -10,8 +10,8 @@ pub async fn hello() -> String { #[tokio::test] async fn test() { let event = "1"; - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); - let dispatch = Arc::new(AFPluginDispatcher::new( + let runtime = Rc::new(AFPluginRuntime::new().unwrap()); + let dispatch = Rc::new(AFPluginDispatcher::new( runtime, vec![AFPlugin::new().event(event, hello)], )); From b3a0119c1890399436181113a44e88b3f24bd54b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 14 Aug 2024 09:31:30 +0800 Subject: [PATCH 3/4] feat: optimize date picker & mention block (#5954) * chore: optimize rename button on mobile * fix: mention block id empty error * chore: optimize mention block style * feat: add confirm button in date picker --- .../bottom_sheet_rename_widget.dart | 2 +- .../editor_transaction_adapter.dart | 18 +- .../editor_plugins/mention/mention_block.dart | 6 +- .../mention/mention_date_block.dart | 188 +++++++++++------- .../mobile_appflowy_date_picker.dart | 146 +++++++++----- 5 files changed, 236 insertions(+), 124 deletions(-) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart index d4f49cb9a9..e61f27b6b2 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart @@ -64,7 +64,7 @@ class _MobileBottomSheetRenameWidgetState padding: const EdgeInsets.symmetric( horizontal: 16.0, ), - fontColor: Colors.white, + textColor: Colors.white, fillColor: Theme.of(context).primaryColor, onPressed: () { widget.onRename(controller.text); diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart b/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart index f69d116df5..172c3b2bc9 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/editor_transaction_adapter.dart @@ -18,7 +18,8 @@ import 'package:appflowy_editor/appflowy_editor.dart' Node, Path, Delta, - composeAttributes; + composeAttributes, + blockComponentDelta; import 'package:collection/collection.dart'; import 'package:nanoid/nanoid.dart'; @@ -81,6 +82,15 @@ class TransactionAdapter { } final blockActions = actions.map((e) => e.blockActionPB).toList(growable: false); + + for (final action in blockActions) { + if (enableDocumentInternalLog) { + Log.debug( + '[editor_transaction_adapter] action => ${action.toProto3Json()}', + ); + } + } + await documentService.applyAction( documentId: documentId, actions: blockActions, @@ -164,6 +174,7 @@ extension on InsertOperation { childrenId: nanoid(6), externalId: textId, externalType: textId != null ? _kExternalTextType : null, + attributes: {...node.attributes}..remove(blockComponentDelta), ) ..parentId = parentId ..prevId = prevId; @@ -234,10 +245,13 @@ extension on UpdateOperation { ) : null; + final composedAttributes = composeAttributes(oldAttributes, attributes); + composedAttributes?.remove(blockComponentDelta); + final payload = BlockActionPayloadPB() ..block = node.toBlock( parentId: parentId, - attributes: composeAttributes(oldAttributes, attributes), + attributes: composedAttributes, ) ..parentId = parentId; final blockActionPB = BlockActionPB() diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart index 62d16a1714..c66f553bc2 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart @@ -73,7 +73,11 @@ class MentionBlock extends StatelessWidget { switch (type) { case MentionType.page: - final String pageId = mention[MentionBlockKeys.pageId]; + final String? pageId = mention[MentionBlockKeys.pageId] as String?; + if (pageId == null) { + return const SizedBox.shrink(); + } + return MentionPageBlock( key: ValueKey(pageId), editorState: editorState, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart index a4a4f670f2..9f2a16b0cf 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart @@ -26,6 +26,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nanoid/non_secure.dart'; @@ -182,68 +183,13 @@ class _MentionDateBlockState extends State { return GestureDetector( onTapDown: (details) { - if (widget.editorState.editable) { - if (PlatformExtension.isMobile) { - showMobileBottomSheet( - context, - builder: (_) => DraggableScrollableSheet( - expand: false, - snap: true, - initialChildSize: 0.7, - minChildSize: 0.4, - snapSizes: const [0.4, 0.7, 1.0], - builder: (_, controller) => Material( - color: - Theme.of(context).colorScheme.secondaryContainer, - child: ListView( - controller: controller, - children: [ - ColoredBox( - color: Theme.of(context).colorScheme.surface, - child: const Center(child: DragHandle()), - ), - const MobileDateHeader(), - MobileAppFlowyDatePicker( - selectedDay: parsedDate, - timeStr: timeStr, - dateStr: parsedDate != null - ? options.dateFormat - .formatDate(parsedDate!, _includeTime) - : null, - includeTime: options.includeTime, - use24hFormat: options.timeFormat == - UserTimeFormatPB.TwentyFourHour, - rebuildOnDaySelected: true, - rebuildOnTimeChanged: true, - timeFormat: options.timeFormat.simplified, - selectedReminderOption: widget.reminderOption, - onDaySelected: options.onDaySelected, - onStartTimeChanged: (time) => options - .onStartTimeChanged - ?.call(time ?? ""), - onIncludeTimeChanged: - options.onIncludeTimeChanged, - liveDateFormatter: (selected) => - appearance.dateFormat.formatDate( - selected, - false, - appearance.timeFormat, - ), - onReminderSelected: (option) => - _updateReminder(option, reminder), - ), - ], - ), - ), - ), - ); - } else { - DatePickerMenu( - context: context, - editorState: widget.editorState, - ).show(details.globalPosition, options: options); - } - } + _showDatePicker( + context: context, + offset: details.globalPosition, + reminder: reminder, + timeStr: timeStr, + options: options, + ); }, child: MouseRegion( cursor: SystemMouseCursors.click, @@ -251,15 +197,10 @@ class _MentionDateBlockState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - widget.reminderId != null - ? '@$formattedDate' - : formattedDate, - style: widget.textStyle?.copyWith( - color: color, - leadingDistribution: TextLeadingDistribution.even, - ), - strutStyle: widget.textStyle != null - ? StrutStyle.fromTextStyle(widget.textStyle!) + '@$formattedDate', + style: textStyle, + strutStyle: textStyle != null + ? StrutStyle.fromTextStyle(textStyle) : null, ), const HSpace(4), @@ -402,4 +343,109 @@ class _MentionDateBlockState extends State { ), ); } + + void _showDatePicker({ + required BuildContext context, + required DatePickerOptions options, + required Offset offset, + String? timeStr, + ReminderPB? reminder, + }) { + if (!widget.editorState.editable) { + return; + } + if (PlatformExtension.isMobile) { + SystemChannels.textInput.invokeMethod('TextInput.hide'); + + showMobileBottomSheet( + context, + builder: (_) => DraggableScrollableSheet( + expand: false, + snap: true, + initialChildSize: 0.7, + minChildSize: 0.4, + snapSizes: const [0.4, 0.7, 1.0], + builder: (_, controller) => _DatePickerBottomSheet( + controller: controller, + parsedDate: parsedDate, + timeStr: timeStr, + options: options, + includeTime: _includeTime, + reminderOption: widget.reminderOption, + onReminderSelected: (option) => _updateReminder( + option, + reminder, + ), + ), + ), + ); + } else { + DatePickerMenu( + context: context, + editorState: widget.editorState, + ).show(offset, options: options); + } + } +} + +class _DatePickerBottomSheet extends StatelessWidget { + const _DatePickerBottomSheet({ + required this.controller, + required this.parsedDate, + required this.timeStr, + required this.options, + required this.includeTime, + this.reminderOption, + required this.onReminderSelected, + }); + + final ScrollController controller; + final DateTime? parsedDate; + final String? timeStr; + final DatePickerOptions options; + final bool includeTime; + final ReminderOption? reminderOption; + final void Function(ReminderOption) onReminderSelected; + + @override + Widget build(BuildContext context) { + final appearance = context.read().state; + + return Material( + color: Theme.of(context).colorScheme.secondaryContainer, + child: ListView( + controller: controller, + children: [ + ColoredBox( + color: Theme.of(context).colorScheme.surface, + child: const Center(child: DragHandle()), + ), + const MobileDateHeader(), + MobileAppFlowyDatePicker( + selectedDay: parsedDate, + timeStr: timeStr, + dateStr: parsedDate != null + ? options.dateFormat.formatDate(parsedDate!, includeTime) + : null, + includeTime: options.includeTime, + use24hFormat: options.timeFormat == UserTimeFormatPB.TwentyFourHour, + rebuildOnDaySelected: true, + rebuildOnTimeChanged: true, + timeFormat: options.timeFormat.simplified, + selectedReminderOption: reminderOption, + onDaySelected: options.onDaySelected, + onStartTimeChanged: (time) => + options.onStartTimeChanged?.call(time ?? ""), + onIncludeTimeChanged: options.onIncludeTimeChanged, + liveDateFormatter: (selected) => appearance.dateFormat.formatDate( + selected, + false, + appearance.timeFormat, + ), + onReminderSelected: onReminderSelected, + ), + ], + ), + ); + } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/mobile_appflowy_date_picker.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/mobile_appflowy_date_picker.dart index 12714e04df..cdeec95e88 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/mobile_appflowy_date_picker.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/mobile_appflowy_date_picker.dart @@ -1,6 +1,3 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/base/app_bar/app_bar_actions.dart'; @@ -13,8 +10,9 @@ import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/mobi import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pbenum.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class MobileAppFlowyDatePicker extends StatefulWidget { @@ -389,57 +387,107 @@ class _IncludeTimePickerState extends State<_IncludeTimePicker> { children.addAll([ Expanded(child: FlowyText(dateStr, textAlign: TextAlign.center)), Container(width: 1, height: 16, color: Colors.grey), - Expanded(child: FlowyText(timeStr ?? '', textAlign: TextAlign.center)), + Expanded( + child: GestureDetector( + onTap: () => _showTimePicker( + context, + use24hFormat: use24hFormat, + isStartDay: isStartDay, + ), + child: FlowyText(timeStr ?? '', textAlign: TextAlign.center), + ), + ), ]); } - return GestureDetector( - onTap: !isIncludeTime - ? null - : () async { - await showMobileBottomSheet( - context, - builder: (context) => ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 300), - child: CupertinoDatePicker( - mode: CupertinoDatePickerMode.time, - use24hFormat: use24hFormat, - onDateTimeChanged: (dateTime) { - final selectedTime = use24hFormat - ? DateFormat('HH:mm').format(dateTime) - : DateFormat('hh:mm a').format(dateTime); - - if (isStartDay) { - widget.onStartTimeChanged(selectedTime); - - if (widget.rebuildOnTimeChanged && mounted) { - setState(() => _timeStr = selectedTime); - } - } else { - widget.onEndTimeChanged?.call(selectedTime); - - if (widget.rebuildOnTimeChanged && mounted) { - setState(() => _endTimeStr = selectedTime); - } - } - }, - ), - ), - ); - }, - child: Container( - constraints: const BoxConstraints(minHeight: 36), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(6), - color: Theme.of(context).colorScheme.secondaryContainer, - border: Border.all( - color: Theme.of(context).colorScheme.outline, - ), + return Container( + constraints: const BoxConstraints(minHeight: 36), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: Theme.of(context).colorScheme.secondaryContainer, + border: Border.all( + color: Theme.of(context).colorScheme.outline, ), - child: Row(children: children), + ), + child: Row(children: children), + ); + } + + Future _showTimePicker( + BuildContext context, { + required bool use24hFormat, + required bool isStartDay, + }) async { + String? selectedTime = isStartDay ? _timeStr : _endTimeStr; + final initialDateTime = selectedTime != null + ? _convertTimeStringToDateTime(selectedTime) + : null; + + return showMobileBottomSheet( + context, + builder: (context) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 300), + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + initialDateTime: initialDateTime, + use24hFormat: use24hFormat, + onDateTimeChanged: (dateTime) { + selectedTime = use24hFormat + ? DateFormat('HH:mm').format(dateTime) + : DateFormat('hh:mm a').format(dateTime); + }, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 36), + child: FlowyTextButton( + LocaleKeys.button_confirm.tr(), + constraints: const BoxConstraints.tightFor(height: 42), + mainAxisAlignment: MainAxisAlignment.center, + textColor: Theme.of(context).colorScheme.onPrimary, + fillColor: Theme.of(context).primaryColor, + onPressed: () { + if (isStartDay) { + widget.onStartTimeChanged(selectedTime); + + if (widget.rebuildOnTimeChanged && mounted) { + setState(() => _timeStr = selectedTime); + } + } else { + widget.onEndTimeChanged?.call(selectedTime); + + if (widget.rebuildOnTimeChanged && mounted) { + setState(() => _endTimeStr = selectedTime); + } + } + + Navigator.of(context).pop(); + }, + ), + ), + const VSpace(18.0), + ], ), ); } + + DateTime _convertTimeStringToDateTime(String timeString) { + final DateTime now = DateTime.now(); + + final List timeParts = timeString.split(':'); + + if (timeParts.length != 2) { + return now; + } + + final int hour = int.parse(timeParts[0]); + final int minute = int.parse(timeParts[1]); + + return DateTime(now.year, now.month, now.day, hour, minute); + } } class _EndDateSwitch extends StatelessWidget { From 4b24b41dd4f3ecca1e866e01d11681619e255f8b Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Wed, 14 Aug 2024 10:33:23 +0800 Subject: [PATCH 4/4] chore: rm fut (#5958) * chore: rm fut * chore: clippy --- .../appflowy_tauri/src-tauri/src/request.rs | 2 +- .../appflowy_web_app/src-tauri/src/request.rs | 2 +- frontend/rust-lib/flowy-ai-pub/src/cloud.rs | 5 +- .../rust-lib/flowy-ai/src/event_handler.rs | 81 +-- .../src/middleware/chat_service_mw.rs | 10 +- .../src/deps_resolve/folder_deps.rs | 596 ++++++++---------- .../flowy-core/src/integrate/trait_impls.rs | 17 +- .../rust-lib/flowy-core/src/integrate/user.rs | 242 ++++--- .../flowy-folder/src/view_operation.rs | 48 +- .../flowy-server/src/af_cloud/impls/chat.rs | 29 +- .../rust-lib/flowy-server/src/default_impl.rs | 9 +- frontend/rust-lib/flowy-user/src/event_map.rs | 97 ++- .../src/services/data_import/importer.rs | 2 +- .../user_manager/manager_user_workspace.rs | 1 - 14 files changed, 477 insertions(+), 664 deletions(-) diff --git a/frontend/appflowy_tauri/src-tauri/src/request.rs b/frontend/appflowy_tauri/src-tauri/src/request.rs index 6d2d01fb6e..146d303cc0 100644 --- a/frontend/appflowy_tauri/src-tauri/src/request.rs +++ b/frontend/appflowy_tauri/src-tauri/src/request.rs @@ -1,4 +1,4 @@ -use flowy_core::MutexAppFlowyCore; +use flowy_core::; use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, }; diff --git a/frontend/appflowy_web_app/src-tauri/src/request.rs b/frontend/appflowy_web_app/src-tauri/src/request.rs index 0ec6a8dadc..6d2d01fb6e 100644 --- a/frontend/appflowy_web_app/src-tauri/src/request.rs +++ b/frontend/appflowy_web_app/src-tauri/src/request.rs @@ -1,4 +1,4 @@ -use flowy_core::{AppFlowyCore, MutexAppFlowyCore}; +use flowy_core::MutexAppFlowyCore; use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, }; diff --git a/frontend/rust-lib/flowy-ai-pub/src/cloud.rs b/frontend/rust-lib/flowy-ai-pub/src/cloud.rs index 30f29d6c7f..918477b634 100644 --- a/frontend/rust-lib/flowy-ai-pub/src/cloud.rs +++ b/frontend/rust-lib/flowy-ai-pub/src/cloud.rs @@ -11,7 +11,6 @@ use client_api::error::AppResponseError; use flowy_error::FlowyError; use futures::stream::BoxStream; use lib_infra::async_trait::async_trait; -use lib_infra::future::FutureResult; use serde_json::Value; use std::collections::HashMap; use std::path::Path; @@ -21,12 +20,12 @@ pub type StreamAnswer = BoxStream<'static, Result>; #[async_trait] pub trait ChatCloudService: Send + Sync + 'static { - fn create_chat( + async fn create_chat( &self, uid: &i64, workspace_id: &str, chat_id: &str, - ) -> FutureResult<(), FlowyError>; + ) -> Result<(), FlowyError>; async fn create_question( &self, diff --git a/frontend/rust-lib/flowy-ai/src/event_handler.rs b/frontend/rust-lib/flowy-ai/src/event_handler.rs index 3ec6eeb660..99933456f5 100644 --- a/frontend/rust-lib/flowy-ai/src/event_handler.rs +++ b/frontend/rust-lib/flowy-ai/src/event_handler.rs @@ -14,7 +14,6 @@ use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult}; use lib_infra::isolate_stream::IsolateSink; use std::sync::{Arc, Weak}; -use tokio::sync::oneshot; use tracing::trace; use validator::Validate; @@ -114,15 +113,9 @@ pub(crate) async fn get_related_question_handler( ) -> DataResult { let ai_manager = upgrade_ai_manager(ai_manager)?; let data = data.into_inner(); - let (tx, rx) = tokio::sync::oneshot::channel(); - tokio::spawn(async move { - let messages = ai_manager - .get_related_questions(&data.chat_id, data.message_id) - .await?; - let _ = tx.send(messages); - Ok::<_, FlowyError>(()) - }); - let messages = rx.await?; + let messages = ai_manager + .get_related_questions(&data.chat_id, data.message_id) + .await?; data_result_ok(messages) } @@ -133,15 +126,9 @@ pub(crate) async fn get_answer_handler( ) -> DataResult { let ai_manager = upgrade_ai_manager(ai_manager)?; let data = data.into_inner(); - let (tx, rx) = tokio::sync::oneshot::channel(); - tokio::spawn(async move { - let message = ai_manager - .generate_answer(&data.chat_id, data.message_id) - .await?; - let _ = tx.send(message); - Ok::<_, FlowyError>(()) - }); - let message = rx.await?; + let message = ai_manager + .generate_answer(&data.chat_id, data.message_id) + .await?; data_result_ok(message) } @@ -163,25 +150,17 @@ pub(crate) async fn refresh_local_ai_info_handler( ai_manager: AFPluginState>, ) -> DataResult { let ai_manager = upgrade_ai_manager(ai_manager)?; - let (tx, rx) = oneshot::channel::>(); - tokio::spawn(async move { - let model_info = ai_manager.local_ai_controller.refresh().await; - if model_info.is_err() { - if let Some(llm_model) = ai_manager.local_ai_controller.get_current_model() { - let model_info = LLMModelInfo { - selected_model: llm_model.clone(), - models: vec![llm_model], - }; - let _ = tx.send(Ok(model_info)); - return; - } + let model_info = ai_manager.local_ai_controller.refresh().await; + if model_info.is_err() { + if let Some(llm_model) = ai_manager.local_ai_controller.get_current_model() { + let model_info = LLMModelInfo { + selected_model: llm_model.clone(), + models: vec![llm_model], + }; + return data_result_ok(model_info.into()); } - - let _ = tx.send(model_info); - }); - - let model_info = rx.await??; - data_result_ok(model_info.into()) + } + data_result_ok(model_info?.into()) } #[tracing::instrument(level = "debug", skip_all, err)] @@ -268,16 +247,9 @@ pub(crate) async fn chat_file_handler( } tracing::debug!("File size: {} bytes", file_size); - - let (tx, rx) = oneshot::channel::>(); - tokio::spawn(async move { - let ai_manager = upgrade_ai_manager(ai_manager)?; - ai_manager.chat_with_file(&data.chat_id, file_path).await?; - let _ = tx.send(Ok(())); - Ok::<_, FlowyError>(()) - }); - - rx.await? + let ai_manager = upgrade_ai_manager(ai_manager)?; + ai_manager.chat_with_file(&data.chat_id, file_path).await?; + Ok(()) } #[tracing::instrument(level = "debug", skip_all, err)] @@ -420,17 +392,10 @@ pub(crate) async fn get_offline_app_handler( ai_manager: AFPluginState>, ) -> DataResult { let ai_manager = upgrade_ai_manager(ai_manager)?; - let (tx, rx) = oneshot::channel::>(); - tokio::spawn(async move { - let link = ai_manager - .local_ai_controller - .get_offline_ai_app_download_link() - .await?; - let _ = tx.send(Ok(link)); - Ok::<_, FlowyError>(()) - }); - - let link = rx.await??; + let link = ai_manager + .local_ai_controller + .get_offline_ai_app_download_link() + .await?; data_result_ok(OfflineAIPB { link }) } diff --git a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs index 598d5716c6..c2e46b3a80 100644 --- a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs +++ b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs @@ -14,7 +14,6 @@ use flowy_ai_pub::cloud::{ use flowy_error::{FlowyError, FlowyResult}; use futures::{stream, Sink, StreamExt, TryStreamExt}; use lib_infra::async_trait::async_trait; -use lib_infra::future::FutureResult; use crate::local_ai::stream_util::QuestionStream; use crate::stream_message::StreamMessage; @@ -108,13 +107,16 @@ impl AICloudServiceMiddleware { #[async_trait] impl ChatCloudService for AICloudServiceMiddleware { - fn create_chat( + async fn create_chat( &self, uid: &i64, workspace_id: &str, chat_id: &str, - ) -> FutureResult<(), FlowyError> { - self.cloud_service.create_chat(uid, workspace_id, chat_id) + ) -> Result<(), FlowyError> { + self + .cloud_service + .create_chat(uid, workspace_id, chat_id) + .await } async fn create_question( diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index 6b6b22fa9d..78994e8a34 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -26,7 +26,7 @@ use flowy_sqlite::kv::KVStorePreferences; use flowy_user::services::authenticate_user::AuthenticateUser; use flowy_user::services::data_import::{load_collab_by_object_id, load_collab_by_object_ids}; use lib_dispatch::prelude::ToBytes; -use lib_infra::future::FutureResult; + use std::collections::HashMap; use std::convert::TryFrom; use std::sync::{Arc, Weak}; @@ -35,6 +35,7 @@ use tokio::sync::RwLock; use crate::integrate::server::ServerProvider; use collab_plugins::local_storage::kv::KVTransactionDB; +use lib_infra::async_trait::async_trait; pub struct FolderDepsResolver(); #[allow(clippy::too_many_arguments)] @@ -113,363 +114,305 @@ impl FolderUser for FolderUserImpl { } struct DocumentFolderOperation(Arc); +#[async_trait] impl FolderOperationHandler for DocumentFolderOperation { - fn create_workspace_view( + async fn create_workspace_view( &self, uid: i64, workspace_view_builder: Arc>, - ) -> FutureResult<(), FlowyError> { + ) -> Result<(), FlowyError> { let manager = self.0.clone(); - FutureResult::new(async move { - let mut write_guard = workspace_view_builder.write().await; - - // Create a view named "Getting started" with an icon ⭐️ and the built-in README data. - // Don't modify this code unless you know what you are doing. - write_guard - .with_view_builder(|view_builder| async { - let view = view_builder - .with_name("Getting started") - .with_icon("⭐️") - .build(); - // create a empty document - let json_str = include_str!("../../assets/read_me.json"); - let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap(); - manager - .create_document(uid, &view.parent_view.id, Some(document_pb.into())) - .await - .unwrap(); - view - }) - .await; - Ok(()) - }) + let mut write_guard = workspace_view_builder.write().await; + // Create a view named "Getting started" with an icon ⭐️ and the built-in README data. + // Don't modify this code unless you know what you are doing. + write_guard + .with_view_builder(|view_builder| async { + let view = view_builder + .with_name("Getting started") + .with_icon("⭐️") + .build(); + // create a empty document + let json_str = include_str!("../../assets/read_me.json"); + let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap(); + manager + .create_document(uid, &view.parent_view.id, Some(document_pb.into())) + .await + .unwrap(); + view + }) + .await; + Ok(()) } - fn open_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.open_document(&view_id).await?; - Ok(()) - }) + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_document(view_id).await?; + Ok(()) } /// Close the document view. - fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.close_document(&view_id).await?; - Ok(()) - }) + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_document(view_id).await?; + Ok(()) } - fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - match manager.delete_document(&view_id).await { - Ok(_) => tracing::trace!("Delete document: {}", view_id), - Err(e) => tracing::error!("🔴delete document failed: {}", e), - } - Ok(()) - }) + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + match self.0.delete_document(view_id).await { + Ok(_) => tracing::trace!("Delete document: {}", view_id), + Err(e) => tracing::error!("🔴delete document failed: {}", e), + } + Ok(()) } - fn duplicate_view(&self, view_id: &str) -> FutureResult { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - let data: DocumentDataPB = manager.get_document_data(&view_id).await?.into(); - let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?; - Ok(data_bytes) - }) + async fn duplicate_view(&self, view_id: &str) -> Result { + let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into(); + let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?; + Ok(data_bytes) } - fn create_view_with_view_data( + async fn create_view_with_view_data( &self, user_id: i64, params: CreateViewParams, - ) -> FutureResult, FlowyError> { + ) -> Result, FlowyError> { debug_assert_eq!(params.layout, ViewLayoutPB::Document); - let view_id = params.view_id.to_string(); - let manager = self.0.clone(); - FutureResult::new(async move { - let data = DocumentDataPB::try_from(Bytes::from(params.initial_data))?; - let encoded_collab = manager - .create_document(user_id, &view_id, Some(data.into())) - .await?; - Ok(Some(encoded_collab)) - }) + let data = DocumentDataPB::try_from(Bytes::from(params.initial_data))?; + let encoded_collab = self + .0 + .create_document(user_id, ¶ms.view_id, Some(data.into())) + .await?; + Ok(Some(encoded_collab)) } - fn get_encoded_collab_v1_from_disk( + async fn get_encoded_collab_v1_from_disk( &self, user: Arc, view_id: &str, - ) -> FutureResult { + ) -> Result { // get the collab_object_id for the document. // the collab_object_id for the document is the view_id. - let oid = view_id.to_string(); - FutureResult::new(async move { - let uid = user - .user_id() - .map_err(|e| e.with_context("unable to get the uid: {}"))?; + let uid = user + .user_id() + .map_err(|e| e.with_context("unable to get the uid: {}"))?; - // get the collab db - let collab_db = user - .collab_db(uid) - .map_err(|e| e.with_context("unable to get the collab"))?; - let collab_db = collab_db.upgrade().ok_or_else(|| { - FlowyError::internal().with_context( - "The collab db has been dropped, indicating that the user has switched to a new account", - ) - })?; - let collab_read_txn = collab_db.read_txn(); + // get the collab db + let collab_db = user + .collab_db(uid) + .map_err(|e| e.with_context("unable to get the collab"))?; + let collab_db = collab_db.upgrade().ok_or_else(|| { + FlowyError::internal().with_context( + "The collab db has been dropped, indicating that the user has switched to a new account", + ) + })?; + let collab_read_txn = collab_db.read_txn(); - // read the collab from the db - let collab = load_collab_by_object_id(uid, &collab_read_txn, &oid).map_err(|e| { - FlowyError::internal().with_context(format!("load document collab failed: {}", e)) - })?; + // read the collab from the db + let collab = load_collab_by_object_id(uid, &collab_read_txn, view_id).map_err(|e| { + FlowyError::internal().with_context(format!("load document collab failed: {}", e)) + })?; - let encoded_collab = collab + let encoded_collab = collab // encode the collab and check the integrity of the collab .encode_collab_v1(|collab| CollabType::Document.validate_require_data(collab)) .map_err(|e| { FlowyError::internal().with_context(format!("encode document collab failed: {}", e)) })?; - Ok(EncodedCollabWrapper::Document(DocumentEncodedCollab { - document_encoded_collab: encoded_collab, - })) - }) + Ok(EncodedCollabWrapper::Document(DocumentEncodedCollab { + document_encoded_collab: encoded_collab, + })) } /// Create a view with built-in data. - fn create_built_in_view( + async fn create_built_in_view( &self, user_id: i64, view_id: &str, _name: &str, layout: ViewLayout, - ) -> FutureResult<(), FlowyError> { + ) -> Result<(), FlowyError> { debug_assert_eq!(layout, ViewLayout::Document); - let view_id = view_id.to_string(); - let manager = self.0.clone(); - FutureResult::new(async move { - match manager.create_document(user_id, &view_id, None).await { - Ok(_) => Ok(()), - Err(err) => { - if err.is_already_exists() { - Ok(()) - } else { - Err(err) - } - }, - } - }) + match self.0.create_document(user_id, view_id, None).await { + Ok(_) => Ok(()), + Err(err) => { + if err.is_already_exists() { + Ok(()) + } else { + Err(err) + } + }, + } } - fn import_from_bytes( + async fn import_from_bytes( &self, uid: i64, view_id: &str, _name: &str, _import_type: ImportType, bytes: Vec, - ) -> FutureResult { - let view_id = view_id.to_string(); - let manager = self.0.clone(); - FutureResult::new(async move { - let data = DocumentDataPB::try_from(Bytes::from(bytes))?; - let encoded_collab = manager - .create_document(uid, &view_id, Some(data.into())) - .await?; - Ok(encoded_collab) - }) + ) -> Result { + let data = DocumentDataPB::try_from(Bytes::from(bytes))?; + let encoded_collab = self + .0 + .create_document(uid, view_id, Some(data.into())) + .await?; + Ok(encoded_collab) } // will implement soon - fn import_from_file_path( + async fn import_from_file_path( &self, _view_id: &str, _name: &str, _path: String, - ) -> FutureResult<(), FlowyError> { - FutureResult::new(async move { Ok(()) }) + ) -> Result<(), FlowyError> { + Ok(()) } } struct DatabaseFolderOperation(Arc); + +#[async_trait] impl FolderOperationHandler for DatabaseFolderOperation { - fn open_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let database_manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - database_manager.open_database_view(view_id).await?; - Ok(()) - }) + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_database_view(view_id).await?; + Ok(()) } - fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let database_manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - database_manager.close_database_view(view_id).await?; - Ok(()) - }) + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_database_view(view_id).await?; + Ok(()) } - fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let database_manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - match database_manager.delete_database_view(&view_id).await { - Ok(_) => tracing::trace!("Delete database view: {}", view_id), - Err(e) => tracing::error!("🔴delete database failed: {}", e), - } - Ok(()) - }) + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + match self.0.delete_database_view(view_id).await { + Ok(_) => tracing::trace!("Delete database view: {}", view_id), + Err(e) => tracing::error!("🔴delete database failed: {}", e), + } + Ok(()) } - fn get_encoded_collab_v1_from_disk( + async fn get_encoded_collab_v1_from_disk( &self, user: Arc, view_id: &str, - ) -> FutureResult { - let manager = self.0.clone(); - let view_id = view_id.to_string(); + ) -> Result { + // get the collab_object_id for the database. + // + // the collab object_id for the database is not the view_id, + // we should use the view_id to get the database_id + let oid = self.0.get_database_id_with_view_id(view_id).await?; + let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?; + let row_oids = row_oids + .into_iter() + .map(|oid| oid.into_inner()) + .collect::>(); + let database_metas = self.0.get_all_databases_meta().await; - FutureResult::new(async move { - // get the collab_object_id for the database. - // - // the collab object_id for the database is not the view_id, - // we should use the view_id to get the database_id - let oid = manager.get_database_id_with_view_id(&view_id).await?; - let row_oids = manager.get_database_row_ids_with_view_id(&view_id).await?; - let row_oids = row_oids - .into_iter() - .map(|oid| oid.into_inner()) - .collect::>(); - let database_metas = manager.get_all_databases_meta().await; + let uid = user + .user_id() + .map_err(|e| e.with_context("unable to get the uid: {}"))?; - let uid = user - .user_id() - .map_err(|e| e.with_context("unable to get the uid: {}"))?; + // get the collab db + let collab_db = user + .collab_db(uid) + .map_err(|e| e.with_context("unable to get the collab"))?; + let collab_db = collab_db.upgrade().ok_or_else(|| { + FlowyError::internal().with_context( + "The collab db has been dropped, indicating that the user has switched to a new account", + ) + })?; - // get the collab db - let collab_db = user - .collab_db(uid) - .map_err(|e| e.with_context("unable to get the collab"))?; - let collab_db = collab_db.upgrade().ok_or_else(|| { - FlowyError::internal().with_context( - "The collab db has been dropped, indicating that the user has switched to a new account", - ) - })?; + let collab_read_txn = collab_db.read_txn(); - let collab_read_txn = collab_db.read_txn(); + // read the database collab from the db + let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &oid).map_err(|e| { + FlowyError::internal().with_context(format!("load database collab failed: {}", e)) + })?; - // read the database collab from the db - let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &oid).map_err(|e| { - FlowyError::internal().with_context(format!("load database collab failed: {}", e)) - })?; - - let database_encoded_collab = database_collab + let database_encoded_collab = database_collab // encode the collab and check the integrity of the collab .encode_collab_v1(|collab| CollabType::Database.validate_require_data(collab)) .map_err(|e| { FlowyError::internal().with_context(format!("encode database collab failed: {}", e)) })?; - // read the database rows collab from the db - let database_row_collabs = load_collab_by_object_ids(uid, &collab_read_txn, &row_oids); - let database_row_encoded_collabs = database_row_collabs - .into_iter() - .map(|(oid, collab)| { - // encode the collab and check the integrity of the collab - let encoded_collab = collab - .encode_collab_v1(|collab| CollabType::DatabaseRow.validate_require_data(collab)) - .map_err(|e| { - FlowyError::internal() - .with_context(format!("encode database row collab failed: {}", e)) - })?; - Ok((oid, encoded_collab)) - }) - .collect::, FlowyError>>()?; + // read the database rows collab from the db + let database_row_collabs = load_collab_by_object_ids(uid, &collab_read_txn, &row_oids); + let database_row_encoded_collabs = database_row_collabs + .into_iter() + .map(|(oid, collab)| { + // encode the collab and check the integrity of the collab + let encoded_collab = collab + .encode_collab_v1(|collab| CollabType::DatabaseRow.validate_require_data(collab)) + .map_err(|e| { + FlowyError::internal().with_context(format!("encode database row collab failed: {}", e)) + })?; + Ok((oid, encoded_collab)) + }) + .collect::, FlowyError>>()?; - // get the relation info from the database meta - let database_relations = database_metas - .into_iter() - .filter_map(|meta| { - let linked_views = meta.linked_views.into_iter().next()?; - Some((meta.database_id, linked_views)) - }) - .collect::>(); + // get the relation info from the database meta + let database_relations = database_metas + .into_iter() + .filter_map(|meta| { + let linked_views = meta.linked_views.into_iter().next()?; + Some((meta.database_id, linked_views)) + }) + .collect::>(); - Ok(EncodedCollabWrapper::Database(DatabaseEncodedCollab { - database_encoded_collab, - database_row_encoded_collabs, - database_relations, - })) - }) + Ok(EncodedCollabWrapper::Database(DatabaseEncodedCollab { + database_encoded_collab, + database_row_encoded_collabs, + database_relations, + })) } - fn duplicate_view(&self, view_id: &str) -> FutureResult { - let database_manager = self.0.clone(); - let view_id = view_id.to_owned(); - FutureResult::new(async move { - let delta_bytes = database_manager.duplicate_database(&view_id).await?; - Ok(Bytes::from(delta_bytes)) - }) + async fn duplicate_view(&self, view_id: &str) -> Result { + let delta_bytes = self.0.duplicate_database(view_id).await?; + Ok(Bytes::from(delta_bytes)) } /// Create a database view with duplicated data. /// If the ext contains the {"database_id": "xx"}, then it will link /// to the existing database. - fn create_view_with_view_data( + async fn create_view_with_view_data( &self, _user_id: i64, params: CreateViewParams, - ) -> FutureResult, FlowyError> { + ) -> Result, FlowyError> { match CreateDatabaseExtParams::from_map(params.meta.clone()) { None => { - let database_manager = self.0.clone(); - let view_id = params.view_id.to_string(); - FutureResult::new(async move { - let encoded_collab = database_manager - .create_database_with_database_data(&view_id, params.initial_data) - .await?; - Ok(Some(encoded_collab)) - }) + let encoded_collab = self + .0 + .create_database_with_database_data(¶ms.view_id, params.initial_data) + .await?; + Ok(Some(encoded_collab)) }, Some(database_params) => { - let database_manager = self.0.clone(); - let layout = match params.layout { ViewLayoutPB::Board => DatabaseLayoutPB::Board, ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar, ViewLayoutPB::Grid => DatabaseLayoutPB::Grid, ViewLayoutPB::Document | ViewLayoutPB::Chat => { - return FutureResult::new(async move { Err(FlowyError::not_support()) }); + return Err(FlowyError::not_support()); }, }; let name = params.name.to_string(); let database_view_id = params.view_id.to_string(); let database_parent_view_id = params.parent_view_id.to_string(); - - FutureResult::new(async move { - database_manager - .create_linked_view( - name, - layout.into(), - database_params.database_id, - database_view_id, - database_parent_view_id, - ) - .await?; - Ok(None) - }) + self + .0 + .create_linked_view( + name, + layout.into(), + database_params.database_id, + database_view_id, + database_parent_view_id, + ) + .await?; + Ok(None) }, } } @@ -478,110 +421,90 @@ impl FolderOperationHandler for DatabaseFolderOperation { /// If the ext contains the {"database_id": "xx"}, then it will link to /// the existing database. The data of the database will be shared within /// these references views. - fn create_built_in_view( + async fn create_built_in_view( &self, _user_id: i64, view_id: &str, name: &str, layout: ViewLayout, - ) -> FutureResult<(), FlowyError> { + ) -> Result<(), FlowyError> { let name = name.to_string(); - let database_manager = self.0.clone(); let data = match layout { ViewLayout::Grid => make_default_grid(view_id, &name), ViewLayout::Board => make_default_board(view_id, &name), ViewLayout::Calendar => make_default_calendar(view_id, &name), - ViewLayout::Document => { - return FutureResult::new(async move { - Err(FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout))) - }); - }, - ViewLayout::Chat => { - // TODO(nathan): AI - todo!("AI") + ViewLayout::Document | ViewLayout::Chat => { + return Err( + FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)), + ); }, }; - FutureResult::new(async move { - let result = database_manager.create_database_with_params(data).await; - match result { - Ok(_) => Ok(()), - Err(err) => { - if err.is_already_exists() { - Ok(()) - } else { - Err(err) - } - }, - } - }) + let result = self.0.create_database_with_params(data).await; + match result { + Ok(_) => Ok(()), + Err(err) => { + if err.is_already_exists() { + Ok(()) + } else { + Err(err) + } + }, + } } - fn import_from_bytes( + async fn import_from_bytes( &self, _uid: i64, view_id: &str, _name: &str, import_type: ImportType, bytes: Vec, - ) -> FutureResult { - let database_manager = self.0.clone(); - let view_id = view_id.to_string(); + ) -> Result { let format = match import_type { ImportType::CSV => CSVFormat::Original, ImportType::HistoryDatabase => CSVFormat::META, ImportType::RawDatabase => CSVFormat::META, _ => CSVFormat::Original, }; - FutureResult::new(async move { - let content = tokio::task::spawn_blocking(move || { - String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err)) - }) - .await??; - let result = database_manager - .import_csv(view_id, content, format) - .await?; - Ok(result.encoded_collab) + let content = tokio::task::spawn_blocking(move || { + String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err)) }) + .await??; + let result = self + .0 + .import_csv(view_id.to_string(), content, format) + .await?; + Ok(result.encoded_collab) } - fn import_from_file_path( + async fn import_from_file_path( &self, _view_id: &str, _name: &str, path: String, - ) -> FutureResult<(), FlowyError> { - let database_manager = self.0.clone(); - FutureResult::new(async move { - database_manager - .import_csv_from_file(path, CSVFormat::META) - .await?; - Ok(()) - }) + ) -> Result<(), FlowyError> { + self.0.import_csv_from_file(path, CSVFormat::META).await?; + Ok(()) } - fn did_update_view(&self, old: &View, new: &View) -> FutureResult<(), FlowyError> { + async fn did_update_view(&self, old: &View, new: &View) -> Result<(), FlowyError> { let database_layout = match new.layout { ViewLayout::Document | ViewLayout::Chat => { - return FutureResult::new(async { - Err(FlowyError::internal().with_context("Can't handle document layout type")) - }); + return Err(FlowyError::internal().with_context("Can't handle document layout type")); }, ViewLayout::Grid => DatabaseLayoutPB::Grid, ViewLayout::Board => DatabaseLayoutPB::Board, ViewLayout::Calendar => DatabaseLayoutPB::Calendar, }; - let database_manager = self.0.clone(); - let view_id = new.id.clone(); if old.layout != new.layout { - FutureResult::new(async move { - database_manager - .update_database_layout(&view_id, database_layout) - .await?; - Ok(()) - }) + self + .0 + .update_database_layout(&new.id, database_layout) + .await?; + Ok(()) } else { - FutureResult::new(async move { Ok(()) }) + Ok(()) } } } @@ -599,78 +522,61 @@ impl CreateDatabaseExtParams { } struct ChatFolderOperation(Arc); + +#[async_trait] impl FolderOperationHandler for ChatFolderOperation { - fn open_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.open_chat(&view_id).await?; - Ok(()) - }) + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_chat(view_id).await } - fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.close_chat(&view_id).await?; - Ok(()) - }) + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_chat(view_id).await } - fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.delete_chat(&view_id).await?; - Ok(()) - }) + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.delete_chat(view_id).await } - fn duplicate_view(&self, _view_id: &str) -> FutureResult { - FutureResult::new(async move { Err(FlowyError::not_support()) }) + async fn duplicate_view(&self, _view_id: &str) -> Result { + Err(FlowyError::not_support()) } - fn create_view_with_view_data( + async fn create_view_with_view_data( &self, _user_id: i64, _params: CreateViewParams, - ) -> FutureResult, FlowyError> { - FutureResult::new(async move { Err(FlowyError::not_support()) }) + ) -> Result, FlowyError> { + Err(FlowyError::not_support()) } - fn create_built_in_view( + async fn create_built_in_view( &self, user_id: i64, view_id: &str, _name: &str, _layout: ViewLayout, - ) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - manager.create_chat(&user_id, &view_id).await?; - Ok(()) - }) + ) -> Result<(), FlowyError> { + self.0.create_chat(&user_id, view_id).await?; + Ok(()) } - fn import_from_bytes( + async fn import_from_bytes( &self, _uid: i64, _view_id: &str, _name: &str, _import_type: ImportType, _bytes: Vec, - ) -> FutureResult { - FutureResult::new(async move { Err(FlowyError::not_support()) }) + ) -> Result { + Err(FlowyError::not_support()) } - fn import_from_file_path( + async fn import_from_file_path( &self, _view_id: &str, _name: &str, _path: String, - ) -> FutureResult<(), FlowyError> { - FutureResult::new(async move { Err(FlowyError::not_support()) }) + ) -> Result<(), FlowyError> { + Err(FlowyError::not_support()) } } diff --git a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs index f184b5a020..01ef68636f 100644 --- a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs +++ b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs @@ -595,22 +595,17 @@ impl CollabCloudPluginProvider for ServerProvider { #[async_trait] impl ChatCloudService for ServerProvider { - fn create_chat( + async fn create_chat( &self, uid: &i64, workspace_id: &str, chat_id: &str, - ) -> FutureResult<(), FlowyError> { - let workspace_id = workspace_id.to_string(); + ) -> Result<(), FlowyError> { let server = self.get_server(); - let chat_id = chat_id.to_string(); - let uid = *uid; - FutureResult::new(async move { - server? - .chat_service() - .create_chat(&uid, &workspace_id, &chat_id) - .await - }) + server? + .chat_service() + .create_chat(uid, workspace_id, chat_id) + .await } async fn create_question( diff --git a/frontend/rust-lib/flowy-core/src/integrate/user.rs b/frontend/rust-lib/flowy-core/src/integrate/user.rs index 5cc5f787a4..f9bfb46280 100644 --- a/frontend/rust-lib/flowy-core/src/integrate/user.rs +++ b/frontend/rust-lib/flowy-core/src/integrate/user.rs @@ -15,7 +15,7 @@ use flowy_storage::manager::StorageManager; use flowy_user::event_map::UserStatusCallback; use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider}; use flowy_user_pub::entities::{Authenticator, UserProfile, UserWorkspace}; -use lib_infra::future::{to_fut, Fut}; +use lib_infra::async_trait::async_trait; use crate::integrate::server::{Server, ServerProvider}; @@ -29,21 +29,16 @@ pub(crate) struct UserStatusCallbackImpl { pub(crate) ai_manager: Arc, } +#[async_trait] impl UserStatusCallback for UserStatusCallbackImpl { - fn did_init( + async fn did_init( &self, user_id: i64, user_authenticator: &Authenticator, cloud_config: &Option, user_workspace: &UserWorkspace, _device_id: &str, - ) -> Fut> { - let user_id = user_id.to_owned(); - let user_workspace = user_workspace.clone(); - let folder_manager = self.folder_manager.clone(); - let database_manager = self.database_manager.clone(); - let document_manager = self.document_manager.clone(); - + ) -> FlowyResult<()> { self .server_provider .set_user_authenticator(user_authenticator); @@ -59,159 +54,142 @@ impl UserStatusCallback for UserStatusCallbackImpl { } } - to_fut(async move { - folder_manager - .initialize( - user_id, - &user_workspace.id, - FolderInitDataSource::LocalDisk { - create_if_not_exist: false, - }, - ) - .await?; - database_manager.initialize(user_id).await?; - document_manager.initialize(user_id).await?; - Ok(()) - }) + self + .folder_manager + .initialize( + user_id, + &user_workspace.id, + FolderInitDataSource::LocalDisk { + create_if_not_exist: false, + }, + ) + .await?; + self.database_manager.initialize(user_id).await?; + self.document_manager.initialize(user_id).await?; + Ok(()) } - fn did_sign_in( + async fn did_sign_in( &self, user_id: i64, user_workspace: &UserWorkspace, device_id: &str, - ) -> Fut> { - let device_id = device_id.to_owned(); - let user_id = user_id.to_owned(); - let user_workspace = user_workspace.clone(); - let folder_manager = self.folder_manager.clone(); - let database_manager = self.database_manager.clone(); - let document_manager = self.document_manager.clone(); + ) -> FlowyResult<()> { + event!( + tracing::Level::TRACE, + "Notify did sign in: latest_workspace: {:?}, device_id: {}", + user_workspace, + device_id + ); - to_fut(async move { - event!( - tracing::Level::TRACE, - "Notify did sign in: latest_workspace: {:?}, device_id: {}", - user_workspace, - device_id - ); - - folder_manager.initialize_with_workspace_id(user_id).await?; - database_manager.initialize(user_id).await?; - document_manager.initialize(user_id).await?; - Ok(()) - }) + self + .folder_manager + .initialize_with_workspace_id(user_id) + .await?; + self.database_manager.initialize(user_id).await?; + self.document_manager.initialize(user_id).await?; + Ok(()) } - fn did_sign_up( + async fn did_sign_up( &self, is_new_user: bool, user_profile: &UserProfile, user_workspace: &UserWorkspace, device_id: &str, - ) -> Fut> { - let device_id = device_id.to_owned(); - let user_profile = user_profile.clone(); - let folder_manager = self.folder_manager.clone(); - let database_manager = self.database_manager.clone(); - let user_workspace = user_workspace.clone(); - let document_manager = self.document_manager.clone(); + ) -> FlowyResult<()> { self .server_provider .set_user_authenticator(&user_profile.authenticator); let server_type = self.server_provider.get_server_type(); - to_fut(async move { - event!( - tracing::Level::TRACE, - "Notify did sign up: is new: {} user_workspace: {:?}, device_id: {}", - is_new_user, - user_workspace, - device_id - ); + event!( + tracing::Level::TRACE, + "Notify did sign up: is new: {} user_workspace: {:?}, device_id: {}", + is_new_user, + user_workspace, + device_id + ); - // In the current implementation, when a user signs up for AppFlowy Cloud, a default workspace - // is automatically created for them. However, for users who sign up through Supabase, the creation - // of the default workspace relies on the client-side operation. This means that the process - // for initializing a default workspace differs depending on the sign-up method used. - let data_source = match folder_manager - .cloud_service - .get_folder_doc_state( - &user_workspace.id, - user_profile.uid, - CollabType::Folder, - &user_workspace.id, - ) - .await - { - Ok(doc_state) => match server_type { - Server::Local => FolderInitDataSource::LocalDisk { - create_if_not_exist: true, - }, - Server::AppFlowyCloud => FolderInitDataSource::Cloud(doc_state), - Server::Supabase => { - if is_new_user { - FolderInitDataSource::LocalDisk { - create_if_not_exist: true, - } - } else { - FolderInitDataSource::Cloud(doc_state) + // In the current implementation, when a user signs up for AppFlowy Cloud, a default workspace + // is automatically created for them. However, for users who sign up through Supabase, the creation + // of the default workspace relies on the client-side operation. This means that the process + // for initializing a default workspace differs depending on the sign-up method used. + let data_source = match self + .folder_manager + .cloud_service + .get_folder_doc_state( + &user_workspace.id, + user_profile.uid, + CollabType::Folder, + &user_workspace.id, + ) + .await + { + Ok(doc_state) => match server_type { + Server::Local => FolderInitDataSource::LocalDisk { + create_if_not_exist: true, + }, + Server::AppFlowyCloud => FolderInitDataSource::Cloud(doc_state), + Server::Supabase => { + if is_new_user { + FolderInitDataSource::LocalDisk { + create_if_not_exist: true, } - }, + } else { + FolderInitDataSource::Cloud(doc_state) + } }, - Err(err) => match server_type { - Server::Local => FolderInitDataSource::LocalDisk { - create_if_not_exist: true, - }, - Server::AppFlowyCloud | Server::Supabase => { - return Err(FlowyError::from(err)); - }, + }, + Err(err) => match server_type { + Server::Local => FolderInitDataSource::LocalDisk { + create_if_not_exist: true, }, - }; + Server::AppFlowyCloud | Server::Supabase => { + return Err(FlowyError::from(err)); + }, + }, + }; - folder_manager - .initialize_with_new_user( - user_profile.uid, - &user_profile.token, - is_new_user, - data_source, - &user_workspace.id, - ) - .await - .context("FolderManager error")?; + self + .folder_manager + .initialize_with_new_user( + user_profile.uid, + &user_profile.token, + is_new_user, + data_source, + &user_workspace.id, + ) + .await + .context("FolderManager error")?; - database_manager - .initialize_with_new_user(user_profile.uid) - .await - .context("DatabaseManager error")?; + self + .database_manager + .initialize_with_new_user(user_profile.uid) + .await + .context("DatabaseManager error")?; - document_manager - .initialize_with_new_user(user_profile.uid) - .await - .context("DocumentManager error")?; - Ok(()) - }) + self + .document_manager + .initialize_with_new_user(user_profile.uid) + .await + .context("DocumentManager error")?; + Ok(()) } - fn did_expired(&self, _token: &str, user_id: i64) -> Fut> { - let folder_manager = self.folder_manager.clone(); - to_fut(async move { - folder_manager.clear(user_id).await; - Ok(()) - }) + async fn did_expired(&self, _token: &str, user_id: i64) -> FlowyResult<()> { + self.folder_manager.clear(user_id).await; + Ok(()) } - fn open_workspace(&self, user_id: i64, _user_workspace: &UserWorkspace) -> Fut> { - let folder_manager = self.folder_manager.clone(); - let database_manager = self.database_manager.clone(); - let document_manager = self.document_manager.clone(); - - to_fut(async move { - folder_manager.initialize_with_workspace_id(user_id).await?; - database_manager.initialize(user_id).await?; - document_manager.initialize(user_id).await?; - Ok(()) - }) + async fn open_workspace(&self, user_id: i64, _user_workspace: &UserWorkspace) -> FlowyResult<()> { + self + .folder_manager + .initialize_with_workspace_id(user_id) + .await?; + self.database_manager.initialize(user_id).await?; + self.document_manager.initialize(user_id).await?; + Ok(()) } fn did_update_network(&self, reachable: bool) { diff --git a/frontend/rust-lib/flowy-folder/src/view_operation.rs b/frontend/rust-lib/flowy-folder/src/view_operation.rs index 0f8df99e08..fd5f90d206 100644 --- a/frontend/rust-lib/flowy-folder/src/view_operation.rs +++ b/frontend/rust-lib/flowy-folder/src/view_operation.rs @@ -1,16 +1,15 @@ -use std::collections::HashMap; -use std::sync::Arc; - +use async_trait::async_trait; use bytes::Bytes; use collab::entity::EncodedCollab; pub use collab_folder::View; use collab_folder::ViewLayout; +use std::collections::HashMap; +use std::sync::Arc; use tokio::sync::RwLock; use flowy_error::FlowyError; use flowy_folder_pub::folder_builder::NestedViewBuilder; -use lib_infra::future::FutureResult; use lib_infra::util::timestamp; use crate::entities::{CreateViewParams, ViewLayoutPB}; @@ -42,36 +41,37 @@ pub struct DatabaseEncodedCollab { /// view layout. Each [ViewLayout] will have a handler. So when creating a new /// view, the [ViewLayout] will be used to get the handler. /// +#[async_trait] pub trait FolderOperationHandler { /// Create the view for the workspace of new user. /// Only called once when the user is created. - fn create_workspace_view( + async fn create_workspace_view( &self, _uid: i64, _workspace_view_builder: Arc>, - ) -> FutureResult<(), FlowyError> { - FutureResult::new(async { Ok(()) }) + ) -> Result<(), FlowyError> { + Ok(()) } - fn open_view(&self, view_id: &str) -> FutureResult<(), FlowyError>; + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError>; /// Closes the view and releases the resources that this view has in /// the backend - fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError>; + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError>; /// Called when the view is deleted. /// This will called after the view is deleted from the trash. - fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError>; + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError>; /// Returns the [ViewData] that can be used to create the same view. - fn duplicate_view(&self, view_id: &str) -> FutureResult; + async fn duplicate_view(&self, view_id: &str) -> Result; /// get the encoded collab data from the disk. - fn get_encoded_collab_v1_from_disk( + async fn get_encoded_collab_v1_from_disk( &self, _user: Arc, _view_id: &str, - ) -> FutureResult { - FutureResult::new(async move { Err(FlowyError::not_support()) }) + ) -> Result { + Err(FlowyError::not_support()) } /// Create a view with the data. @@ -92,46 +92,46 @@ pub trait FolderOperationHandler { /// /// The return value is the [Option] that can be used to create the view. /// It can be used in syncing the view data to cloud. - fn create_view_with_view_data( + async fn create_view_with_view_data( &self, user_id: i64, params: CreateViewParams, - ) -> FutureResult, FlowyError>; + ) -> Result, FlowyError>; /// Create a view with the pre-defined data. /// For example, the initial data of the grid/calendar/kanban board when /// you create a new view. - fn create_built_in_view( + async fn create_built_in_view( &self, user_id: i64, view_id: &str, name: &str, layout: ViewLayout, - ) -> FutureResult<(), FlowyError>; + ) -> Result<(), FlowyError>; /// Create a view by importing data /// /// The return value - fn import_from_bytes( + async fn import_from_bytes( &self, uid: i64, view_id: &str, name: &str, import_type: ImportType, bytes: Vec, - ) -> FutureResult; + ) -> Result; /// Create a view by importing data from a file - fn import_from_file_path( + async fn import_from_file_path( &self, view_id: &str, name: &str, path: String, - ) -> FutureResult<(), FlowyError>; + ) -> Result<(), FlowyError>; /// Called when the view is updated. The handler is the `old` registered handler. - fn did_update_view(&self, _old: &View, _new: &View) -> FutureResult<(), FlowyError> { - FutureResult::new(async move { Ok(()) }) + async fn did_update_view(&self, _old: &View, _new: &View) -> Result<(), FlowyError> { + Ok(()) } } diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs index 2e8e1a8eab..67df702860 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs @@ -13,7 +13,6 @@ use flowy_ai_pub::cloud::{ use flowy_error::FlowyError; use futures_util::{StreamExt, TryStreamExt}; use lib_infra::async_trait::async_trait; -use lib_infra::future::FutureResult; use lib_infra::util::{get_operating_system, OperatingSystem}; use serde_json::{json, Value}; use std::collections::HashMap; @@ -28,29 +27,25 @@ impl ChatCloudService for AFCloudChatCloudServiceImpl where T: AFServer, { - fn create_chat( + async fn create_chat( &self, _uid: &i64, workspace_id: &str, chat_id: &str, - ) -> FutureResult<(), FlowyError> { - let workspace_id = workspace_id.to_string(); + ) -> Result<(), FlowyError> { let chat_id = chat_id.to_string(); let try_get_client = self.inner.try_get_client(); + let params = CreateChatParams { + chat_id, + name: "".to_string(), + rag_ids: vec![], + }; + try_get_client? + .create_chat(workspace_id, params) + .await + .map_err(FlowyError::from)?; - FutureResult::new(async move { - let params = CreateChatParams { - chat_id, - name: "".to_string(), - rag_ids: vec![], - }; - try_get_client? - .create_chat(&workspace_id, params) - .await - .map_err(FlowyError::from)?; - - Ok(()) - }) + Ok(()) } async fn create_question( diff --git a/frontend/rust-lib/flowy-server/src/default_impl.rs b/frontend/rust-lib/flowy-server/src/default_impl.rs index 0e22a6313f..ea41746424 100644 --- a/frontend/rust-lib/flowy-server/src/default_impl.rs +++ b/frontend/rust-lib/flowy-server/src/default_impl.rs @@ -5,7 +5,6 @@ use flowy_ai_pub::cloud::{ }; use flowy_error::FlowyError; use lib_infra::async_trait::async_trait; -use lib_infra::future::FutureResult; use serde_json::Value; use std::collections::HashMap; use std::path::Path; @@ -14,15 +13,13 @@ pub(crate) struct DefaultChatCloudServiceImpl; #[async_trait] impl ChatCloudService for DefaultChatCloudServiceImpl { - fn create_chat( + async fn create_chat( &self, _uid: &i64, _workspace_id: &str, _chat_id: &str, - ) -> FutureResult<(), FlowyError> { - FutureResult::new(async move { - Err(FlowyError::not_support().with_context("Chat is not supported in local server.")) - }) + ) -> Result<(), FlowyError> { + Err(FlowyError::not_support().with_context("Chat is not supported in local server.")) } async fn create_question( diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 6a9a1403d5..6e03928c6e 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -7,7 +7,7 @@ use flowy_error::FlowyResult; use flowy_user_pub::cloud::UserCloudConfig; use flowy_user_pub::entities::*; use lib_dispatch::prelude::*; -use lib_infra::future::{to_fut, Fut}; +use lib_infra::async_trait::async_trait; use crate::event_handler::*; use crate::user_manager::UserManager; @@ -276,38 +276,53 @@ pub enum UserEvent { NotifyDidSwitchPlan = 63, } +#[async_trait] pub trait UserStatusCallback: Send + Sync + 'static { /// When the [Authenticator] changed, this method will be called. Currently, the auth type /// will be changed when the user sign in or sign up. fn authenticator_did_changed(&self, _authenticator: Authenticator) {} /// This will be called after the application launches if the user is already signed in. /// If the user is not signed in, this method will not be called - fn did_init( + async fn did_init( &self, - user_id: i64, - user_authenticator: &Authenticator, - cloud_config: &Option, - user_workspace: &UserWorkspace, - device_id: &str, - ) -> Fut>; + _user_id: i64, + _user_authenticator: &Authenticator, + _cloud_config: &Option, + _user_workspace: &UserWorkspace, + _device_id: &str, + ) -> FlowyResult<()> { + Ok(()) + } /// Will be called after the user signed in. - fn did_sign_in( + async fn did_sign_in( &self, - user_id: i64, - user_workspace: &UserWorkspace, - device_id: &str, - ) -> Fut>; + _user_id: i64, + _user_workspace: &UserWorkspace, + _device_id: &str, + ) -> FlowyResult<()> { + Ok(()) + } /// Will be called after the user signed up. - fn did_sign_up( + async fn did_sign_up( &self, - is_new_user: bool, - user_profile: &UserProfile, - user_workspace: &UserWorkspace, - device_id: &str, - ) -> Fut>; + _is_new_user: bool, + _user_profile: &UserProfile, + _user_workspace: &UserWorkspace, + _device_id: &str, + ) -> FlowyResult<()> { + Ok(()) + } - fn did_expired(&self, token: &str, user_id: i64) -> Fut>; - fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> Fut>; + async fn did_expired(&self, _token: &str, _user_id: i64) -> FlowyResult<()> { + Ok(()) + } + async fn open_workspace( + &self, + _user_id: i64, + _user_workspace: &UserWorkspace, + ) -> FlowyResult<()> { + Ok(()) + } fn did_update_network(&self, _reachable: bool) {} fn did_update_plans(&self, _plans: Vec) {} fn did_update_storage_limitation(&self, _can_write: bool) {} @@ -315,42 +330,4 @@ pub trait UserStatusCallback: Send + Sync + 'static { /// Acts as a placeholder [UserStatusCallback] for the user session, but does not perform any function pub(crate) struct DefaultUserStatusCallback; -impl UserStatusCallback for DefaultUserStatusCallback { - fn did_init( - &self, - _user_id: i64, - _authenticator: &Authenticator, - _cloud_config: &Option, - _user_workspace: &UserWorkspace, - _device_id: &str, - ) -> Fut> { - to_fut(async { Ok(()) }) - } - - fn did_sign_in( - &self, - _user_id: i64, - _user_workspace: &UserWorkspace, - _device_id: &str, - ) -> Fut> { - to_fut(async { Ok(()) }) - } - - fn did_sign_up( - &self, - _is_new_user: bool, - _user_profile: &UserProfile, - _user_workspace: &UserWorkspace, - _device_id: &str, - ) -> Fut> { - to_fut(async { Ok(()) }) - } - - fn did_expired(&self, _token: &str, _user_id: i64) -> Fut> { - to_fut(async { Ok(()) }) - } - - fn open_workspace(&self, _user_id: i64, _user_workspace: &UserWorkspace) -> Fut> { - to_fut(async { Ok(()) }) - } -} +impl UserStatusCallback for DefaultUserStatusCallback {} diff --git a/frontend/rust-lib/flowy-user/src/services/data_import/importer.rs b/frontend/rust-lib/flowy-user/src/services/data_import/importer.rs index 5604024be3..47d7167fb4 100644 --- a/frontend/rust-lib/flowy-user/src/services/data_import/importer.rs +++ b/frontend/rust-lib/flowy-user/src/services/data_import/importer.rs @@ -33,7 +33,7 @@ where pub fn load_collab_by_object_id<'a, R>( uid: i64, collab_read_txn: &R, - object_id: &String, + object_id: &str, ) -> Result where R: CollabKVAction<'a>, diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 6b448ba6f8..97cc6747f2 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -581,7 +581,6 @@ impl UserManager { Ok(UseAISettingPB::from(settings)) } - #[instrument(level = "debug", skip(self), err)] pub async fn get_workspace_member_info(&self, uid: i64) -> FlowyResult { let workspace_id = self.get_session()?.user_workspace.id.clone(); let db = self.authenticate_user.get_sqlite_connection(uid)?;