feat: add a loading indicator when creating, deleting, or switching workspaces. (#5067)

This commit is contained in:
Lucas.Xu 2024-04-07 23:06:33 +08:00 committed by GitHub
parent 3e32fac876
commit 9536cde789
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 241 additions and 108 deletions

View File

@ -86,6 +86,7 @@ jobs:
model: 'iPhone 15'
shutdown_after_job: false
- name: Run integration tests
working-directory: frontend/appflowy_flutter
run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}
# enable it again if the 12 mins timeout is fixed
# - name: Run integration tests
# working-directory: frontend/appflowy_flutter
# run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
@ -51,33 +52,23 @@ void main() {
await tester.expectToSeeHomePageWithGetStartedPage();
const name = 'AppFlowy.IO';
// the workspace will be opened after created
await tester.createCollaborativeWorkspace(name);
// see the success message
var success = find.text(LocaleKeys.workspace_createSuccess.tr());
expect(success, findsOneWidget);
await tester.pumpUntilNotFound(success);
final loading = find.byType(Loading);
await tester.pumpUntilNotFound(loading);
// check the create result
Finder success;
// delete the newly created workspace
await tester.openCollaborativeWorkspaceMenu();
var items = find.byType(WorkspaceMenuItem);
final Finder items = find.byType(WorkspaceMenuItem);
expect(items, findsNWidgets(2));
expect(
tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
name,
);
// open the newly created workspace
await tester.tapButton(items.last, milliseconds: 1000);
success = find.text(LocaleKeys.workspace_openSuccess.tr());
await tester.pumpUntilFound(success);
expect(success, findsOneWidget);
await tester.pumpUntilNotFound(success);
await tester.closeCollaborativeWorkspaceMenu();
// delete the newly created workspace
await tester.openCollaborativeWorkspaceMenu();
final secondWorkspace = find.byType(WorkspaceMenuItem).last;
await tester.hoverOnWidget(
secondWorkspace,
@ -97,20 +88,11 @@ void main() {
await tester.tapButton(find.text(LocaleKeys.button_ok.tr()));
// delete success
success = find.text(LocaleKeys.workspace_createSuccess.tr());
await tester.pumpUntilFound(success);
expect(success, findsOneWidget);
await tester.pumpUntilNotFound(success);
},
);
// check the result
await tester.openCollaborativeWorkspaceMenu();
items = find.byType(WorkspaceMenuItem);
expect(items, findsOneWidget);
expect(
tester.widget<WorkspaceMenuItem>(items.last).workspace.name != name,
true,
);
await tester.closeCollaborativeWorkspaceMenu();
});
});
}

View File

