Merge branch 'main' into workspace-rename-no-icon

This commit is contained in:
Zack Fu Zi Xiang 2024-03-04 15:19:04 +08:00
commit 76fcba5966
No known key found for this signature in database
64 changed files with 1279 additions and 432 deletions

View File

@ -12,7 +12,7 @@
"program": "./lib/main.dart", "program": "./lib/main.dart",
"type": "dart", "type": "dart",
"env": { "env": {
"RUST_LOG": "trace", "RUST_LOG": "debug",
"RUST_BACKTRACE": "1" "RUST_BACKTRACE": "1"
}, },
// uncomment the following line to testing performance. // uncomment the following line to testing performance.

View File

@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
CARGO_MAKE_CRATE_NAME = "dart-ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi"
LIB_NAME = "dart_ffi" LIB_NAME = "dart_ffi"
APPFLOWY_VERSION = "0.5.0" APPFLOWY_VERSION = "0.5.1"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite" FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
PRODUCT_NAME = "AppFlowy" PRODUCT_NAME = "AppFlowy"
MACOSX_DEPLOYMENT_TARGET = "11.0" MACOSX_DEPLOYMENT_TARGET = "11.0"

View File

@ -40,19 +40,19 @@ void main() {
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
final userAvatarFinder = find.descendant( // final userAvatarFinder = find.descendant(
of: find.byType(SettingsUserView), // of: find.byType(SettingsUserView),
matching: find.byType(UserAvatar), // matching: find.byType(UserAvatar),
); // );
// Open icon picker dialog and select emoji // Open icon picker dialog and select emoji
await tester.tap(userAvatarFinder); // await tester.tap(userAvatarFinder);
await tester.pumpAndSettle(); // await tester.pumpAndSettle();
await tester.tapEmoji('😁'); // await tester.tapEmoji('😁');
await tester.pumpAndSettle(); // await tester.pumpAndSettle();
final UserAvatar userAvatar = // final UserAvatar userAvatar =
tester.widget(userAvatarFinder) as UserAvatar; // tester.widget(userAvatarFinder) as UserAvatar;
expect(userAvatar.iconUrl, '😁'); // expect(userAvatar.iconUrl, '😁');
// enter user name // enter user name
final userNameFinder = find.descendant( final userNameFinder = find.descendant(
@ -81,12 +81,12 @@ void main() {
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
// verify icon // verify icon
final userAvatarFinder = find.descendant( // final userAvatarFinder = find.descendant(
of: find.byType(SettingsUserView), // of: find.byType(SettingsUserView),
matching: find.byType(UserAvatar), // matching: find.byType(UserAvatar),
); // );
final UserAvatar userAvatar = tester.widget(userAvatarFinder) as UserAvatar; // final UserAvatar userAvatar = tester.widget(userAvatarFinder) as UserAvatar;
expect(userAvatar.iconUrl, '😁'); // expect(userAvatar.iconUrl, '😁');
// verify name // verify name
final userNameFinder = find.descendant( final userNameFinder = find.descendant(

View File

@ -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/settings/prelude.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_user.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart'; import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';

View File

@ -1,13 +1,15 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/share_log_files.dart'; import 'package:appflowy/util/share_log_files.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:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'widgets/widgets.dart'; import 'widgets/widgets.dart';
@ -51,6 +53,31 @@ class SupportSettingGroup extends StatelessWidget {
); );
}, },
), ),
MobileSettingItem(
name: LocaleKeys.settings_files_clearCache.tr(),
trailing: const Icon(
Icons.chevron_right,
),
onTap: () async {
await showFlowyMobileConfirmDialog(
context,
title: FlowyText(
LocaleKeys.settings_files_areYouSureToClearCache.tr(),
maxLines: 2,
),
content: FlowyText(
LocaleKeys.settings_files_clearCacheDesc.tr(),
fontSize: 12,
maxLines: 4,
),
actionButtonTitle: LocaleKeys.button_yes.tr(),
actionButtonColor: Theme.of(context).colorScheme.error,
onActionButtonPressed: () async {
await getIt<FlowyCacheManager>().clearAllCache();
},
);
},
),
], ],
), ),
); );

View File

@ -1,9 +1,7 @@
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart'; import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.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/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
@ -13,6 +11,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'tab_bar_add_button.dart'; import 'tab_bar_add_button.dart';
@ -235,7 +234,7 @@ class TabBarItemButton extends StatelessWidget {
NavigatorTextFieldDialog( NavigatorTextFieldDialog(
title: LocaleKeys.menuAppHeader_renameDialog.tr(), title: LocaleKeys.menuAppHeader_renameDialog.tr(),
value: view.name, value: view.name,
confirm: (newValue) { onConfirm: (newValue, _) {
context.read<DatabaseTabBarBloc>().add( context.read<DatabaseTabBarBloc>().add(
DatabaseTabBarEvent.renameView(view.id, newValue), DatabaseTabBarEvent.renameView(view.id, newValue),
); );

View File

@ -360,9 +360,7 @@ class DepthOptionAction extends PopoverActionCell {
(e) => HoverButton( (e) => HoverButton(
onTap: () => onTap(e.inner), onTap: () => onTap(e.inner),
itemHeight: ActionListSizes.itemHeight, itemHeight: ActionListSizes.itemHeight,
leftIcon: null,
name: e.name, name: e.name,
rightIcon: null,
), ),
) )
.toList(); .toList();

View File

@ -59,9 +59,7 @@ class _ResizableImageState extends State<ResizableImage> {
imageWidth = widget.width; imageWidth = widget.width;
if (widget.type == CustomImageType.internal) { _userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
_userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
}
} }
@override @override

View File

@ -0,0 +1,25 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
extension AFRolePBExtension on AFRolePB {
bool get isOwner => this == AFRolePB.Owner;
bool get canInvite => isOwner;
bool get canDelete => isOwner;
bool get canUpdate => isOwner;
String get description {
switch (this) {
case AFRolePB.Owner:
return LocaleKeys.settings_appearance_members_owner.tr();
case AFRolePB.Member:
return LocaleKeys.settings_appearance_members_member.tr();
case AFRolePB.Guest:
return LocaleKeys.settings_appearance_members_guest.tr();
}
throw UnimplementedError('Unknown role: $this');
}
}

View File

@ -0,0 +1,61 @@
import 'package:appflowy_backend/log.dart';
import 'package:path_provider/path_provider.dart';
class FlowyCacheManager {
final _caches = <ICache>[];
// if you add a new cache, you should register it here.
void registerCache(ICache cache) {
_caches.add(cache);
}
void unregisterAllCache(ICache cache) {
_caches.clear();
}
Future<void> clearAllCache() async {
try {
for (final cache in _caches) {
await cache.clearAll();
}
Log.info('Cache cleared');
} catch (e) {
Log.error(e);
}
}
Future<int> getCacheSize() async {
try {
int tmpDirSize = 0;
for (final cache in _caches) {
tmpDirSize += await cache.cacheSize();
}
Log.info('Cache size: $tmpDirSize');
return tmpDirSize;
} catch (e) {
Log.error(e);
return 0;
}
}
}
abstract class ICache {
Future<int> cacheSize();
Future<void> clearAll();
}
class TemporaryDirectoryCache implements ICache {
@override
Future<int> cacheSize() async {
final tmpDir = await getTemporaryDirectory();
final tmpDirStat = await tmpDir.stat();
return tmpDirStat.size;
}
@override
Future<void> clearAll() async {
final tmpDir = await getTemporaryDirectory();
await tmpDir.delete(recursive: true);
}
}

View File

@ -39,8 +39,10 @@ class FlowyNetworkImage extends StatelessWidget {
assert(userProfilePB != null && userProfilePB!.token.isNotEmpty); assert(userProfilePB != null && userProfilePB!.token.isNotEmpty);
} }
final manager = CustomImageCacheManager();
return CachedNetworkImage( return CachedNetworkImage(
cacheManager: CustomImageCacheManager(), cacheManager: manager,
httpHeaders: _header(), httpHeaders: _header(),
imageUrl: url, imageUrl: url,
fit: fit, fit: fit,
@ -50,6 +52,12 @@ class FlowyNetworkImage extends StatelessWidget {
errorWidget: (context, url, error) => errorWidget: (context, url, error) =>
errorWidgetBuilder?.call(context, url, error) ?? errorWidgetBuilder?.call(context, url, error) ??
const SizedBox.shrink(), const SizedBox.shrink(),
errorListener: (value) {
// try to clear the image cache.
manager.removeFile(url);
Log.error(value.toString());
},
); );
} }

View File

@ -1,6 +1,9 @@
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
class CustomImageCacheManager extends CacheManager with ImageCacheManager { class CustomImageCacheManager extends CacheManager
with ImageCacheManager
implements ICache {
CustomImageCacheManager._() : super(Config(key)); CustomImageCacheManager._() : super(Config(key));
factory CustomImageCacheManager() => _instance; factory CustomImageCacheManager() => _instance;
@ -8,4 +11,16 @@ class CustomImageCacheManager extends CacheManager with ImageCacheManager {
static final CustomImageCacheManager _instance = CustomImageCacheManager._(); static final CustomImageCacheManager _instance = CustomImageCacheManager._();
static const key = 'appflowy_image_cache'; static const key = 'appflowy_image_cache';
@override
Future<int> cacheSize() async {
// https://github.com/Baseflow/flutter_cache_manager/issues/239#issuecomment-719475429
// this package does not provide a way to get the cache size
return 0;
}
@override
Future<void> clearAll() async {
await emptyCache();
}
} }

View File

@ -6,6 +6,8 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/stability_ai/stability_ai_client.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/stability_ai/stability_ai_client.dart';
import 'package:appflowy/plugins/trash/application/prelude.dart'; import 'package:appflowy/plugins/trash/application/prelude.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/shared/custom_image_cache_manager.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart'; import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart';
import 'package:appflowy/user/application/auth/af_cloud_auth_service.dart'; import 'package:appflowy/user/application/auth/af_cloud_auth_service.dart';
@ -128,6 +130,12 @@ void _resolveCommonService(
getIt.registerFactory<BaseAppearance>( getIt.registerFactory<BaseAppearance>(
() => PlatformExtension.isMobile ? MobileAppearance() : DesktopAppearance(), () => PlatformExtension.isMobile ? MobileAppearance() : DesktopAppearance(),
); );
getIt.registerFactory<FlowyCacheManager>(
() => FlowyCacheManager()
..registerCache(TemporaryDirectoryCache())
..registerCache(CustomImageCacheManager()),
);
} }
void _resolveUserDeps(GetIt getIt, IntegrationMode mode) { void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {

View File

@ -5,7 +5,6 @@ import 'package:appflowy/env/backend_env.dart';
import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/user/application/auth/device_id.dart'; import 'package:appflowy/user/application/auth/device_id.dart';
import 'package:appflowy_backend/appflowy_backend.dart'; import 'package:appflowy_backend/appflowy_backend.dart';
import 'package:appflowy_backend/log.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
@ -39,7 +38,6 @@ class InitRustSDKTask extends LaunchTask {
rustEnvs: context.config.rustEnvs, rustEnvs: context.config.rustEnvs,
); );
await context.getIt<FlowySDK>().init(jsonEncode(env.toJson())); await context.getIt<FlowySDK>().init(jsonEncode(env.toJson()));
Log.info('Rust SDK initialized');
} }
@override @override

