mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: adjust cover plugin and support recent section on mobile platform (#3921)
This commit is contained in:
@ -48,6 +48,9 @@ PODS:
|
|||||||
- fluttertoast (0.0.2):
|
- fluttertoast (0.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Toast
|
- Toast
|
||||||
|
- FMDB (2.7.5):
|
||||||
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
- image_gallery_saver (2.0.2):
|
- image_gallery_saver (2.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
@ -72,6 +75,9 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sign_in_with_apple (0.0.1):
|
- sign_in_with_apple (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- sqflite (0.0.3):
|
||||||
|
- Flutter
|
||||||
|
- FMDB (>= 2.7.5)
|
||||||
- super_native_extensions (0.0.1):
|
- super_native_extensions (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- SwiftyGif (5.4.3)
|
- SwiftyGif (5.4.3)
|
||||||
@ -99,6 +105,7 @@ DEPENDENCIES:
|
|||||||
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
|
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
||||||
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
|
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||||
@ -107,6 +114,7 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
|
- FMDB
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
@ -147,6 +155,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
sign_in_with_apple:
|
sign_in_with_apple:
|
||||||
:path: ".symlinks/plugins/sign_in_with_apple/ios"
|
:path: ".symlinks/plugins/sign_in_with_apple/ios"
|
||||||
|
sqflite:
|
||||||
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
super_native_extensions:
|
super_native_extensions:
|
||||||
:path: ".symlinks/plugins/super_native_extensions/ios"
|
:path: ".symlinks/plugins/super_native_extensions/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
@ -165,6 +175,7 @@ SPEC CHECKSUMS:
|
|||||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
||||||
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
|
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
|
||||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||||
@ -176,6 +187,7 @@ SPEC CHECKSUMS:
|
|||||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||||
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||||
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
|
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
|
||||||
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
||||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||||
|
@ -1,68 +1,70 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>AppFlowy requires access to the camera.</string>
|
<string>AppFlowy requires access to the camera.</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>AppFlowy requires access to the photo library.</string>
|
<string>AppFlowy requires access to the photo library.</string>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleLocalizations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleName</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<string>AppFlowy</string>
|
<false />
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundleName</key>
|
||||||
<string>APPL</string>
|
<string>AppFlowy</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>????</string>
|
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleSignature</key>
|
||||||
<array>
|
<string>????</string>
|
||||||
<dict>
|
<key>CFBundleURLTypes</key>
|
||||||
<key>CFBundleURLName</key>
|
<array>
|
||||||
<string></string>
|
<dict>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLName</key>
|
||||||
<array>
|
<string></string>
|
||||||
<string>appflowy-flutter</string>
|
<key>CFBundleURLSchemes</key>
|
||||||
</array>
|
<array>
|
||||||
</dict>
|
<string>appflowy-flutter</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
</dict>
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
</array>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>CFBundleVersion</key>
|
||||||
<true/>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<string>LaunchScreen</string>
|
<true />
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>Main</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
<array>
|
<string>Main</string>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<array>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
</array>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<array>
|
</array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<array>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
</array>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<false/>
|
</array>
|
||||||
</dict>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
</plist>
|
<false />
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -2,12 +2,22 @@ import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
|||||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
||||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||||
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class MobileRouterRecord {
|
||||||
|
PropertyValueNotifier<String> lastPushedRouter =
|
||||||
|
PropertyValueNotifier<String>('');
|
||||||
|
}
|
||||||
|
|
||||||
extension MobileRouter on BuildContext {
|
extension MobileRouter on BuildContext {
|
||||||
Future<void> pushView(ViewPB view) async {
|
Future<void> pushView(ViewPB view) async {
|
||||||
|
await FolderEventSetLatestView(ViewIdPB(value: view.id)).send();
|
||||||
|
getIt<MobileRouterRecord>().lastPushedRouter.value = view.routeName;
|
||||||
push(
|
push(
|
||||||
Uri(
|
Uri(
|
||||||
path: view.routeName,
|
path: view.routeName,
|
||||||
|
@ -117,15 +117,19 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Row(
|
title: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (icon != null)
|
if (icon != null)
|
||||||
FlowyText(
|
FlowyText(
|
||||||
'$icon ',
|
'$icon ',
|
||||||
fontSize: 22.0,
|
fontSize: 22.0,
|
||||||
),
|
),
|
||||||
FlowyText.regular(
|
Expanded(
|
||||||
view?.name ?? widget.title ?? '',
|
child: FlowyText.regular(
|
||||||
fontSize: 14.0,
|
view?.name ?? widget.title ?? '',
|
||||||
|
fontSize: 14.0,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -2,7 +2,7 @@ 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/mobile/presentation/home/mobile_folders.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_folders.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_recent_files.dart';
|
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
||||||
@ -97,8 +97,7 @@ class MobileHomePage extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Recent files
|
// Recent files
|
||||||
const MobileHomePageRecentFilesWidget(),
|
const MobileRecentFolder(),
|
||||||
const Divider(),
|
|
||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
// TODO(yijing): replace by real data later
|
|
||||||
class MockRecentFile {
|
|
||||||
MockRecentFile({
|
|
||||||
required this.title,
|
|
||||||
});
|
|
||||||
final String title;
|
|
||||||
final String icon = '🐼';
|
|
||||||
|
|
||||||
final image = Image.asset(
|
|
||||||
'assets/images/app_flowy_abstract_cover_1.jpg',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final recentFilesList = <MockRecentFile>[
|
|
||||||
MockRecentFile(title: 'Work out plan'),
|
|
||||||
MockRecentFile(title: 'Travel plan'),
|
|
||||||
MockRecentFile(title: 'Meeting notes'),
|
|
||||||
MockRecentFile(title: 'Recipes'),
|
|
||||||
MockRecentFile(title: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
|
|
||||||
];
|
|
||||||
|
|
||||||
class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
|
||||||
const MobileHomePageRecentFilesWidget({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
// TODO: implement the details later.
|
|
||||||
return SizedBox(
|
|
||||||
height: 168,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: FlowyText.semibold(
|
|
||||||
'Recent',
|
|
||||||
fontSize: 20.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: ListView.separated(
|
|
||||||
separatorBuilder: (context, index) => const HSpace(8),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: recentFilesList.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return Container(
|
|
||||||
width: 120,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: theme.colorScheme.background,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(
|
|
||||||
color: theme.colorScheme.outline.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 60,
|
|
||||||
width: double.infinity,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(8),
|
|
||||||
topRight: Radius.circular(8),
|
|
||||||
),
|
|
||||||
child: recentFilesList[index].image,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Container(
|
|
||||||
height: 32,
|
|
||||||
width: 32,
|
|
||||||
margin: const EdgeInsets.only(left: 8),
|
|
||||||
child: Text(
|
|
||||||
recentFilesList[index].icon,
|
|
||||||
style: const TextStyle(fontSize: 32),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Container(
|
|
||||||
height: 32,
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(
|
|
||||||
left: 8,
|
|
||||||
right: 8,
|
|
||||||
bottom: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
recentFilesList[index].title,
|
|
||||||
softWrap: true,
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: theme.colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,104 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_recent_view.dart';
|
||||||
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||||
|
import 'package:dartz/dartz.dart' hide State;
|
||||||
|
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:flutter/material.dart';
|
||||||
|
|
||||||
|
class MobileRecentFolder extends StatefulWidget {
|
||||||
|
const MobileRecentFolder({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileRecentFolder> createState() => _MobileRecentFolderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileRecentFolderState extends State<MobileRecentFolder> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: getIt<MobileRouterRecord>().lastPushedRouter,
|
||||||
|
builder: (context, value, child) {
|
||||||
|
return FutureBuilder<Either<RepeatedViewPB, FlowyError>>(
|
||||||
|
future: FolderEventReadRecentViews().send(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final recentViews = snapshot.data
|
||||||
|
?.fold<List<ViewPB>>(
|
||||||
|
(l) => l.items,
|
||||||
|
(r) => [],
|
||||||
|
)
|
||||||
|
// only keep the first 10 items.
|
||||||
|
.reversed
|
||||||
|
.take(10)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (recentViews == null || recentViews.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
_RecentViews(
|
||||||
|
key: ValueKey(recentViews),
|
||||||
|
// the recent views are in reverse order
|
||||||
|
recentViews: recentViews,
|
||||||
|
),
|
||||||
|
const VSpace(12.0)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecentViews extends StatelessWidget {
|
||||||
|
const _RecentViews({
|
||||||
|
super.key,
|
||||||
|
required this.recentViews,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<ViewPB> recentViews;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 168,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: FlowyText.semibold(
|
||||||
|
LocaleKeys.sideBar_recent.tr(),
|
||||||
|
fontSize: 20.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.separated(
|
||||||
|
separatorBuilder: (context, index) => const HSpace(8),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: recentViews.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return MobileRecentView(
|
||||||
|
view: recentViews[index],
|
||||||
|
height: 120,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
|
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
|
import 'package:appflowy/workspace/application/doc/doc_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:string_validator/string_validator.dart';
|
||||||
|
|
||||||
|
class MobileRecentView extends StatefulWidget {
|
||||||
|
const MobileRecentView({
|
||||||
|
super.key,
|
||||||
|
required this.view,
|
||||||
|
required this.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ViewPB view;
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MobileRecentView> createState() => _MobileRecentViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileRecentViewState extends State<MobileRecentView> {
|
||||||
|
late final ViewListener viewListener;
|
||||||
|
late ViewPB view;
|
||||||
|
late final DocumentListener documentListener;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
view = widget.view;
|
||||||
|
|
||||||
|
viewListener = ViewListener(
|
||||||
|
viewId: view.id,
|
||||||
|
)..start(
|
||||||
|
onViewUpdated: (view) {
|
||||||
|
setState(() {
|
||||||
|
this.view = view;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
documentListener = DocumentListener(id: view.id)
|
||||||
|
..start(
|
||||||
|
didReceiveUpdate: (document) {
|
||||||
|
setState(() {
|
||||||
|
view = view;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
viewListener.stop();
|
||||||
|
documentListener.stop();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final icon = view.icon.value;
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => context.pushView(view),
|
||||||
|
child: Container(
|
||||||
|
height: widget.height,
|
||||||
|
width: widget.height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.colorScheme.background,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: theme.colorScheme.outline.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: SizedBox(
|
||||||
|
height: widget.height / 2.0,
|
||||||
|
width: double.infinity,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
child: _buildCoverWidget(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 4),
|
||||||
|
child: icon.isNotEmpty
|
||||||
|
? FlowyText(
|
||||||
|
icon,
|
||||||
|
fontSize: 30.0,
|
||||||
|
)
|
||||||
|
: SizedBox.square(
|
||||||
|
dimension: 32.0,
|
||||||
|
child: view.defaultIcon(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
height: widget.height / 2.0,
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 8.0,
|
||||||
|
top: 14.0,
|
||||||
|
right: 8.0,
|
||||||
|
),
|
||||||
|
child: FlowyText(
|
||||||
|
view.name,
|
||||||
|
maxLines: 2,
|
||||||
|
fontSize: 16.0,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCoverWidget() {
|
||||||
|
return FutureBuilder<Node?>(
|
||||||
|
future: _getPageNode(),
|
||||||
|
builder: ((context, snapshot) {
|
||||||
|
final node = snapshot.data;
|
||||||
|
final placeholder = Container(
|
||||||
|
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
|
);
|
||||||
|
if (node == null) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
final type = CoverType.fromString(
|
||||||
|
node.attributes[DocumentHeaderBlockKeys.coverType],
|
||||||
|
);
|
||||||
|
final cover =
|
||||||
|
node.attributes[DocumentHeaderBlockKeys.coverDetails] as String?;
|
||||||
|
if (cover == null) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case CoverType.file:
|
||||||
|
if (isURL(cover)) {
|
||||||
|
return CachedNetworkImage(
|
||||||
|
imageUrl: cover,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final imageFile = File(cover);
|
||||||
|
if (!imageFile.existsSync()) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
return Image.file(
|
||||||
|
imageFile,
|
||||||
|
);
|
||||||
|
case CoverType.asset:
|
||||||
|
return Image.asset(
|
||||||
|
cover,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
case CoverType.color:
|
||||||
|
final color = cover.tryToColor() ?? Colors.white;
|
||||||
|
return Container(
|
||||||
|
color: color,
|
||||||
|
);
|
||||||
|
case CoverType.none:
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Node?> _getPageNode() async {
|
||||||
|
final data = await DocumentEventGetDocumentData(
|
||||||
|
OpenDocumentPayloadPB(documentId: view.id),
|
||||||
|
).send();
|
||||||
|
final document = data.fold((l) => l.toDocument(), (r) => null);
|
||||||
|
if (document != null) {
|
||||||
|
return document.root;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ Future<T?> showFlowyMobileBottomSheet<T>(
|
|||||||
}) async {
|
}) async {
|
||||||
return showModalBottomSheet(
|
return showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
builder: (context) => Padding(
|
builder: (context) => Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -529,14 +529,12 @@ class ColorItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return Padding(
|
||||||
customBorder: const RoundedRectangleBorder(
|
padding: const EdgeInsets.only(right: 10.0),
|
||||||
borderRadius: Corners.s6Border,
|
child: InkWell(
|
||||||
),
|
customBorder: const CircleBorder(),
|
||||||
hoverColor: hoverColor,
|
hoverColor: hoverColor,
|
||||||
onTap: () => onTap(option.colorHex),
|
onTap: () => onTap(option.colorHex),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 10.0),
|
|
||||||
child: SizedBox.square(
|
child: SizedBox.square(
|
||||||
dimension: 25,
|
dimension: 25,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
|
@ -2,19 +2,23 @@ import 'dart:io';
|
|||||||
|
|
||||||
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/mobile/presentation/widgets/widgets.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:string_validator/string_validator.dart';
|
||||||
|
|
||||||
import 'cover_editor.dart';
|
import 'cover_editor.dart';
|
||||||
|
|
||||||
@ -262,7 +266,9 @@ class _DocumentHeaderToolbarState extends State<DocumentHeaderToolbar> {
|
|||||||
FlowyButton(
|
FlowyButton(
|
||||||
leftIconSize: const Size.square(18),
|
leftIconSize: const Size.square(18),
|
||||||
onTap: () => widget.onCoverChanged(
|
onTap: () => widget.onCoverChanged(
|
||||||
cover: (CoverType.asset, builtInAssetImages.first),
|
cover: PlatformExtension.isDesktopOrWeb
|
||||||
|
? (CoverType.asset, builtInAssetImages.first)
|
||||||
|
: (CoverType.color, '0xffe8e0ff'),
|
||||||
),
|
),
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: true,
|
||||||
leftIcon: const FlowySvg(FlowySvgs.image_s),
|
leftIcon: const FlowySvg(FlowySvgs.image_s),
|
||||||
@ -373,6 +379,12 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return PlatformExtension.isDesktopOrWeb
|
||||||
|
? _buildDesktopCover()
|
||||||
|
: _buildMobileCover();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDesktopCover() {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: kCoverHeight,
|
height: kCoverHeight,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
@ -393,10 +405,82 @@ class DocumentCoverState extends State<DocumentCover> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildMobileCover() {
|
||||||
|
return SizedBox(
|
||||||
|
height: kCoverHeight,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: double.infinity,
|
||||||
|
width: double.infinity,
|
||||||
|
child: _buildCoverImage(),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 8,
|
||||||
|
right: 12,
|
||||||
|
child: RoundedTextButton(
|
||||||
|
onPressed: () {
|
||||||
|
showFlowyMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
title: LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||||
|
builder: (context) {
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxHeight: 340,
|
||||||
|
minHeight: 80,
|
||||||
|
),
|
||||||
|
child: UploadImageMenu(
|
||||||
|
supportTypes: const [
|
||||||
|
UploadImageType.color,
|
||||||
|
UploadImageType.local,
|
||||||
|
UploadImageType.url,
|
||||||
|
UploadImageType.unsplash,
|
||||||
|
],
|
||||||
|
onSelectedLocalImage: (path) async {
|
||||||
|
context.pop();
|
||||||
|
widget.onCoverChanged(CoverType.file, path);
|
||||||
|
},
|
||||||
|
onSelectedAIImage: (_) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
},
|
||||||
|
onSelectedNetworkImage: (url) async {
|
||||||
|
context.pop();
|
||||||
|
widget.onCoverChanged(CoverType.file, url);
|
||||||
|
},
|
||||||
|
onSelectedColor: (color) {
|
||||||
|
context.pop();
|
||||||
|
widget.onCoverChanged(CoverType.color, color);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
fillColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
width: 120,
|
||||||
|
height: 32,
|
||||||
|
title: LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildCoverImage() {
|
Widget _buildCoverImage() {
|
||||||
|
final detail = widget.coverDetails;
|
||||||
|
if (detail == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
switch (widget.coverType) {
|
switch (widget.coverType) {
|
||||||
case CoverType.file:
|
case CoverType.file:
|
||||||
final imageFile = File(widget.coverDetails ?? "");
|
if (isURL(detail)) {
|
||||||
|
return CachedNetworkImage(
|
||||||
|
imageUrl: detail,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final imageFile = File(detail);
|
||||||
if (!imageFile.existsSync()) {
|
if (!imageFile.existsSync()) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
widget.onCoverChanged(CoverType.none, null);
|
widget.onCoverChanged(CoverType.none, null);
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class ImagePickerPage extends StatefulWidget {
|
||||||
|
const ImagePickerPage({
|
||||||
|
super.key,
|
||||||
|
// required this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
// final void Function(EmojiPickerResult) onSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ImagePickerPage> createState() => _ImagePickerPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImagePickerPageState extends State<ImagePickerPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
titleSpacing: 0,
|
||||||
|
title: const FlowyText.semibold(
|
||||||
|
'Page icon',
|
||||||
|
fontSize: 14.0,
|
||||||
|
),
|
||||||
|
leading: AppBarBackButton(
|
||||||
|
onTap: () => context.pop(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: UploadImageMenu(
|
||||||
|
onSubmitted: (_) {},
|
||||||
|
onUpload: (_) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MobileImagePickerScreen extends StatelessWidget {
|
||||||
|
static const routeName = '/image_picker';
|
||||||
|
|
||||||
|
const MobileImagePickerScreen({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const ImagePickerPage();
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,15 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/open_ai_image_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/open_ai_image_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/stability_ai_image_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/stability_ai_image_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
import 'package:appflowy/user/application/user_service.dart';
|
import 'package:appflowy/user/application/user_service.dart';
|
||||||
import 'package:appflowy/util/platform_extension.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart' hide ColorOption;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -16,7 +19,8 @@ enum UploadImageType {
|
|||||||
url,
|
url,
|
||||||
unsplash,
|
unsplash,
|
||||||
stabilityAI,
|
stabilityAI,
|
||||||
openAI;
|
openAI,
|
||||||
|
color;
|
||||||
|
|
||||||
String get description {
|
String get description {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
@ -30,6 +34,8 @@ enum UploadImageType {
|
|||||||
return LocaleKeys.document_imageBlock_ai_label.tr();
|
return LocaleKeys.document_imageBlock_ai_label.tr();
|
||||||
case UploadImageType.stabilityAI:
|
case UploadImageType.stabilityAI:
|
||||||
return LocaleKeys.document_imageBlock_stability_ai_label.tr();
|
return LocaleKeys.document_imageBlock_stability_ai_label.tr();
|
||||||
|
case UploadImageType.color:
|
||||||
|
return LocaleKeys.document_plugins_cover_colors.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,12 +46,14 @@ class UploadImageMenu extends StatefulWidget {
|
|||||||
required this.onSelectedLocalImage,
|
required this.onSelectedLocalImage,
|
||||||
required this.onSelectedAIImage,
|
required this.onSelectedAIImage,
|
||||||
required this.onSelectedNetworkImage,
|
required this.onSelectedNetworkImage,
|
||||||
|
this.onSelectedColor,
|
||||||
this.supportTypes = UploadImageType.values,
|
this.supportTypes = UploadImageType.values,
|
||||||
});
|
});
|
||||||
|
|
||||||
final void Function(String? path) onSelectedLocalImage;
|
final void Function(String? path) onSelectedLocalImage;
|
||||||
final void Function(String url) onSelectedAIImage;
|
final void Function(String url) onSelectedAIImage;
|
||||||
final void Function(String url) onSelectedNetworkImage;
|
final void Function(String url) onSelectedNetworkImage;
|
||||||
|
final void Function(String color)? onSelectedColor;
|
||||||
final List<UploadImageType> supportTypes;
|
final List<UploadImageType> supportTypes;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -128,18 +136,23 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTab() {
|
Widget _buildTab() {
|
||||||
final type = UploadImageType.values[currentTabIndex];
|
final constraints =
|
||||||
|
PlatformExtension.isMobile ? const BoxConstraints(minHeight: 92) : null;
|
||||||
|
final type = values[currentTabIndex];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case UploadImageType.local:
|
case UploadImageType.local:
|
||||||
return Padding(
|
return Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
constraints: constraints,
|
||||||
child: UploadImageFileWidget(
|
child: UploadImageFileWidget(
|
||||||
onPickFile: widget.onSelectedLocalImage,
|
onPickFile: widget.onSelectedLocalImage,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case UploadImageType.url:
|
case UploadImageType.url:
|
||||||
return Padding(
|
return Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
constraints: constraints,
|
||||||
child: EmbedImageUrlWidget(
|
child: EmbedImageUrlWidget(
|
||||||
onSubmit: widget.onSelectedNetworkImage,
|
onSubmit: widget.onSelectedNetworkImage,
|
||||||
),
|
),
|
||||||
@ -156,8 +169,9 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
|||||||
case UploadImageType.openAI:
|
case UploadImageType.openAI:
|
||||||
return supportOpenAI
|
return supportOpenAI
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
constraints: constraints,
|
||||||
child: OpenAIImageWidget(
|
child: OpenAIImageWidget(
|
||||||
onSelectNetworkImage: widget.onSelectedAIImage,
|
onSelectNetworkImage: widget.onSelectedAIImage,
|
||||||
),
|
),
|
||||||
@ -172,7 +186,7 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
|||||||
case UploadImageType.stabilityAI:
|
case UploadImageType.stabilityAI:
|
||||||
return supportStabilityAI
|
return supportStabilityAI
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: StabilityAIImageWidget(
|
child: StabilityAIImageWidget(
|
||||||
onSelectImage: widget.onSelectedLocalImage,
|
onSelectImage: widget.onSelectedLocalImage,
|
||||||
@ -186,6 +200,28 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
|||||||
.tr(),
|
.tr(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
case UploadImageType.color:
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return Container(
|
||||||
|
constraints: constraints,
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: CoverColorPicker(
|
||||||
|
pickerBackgroundColor: theme.cardColor,
|
||||||
|
pickerItemHoverColor: theme.hoverColor,
|
||||||
|
backgroundColorOptions: FlowyTint.values
|
||||||
|
.map<ColorOption>(
|
||||||
|
(t) => ColorOption(
|
||||||
|
colorHex: t.color(context).toHex(),
|
||||||
|
name: t.tintName(AppFlowyEditorL10n.current),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onSubmittedBackgroundColorHex: (color) {
|
||||||
|
widget.onSelectedColor?.call(color);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/network_monitor.dart';
|
import 'package:appflowy/core/network_monitor.dart';
|
||||||
import 'package:appflowy/env/env.dart';
|
import 'package:appflowy/env/env.dart';
|
||||||
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||||
@ -143,6 +144,7 @@ void _resolveHomeDeps(GetIt getIt) {
|
|||||||
getIt.registerSingleton(FToast());
|
getIt.registerSingleton(FToast());
|
||||||
|
|
||||||
getIt.registerSingleton(MenuSharedState());
|
getIt.registerSingleton(MenuSharedState());
|
||||||
|
getIt.registerSingleton(MobileRouterRecord());
|
||||||
|
|
||||||
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
|
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
|
||||||
(user, _) => UserListener(userProfile: user),
|
(user, _) => UserListener(userProfile: user),
|
||||||
|
@ -4,6 +4,7 @@ import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
|||||||
import 'package:appflowy/mobile/presentation/favorite/mobile_favorite_page.dart';
|
import 'package:appflowy/mobile/presentation/favorite/mobile_favorite_page.dart';
|
||||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_picker_screen.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/startup/tasks/app_widget.dart';
|
import 'package:appflowy/startup/tasks/app_widget.dart';
|
||||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||||
@ -51,6 +52,7 @@ GoRouter generateRouter(Widget child) {
|
|||||||
|
|
||||||
// emoji picker
|
// emoji picker
|
||||||
_mobileEmojiPickerPageRoute(),
|
_mobileEmojiPickerPageRoute(),
|
||||||
|
_mobileImagePickerPageRoute(),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Desktop and Mobile
|
// Desktop and Mobile
|
||||||
@ -216,6 +218,18 @@ GoRoute _mobileEmojiPickerPageRoute() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GoRoute _mobileImagePickerPageRoute() {
|
||||||
|
return GoRoute(
|
||||||
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
|
path: MobileImagePickerScreen.routeName,
|
||||||
|
pageBuilder: (context, state) {
|
||||||
|
return const MaterialPage(
|
||||||
|
child: MobileImagePickerScreen(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
GoRoute _desktopHomeScreenRoute() {
|
GoRoute _desktopHomeScreenRoute() {
|
||||||
return GoRoute(
|
return GoRoute(
|
||||||
path: DesktopHomeScreen.routeName,
|
path: DesktopHomeScreen.routeName,
|
||||||
|
@ -178,6 +178,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.6.0"
|
version: "8.6.0"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
calendar_view:
|
calendar_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -539,6 +563,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.1.3"
|
version: "8.1.3"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.1"
|
||||||
flutter_colorpicker:
|
flutter_colorpicker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1082,6 +1114,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1575,6 +1615,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: "8ed044102f3135add97be8653662052838859f5400075ef227f8ad72ae320803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1672,6 +1728,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.1"
|
version: "0.3.1"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
table_calendar:
|
table_calendar:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -124,6 +124,7 @@ dependencies:
|
|||||||
flutter_slidable: ^3.0.0
|
flutter_slidable: ^3.0.0
|
||||||
image_picker: ^1.0.4
|
image_picker: ^1.0.4
|
||||||
image_gallery_saver: ^2.0.3
|
image_gallery_saver: ^2.0.3
|
||||||
|
cached_network_image: ^3.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^2.0.1
|
||||||
|
@ -186,7 +186,8 @@
|
|||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
"clickToHidePersonal": "Click to hide personal section",
|
"clickToHidePersonal": "Click to hide personal section",
|
||||||
"clickToHideFavorites": "Click to hide favorite section",
|
"clickToHideFavorites": "Click to hide favorite section",
|
||||||
"addAPage": "Add a page"
|
"addAPage": "Add a page",
|
||||||
|
"recent": "Recent"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"export": {
|
"export": {
|
||||||
|
@ -228,6 +228,22 @@ pub(crate) async fn read_favorites_handler(
|
|||||||
}
|
}
|
||||||
data_result_ok(RepeatedViewPB { items: views })
|
data_result_ok(RepeatedViewPB { items: views })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
|
pub(crate) async fn read_recent_views_handler(
|
||||||
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> DataResult<RepeatedViewPB, FlowyError> {
|
||||||
|
let folder = upgrade_folder(folder)?;
|
||||||
|
let recent_items = folder.get_all_recent_sections().await;
|
||||||
|
let mut views = vec![];
|
||||||
|
for item in recent_items {
|
||||||
|
if let Ok(view) = folder.get_view_pb(&item.id).await {
|
||||||
|
views.push(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_result_ok(RepeatedViewPB { items: views })
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub(crate) async fn read_trash_handler(
|
pub(crate) async fn read_trash_handler(
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
@ -36,6 +36,7 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||||||
.event(FolderEvent::GetFolderSnapshots, get_folder_snapshots_handler)
|
.event(FolderEvent::GetFolderSnapshots, get_folder_snapshots_handler)
|
||||||
.event(FolderEvent::UpdateViewIcon, update_view_icon_handler)
|
.event(FolderEvent::UpdateViewIcon, update_view_icon_handler)
|
||||||
.event(FolderEvent::ReadFavorites, read_favorites_handler)
|
.event(FolderEvent::ReadFavorites, read_favorites_handler)
|
||||||
|
.event(FolderEvent::ReadRecentViews, read_recent_views_handler)
|
||||||
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,4 +146,7 @@ pub enum FolderEvent {
|
|||||||
|
|
||||||
#[event(input = "UpdateViewIconPayloadPB")]
|
#[event(input = "UpdateViewIconPayloadPB")]
|
||||||
UpdateViewIcon = 35,
|
UpdateViewIcon = 35,
|
||||||
|
|
||||||
|
#[event(output = "RepeatedViewPB")]
|
||||||
|
ReadRecentViews = 36,
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ use collab::core::collab::{CollabRawData, MutexCollab};
|
|||||||
use collab::core::collab_state::SyncState;
|
use collab::core::collab_state::SyncState;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use collab_folder::{
|
use collab_folder::{
|
||||||
Folder, FolderData, FolderNotify, SectionItem, TrashChange, TrashChangeReceiver, TrashInfo,
|
Folder, FolderData, FolderNotify, Section, SectionItem, TrashChange, TrashChangeReceiver,
|
||||||
UserId, View, ViewChange, ViewChangeReceiver, ViewLayout, ViewUpdate, Workspace,
|
TrashInfo, UserId, View, ViewChange, ViewChangeReceiver, ViewLayout, ViewUpdate, Workspace,
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use tokio_stream::wrappers::WatchStream;
|
use tokio_stream::wrappers::WatchStream;
|
||||||
@ -745,6 +745,7 @@ impl FolderManager {
|
|||||||
|| Err(FlowyError::record_not_found()),
|
|| Err(FlowyError::record_not_found()),
|
||||||
|folder| {
|
|folder| {
|
||||||
folder.set_current_view(view_id);
|
folder.set_current_view(view_id);
|
||||||
|
folder.add_recent_view_ids(vec![view_id.to_string()]);
|
||||||
Ok(folder.get_workspace_id())
|
Ok(folder.get_workspace_id())
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@ -800,17 +801,12 @@ impl FolderManager {
|
|||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
pub(crate) async fn get_all_favorites(&self) -> Vec<SectionItem> {
|
pub(crate) async fn get_all_favorites(&self) -> Vec<SectionItem> {
|
||||||
self.with_folder(Vec::new, |folder| {
|
self.get_sections(Section::Favorite)
|
||||||
let trash_ids = folder
|
}
|
||||||
.get_all_trash()
|
|
||||||
.into_iter()
|
|
||||||
.map(|trash| trash.id)
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
let mut views = folder.get_all_favorites();
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
views.retain(|view| !trash_ids.contains(&view.id));
|
pub(crate) async fn get_all_recent_sections(&self) -> Vec<SectionItem> {
|
||||||
views
|
self.get_sections(Section::Recent)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
@ -1039,6 +1035,26 @@ impl FolderManager {
|
|||||||
pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
|
pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
|
||||||
&self.cloud_service
|
&self.cloud_service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_sections(&self, section_type: Section) -> Vec<SectionItem> {
|
||||||
|
self.with_folder(Vec::new, |folder| {
|
||||||
|
let trash_ids = folder
|
||||||
|
.get_all_trash()
|
||||||
|
.into_iter()
|
||||||
|
.map(|trash| trash.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let mut views = match section_type {
|
||||||
|
Section::Favorite => folder.get_all_favorites(),
|
||||||
|
Section::Recent => folder.get_all_recent_sections(),
|
||||||
|
_ => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
// filter the views that are in the trash
|
||||||
|
views.retain(|view| !trash_ids.contains(&view.id));
|
||||||
|
views
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Listen on the [ViewChange] after create/delete/update events happened
|
/// Listen on the [ViewChange] after create/delete/update events happened
|
||||||
|
Reference in New Issue
Block a user