mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support private section (#4882)
This commit is contained in:
parent
9201cd6347
commit
ef9891abfe
5
frontend/.vscode/launch.json
vendored
5
frontend/.vscode/launch.json
vendored
@ -115,9 +115,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AF-desktop: Debug Rust",
|
"name": "AF-desktop: Debug Rust",
|
||||||
"request": "attach",
|
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
|
"request": "attach",
|
||||||
"pid": "${command:pickMyProcess}"
|
"pid": "${command:pickMyProcess}"
|
||||||
|
// To launch the application directly, use the following configuration:
|
||||||
|
// "request": "launch",
|
||||||
|
// "program": "[YOUR_APPLICATION_PATH]",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// https://tauri.app/v1/guides/debugging/vs-code
|
// https://tauri.app/v1/guides/debugging/vs-code
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// ignore_for_file: unused_import
|
// ignore_for_file: unused_import
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:appflowy/env/cloud_env.dart';
|
import 'package:appflowy/env/cloud_env.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
@ -14,8 +15,9 @@ 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/uuid.dart';
|
import 'package:flowy_infra/uuid.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
import '../shared/dir.dart';
|
import '../shared/dir.dart';
|
||||||
import '../shared/mock/mock_file_picker.dart';
|
import '../shared/mock/mock_file_picker.dart';
|
||||||
import '../shared/util.dart';
|
import '../shared/util.dart';
|
||||||
|
@ -28,15 +28,16 @@ void main() {
|
|||||||
final email = '${uuid()}@appflowy.io';
|
final email = '${uuid()}@appflowy.io';
|
||||||
|
|
||||||
testWidgets('change name and icon', (tester) async {
|
testWidgets('change name and icon', (tester) async {
|
||||||
|
// only run the test when the feature flag is on
|
||||||
|
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await tester.initializeAppFlowy(
|
await tester.initializeAppFlowy(
|
||||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||||
email: email, // use the same email to check the next test
|
email: email, // use the same email to check the next test
|
||||||
);
|
);
|
||||||
|
|
||||||
// turn on the collaborative workspace feature flag before testing,
|
|
||||||
// if the feature is released to the public, this step can be removed
|
|
||||||
await FeatureFlag.collaborativeWorkspace.turnOn();
|
|
||||||
|
|
||||||
await tester.tapGoogleLoginInButton();
|
await tester.tapGoogleLoginInButton();
|
||||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||||
|
|
||||||
@ -57,15 +58,16 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('verify the result again after relaunching', (tester) async {
|
testWidgets('verify the result again after relaunching', (tester) async {
|
||||||
|
// only run the test when the feature flag is on
|
||||||
|
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await tester.initializeAppFlowy(
|
await tester.initializeAppFlowy(
|
||||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||||
email: email, // use the same email to check the next test
|
email: email, // use the same email to check the next test
|
||||||
);
|
);
|
||||||
|
|
||||||
// turn on the collaborative workspace feature flag before testing,
|
|
||||||
// if the feature is released to the public, this step can be removed
|
|
||||||
await FeatureFlag.collaborativeWorkspace.turnOn();
|
|
||||||
|
|
||||||
await tester.tapGoogleLoginInButton();
|
await tester.tapGoogleLoginInButton();
|
||||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||||
|
|
||||||
|
@ -35,14 +35,14 @@ void main() {
|
|||||||
final email = '${uuid()}@appflowy.io';
|
final email = '${uuid()}@appflowy.io';
|
||||||
|
|
||||||
group('collaborative workspace', () {
|
group('collaborative workspace', () {
|
||||||
|
// combine the create and delete workspace test to reduce the time
|
||||||
|
testWidgets('create a new workspace, open it and then delete it',
|
||||||
|
(tester) async {
|
||||||
// only run the test when the feature flag is on
|
// only run the test when the feature flag is on
|
||||||
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// combine the create and delete workspace test to reduce the time
|
|
||||||
testWidgets('create a new workspace, open it and then delete it',
|
|
||||||
(tester) async {
|
|
||||||
await tester.initializeAppFlowy(
|
await tester.initializeAppFlowy(
|
||||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_folder.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -13,10 +13,10 @@ void main() {
|
|||||||
|
|
||||||
group('sidebar expand test', () {
|
group('sidebar expand test', () {
|
||||||
bool isExpanded({required FolderCategoryType type}) {
|
bool isExpanded({required FolderCategoryType type}) {
|
||||||
if (type == FolderCategoryType.personal) {
|
if (type == FolderCategoryType.private) {
|
||||||
return find
|
return find
|
||||||
.descendant(
|
.descendant(
|
||||||
of: find.byType(PersonalFolder),
|
of: find.byType(PrivateSectionFolder),
|
||||||
matching: find.byType(ViewItem),
|
matching: find.byType(ViewItem),
|
||||||
)
|
)
|
||||||
.evaluate()
|
.evaluate()
|
||||||
@ -30,19 +30,19 @@ void main() {
|
|||||||
await tester.tapGoButton();
|
await tester.tapGoButton();
|
||||||
|
|
||||||
// first time is expanded
|
// first time is expanded
|
||||||
expect(isExpanded(type: FolderCategoryType.personal), true);
|
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||||
|
|
||||||
// collapse the personal folder
|
// collapse the personal folder
|
||||||
await tester.tapButton(
|
await tester.tapButton(
|
||||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePersonal.tr()),
|
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||||
);
|
);
|
||||||
expect(isExpanded(type: FolderCategoryType.personal), false);
|
expect(isExpanded(type: FolderCategoryType.private), false);
|
||||||
|
|
||||||
// expand the personal folder
|
// expand the personal folder
|
||||||
await tester.tapButton(
|
await tester.tapButton(
|
||||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePersonal.tr()),
|
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||||
);
|
);
|
||||||
expect(isExpanded(type: FolderCategoryType.personal), true);
|
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/_favorite_folder.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
import 'sidebar_expand_test.dart' as sidebar_expanded_test;
|
|
||||||
import 'sidebar_favorites_test.dart' as sidebar_favorite_test;
|
import 'sidebar_favorites_test.dart' as sidebar_favorite_test;
|
||||||
import 'sidebar_icon_test.dart' as sidebar_icon_test;
|
import 'sidebar_icon_test.dart' as sidebar_icon_test;
|
||||||
import 'sidebar_test.dart' as sidebar_test;
|
import 'sidebar_test.dart' as sidebar_test;
|
||||||
@ -10,7 +9,7 @@ void startTesting() {
|
|||||||
|
|
||||||
// Sidebar integration tests
|
// Sidebar integration tests
|
||||||
sidebar_test.main();
|
sidebar_test.main();
|
||||||
sidebar_expanded_test.main();
|
// sidebar_expanded_test.main();
|
||||||
sidebar_favorite_test.main();
|
sidebar_favorite_test.main();
|
||||||
sidebar_icon_test.main();
|
sidebar_icon_test.main();
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder.dart';
|
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder.dart';
|
||||||
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
|
// Contains Public And Private Sections
|
||||||
class MobileFolders extends StatelessWidget {
|
class MobileFolders extends StatelessWidget {
|
||||||
const MobileFolders({
|
const MobileFolders({
|
||||||
super.key,
|
super.key,
|
||||||
@ -26,9 +30,9 @@ class MobileFolders extends StatelessWidget {
|
|||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) => SidebarRootViewsBloc()
|
create: (_) => SidebarSectionsBloc()
|
||||||
..add(
|
..add(
|
||||||
SidebarRootViewsEvent.initial(
|
SidebarSectionsEvent.initial(
|
||||||
user,
|
user,
|
||||||
workspaceSetting.workspaceId,
|
workspaceSetting.workspaceId,
|
||||||
),
|
),
|
||||||
@ -38,31 +42,46 @@ class MobileFolders extends StatelessWidget {
|
|||||||
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
|
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MultiBlocListener(
|
child: BlocConsumer<SidebarSectionsBloc, SidebarSectionsState>(
|
||||||
listeners: [
|
|
||||||
BlocListener<SidebarRootViewsBloc, SidebarRootViewState>(
|
|
||||||
listenWhen: (p, c) =>
|
listenWhen: (p, c) =>
|
||||||
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
||||||
listener: (context, state) =>
|
listener: (context, state) {
|
||||||
context.pushView(state.lastCreatedRootView!),
|
final lastCreatedRootView = state.lastCreatedRootView;
|
||||||
),
|
if (lastCreatedRootView != null) {
|
||||||
],
|
context.pushView(lastCreatedRootView);
|
||||||
child: Builder(
|
}
|
||||||
builder: (context) {
|
},
|
||||||
final menuState = context.watch<SidebarRootViewsBloc>().state;
|
builder: (context, state) {
|
||||||
|
final isCollaborativeWorkspace =
|
||||||
|
user.authenticator != AuthenticatorPB.Local &&
|
||||||
|
FeatureFlag.collaborativeWorkspace.isOn;
|
||||||
return SlidableAutoCloseBehavior(
|
return SlidableAutoCloseBehavior(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
MobilePersonalFolder(
|
...isCollaborativeWorkspace
|
||||||
views: menuState.views,
|
? [
|
||||||
|
MobileSectionFolder(
|
||||||
|
title: LocaleKeys.sideBar_public.tr(),
|
||||||
|
views: state.section.publicViews,
|
||||||
),
|
),
|
||||||
const VSpace(8.0),
|
const VSpace(8.0),
|
||||||
|
MobileSectionFolder(
|
||||||
|
title: LocaleKeys.sideBar_private.tr(),
|
||||||
|
views: state.section.privateViews,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
MobileSectionFolder(
|
||||||
|
title: LocaleKeys.sideBar_personal.tr(),
|
||||||
|
views: state.section.publicViews,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
const VSpace(8.0),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
|||||||
import 'package:appflowy/mobile/presentation/home/recent_folder/mobile_home_recent_views.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/application/user/user_workspace_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
||||||
@ -15,6 +16,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@ -82,6 +84,16 @@ class MobileHomePage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (_) => UserWorkspaceBloc(userProfile: userProfile)
|
||||||
|
..add(
|
||||||
|
const UserWorkspaceEvent.initial(),
|
||||||
|
),
|
||||||
|
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
|
||||||
|
buildWhen: (previous, current) =>
|
||||||
|
previous.currentWorkspace?.workspaceId !=
|
||||||
|
current.currentWorkspace?.workspaceId,
|
||||||
|
builder: (context, state) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// Header
|
// Header
|
||||||
@ -132,6 +144,9 @@ class MobileHomePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,13 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:appflowy/mobile/presentation/home/mobile_home_setting_page.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_home_setting_page.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/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
|
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -14,7 +17,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class MobileHomePageHeader extends StatelessWidget {
|
class MobileHomePageHeader extends StatelessWidget {
|
||||||
const MobileHomePageHeader({super.key, required this.userProfile});
|
const MobileHomePageHeader({
|
||||||
|
super.key,
|
||||||
|
required this.userProfile,
|
||||||
|
});
|
||||||
|
|
||||||
final UserProfilePB userProfile;
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
@ -25,29 +31,18 @@ class MobileHomePageHeader extends StatelessWidget {
|
|||||||
..add(const SettingsUserEvent.initial()),
|
..add(const SettingsUserEvent.initial()),
|
||||||
child: BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
|
child: BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final userIcon = state.userProfile.iconUrl;
|
final isCollaborativeWorkspace =
|
||||||
|
userProfile.authenticator != AuthenticatorPB.Local &&
|
||||||
|
FeatureFlag.collaborativeWorkspace.isOn;
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minHeight: 52),
|
constraints: const BoxConstraints(minHeight: 52),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_UserIcon(userIcon: userIcon),
|
|
||||||
const HSpace(12),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: isCollaborativeWorkspace
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
? _MobileWorkspace(userProfile: userProfile)
|
||||||
children: [
|
: _MobileUser(userProfile: userProfile),
|
||||||
const FlowyText.medium('AppFlowy', fontSize: 18),
|
|
||||||
const VSpace(4),
|
|
||||||
FlowyText.regular(
|
|
||||||
userProfile.email.isNotEmpty
|
|
||||||
? state.userProfile.email
|
|
||||||
: state.userProfile.name,
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
@ -63,6 +58,83 @@ class MobileHomePageHeader extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _MobileUser extends StatelessWidget {
|
||||||
|
const _MobileUser({
|
||||||
|
required this.userProfile,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final userIcon = userProfile.iconUrl;
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
_UserIcon(userIcon: userIcon),
|
||||||
|
const HSpace(12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const FlowyText.medium('AppFlowy', fontSize: 18),
|
||||||
|
const VSpace(4),
|
||||||
|
FlowyText.regular(
|
||||||
|
userProfile.email.isNotEmpty
|
||||||
|
? userProfile.email
|
||||||
|
: userProfile.name,
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MobileWorkspace extends StatelessWidget {
|
||||||
|
const _MobileWorkspace({
|
||||||
|
required this.userProfile,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final currentWorkspace = state.currentWorkspace;
|
||||||
|
final workspaces = state.workspaces;
|
||||||
|
if (currentWorkspace == null || workspaces.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
const HSpace(2.0),
|
||||||
|
SizedBox.square(
|
||||||
|
dimension: 34.0,
|
||||||
|
child: WorkspaceIcon(
|
||||||
|
workspace: currentWorkspace,
|
||||||
|
iconSize: 26,
|
||||||
|
enableEdit: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(8),
|
||||||
|
Expanded(
|
||||||
|
child: FlowyText.medium(
|
||||||
|
currentWorkspace.name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _UserIcon extends StatelessWidget {
|
class _UserIcon extends StatelessWidget {
|
||||||
const _UserIcon({
|
const _UserIcon({
|
||||||
required this.userIcon,
|
required this.userIcon,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/personal_folder/mobile_home_personal_folder_header.dart';
|
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder_header.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
@ -9,18 +9,20 @@ 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';
|
||||||
|
|
||||||
class MobilePersonalFolder extends StatelessWidget {
|
class MobileSectionFolder extends StatelessWidget {
|
||||||
const MobilePersonalFolder({
|
const MobileSectionFolder({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.title,
|
||||||
required this.views,
|
required this.views,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
final List<ViewPB> views;
|
final List<ViewPB> views;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<FolderBloc>(
|
return BlocProvider<FolderBloc>(
|
||||||
create: (context) => FolderBloc(type: FolderCategoryType.personal)
|
create: (context) => FolderBloc(type: FolderCategoryType.private)
|
||||||
..add(
|
..add(
|
||||||
const FolderEvent.initial(),
|
const FolderEvent.initial(),
|
||||||
),
|
),
|
||||||
@ -28,7 +30,8 @@ class MobilePersonalFolder extends StatelessWidget {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
MobilePersonalFolderHeader(
|
MobileSectionFolderHeader(
|
||||||
|
title: title,
|
||||||
isExpanded: context.read<FolderBloc>().state.isExpanded,
|
isExpanded: context.read<FolderBloc>().state.isExpanded,
|
||||||
onPressed: () => context
|
onPressed: () => context
|
||||||
.read<FolderBloc>()
|
.read<FolderBloc>()
|
||||||
@ -45,9 +48,9 @@ class MobilePersonalFolder extends StatelessWidget {
|
|||||||
...views.map(
|
...views.map(
|
||||||
(view) => MobileViewItem(
|
(view) => MobileViewItem(
|
||||||
key: ValueKey(
|
key: ValueKey(
|
||||||
'${FolderCategoryType.personal.name} ${view.id}',
|
'${FolderCategoryType.private.name} ${view.id}',
|
||||||
),
|
),
|
||||||
categoryType: FolderCategoryType.personal,
|
categoryType: FolderCategoryType.private,
|
||||||
isFirstChild: view.id == views.first.id,
|
isFirstChild: view.id == views.first.id,
|
||||||
view: view,
|
view: view,
|
||||||
level: 0,
|
level: 0,
|
@ -1,30 +1,32 @@
|
|||||||
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/menu/sidebar_root_views_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.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/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';
|
||||||
|
|
||||||
class MobilePersonalFolderHeader extends StatefulWidget {
|
class MobileSectionFolderHeader extends StatefulWidget {
|
||||||
const MobilePersonalFolderHeader({
|
const MobileSectionFolderHeader({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.title,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
required this.onAdded,
|
required this.onAdded,
|
||||||
required this.isExpanded,
|
required this.isExpanded,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
final VoidCallback onAdded;
|
final VoidCallback onAdded;
|
||||||
final bool isExpanded;
|
final bool isExpanded;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MobilePersonalFolderHeader> createState() =>
|
State<MobileSectionFolderHeader> createState() =>
|
||||||
_MobilePersonalFolderHeaderState();
|
_MobileSectionFolderHeaderState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MobilePersonalFolderHeaderState
|
class _MobileSectionFolderHeaderState extends State<MobileSectionFolderHeader> {
|
||||||
extends State<MobilePersonalFolderHeader> {
|
|
||||||
double _turns = 0;
|
double _turns = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -35,7 +37,7 @@ class _MobilePersonalFolderHeaderState
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
text: FlowyText.semibold(
|
text: FlowyText.semibold(
|
||||||
LocaleKeys.sideBar_personal.tr(),
|
widget.title,
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||||
@ -71,6 +73,7 @@ class _MobilePersonalFolderHeaderState
|
|||||||
SidebarRootViewsEvent.createRootView(
|
SidebarRootViewsEvent.createRootView(
|
||||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
index: 0,
|
index: 0,
|
||||||
|
viewSection: ViewSectionPB.Private,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
@ -4,7 +4,7 @@ import 'package:appflowy/mobile/presentation/notifications/widgets/mobile_notifi
|
|||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/notification_filter/notification_filter_bloc.dart';
|
import 'package:appflowy/user/application/notification_filter/notification_filter_bloc.dart';
|
||||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
||||||
import 'package:appflowy/workspace/presentation/notifications/reminder_extension.dart';
|
import 'package:appflowy/workspace/presentation/notifications/reminder_extension.dart';
|
||||||
import 'package:appflowy/workspace/presentation/notifications/widgets/inbox_action_bar.dart';
|
import 'package:appflowy/workspace/presentation/notifications/widgets/inbox_action_bar.dart';
|
||||||
@ -80,15 +80,15 @@ class _NotificationScreenContent extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => SidebarRootViewsBloc()
|
create: (_) => SidebarSectionsBloc()
|
||||||
..add(
|
..add(
|
||||||
SidebarRootViewsEvent.initial(
|
SidebarSectionsEvent.initial(
|
||||||
userProfile,
|
userProfile,
|
||||||
workspaceSetting.workspaceId,
|
workspaceSetting.workspaceId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: BlocBuilder<SidebarRootViewsBloc, SidebarRootViewState>(
|
child: BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
|
||||||
builder: (context, menuState) =>
|
builder: (context, sectionState) =>
|
||||||
BlocBuilder<NotificationFilterBloc, NotificationFilterState>(
|
BlocBuilder<NotificationFilterBloc, NotificationFilterState>(
|
||||||
builder: (context, filterState) =>
|
builder: (context, filterState) =>
|
||||||
BlocBuilder<ReminderBloc, ReminderState>(
|
BlocBuilder<ReminderBloc, ReminderState>(
|
||||||
@ -122,7 +122,7 @@ class _NotificationScreenContent extends StatelessWidget {
|
|||||||
NotificationsView(
|
NotificationsView(
|
||||||
shownReminders: pastReminders,
|
shownReminders: pastReminders,
|
||||||
reminderBloc: reminderBloc,
|
reminderBloc: reminderBloc,
|
||||||
views: menuState.views,
|
views: sectionState.section.publicViews,
|
||||||
onAction: _onAction,
|
onAction: _onAction,
|
||||||
onDelete: _onDelete,
|
onDelete: _onDelete,
|
||||||
onReadChanged: _onReadChanged,
|
onReadChanged: _onReadChanged,
|
||||||
@ -134,7 +134,7 @@ class _NotificationScreenContent extends StatelessWidget {
|
|||||||
NotificationsView(
|
NotificationsView(
|
||||||
shownReminders: upcomingReminders,
|
shownReminders: upcomingReminders,
|
||||||
reminderBloc: reminderBloc,
|
reminderBloc: reminderBloc,
|
||||||
views: menuState.views,
|
views: sectionState.section.publicViews,
|
||||||
isUpcoming: true,
|
isUpcoming: true,
|
||||||
onAction: _onAction,
|
onAction: _onAction,
|
||||||
),
|
),
|
||||||
|
@ -406,6 +406,7 @@ class _SingleMobileInnerViewItemState extends State<SingleMobileInnerViewItem> {
|
|||||||
ViewEvent.createView(
|
ViewEvent.createView(
|
||||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
layout,
|
layout,
|
||||||
|
section: widget.categoryType.toViewSectionPB,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -6,6 +6,7 @@ import 'package:appflowy/plugins/document/application/editor_transaction_adapter
|
|||||||
import 'package:appflowy/plugins/trash/application/trash_service.dart';
|
import 'package:appflowy/plugins/trash/application/trash_service.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/util/json_print.dart';
|
||||||
import 'package:appflowy/workspace/application/doc/doc_listener.dart';
|
import 'package:appflowy/workspace/application/doc/doc_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/doc/sync_state_listener.dart';
|
import 'package:appflowy/workspace/application/doc/sync_state_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
@ -81,30 +82,24 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
final editorState = await _fetchDocumentState();
|
final editorState = await _fetchDocumentState();
|
||||||
_onViewChanged();
|
_onViewChanged();
|
||||||
_onDocumentChanged();
|
_onDocumentChanged();
|
||||||
await editorState.fold(
|
final newState = await editorState.fold(
|
||||||
(s) async {
|
(s) async {
|
||||||
final result = await getIt<AuthService>().getUser();
|
final userProfilePB =
|
||||||
final userProfilePB = result.fold(
|
await getIt<AuthService>().getUser().toNullable();
|
||||||
(s) => s,
|
return state.copyWith(
|
||||||
(e) => null,
|
|
||||||
);
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
error: null,
|
error: null,
|
||||||
editorState: s,
|
editorState: s,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
userProfilePB: userProfilePB,
|
userProfilePB: userProfilePB,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(f) async => emit(
|
(f) async => state.copyWith(
|
||||||
state.copyWith(
|
|
||||||
error: f,
|
error: f,
|
||||||
editorState: null,
|
editorState: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
emit(newState);
|
||||||
},
|
},
|
||||||
moveToTrash: () async {
|
moveToTrash: () async {
|
||||||
emit(state.copyWith(isDeleted: true));
|
emit(state.copyWith(isDeleted: true));
|
||||||
@ -242,21 +237,20 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void syncDocumentDataPB(DocEventPB docEvent) {
|
void syncDocumentDataPB(DocEventPB docEvent) {
|
||||||
// prettyPrintJson(docEvent.toProto3Json());
|
prettyPrintJson(docEvent.toProto3Json());
|
||||||
// todo: integrate the document change to the editor
|
for (final event in docEvent.events) {
|
||||||
// for (final event in docEvent.events) {
|
for (final blockEvent in event.event) {
|
||||||
// for (final blockEvent in event.event) {
|
switch (blockEvent.command) {
|
||||||
// switch (blockEvent.command) {
|
case DeltaTypePB.Inserted:
|
||||||
// case DeltaTypePB.Inserted:
|
break;
|
||||||
// break;
|
case DeltaTypePB.Updated:
|
||||||
// case DeltaTypePB.Updated:
|
break;
|
||||||
// break;
|
case DeltaTypePB.Removed:
|
||||||
// case DeltaTypePB.Removed:
|
break;
|
||||||
// break;
|
default:
|
||||||
// default:
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import 'dart:collection';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/config/kv_keys.dart';
|
import 'package:appflowy/core/config/kv_keys.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
typedef FeatureFlagMap = Map<FeatureFlag, bool>;
|
typedef FeatureFlagMap = Map<FeatureFlag, bool>;
|
||||||
|
|
||||||
@ -19,16 +19,22 @@ enum FeatureFlag {
|
|||||||
|
|
||||||
// used to control the visibility of the members settings
|
// used to control the visibility of the members settings
|
||||||
// if it's on, you can see the members settings in the settings page
|
// if it's on, you can see the members settings in the settings page
|
||||||
membersSettings;
|
membersSettings,
|
||||||
|
|
||||||
|
// used for ignore the conflicted feature flag
|
||||||
|
unknown;
|
||||||
|
|
||||||
static Future<void> initialize() async {
|
static Future<void> initialize() async {
|
||||||
final values = await getIt<KeyValueStorage>().getWithFormat<FeatureFlagMap>(
|
final values = await getIt<KeyValueStorage>().getWithFormat<FeatureFlagMap>(
|
||||||
KVKeys.featureFlag,
|
KVKeys.featureFlag,
|
||||||
(value) => Map.from(jsonDecode(value)).map(
|
(value) => Map.from(jsonDecode(value)).map(
|
||||||
(key, value) => MapEntry(
|
(key, value) {
|
||||||
FeatureFlag.values.firstWhere((e) => e.name == key),
|
final k = FeatureFlag.values.firstWhereOrNull(
|
||||||
value as bool,
|
(e) => e.name == key,
|
||||||
),
|
) ??
|
||||||
|
FeatureFlag.unknown;
|
||||||
|
return MapEntry(k, value as bool);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
) ??
|
) ??
|
||||||
{};
|
{};
|
||||||
@ -76,6 +82,8 @@ enum FeatureFlag {
|
|||||||
return false;
|
return false;
|
||||||
case FeatureFlag.membersSettings:
|
case FeatureFlag.membersSettings:
|
||||||
return false;
|
return false;
|
||||||
|
case FeatureFlag.unknown:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +93,8 @@ enum FeatureFlag {
|
|||||||
return 'if it\'s on, you can see the workspace list and the workspace settings in the top-left corner of the app';
|
return 'if it\'s on, you can see the workspace list and the workspace settings in the top-left corner of the app';
|
||||||
case FeatureFlag.membersSettings:
|
case FeatureFlag.membersSettings:
|
||||||
return 'if it\'s on, you can see the members settings in the settings page';
|
return 'if it\'s on, you can see the members settings in the settings page';
|
||||||
|
case FeatureFlag.unknown:
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,4 +150,44 @@ class UserBackendService {
|
|||||||
..newIcon = icon;
|
..newIcon = icon;
|
||||||
return UserEventChangeWorkspaceIcon(request).send();
|
return UserEventChangeWorkspaceIcon(request).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<FlowyResult<RepeatedWorkspaceMemberPB, FlowyError>>
|
||||||
|
getWorkspaceMembers(
|
||||||
|
String workspaceId,
|
||||||
|
) async {
|
||||||
|
final data = QueryWorkspacePB()..workspaceId = workspaceId;
|
||||||
|
return UserEventGetWorkspaceMember(data).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FlowyResult<void, FlowyError>> addWorkspaceMember(
|
||||||
|
String workspaceId,
|
||||||
|
String email,
|
||||||
|
) async {
|
||||||
|
final data = AddWorkspaceMemberPB()
|
||||||
|
..workspaceId = workspaceId
|
||||||
|
..email = email;
|
||||||
|
return UserEventAddWorkspaceMember(data).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FlowyResult<void, FlowyError>> removeWorkspaceMember(
|
||||||
|
String workspaceId,
|
||||||
|
String email,
|
||||||
|
) async {
|
||||||
|
final data = RemoveWorkspaceMemberPB()
|
||||||
|
..workspaceId = workspaceId
|
||||||
|
..email = email;
|
||||||
|
return UserEventRemoveWorkspaceMember(data).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FlowyResult<void, FlowyError>> updateWorkspaceMember(
|
||||||
|
String workspaceId,
|
||||||
|
String email,
|
||||||
|
AFRolePB role,
|
||||||
|
) async {
|
||||||
|
final data = UpdateWorkspaceMemberPB()
|
||||||
|
..workspaceId = workspaceId
|
||||||
|
..email = email
|
||||||
|
..role = role;
|
||||||
|
return UserEventUpdateWorkspaceMember(data).send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,18 +33,19 @@ class SidebarRootViewsBloc
|
|||||||
await event.when(
|
await event.when(
|
||||||
initial: (userProfile, workspaceId) async {
|
initial: (userProfile, workspaceId) async {
|
||||||
_initial(userProfile, workspaceId);
|
_initial(userProfile, workspaceId);
|
||||||
await _fetchApps(emit);
|
await _fetchRootViews(emit);
|
||||||
},
|
},
|
||||||
reset: (userProfile, workspaceId) async {
|
reset: (userProfile, workspaceId) async {
|
||||||
await _listener?.stop();
|
await _listener?.stop();
|
||||||
_initial(userProfile, workspaceId);
|
_initial(userProfile, workspaceId);
|
||||||
await _fetchApps(emit);
|
await _fetchRootViews(emit);
|
||||||
},
|
},
|
||||||
createRootView: (name, desc, index) async {
|
createRootView: (name, desc, index, section) async {
|
||||||
final result = await _workspaceService.createApp(
|
final result = await _workspaceService.createView(
|
||||||
name: name,
|
name: name,
|
||||||
desc: desc,
|
desc: desc,
|
||||||
index: index,
|
index: index,
|
||||||
|
viewSection: section,
|
||||||
);
|
);
|
||||||
result.fold(
|
result.fold(
|
||||||
(view) => emit(state.copyWith(lastCreatedRootView: view)),
|
(view) => emit(state.copyWith(lastCreatedRootView: view)),
|
||||||
@ -59,48 +60,59 @@ class SidebarRootViewsBloc
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
didReceiveViews: (viewsOrFailure) async {
|
didReceiveViews: (viewsOrFailure) async {
|
||||||
emit(
|
// emit(
|
||||||
viewsOrFailure.fold(
|
// viewsOrFailure.fold(
|
||||||
(views) => state.copyWith(
|
// (views) => state.copyWith(
|
||||||
views: views,
|
// views: views,
|
||||||
successOrFailure: FlowyResult.success(null),
|
// successOrFailure: FlowyResult.success(null),
|
||||||
),
|
// ),
|
||||||
(err) =>
|
// (err) =>
|
||||||
state.copyWith(successOrFailure: FlowyResult.failure(err)),
|
// state.copyWith(successOrFailure: FlowyResult.failure(err)),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
},
|
},
|
||||||
moveRootView: (int fromIndex, int toIndex) {
|
moveRootView: (int fromIndex, int toIndex) {
|
||||||
if (state.views.length > fromIndex) {
|
// if (state.views.length > fromIndex) {
|
||||||
final view = state.views[fromIndex];
|
// final view = state.views[fromIndex];
|
||||||
|
|
||||||
_workspaceService.moveApp(
|
// _workspaceService.moveApp(
|
||||||
appId: view.id,
|
// appId: view.id,
|
||||||
fromIndex: fromIndex,
|
// fromIndex: fromIndex,
|
||||||
toIndex: toIndex,
|
// toIndex: toIndex,
|
||||||
);
|
// );
|
||||||
|
|
||||||
final views = List<ViewPB>.from(state.views);
|
// final views = List<ViewPB>.from(state.views);
|
||||||
views.insert(toIndex, views.removeAt(fromIndex));
|
// views.insert(toIndex, views.removeAt(fromIndex));
|
||||||
emit(state.copyWith(views: views));
|
// emit(state.copyWith(views: views));
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchApps(Emitter<SidebarRootViewState> emit) async {
|
Future<void> _fetchRootViews(
|
||||||
final viewsOrError = await _workspaceService.getViews();
|
Emitter<SidebarRootViewState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final publicViews = await _workspaceService.getPublicViews().getOrThrow();
|
||||||
|
final privateViews =
|
||||||
|
await _workspaceService.getPrivateViews().getOrThrow();
|
||||||
emit(
|
emit(
|
||||||
viewsOrError.fold(
|
state.copyWith(
|
||||||
(views) => state.copyWith(views: views),
|
publicViews: publicViews,
|
||||||
(error) {
|
privateViews: privateViews,
|
||||||
Log.error(error);
|
|
||||||
return state.copyWith(successOrFailure: FlowyResult.failure(error));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error(e);
|
||||||
|
// TODO: handle error
|
||||||
|
// emit(
|
||||||
|
// state.copyWith(
|
||||||
|
// successOrFailure: FlowyResult.failure(e),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleAppsOrFail(FlowyResult<List<ViewPB>, FlowyError> viewsOrFail) {
|
void _handleAppsOrFail(FlowyResult<List<ViewPB>, FlowyError> viewsOrFail) {
|
||||||
@ -137,9 +149,12 @@ class SidebarRootViewsEvent with _$SidebarRootViewsEvent {
|
|||||||
String name, {
|
String name, {
|
||||||
String? desc,
|
String? desc,
|
||||||
int? index,
|
int? index,
|
||||||
|
required ViewSectionPB viewSection,
|
||||||
}) = _createRootView;
|
}) = _createRootView;
|
||||||
const factory SidebarRootViewsEvent.moveRootView(int fromIndex, int toIndex) =
|
const factory SidebarRootViewsEvent.moveRootView(
|
||||||
_MoveRootView;
|
int fromIndex,
|
||||||
|
int toIndex,
|
||||||
|
) = _MoveRootView;
|
||||||
const factory SidebarRootViewsEvent.didReceiveViews(
|
const factory SidebarRootViewsEvent.didReceiveViews(
|
||||||
FlowyResult<List<ViewPB>, FlowyError> appsOrFail,
|
FlowyResult<List<ViewPB>, FlowyError> appsOrFail,
|
||||||
) = _ReceiveApps;
|
) = _ReceiveApps;
|
||||||
@ -148,13 +163,13 @@ class SidebarRootViewsEvent with _$SidebarRootViewsEvent {
|
|||||||
@freezed
|
@freezed
|
||||||
class SidebarRootViewState with _$SidebarRootViewState {
|
class SidebarRootViewState with _$SidebarRootViewState {
|
||||||
const factory SidebarRootViewState({
|
const factory SidebarRootViewState({
|
||||||
required List<ViewPB> views,
|
@Default([]) List<ViewPB> privateViews,
|
||||||
|
@Default([]) List<ViewPB> publicViews,
|
||||||
required FlowyResult<void, FlowyError> successOrFailure,
|
required FlowyResult<void, FlowyError> successOrFailure,
|
||||||
@Default(null) ViewPB? lastCreatedRootView,
|
@Default(null) ViewPB? lastCreatedRootView,
|
||||||
}) = _SidebarRootViewState;
|
}) = _SidebarRootViewState;
|
||||||
|
|
||||||
factory SidebarRootViewState.initial() => SidebarRootViewState(
|
factory SidebarRootViewState.initial() => SidebarRootViewState(
|
||||||
views: [],
|
|
||||||
successOrFailure: FlowyResult.success(null),
|
successOrFailure: FlowyResult.success(null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,261 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:appflowy/workspace/application/workspace/workspace_sections_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/workspace/workspace_service.dart';
|
||||||
|
import 'package:appflowy_backend/log.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-user/user_profile.pb.dart';
|
||||||
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'sidebar_sections_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class SidebarSection {
|
||||||
|
const SidebarSection({
|
||||||
|
required this.publicViews,
|
||||||
|
required this.privateViews,
|
||||||
|
});
|
||||||
|
|
||||||
|
const SidebarSection.empty()
|
||||||
|
: publicViews = const [],
|
||||||
|
privateViews = const [];
|
||||||
|
|
||||||
|
final List<ViewPB> publicViews;
|
||||||
|
final List<ViewPB> privateViews;
|
||||||
|
|
||||||
|
List<ViewPB> get views => publicViews + privateViews;
|
||||||
|
|
||||||
|
SidebarSection copyWith({
|
||||||
|
List<ViewPB>? publicViews,
|
||||||
|
List<ViewPB>? privateViews,
|
||||||
|
}) {
|
||||||
|
return SidebarSection(
|
||||||
|
publicViews: publicViews ?? this.publicViews,
|
||||||
|
privateViews: privateViews ?? this.privateViews,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [SidebarSectionsBloc] is responsible for
|
||||||
|
/// managing the root views in different sections of the workspace.
|
||||||
|
class SidebarSectionsBloc
|
||||||
|
extends Bloc<SidebarSectionsEvent, SidebarSectionsState> {
|
||||||
|
SidebarSectionsBloc() : super(SidebarSectionsState.initial()) {
|
||||||
|
on<SidebarSectionsEvent>(
|
||||||
|
(event, emit) async {
|
||||||
|
await event.when(
|
||||||
|
initial: (userProfile, workspaceId) async {
|
||||||
|
_initial(userProfile, workspaceId);
|
||||||
|
final sectionViews = await _getSectionViews();
|
||||||
|
if (sectionViews != null) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
section: sectionViews,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset: (userProfile, workspaceId) async {
|
||||||
|
_reset(userProfile, workspaceId);
|
||||||
|
final sectionViews = await _getSectionViews();
|
||||||
|
if (sectionViews != null) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
section: sectionViews,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createRootViewInSection: (name, section, desc, index) async {
|
||||||
|
final result = await _workspaceService.createView(
|
||||||
|
name: name,
|
||||||
|
viewSection: section,
|
||||||
|
desc: desc,
|
||||||
|
index: index,
|
||||||
|
);
|
||||||
|
result.fold(
|
||||||
|
(view) => emit(
|
||||||
|
state.copyWith(
|
||||||
|
lastCreatedRootView: view,
|
||||||
|
createRootViewResult: FlowyResult.success(null),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(error) {
|
||||||
|
Log.error('Failed to create root view: $error');
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
createRootViewResult: FlowyResult.failure(error),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
receiveSectionViewsUpdate: (sectionViews) async {
|
||||||
|
final section = sectionViews.section;
|
||||||
|
switch (section) {
|
||||||
|
case ViewSectionPB.Public:
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
section: state.section.copyWith(
|
||||||
|
publicViews: sectionViews.views,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case ViewSectionPB.Private:
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
section: state.section.copyWith(
|
||||||
|
privateViews: sectionViews.views,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveRootView: (fromIndex, toIndex, fromSection, toSection) async {
|
||||||
|
final views = fromSection == ViewSectionPB.Public
|
||||||
|
? List<ViewPB>.from(state.section.publicViews)
|
||||||
|
: List<ViewPB>.from(state.section.privateViews);
|
||||||
|
if (fromIndex < 0 || fromIndex >= views.length) {
|
||||||
|
Log.error(
|
||||||
|
'Invalid fromIndex: $fromIndex, maxIndex: ${views.length - 1}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final view = views[fromIndex];
|
||||||
|
final result = await _workspaceService.moveView(
|
||||||
|
viewId: view.id,
|
||||||
|
fromIndex: fromIndex,
|
||||||
|
toIndex: toIndex,
|
||||||
|
);
|
||||||
|
result.fold(
|
||||||
|
(value) {
|
||||||
|
views.insert(toIndex, views.removeAt(fromIndex));
|
||||||
|
var newState = state;
|
||||||
|
if (fromSection == ViewSectionPB.Public) {
|
||||||
|
newState = newState.copyWith(
|
||||||
|
section: newState.section.copyWith(publicViews: views),
|
||||||
|
);
|
||||||
|
} else if (fromSection == ViewSectionPB.Private) {
|
||||||
|
newState = newState.copyWith(
|
||||||
|
section: newState.section.copyWith(privateViews: views),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
emit(newState);
|
||||||
|
},
|
||||||
|
(error) {
|
||||||
|
Log.error('Failed to move root view: $error');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late WorkspaceService _workspaceService;
|
||||||
|
WorkspaceSectionsListener? _listener;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _listener?.stop();
|
||||||
|
_listener = null;
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewSectionPB? getViewSection(ViewPB view) {
|
||||||
|
final publicViews = state.section.publicViews.map((e) => e.id);
|
||||||
|
final privateViews = state.section.privateViews.map((e) => e.id);
|
||||||
|
if (publicViews.contains(view.id)) {
|
||||||
|
return ViewSectionPB.Public;
|
||||||
|
} else if (privateViews.contains(view.id)) {
|
||||||
|
return ViewSectionPB.Private;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SidebarSection?> _getSectionViews() async {
|
||||||
|
try {
|
||||||
|
final publicViews = await _workspaceService.getPublicViews().getOrThrow();
|
||||||
|
final privateViews =
|
||||||
|
await _workspaceService.getPrivateViews().getOrThrow();
|
||||||
|
return SidebarSection(
|
||||||
|
publicViews: publicViews,
|
||||||
|
privateViews: privateViews,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Failed to get section views: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initial(UserProfilePB userProfile, String workspaceId) {
|
||||||
|
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
||||||
|
|
||||||
|
_listener = WorkspaceSectionsListener(
|
||||||
|
user: userProfile,
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
)..start(
|
||||||
|
sectionChanged: (result) {
|
||||||
|
if (!isClosed) {
|
||||||
|
result.fold(
|
||||||
|
(s) => add(SidebarSectionsEvent.receiveSectionViewsUpdate(s)),
|
||||||
|
(f) => Log.error('Failed to receive section views: $f'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _reset(UserProfilePB userProfile, String workspaceId) {
|
||||||
|
_listener?.stop();
|
||||||
|
_listener = null;
|
||||||
|
|
||||||
|
_initial(userProfile, workspaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SidebarSectionsEvent with _$SidebarSectionsEvent {
|
||||||
|
const factory SidebarSectionsEvent.initial(
|
||||||
|
UserProfilePB userProfile,
|
||||||
|
String workspaceId,
|
||||||
|
) = _Initial;
|
||||||
|
const factory SidebarSectionsEvent.reset(
|
||||||
|
UserProfilePB userProfile,
|
||||||
|
String workspaceId,
|
||||||
|
) = _Reset;
|
||||||
|
const factory SidebarSectionsEvent.createRootViewInSection({
|
||||||
|
required String name,
|
||||||
|
required ViewSectionPB viewSection,
|
||||||
|
String? desc,
|
||||||
|
int? index,
|
||||||
|
}) = _CreateRootViewInSection;
|
||||||
|
const factory SidebarSectionsEvent.moveRootView({
|
||||||
|
required int fromIndex,
|
||||||
|
required int toIndex,
|
||||||
|
required ViewSectionPB fromSection,
|
||||||
|
required ViewSectionPB toSection,
|
||||||
|
}) = _MoveRootView;
|
||||||
|
const factory SidebarSectionsEvent.receiveSectionViewsUpdate(
|
||||||
|
SectionViewsPB sectionViews,
|
||||||
|
) = _ReceiveSectionViewsUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SidebarSectionsState with _$SidebarSectionsState {
|
||||||
|
const factory SidebarSectionsState({
|
||||||
|
required SidebarSection section,
|
||||||
|
@Default(null) ViewPB? lastCreatedRootView,
|
||||||
|
FlowyResult<void, FlowyError>? createRootViewResult,
|
||||||
|
}) = _SidebarSectionsState;
|
||||||
|
|
||||||
|
factory SidebarSectionsState.initial() => const SidebarSectionsState(
|
||||||
|
section: SidebarSection.empty(),
|
||||||
|
);
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/config/kv_keys.dart';
|
import 'package:appflowy/core/config/kv_keys.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
@ -10,7 +11,19 @@ part 'folder_bloc.freezed.dart';
|
|||||||
|
|
||||||
enum FolderCategoryType {
|
enum FolderCategoryType {
|
||||||
favorite,
|
favorite,
|
||||||
personal,
|
private,
|
||||||
|
public;
|
||||||
|
|
||||||
|
ViewSectionPB get toViewSectionPB {
|
||||||
|
switch (this) {
|
||||||
|
case FolderCategoryType.private:
|
||||||
|
return ViewSectionPB.Private;
|
||||||
|
case FolderCategoryType.public:
|
||||||
|
return ViewSectionPB.Public;
|
||||||
|
case FolderCategoryType.favorite:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FolderBloc extends Bloc<FolderEvent, FolderState> {
|
class FolderBloc extends Bloc<FolderEvent, FolderState> {
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy/user/application/user_service.dart';
|
|||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.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-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -20,14 +20,20 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
|||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
await event.when(
|
await event.when(
|
||||||
initial: () async {
|
initial: () async {
|
||||||
// do nothing
|
add(const FetchWorkspaces());
|
||||||
},
|
},
|
||||||
workspacesReceived: (workspaceId) async {},
|
workspacesReceived: (workspaceId) async {},
|
||||||
fetchWorkspaces: () async {
|
fetchWorkspaces: () async {
|
||||||
final result = await _fetchWorkspaces();
|
final result = await _fetchWorkspaces();
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
final members = await _userService
|
||||||
|
.getWorkspaceMembers(
|
||||||
|
result.$1.workspaceId,
|
||||||
|
)
|
||||||
|
.fold((s) => s.items.length, (f) => -1);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
|
isCollaborativeWorkspace: members > 1,
|
||||||
currentWorkspace: result.$1,
|
currentWorkspace: result.$1,
|
||||||
workspaces: result.$2,
|
workspaces: result.$2,
|
||||||
),
|
),
|
||||||
@ -258,7 +264,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
|||||||
workspaces.firstWhere((e) => e.workspaceId == currentWorkspace.id);
|
workspaces.firstWhere((e) => e.workspaceId == currentWorkspace.id);
|
||||||
return (currentWorkspaceInList, workspaces);
|
return (currentWorkspaceInList, workspaces);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error('fetch workspace error: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,6 +298,7 @@ class UserWorkspaceState with _$UserWorkspaceState {
|
|||||||
const factory UserWorkspaceState({
|
const factory UserWorkspaceState({
|
||||||
required UserWorkspacePB? currentWorkspace,
|
required UserWorkspacePB? currentWorkspace,
|
||||||
required List<UserWorkspacePB> workspaces,
|
required List<UserWorkspacePB> workspaces,
|
||||||
|
@Default(false) bool isCollaborativeWorkspace,
|
||||||
@Default(null) FlowyResult<void, FlowyError>? createWorkspaceResult,
|
@Default(null) FlowyResult<void, FlowyError>? createWorkspaceResult,
|
||||||
@Default(null) FlowyResult<void, FlowyError>? deleteWorkspaceResult,
|
@Default(null) FlowyResult<void, FlowyError>? deleteWorkspaceResult,
|
||||||
@Default(null) FlowyResult<void, FlowyError>? openWorkspaceResult,
|
@Default(null) FlowyResult<void, FlowyError>? openWorkspaceResult,
|
||||||
|
@ -165,6 +165,8 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
viewId: value.from.id,
|
viewId: value.from.id,
|
||||||
newParentId: value.newParentId,
|
newParentId: value.newParentId,
|
||||||
prevViewId: value.prevId,
|
prevViewId: value.prevId,
|
||||||
|
fromSection: value.fromSection,
|
||||||
|
toSection: value.toSection,
|
||||||
);
|
);
|
||||||
emit(
|
emit(
|
||||||
result.fold(
|
result.fold(
|
||||||
@ -184,8 +186,8 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
layoutType: e.layoutType,
|
layoutType: e.layoutType,
|
||||||
ext: {},
|
ext: {},
|
||||||
openAfterCreate: e.openAfterCreated,
|
openAfterCreate: e.openAfterCreated,
|
||||||
|
section: e.section,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
result.fold(
|
result.fold(
|
||||||
(view) => state.copyWith(
|
(view) => state.copyWith(
|
||||||
@ -353,12 +355,15 @@ class ViewEvent with _$ViewEvent {
|
|||||||
ViewPB from,
|
ViewPB from,
|
||||||
String newParentId,
|
String newParentId,
|
||||||
String? prevId,
|
String? prevId,
|
||||||
|
ViewSectionPB? fromSection,
|
||||||
|
ViewSectionPB? toSection,
|
||||||
) = Move;
|
) = Move;
|
||||||
const factory ViewEvent.createView(
|
const factory ViewEvent.createView(
|
||||||
String name,
|
String name,
|
||||||
ViewLayoutPB layoutType, {
|
ViewLayoutPB layoutType, {
|
||||||
/// open the view after created
|
/// open the view after created
|
||||||
@Default(true) bool openAfterCreated,
|
@Default(true) bool openAfterCreated,
|
||||||
|
required ViewSectionPB section,
|
||||||
}) = CreateView;
|
}) = CreateView;
|
||||||
const factory ViewEvent.viewDidUpdate(
|
const factory ViewEvent.viewDidUpdate(
|
||||||
FlowyResult<ViewPB, FlowyError> result,
|
FlowyResult<ViewPB, FlowyError> result,
|
||||||
|
@ -37,6 +37,7 @@ class ViewBackendService {
|
|||||||
/// The [index] is the index of the view in the parent view.
|
/// The [index] is the index of the view in the parent view.
|
||||||
/// If the index is null, the view will be added to the end of the list.
|
/// If the index is null, the view will be added to the end of the list.
|
||||||
int? index,
|
int? index,
|
||||||
|
ViewSectionPB? section,
|
||||||
}) {
|
}) {
|
||||||
final payload = CreateViewPayloadPB.create()
|
final payload = CreateViewPayloadPB.create()
|
||||||
..parentViewId = parentViewId
|
..parentViewId = parentViewId
|
||||||
@ -58,6 +59,10 @@ class ViewBackendService {
|
|||||||
payload.index = index;
|
payload.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (section != null) {
|
||||||
|
payload.section = section;
|
||||||
|
}
|
||||||
|
|
||||||
return FolderEventCreateView(payload).send();
|
return FolderEventCreateView(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,11 +200,15 @@ class ViewBackendService {
|
|||||||
required String viewId,
|
required String viewId,
|
||||||
required String newParentId,
|
required String newParentId,
|
||||||
required String? prevViewId,
|
required String? prevViewId,
|
||||||
|
ViewSectionPB? fromSection,
|
||||||
|
ViewSectionPB? toSection,
|
||||||
}) {
|
}) {
|
||||||
final payload = MoveNestedViewPayloadPB(
|
final payload = MoveNestedViewPayloadPB(
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
newParentId: newParentId,
|
newParentId: newParentId,
|
||||||
prevViewId: prevViewId,
|
prevViewId: prevViewId,
|
||||||
|
fromSection: fromSection,
|
||||||
|
toSection: toSection,
|
||||||
);
|
);
|
||||||
|
|
||||||
return FolderEventMoveNestedView(payload).send();
|
return FolderEventMoveNestedView(payload).send();
|
||||||
|
@ -11,23 +11,28 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
|||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
|
|
||||||
typedef AppListNotifyValue = FlowyResult<List<ViewPB>, FlowyError>;
|
typedef RootViewsNotifyValue = FlowyResult<List<ViewPB>, FlowyError>;
|
||||||
typedef WorkspaceNotifyValue = FlowyResult<WorkspacePB, FlowyError>;
|
typedef WorkspaceNotifyValue = FlowyResult<WorkspacePB, FlowyError>;
|
||||||
|
|
||||||
|
/// The [WorkspaceListener] listens to the changes including the below:
|
||||||
|
///
|
||||||
|
/// - The root views of the workspace. (Not including the views are inside the root views)
|
||||||
|
/// - The workspace itself.
|
||||||
class WorkspaceListener {
|
class WorkspaceListener {
|
||||||
WorkspaceListener({required this.user, required this.workspaceId});
|
WorkspaceListener({required this.user, required this.workspaceId});
|
||||||
|
|
||||||
final UserProfilePB user;
|
final UserProfilePB user;
|
||||||
final String workspaceId;
|
final String workspaceId;
|
||||||
|
|
||||||
PublishNotifier<AppListNotifyValue>? _appsChangedNotifier = PublishNotifier();
|
PublishNotifier<RootViewsNotifyValue>? _appsChangedNotifier =
|
||||||
|
PublishNotifier();
|
||||||
PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier =
|
PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier =
|
||||||
PublishNotifier();
|
PublishNotifier();
|
||||||
|
|
||||||
FolderNotificationListener? _listener;
|
FolderNotificationListener? _listener;
|
||||||
|
|
||||||
void start({
|
void start({
|
||||||
void Function(AppListNotifyValue)? appsChanged,
|
void Function(RootViewsNotifyValue)? appsChanged,
|
||||||
void Function(WorkspaceNotifyValue)? onWorkspaceUpdated,
|
void Function(WorkspaceNotifyValue)? onWorkspaceUpdated,
|
||||||
}) {
|
}) {
|
||||||
if (appsChanged != null) {
|
if (appsChanged != null) {
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:appflowy/core/notification/folder_notification.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||||
|
show UserProfilePB;
|
||||||
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
|
import 'package:flowy_infra/notifier.dart';
|
||||||
|
|
||||||
|
typedef SectionNotifyValue = FlowyResult<SectionViewsPB, FlowyError>;
|
||||||
|
|
||||||
|
/// The [WorkspaceSectionsListener] listens to the changes including the below:
|
||||||
|
///
|
||||||
|
/// - The root views inside different section of the workspace. (Not including the views are inside the root views)
|
||||||
|
/// depends on the section type(s).
|
||||||
|
class WorkspaceSectionsListener {
|
||||||
|
WorkspaceSectionsListener({
|
||||||
|
required this.user,
|
||||||
|
required this.workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UserProfilePB user;
|
||||||
|
final String workspaceId;
|
||||||
|
|
||||||
|
final _sectionNotifier = PublishNotifier<SectionNotifyValue>();
|
||||||
|
late final FolderNotificationListener _listener;
|
||||||
|
|
||||||
|
void start({
|
||||||
|
void Function(SectionNotifyValue)? sectionChanged,
|
||||||
|
}) {
|
||||||
|
if (sectionChanged != null) {
|
||||||
|
_sectionNotifier.addPublishListener(sectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener = FolderNotificationListener(
|
||||||
|
objectId: workspaceId,
|
||||||
|
handler: _handleObservableType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleObservableType(
|
||||||
|
FolderNotification ty,
|
||||||
|
FlowyResult<Uint8List, FlowyError> result,
|
||||||
|
) {
|
||||||
|
switch (ty) {
|
||||||
|
case FolderNotification.DidUpdateSectionViews:
|
||||||
|
final FlowyResult<SectionViewsPB, FlowyError> value = result.fold(
|
||||||
|
(s) => FlowyResult.success(
|
||||||
|
SectionViewsPB.fromBuffer(s),
|
||||||
|
),
|
||||||
|
(f) => FlowyResult.failure(f),
|
||||||
|
);
|
||||||
|
_sectionNotifier.value = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
_sectionNotifier.dispose();
|
||||||
|
|
||||||
|
await _listener.stop();
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
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/protobuf.dart';
|
||||||
show CreateViewPayloadPB, MoveViewPayloadPB, ViewLayoutPB, ViewPB;
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
|
|
||||||
class WorkspaceService {
|
class WorkspaceService {
|
||||||
@ -12,15 +10,18 @@ class WorkspaceService {
|
|||||||
|
|
||||||
final String workspaceId;
|
final String workspaceId;
|
||||||
|
|
||||||
Future<FlowyResult<ViewPB, FlowyError>> createApp({
|
Future<FlowyResult<ViewPB, FlowyError>> createView({
|
||||||
required String name,
|
required String name,
|
||||||
|
required ViewSectionPB viewSection,
|
||||||
String? desc,
|
String? desc,
|
||||||
int? index,
|
int? index,
|
||||||
}) {
|
}) {
|
||||||
final payload = CreateViewPayloadPB.create()
|
final payload = CreateViewPayloadPB.create()
|
||||||
..parentViewId = workspaceId
|
..parentViewId = workspaceId
|
||||||
..name = name
|
..name = name
|
||||||
..layout = ViewLayoutPB.Document;
|
// only allow document layout for the top-level views
|
||||||
|
..layout = ViewLayoutPB.Document
|
||||||
|
..section = viewSection;
|
||||||
|
|
||||||
if (desc != null) {
|
if (desc != null) {
|
||||||
payload.desc = desc;
|
payload.desc = desc;
|
||||||
@ -37,8 +38,8 @@ class WorkspaceService {
|
|||||||
return FolderEventReadCurrentWorkspace().send();
|
return FolderEventReadCurrentWorkspace().send();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<FlowyResult<List<ViewPB>, FlowyError>> getViews() {
|
Future<FlowyResult<List<ViewPB>, FlowyError>> getPublicViews() {
|
||||||
final payload = WorkspaceIdPB.create()..value = workspaceId;
|
final payload = GetWorkspaceViewPB.create()..value = workspaceId;
|
||||||
return FolderEventReadWorkspaceViews(payload).send().then((result) {
|
return FolderEventReadWorkspaceViews(payload).send().then((result) {
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(views) => FlowyResult.success(views.items),
|
(views) => FlowyResult.success(views.items),
|
||||||
@ -47,13 +48,23 @@ class WorkspaceService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<FlowyResult<void, FlowyError>> moveApp({
|
Future<FlowyResult<List<ViewPB>, FlowyError>> getPrivateViews() {
|
||||||
required String appId,
|
final payload = GetWorkspaceViewPB.create()..value = workspaceId;
|
||||||
|
return FolderEventReadPrivateViews(payload).send().then((result) {
|
||||||
|
return result.fold(
|
||||||
|
(views) => FlowyResult.success(views.items),
|
||||||
|
(error) => FlowyResult.failure(error),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FlowyResult<void, FlowyError>> moveView({
|
||||||
|
required String viewId,
|
||||||
required int fromIndex,
|
required int fromIndex,
|
||||||
required int toIndex,
|
required int toIndex,
|
||||||
}) {
|
}) {
|
||||||
final payload = MoveViewPayloadPB.create()
|
final payload = MoveViewPayloadPB.create()
|
||||||
..viewId = appId
|
..viewId = viewId
|
||||||
..from = fromIndex
|
..from = fromIndex
|
||||||
..to = toIndex;
|
..to = toIndex;
|
||||||
|
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FolderHeader extends StatefulWidget {
|
||||||
|
const FolderHeader({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.expandButtonTooltip,
|
||||||
|
required this.addButtonTooltip,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.onAdded,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String expandButtonTooltip;
|
||||||
|
final String addButtonTooltip;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final VoidCallback onAdded;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FolderHeader> createState() => _FolderHeaderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FolderHeaderState extends State<FolderHeader> {
|
||||||
|
bool onHover = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const iconSize = 26.0;
|
||||||
|
const textPadding = 4.0;
|
||||||
|
return MouseRegion(
|
||||||
|
onEnter: (event) => setState(() => onHover = true),
|
||||||
|
onExit: (event) => setState(() => onHover = false),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
FlowyTextButton(
|
||||||
|
widget.title,
|
||||||
|
tooltip: widget.expandButtonTooltip,
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minHeight: iconSize + textPadding * 2,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(textPadding),
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
onPressed: widget.onPressed,
|
||||||
|
),
|
||||||
|
if (onHover) ...[
|
||||||
|
const Spacer(),
|
||||||
|
FlowyIconButton(
|
||||||
|
tooltipText: widget.addButtonTooltip,
|
||||||
|
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
iconPadding: const EdgeInsets.all(2),
|
||||||
|
height: iconSize,
|
||||||
|
width: iconSize,
|
||||||
|
icon: const FlowySvg(FlowySvgs.add_s),
|
||||||
|
onPressed: widget.onAdded,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/_folder_header.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class SectionFolder extends StatelessWidget {
|
||||||
|
const SectionFolder({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.categoryType,
|
||||||
|
required this.views,
|
||||||
|
this.isHoverEnabled = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final FolderCategoryType categoryType;
|
||||||
|
final List<ViewPB> views;
|
||||||
|
final bool isHoverEnabled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider<FolderBloc>(
|
||||||
|
create: (context) => FolderBloc(type: categoryType)
|
||||||
|
..add(
|
||||||
|
const FolderEvent.initial(),
|
||||||
|
),
|
||||||
|
child: BlocBuilder<FolderBloc, FolderState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
FolderHeader(
|
||||||
|
title: title,
|
||||||
|
expandButtonTooltip: expandButtonTooltip,
|
||||||
|
addButtonTooltip: addButtonTooltip,
|
||||||
|
onPressed: () => context
|
||||||
|
.read<FolderBloc>()
|
||||||
|
.add(const FolderEvent.expandOrUnExpand()),
|
||||||
|
onAdded: () {
|
||||||
|
createViewAndShowRenameDialogIfNeeded(
|
||||||
|
context,
|
||||||
|
LocaleKeys.newPageText.tr(),
|
||||||
|
(viewName, _) {
|
||||||
|
if (viewName.isNotEmpty) {
|
||||||
|
context.read<SidebarSectionsBloc>().add(
|
||||||
|
SidebarSectionsEvent.createRootViewInSection(
|
||||||
|
name: viewName,
|
||||||
|
index: 0,
|
||||||
|
viewSection: categoryType.toViewSectionPB,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
context.read<FolderBloc>().add(
|
||||||
|
const FolderEvent.expandOrUnExpand(
|
||||||
|
isExpanded: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (state.isExpanded)
|
||||||
|
...views.map(
|
||||||
|
(view) => ViewItem(
|
||||||
|
key: ValueKey(
|
||||||
|
'${categoryType.name} ${view.id}',
|
||||||
|
),
|
||||||
|
categoryType: categoryType,
|
||||||
|
isFirstChild: view.id == views.first.id,
|
||||||
|
view: view,
|
||||||
|
level: 0,
|
||||||
|
leftPadding: 16,
|
||||||
|
isFeedback: false,
|
||||||
|
onSelected: (view) {
|
||||||
|
if (HardwareKeyboard.instance.isControlPressed) {
|
||||||
|
context.read<TabsBloc>().openTab(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.read<TabsBloc>().openPlugin(view);
|
||||||
|
},
|
||||||
|
onTertiarySelected: (view) =>
|
||||||
|
context.read<TabsBloc>().openTab(view),
|
||||||
|
isHoverEnabled: isHoverEnabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get expandButtonTooltip {
|
||||||
|
return switch (categoryType) {
|
||||||
|
FolderCategoryType.public => LocaleKeys.sideBar_clickToHidePublic.tr(),
|
||||||
|
FolderCategoryType.private => LocaleKeys.sideBar_clickToHidePrivate.tr(),
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
String get addButtonTooltip {
|
||||||
|
return switch (categoryType) {
|
||||||
|
FolderCategoryType.public => LocaleKeys.sideBar_addAPageToPublic.tr(),
|
||||||
|
FolderCategoryType.private => LocaleKeys.sideBar_addAPageToPrivate.tr(),
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,146 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
|
||||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart';
|
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
|
|
||||||
class PersonalFolder extends StatelessWidget {
|
|
||||||
const PersonalFolder({
|
|
||||||
super.key,
|
|
||||||
required this.views,
|
|
||||||
this.isHoverEnabled = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<ViewPB> views;
|
|
||||||
final bool isHoverEnabled;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocProvider<FolderBloc>(
|
|
||||||
create: (context) => FolderBloc(type: FolderCategoryType.personal)
|
|
||||||
..add(
|
|
||||||
const FolderEvent.initial(),
|
|
||||||
),
|
|
||||||
child: BlocBuilder<FolderBloc, FolderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
PersonalFolderHeader(
|
|
||||||
onPressed: () => context
|
|
||||||
.read<FolderBloc>()
|
|
||||||
.add(const FolderEvent.expandOrUnExpand()),
|
|
||||||
onAdded: () => context
|
|
||||||
.read<FolderBloc>()
|
|
||||||
.add(const FolderEvent.expandOrUnExpand(isExpanded: true)),
|
|
||||||
),
|
|
||||||
if (state.isExpanded)
|
|
||||||
...views.map(
|
|
||||||
(view) => ViewItem(
|
|
||||||
key: ValueKey(
|
|
||||||
'${FolderCategoryType.personal.name} ${view.id}',
|
|
||||||
),
|
|
||||||
categoryType: FolderCategoryType.personal,
|
|
||||||
isFirstChild: view.id == views.first.id,
|
|
||||||
view: view,
|
|
||||||
level: 0,
|
|
||||||
leftPadding: 16,
|
|
||||||
isFeedback: false,
|
|
||||||
onSelected: (view) {
|
|
||||||
if (HardwareKeyboard.instance.isControlPressed) {
|
|
||||||
context.read<TabsBloc>().openTab(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.read<TabsBloc>().openPlugin(view);
|
|
||||||
},
|
|
||||||
onTertiarySelected: (view) =>
|
|
||||||
context.read<TabsBloc>().openTab(view),
|
|
||||||
isHoverEnabled: isHoverEnabled,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PersonalFolderHeader extends StatefulWidget {
|
|
||||||
const PersonalFolderHeader({
|
|
||||||
super.key,
|
|
||||||
required this.onPressed,
|
|
||||||
required this.onAdded,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
final VoidCallback onAdded;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<PersonalFolderHeader> createState() => _PersonalFolderHeaderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PersonalFolderHeaderState extends State<PersonalFolderHeader> {
|
|
||||||
bool onHover = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const iconSize = 26.0;
|
|
||||||
const textPadding = 4.0;
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (event) => setState(() => onHover = true),
|
|
||||||
onExit: (event) => setState(() => onHover = false),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
FlowyTextButton(
|
|
||||||
LocaleKeys.sideBar_personal.tr(),
|
|
||||||
tooltip: LocaleKeys.sideBar_clickToHidePersonal.tr(),
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: iconSize + textPadding * 2,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(textPadding),
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
onPressed: widget.onPressed,
|
|
||||||
),
|
|
||||||
if (onHover) ...[
|
|
||||||
const Spacer(),
|
|
||||||
FlowyIconButton(
|
|
||||||
tooltipText: LocaleKeys.sideBar_addAPage.tr(),
|
|
||||||
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
|
||||||
iconPadding: const EdgeInsets.all(2),
|
|
||||||
height: iconSize,
|
|
||||||
width: iconSize,
|
|
||||||
icon: const FlowySvg(FlowySvgs.add_s),
|
|
||||||
onPressed: () {
|
|
||||||
createViewAndShowRenameDialogIfNeeded(
|
|
||||||
context,
|
|
||||||
LocaleKeys.newPageText.tr(),
|
|
||||||
(viewName, _) {
|
|
||||||
if (viewName.isNotEmpty) {
|
|
||||||
context.read<SidebarRootViewsBloc>().add(
|
|
||||||
SidebarRootViewsEvent.createRootView(
|
|
||||||
viewName,
|
|
||||||
index: 0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
widget.onAdded();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:appflowy/shared/feature_flags.dart';
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
|
||||||
import 'package:appflowy/workspace/application/notifications/notification_action.dart';
|
import 'package:appflowy/workspace/application/notifications/notification_action.dart';
|
||||||
import 'package:appflowy/workspace/application/notifications/notification_action_bloc.dart';
|
import 'package:appflowy/workspace/application/notifications/notification_action_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||||
@ -15,8 +14,8 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_top_me
|
|||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_trash.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_trash.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_user.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_user.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||||
show UserProfilePB;
|
show UserProfilePB;
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
@ -31,7 +30,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
/// - settings
|
/// - settings
|
||||||
/// - scrollable document list
|
/// - scrollable document list
|
||||||
/// - trash
|
/// - trash
|
||||||
class HomeSideBar extends StatefulWidget {
|
class HomeSideBar extends StatelessWidget {
|
||||||
const HomeSideBar({
|
const HomeSideBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.userProfile,
|
required this.userProfile,
|
||||||
@ -42,49 +41,30 @@ class HomeSideBar extends StatefulWidget {
|
|||||||
|
|
||||||
final WorkspaceSettingPB workspaceSetting;
|
final WorkspaceSettingPB workspaceSetting;
|
||||||
|
|
||||||
@override
|
|
||||||
State<HomeSideBar> createState() => _HomeSideBarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HomeSideBarState extends State<HomeSideBar> {
|
|
||||||
final _scrollController = ScrollController();
|
|
||||||
Timer? _srollDebounce;
|
|
||||||
bool isScrolling = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_scrollController.addListener(_onScrollChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScrollChanged() {
|
|
||||||
setState(() => isScrolling = true);
|
|
||||||
|
|
||||||
_srollDebounce?.cancel();
|
|
||||||
_srollDebounce =
|
|
||||||
Timer(const Duration(milliseconds: 300), _setScrollStopped);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setScrollStopped() {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => isScrolling = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_srollDebounce?.cancel();
|
|
||||||
_scrollController.removeListener(_onScrollChanged);
|
|
||||||
_scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Workspace Bloc: control the current workspace
|
||||||
|
// |
|
||||||
|
// +-- Workspace Menu
|
||||||
|
// | |
|
||||||
|
// | +-- Workspace List: control to switch workspace
|
||||||
|
// | |
|
||||||
|
// | +-- Workspace Settings
|
||||||
|
// | |
|
||||||
|
// | +-- Notification Center
|
||||||
|
// |
|
||||||
|
// +-- Favorite Section
|
||||||
|
// |
|
||||||
|
// +-- Public Or Private Section: control the sections of the workspace
|
||||||
|
// |
|
||||||
|
// +-- Trash Section
|
||||||
return BlocProvider<UserWorkspaceBloc>(
|
return BlocProvider<UserWorkspaceBloc>(
|
||||||
create: (_) => UserWorkspaceBloc(userProfile: widget.userProfile)
|
create: (_) => UserWorkspaceBloc(userProfile: userProfile)
|
||||||
..add(const UserWorkspaceEvent.fetchWorkspaces()),
|
..add(
|
||||||
|
const UserWorkspaceEvent.initial(),
|
||||||
|
),
|
||||||
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
|
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
|
||||||
|
// Rebuild the whole sidebar when the current workspace changes
|
||||||
buildWhen: (previous, current) =>
|
buildWhen: (previous, current) =>
|
||||||
previous.currentWorkspace?.workspaceId !=
|
previous.currentWorkspace?.workspaceId !=
|
||||||
current.currentWorkspace?.workspaceId,
|
current.currentWorkspace?.workspaceId,
|
||||||
@ -95,19 +75,19 @@ class _HomeSideBarState extends State<HomeSideBar> {
|
|||||||
create: (_) => getIt<NotificationActionBloc>(),
|
create: (_) => getIt<NotificationActionBloc>(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) => SidebarRootViewsBloc()
|
create: (_) => SidebarSectionsBloc()
|
||||||
..add(
|
..add(
|
||||||
SidebarRootViewsEvent.initial(
|
SidebarSectionsEvent.initial(
|
||||||
widget.userProfile,
|
userProfile,
|
||||||
state.currentWorkspace?.workspaceId ??
|
state.currentWorkspace?.workspaceId ??
|
||||||
widget.workspaceSetting.workspaceId,
|
workspaceSetting.workspaceId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MultiBlocListener(
|
child: MultiBlocListener(
|
||||||
listeners: [
|
listeners: [
|
||||||
BlocListener<SidebarRootViewsBloc, SidebarRootViewState>(
|
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(
|
||||||
listenWhen: (p, c) =>
|
listenWhen: (p, c) =>
|
||||||
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
||||||
listener: (context, state) => context.read<TabsBloc>().add(
|
listener: (context, state) => context.read<TabsBloc>().add(
|
||||||
@ -122,28 +102,17 @@ class _HomeSideBarState extends State<HomeSideBar> {
|
|||||||
),
|
),
|
||||||
BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
|
BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
context.read<SidebarRootViewsBloc>().add(
|
context.read<SidebarSectionsBloc>().add(
|
||||||
SidebarRootViewsEvent.reset(
|
SidebarSectionsEvent.initial(
|
||||||
widget.userProfile,
|
userProfile,
|
||||||
state.currentWorkspace?.workspaceId ??
|
state.currentWorkspace?.workspaceId ??
|
||||||
widget.workspaceSetting.workspaceId,
|
workspaceSetting.workspaceId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: Builder(
|
child: _Sidebar(userProfile: userProfile),
|
||||||
builder: (context) {
|
|
||||||
final menuState = context.watch<SidebarRootViewsBloc>().state;
|
|
||||||
final favoriteState = context.watch<FavoriteBloc>().state;
|
|
||||||
|
|
||||||
return _buildSidebar(
|
|
||||||
context,
|
|
||||||
menuState.views,
|
|
||||||
favoriteState.views,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -151,71 +120,6 @@ class _HomeSideBarState extends State<HomeSideBar> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSidebar(
|
|
||||||
BuildContext context,
|
|
||||||
List<ViewPB> views,
|
|
||||||
List<ViewPB> favoriteViews,
|
|
||||||
) {
|
|
||||||
const menuHorizontalInset = EdgeInsets.symmetric(horizontal: 12);
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
border: Border(
|
|
||||||
right: BorderSide(color: Theme.of(context).dividerColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
// top menu
|
|
||||||
const Padding(
|
|
||||||
padding: menuHorizontalInset,
|
|
||||||
child: SidebarTopMenu(),
|
|
||||||
),
|
|
||||||
// user or workspace, setting
|
|
||||||
Padding(
|
|
||||||
padding: menuHorizontalInset,
|
|
||||||
child: FeatureFlag.collaborativeWorkspace.isOn
|
|
||||||
? SidebarWorkspace(
|
|
||||||
userProfile: widget.userProfile,
|
|
||||||
views: views,
|
|
||||||
)
|
|
||||||
: SidebarUser(
|
|
||||||
userProfile: widget.userProfile,
|
|
||||||
views: views,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const VSpace(20),
|
|
||||||
// scrollable document list
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: menuHorizontalInset,
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
controller: _scrollController,
|
|
||||||
physics: const ClampingScrollPhysics(),
|
|
||||||
child: SidebarFolder(
|
|
||||||
views: views,
|
|
||||||
favoriteViews: favoriteViews,
|
|
||||||
isHoverEnabled: !isScrolling,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const VSpace(10),
|
|
||||||
// trash
|
|
||||||
const Padding(
|
|
||||||
padding: menuHorizontalInset,
|
|
||||||
child: SidebarTrashButton(),
|
|
||||||
),
|
|
||||||
const VSpace(10),
|
|
||||||
// new page button
|
|
||||||
const SidebarNewPageButton(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onNotificationAction(
|
void _onNotificationAction(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
NotificationActionState state,
|
NotificationActionState state,
|
||||||
@ -224,9 +128,10 @@ class _HomeSideBarState extends State<HomeSideBar> {
|
|||||||
if (action != null) {
|
if (action != null) {
|
||||||
if (action.type == ActionType.openView) {
|
if (action.type == ActionType.openView) {
|
||||||
final view = context
|
final view = context
|
||||||
.read<SidebarRootViewsBloc>()
|
.read<SidebarSectionsBloc>()
|
||||||
.state
|
.state
|
||||||
.views
|
.section
|
||||||
|
.publicViews
|
||||||
.findView(action.objectId);
|
.findView(action.objectId);
|
||||||
|
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
@ -250,3 +155,108 @@ class _HomeSideBarState extends State<HomeSideBar> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Sidebar extends StatefulWidget {
|
||||||
|
const _Sidebar({
|
||||||
|
required this.userProfile,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Sidebar> createState() => _SidebarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SidebarState extends State<_Sidebar> {
|
||||||
|
final _scrollController = ScrollController();
|
||||||
|
Timer? _scrollDebounce;
|
||||||
|
bool isScrolling = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scrollController.addListener(_onScrollChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollDebounce?.cancel();
|
||||||
|
_scrollController.removeListener(_onScrollChanged);
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const menuHorizontalInset = EdgeInsets.symmetric(horizontal: 12);
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(color: Theme.of(context).dividerColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// top menu
|
||||||
|
const Padding(
|
||||||
|
padding: menuHorizontalInset,
|
||||||
|
child: SidebarTopMenu(),
|
||||||
|
),
|
||||||
|
// user or workspace, setting
|
||||||
|
Padding(
|
||||||
|
padding: menuHorizontalInset,
|
||||||
|
child: widget.userProfile.authenticator != AuthenticatorPB.Local &&
|
||||||
|
FeatureFlag.collaborativeWorkspace.isOn
|
||||||
|
? SidebarWorkspace(
|
||||||
|
userProfile: widget.userProfile,
|
||||||
|
)
|
||||||
|
: SidebarUser(
|
||||||
|
userProfile: widget.userProfile,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const VSpace(20),
|
||||||
|
// scrollable document list
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: menuHorizontalInset,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
child: SidebarFolder(
|
||||||
|
userProfile: widget.userProfile,
|
||||||
|
isHoverEnabled: !isScrolling,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const VSpace(10),
|
||||||
|
// trash
|
||||||
|
const Padding(
|
||||||
|
padding: menuHorizontalInset,
|
||||||
|
child: SidebarTrashButton(),
|
||||||
|
),
|
||||||
|
const VSpace(10),
|
||||||
|
// new page button
|
||||||
|
const SidebarNewPageButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScrollChanged() {
|
||||||
|
setState(() => isScrolling = true);
|
||||||
|
|
||||||
|
_scrollDebounce?.cancel();
|
||||||
|
_scrollDebounce =
|
||||||
|
Timer(const Duration(milliseconds: 300), _setScrollStopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setScrollStopped() {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => isScrolling = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,50 +1,118 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
|
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/_favorite_folder.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/_section_folder.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.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:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SidebarFolder extends StatelessWidget {
|
class SidebarFolder extends StatelessWidget {
|
||||||
const SidebarFolder({
|
const SidebarFolder({
|
||||||
super.key,
|
super.key,
|
||||||
required this.views,
|
|
||||||
required this.favoriteViews,
|
|
||||||
this.isHoverEnabled = true,
|
this.isHoverEnabled = true,
|
||||||
|
required this.userProfile,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<ViewPB> views;
|
|
||||||
final List<ViewPB> favoriteViews;
|
|
||||||
final bool isHoverEnabled;
|
final bool isHoverEnabled;
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// check if there is any duplicate views
|
|
||||||
final views = this.views.toSet().toList();
|
|
||||||
final favoriteViews = this.favoriteViews.toSet().toList();
|
|
||||||
assert(views.length == this.views.length);
|
|
||||||
assert(favoriteViews.length == favoriteViews.length);
|
|
||||||
|
|
||||||
return ValueListenableBuilder(
|
return ValueListenableBuilder(
|
||||||
valueListenable: getIt<MenuSharedState>().notifier,
|
valueListenable: getIt<MenuSharedState>().notifier,
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// favorite
|
// favorite
|
||||||
if (favoriteViews.isNotEmpty) ...[
|
BlocBuilder<FavoriteBloc, FavoriteState>(
|
||||||
FavoriteFolder(
|
builder: (context, state) {
|
||||||
|
if (state.views.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10),
|
||||||
|
child: FavoriteFolder(
|
||||||
// remove the duplicate views
|
// remove the duplicate views
|
||||||
views: favoriteViews,
|
views: state.views,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// public or private
|
||||||
|
BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
// only show public and private section if the workspace is collaborative and not local
|
||||||
|
final isCollaborativeWorkspace =
|
||||||
|
userProfile.authenticator != AuthenticatorPB.Local &&
|
||||||
|
FeatureFlag.collaborativeWorkspace.isOn;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children:
|
||||||
|
// only show public and private section if the workspace is collaborative
|
||||||
|
isCollaborativeWorkspace
|
||||||
|
? [
|
||||||
|
// public
|
||||||
const VSpace(10),
|
const VSpace(10),
|
||||||
],
|
PublicSectionFolder(
|
||||||
|
views: state.section.publicViews,
|
||||||
|
),
|
||||||
|
|
||||||
|
// private
|
||||||
|
const VSpace(10),
|
||||||
|
PrivateSectionFolder(
|
||||||
|
views: state.section.privateViews,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [
|
||||||
// personal
|
// personal
|
||||||
PersonalFolder(views: views, isHoverEnabled: isHoverEnabled),
|
const VSpace(10),
|
||||||
|
PersonalSectionFolder(
|
||||||
|
views: state.section.publicViews,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PrivateSectionFolder extends SectionFolder {
|
||||||
|
PrivateSectionFolder({
|
||||||
|
super.key,
|
||||||
|
required super.views,
|
||||||
|
}) : super(
|
||||||
|
title: LocaleKeys.sideBar_private.tr(),
|
||||||
|
categoryType: FolderCategoryType.private,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PublicSectionFolder extends SectionFolder {
|
||||||
|
PublicSectionFolder({
|
||||||
|
super.key,
|
||||||
|
required super.views,
|
||||||
|
}) : super(
|
||||||
|
title: LocaleKeys.sideBar_public.tr(),
|
||||||
|
categoryType: FolderCategoryType.public,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PersonalSectionFolder extends SectionFolder {
|
||||||
|
PersonalSectionFolder({
|
||||||
|
super.key,
|
||||||
|
required super.views,
|
||||||
|
}) : super(
|
||||||
|
title: LocaleKeys.sideBar_personal.tr(),
|
||||||
|
categoryType: FolderCategoryType.public,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
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/menu/sidebar_root_views_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/rename_view_dialog.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/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||||
@ -25,9 +26,12 @@ class SidebarNewPageButton extends StatelessWidget {
|
|||||||
LocaleKeys.newPageText.tr(),
|
LocaleKeys.newPageText.tr(),
|
||||||
(viewName, _) {
|
(viewName, _) {
|
||||||
if (viewName.isNotEmpty) {
|
if (viewName.isNotEmpty) {
|
||||||
context
|
context.read<SidebarSectionsBloc>().add(
|
||||||
.read<SidebarRootViewsBloc>()
|
SidebarSectionsEvent.createRootViewInSection(
|
||||||
.add(SidebarRootViewsEvent.createRootView(viewName));
|
name: viewName,
|
||||||
|
viewSection: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,7 @@ import 'package:appflowy/core/frameless_window.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/home/home_setting_bloc.dart';
|
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||||
@ -24,7 +24,7 @@ class SidebarTopMenu extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SidebarRootViewsBloc, SidebarRootViewState>(
|
return BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: HomeSizes.topBarHeight,
|
height: HomeSizes.topBarHeight,
|
||||||
|
@ -3,7 +3,6 @@ import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart';
|
|||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
|
||||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||||
show UserProfilePB;
|
show UserProfilePB;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -17,11 +16,9 @@ class SidebarUser extends StatelessWidget {
|
|||||||
const SidebarUser({
|
const SidebarUser({
|
||||||
super.key,
|
super.key,
|
||||||
required this.userProfile,
|
required this.userProfile,
|
||||||
required this.views,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final UserProfilePB userProfile;
|
final UserProfilePB userProfile;
|
||||||
final List<ViewPB> views;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -37,13 +34,13 @@ class SidebarUser extends StatelessWidget {
|
|||||||
iconUrl: state.userProfile.iconUrl,
|
iconUrl: state.userProfile.iconUrl,
|
||||||
name: state.userProfile.name,
|
name: state.userProfile.name,
|
||||||
),
|
),
|
||||||
const HSpace(4),
|
const HSpace(8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildUserName(context, state),
|
child: _buildUserName(context, state),
|
||||||
),
|
),
|
||||||
UserSettingButton(userProfile: state.userProfile),
|
UserSettingButton(userProfile: state.userProfile),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
NotificationButton(views: views),
|
const NotificationButton(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -6,7 +6,6 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sid
|
|||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -20,11 +19,9 @@ class SidebarWorkspace extends StatelessWidget {
|
|||||||
const SidebarWorkspace({
|
const SidebarWorkspace({
|
||||||
super.key,
|
super.key,
|
||||||
required this.userProfile,
|
required this.userProfile,
|
||||||
required this.views,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final UserProfilePB userProfile;
|
final UserProfilePB userProfile;
|
||||||
final List<ViewPB> views;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -46,7 +43,7 @@ class SidebarWorkspace extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
UserSettingButton(userProfile: userProfile),
|
UserSettingButton(userProfile: userProfile),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
NotificationButton(views: views),
|
const NotificationButton(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -182,13 +182,13 @@ class WorkspaceMenuItem extends StatelessWidget {
|
|||||||
Widget _buildRightIcon(BuildContext context) {
|
Widget _buildRightIcon(BuildContext context) {
|
||||||
// only the owner can update or delete workspace.
|
// only the owner can update or delete workspace.
|
||||||
// only show the more action button when the workspace is selected.
|
// only show the more action button when the workspace is selected.
|
||||||
if (!isSelected ||
|
if (!isSelected) {
|
||||||
!context.read<WorkspaceMemberBloc>().state.myRole.isOwner) {
|
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
if (context.read<WorkspaceMemberBloc>().state.myRole.isOwner)
|
||||||
WorkspaceMoreActionList(workspace: workspace),
|
WorkspaceMoreActionList(workspace: workspace),
|
||||||
const FlowySvg(
|
const FlowySvg(
|
||||||
FlowySvgs.blue_check_s,
|
FlowySvgs.blue_check_s,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/draggable_item/draggable_item.dart';
|
import 'package:appflowy/workspace/presentation/widgets/draggable_item/draggable_item.dart';
|
||||||
@ -188,6 +189,9 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final fromSection = getViewSection(from);
|
||||||
|
final toSection = getViewSection(to);
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case DraggableHoverPosition.top:
|
case DraggableHoverPosition.top:
|
||||||
context.read<ViewBloc>().add(
|
context.read<ViewBloc>().add(
|
||||||
@ -195,6 +199,8 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
from,
|
from,
|
||||||
to.parentViewId,
|
to.parentViewId,
|
||||||
null,
|
null,
|
||||||
|
fromSection,
|
||||||
|
toSection,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -204,6 +210,8 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
from,
|
from,
|
||||||
to.parentViewId,
|
to.parentViewId,
|
||||||
to.id,
|
to.id,
|
||||||
|
fromSection,
|
||||||
|
toSection,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -213,6 +221,8 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
from,
|
from,
|
||||||
to.id,
|
to.id,
|
||||||
to.childViews.lastOrNull?.id,
|
to.childViews.lastOrNull?.id,
|
||||||
|
fromSection,
|
||||||
|
toSection,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -251,6 +261,10 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewSectionPB? getViewSection(ViewPB view) {
|
||||||
|
return context.read<SidebarSectionsBloc>().getViewSection(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on ViewPB {
|
extension on ViewPB {
|
||||||
|
@ -475,6 +475,7 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
viewName,
|
viewName,
|
||||||
pluginBuilder.layoutType!,
|
pluginBuilder.layoutType!,
|
||||||
openAfterCreated: openAfterCreated,
|
openAfterCreated: openAfterCreated,
|
||||||
|
section: widget.categoryType.toViewSectionPB,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ 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/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/notifications/notification_dialog.dart';
|
import 'package:appflowy/workspace/presentation/notifications/notification_dialog.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
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/theme_extension.dart';
|
||||||
@ -13,12 +13,13 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class NotificationButton extends StatelessWidget {
|
class NotificationButton extends StatelessWidget {
|
||||||
const NotificationButton({super.key, required this.views});
|
const NotificationButton({
|
||||||
|
super.key,
|
||||||
final List<ViewPB> views;
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final views = context.watch<SidebarSectionsBloc>().state.section.views;
|
||||||
final mutex = PopoverMutex();
|
final mutex = PopoverMutex();
|
||||||
|
|
||||||
return BlocProvider<ReminderBloc>.value(
|
return BlocProvider<ReminderBloc>.value(
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import 'package:appflowy/user/application/user_service.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -21,7 +23,8 @@ class WorkspaceMemberBloc
|
|||||||
WorkspaceMemberBloc({
|
WorkspaceMemberBloc({
|
||||||
required this.userProfile,
|
required this.userProfile,
|
||||||
this.workspace,
|
this.workspace,
|
||||||
}) : super(WorkspaceMemberState.initial()) {
|
}) : _userBackendService = UserBackendService(userId: userProfile.id),
|
||||||
|
super(WorkspaceMemberState.initial()) {
|
||||||
on<WorkspaceMemberEvent>((event, emit) async {
|
on<WorkspaceMemberEvent>((event, emit) async {
|
||||||
await event.when(
|
await event.when(
|
||||||
initial: () async {
|
initial: () async {
|
||||||
@ -73,14 +76,16 @@ class WorkspaceMemberBloc
|
|||||||
final UserWorkspacePB? workspace;
|
final UserWorkspacePB? workspace;
|
||||||
|
|
||||||
late final String workspaceId;
|
late final String workspaceId;
|
||||||
|
late final UserBackendService _userBackendService;
|
||||||
|
|
||||||
Future<List<WorkspaceMemberPB>> _getWorkspaceMembers() async {
|
Future<List<WorkspaceMemberPB>> _getWorkspaceMembers() async {
|
||||||
final data = QueryWorkspacePB()..workspaceId = workspaceId;
|
return _userBackendService.getWorkspaceMembers(workspaceId).fold(
|
||||||
final result = await UserEventGetWorkspaceMember(data).send();
|
(s) => s.items,
|
||||||
return result.fold((s) => s.items, (e) {
|
(e) {
|
||||||
Log.error('Failed to read workspace members: $e');
|
Log.error('Failed to read workspace members: $e');
|
||||||
return [];
|
return [];
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
AFRolePB _getMyRole(List<WorkspaceMemberPB> members) {
|
AFRolePB _getMyRole(List<WorkspaceMemberPB> members) {
|
||||||
@ -97,40 +102,26 @@ class WorkspaceMemberBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addWorkspaceMember(String email) async {
|
Future<void> _addWorkspaceMember(String email) async {
|
||||||
final data = AddWorkspaceMemberPB()
|
return _userBackendService.addWorkspaceMember(workspaceId, email).fold(
|
||||||
..workspaceId = workspaceId
|
(s) => Log.debug('Added workspace member: $email'),
|
||||||
..email = email;
|
(e) => Log.error('Failed to add workspace member: $e'),
|
||||||
final result = await UserEventAddWorkspaceMember(data).send();
|
);
|
||||||
result.fold((s) {
|
|
||||||
Log.info('Added workspace member: $data');
|
|
||||||
}, (e) {
|
|
||||||
Log.error('Failed to add workspace member: $e');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _removeWorkspaceMember(String email) async {
|
Future<void> _removeWorkspaceMember(String email) async {
|
||||||
final data = RemoveWorkspaceMemberPB()
|
return _userBackendService.removeWorkspaceMember(workspaceId, email).fold(
|
||||||
..workspaceId = workspaceId
|
(s) => Log.debug('Removed workspace member: $email'),
|
||||||
..email = email;
|
(e) => Log.error('Failed to remove workspace member: $e'),
|
||||||
final result = await UserEventRemoveWorkspaceMember(data).send();
|
);
|
||||||
result.fold((s) {
|
|
||||||
Log.info('Removed workspace member: $data');
|
|
||||||
}, (e) {
|
|
||||||
Log.error('Failed to remove workspace member: $e');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateWorkspaceMember(String email, AFRolePB role) async {
|
Future<void> _updateWorkspaceMember(String email, AFRolePB role) async {
|
||||||
final data = UpdateWorkspaceMemberPB()
|
return _userBackendService
|
||||||
..workspaceId = workspaceId
|
.updateWorkspaceMember(workspaceId, email, role)
|
||||||
..email = email
|
.fold(
|
||||||
..role = role;
|
(s) => Log.debug('Updated workspace member: $email'),
|
||||||
final result = await UserEventUpdateWorkspaceMember(data).send();
|
(e) => Log.error('Failed to update workspace member: $e'),
|
||||||
result.fold((s) {
|
);
|
||||||
Log.info('Updated workspace member: $data');
|
|
||||||
}, (e) {
|
|
||||||
Log.error('Failed to update workspace member: $e');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class UserAvatar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: FlowyText.semibold(
|
child: FlowyText.semibold(
|
||||||
nameInitials,
|
nameInitials,
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
fontSize: isLarge
|
fontSize: isLarge
|
||||||
? nameInitials.length == initialsCount
|
? nameInitials.length == initialsCount
|
||||||
? 20
|
? 20
|
||||||
|
@ -8,6 +8,10 @@ extension FlowyAsyncResultExtension<S, F extends Object>
|
|||||||
return then((result) => result.getOrElse(onFailure));
|
return then((result) => result.getOrElse(onFailure));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<S?> toNullable() {
|
||||||
|
return then((result) => result.toNullable());
|
||||||
|
}
|
||||||
|
|
||||||
Future<S> getOrThrow() {
|
Future<S> getOrThrow() {
|
||||||
return then((result) => result.getOrThrow());
|
return then((result) => result.getOrThrow());
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,13 @@ void main() {
|
|||||||
final appBloc = ViewBloc(view: app)..add(const ViewEvent.initial());
|
final appBloc = ViewBloc(view: app)..add(const ViewEvent.initial());
|
||||||
assert(appBloc.state.lastCreatedView == null);
|
assert(appBloc.state.lastCreatedView == null);
|
||||||
|
|
||||||
appBloc
|
appBloc.add(
|
||||||
.add(const ViewEvent.createView("New document", ViewLayoutPB.Document));
|
const ViewEvent.createView(
|
||||||
|
"New document",
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
|
|
||||||
assert(appBloc.state.lastCreatedView != null);
|
assert(appBloc.state.lastCreatedView != null);
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import 'package:appflowy/workspace/application/menu/sidebar_root_views_bloc.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import '../../util.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late AppFlowyUnitTest testContext;
|
|
||||||
setUpAll(() async {
|
|
||||||
testContext = await AppFlowyUnitTest.ensureInitialized();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('assert initial apps is the build-in app', () async {
|
|
||||||
final menuBloc = SidebarRootViewsBloc()
|
|
||||||
..add(
|
|
||||||
SidebarRootViewsEvent.initial(
|
|
||||||
testContext.userProfile,
|
|
||||||
testContext.currentWorkspace.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await blocResponseFuture();
|
|
||||||
|
|
||||||
assert(menuBloc.state.views.length == 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('reorder apps', () async {
|
|
||||||
final menuBloc = SidebarRootViewsBloc()
|
|
||||||
..add(
|
|
||||||
SidebarRootViewsEvent.initial(
|
|
||||||
testContext.userProfile,
|
|
||||||
testContext.currentWorkspace.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await blocResponseFuture();
|
|
||||||
menuBloc.add(const SidebarRootViewsEvent.createRootView("App 1"));
|
|
||||||
await blocResponseFuture();
|
|
||||||
menuBloc.add(const SidebarRootViewsEvent.createRootView("App 2"));
|
|
||||||
await blocResponseFuture();
|
|
||||||
menuBloc.add(const SidebarRootViewsEvent.createRootView("App 3"));
|
|
||||||
await blocResponseFuture();
|
|
||||||
|
|
||||||
assert(menuBloc.state.views[1].name == 'App 1');
|
|
||||||
assert(menuBloc.state.views[2].name == 'App 2');
|
|
||||||
assert(menuBloc.state.views[3].name == 'App 3');
|
|
||||||
});
|
|
||||||
}
|
|
@ -0,0 +1,57 @@
|
|||||||
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../../util.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late AppFlowyUnitTest testContext;
|
||||||
|
setUpAll(() async {
|
||||||
|
testContext = await AppFlowyUnitTest.ensureInitialized();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('assert initial apps is the build-in app', () async {
|
||||||
|
final menuBloc = SidebarSectionsBloc()
|
||||||
|
..add(
|
||||||
|
SidebarSectionsEvent.initial(
|
||||||
|
testContext.userProfile,
|
||||||
|
testContext.currentWorkspace.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await blocResponseFuture();
|
||||||
|
|
||||||
|
assert(menuBloc.state.section.publicViews.length == 1);
|
||||||
|
assert(menuBloc.state.section.privateViews.isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create views', () async {
|
||||||
|
final menuBloc = SidebarSectionsBloc()
|
||||||
|
..add(
|
||||||
|
SidebarSectionsEvent.initial(
|
||||||
|
testContext.userProfile,
|
||||||
|
testContext.currentWorkspace.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await blocResponseFuture();
|
||||||
|
|
||||||
|
final names = ['View 1', 'View 2', 'View 3'];
|
||||||
|
for (final name in names) {
|
||||||
|
menuBloc.add(
|
||||||
|
SidebarSectionsEvent.createRootViewInSection(
|
||||||
|
name: name,
|
||||||
|
index: 0,
|
||||||
|
viewSection: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await blocResponseFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
final reversedNames = names.reversed.toList();
|
||||||
|
for (var i = 0; i < names.length; i++) {
|
||||||
|
assert(
|
||||||
|
menuBloc.state.section.publicViews[i].name == reversedNames[i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -22,6 +22,7 @@ class TrashTestContext {
|
|||||||
const ViewEvent.createView(
|
const ViewEvent.createView(
|
||||||
"Document 1",
|
"Document 1",
|
||||||
ViewLayoutPB.Document,
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
@ -30,6 +31,7 @@ class TrashTestContext {
|
|||||||
const ViewEvent.createView(
|
const ViewEvent.createView(
|
||||||
"Document 2",
|
"Document 2",
|
||||||
ViewLayoutPB.Document,
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
@ -38,6 +40,7 @@ class TrashTestContext {
|
|||||||
const ViewEvent.createView(
|
const ViewEvent.createView(
|
||||||
"Document 3",
|
"Document 3",
|
||||||
ViewLayoutPB.Document,
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
|
@ -36,7 +36,11 @@ void main() {
|
|||||||
final viewBloc = await createTestViewBloc();
|
final viewBloc = await createTestViewBloc();
|
||||||
// create a nested view
|
// create a nested view
|
||||||
viewBloc.add(
|
viewBloc.add(
|
||||||
const ViewEvent.createView(name, ViewLayoutPB.Document),
|
const ViewEvent.createView(
|
||||||
|
name,
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(viewBloc.state.view.childViews.length, 1);
|
expect(viewBloc.state.view.childViews.length, 1);
|
||||||
@ -52,7 +56,11 @@ void main() {
|
|||||||
test('delete view test', () async {
|
test('delete view test', () async {
|
||||||
final viewBloc = await createTestViewBloc();
|
final viewBloc = await createTestViewBloc();
|
||||||
viewBloc.add(
|
viewBloc.add(
|
||||||
const ViewEvent.createView(name, ViewLayoutPB.Document),
|
const ViewEvent.createView(
|
||||||
|
name,
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(viewBloc.state.view.childViews.length, 1);
|
expect(viewBloc.state.view.childViews.length, 1);
|
||||||
@ -69,7 +77,11 @@ void main() {
|
|||||||
test('create nested view test', () async {
|
test('create nested view test', () async {
|
||||||
final viewBloc = await createTestViewBloc();
|
final viewBloc = await createTestViewBloc();
|
||||||
viewBloc.add(
|
viewBloc.add(
|
||||||
const ViewEvent.createView('Document 1', ViewLayoutPB.Document),
|
const ViewEvent.createView(
|
||||||
|
'Document 1',
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
final document1Bloc = ViewBloc(view: viewBloc.state.view.childViews.first)
|
final document1Bloc = ViewBloc(view: viewBloc.state.view.childViews.first)
|
||||||
@ -79,7 +91,11 @@ void main() {
|
|||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
const name = 'Document 1 - 1';
|
const name = 'Document 1 - 1';
|
||||||
document1Bloc.add(
|
document1Bloc.add(
|
||||||
const ViewEvent.createView('Document 1 - 1', ViewLayoutPB.Document),
|
const ViewEvent.createView(
|
||||||
|
'Document 1 - 1',
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(document1Bloc.state.view.childViews.length, 1);
|
expect(document1Bloc.state.view.childViews.length, 1);
|
||||||
@ -91,7 +107,11 @@ void main() {
|
|||||||
final names = ['1', '2', '3'];
|
final names = ['1', '2', '3'];
|
||||||
for (final name in names) {
|
for (final name in names) {
|
||||||
viewBloc.add(
|
viewBloc.add(
|
||||||
ViewEvent.createView(name, ViewLayoutPB.Document),
|
ViewEvent.createView(
|
||||||
|
name,
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
}
|
}
|
||||||
@ -106,7 +126,13 @@ void main() {
|
|||||||
final viewBloc = await createTestViewBloc();
|
final viewBloc = await createTestViewBloc();
|
||||||
expect(viewBloc.state.lastCreatedView, isNull);
|
expect(viewBloc.state.lastCreatedView, isNull);
|
||||||
|
|
||||||
viewBloc.add(const ViewEvent.createView('1', ViewLayoutPB.Document));
|
viewBloc.add(
|
||||||
|
const ViewEvent.createView(
|
||||||
|
'1',
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(
|
expect(
|
||||||
viewBloc.state.lastCreatedView!.id,
|
viewBloc.state.lastCreatedView!.id,
|
||||||
@ -117,7 +143,13 @@ void main() {
|
|||||||
'1',
|
'1',
|
||||||
);
|
);
|
||||||
|
|
||||||
viewBloc.add(const ViewEvent.createView('2', ViewLayoutPB.Document));
|
viewBloc.add(
|
||||||
|
const ViewEvent.createView(
|
||||||
|
'2',
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(
|
expect(
|
||||||
viewBloc.state.lastCreatedView!.name,
|
viewBloc.state.lastCreatedView!.name,
|
||||||
@ -128,13 +160,25 @@ void main() {
|
|||||||
test('open latest document test', () async {
|
test('open latest document test', () async {
|
||||||
const name1 = 'document';
|
const name1 = 'document';
|
||||||
final viewBloc = await createTestViewBloc();
|
final viewBloc = await createTestViewBloc();
|
||||||
viewBloc.add(const ViewEvent.createView(name1, ViewLayoutPB.Document));
|
viewBloc.add(
|
||||||
|
const ViewEvent.createView(
|
||||||
|
name1,
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
final document = viewBloc.state.lastCreatedView!;
|
final document = viewBloc.state.lastCreatedView!;
|
||||||
assert(document.name == name1);
|
assert(document.name == name1);
|
||||||
|
|
||||||
const gird = 'grid';
|
const gird = 'grid';
|
||||||
viewBloc.add(const ViewEvent.createView(gird, ViewLayoutPB.Document));
|
viewBloc.add(
|
||||||
|
const ViewEvent.createView(
|
||||||
|
gird,
|
||||||
|
ViewLayoutPB.Document,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
assert(viewBloc.state.lastCreatedView!.name == gird);
|
assert(viewBloc.state.lastCreatedView!.name == gird);
|
||||||
|
|
||||||
@ -170,7 +214,11 @@ void main() {
|
|||||||
for (var i = 0; i < layouts.length; i++) {
|
for (var i = 0; i < layouts.length; i++) {
|
||||||
final layout = layouts[i];
|
final layout = layouts[i];
|
||||||
viewBloc.add(
|
viewBloc.add(
|
||||||
ViewEvent.createView('Test $layout', layout),
|
ViewEvent.createView(
|
||||||
|
'Test $layout',
|
||||||
|
layout,
|
||||||
|
section: ViewSectionPB.Public,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
expect(viewBloc.state.view.childViews.length, i + 1);
|
expect(viewBloc.state.view.childViews.length, i + 1);
|
||||||
|
@ -74,7 +74,10 @@ class AppFlowyUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<ViewPB> createWorkspace() async {
|
Future<ViewPB> createWorkspace() async {
|
||||||
final result = await workspaceService.createApp(name: "Test App");
|
final result = await workspaceService.createView(
|
||||||
|
name: "Test App",
|
||||||
|
viewSection: ViewSectionPB.Public,
|
||||||
|
);
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(app) => app,
|
(app) => app,
|
||||||
(error) => throw Exception(error),
|
(error) => throw Exception(error),
|
||||||
@ -82,7 +85,7 @@ class AppFlowyUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ViewPB>> loadApps() async {
|
Future<List<ViewPB>> loadApps() async {
|
||||||
final result = await workspaceService.getViews();
|
final result = await workspaceService.getPublicViews();
|
||||||
|
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(apps) => apps,
|
(apps) => apps,
|
||||||
|
37
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
37
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -162,7 +162,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -716,7 +716,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -764,7 +764,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -838,7 +838,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -854,6 +854,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"unicode-segmentation",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yrs",
|
"yrs",
|
||||||
]
|
]
|
||||||
@ -861,7 +862,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -891,7 +892,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -910,7 +911,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -925,7 +926,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -962,7 +963,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1001,7 +1002,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1335,7 +1336,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2637,7 +2638,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2654,7 +2655,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3109,7 +3110,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -4892,7 +4893,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -4916,7 +4917,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-protocol"
|
name = "realtime-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -5588,7 +5589,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -7551,7 +7552,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace-template"
|
name = "workspace-template"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -86,7 +86,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c5112cc761736ac91f0a518552e7bbe522bceae6" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ab9496c248b7c733d1aa160062abeb66c4e41325" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
@ -96,10 +96,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c51
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
|
import { parserViewPBToPage } from '$app_reducers/pages/slice';
|
||||||
import {
|
import {
|
||||||
|
ChangeWorkspaceIconPB,
|
||||||
CreateViewPayloadPB,
|
CreateViewPayloadPB,
|
||||||
|
GetWorkspaceViewPB,
|
||||||
|
RenameWorkspacePB,
|
||||||
UserWorkspaceIdPB,
|
UserWorkspaceIdPB,
|
||||||
WorkspaceIdPB,
|
WorkspaceIdPB,
|
||||||
RenameWorkspacePB,
|
|
||||||
ChangeWorkspaceIconPB,
|
|
||||||
} from '@/services/backend';
|
} from '@/services/backend';
|
||||||
import {
|
|
||||||
UserEventOpenWorkspace,
|
|
||||||
UserEventRenameWorkspace,
|
|
||||||
UserEventChangeWorkspaceIcon,
|
|
||||||
UserEventGetAllWorkspace,
|
|
||||||
} from '@/services/backend/events/flowy-user';
|
|
||||||
import {
|
import {
|
||||||
FolderEventCreateView,
|
FolderEventCreateView,
|
||||||
FolderEventDeleteWorkspace,
|
FolderEventDeleteWorkspace,
|
||||||
@ -18,7 +14,12 @@ import {
|
|||||||
FolderEventReadCurrentWorkspace,
|
FolderEventReadCurrentWorkspace,
|
||||||
FolderEventReadWorkspaceViews,
|
FolderEventReadWorkspaceViews,
|
||||||
} from '@/services/backend/events/flowy-folder';
|
} from '@/services/backend/events/flowy-folder';
|
||||||
import { parserViewPBToPage } from '$app_reducers/pages/slice';
|
import {
|
||||||
|
UserEventChangeWorkspaceIcon,
|
||||||
|
UserEventGetAllWorkspace,
|
||||||
|
UserEventOpenWorkspace,
|
||||||
|
UserEventRenameWorkspace,
|
||||||
|
} from '@/services/backend/events/flowy-user';
|
||||||
|
|
||||||
export async function openWorkspace(id: string) {
|
export async function openWorkspace(id: string) {
|
||||||
const payload = new UserWorkspaceIdPB({
|
const payload = new UserWorkspaceIdPB({
|
||||||
@ -49,7 +50,7 @@ export async function deleteWorkspace(id: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkspaceChildViews(id: string) {
|
export async function getWorkspaceChildViews(id: string) {
|
||||||
const payload = new WorkspaceIdPB({
|
const payload = new GetWorkspaceViewPB({
|
||||||
value: id,
|
value: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
35
frontend/appflowy_web/wasm-libs/Cargo.lock
generated
35
frontend/appflowy_web/wasm-libs/Cargo.lock
generated
@ -221,7 +221,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -545,7 +545,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -592,7 +592,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -636,7 +636,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -652,6 +652,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"unicode-segmentation",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yrs",
|
"yrs",
|
||||||
]
|
]
|
||||||
@ -659,7 +660,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -678,7 +679,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -693,7 +694,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -730,7 +731,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -768,7 +769,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -965,7 +966,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -1720,7 +1721,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1737,7 +1738,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2071,7 +2072,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3315,7 +3316,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -3339,7 +3340,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-protocol"
|
name = "realtime-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -3792,7 +3793,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -5024,4 +5025,4 @@ dependencies = [
|
|||||||
[[patch.unused]]
|
[[patch.unused]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
|
@ -55,7 +55,7 @@ codegen-units = 1
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c5112cc761736ac91f0a518552e7bbe522bceae6" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ab9496c248b7c733d1aa160062abeb66c4e41325" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
@ -65,10 +65,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c51
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
|
@ -205,10 +205,16 @@
|
|||||||
"closeSidebar": "Close side bar",
|
"closeSidebar": "Close side bar",
|
||||||
"openSidebar": "Open side bar",
|
"openSidebar": "Open side bar",
|
||||||
"personal": "Personal",
|
"personal": "Personal",
|
||||||
|
"private": "Private",
|
||||||
|
"public": "Public",
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
|
"clickToHidePrivate": "Click to hide private section\nPages you created here are only visible to you",
|
||||||
|
"clickToHidePublic": "Click to hide public section\nPages you created here are visible to every member",
|
||||||
"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",
|
||||||
|
"addAPageToPrivate": "Add a page to private section",
|
||||||
|
"addAPageToPublic": "Add a page to public section",
|
||||||
"recent": "Recent"
|
"recent": "Recent"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
37
frontend/rust-lib/Cargo.lock
generated
37
frontend/rust-lib/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -673,7 +673,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -721,7 +721,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -764,7 +764,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -780,6 +780,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"unicode-segmentation",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yrs",
|
"yrs",
|
||||||
]
|
]
|
||||||
@ -787,7 +788,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -817,7 +818,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -836,7 +837,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -851,7 +852,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -888,7 +889,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -927,7 +928,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2d7b1838e463ce0348cf700ff43f33f5718203be#2d7b1838e463ce0348cf700ff43f33f5718203be"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0970b2e1440134af7c83bb8fc80cac5d2dedebb7#0970b2e1440134af7c83bb8fc80cac5d2dedebb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1257,7 +1258,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2432,7 +2433,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2449,7 +2450,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2843,7 +2844,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -4325,7 +4326,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -4349,7 +4350,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-protocol"
|
name = "realtime-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -4942,7 +4943,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -6355,7 +6356,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace-template"
|
name = "workspace-template"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c5112cc761736ac91f0a518552e7bbe522bceae6#c5112cc761736ac91f0a518552e7bbe522bceae6"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ab9496c248b7c733d1aa160062abeb66c4e41325#ab9496c248b7c733d1aa160062abeb66c4e41325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -110,7 +110,7 @@ rocksdb = { git = "https://github.com/LucasXu0/rust-rocksdb", rev = "21cf4a23ec1
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c5112cc761736ac91f0a518552e7bbe522bceae6" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ab9496c248b7c733d1aa160062abeb66c4e41325" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
@ -120,10 +120,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c51
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2d7b1838e463ce0348cf700ff43f33f5718203be" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0970b2e1440134af7c83bb8fc80cac5d2dedebb7" }
|
||||||
|
@ -36,6 +36,7 @@ impl EventIntegrationTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
@ -66,6 +67,7 @@ impl EventIntegrationTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
@ -91,6 +93,7 @@ impl EventIntegrationTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
|
@ -64,6 +64,7 @@ impl DocumentEventTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(core.clone())
|
EventBuilder::new(core.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
|
@ -41,6 +41,7 @@ impl EventIntegrationTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
let view = EventBuilder::new(self.clone())
|
let view = EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
|
@ -57,7 +57,7 @@ impl EventIntegrationTest {
|
|||||||
|
|
||||||
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {
|
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::ReadWorkspaceViews)
|
.event(FolderEvent::ReadCurrentWorkspaceViews)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.parse::<RepeatedViewPB>()
|
.parse::<RepeatedViewPB>()
|
||||||
@ -115,6 +115,7 @@ impl EventIntegrationTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: false,
|
set_as_current: false,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::CreateView)
|
.event(FolderEvent::CreateView)
|
||||||
@ -165,6 +166,7 @@ impl ViewTest {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let view = EventBuilder::new(sdk.clone())
|
let view = EventBuilder::new(sdk.clone())
|
||||||
|
@ -276,9 +276,9 @@ impl EventIntegrationTest {
|
|||||||
.parse()
|
.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn folder_read_workspace_views(&self) -> RepeatedViewPB {
|
pub async fn folder_read_current_workspace_views(&self) -> RepeatedViewPB {
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(FolderEvent::ReadWorkspaceViews)
|
.event(FolderEvent::ReadCurrentWorkspaceViews)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.parse()
|
.parse()
|
||||||
|
@ -246,6 +246,7 @@ pub async fn create_view(
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(CreateView)
|
.event(CreateView)
|
||||||
@ -275,6 +276,8 @@ pub async fn move_view(
|
|||||||
view_id,
|
view_id,
|
||||||
new_parent_id: parent_id,
|
new_parent_id: parent_id,
|
||||||
prev_view_id,
|
prev_view_id,
|
||||||
|
from_section: None,
|
||||||
|
to_section: None,
|
||||||
};
|
};
|
||||||
let error = EventBuilder::new(sdk.clone())
|
let error = EventBuilder::new(sdk.clone())
|
||||||
.event(MoveNestedView)
|
.event(MoveNestedView)
|
||||||
|
@ -549,6 +549,8 @@ async fn move_folder_nested_view(
|
|||||||
view_id,
|
view_id,
|
||||||
new_parent_id,
|
new_parent_id,
|
||||||
prev_view_id,
|
prev_view_id,
|
||||||
|
from_section: None,
|
||||||
|
to_section: None,
|
||||||
};
|
};
|
||||||
EventBuilder::new(sdk)
|
EventBuilder::new(sdk)
|
||||||
.event(flowy_folder::event_map::FolderEvent::MoveNestedView)
|
.event(flowy_folder::event_map::FolderEvent::MoveNestedView)
|
||||||
|
@ -77,7 +77,7 @@ async fn af_cloud_create_workspace_test() {
|
|||||||
// before opening new workspace
|
// before opening new workspace
|
||||||
let folder_ws = test.folder_read_current_workspace().await;
|
let folder_ws = test.folder_read_current_workspace().await;
|
||||||
assert_eq!(&folder_ws.id, first_workspace_id);
|
assert_eq!(&folder_ws.id, first_workspace_id);
|
||||||
let views = test.folder_read_workspace_views().await;
|
let views = test.folder_read_current_workspace_views().await;
|
||||||
assert_eq!(views.items[0].parent_view_id.as_str(), first_workspace_id);
|
assert_eq!(views.items[0].parent_view_id.as_str(), first_workspace_id);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -85,7 +85,7 @@ async fn af_cloud_create_workspace_test() {
|
|||||||
test.open_workspace(&created_workspace.workspace_id).await;
|
test.open_workspace(&created_workspace.workspace_id).await;
|
||||||
let folder_ws = test.folder_read_current_workspace().await;
|
let folder_ws = test.folder_read_current_workspace().await;
|
||||||
assert_eq!(folder_ws.id, created_workspace.workspace_id);
|
assert_eq!(folder_ws.id, created_workspace.workspace_id);
|
||||||
let views = test.folder_read_workspace_views().await;
|
let views = test.folder_read_current_workspace_views().await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
views.items[0].parent_view_id.as_str(),
|
views.items[0].parent_view_id.as_str(),
|
||||||
created_workspace.workspace_id
|
created_workspace.workspace_id
|
||||||
|
@ -59,6 +59,7 @@ impl ViewBuilder {
|
|||||||
layout: ViewLayout::Document,
|
layout: ViewLayout::Document,
|
||||||
child_views: vec![],
|
child_views: vec![],
|
||||||
is_favorite: false,
|
is_favorite: false,
|
||||||
|
|
||||||
icon: None,
|
icon: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,15 @@ impl std::convert::From<ViewLayout> for ViewLayoutPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
|
||||||
|
pub struct SectionViewsPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub section: ViewSectionPB,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub views: Vec<ViewPB>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
|
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
|
||||||
pub struct RepeatedViewPB {
|
pub struct RepeatedViewPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
@ -181,6 +190,20 @@ pub struct CreateViewPayloadPB {
|
|||||||
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
|
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
|
||||||
#[pb(index = 9, one_of)]
|
#[pb(index = 9, one_of)]
|
||||||
pub index: Option<u32>,
|
pub index: Option<u32>,
|
||||||
|
|
||||||
|
// The section of the view.
|
||||||
|
// Only the view in public section will be shown in the shared workspace view list.
|
||||||
|
// The view in private section will only be shown in the user's private view list.
|
||||||
|
#[pb(index = 10, one_of)]
|
||||||
|
pub section: Option<ViewSectionPB>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone, Default)]
|
||||||
|
pub enum ViewSectionPB {
|
||||||
|
#[default]
|
||||||
|
// only support public and private section now.
|
||||||
|
Private = 0,
|
||||||
|
Public = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The orphan view is meant to be a view that is not attached to any parent view. By default, this
|
/// The orphan view is meant to be a view that is not attached to any parent view. By default, this
|
||||||
@ -218,6 +241,8 @@ pub struct CreateViewParams {
|
|||||||
// The index of the view in the parent view.
|
// The index of the view in the parent view.
|
||||||
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
|
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
|
||||||
pub index: Option<u32>,
|
pub index: Option<u32>,
|
||||||
|
// The section of the view.
|
||||||
|
pub section: Option<ViewSectionPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<CreateViewParams> for CreateViewPayloadPB {
|
impl TryInto<CreateViewParams> for CreateViewPayloadPB {
|
||||||
@ -238,6 +263,7 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
|
|||||||
meta: self.meta,
|
meta: self.meta,
|
||||||
set_as_current: self.set_as_current,
|
set_as_current: self.set_as_current,
|
||||||
index: self.index,
|
index: self.index,
|
||||||
|
section: self.section,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,6 +285,8 @@ impl TryInto<CreateViewParams> for CreateOrphanViewPayloadPB {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: false,
|
set_as_current: false,
|
||||||
index: None,
|
index: None,
|
||||||
|
// TODO: lucas.xu add section to CreateOrphanViewPayloadPB
|
||||||
|
section: Some(ViewSectionPB::Public),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,6 +412,12 @@ pub struct MoveNestedViewPayloadPB {
|
|||||||
|
|
||||||
#[pb(index = 3, one_of)]
|
#[pb(index = 3, one_of)]
|
||||||
pub prev_view_id: Option<String>,
|
pub prev_view_id: Option<String>,
|
||||||
|
|
||||||
|
#[pb(index = 4, one_of)]
|
||||||
|
pub from_section: Option<ViewSectionPB>,
|
||||||
|
|
||||||
|
#[pb(index = 5, one_of)]
|
||||||
|
pub to_section: Option<ViewSectionPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MoveViewParams {
|
pub struct MoveViewParams {
|
||||||
@ -405,10 +439,13 @@ impl TryInto<MoveViewParams> for MoveViewPayloadPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MoveNestedViewParams {
|
pub struct MoveNestedViewParams {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub new_parent_id: String,
|
pub new_parent_id: String,
|
||||||
pub prev_view_id: Option<String>,
|
pub prev_view_id: Option<String>,
|
||||||
|
pub from_section: Option<ViewSectionPB>,
|
||||||
|
pub to_section: Option<ViewSectionPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
|
impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
|
||||||
@ -422,6 +459,8 @@ impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
|
|||||||
view_id,
|
view_id,
|
||||||
new_parent_id,
|
new_parent_id,
|
||||||
prev_view_id,
|
prev_view_id,
|
||||||
|
from_section: self.from_section,
|
||||||
|
to_section: self.to_section,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,42 @@ pub struct WorkspaceIdPB {
|
|||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WorkspaceIdParams {
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<WorkspaceIdParams> for WorkspaceIdPB {
|
||||||
|
type Error = ErrorCode;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<WorkspaceIdParams, Self::Error> {
|
||||||
|
Ok(WorkspaceIdParams {
|
||||||
|
value: WorkspaceIdentify::parse(self.value)?.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, ProtoBuf, Default, Debug)]
|
||||||
|
pub struct GetWorkspaceViewPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct GetWorkspaceViewParams {
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<GetWorkspaceViewParams> for GetWorkspaceViewPB {
|
||||||
|
type Error = ErrorCode;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<GetWorkspaceViewParams, Self::Error> {
|
||||||
|
Ok(GetWorkspaceViewParams {
|
||||||
|
value: WorkspaceIdentify::parse(self.value)?.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf, Debug, Clone)]
|
#[derive(Default, ProtoBuf, Debug, Clone)]
|
||||||
pub struct WorkspaceSettingPB {
|
pub struct WorkspaceSettingPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
|
@ -48,6 +48,18 @@ pub(crate) async fn get_all_workspace_handler(
|
|||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub(crate) async fn get_workspace_views_handler(
|
pub(crate) async fn get_workspace_views_handler(
|
||||||
|
data: AFPluginData<GetWorkspaceViewPB>,
|
||||||
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> DataResult<RepeatedViewPB, FlowyError> {
|
||||||
|
let folder = upgrade_folder(folder)?;
|
||||||
|
let params: GetWorkspaceViewParams = data.into_inner().try_into()?;
|
||||||
|
let child_views = folder.get_workspace_views(¶ms.value).await?;
|
||||||
|
let repeated_view: RepeatedViewPB = child_views.into();
|
||||||
|
data_result_ok(repeated_view)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
|
pub(crate) async fn get_current_workspace_views_handler(
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
) -> DataResult<RepeatedViewPB, FlowyError> {
|
) -> DataResult<RepeatedViewPB, FlowyError> {
|
||||||
let folder = upgrade_folder(folder)?;
|
let folder = upgrade_folder(folder)?;
|
||||||
@ -56,6 +68,18 @@ pub(crate) async fn get_workspace_views_handler(
|
|||||||
data_result_ok(repeated_view)
|
data_result_ok(repeated_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
|
pub(crate) async fn read_private_views_handler(
|
||||||
|
data: AFPluginData<GetWorkspaceViewPB>,
|
||||||
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> DataResult<RepeatedViewPB, FlowyError> {
|
||||||
|
let folder = upgrade_folder(folder)?;
|
||||||
|
let params: GetWorkspaceViewParams = data.into_inner().try_into()?;
|
||||||
|
let child_views = folder.get_workspace_private_views(¶ms.value).await?;
|
||||||
|
let repeated_view: RepeatedViewPB = child_views.into();
|
||||||
|
data_result_ok(repeated_view)
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub(crate) async fn read_current_workspace_setting_handler(
|
pub(crate) async fn read_current_workspace_setting_handler(
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
@ -212,9 +236,7 @@ pub(crate) async fn move_nested_view_handler(
|
|||||||
) -> Result<(), FlowyError> {
|
) -> Result<(), FlowyError> {
|
||||||
let folder = upgrade_folder(folder)?;
|
let folder = upgrade_folder(folder)?;
|
||||||
let params: MoveNestedViewParams = data.into_inner().try_into()?;
|
let params: MoveNestedViewParams = data.into_inner().try_into()?;
|
||||||
folder
|
folder.move_nested_view(params).await?;
|
||||||
.move_nested_view(params.view_id, params.new_parent_id, params.prev_view_id)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||||||
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
|
||||||
.event(FolderEvent::UpdateRecentViews, update_recent_views_handler)
|
.event(FolderEvent::UpdateRecentViews, update_recent_views_handler)
|
||||||
.event(FolderEvent::ReloadWorkspace, reload_workspace_handler)
|
.event(FolderEvent::ReloadWorkspace, reload_workspace_handler)
|
||||||
|
.event(FolderEvent::ReadPrivateViews, read_private_views_handler)
|
||||||
|
.event(FolderEvent::ReadCurrentWorkspaceViews, get_current_workspace_views_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -59,9 +61,9 @@ pub enum FolderEvent {
|
|||||||
#[event(input = "WorkspaceIdPB")]
|
#[event(input = "WorkspaceIdPB")]
|
||||||
DeleteWorkspace = 3,
|
DeleteWorkspace = 3,
|
||||||
|
|
||||||
/// Return a list of views of the current workspace.
|
/// Return a list of views of the specified workspace.
|
||||||
/// Only the first level of child views are included.
|
/// Only the first level of child views are included.
|
||||||
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]
|
#[event(input = "GetWorkspaceViewPB", output = "RepeatedViewPB")]
|
||||||
ReadWorkspaceViews = 5,
|
ReadWorkspaceViews = 5,
|
||||||
|
|
||||||
/// Create a new view in the corresponding app
|
/// Create a new view in the corresponding app
|
||||||
@ -156,4 +158,12 @@ pub enum FolderEvent {
|
|||||||
|
|
||||||
#[event()]
|
#[event()]
|
||||||
ReloadWorkspace = 38,
|
ReloadWorkspace = 38,
|
||||||
|
|
||||||
|
#[event(input = "GetWorkspaceViewPB", output = "RepeatedViewPB")]
|
||||||
|
ReadPrivateViews = 39,
|
||||||
|
|
||||||
|
/// Return a list of views of the current workspace.
|
||||||
|
/// Only the first level of child views are included.
|
||||||
|
#[event(output = "RepeatedViewPB")]
|
||||||
|
ReadCurrentWorkspaceViews = 40,
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,8 @@ use lib_infra::conditional_send_sync_trait;
|
|||||||
use crate::entities::icon::UpdateViewIconParams;
|
use crate::entities::icon::UpdateViewIconParams;
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
view_pb_with_child_views, view_pb_without_child_views, CreateViewParams, CreateWorkspaceParams,
|
view_pb_with_child_views, view_pb_without_child_views, CreateViewParams, CreateWorkspaceParams,
|
||||||
DeletedViewPB, FolderSnapshotPB, RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB,
|
DeletedViewPB, FolderSnapshotPB, MoveNestedViewParams, RepeatedTrashPB, RepeatedViewIdPB,
|
||||||
UpdateViewParams, ViewPB, WorkspacePB, WorkspaceSettingPB,
|
RepeatedViewPB, UpdateViewParams, ViewPB, ViewSectionPB, WorkspacePB, WorkspaceSettingPB,
|
||||||
};
|
};
|
||||||
use crate::manager_observer::{
|
use crate::manager_observer::{
|
||||||
notify_child_views_changed, notify_did_update_workspace, notify_parent_view_did_change,
|
notify_child_views_changed, notify_did_update_workspace, notify_parent_view_did_change,
|
||||||
@ -113,7 +113,7 @@ impl FolderManager {
|
|||||||
},
|
},
|
||||||
|folder| {
|
|folder| {
|
||||||
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
|
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
|
||||||
let views = get_workspace_view_pbs(&workspace.id, folder);
|
let views = get_workspace_public_view_pbs(&workspace.id, folder);
|
||||||
let workspace: WorkspacePB = (workspace, views).into();
|
let workspace: WorkspacePB = (workspace, views).into();
|
||||||
Ok::<WorkspacePB, FlowyError>(workspace)
|
Ok::<WorkspacePB, FlowyError>(workspace)
|
||||||
};
|
};
|
||||||
@ -145,7 +145,15 @@ impl FolderManager {
|
|||||||
|
|
||||||
pub async fn get_workspace_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
|
pub async fn get_workspace_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
|
||||||
let views = self.with_folder(Vec::new, |folder| {
|
let views = self.with_folder(Vec::new, |folder| {
|
||||||
get_workspace_view_pbs(workspace_id, folder)
|
get_workspace_public_view_pbs(workspace_id, folder)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(views)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_workspace_private_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
|
||||||
|
let views = self.with_folder(Vec::new, |folder| {
|
||||||
|
get_workspace_private_view_pbs(workspace_id, folder)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(views)
|
Ok(views)
|
||||||
@ -452,11 +460,16 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let index = params.index;
|
let index = params.index;
|
||||||
|
let section = params.section.clone().unwrap_or(ViewSectionPB::Public);
|
||||||
|
let is_private = section == ViewSectionPB::Private;
|
||||||
let view = create_view(self.user.user_id()?, params, view_layout);
|
let view = create_view(self.user.user_id()?, params, view_layout);
|
||||||
self.with_folder(
|
self.with_folder(
|
||||||
|| (),
|
|| (),
|
||||||
|folder| {
|
|folder| {
|
||||||
folder.insert_view(view.clone(), index);
|
folder.insert_view(view.clone(), index);
|
||||||
|
if is_private {
|
||||||
|
folder.add_private_view_ids(vec![view.id.clone()]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -609,18 +622,26 @@ impl FolderManager {
|
|||||||
/// * `prev_view_id` - An `Option<String>` that holds the id of the view after which the `view_id` should be positioned.
|
/// * `prev_view_id` - An `Option<String>` that holds the id of the view after which the `view_id` should be positioned.
|
||||||
///
|
///
|
||||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||||
pub async fn move_nested_view(
|
pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> {
|
||||||
&self,
|
let view_id = params.view_id;
|
||||||
view_id: String,
|
let new_parent_id = params.new_parent_id;
|
||||||
new_parent_id: String,
|
let prev_view_id = params.prev_view_id;
|
||||||
prev_view_id: Option<String>,
|
let from_section = params.from_section;
|
||||||
) -> FlowyResult<()> {
|
let to_section = params.to_section;
|
||||||
let view = self.get_view_pb(&view_id).await?;
|
let view = self.get_view_pb(&view_id).await?;
|
||||||
let old_parent_id = view.parent_view_id;
|
let old_parent_id = view.parent_view_id;
|
||||||
self.with_folder(
|
self.with_folder(
|
||||||
|| (),
|
|| (),
|
||||||
|folder| {
|
|folder| {
|
||||||
folder.move_nested_view(&view_id, &new_parent_id, prev_view_id);
|
folder.move_nested_view(&view_id, &new_parent_id, prev_view_id);
|
||||||
|
|
||||||
|
if from_section != to_section {
|
||||||
|
if to_section == Some(ViewSectionPB::Private) {
|
||||||
|
folder.add_private_view_ids(vec![view_id.clone()]);
|
||||||
|
} else {
|
||||||
|
folder.delete_private_view_ids(vec![view_id.clone()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
notify_parent_view_did_change(
|
notify_parent_view_did_change(
|
||||||
@ -743,6 +764,8 @@ impl FolderManager {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index,
|
index,
|
||||||
|
// TODO: lucas.xu fetch the section from the view
|
||||||
|
section: Some(ViewSectionPB::Public),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.create_view_with_params(duplicate_params).await?;
|
self.create_view_with_params(duplicate_params).await?;
|
||||||
@ -954,6 +977,8 @@ impl FolderManager {
|
|||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
set_as_current: false,
|
set_as_current: false,
|
||||||
index: None,
|
index: None,
|
||||||
|
// TODO: Lucas.xu fetch the section from the view
|
||||||
|
section: Some(ViewSectionPB::Public),
|
||||||
};
|
};
|
||||||
|
|
||||||
let view = create_view(self.user.user_id()?, params, import_data.view_layout);
|
let view = create_view(self.user.user_id()?, params, import_data.view_layout);
|
||||||
@ -1110,16 +1135,61 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the views that belong to the workspace. The views are filtered by the trash.
|
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
|
||||||
pub(crate) fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
pub(crate) fn get_workspace_public_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
||||||
let items = folder.get_all_trash();
|
// get the trash ids
|
||||||
let trash_ids = items
|
let trash_ids = folder
|
||||||
|
.get_all_trash()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|trash| trash.id)
|
.map(|trash| trash.id)
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// get the private view ids
|
||||||
|
let private_view_ids = folder
|
||||||
|
.get_all_private_views()
|
||||||
|
.into_iter()
|
||||||
|
.map(|view| view.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let mut views = folder.get_workspace_views();
|
let mut views = folder.get_workspace_views();
|
||||||
views.retain(|view| !trash_ids.contains(&view.id));
|
|
||||||
|
// filter the views that are in the trash and all the private views
|
||||||
|
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
|
||||||
|
|
||||||
|
views
|
||||||
|
.into_iter()
|
||||||
|
.map(|view| {
|
||||||
|
// Get child views
|
||||||
|
let child_views = folder
|
||||||
|
.views
|
||||||
|
.get_views_belong_to(&view.id)
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
view_pb_with_child_views(view, child_views)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current private views of the user.
|
||||||
|
pub(crate) fn get_workspace_private_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
||||||
|
// get the trash ids
|
||||||
|
let trash_ids = folder
|
||||||
|
.get_all_trash()
|
||||||
|
.into_iter()
|
||||||
|
.map(|trash| trash.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// get the private view ids
|
||||||
|
let private_view_ids = folder
|
||||||
|
.get_my_private_views()
|
||||||
|
.into_iter()
|
||||||
|
.map(|view| view.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let mut views = folder.get_workspace_views();
|
||||||
|
|
||||||
|
// filter the views that are in the trash and not in the private view ids
|
||||||
|
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));
|
||||||
|
|
||||||
views
|
views
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -14,9 +14,9 @@ use lib_dispatch::prelude::af_spawn;
|
|||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
|
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
|
||||||
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, ViewPB,
|
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
|
||||||
};
|
};
|
||||||
use crate::manager::{get_workspace_view_pbs, MutexFolder};
|
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, MutexFolder};
|
||||||
use crate::notification::{send_notification, FolderNotification};
|
use crate::notification::{send_notification, FolderNotification};
|
||||||
|
|
||||||
/// Listen on the [ViewChange] after create/delete/update events happened
|
/// Listen on the [ViewChange] after create/delete/update events happened
|
||||||
@ -161,7 +161,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
|||||||
// if the view's parent id equal to workspace id. Then it will fetch the current
|
// if the view's parent id equal to workspace id. Then it will fetch the current
|
||||||
// workspace views. Because the the workspace is not a view stored in the views map.
|
// workspace views. Because the the workspace is not a view stored in the views map.
|
||||||
if parent_view_id == workspace_id {
|
if parent_view_id == workspace_id {
|
||||||
notify_did_update_workspace(&workspace_id, folder)
|
notify_did_update_workspace(&workspace_id, folder);
|
||||||
|
notify_did_update_section_views(&workspace_id, folder);
|
||||||
} else {
|
} else {
|
||||||
// Parent view can contain a list of child views. Currently, only get the first level
|
// Parent view can contain a list of child views. Currently, only get the first level
|
||||||
// child views.
|
// child views.
|
||||||
@ -181,8 +182,35 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
|
||||||
|
let public_views = get_workspace_public_view_pbs(workspace_id, folder);
|
||||||
|
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
|
||||||
|
tracing::trace!(
|
||||||
|
"Did update section views: public len = {}, private len = {}",
|
||||||
|
public_views.len(),
|
||||||
|
private_views.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(Lucas.xu) - Only notify the section changed, not the public/private both.
|
||||||
|
// Notify the public views
|
||||||
|
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||||
|
.payload(SectionViewsPB {
|
||||||
|
section: ViewSectionPB::Public,
|
||||||
|
views: public_views,
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
|
||||||
|
// Notify the private views
|
||||||
|
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||||
|
.payload(SectionViewsPB {
|
||||||
|
section: ViewSectionPB::Private,
|
||||||
|
views: private_views,
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
||||||
let repeated_view: RepeatedViewPB = get_workspace_view_pbs(workspace_id, folder).into();
|
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
|
||||||
tracing::trace!("Did update workspace views: {:?}", repeated_view);
|
tracing::trace!("Did update workspace views: {:?}", repeated_view);
|
||||||
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
||||||
.payload(repeated_view)
|
.payload(repeated_view)
|
||||||
|
@ -35,6 +35,9 @@ pub enum FolderNotification {
|
|||||||
DidUnfavoriteView = 37,
|
DidUnfavoriteView = 37,
|
||||||
|
|
||||||
DidUpdateRecentViews = 38,
|
DidUpdateRecentViews = 38,
|
||||||
|
|
||||||
|
/// Trigger when the ROOT views (the first level) in section are updated
|
||||||
|
DidUpdateSectionViews = 39,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<FolderNotification> for i32 {
|
impl std::convert::From<FolderNotification> for i32 {
|
||||||
@ -60,6 +63,8 @@ impl std::convert::From<i32> for FolderNotification {
|
|||||||
17 => FolderNotification::DidUpdateFolderSyncUpdate,
|
17 => FolderNotification::DidUpdateFolderSyncUpdate,
|
||||||
36 => FolderNotification::DidFavoriteView,
|
36 => FolderNotification::DidFavoriteView,
|
||||||
37 => FolderNotification::DidUnfavoriteView,
|
37 => FolderNotification::DidUnfavoriteView,
|
||||||
|
38 => FolderNotification::DidUpdateRecentViews,
|
||||||
|
39 => FolderNotification::DidUpdateSectionViews,
|
||||||
_ => FolderNotification::Unknown,
|
_ => FolderNotification::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use flowy_folder_pub::cloud::gen_view_id;
|
use flowy_folder_pub::cloud::gen_view_id;
|
||||||
|
|
||||||
use crate::entities::{CreateViewParams, ViewLayoutPB};
|
use crate::entities::{CreateViewParams, ViewLayoutPB, ViewSectionPB};
|
||||||
use crate::manager::FolderManager;
|
use crate::manager::FolderManager;
|
||||||
|
|
||||||
#[cfg(feature = "test_helper")]
|
#[cfg(feature = "test_helper")]
|
||||||
@ -47,6 +47,7 @@ impl FolderManager {
|
|||||||
meta: ext,
|
meta: ext,
|
||||||
set_as_current: true,
|
set_as_current: true,
|
||||||
index: None,
|
index: None,
|
||||||
|
section: Some(ViewSectionPB::Public),
|
||||||
};
|
};
|
||||||
self.create_view_with_params(params).await.unwrap();
|
self.create_view_with_params(params).await.unwrap();
|
||||||
view_id
|
view_id
|
||||||
|
@ -54,6 +54,7 @@ impl DefaultFolderBuilder {
|
|||||||
favorites: Default::default(),
|
favorites: Default::default(),
|
||||||
recent: Default::default(),
|
recent: Default::default(),
|
||||||
trash: Default::default(),
|
trash: Default::default(),
|
||||||
|
private: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user