View File

@ -79,15 +79,13 @@ class UserBackendService {
return UserEventOpenAnonUser().send(); return UserEventOpenAnonUser().send();
} }
Future<FlowyResult<List<WorkspacePB>, FlowyError>> getWorkspaces() { Future<FlowyResult<List<UserWorkspacePB>, FlowyError>> getWorkspaces() {
// final request = WorkspaceIdPB.create(); return UserEventGetAllWorkspace().send().then((value) {
// return FolderEventReadAllWorkspaces(request).send().then((result) { return value.fold(
// return result.fold( (workspaces) => FlowyResult.success(workspaces.items),
// (workspaces) => FlowyResult.success(workspaces.items), (error) => FlowyResult.failure(error),
// (error) => FlowyResult.failure(error), );
// ); });
// });
return Future.value(FlowyResult.success([]));
} }
Future<FlowyResult<void, FlowyError>> openWorkspace(String workspaceId) { Future<FlowyResult<void, FlowyError>> openWorkspace(String workspaceId) {
@ -118,4 +116,18 @@ class UserBackendService {
); );
}); });
} }
Future<FlowyResult<UserWorkspacePB, FlowyError>> createUserWorkspace(
String name,
) {
final request = CreateWorkspacePB.create()..name = name;
return UserEventCreateWorkspace(request).send();
}
Future<FlowyResult<void, FlowyError>> deleteWorkspaceById(
String workspaceId,
) {
final request = UserWorkspaceIdPB.create()..workspaceId = workspaceId;
return UserEventDeleteWorkspace(request).send();
}
} }

View File

@ -1,11 +1,10 @@
import 'dart:ui'; import 'package:flutter/material.dart';
class ColorGenerator { class ColorGenerator {
Color generateColorFromString(String string) { static Color generateColorFromString(String string) {
final hash = string.hashCode; final int hash =
final int r = (hash & 0xFF0000) >> 16; string.codeUnits.fold(0, (int acc, int unit) => acc + unit);
final int g = (hash & 0x00FF00) >> 8; final double hue = (hash % 360).toDouble();
final int b = hash & 0x0000FF; return HSLColor.fromAHSL(1.0, hue, 0.5, 0.8).toColor();
return Color.fromRGBO(r, g, b, 0.5);
} }
} }

View File

@ -1 +1,2 @@
export 'settings_user_bloc.dart'; export 'settings_user_bloc.dart';
export 'user_workspace_bloc.dart';

View File

@ -0,0 +1,134 @@
import 'package:appflowy/user/application/user_service.dart';
import 'package:appflowy_backend/log.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_result/appflowy_result.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_workspace_bloc.freezed.dart';
class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
UserWorkspaceBloc({
required this.userProfile,
}) : _userService = UserBackendService(userId: userProfile.id),
super(UserWorkspaceState.initial()) {
on<UserWorkspaceEvent>(
(event, emit) async {
await event.when(
initial: () async {
// do nothing
},
workspacesReceived: (workspaceId) async {},
fetchWorkspaces: () async {
final result = await _fetchWorkspaces();
if (result != null) {
emit(
state.copyWith(
currentWorkspace: result.$1,
workspaces: result.$2,
),
);
}
},
createWorkspace: (name, desc) async {
final result = await _userService.createUserWorkspace(name);
emit(
state.copyWith(
openWorkspaceResult: null,
deleteWorkspaceResult: null,
createWorkspaceResult:
result.fold((s) => FlowyResult.success(null), (e) {
Log.error(e);
return FlowyResult.failure(e);
}),
),
);
},
deleteWorkspace: (workspaceId) async {
final result = await _userService.deleteWorkspaceById(workspaceId);
emit(
state.copyWith(
openWorkspaceResult: null,
createWorkspaceResult: null,
deleteWorkspaceResult:
result.fold((s) => FlowyResult.success(null), (e) {
Log.error(e);
return FlowyResult.failure(e);
}),
),
);
},
openWorkspace: (workspaceId) async {
final result = await _userService.openWorkspace(workspaceId);
emit(
state.copyWith(
createWorkspaceResult: null,
deleteWorkspaceResult: null,
openWorkspaceResult:
result.fold((s) => FlowyResult.success(null), (e) {
Log.error(e);
return FlowyResult.failure(e);
}),
),
);
},
);
},
);
}
final UserProfilePB userProfile;
final UserBackendService _userService;
Future<(UserWorkspacePB currentWorkspace, List<UserWorkspacePB> workspaces)?>
_fetchWorkspaces() async {
final result = await _userService.getCurrentWorkspace();
return result.fold((currentWorkspace) async {
final result = await _userService.getWorkspaces();
return result.fold((workspaces) {
return (
workspaces.firstWhere(
(e) => e.workspaceId == currentWorkspace.id,
),
workspaces
);
}, (e) {
Log.error(e);
return null;
});
}, (e) {
Log.error(e);
return null;
});
}
}
@freezed
class UserWorkspaceEvent with _$UserWorkspaceEvent {
const factory UserWorkspaceEvent.initial() = Initial;
const factory UserWorkspaceEvent.createWorkspace(String name, String desc) =
CreateWorkspace;
const factory UserWorkspaceEvent.fetchWorkspaces() = FetchWorkspaces;
const factory UserWorkspaceEvent.deleteWorkspace(String workspaceId) =
DeleteWorkspace;
const factory UserWorkspaceEvent.openWorkspace(String workspaceId) =
OpenWorkspace;
const factory UserWorkspaceEvent.workspacesReceived(
FlowyResult<List<UserWorkspacePB>, FlowyError> workspacesOrFail,
) = WorkspacesReceived;
}
@freezed
class UserWorkspaceState with _$UserWorkspaceState {
const factory UserWorkspaceState({
required UserWorkspacePB? currentWorkspace,
required List<UserWorkspacePB> workspaces,
@Default(null) FlowyResult<void, FlowyError>? createWorkspaceResult,
@Default(null) FlowyResult<void, FlowyError>? deleteWorkspaceResult,
@Default(null) FlowyResult<void, FlowyError>? openWorkspaceResult,
}) = _UserWorkspaceState;
factory UserWorkspaceState.initial() =>
const UserWorkspaceState(currentWorkspace: null, workspaces: []);
}

View File

