feat: optimize the read recent views speed (#5726)

* feat: optimize the read recent views speed

* fix: order of recent views should be from the latest to the oldest
This commit is contained in:
Lucas.Xu 2024-07-15 14:23:20 +08:00 committed by GitHub
parent d2e3fdfefd
commit 253e7597c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 83 additions and 26 deletions

View File

@ -65,6 +65,8 @@ PODS:
- FlutterMacOS - FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- printing (1.0.0):
- Flutter
- ReachabilitySwift (5.0.0) - ReachabilitySwift (5.0.0)
- SDWebImage (5.14.2): - SDWebImage (5.14.2):
- SDWebImage/Core (= 5.14.2) - SDWebImage/Core (= 5.14.2)
@ -101,6 +103,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- printing (from `.symlinks/plugins/printing/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`)
@ -149,6 +152,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
printing:
:path: ".symlinks/plugins/printing/ios"
share_plus: share_plus:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation: shared_preferences_foundation:
@ -179,6 +184,7 @@ SPEC CHECKSUMS:
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
printing: 233e1b73bd1f4a05615548e9b5a324c98588640b
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5

View File

@ -38,8 +38,7 @@ class _MobileRecentFolderState extends State<MobileRecentFolder> {
builder: (context, state) { builder: (context, state) {
final ids = <String>{}; final ids = <String>{};
List<ViewPB> recentViews = List<ViewPB> recentViews = state.views.map((e) => e.item).toList();
state.views.reversed.map((e) => e.item).toList();
recentViews.retainWhere((element) => ids.add(element.id)); recentViews.retainWhere((element) => ids.add(element.id));
// only keep the first 20 items. // only keep the first 20 items.

View File

@ -49,7 +49,7 @@ class _MobileRecentSpaceState extends State<MobileRecentSpace>
List<SectionViewPB> _filterRecentViews(List<SectionViewPB> recentViews) { List<SectionViewPB> _filterRecentViews(List<SectionViewPB> recentViews) {
final ids = <String>{}; final ids = <String>{};
final filteredRecentViews = recentViews.reversed.toList(); final filteredRecentViews = recentViews.toList();
filteredRecentViews.retainWhere((e) => ids.add(e.item.id)); filteredRecentViews.retainWhere((e) => ids.add(e.item.id));
return filteredRecentViews; return filteredRecentViews;
} }

View File

@ -1,6 +1,6 @@
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -8,6 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/me
import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_result.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_result.dart';
import 'package:appflowy/plugins/inline_actions/service_handler.dart'; import 'package:appflowy/plugins/inline_actions/service_handler.dart';
import 'package:appflowy/shared/list_extension.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart'; import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart';
@ -64,11 +65,9 @@ class InlinePageReferenceService extends InlineActionsDelegate {
_recentViewsInitialized = true; _recentViewsInitialized = true;
final views = (await _recentService.recentViews()) final sectionViews = await _recentService.recentViews();
.reversed final views =
.map((e) => e.item) sectionViews.unique((e) => e.item.id).map((e) => e.item).toList();
.toSet()
.toList();
// Filter by viewLayout // Filter by viewLayout
views.retainWhere( views.retainWhere(

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/shared/list_extension.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/recent/recent_listener.dart'; import 'package:appflowy/workspace/application/recent/recent_listener.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart';
@ -7,6 +8,7 @@ import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_result/appflowy_result.dart'; import 'package:appflowy_result/appflowy_result.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
/// This is a lazy-singleton to share recent views across the application. /// This is a lazy-singleton to share recent views across the application.
@ -37,7 +39,7 @@ class CachedRecentService {
_listener.start(recentViewsUpdated: _recentViewsUpdated); _listener.start(recentViewsUpdated: _recentViewsUpdated);
_recentViews = await _readRecentViews().fold( _recentViews = await _readRecentViews().fold(
(s) => s.items, (s) => s.items.unique((e) => e.item.id),
(_) => [], (_) => [],
); );
_completer.complete(); _completer.complete();
@ -68,7 +70,8 @@ class CachedRecentService {
Future<FlowyResult<RepeatedRecentViewPB, FlowyError>> Future<FlowyResult<RepeatedRecentViewPB, FlowyError>>
_readRecentViews() async { _readRecentViews() async {
final result = await FolderEventReadRecentViews().send(); final payload = ReadRecentViewsPB(start: Int64(), limit: Int64(100));
final result = await FolderEventReadRecentViews(payload).send();
return result.fold( return result.fold(
(recentViews) { (recentViews) {
return FlowyResult.success( return FlowyResult.success(
@ -101,7 +104,7 @@ class CachedRecentService {
final viewIds = result.toNullable(); final viewIds = result.toNullable();
if (viewIds != null) { if (viewIds != null) {
_recentViews = await _readRecentViews().fold( _recentViews = await _readRecentViews().fold(
(s) => s.items, (s) => s.items.unique((e) => e.item.id),
(_) => [], (_) => [],
); );
} }

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart'; import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
@ -8,6 +6,7 @@ import 'package:appflowy/workspace/presentation/command_palette/widgets/recent_v
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class RecentViewsList extends StatelessWidget { class RecentViewsList extends StatelessWidget {
@ -24,7 +23,7 @@ class RecentViewsList extends StatelessWidget {
builder: (context, state) { builder: (context, state) {
// We remove duplicates by converting the list to a set first // We remove duplicates by converting the list to a set first
final List<ViewPB> recentViews = final List<ViewPB> recentViews =
state.views.reversed.map((e) => e.item).toSet().toList(); state.views.map((e) => e.item).toSet().toList();
return ListView.separated( return ListView.separated(
shrinkWrap: true, shrinkWrap: true,

View File

@ -181,6 +181,15 @@ pub struct RepeatedFavoriteViewPB {
pub items: Vec<SectionViewPB>, pub items: Vec<SectionViewPB>,
} }
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
pub struct ReadRecentViewsPB {
#[pb(index = 1)]
pub start: u64,
#[pb(index = 2)]
pub limit: u64,
}
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)] #[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
pub struct RepeatedRecentViewPB { pub struct RepeatedRecentViewPB {
#[pb(index = 1)] #[pb(index = 1)]

View File

@ -295,20 +295,30 @@ pub(crate) async fn read_favorites_handler(
#[tracing::instrument(level = "debug", skip(folder), err)] #[tracing::instrument(level = "debug", skip(folder), err)]
pub(crate) async fn read_recent_views_handler( pub(crate) async fn read_recent_views_handler(
data: AFPluginData<ReadRecentViewsPB>,
folder: AFPluginState<Weak<FolderManager>>, folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<RepeatedRecentViewPB, FlowyError> { ) -> DataResult<RepeatedRecentViewPB, FlowyError> {
let folder = upgrade_folder(folder)?; let folder = upgrade_folder(folder)?;
let recent_items = folder.get_my_recent_sections().await; let recent_items = folder.get_my_recent_sections().await;
let mut views = vec![]; let start = data.start;
for item in recent_items { let limit = data.limit;
if let Ok(view) = folder.get_view_pb(&item.id).await { let ids = recent_items
views.push(SectionViewPB { .iter()
.rev() // the most recent view is at the end of the list
.map(|item| item.id.clone())
.skip(start as usize)
.take(limit as usize)
.collect::<Vec<_>>();
let views = folder.get_view_pbs_without_children(ids).await?;
let items = views
.into_iter()
.zip(recent_items.into_iter().rev())
.map(|(view, item)| SectionViewPB {
item: view, item: view,
timestamp: item.timestamp, timestamp: item.timestamp,
}); })
} .collect::<Vec<_>>();
} data_result_ok(RepeatedRecentViewPB { items })
data_result_ok(RepeatedRecentViewPB { items: views })
} }
#[tracing::instrument(level = "debug", skip(folder), err)] #[tracing::instrument(level = "debug", skip(folder), err)]

View File

@ -160,7 +160,7 @@ pub enum FolderEvent {
#[event(input = "UpdateViewIconPayloadPB")] #[event(input = "UpdateViewIconPayloadPB")]
UpdateViewIcon = 35, UpdateViewIcon = 35,
#[event(output = "RepeatedRecentViewPB")] #[event(input = "ReadRecentViewsPB", output = "RepeatedRecentViewPB")]
ReadRecentViews = 36, ReadRecentViews = 36,
// used for add or remove recent views, like history // used for add or remove recent views, like history

View File

@ -504,6 +504,38 @@ impl FolderManager {
} }
} }
/// Retrieves the views corresponding to the specified view IDs.
///
/// It is important to note that if the target view contains child views,
/// this method only provides access to the first level of child views.
///
/// Therefore, to access a nested child view within one of the initial child views, you must invoke this method
/// again using the ID of the child view you wish to access.
#[tracing::instrument(level = "debug", skip(self))]
pub async fn get_view_pbs_without_children(
&self,
view_ids: Vec<String>,
) -> FlowyResult<Vec<ViewPB>> {
let folder = self.mutex_folder.read();
let folder = folder.as_ref().ok_or_else(folder_not_init_error)?;
// trash views and other private views should not be accessed
let view_ids_should_be_filtered = self.get_view_ids_should_be_filtered(folder);
let views = view_ids
.into_iter()
.filter_map(|view_id| {
if view_ids_should_be_filtered.contains(&view_id) {
return None;
}
folder.views.get_view(&view_id)
})
.map(view_pb_without_child_views_from_arc)
.collect::<Vec<_>>();
Ok(views)
}
/// Retrieves all views. /// Retrieves all views.
/// ///
/// It is important to note that this will return a flat map of all views, /// It is important to note that this will return a flat map of all views,