@ -180,7 +180,11 @@ extension AppFlowyTestBase on WidgetTester {
buttons: buttons,
warnIfMissed: warnIfMissed,
);
await pumpAndSettle(Duration(milliseconds: milliseconds));
await pumpAndSettle(
Duration(milliseconds: milliseconds),
EnginePhase.sendSemanticsUpdate,
const Duration(seconds: 5),
);
}
Future<void> tapButtonWithName(

View File

@ -528,7 +528,7 @@ extension CommonOperations on WidgetTester {
final workspace = find.byType(SidebarWorkspace);
expect(workspace, findsOneWidget);
// click it
await tapButton(workspace);
await tapButton(workspace, milliseconds: 2000);
}
Future<void> closeCollaborativeWorkspaceMenu() async {
@ -560,7 +560,6 @@ extension CommonOperations on WidgetTester {
// input the workspace name
await enterText(find.byType(TextField), name);
await pumpAndSettle();
await tapButtonWithName(LocaleKeys.button_ok.tr());
}

View File

@ -69,7 +69,7 @@ class MobileFolders extends StatelessWidget {
...isCollaborativeWorkspace
? [
MobileSectionFolder(
title: LocaleKeys.sideBar_public.tr(),
title: LocaleKeys.sideBar_workspace.tr(),
categoryType: FolderCategoryType.public,
views: state.section.publicViews,
),

View File

@ -31,6 +31,7 @@ class MobileWorkspaceMenu extends StatelessWidget {
final workspace = workspaces[i];
children.add(
_WorkspaceMenuItem(
key: ValueKey(workspace.workspaceId),
userProfile: userProfile,
workspace: workspace,
showTopBorder: i == 0,
@ -47,6 +48,7 @@ class MobileWorkspaceMenu extends StatelessWidget {
class _WorkspaceMenuItem extends StatelessWidget {
const _WorkspaceMenuItem({
super.key,
required this.userProfile,
required this.workspace,
required this.showTopBorder,

View File

@ -30,9 +30,9 @@ class DatabaseSyncBloc extends Bloc<DatabaseSyncEvent, DatabaseSyncBlocState> {
.then((value) => value.fold((s) => s, (f) => null));
emit(
state.copyWith(
shouldShowIndicator:
userProfile?.authenticator != AuthenticatorPB.Local &&
databaseId != null,
shouldShowIndicator: userProfile?.authenticator ==
AuthenticatorPB.AppFlowyCloud &&
databaseId != null,
),
);
if (databaseId != null) {

View File

@ -32,7 +32,7 @@ class DocumentCollaboratorsBloc
emit(
state.copyWith(
shouldShowIndicator:
userProfile?.authenticator != AuthenticatorPB.Local,
userProfile?.authenticator == AuthenticatorPB.AppFlowyCloud,
),
);
final deviceId = ApplicationInfo.deviceId;

View File

@ -31,7 +31,7 @@ class DocumentSyncBloc extends Bloc<DocumentSyncEvent, DocumentSyncBlocState> {
emit(
state.copyWith(
shouldShowIndicator:
userProfile?.authenticator != AuthenticatorPB.Local,
userProfile?.authenticator == AuthenticatorPB.AppFlowyCloud,
),
);
_syncStateListener.start(

View File

@ -145,7 +145,7 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
? [
DocumentCollaborators(
key: ValueKey('collaborators_${view.id}'),
width: 100,
width: 150,
height: 32,
view: view,
),

View File

@ -13,6 +13,7 @@ class CollaboratorAvatarStack extends StatelessWidget {
this.borderWidth,
this.borderColor,
this.backgroundColor,
required this.plusWidgetBuilder,
});
final List<Widget> avatars;
@ -31,13 +32,16 @@ class CollaboratorAvatarStack extends StatelessWidget {
final Color? backgroundColor;
final Widget Function(int value, BorderSide border) plusWidgetBuilder;
@override
Widget build(BuildContext context) {
final settings = this.settings ??
RestrictedPositions(
maxCoverage: 0.3,
minCoverage: 0.1,
minCoverage: 0.2,
align: StackAlign.right,
laying: StackLaying.first,
);
final border = BorderSide(
@ -45,27 +49,12 @@ class CollaboratorAvatarStack extends StatelessWidget {
width: borderWidth ?? 2.0,
);
Widget textInfoWidgetBuilder(surplus) => BorderedCircleAvatar(
border: border,
backgroundColor: backgroundColor,
child: FittedBox(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'+$surplus',
style: Theme.of(context).textTheme.titleLarge,
),
),
),
);
final infoWidgetBuilder = this.infoWidgetBuilder ?? textInfoWidgetBuilder;
return SizedBox(
height: height,
width: width,
child: WidgetStack(
positions: settings,
buildInfoWidget: infoWidgetBuilder,
buildInfoWidget: (value) => plusWidgetBuilder(value, border),
stackedWidgets: avatars
.map(
(avatar) => CircleAvatar(

View File

@ -2,6 +2,7 @@ import 'package:appflowy/plugins/document/application/doc_collaborators_bloc.dar
import 'package:appflowy/plugins/document/presentation/collaborator_avater_stack.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:avatar_stack/avatar_stack.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
@ -41,8 +42,30 @@ class DocumentCollaborators extends StatelessWidget {
height: height,
width: width,
borderWidth: 1.0,
backgroundColor:
Theme.of(context).colorScheme.onSecondaryContainer,
plusWidgetBuilder: (value, border) {
final lastXCollaborators = collaborators.sublist(
collaborators.length - value,
);
return BorderedCircleAvatar(
border: border,
backgroundColor: Theme.of(context).hoverColor,
child: FittedBox(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FlowyTooltip(
message: lastXCollaborators
.map((e) => e.userName)
.join('\n'),
child: FlowyText(
'+$value',
fontSize: fontSize,
color: Colors.black,
),
),
),
),
);
},
avatars: collaborators
.map(
(c) => FlowyTooltip(

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
class Loading {
Loading(this.context);
late BuildContext loadingContext;
BuildContext? loadingContext;
final BuildContext context;
Future<void> start() async => showDialog<void>(
@ -24,7 +24,12 @@ class Loading {
},
);
Future<void> stop() async => Navigator.of(loadingContext).pop();
Future<void> stop() async {
if (loadingContext != null) {
Navigator.of(loadingContext!).pop();
loadingContext = null;
}
}
}
class BarrierDialog {

View File

@ -1,5 +1,9 @@
import 'dart:async';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/workspace/workspace_sections_listener.dart';
import 'package:appflowy/workspace/application/workspace/workspace_service.dart';
import 'package:appflowy_backend/log.dart';
@ -152,6 +156,37 @@ class SidebarSectionsBloc
},
);
},
reload: (userProfile, workspaceId) async {
_initial(userProfile, workspaceId);
final sectionViews = await _getSectionViews();
if (sectionViews != null) {
emit(
state.copyWith(
section: sectionViews,
),
);
// try to open the fist view in public section or private section
if (sectionViews.publicViews.isNotEmpty) {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: sectionViews.publicViews.first.plugin(),
),
);
} else if (sectionViews.privateViews.isNotEmpty) {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: sectionViews.privateViews.first.plugin(),
),
);
} else {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: makePlugin(pluginType: PluginType.blank),
),
);
}
}
},
);
},
);
@ -245,6 +280,10 @@ class SidebarSectionsEvent with _$SidebarSectionsEvent {
const factory SidebarSectionsEvent.receiveSectionViewsUpdate(
SectionViewsPB sectionViews,
) = _ReceiveSectionViewsUpdate;
const factory SidebarSectionsEvent.reload(
UserProfilePB userProfile,
String workspaceId,
) = _Reload;
}
@freezed

View File

@ -40,7 +40,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
final currentWorkspace = result.$1;
final workspaces = result.$2;
final isCollabWorkspaceOn =
userProfile.authenticator != AuthenticatorPB.Local &&
userProfile.authenticator == AuthenticatorPB.AppFlowyCloud &&
FeatureFlag.collaborativeWorkspace.isOn;
if (currentWorkspace != null && result.$3 == true) {
final result = await _userService
@ -71,6 +71,15 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
);
},
createWorkspace: (name) async {
emit(
state.copyWith(
actionResult: const UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.create,
isLoading: true,
result: null,
),
),
);
final result = await _userService.createUserWorkspace(name);
final workspaces = result.fold(
(s) => [...state.workspaces, s],
@ -81,6 +90,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
workspaces: workspaces,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.create,
isLoading: false,
result: result,
),
),
@ -91,6 +101,15 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
});
},
deleteWorkspace: (workspaceId) async {
emit(
state.copyWith(
actionResult: const UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.delete,
isLoading: true,
result: null,
),
),
);
final remoteWorkspaces = await _fetchWorkspaces().then(
(value) => value.$2,
);
@ -108,6 +127,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.delete,
result: result,
isLoading: false,
),
),
);
@ -134,11 +154,21 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.delete,
result: result,
isLoading: false,
),
),
);
},
openWorkspace: (workspaceId) async {
emit(
state.copyWith(
actionResult: const UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.open,
isLoading: true,
result: null,
),
),
);
final result = await _userService.openWorkspace(workspaceId);
final currentWorkspace = result.fold(
(s) => state.workspaces.firstWhereOrNull(
@ -157,6 +187,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
currentWorkspace: currentWorkspace,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.open,
isLoading: false,
result: result,
),
),
@ -188,6 +219,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
currentWorkspace: currentWorkspace,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.rename,
isLoading: false,
result: result,
),
),
@ -221,6 +253,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
currentWorkspace: currentWorkspace,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.updateIcon,
isLoading: false,
result: result,
),
),
@ -245,6 +278,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
workspaces: workspaces,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.leave,
isLoading: false,
result: result,
),
),
@ -253,7 +287,11 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
updateWorkspaces: (workspaces) async {
emit(
state.copyWith(
workspaces: workspaces.items,
workspaces: workspaces.items
..sort(
(a, b) =>
a.createdAtTimestamp.compareTo(b.createdAtTimestamp),
),
),
);
},
@ -359,11 +397,13 @@ enum UserWorkspaceActionType {
class UserWorkspaceActionResult {
const UserWorkspaceActionResult({
required this.actionType,
required this.isLoading,
required this.result,
});
final UserWorkspaceActionType actionType;
final FlowyResult<void, FlowyError> result;
final bool isLoading;
final FlowyResult<void, FlowyError>? result;
}
@freezed

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/favorite/prelude.dart';
@ -106,21 +105,22 @@ class HomeSideBar extends StatelessWidget {
),
BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
listener: (context, state) {
context.read<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: makePlugin(pluginType: PluginType.blank),
),
);
context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.initial(
userProfile,
state.currentWorkspace?.workspaceId ??
workspaceSetting.workspaceId,
),
);
context.read<FavoriteBloc>().add(
const FavoriteEvent.fetchFavorites(),
);
final actionType = state.actionResult?.actionType;
if (actionType == UserWorkspaceActionType.create ||
actionType == UserWorkspaceActionType.delete ||
actionType == UserWorkspaceActionType.open) {
context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.reload(
userProfile,
state.currentWorkspace?.workspaceId ??
workspaceSetting.workspaceId,
),
);
context.read<FavoriteBloc>().add(
const FavoriteEvent.fetchFavorites(),
);
}
},
),
],