@ -48,7 +48,7 @@ class WorkspaceBloc extends Bloc<WorkspaceEvent, WorkspaceState> {
emit( emit(
workspacesOrFailed.fold( workspacesOrFailed.fold(
(workspaces) => state.copyWith( (workspaces) => state.copyWith(
workspaces: workspaces, workspaces: [],
successOrFailure: FlowyResult.success(null), successOrFailure: FlowyResult.success(null),
), ),
(error) { (error) {

View File

@ -182,7 +182,7 @@ class DesktopHomeScreen extends StatelessWidget {
required WorkspaceSettingPB workspaceSetting, required WorkspaceSettingPB workspaceSetting,
}) { }) {
final homeMenu = HomeSideBar( final homeMenu = HomeSideBar(
user: userProfile, userProfile: userProfile,
workspaceSetting: workspaceSetting, workspaceSetting: workspaceSetting,
); );
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu)); return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));

View File

@ -1,13 +1,12 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.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/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/sidebar/rename_view/rename_view_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/rename_view/rename_view_bloc.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_user.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
import 'package:flutter/material.dart';
import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View File

@ -119,7 +119,7 @@ class _PersonalFolderHeaderState extends State<PersonalFolderHeader> {
createViewAndShowRenameDialogIfNeeded( createViewAndShowRenameDialogIfNeeded(
context, context,
LocaleKeys.newPageText.tr(), LocaleKeys.newPageText.tr(),
(viewName) { (viewName, _) {
if (viewName.isNotEmpty) { if (viewName.isNotEmpty) {
context.read<MenuBloc>().add( context.read<MenuBloc>().add(
MenuEvent.createApp( MenuEvent.createApp(

View File

@ -15,7 +15,7 @@ import 'package:flutter/material.dart';
Future<void> createViewAndShowRenameDialogIfNeeded( Future<void> createViewAndShowRenameDialogIfNeeded(
BuildContext context, BuildContext context,
String dialogTitle, String dialogTitle,
void Function(String viewName) createView, void Function(String viewName, BuildContext context) createView,
) async { ) async {
final value = await getIt<KeyValueStorage>().getWithFormat( final value = await getIt<KeyValueStorage>().getWithFormat(
KVKeys.showRenameDialogWhenCreatingNewFile, KVKeys.showRenameDialogWhenCreatingNewFile,
@ -27,9 +27,9 @@ Future<void> createViewAndShowRenameDialogIfNeeded(
title: dialogTitle, title: dialogTitle,
value: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), value: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
autoSelectAllText: true, autoSelectAllText: true,
confirm: createView, onConfirm: createView,
).show(context); ).show(context);
} else { } else if (context.mounted) {
createView(LocaleKeys.menuAppHeader_defaultNewPageName.tr()); createView(LocaleKeys.menuAppHeader_defaultNewPageName.tr(), context);
} }
} }

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.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/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/menu/menu_bloc.dart'; import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
@ -12,12 +11,14 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_new_pa
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_top_menu.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_top_menu.dart';
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_backend/protobuf/flowy-folder/view.pb.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/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';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
/// Home Sidebar is the left side bar of the home page. /// Home Sidebar is the left side bar of the home page.
@ -30,11 +31,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class HomeSideBar extends StatelessWidget { class HomeSideBar extends StatelessWidget {
const HomeSideBar({ const HomeSideBar({
super.key, super.key,
required this.user, required this.userProfile,
required this.workspaceSetting, required this.workspaceSetting,
}); });
final UserProfilePB user; final UserProfilePB userProfile;
final WorkspaceSettingPB workspaceSetting; final WorkspaceSettingPB workspaceSetting;
@ -47,7 +48,7 @@ class HomeSideBar extends StatelessWidget {
), ),
BlocProvider( BlocProvider(
create: (_) => MenuBloc( create: (_) => MenuBloc(
user: user, user: userProfile,
workspaceId: workspaceSetting.workspaceId, workspaceId: workspaceSetting.workspaceId,
)..add(const MenuEvent.initial()), )..add(const MenuEvent.initial()),
), ),
@ -103,11 +104,14 @@ class HomeSideBar extends StatelessWidget {
padding: menuHorizontalInset, padding: menuHorizontalInset,
child: SidebarTopMenu(), child: SidebarTopMenu(),
), ),
// user, setting // user or workspace, setting
Padding( Padding(
padding: menuHorizontalInset, padding: menuHorizontalInset,
child: SidebarUser(user: user, views: views), child: FeatureFlag.collaborativeWorkspace.isOn
? SidebarWorkspace(userProfile: userProfile, views: views)
: SidebarUser(userProfile: userProfile, views: views),
), ),
const VSpace(20), const VSpace(20),
// scrollable document list // scrollable document list
Expanded( Expanded(

View File

@ -23,7 +23,7 @@ class SidebarNewPageButton extends StatelessWidget {
onPressed: () async => createViewAndShowRenameDialogIfNeeded( onPressed: () async => createViewAndShowRenameDialogIfNeeded(
context, context,
LocaleKeys.newPageText.tr(), LocaleKeys.newPageText.tr(),
(viewName) { (viewName, _) {
if (viewName.isNotEmpty) { if (viewName.isNotEmpty) {
context.read<MenuBloc>().add(MenuEvent.createApp(viewName)); context.read<MenuBloc>().add(MenuEvent.createApp(viewName));
} }

View File

@ -0,0 +1,104 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/presentation/home/hotkeys.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
final GlobalKey _settingsDialogKey = GlobalKey();
Future<HotKeyItem?> openSettingsHotKey(BuildContext context) async {
final userProfileOrFailure = await getIt<AuthService>().getUser();
return userProfileOrFailure.fold(
(userProfile) => HotKeyItem(
hotKey: HotKey(
KeyCode.comma,
scope: HotKeyScope.inapp,
modifiers: [
PlatformExtension.isMacOS ? KeyModifier.meta : KeyModifier.control,
],
),
keyDownHandler: (_) {
if (_settingsDialogKey.currentContext == null) {
showSettingsDialog(context, userProfile);
} else {
Navigator.of(context, rootNavigator: true)
.popUntil((route) => route.isFirst);
}
},
),
(e) {
Log.error('Failed to get user $e');
return null;
},
);
}
class UserSettingButton extends StatelessWidget {
const UserSettingButton({required this.userProfile, super.key});
final UserProfilePB userProfile;
@override
Widget build(BuildContext context) {
return FlowyTooltip(
message: LocaleKeys.settings_menu_open.tr(),
child: IconButton(
onPressed: () => showSettingsDialog(context, userProfile),
icon: SizedBox.square(
dimension: 20,
child: FlowySvg(
FlowySvgs.settings_m,
color: Theme.of(context).colorScheme.tertiary,
),
),
),
);
}
}
void showSettingsDialog(
BuildContext context,
UserProfilePB userProfile,
) {
showDialog(
context: context,
builder: (dialogContext) {
return BlocProvider<DocumentAppearanceCubit>.value(
key: _settingsDialogKey,
value: BlocProvider.of<DocumentAppearanceCubit>(dialogContext),
child: SettingsDialog(
userProfile,
didLogout: () async {
// Pop the dialog using the dialog context
Navigator.of(dialogContext).pop();
await runAppFlowy();
},
dismissDialog: () {
if (Navigator.of(dialogContext).canPop()) {
Navigator.of(dialogContext).pop();
} else {
Log.warn("Can't pop dialog context");
}
},
restartApp: () async {
// Pop the dialog using the dialog context
Navigator.of(dialogContext).pop();
await runAppFlowy();
},
),
);
},
);
}

View File

@ -1,70 +1,32 @@
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/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart'; import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart';
import 'package:appflowy/workspace/presentation/home/hotkeys.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/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart'; import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.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:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.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:hotkey_manager/hotkey_manager.dart';
final GlobalKey _settingsDialogKey = GlobalKey();
Future<HotKeyItem?> openSettingsHotKey(BuildContext context) async {
final userProfileOrFailure = await getIt<AuthService>().getUser();
return userProfileOrFailure.fold(
(userProfile) => HotKeyItem(
hotKey: HotKey(
KeyCode.comma,
scope: HotKeyScope.inapp,
modifiers: [
PlatformExtension.isMacOS ? KeyModifier.meta : KeyModifier.control,
],
),
keyDownHandler: (_) {
if (_settingsDialogKey.currentContext == null) {
_showSettingsDialog(context, userProfile);
} else {
Navigator.of(context, rootNavigator: true)
.popUntil((route) => route.isFirst);
}
},
),
(e) {
Log.error('Failed to get user $e');
return null;
},
);
}
// keep this widget in case we need to roll back (lucas.xu)
class SidebarUser extends StatelessWidget { class SidebarUser extends StatelessWidget {
const SidebarUser({ const SidebarUser({
super.key, super.key,
required this.user, required this.userProfile,
required this.views, required this.views,
}); });
final UserProfilePB user; final UserProfilePB userProfile;
final List<ViewPB> views; final List<ViewPB> views;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider<MenuUserBloc>( return BlocProvider<MenuUserBloc>(
create: (context) => MenuUserBloc(user) create: (context) => MenuUserBloc(userProfile)
..add( ..add(
const MenuUserEvent.initial(), const MenuUserEvent.initial(),
), ),
@ -106,61 +68,3 @@ class SidebarUser extends StatelessWidget {
return name; return name;
} }
} }
class UserSettingButton extends StatelessWidget {
const UserSettingButton({required this.userProfile, super.key});
final UserProfilePB userProfile;
@override
Widget build(BuildContext context) {
return FlowyTooltip(
message: LocaleKeys.settings_menu_open.tr(),
child: IconButton(
onPressed: () => _showSettingsDialog(context, userProfile),
icon: SizedBox.square(
dimension: 20,
child: FlowySvg(
FlowySvgs.settings_m,
color: Theme.of(context).colorScheme.tertiary,
),
),
),
);
}
}
void _showSettingsDialog(
BuildContext context,
UserProfilePB userProfile,
) {
showDialog(
context: context,
builder: (dialogContext) {
return BlocProvider<DocumentAppearanceCubit>.value(
key: _settingsDialogKey,
value: BlocProvider.of<DocumentAppearanceCubit>(dialogContext),
child: SettingsDialog(
userProfile,
didLogout: () async {
// Pop the dialog using the dialog context
Navigator.of(dialogContext).pop();
await runAppFlowy();
},
dismissDialog: () {
if (Navigator.of(dialogContext).canPop()) {
Navigator.of(dialogContext).pop();
} else {
Log.warn("Can't pop dialog context");
}
},
restartApp: () async {
// Pop the dialog using the dialog context
Navigator.of(dialogContext).pop();
await runAppFlowy();
},
),
);
},
);
}

View File

@ -0,0 +1,183 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_item_list.dart';
import 'package:appflowy/workspace/presentation/home/toast.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_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SidebarWorkspace extends StatelessWidget {
const SidebarWorkspace({
super.key,
required this.userProfile,
required this.views,
});
final UserProfilePB userProfile;
final List<ViewPB> views;
@override
Widget build(BuildContext context) {
return BlocProvider<UserWorkspaceBloc>(
create: (_) => UserWorkspaceBloc(userProfile: userProfile)
..add(const UserWorkspaceEvent.fetchWorkspaces()),
child: BlocConsumer<UserWorkspaceBloc, UserWorkspaceState>(
listener: _showResultDialog,
builder: (context, state) {
final currentWorkspace = state.currentWorkspace;
// todo: show something if there is no workspace
if (currentWorkspace == null) {
return const SizedBox.shrink();
}
return Row(
children: [
Expanded(
child: _WorkspaceWrapper(
userProfile: userProfile,
currentWorkspace: currentWorkspace,
),
),
UserSettingButton(userProfile: userProfile),
const HSpace(4),
NotificationButton(views: views),
],
);
},
),
);
}
void _showResultDialog(BuildContext context, UserWorkspaceState state) {
var result = state.createWorkspaceResult;
if (result != null) {
final message = result.fold(
(s) => LocaleKeys.workspace_createSuccess.tr(),
(e) => '${LocaleKeys.workspace_createFailed.tr()}: ${e.msg}',
);
return showSnackBarMessage(context, message);
}
result = state.deleteWorkspaceResult;
if (result != null) {
final message = result.fold(
(s) => LocaleKeys.workspace_deleteSuccess.tr(),
(e) => '${LocaleKeys.workspace_deleteFailed.tr()}: ${e.msg}',
);
showSnackBarMessage(context, message);
return;
}
result = state.openWorkspaceResult;
if (result != null) {
final message = result.fold(
(s) => LocaleKeys.workspace_openSuccess.tr(),
(e) => '${LocaleKeys.workspace_openFailed.tr()}: ${e.msg}',
);
showSnackBarMessage(context, message);
return;
}
}
}
class _WorkspaceWrapper extends StatefulWidget {
const _WorkspaceWrapper({
required this.userProfile,
required this.currentWorkspace,
});
final UserWorkspacePB currentWorkspace;
final UserProfilePB userProfile;
@override
State<_WorkspaceWrapper> createState() => _WorkspaceWrapperState();
}
class _WorkspaceWrapperState extends State<_WorkspaceWrapper> {
@override
Widget build(BuildContext context) {
if (PlatformExtension.isDesktopOrWeb) {
return _DesktopWorkspaceWrapper(
userProfile: widget.userProfile,
currentWorkspace: widget.currentWorkspace,
);
} else {
// TODO(Lucas) mobile workspace menu
return const Placeholder();
}
}
}
class _DesktopWorkspaceWrapper extends StatefulWidget {
const _DesktopWorkspaceWrapper({
required this.userProfile,
required this.currentWorkspace,
});
final UserWorkspacePB currentWorkspace;
final UserProfilePB userProfile;
@override
State<_DesktopWorkspaceWrapper> createState() =>
_DesktopWorkspaceWrapperState();
}
class _DesktopWorkspaceWrapperState extends State<_DesktopWorkspaceWrapper> {
final controller = PopoverController();
@override
Widget build(BuildContext context) {
return AppFlowyPopover(
direction: PopoverDirection.bottomWithCenterAligned,
offset: const Offset(0, 10),
constraints: const BoxConstraints(maxWidth: 260, maxHeight: 600),
popupBuilder: (_) {
return BlocProvider<UserWorkspaceBloc>.value(
value: context.read<UserWorkspaceBloc>(),
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
builder: (context, state) {
final currentWorkspace = state.currentWorkspace;
final workspaces = state.workspaces;
if (currentWorkspace == null || workspaces.isEmpty) {
return const SizedBox.shrink();
}
return WorkspacesMenu(
userProfile: widget.userProfile,
currentWorkspace: currentWorkspace,
workspaces: workspaces,
);
},
),
);
},
child: FlowyButton(
onTap: () => controller.show(),
margin: const EdgeInsets.symmetric(vertical: 8),
text: Row(
children: [
const HSpace(4.0),
SizedBox(
width: 24.0,
child: WorkspaceIcon(workspace: widget.currentWorkspace),
),
const HSpace(8),
FlowyText.medium(
widget.currentWorkspace.name,
overflow: TextOverflow.ellipsis,
),
const FlowySvg(FlowySvgs.drop_menu_show_m),
],
),
),
);
}
}

View File

@ -0,0 +1,97 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
enum WorkspaceMoreAction {
rename,
delete,
}
class WorkspaceMoreActionList extends StatelessWidget {
const WorkspaceMoreActionList({
super.key,
required this.workspace,
});
final UserWorkspacePB workspace;
@override
Widget build(BuildContext context) {
return PopoverActionList<_WorkspaceMoreActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: WorkspaceMoreAction.values
.map((e) => _WorkspaceMoreActionWrapper(e, workspace))
.toList(),
buildChild: (controller) {
return FlowyButton(
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
useIntrinsicWidth: true,
text: const FlowySvg(
FlowySvgs.three_dots_vertical_s,
),
onTap: () {
controller.show();
},
);
},
onSelected: (action, controller) {},
);
}
}
class _WorkspaceMoreActionWrapper extends CustomActionCell {
_WorkspaceMoreActionWrapper(this.inner, this.workspace);
final WorkspaceMoreAction inner;
final UserWorkspacePB workspace;
@override
Widget buildWithContext(BuildContext context) {
return FlowyButton(
text: FlowyText(
name,
color: inner == WorkspaceMoreAction.delete
? Theme.of(context).colorScheme.error
: null,
),
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
onTap: () async {
switch (inner) {
case WorkspaceMoreAction.delete:
await NavigatorAlertDialog(
title: LocaleKeys.workspace_deleteWorkspaceHintText.tr(),
confirm: () {
context.read<UserWorkspaceBloc>().add(
UserWorkspaceEvent.deleteWorkspace(workspace.workspaceId),
);
},
).show(context);
case WorkspaceMoreAction.rename:
// TODO(Lucas): integrate with the backend
}
if (context.mounted) {
PopoverContainer.of(context).closeAll();
}
},
);
}
String get name {
switch (inner) {
case WorkspaceMoreAction.delete:
return LocaleKeys.button_delete.tr();
case WorkspaceMoreAction.rename:
return LocaleKeys.button_rename.tr();
}
}
}

View File

@ -0,0 +1,30 @@
import 'package:appflowy/util/color_generator/color_generator.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class WorkspaceIcon extends StatelessWidget {
const WorkspaceIcon({
super.key,
required this.workspace,
});
final UserWorkspacePB workspace;
@override
Widget build(BuildContext context) {
// TODO(Lucas): support icon later
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: ColorGenerator.generateColorFromString(workspace.name),
borderRadius: BorderRadius.circular(4),
),
child: FlowyText(
workspace.name.isEmpty ? '' : workspace.name.substring(0, 1),
fontSize: 16,
color: Colors.black,
),
);
}
}

View File

@ -0,0 +1,195 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/af_role_pb_extension.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class WorkspacesMenu extends StatelessWidget {
const WorkspacesMenu({
super.key,
required this.userProfile,
required this.currentWorkspace,
required this.workspaces,
});
final UserProfilePB userProfile;
final UserWorkspacePB currentWorkspace;
final List<UserWorkspacePB> workspaces;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// user email
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
child: Row(
children: [
FlowyText.medium(
_getUserInfo(),
fontSize: 12.0,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).hintColor,
),
const Spacer(),
FlowyButton(
useIntrinsicWidth: true,
text: const FlowySvg(FlowySvgs.add_m),
onTap: () {
_showCreateWorkspaceDialog(context);
PopoverContainer.of(context).closeAll();
},
),
],
),
),
for (final workspace in workspaces) ...[
_WorkspaceMenuItem(
workspace: workspace,
userProfile: userProfile,
isSelected: workspace.workspaceId == currentWorkspace.workspaceId,
),
const VSpace(4.0),
],
],
);
}
String _getUserInfo() {
if (userProfile.email.isNotEmpty) {
return userProfile.email;
}
if (userProfile.name.isNotEmpty) {
return userProfile.name;
}
return LocaleKeys.defaultUsername.tr();
}
Future<void> _showCreateWorkspaceDialog(BuildContext context) async {
if (context.mounted) {
await NavigatorTextFieldDialog(
title: LocaleKeys.workspace_create.tr(),
value: '',
hintText: '',
autoSelectAllText: true,
onConfirm: (name, context) async {
final request = CreateWorkspacePB.create()..name = name;
final result = await UserEventCreateWorkspace(request).send();
final message = result.fold(
(s) => LocaleKeys.workspace_createSuccess.tr(),
(e) => '${LocaleKeys.workspace_createFailed.tr()}: ${e.msg}',
);
if (context.mounted) {
showSnackBarMessage(context, message);
}
},
).show(context);
}
}
}
class _WorkspaceMenuItem extends StatelessWidget {
const _WorkspaceMenuItem({
required this.workspace,
required this.userProfile,
required this.isSelected,
});
final UserProfilePB userProfile;
final UserWorkspacePB workspace;
final bool isSelected;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) =>
WorkspaceMemberBloc(userProfile: userProfile, workspace: workspace)
..add(const WorkspaceMemberEvent.initial())
..add(const WorkspaceMemberEvent.getWorkspaceMembers()),
child: BlocBuilder<WorkspaceMemberBloc, WorkspaceMemberState>(
builder: (context, state) {
final members = state.members;
// settings right icon inside the flowy button will
// cause the popover dismiss intermediately when click the right icon.
// so using the stack to put the right icon on the flowy button.
return Stack(
alignment: Alignment.center,
children: [
FlowyButton(
onTap: () {
if (!isSelected) {
context.read<UserWorkspaceBloc>().add(
UserWorkspaceEvent.openWorkspace(
workspace.workspaceId,
),
);
}
},
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
iconPadding: 10.0,
leftIconSize: const Size.square(32),
leftIcon: WorkspaceIcon(
workspace: workspace,
),
text: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlowyText.medium(
workspace.name,
fontSize: 14.0,
overflow: TextOverflow.ellipsis,
),
if (members.length > 1)
FlowyText(
'${members.length} ${LocaleKeys.settings_appearance_members_members.tr()}',
fontSize: 10.0,
color: Theme.of(context).hintColor,
),
],
),
),
Positioned(
right: 12.0,
child: Align(child: _buildRightIcon(context)),
),
],
);
},
),
);
}
Widget _buildRightIcon(BuildContext context) {
// only the owner can update or delete workspace.
// only show the more action button when the workspace is selected.
if (!isSelected ||
!context.read<WorkspaceMemberBloc>().state.myRole.isOwner) {
return const SizedBox.shrink();
}
return Row(
children: [
WorkspaceMoreActionList(workspace: workspace),
const FlowySvg(
FlowySvgs.blue_check_s,
blendMode: null,
),
],
);
}
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/base/emoji/emoji_text.dart'; import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
@ -25,6 +23,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
typedef ViewItemOnSelected = void Function(ViewPB); typedef ViewItemOnSelected = void Function(ViewPB);
@ -456,7 +455,7 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
createViewAndShowRenameDialogIfNeeded( createViewAndShowRenameDialogIfNeeded(
context, context,
_convertLayoutToHintText(pluginBuilder.layoutType!), _convertLayoutToHintText(pluginBuilder.layoutType!),
(viewName) { (viewName, _) {
if (viewName.isNotEmpty) { if (viewName.isNotEmpty) {
viewBloc.add( viewBloc.add(
ViewEvent.createView( ViewEvent.createView(
@ -499,7 +498,7 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
autoSelectAllText: true, autoSelectAllText: true,
value: widget.view.name, value: widget.view.name,
maxLength: 256, maxLength: 256,
confirm: (newValue) { onConfirm: (newValue, _) {
context.read<ViewBloc>().add(ViewEvent.rename(newValue)); context.read<ViewBloc>().add(ViewEvent.rename(newValue));
}, },
).show(context); ).show(context);

View File

@ -1,11 +1,11 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_exporter_widget.dart';
import 'package:flutter/material.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:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import '../../../../generated/locale_keys.g.dart'; import '../../../../../generated/locale_keys.g.dart';
class SettingsExportFileWidget extends StatefulWidget { class SettingsExportFileWidget extends StatefulWidget {
const SettingsExportFileWidget({ const SettingsExportFileWidget({

View File

@ -0,0 +1,80 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class SettingsFileCacheWidget extends StatelessWidget {
const SettingsFileCacheWidget({
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: FlowyText.medium(
LocaleKeys.settings_files_clearCache.tr(),
fontSize: 13,
overflow: TextOverflow.ellipsis,
),
),
const VSpace(8),
Opacity(
opacity: 0.6,
child: FlowyText(
LocaleKeys.settings_files_clearCacheDesc.tr(),
fontSize: 10,
maxLines: 3,
),
),
],
),
),
const _ClearCacheButton(),
],
);
}
}
class _ClearCacheButton extends StatelessWidget {
const _ClearCacheButton();
@override
Widget build(BuildContext context) {
return FlowyIconButton(
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
tooltipText: LocaleKeys.settings_files_clearCache.tr(),
icon: FlowySvg(
FlowySvgs.delete_s,
size: const Size.square(18),
color: Theme.of(context).iconTheme.color,
),
onPressed: () {
NavigatorAlertDialog(
title: LocaleKeys.settings_files_areYouSureToClearCache.tr(),
confirm: () async {
await getIt<FlowyCacheManager>().clearAllCache();
if (context.mounted) {
showSnackBarMessage(
context,
LocaleKeys.settings_files_clearCacheSuccess.tr(),
);
}
},
).show(context);
},
);
}
}

View File

@ -1,8 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart'; import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
@ -12,12 +9,14 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import '../../../../generated/locale_keys.g.dart'; import '../../../../../generated/locale_keys.g.dart';
import '../../../../startup/startup.dart'; import '../../../../../startup/startup.dart';
import '../../../../startup/tasks/prelude.dart'; import '../../../../../startup/tasks/prelude.dart';
class SettingsFileLocationCustomizer extends StatefulWidget { class SettingsFileLocationCustomizer extends StatefulWidget {
const SettingsFileLocationCustomizer({ const SettingsFileLocationCustomizer({
@ -262,7 +261,7 @@ class _RecoverDefaultStorageButtonState
tooltipText: LocaleKeys.settings_files_recoverLocationTooltips.tr(), tooltipText: LocaleKeys.settings_files_recoverLocationTooltips.tr(),
icon: const FlowySvg( icon: const FlowySvg(
FlowySvgs.restore_s, FlowySvgs.restore_s,
size: Size.square(24), size: Size.square(20),
), ),
onPressed: () async { onPressed: () async {
// reset to the default directory and reload app // reset to the default directory and reload app

View File

@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import '../../../../generated/locale_keys.g.dart'; import '../../../../../generated/locale_keys.g.dart';
class FileExporterWidget extends StatefulWidget { class FileExporterWidget extends StatefulWidget {
const FileExporterWidget({super.key}); const FileExporterWidget({super.key});

View File

@ -20,10 +20,26 @@ class WorkspaceMemberBloc
extends Bloc<WorkspaceMemberEvent, WorkspaceMemberState> { extends Bloc<WorkspaceMemberEvent, WorkspaceMemberState> {
WorkspaceMemberBloc({ WorkspaceMemberBloc({
required this.userProfile, required this.userProfile,
this.workspace,
}) : super(WorkspaceMemberState.initial()) { }) : super(WorkspaceMemberState.initial()) {
on<WorkspaceMemberEvent>((event, emit) async { on<WorkspaceMemberEvent>((event, emit) async {
await event.map( await event.when(
getWorkspaceMembers: (_) async { initial: () async {
if (workspace != null) {
workspaceId = workspace!.workspaceId;
} else {
final currentWorkspace =
await FolderEventReadCurrentWorkspace().send();
currentWorkspace.fold((s) {
workspaceId = s.id;
}, (e) {
assert(false, 'Failed to read current workspace: $e');
Log.error('Failed to read current workspace: $e');
workspaceId = '';
});
}
},
getWorkspaceMembers: () async {
final members = await _getWorkspaceMembers(); final members = await _getWorkspaceMembers();
final myRole = _getMyRole(members); final myRole = _getMyRole(members);
emit( emit(
@ -33,16 +49,16 @@ class WorkspaceMemberBloc
), ),
); );
}, },
addWorkspaceMember: (e) async { addWorkspaceMember: (email) async {
await _addWorkspaceMember(e.email); await _addWorkspaceMember(email);
add(const WorkspaceMemberEvent.getWorkspaceMembers()); add(const WorkspaceMemberEvent.getWorkspaceMembers());
}, },
removeWorkspaceMember: (e) async { removeWorkspaceMember: (email) async {
await _removeWorkspaceMember(e.email); await _removeWorkspaceMember(email);
add(const WorkspaceMemberEvent.getWorkspaceMembers()); add(const WorkspaceMemberEvent.getWorkspaceMembers());
}, },
updateWorkspaceMember: (e) async { updateWorkspaceMember: (email, role) async {
await _updateWorkspaceMember(e.email, e.role); await _updateWorkspaceMember(email, role);
add(const WorkspaceMemberEvent.getWorkspaceMembers()); add(const WorkspaceMemberEvent.getWorkspaceMembers());
}, },
); );
@ -51,18 +67,16 @@ class WorkspaceMemberBloc
final UserProfilePB userProfile; final UserProfilePB userProfile;
// if the workspace is null, use the current workspace
final UserWorkspacePB? workspace;
late final String workspaceId;
Future<List<WorkspaceMemberPB>> _getWorkspaceMembers() async { Future<List<WorkspaceMemberPB>> _getWorkspaceMembers() async {
// will the current workspace be synced across the app? final data = QueryWorkspacePB()..workspaceId = workspaceId;
final currentWorkspace = await FolderEventReadCurrentWorkspace().send(); final result = await UserEventGetWorkspaceMember(data).send();
return currentWorkspace.fold((s) async { return result.fold((s) => s.items, (e) {
final data = QueryWorkspacePB()..workspaceId = s.id; Log.error('Failed to read workspace members: $e');
final result = await UserEventGetWorkspaceMember(data).send();
return result.fold((s) => s.items, (e) {
Log.error('Failed to read workspace members: $e');
return [];
});
}, (e) {
Log.error('Failed to read current workspace: $e');
return []; return [];
}); });
} }
@ -81,60 +95,46 @@ class WorkspaceMemberBloc
} }
Future<void> _addWorkspaceMember(String email) async { Future<void> _addWorkspaceMember(String email) async {
final currentWorkspace = await FolderEventReadCurrentWorkspace().send(); final data = AddWorkspaceMemberPB()
return currentWorkspace.fold((s) async { ..workspaceId = workspaceId
final data = AddWorkspaceMemberPB() ..email = email;
..workspaceId = s.id final result = await UserEventAddWorkspaceMember(data).send();
..email = email; result.fold((s) {
final result = await UserEventAddWorkspaceMember(data).send(); Log.info('Added workspace member: $data');
result.fold((s) {
Log.info('Added workspace member: $data');
}, (e) {
Log.error('Failed to add workspace member: $e');
});
}, (e) { }, (e) {
Log.error('Failed to read current workspace: $e'); Log.error('Failed to add workspace member: $e');
}); });
} }
Future<void> _removeWorkspaceMember(String email) async { Future<void> _removeWorkspaceMember(String email) async {
final currentWorkspace = await FolderEventReadCurrentWorkspace().send(); final data = RemoveWorkspaceMemberPB()
return currentWorkspace.fold((s) async { ..workspaceId = workspaceId
final data = RemoveWorkspaceMemberPB() ..email = email;
..workspaceId = s.id final result = await UserEventRemoveWorkspaceMember(data).send();
..email = email; result.fold((s) {
final result = await UserEventRemoveWorkspaceMember(data).send(); Log.info('Removed workspace member: $data');
result.fold((s) {
Log.info('Removed workspace member: $data');
}, (e) {
Log.error('Failed to remove workspace member: $e');
});
}, (e) { }, (e) {
Log.error('Failed to read current workspace: $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 currentWorkspace = await FolderEventReadCurrentWorkspace().send(); final data = UpdateWorkspaceMemberPB()
return currentWorkspace.fold((s) async { ..workspaceId = workspaceId
final data = UpdateWorkspaceMemberPB() ..email = email
..workspaceId = s.id ..role = role;
..email = email final result = await UserEventUpdateWorkspaceMember(data).send();
..role = role; result.fold((s) {
final result = await UserEventUpdateWorkspaceMember(data).send(); Log.info('Updated workspace member: $data');
result.fold((s) {
Log.info('Updated workspace member: $data');
}, (e) {
Log.error('Failed to update workspace member: $e');
});
}, (e) { }, (e) {
Log.error('Failed to read current workspace: $e'); Log.error('Failed to update workspace member: $e');
}); });
} }
} }
@freezed @freezed
class WorkspaceMemberEvent with _$WorkspaceMemberEvent { class WorkspaceMemberEvent with _$WorkspaceMemberEvent {
const factory WorkspaceMemberEvent.initial() = Initial;
const factory WorkspaceMemberEvent.getWorkspaceMembers() = const factory WorkspaceMemberEvent.getWorkspaceMembers() =
GetWorkspaceMembers; GetWorkspaceMembers;
const factory WorkspaceMemberEvent.addWorkspaceMember(String email) = const factory WorkspaceMemberEvent.addWorkspaceMember(String email) =

View File

@ -1,5 +1,6 @@
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/shared/af_role_pb_extension.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
@ -454,25 +455,3 @@ class _MemberRoleActionWrapper extends ActionCell {
throw UnimplementedError('Unknown role: $inner'); throw UnimplementedError('Unknown role: $inner');
} }
} }
extension on AFRolePB {
bool get isOwner => this == AFRolePB.Owner;
bool get canInvite => isOwner;
bool get canDelete => isOwner;
bool get canUpdate => isOwner;
String get description {
switch (this) {
case AFRolePB.Owner:
return LocaleKeys.settings_appearance_members_owner.tr();
case AFRolePB.Member:
return LocaleKeys.settings_appearance_members_member.tr();
case AFRolePB.Guest:
return LocaleKeys.settings_appearance_members_guest.tr();
}
throw UnimplementedError('Unknown role: $this');
}
}

View File

@ -217,7 +217,7 @@ class CloudTypeItem extends StatelessWidget {
confirm: () async { confirm: () async {
onSelected(cloudType); onSelected(cloudType);
}, },
hideCancleButton: true, hideCancelButton: true,
).show(context); ).show(context);
} }
PopoverContainer.of(context).close(); PopoverContainer.of(context).close();

View File

@ -1,6 +1,8 @@
import 'package:appflowy/workspace/presentation/settings/widgets/setting_file_import_appflowy_data_view.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/files/setting_file_import_appflowy_data_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_export_file_widget.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_export_file_widget.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_cache_widget.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/files/settings_file_customize_location_view.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -19,15 +21,15 @@ class _SettingsFileSystemViewState extends State<SettingsFileSystemView> {
// disable export data for v0.2.0 in release mode. // disable export data for v0.2.0 in release mode.
if (kDebugMode) const SettingsExportFileWidget(), if (kDebugMode) const SettingsExportFileWidget(),
const ImportAppFlowyData(), const ImportAppFlowyData(),
// clear the cache
const SettingsFileCacheWidget(),
]; ];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView.separated( return SeparatedColumn(
shrinkWrap: true, separatorBuilder: () => const Divider(),
itemBuilder: (context, index) => _items[index], children: _items,
separatorBuilder: (context, index) => const Divider(),
itemCount: _items.length,
); );
} }
} }

View File

@ -1,15 +1,16 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/tasks/app_widget.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/style_widget/text_input.dart';
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart'; import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:appflowy/startup/tasks/app_widget.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/text_input.dart';
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart'; export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
class NavigatorTextFieldDialog extends StatefulWidget { class NavigatorTextFieldDialog extends StatefulWidget {
const NavigatorTextFieldDialog({ const NavigatorTextFieldDialog({
@ -17,17 +18,19 @@ class NavigatorTextFieldDialog extends StatefulWidget {
required this.title, required this.title,
this.autoSelectAllText = false, this.autoSelectAllText = false,
required this.value, required this.value,
required this.confirm, required this.onConfirm,
this.cancel, this.onCancel,
this.maxLength, this.maxLength,
this.hintText,
}); });
final String value; final String value;
final String title; final String title;
final void Function()? cancel; final VoidCallback? onCancel;
final void Function(String) confirm; final void Function(String, BuildContext) onConfirm;
final bool autoSelectAllText; final bool autoSelectAllText;
final int? maxLength; final int? maxLength;
final String? hintText;
@override @override
State<NavigatorTextFieldDialog> createState() => State<NavigatorTextFieldDialog> createState() =>
@ -69,7 +72,8 @@ class _NavigatorTextFieldDialogState extends State<NavigatorTextFieldDialog> {
), ),
VSpace(Insets.m), VSpace(Insets.m),
FlowyFormTextInput( FlowyFormTextInput(
hintText: LocaleKeys.dialogCreatePageNameHint.tr(), hintText:
widget.hintText ?? LocaleKeys.dialogCreatePageNameHint.tr(),
controller: controller, controller: controller,
textStyle: Theme.of(context) textStyle: Theme.of(context)
.textTheme .textTheme
@ -82,20 +86,18 @@ class _NavigatorTextFieldDialogState extends State<NavigatorTextFieldDialog> {
newValue = text; newValue = text;
}, },
onEditingComplete: () { onEditingComplete: () {
widget.confirm(newValue); widget.onConfirm(newValue, context);
AppGlobals.nav.pop(); AppGlobals.nav.pop();
}, },
), ),
VSpace(Insets.xl), VSpace(Insets.xl),
OkCancelButton( OkCancelButton(
onOkPressed: () { onOkPressed: () {
widget.confirm(newValue); widget.onConfirm(newValue, context);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
onCancelPressed: () { onCancelPressed: () {
if (widget.cancel != null) { widget.onCancel?.call();
widget.cancel!();
}
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
@ -111,13 +113,13 @@ class NavigatorAlertDialog extends StatefulWidget {
required this.title, required this.title,
this.cancel, this.cancel,
this.confirm, this.confirm,
this.hideCancleButton = false, this.hideCancelButton = false,
}); });
final String title; final String title;
final void Function()? cancel; final void Function()? cancel;
final void Function()? confirm; final void Function()? confirm;
final bool hideCancleButton; final bool hideCancelButton;
@override @override
State<NavigatorAlertDialog> createState() => _CreateFlowyAlertDialog(); State<NavigatorAlertDialog> createState() => _CreateFlowyAlertDialog();
@ -158,7 +160,7 @@ class _CreateFlowyAlertDialog extends State<NavigatorAlertDialog> {
widget.confirm?.call(); widget.confirm?.call();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
onCancelPressed: widget.hideCancleButton onCancelPressed: widget.hideCancelButton
? null ? null
: () { : () {
widget.cancel?.call(); widget.cancel?.call();

View File

@ -219,9 +219,9 @@ class HoverButton extends StatelessWidget {
super.key, super.key,
required this.onTap, required this.onTap,
required this.itemHeight, required this.itemHeight,
required this.leftIcon, this.leftIcon,
required this.name, required this.name,
required this.rightIcon, this.rightIcon,
}); });
final VoidCallback onTap; final VoidCallback onTap;

View File

@ -29,7 +29,7 @@ class UserAvatar extends StatelessWidget {
if (iconUrl.isEmpty) { if (iconUrl.isEmpty) {
final String nameOrDefault = _userName(name); final String nameOrDefault = _userName(name);
final Color color = ColorGenerator().generateColorFromString(name); final Color color = ColorGenerator.generateColorFromString(name);
const initialsCount = 2; const initialsCount = 2;
// Taking the first letters of the name components and limiting to 2 elements // Taking the first letters of the name components and limiting to 2 elements

View File

@ -88,7 +88,7 @@ class FlowyButton extends StatelessWidget {
} }
Widget _render(BuildContext context) { Widget _render(BuildContext context) {
List<Widget> children = List.empty(growable: true); final List<Widget> children = [];
if (leftIcon != null) { if (leftIcon != null) {
children.add( children.add(

View File

@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.5.0 version: 0.5.1
environment: environment:
flutter: ">=3.19.0" flutter: ">=3.19.0"

View File

@ -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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -714,7 +714,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -733,7 +733,6 @@ dependencies = [
"gotrue", "gotrue",
"gotrue-entity", "gotrue-entity",
"governor", "governor",
"log",
"mime", "mime",
"mime_guess", "mime_guess",
"parking_lot 0.12.1", "parking_lot 0.12.1",
@ -818,7 +817,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -840,7 +839,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -869,7 +868,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -888,7 +887,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -903,7 +902,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -940,7 +939,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -979,7 +978,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1313,7 +1312,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2587,7 +2586,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2604,7 +2603,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -3059,7 +3058,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -4790,7 +4789,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -4814,7 +4813,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5462,7 +5461,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -6957,7 +6956,7 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "websocket" name = "websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -7376,7 +7375,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -82,7 +82,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 = "5ed6a149433e34b3dc4bfc66446033a33258e48c" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -92,10 +92,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5ed
# 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 = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }

View File

@ -20,9 +20,9 @@ function GridTableOverlay({
const [openConfirm, setOpenConfirm] = useState(false); const [openConfirm, setOpenConfirm] = useState(false);
const [confirmModalProps, setConfirmModalProps] = useState< const [confirmModalProps, setConfirmModalProps] = useState<
| { | {
onOk: () => Promise<void>; onOk: () => Promise<void>;
onCancel: () => void; onCancel: () => void;
} }
| undefined | undefined
>(undefined); >(undefined);
@ -59,7 +59,7 @@ function GridTableOverlay({
{openConfirm && ( {openConfirm && (
<DeleteConfirmDialog <DeleteConfirmDialog
open={openConfirm} open={openConfirm}
title={t('grid.removeSorting')} title={t('grid.sort.removeSorting')}
okText={t('button.remove')} okText={t('button.remove')}
cancelText={t('button.dontRemove')} cancelText={t('button.dontRemove')}
onClose={() => { onClose={() => {

View File

@ -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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -564,7 +564,6 @@ dependencies = [
"gotrue", "gotrue",
"gotrue-entity", "gotrue-entity",
"governor", "governor",
"log",
"mime", "mime",
"mime_guess", "mime_guess",
"parking_lot 0.12.1", "parking_lot 0.12.1",
@ -618,7 +617,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -640,7 +639,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -659,7 +658,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -674,7 +673,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -711,7 +710,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -749,7 +748,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -946,7 +945,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -1700,7 +1699,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -1717,7 +1716,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2051,7 +2050,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -3309,7 +3308,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -3333,7 +3332,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -3780,7 +3779,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -4722,7 +4721,7 @@ checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
[[package]] [[package]]
name = "websocket" name = "websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -5029,4 +5028,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"

View File

@ -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 = "5ed6a149433e34b3dc4bfc66446033a33258e48c" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" }
# 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 = "5ed
# 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 = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }

View File

@ -193,7 +193,7 @@
"editContact": "እውቂያ ያርትዑ" "editContact": "እውቂያ ያርትዑ"
}, },
"button": { "button": {
"OK": "እሺ", "ok": "እሺ",
"cancel": "ይቅር", "cancel": "ይቅር",
"signIn": "ይግቡ", "signIn": "ይግቡ",
"signOut": "ዘግተው ውጣ", "signOut": "ዘግተው ውጣ",

View File

@ -64,7 +64,14 @@
"reportIssueOnGithub": "Report an issue on Github", "reportIssueOnGithub": "Report an issue on Github",
"exportLogFiles": "Export log files", "exportLogFiles": "Export log files",
"reachOut": "Reach out on Discord" "reachOut": "Reach out on Discord"
} },
"deleteWorkspaceHintText": "Are you sure you want to delete the workspace? This action cannot be undone.",
"createSuccess": "Workspace created successfully",
"createFailed": "Failed to create workspace",
"deleteSuccess": "Workspace deleted successfully",
"deleteFailed": "Failed to delete workspace",
"openSuccess": "Open workspace successfully",
"openFailed": "Failed to open workspace"
}, },
"shareAction": { "shareAction": {
"buttonText": "Share", "buttonText": "Share",
@ -242,8 +249,6 @@
"helpCenter": "Help Center", "helpCenter": "Help Center",
"add": "Add", "add": "Add",
"yes": "Yes", "yes": "Yes",
"Done": "Done",
"Cancel": "Cancel",
"clear": "Clear", "clear": "Clear",
"remove": "Remove", "remove": "Remove",
"dontRemove": "Don't remove", "dontRemove": "Don't remove",
@ -414,7 +419,8 @@
"memberHintText": "A member can read, comment, and edit pages. Invite members and guests.", "memberHintText": "A member can read, comment, and edit pages. Invite members and guests.",
"guestHintText": "A Guest can read, react, comment, and can edit certain pages with permission.", "guestHintText": "A Guest can read, react, comment, and can edit certain pages with permission.",
"emailInvalidError": "Invalid email, please check and try again", "emailInvalidError": "Invalid email, please check and try again",
"emailSent": "Email sent, please check the inbox" "emailSent": "Email sent, please check the inbox",
"members": "members"
} }
}, },
"files": { "files": {
@ -451,7 +457,11 @@
"recoverLocationTooltips": "Reset to AppFlowy's default data directory", "recoverLocationTooltips": "Reset to AppFlowy's default data directory",
"exportFileSuccess": "Export file successfully!", "exportFileSuccess": "Export file successfully!",
"exportFileFail": "Export file failed!", "exportFileFail": "Export file failed!",
"export": "Export" "export": "Export",
"clearCache": "Clear cache",
"clearCacheDesc": "Clear the cache including the images, fonts, and other temporary files. This will not delete your data.",
"areYouSureToClearCache": "Are you sure to clear the cache?",
"clearCacheSuccess": "Cache cleared successfully!"
}, },
"user": { "user": {
"name": "Name", "name": "Name",
@ -664,7 +674,8 @@
"empty": "No active sorts", "empty": "No active sorts",
"cannotFindCreatableField": "Cannot find a suitable field to sort by", "cannotFindCreatableField": "Cannot find a suitable field to sort by",
"deleteAllSorts": "Delete all sorts", "deleteAllSorts": "Delete all sorts",
"addSort": "Add new sort" "addSort": "Add new sort",
"removeSorting": "Would you like to remove sorting?"
}, },
"row": { "row": {
"duplicate": "Duplicate", "duplicate": "Duplicate",
@ -725,8 +736,7 @@
"median": "Median", "median": "Median",
"min": "Min", "min": "Min",
"sum": "Sum" "sum": "Sum"
}, }
"removeSorting": "Would you like to remove sorting?"
}, },
"document": { "document": {
"menuName": "Document", "menuName": "Document",

View File

@ -238,8 +238,6 @@
"helpCenter": "Centro assistenza", "helpCenter": "Centro assistenza",
"add": "Aggiungi", "add": "Aggiungi",
"yes": "SÌ", "yes": "SÌ",
"Done": "Fatto",
"Cancel": "Annulla",
"remove": "Rimuovi", "remove": "Rimuovi",
"dontRemove": "Non rimuovere" "dontRemove": "Non rimuovere"
}, },
@ -621,7 +619,8 @@
"cannotFindCreatableField": "Impossibile trovare un campo adatto per l'ordinamento", "cannotFindCreatableField": "Impossibile trovare un campo adatto per l'ordinamento",
"deleteAllSorts": "Elimina tutti gli ordinamenti", "deleteAllSorts": "Elimina tutti gli ordinamenti",
"addSort": "Aggiungi ordinamento", "addSort": "Aggiungi ordinamento",
"deleteSort": "Elimina ordinamento" "deleteSort": "Elimina ordinamento",
"removeSorting": "Si desidera rimuovere l'ordinamento?"
}, },
"row": { "row": {
"duplicate": "Duplicare", "duplicate": "Duplicare",
@ -675,8 +674,7 @@
"median": "Medio", "median": "Medio",
"min": "Minimo", "min": "Minimo",
"sum": "Somma" "sum": "Somma"
}, }
"removeSorting": "Si desidera rimuovere l'ordinamento?"
}, },
"document": { "document": {
"menuName": "Documento", "menuName": "Documento",

View File

@ -242,8 +242,6 @@
"helpCenter": "Центр помощи", "helpCenter": "Центр помощи",
"add": "Добавить", "add": "Добавить",
"yes": "Да", "yes": "Да",
"Done": "Готово",
"Cancel": "Отмена",
"clear": "Очистить", "clear": "Очистить",
"remove": "Удалить", "remove": "Удалить",
"dontRemove": "Не удалять", "dontRemove": "Не удалять",
@ -645,7 +643,8 @@
"empty": "Нет активных сортировок", "empty": "Нет активных сортировок",
"cannotFindCreatableField": "Не могу найти подходящее поле для сортировки", "cannotFindCreatableField": "Не могу найти подходящее поле для сортировки",
"deleteAllSorts": "Удалить все сортировки", "deleteAllSorts": "Удалить все сортировки",
"addSort": "Добавить сортировку" "addSort": "Добавить сортировку",
"removeSorting": "Убрать сортировку?"
}, },
"row": { "row": {
"duplicate": "Дублировать", "duplicate": "Дублировать",
@ -706,8 +705,7 @@
"median": "Медиана", "median": "Медиана",
"min": "Минимум", "min": "Минимум",
"sum": "Сумма" "sum": "Сумма"
}, }
"removeSorting": "Убрать сортировку?"
}, },
"document": { "document": {
"menuName": "Документ", "menuName": "Документ",

View File

@ -242,8 +242,6 @@
"helpCenter": "帮助中心", "helpCenter": "帮助中心",
"add": "添加", "add": "添加",
"yes": "是", "yes": "是",
"Done": "完成",
"Cancel": "取消",
"clear": "清空", "clear": "清空",
"remove": "删除", "remove": "删除",
"dontRemove": "请勿删除", "dontRemove": "请勿删除",
@ -646,7 +644,8 @@
"descending": "降序", "descending": "降序",
"deleteAllSorts": "删除所有排序", "deleteAllSorts": "删除所有排序",
"addSort": "添加排序", "addSort": "添加排序",
"deleteSort": "取消排序" "deleteSort": "取消排序",
"removeSorting": "你确定要移除排序吗?"
}, },
"row": { "row": {
"duplicate": "复制", "duplicate": "复制",
@ -705,8 +704,7 @@
"median": "中位数", "median": "中位数",
"min": "最小值", "min": "最小值",
"sum": "求和" "sum": "求和"
}, }
"removeSorting": "你确定要移除排序吗?"
}, },
"document": { "document": {
"menuName": "文档", "menuName": "文档",

View File

@ -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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -692,7 +692,6 @@ dependencies = [
"gotrue", "gotrue",
"gotrue-entity", "gotrue-entity",
"governor", "governor",
"log",
"mime", "mime",
"mime_guess", "mime_guess",
"parking_lot 0.12.1", "parking_lot 0.12.1",
@ -746,7 +745,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -768,7 +767,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -797,7 +796,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -816,7 +815,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -831,7 +830,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -868,7 +867,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -907,7 +906,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=9f340e85e09af97a83d44b66069a93d63c6f2279#9f340e85e09af97a83d44b66069a93d63c6f2279" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7148c6f94bd71469782dfad04aeef835d34900fc#7148c6f94bd71469782dfad04aeef835d34900fc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1237,7 +1236,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2411,7 +2410,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2428,7 +2427,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2822,7 +2821,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -4319,7 +4318,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -4343,7 +4342,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -4931,7 +4930,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -6121,7 +6120,7 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "websocket" name = "websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -6361,7 +6360,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=5ed6a149433e34b3dc4bfc66446033a33258e48c#5ed6a149433e34b3dc4bfc66446033a33258e48c" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=15c03e4f85fffd35089a82c2a84aca8042a38946#15c03e4f85fffd35089a82c2a84aca8042a38946"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -105,7 +105,7 @@ incremental = false
# 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 = "5ed6a149433e34b3dc4bfc66446033a33258e48c" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "15c03e4f85fffd35089a82c2a84aca8042a38946" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -115,10 +115,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5ed
# 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 = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9f340e85e09af97a83d44b66069a93d63c6f2279" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7148c6f94bd71469782dfad04aeef835d34900fc" }

View File

@ -176,7 +176,6 @@ async fn post_to_flutter(response: AFPluginEventResponse, port: i64) {
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_log(level: i64, data: *const c_char) { pub extern "C" fn rust_log(level: i64, data: *const c_char) {
info!("backend_log");
// Check if the data pointer is not null // Check if the data pointer is not null
if data.is_null() { if data.is_null() {
error!("[flutter error]: null pointer provided to backend_log"); error!("[flutter error]: null pointer provided to backend_log");

View File

@ -2,7 +2,7 @@ use flowy_storage::{ObjectIdentity, ObjectStorageService};
use std::sync::Arc; use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use client_api::collab_sync::{SinkConfig, SinkStrategy, SyncObject, SyncPlugin}; use client_api::collab_sync::{SinkConfig, SyncObject, SyncPlugin};
use collab::core::collab::CollabDocState; use collab::core::collab::CollabDocState;
use collab::core::origin::{CollabClient, CollabOrigin}; use collab::core::origin::{CollabClient, CollabOrigin};
use collab::preclude::CollabPlugin; use collab::preclude::CollabPlugin;
@ -359,8 +359,7 @@ impl CollabCloudPluginProvider for ServerProvider {
let (sink, stream) = (channel.sink(), channel.stream()); let (sink, stream) = (channel.sink(), channel.stream());
let sink_config = SinkConfig::new() let sink_config = SinkConfig::new()
.send_timeout(8) .send_timeout(8)
.with_max_payload_size(1024 * 10) .with_max_payload_size(1024 * 10);
.with_strategy(sink_strategy_from_object(&sync_object));
let sync_plugin = SyncPlugin::new( let sync_plugin = SyncPlugin::new(
origin, origin,
sync_object, sync_object,
@ -417,14 +416,3 @@ impl CollabCloudPluginProvider for ServerProvider {
*self.user_enable_sync.read() *self.user_enable_sync.read()
} }
} }
fn sink_strategy_from_object(object: &SyncObject) -> SinkStrategy {
match object.collab_type {
CollabType::Document => SinkStrategy::FixInterval(std::time::Duration::from_millis(300)),
CollabType::Folder => SinkStrategy::ASAP,
CollabType::Database => SinkStrategy::ASAP,
CollabType::WorkspaceDatabase => SinkStrategy::ASAP,
CollabType::DatabaseRow => SinkStrategy::ASAP,
CollabType::UserAwareness => SinkStrategy::ASAP,
}
}

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use anyhow::Error; use anyhow::Error;
use client_api::collab_sync::collab_msg::CollabMessage; use client_api::collab_sync::collab_msg::ServerCollabMessage;
use client_api::entity::UserMessage; use client_api::entity::UserMessage;
use client_api::notify::{TokenState, TokenStateReceiver}; use client_api::notify::{TokenState, TokenStateReceiver};
use client_api::ws::{ use client_api::ws::{
@ -203,7 +203,7 @@ impl AppFlowyServer for AppFlowyCloudServer {
_object_id: &str, _object_id: &str,
) -> FutureResult< ) -> FutureResult<
Option<( Option<(
Arc<WebSocketChannel<CollabMessage>>, Arc<WebSocketChannel<ServerCollabMessage>>,
WSConnectStateReceiver, WSConnectStateReceiver,
bool, bool,
)>, )>,

View File

@ -5,7 +5,7 @@ use flowy_storage::ObjectStorageService;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use client_api::collab_sync::collab_msg::CollabMessage; use client_api::collab_sync::collab_msg::ServerCollabMessage;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio_stream::wrappers::WatchStream; use tokio_stream::wrappers::WatchStream;
#[cfg(feature = "enable_supabase")] #[cfg(feature = "enable_supabase")]
@ -125,7 +125,7 @@ pub trait AppFlowyServer: Send + Sync + 'static {
_object_id: &str, _object_id: &str,
) -> FutureResult< ) -> FutureResult<
Option<( Option<(
Arc<WebSocketChannel<CollabMessage>>, Arc<WebSocketChannel<ServerCollabMessage>>,
WSConnectStateReceiver, WSConnectStateReceiver,
bool, bool,
)>, )>,

View File

@ -197,7 +197,7 @@ pub enum UserEvent {
#[event(input = "ImportAppFlowyDataPB")] #[event(input = "ImportAppFlowyDataPB")]
ImportAppFlowyDataFolder = 41, ImportAppFlowyDataFolder = 41,
#[event(output = "CreateWorkspacePB")] #[event(input = "CreateWorkspacePB", output = "UserWorkspacePB")]
CreateWorkspace = 42, CreateWorkspace = 42,
#[event(input = "UserWorkspaceIdPB")] #[event(input = "UserWorkspaceIdPB")]

View File

@ -7,7 +7,7 @@ use tracing_appender::rolling::Rotation;
use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender}; use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender};
use tracing_bunyan_formatter::JsonStorageLayer; use tracing_bunyan_formatter::JsonStorageLayer;
use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
use crate::layer::FlowyFormattingLayer; use crate::layer::FlowyFormattingLayer;
@ -48,7 +48,7 @@ impl Builder {
pub fn build(self) -> Result<(), String> { pub fn build(self) -> Result<(), String> {
let env_filter = EnvFilter::new(self.env_filter); let env_filter = EnvFilter::new(self.env_filter);
let std_out_layer = fmt::layer().with_writer(std::io::stdout).pretty(); // let std_out_layer = std::fmt::layer().with_writer(std::io::stdout).pretty();
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender); let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
let file_layer = FlowyFormattingLayer::new(non_blocking); let file_layer = FlowyFormattingLayer::new(non_blocking);
@ -61,8 +61,7 @@ impl Builder {
.with_env_filter(env_filter) .with_env_filter(env_filter)
.finish() .finish()
.with(JsonStorageLayer) .with(JsonStorageLayer)
.with(file_layer) .with(file_layer);
.with(std_out_layer);
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?; set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;