mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-18 20:05:05 -04:00
Merge pull request #7761 from AppFlowy-IO/do_not_initialize_collab
fix: 0.8.9 release bugs
This commit is contained in:
commit
954e844a21
18 changed files with 163 additions and 66 deletions
|
@ -73,27 +73,24 @@ class RelatedRowDetailPageBloc
|
|||
});
|
||||
}
|
||||
|
||||
/// initialize bloc through the `database_id` and `row_id`. The process is as
|
||||
/// follows:
|
||||
/// 1. use the `database_id` to get the database meta, which contains the
|
||||
/// `inline_view_id`
|
||||
/// 2. use the `inline_view_id` to instantiate a `DatabaseController`.
|
||||
/// 3. use the `row_id` with the DatabaseController` to create `RowController`
|
||||
void _init(String databaseId, String initialRowId) async {
|
||||
final databaseMeta =
|
||||
await DatabaseEventGetDatabaseMeta(DatabaseIdPB(value: databaseId))
|
||||
.send()
|
||||
.fold<DatabaseMetaPB?>((s) => s, (f) => null);
|
||||
if (databaseMeta == null) {
|
||||
final viewId = await DatabaseEventGetDefaultDatabaseViewId(
|
||||
DatabaseIdPB(value: databaseId),
|
||||
).send().fold(
|
||||
(pb) => pb.value,
|
||||
(error) => null,
|
||||
);
|
||||
|
||||
if (viewId == null) {
|
||||
return;
|
||||
}
|
||||
final inlineView =
|
||||
await ViewBackendService.getView(databaseMeta.inlineViewId)
|
||||
.fold((viewPB) => viewPB, (f) => null);
|
||||
if (inlineView == null) {
|
||||
|
||||
final databaseView = await ViewBackendService.getView(viewId)
|
||||
.fold((viewPB) => viewPB, (f) => null);
|
||||
if (databaseView == null) {
|
||||
return;
|
||||
}
|
||||
final databaseController = DatabaseController(view: inlineView);
|
||||
final databaseController = DatabaseController(view: databaseView);
|
||||
await databaseController.open().fold(
|
||||
(s) => databaseController.setIsLoading(false),
|
||||
(f) => null,
|
||||
|
@ -104,7 +101,7 @@ class RelatedRowDetailPageBloc
|
|||
}
|
||||
final rowController = RowController(
|
||||
rowMeta: rowInfo.rowMeta,
|
||||
viewId: inlineView.id,
|
||||
viewId: databaseView.id,
|
||||
rowCache: databaseController.rowCache,
|
||||
);
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@ class WindowSizeManager {
|
|||
static const double maxWindowHeight = 8192.0;
|
||||
static const double maxWindowWidth = 8192.0;
|
||||
|
||||
// Default windows size
|
||||
static const double defaultWindowHeight = 960.0;
|
||||
static const double defaultWindowWidth = 1280.0;
|
||||
|
||||
static const double maxScaleFactor = 2.0;
|
||||
static const double minScaleFactor = 0.5;
|
||||
|
||||
|
@ -36,8 +40,8 @@ class WindowSizeManager {
|
|||
Future<Size> getSize() async {
|
||||
final defaultWindowSize = jsonEncode(
|
||||
{
|
||||
WindowSizeManager.height: minWindowHeight,
|
||||
WindowSizeManager.width: minWindowWidth,
|
||||
WindowSizeManager.height: defaultWindowHeight,
|
||||
WindowSizeManager.width: defaultWindowWidth,
|
||||
},
|
||||
);
|
||||
final windowSize = await getIt<KeyValueStorage>().get(KVKeys.windowSize);
|
||||
|
|
|
@ -244,7 +244,10 @@ class SidebarSectionsBloc
|
|||
}
|
||||
|
||||
void _initial(UserProfilePB userProfile, String workspaceId) {
|
||||
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
||||
_workspaceService = WorkspaceService(
|
||||
workspaceId: workspaceId,
|
||||
userId: userProfile.id,
|
||||
);
|
||||
|
||||
_listener = WorkspaceSectionsListener(
|
||||
user: userProfile,
|
||||
|
|
|
@ -29,7 +29,7 @@ class SettingsBillingBloc
|
|||
required Int64 userId,
|
||||
}) : super(const _Initial()) {
|
||||
_userService = UserBackendService(userId: userId);
|
||||
_service = WorkspaceService(workspaceId: workspaceId);
|
||||
_service = WorkspaceService(workspaceId: workspaceId, userId: userId);
|
||||
_successListenable = getIt<SubscriptionSuccessListenable>();
|
||||
_successListenable.addListener(_onPaymentSuccessful);
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||
required this.workspaceId,
|
||||
required Int64 userId,
|
||||
}) : super(const _Initial()) {
|
||||
_service = WorkspaceService(workspaceId: workspaceId);
|
||||
_service = WorkspaceService(
|
||||
workspaceId: workspaceId,
|
||||
userId: userId,
|
||||
);
|
||||
_userService = UserBackendService(userId: userId);
|
||||
_successListenable = getIt<SubscriptionSuccessListenable>();
|
||||
_successListenable.addListener(_onPaymentSuccessful);
|
||||
|
@ -43,7 +46,7 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||
FlowyError? error;
|
||||
|
||||
final usageResult = snapshots.first.fold(
|
||||
(s) => s as WorkspaceUsagePB,
|
||||
(s) => s as WorkspaceUsagePB?,
|
||||
(f) {
|
||||
error = f;
|
||||
return null;
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/file_storage/file_storage_listener.dart';
|
||||
import 'package:appflowy/workspace/application/subscription_success_listenable/subscription_success_listenable.dart';
|
||||
import 'package:appflowy/workspace/application/workspace/workspace_service.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/dispatch/error.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
|
@ -182,20 +183,24 @@ class SidebarPlanBloc extends Bloc<SidebarPlanEvent, SidebarPlanState> {
|
|||
);
|
||||
}
|
||||
|
||||
void _checkWorkspaceUsage() {
|
||||
if (state.workspaceId != null) {
|
||||
final payload = UserWorkspaceIdPB(workspaceId: state.workspaceId!);
|
||||
UserEventGetWorkspaceUsage(payload).send().then((result) {
|
||||
result.onSuccess(
|
||||
(usage) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
add(SidebarPlanEvent.updateWorkspaceUsage(usage));
|
||||
},
|
||||
);
|
||||
});
|
||||
Future<void> _checkWorkspaceUsage() async {
|
||||
if (state.workspaceId == null || state.userProfile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await WorkspaceService(
|
||||
workspaceId: state.workspaceId!,
|
||||
userId: state.userProfile!.id,
|
||||
).getWorkspaceUsage().then((result) {
|
||||
result.fold(
|
||||
(usage) {
|
||||
if (!isClosed && usage != null) {
|
||||
add(SidebarPlanEvent.updateWorkspaceUsage(usage));
|
||||
}
|
||||
},
|
||||
(error) => Log.error("Failed to get workspace usage: $error"),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -486,7 +486,10 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||
}
|
||||
|
||||
void _initial(UserProfilePB userProfile, String workspaceId) {
|
||||
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
||||
_workspaceService = WorkspaceService(
|
||||
workspaceId: workspaceId,
|
||||
userId: userProfile.id,
|
||||
);
|
||||
|
||||
this.userProfile = userProfile;
|
||||
this.workspaceId = workspaceId;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/shared/af_role_pb_extension.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:fixnum/fixnum.dart' as fixnum;
|
||||
|
||||
class WorkspaceService {
|
||||
WorkspaceService({required this.workspaceId});
|
||||
WorkspaceService({required this.workspaceId, required this.userId});
|
||||
|
||||
final String workspaceId;
|
||||
final fixnum.Int64 userId;
|
||||
|
||||
Future<FlowyResult<ViewPB, FlowyError>> createView({
|
||||
required String name,
|
||||
|
@ -82,7 +85,18 @@ class WorkspaceService {
|
|||
return FolderEventMoveView(payload).send();
|
||||
}
|
||||
|
||||
Future<FlowyResult<WorkspaceUsagePB, FlowyError>> getWorkspaceUsage() {
|
||||
Future<FlowyResult<WorkspaceUsagePB?, FlowyError>> getWorkspaceUsage() async {
|
||||
final request = WorkspaceMemberIdPB()..uid = userId;
|
||||
final result = await UserEventGetMemberInfo(request).send();
|
||||
final isOwner = result.fold(
|
||||
(member) => member.role.isOwner,
|
||||
(_) => false,
|
||||
);
|
||||
|
||||
if (!isOwner) {
|
||||
return FlowyResult.success(null);
|
||||
}
|
||||
|
||||
final payload = UserWorkspaceIdPB(workspaceId: workspaceId);
|
||||
return UserEventGetWorkspaceUsage(payload).send();
|
||||
}
|
||||
|
|
|
@ -69,7 +69,10 @@ class AppFlowyUnitTest {
|
|||
}
|
||||
|
||||
Future<void> _initialServices() async {
|
||||
workspaceService = WorkspaceService(workspaceId: currentWorkspace.id);
|
||||
workspaceService = WorkspaceService(
|
||||
workspaceId: currentWorkspace.id,
|
||||
userId: userProfile.id,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ViewPB> createWorkspace() async {
|
||||
|
|
|
@ -277,7 +277,7 @@ impl AppFlowyCollabBuilder {
|
|||
let collab_db = collab_db.clone();
|
||||
let device_id = self.workspace_integrate.device_id()?;
|
||||
let collab = tokio::task::spawn_blocking(move || {
|
||||
let mut collab = CollabBuilder::new(object.uid, &object.object_id, data_source)
|
||||
let collab = CollabBuilder::new(object.uid, &object.object_id, data_source)
|
||||
.with_device_id(device_id)
|
||||
.build()?;
|
||||
let persistence_config = CollabPersistenceConfig::default();
|
||||
|
@ -290,7 +290,6 @@ impl AppFlowyCollabBuilder {
|
|||
persistence_config,
|
||||
);
|
||||
collab.add_plugin(Box::new(db_plugin));
|
||||
collab.initialize();
|
||||
Ok::<_, Error>(collab)
|
||||
})
|
||||
.await??;
|
||||
|
|
|
@ -865,17 +865,25 @@ pub(crate) async fn delete_group_handler(
|
|||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(manager), err)]
|
||||
pub(crate) async fn get_database_meta_handler(
|
||||
pub(crate) async fn get_default_database_view_id_handler(
|
||||
data: AFPluginData<DatabaseIdPB>,
|
||||
manager: AFPluginState<Weak<DatabaseManager>>,
|
||||
) -> DataResult<DatabaseMetaPB, FlowyError> {
|
||||
) -> DataResult<DatabaseViewIdPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let database_id = data.into_inner().value;
|
||||
let inline_view_id = manager.get_database_inline_view_id(&database_id).await?;
|
||||
let database_view_id = manager
|
||||
.get_database_meta(&database_id)
|
||||
.await?
|
||||
.and_then(|mut d| d.linked_views.pop())
|
||||
.ok_or_else(|| {
|
||||
FlowyError::internal().with_context(format!(
|
||||
"Can't find any database view for given database id: {}",
|
||||
database_id
|
||||
))
|
||||
})?;
|
||||
|
||||
let data = DatabaseMetaPB {
|
||||
database_id,
|
||||
inline_view_id,
|
||||
let data = DatabaseViewIdPB {
|
||||
value: database_view_id,
|
||||
};
|
||||
data_result_ok(data)
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
|
|||
.event(DatabaseEvent::CreateGroup, create_group_handler)
|
||||
.event(DatabaseEvent::DeleteGroup, delete_group_handler)
|
||||
// Database
|
||||
.event(DatabaseEvent::GetDatabaseMeta, get_database_meta_handler)
|
||||
.event(DatabaseEvent::GetDefaultDatabaseViewId, get_default_database_view_id_handler)
|
||||
.event(DatabaseEvent::GetDatabases, get_databases_handler)
|
||||
// Calendar
|
||||
.event(DatabaseEvent::GetAllCalendarEvents, get_calendar_events_handler)
|
||||
|
@ -305,8 +305,8 @@ pub enum DatabaseEvent {
|
|||
#[event(input = "DeleteGroupPayloadPB")]
|
||||
DeleteGroup = 115,
|
||||
|
||||
#[event(input = "DatabaseIdPB", output = "DatabaseMetaPB")]
|
||||
GetDatabaseMeta = 119,
|
||||
#[event(input = "DatabaseIdPB", output = "DatabaseViewIdPB")]
|
||||
GetDefaultDatabaseViewId = 119,
|
||||
|
||||
/// Returns all the databases
|
||||
#[event(output = "RepeatedDatabaseDescriptionPB")]
|
||||
|
|
|
@ -166,6 +166,15 @@ impl DatabaseManager {
|
|||
items
|
||||
}
|
||||
|
||||
pub async fn get_database_meta(&self, database_id: &str) -> FlowyResult<Option<DatabaseMeta>> {
|
||||
let mut database_meta = None;
|
||||
if let Some(lock) = self.workspace_database_manager.load_full() {
|
||||
let wdb = lock.read().await;
|
||||
database_meta = wdb.get_database_meta(database_id);
|
||||
}
|
||||
Ok(database_meta)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, err)]
|
||||
pub async fn update_database_indexing(
|
||||
&self,
|
||||
|
|
|
@ -138,7 +138,12 @@ impl FolderManager {
|
|||
Arc::downgrade(&self.user),
|
||||
);
|
||||
|
||||
self.folder_indexer.initialize().await;
|
||||
let weak_folder_indexer = Arc::downgrade(&self.folder_indexer);
|
||||
tokio::spawn(async move {
|
||||
if let Some(folder_indexer) = weak_folder_indexer.upgrade() {
|
||||
folder_indexer.initialize().await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ impl SearchHandler for DocumentSearchHandler {
|
|||
return;
|
||||
}
|
||||
};
|
||||
trace!("[Search] search result: {:?}", result_items);
|
||||
|
||||
// Prepare input for search summary generation.
|
||||
let summary_input: Vec<SearchResult> = result_items
|
||||
|
@ -122,11 +123,15 @@ impl SearchHandler for DocumentSearchHandler {
|
|||
CreateSearchResultPBArgs::default()
|
||||
.searching(false)
|
||||
.search_result(Some(search_result))
|
||||
.generating_ai_summary(true)
|
||||
.generating_ai_summary(!result_items.is_empty())
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
if result_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate and yield search summary.
|
||||
match cloud_service.generate_search_summary(&workspace_id, query.clone(), summary_input).await {
|
||||
Ok(summary_result) => {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use super::entities::FolderIndexData;
|
||||
use crate::entities::{LocalSearchResponseItemPB, ResultIconTypePB};
|
||||
use crate::folder::schema::{
|
||||
FolderSchema, FOLDER_ICON_FIELD_NAME, FOLDER_ICON_TY_FIELD_NAME, FOLDER_ID_FIELD_NAME,
|
||||
FOLDER_TITLE_FIELD_NAME, FOLDER_WORKSPACE_ID_FIELD_NAME,
|
||||
|
@ -7,27 +9,32 @@ use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewIndexCont
|
|||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData};
|
||||
use flowy_user::services::authenticate_user::AuthenticateUser;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::{collections::HashMap, fs};
|
||||
|
||||
use super::entities::FolderIndexData;
|
||||
use crate::entities::{LocalSearchResponseItemPB, ResultIconTypePB};
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use tantivy::{
|
||||
collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, schema::Field, Document,
|
||||
Index, IndexReader, IndexWriter, TantivyDocument, Term,
|
||||
Index, IndexReader, IndexWriter, TantivyDocument, TantivyError, Term,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct TantivyState {
|
||||
pub path: PathBuf,
|
||||
pub index: Index,
|
||||
pub folder_schema: FolderSchema,
|
||||
pub index_reader: IndexReader,
|
||||
pub index_writer: IndexWriter,
|
||||
}
|
||||
|
||||
impl Drop for TantivyState {
|
||||
fn drop(&mut self) {
|
||||
tracing::trace!("Dropping TantivyState at {:?}", self.path);
|
||||
}
|
||||
}
|
||||
|
||||
const FOLDER_INDEX_DIR: &str = "folder_index";
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -57,7 +64,19 @@ impl FolderIndexManagerImpl {
|
|||
}
|
||||
|
||||
/// Initializes the state using the workspace directory.
|
||||
async fn initialize_with_workspace(&self) -> FlowyResult<()> {
|
||||
async fn initialize(&self) -> FlowyResult<()> {
|
||||
if let Some(state) = self.state.write().await.take() {
|
||||
info!("Re-initializing folder indexer");
|
||||
drop(state);
|
||||
}
|
||||
|
||||
// Since the directory lock may not be immediately released,
|
||||
// a workaround is implemented by waiting for 3 seconds before proceeding further. This delay helps
|
||||
// to avoid errors related to trying to open an index directory while an IndexWriter is still active.
|
||||
//
|
||||
// Also, we don't need to initialize the indexer immediately.
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
|
||||
|
||||
let auth_user = self
|
||||
.auth_user
|
||||
.upgrade()
|
||||
|
@ -73,12 +92,26 @@ impl FolderIndexManagerImpl {
|
|||
|
||||
info!("Folder indexer initialized at: {:?}", index_path);
|
||||
let folder_schema = FolderSchema::new();
|
||||
let dir = MmapDirectory::open(index_path)?;
|
||||
let dir = MmapDirectory::open(index_path.clone())?;
|
||||
let index = Index::open_or_create(dir, folder_schema.schema.clone())?;
|
||||
let index_reader = index.reader()?;
|
||||
let index_writer = index.writer(50_000_000)?;
|
||||
|
||||
let index_writer = match index.writer::<_>(50_000_000) {
|
||||
Ok(index_writer) => index_writer,
|
||||
Err(err) => {
|
||||
if let TantivyError::LockFailure(_, _) = err {
|
||||
error!(
|
||||
"Failed to acquire lock for index writer: {:?}, retry later",
|
||||
err
|
||||
);
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
|
||||
}
|
||||
index.writer::<_>(50_000_000)?
|
||||
},
|
||||
};
|
||||
|
||||
*self.state.write().await = Some(TantivyState {
|
||||
path: index_path,
|
||||
index,
|
||||
folder_schema,
|
||||
index_reader,
|
||||
|
@ -295,7 +328,7 @@ impl IndexManager for FolderIndexManagerImpl {
|
|||
#[async_trait]
|
||||
impl FolderIndexManager for FolderIndexManagerImpl {
|
||||
async fn initialize(&self) {
|
||||
if let Err(e) = self.initialize_with_workspace().await {
|
||||
if let Err(e) = self.initialize().await {
|
||||
error!("Failed to initialize FolderIndexManager: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -896,7 +896,10 @@ pub async fn get_workspace_member_info(
|
|||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<WorkspaceMemberPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let member = manager.get_workspace_member_info(param.uid).await?;
|
||||
let workspace_id = manager.get_session()?.user_workspace.workspace_id()?;
|
||||
let member = manager
|
||||
.get_workspace_member_info(param.uid, &workspace_id)
|
||||
.await?;
|
||||
data_result_ok(member.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -583,8 +583,11 @@ impl UserManager {
|
|||
Ok(UseAISettingPB::from(settings))
|
||||
}
|
||||
|
||||
pub async fn get_workspace_member_info(&self, uid: i64) -> FlowyResult<WorkspaceMember> {
|
||||
let workspace_id = self.get_session()?.user_workspace.workspace_id()?;
|
||||
pub async fn get_workspace_member_info(
|
||||
&self,
|
||||
uid: i64,
|
||||
workspace_id: &Uuid,
|
||||
) -> FlowyResult<WorkspaceMember> {
|
||||
let db = self.authenticate_user.get_sqlite_connection(uid)?;
|
||||
// Can opt in using memory cache
|
||||
if let Ok(member_record) = select_workspace_member(db, &workspace_id.to_string(), uid) {
|
||||
|
@ -603,7 +606,7 @@ impl UserManager {
|
|||
}
|
||||
|
||||
let member = self
|
||||
.get_workspace_member_info_from_remote(&workspace_id, uid)
|
||||
.get_workspace_member_info_from_remote(workspace_id, uid)
|
||||
.await?;
|
||||
|
||||
Ok(member)
|
||||
|
|
Loading…
Add table
Reference in a new issue