View File

@ -103,10 +103,10 @@ class PublicSectionFolder extends SectionFolder {
super.key,
required super.views,
}) : super(
title: LocaleKeys.sideBar_public.tr(),
title: LocaleKeys.sideBar_workspace.tr(),
categoryType: FolderCategoryType.public,
expandButtonTooltip: LocaleKeys.sideBar_clickToHidePublic.tr(),
addButtonTooltip: LocaleKeys.sideBar_addAPageToPublic.tr(),
expandButtonTooltip: LocaleKeys.sideBar_clickToHideWorkspace.tr(),
addButtonTooltip: LocaleKeys.sideBar_addAPageToWorkspace.tr(),
);
}

View File

@ -1,5 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.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';
@ -16,7 +17,7 @@ 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 {
class SidebarWorkspace extends StatefulWidget {
const SidebarWorkspace({
super.key,
required this.userProfile,
@ -24,6 +25,13 @@ class SidebarWorkspace extends StatelessWidget {
final UserProfilePB userProfile;
@override
State<SidebarWorkspace> createState() => _SidebarWorkspaceState();
}
class _SidebarWorkspaceState extends State<SidebarWorkspace> {
Loading? loadingIndicator;
@override
Widget build(BuildContext context) {
return BlocConsumer<UserWorkspaceBloc, UserWorkspaceState>(
@ -39,11 +47,11 @@ class SidebarWorkspace extends StatelessWidget {
children: [
Expanded(
child: SidebarSwitchWorkspaceButton(
userProfile: userProfile,
userProfile: widget.userProfile,
currentWorkspace: currentWorkspace,
),
),
UserSettingButton(userProfile: userProfile),
UserSettingButton(userProfile: widget.userProfile),
const HSpace(4),
const NotificationButton(),
],
@ -60,6 +68,19 @@ class SidebarWorkspace extends StatelessWidget {
final actionType = actionResult.actionType;
final result = actionResult.result;
final isLoading = actionResult.isLoading;
if (isLoading) {
loadingIndicator ??= Loading(context)..start();
return;
} else {
loadingIndicator?.stop();
loadingIndicator = null;
}
if (result == null) {
return;
}
result.onFailure((f) {
Log.error(
@ -195,6 +216,7 @@ class _SidebarSwitchWorkspaceButtonState
child: FlowyText.medium(
widget.currentWorkspace.name,
overflow: TextOverflow.ellipsis,
withTooltip: true,
),
),
const FlowySvg(FlowySvgs.drop_menu_show_m),

View File

@ -110,7 +110,6 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
await showDialog(
context: context,
builder: (_) => NavigatorOkCancelDialog(
title: LocaleKeys.workspace_leaveCurrentWorkspace.tr(),
message: LocaleKeys.workspace_leaveCurrentWorkspacePrompt.tr(),
onOkPressed: () {
workspaceBloc.add(

View File

@ -9,6 +9,7 @@ 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:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -136,8 +137,10 @@ class WorkspaceMenuItem extends StatelessWidget {
PopoverContainer.of(context).closeAll();
}
},
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
margin: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 12,
),
iconPadding: 10.0,
leftIconSize: const Size.square(32),
leftIcon: const SizedBox.square(
@ -146,12 +149,12 @@ class WorkspaceMenuItem extends StatelessWidget {
rightIcon: const HSpace(42.0),
text: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.center,
children: [
FlowyText.medium(
workspace.name,
fontSize: 14.0,
overflow: TextOverflow.ellipsis,
withTooltip: true,
),
FlowyText(
state.isLoading
@ -171,10 +174,14 @@ class WorkspaceMenuItem extends StatelessWidget {
left: 8,
child: SizedBox.square(
dimension: 32,
child: WorkspaceIcon(
workspace: workspace,
iconSize: 26,
enableEdit: true,
child: FlowyTooltip(
message:
LocaleKeys.document_plugins_cover_changeIcon.tr(),
child: WorkspaceIcon(
workspace: workspace,
iconSize: 26,
enableEdit: true,
),
),
),
),

View File

@ -62,6 +62,7 @@ class SettingsDialog extends StatelessWidget {
SizedBox(
width: 200,
child: SettingsMenu(
userProfile: user,
changeSelectedPage: (index) {
context
.read<SettingsDialogBloc>()

View File

@ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/foundation.dart';
@ -12,10 +13,12 @@ class SettingsMenu extends StatelessWidget {
super.key,
required this.changeSelectedPage,
required this.currentPage,
required this.userProfile,
});
final Function changeSelectedPage;
final SettingsPage currentPage;
final UserProfilePB userProfile;
@override
Widget build(BuildContext context) {
@ -72,7 +75,8 @@ class SettingsMenu extends StatelessWidget {
icon: Icons.cut,
changeSelectedPage: changeSelectedPage,
),
if (FeatureFlag.membersSettings.isOn)
if (FeatureFlag.membersSettings.isOn &&
userProfile.authenticator == AuthenticatorPB.AppFlowyCloud)
SettingsMenuElement(
page: SettingsPage.member,
selectedPage: currentPage,

View File

@ -15,6 +15,7 @@ class FlowyText extends StatelessWidget {
final String? fontFamily;
final List<String>? fallbackFontFamily;
final double? lineHeight;
final bool withTooltip;
const FlowyText(
this.text, {
@ -30,6 +31,7 @@ class FlowyText extends StatelessWidget {
this.fontFamily,
this.fallbackFontFamily,
this.lineHeight,
this.withTooltip = false,
});
FlowyText.small(
@ -44,6 +46,7 @@ class FlowyText extends StatelessWidget {
this.fontFamily,
this.fallbackFontFamily,
this.lineHeight,
this.withTooltip = false,
}) : fontWeight = FontWeight.w400,
fontSize = (Platform.isIOS || Platform.isAndroid) ? 14 : 12;
@ -60,6 +63,7 @@ class FlowyText extends StatelessWidget {
this.fontFamily,
this.fallbackFontFamily,
this.lineHeight,
this.withTooltip = false,
}) : fontWeight = FontWeight.w400;
const FlowyText.medium(
@ -75,6 +79,7 @@ class FlowyText extends StatelessWidget {
this.fontFamily,
this.fallbackFontFamily,
this.lineHeight,
this.withTooltip = false,
}) : fontWeight = FontWeight.w500;
const FlowyText.semibold(
@ -90,6 +95,7 @@ class FlowyText extends StatelessWidget {
this.fontFamily,
this.fallbackFontFamily,
this.lineHeight,
this.withTooltip = false,
}) : fontWeight = FontWeight.w600;
// Some emojis are not supported on Linux and Android, fallback to noto color emoji
@ -104,14 +110,17 @@ class FlowyText extends StatelessWidget {
this.decoration,
this.selectable = false,
this.lineHeight,
this.withTooltip = false,
}) : fontWeight = FontWeight.w400,
fontFamily = 'noto color emoji',
fallbackFontFamily = null;
@override
Widget build(BuildContext context) {
Widget child;
if (selectable) {
return SelectableText(
child = SelectableText(
text,
maxLines: maxLines,
textAlign: textAlign,
@ -126,7 +135,7 @@ class FlowyText extends StatelessWidget {
),
);
} else {
return Text(
child = Text(
text,
maxLines: maxLines,
textAlign: textAlign,
@ -142,5 +151,14 @@ class FlowyText extends StatelessWidget {
),
);
}
if (withTooltip) {
child = Tooltip(
message: text,
child: child,
);
}
return child;
}
}

View File

@ -213,15 +213,15 @@
"openSidebar": "Open side bar",
"personal": "Personal",
"private": "Private",
"public": "Public",
"workspace": "Workspace",
"favorites": "Favorites",
"clickToHidePrivate": "Click to hide private space\nPages you created here are only visible to you",
"clickToHidePublic": "Click to hide public space\nPages you created here are visible to every member",
"clickToHideWorkspace": "Click to hide workspace\nPages you created here are visible to every member",
"clickToHidePersonal": "Click to hide personal space",
"clickToHideFavorites": "Click to hide favorite space",
"addAPage": "Add a page",
"addAPageToPrivate": "Add a page to private space",
"addAPageToPublic": "Add a page to public space",
"addAPageToWorkspace": "Add a page to workspace",
"recent": "Recent"
},
"notifications": {

View File

@ -508,16 +508,14 @@ impl FolderManager {
let view_id = view_id.to_string();
let folder = self.mutex_folder.lock();
let folder = folder.as_ref().ok_or_else(folder_not_init_error)?;
let trash_ids = folder
.get_all_trash_sections()
.into_iter()
.map(|trash| trash.id)
.collect::<Vec<String>>();
if trash_ids.contains(&view_id) {
// trash views and other private views should not be accessed
let view_ids_should_be_filtered = self.get_view_ids_should_be_filtered(folder);
if view_ids_should_be_filtered.contains(&view_id) {
return Err(FlowyError::new(
ErrorCode::RecordNotFound,
format!("View:{} is in trash", view_id),
format!("View:{} is in trash or other private section", view_id),
));
}
@ -531,7 +529,7 @@ impl FolderManager {
.views
.get_views_belong_to(&view.id)
.into_iter()
.filter(|view| !trash_ids.contains(&view.id))
.filter(|view| !view_ids_should_be_filtered.contains(&view.id))
.collect::<Vec<_>>();
let view_pb = view_pb_with_child_views(view, child_views);
Ok(view_pb)