feat: optimize the workspace menu loading speed (#6758)

* feat: optimize the workspace menu loading speed

* chore: add workspace menu animation

* feat: optimize workspace menu loading speed on mobile

* test: workspace member count test

* fix: rust ci
This commit is contained in:
Lucas 2024-11-13 20:36:15 +08:00 committed by GitHub
parent 57933736ea
commit cd3be696dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 87 additions and 61 deletions

View File

@ -43,6 +43,7 @@ jobs:
cp deploy.env .env cp deploy.env .env
sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
sed -i '' 's|APPFLOWY_AI_OPENAI_API_KEY=.*|APPFLOWY_AI_OPENAI_API_KEY=${{ secrets.CI_OPENAI_API_KEY }}|' .env
- name: Ensure AppFlowy-Cloud is Running with Correct Version - name: Ensure AppFlowy-Cloud is Running with Correct Version
working-directory: AppFlowy-Cloud working-directory: AppFlowy-Cloud
@ -137,7 +138,7 @@ jobs:
run: | run: |
cp deploy.env .env cp deploy.env .env
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's|GOTRUE_MAILER_AUTOCONFIRM=.*|GOTRUE_MAILER_AUTOCONFIRM=true|' .env sed -i 's|GOTRUE_MAILER_AUTOCONFIRM=.*|GOTRUE_MAILER_AUTOCONFIRM=true|' .env
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
- name: Ensure AppFlowy-Cloud is Running with Correct Version - name: Ensure AppFlowy-Cloud is Running with Correct Version
@ -160,7 +161,7 @@ jobs:
else else
echo "No volumes to remove." echo "No volumes to remove."
fi fi
docker compose pull docker compose pull
docker compose up -d docker compose up -d
echo "Waiting for the container to be ready..." echo "Waiting for the container to be ready..."

View File

@ -13,7 +13,7 @@ import '../../../shared/util.dart';
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('collaborative workspace: ', () { group('collaborative workspace:', () {
// combine the create and delete workspace test to reduce the time // combine the create and delete workspace test to reduce the time
testWidgets('create a new workspace, open it and then delete it', testWidgets('create a new workspace, open it and then delete it',
(tester) async { (tester) async {
@ -74,5 +74,32 @@ void main() {
}, },
); );
}); });
testWidgets('check the member count immediately after creating a workspace',
(tester) async {
// only run the test when the feature flag is on
if (!FeatureFlag.collaborativeWorkspace.isOn) {
return;
}
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
const name = 'AppFlowy.IO';
// the workspace will be opened after created
await tester.createCollaborativeWorkspace(name);
final loading = find.byType(Loading);
await tester.pumpUntilNotFound(loading);
await tester.openCollaborativeWorkspaceMenu();
// expect to see the member count
final memberCount = find.text('1 member');
expect(memberCount, findsNWidgets(2));
});
}); });
} }

View File

@ -234,6 +234,7 @@ class _WorkspaceMenuItemContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final memberCount = workspace.memberCount.toInt();
return Padding( return Padding(
padding: const EdgeInsets.only(left: 12), padding: const EdgeInsets.only(left: 12),
child: Column( child: Column(
@ -247,10 +248,10 @@ class _WorkspaceMenuItemContent extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
FlowyText( FlowyText(
context.read<WorkspaceMemberBloc>().state.isLoading memberCount == 0
? '' ? ''
: LocaleKeys.settings_appearance_members_membersCount.plural( : LocaleKeys.settings_appearance_members_membersCount.plural(
context.read<WorkspaceMemberBloc>().state.members.length, memberCount,
), ),
fontSize: 10.0, fontSize: 10.0,
color: Theme.of(context).hintColor, color: Theme.of(context).hintColor,

View File

@ -170,7 +170,7 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
switch (inner) { switch (inner) {
case WorkspaceMoreAction.delete: case WorkspaceMoreAction.delete:
return FlowySvg( return FlowySvg(
FlowySvgs.delete_s, FlowySvgs.trash_s,
color: onHover ? Theme.of(context).colorScheme.error : null, color: onHover ? Theme.of(context).colorScheme.error : null,
); );
case WorkspaceMoreAction.rename: case WorkspaceMoreAction.rename:

View File

@ -196,26 +196,23 @@ class _WorkspaceMenuItemState extends State<WorkspaceMenuItem> {
} }
Widget _buildRightIcon(BuildContext context, ValueNotifier<bool> isHovered) { Widget _buildRightIcon(BuildContext context, ValueNotifier<bool> isHovered) {
// only the owner can update or delete workspace.
if (context.read<WorkspaceMemberBloc>().state.isLoading) {
return const SizedBox.shrink();
}
return Row( return Row(
children: [ children: [
ValueListenableBuilder( // only the owner can update or delete workspace.
valueListenable: isHovered, if (!context.read<WorkspaceMemberBloc>().state.isLoading)
builder: (context, value, child) { ValueListenableBuilder(
return Padding( valueListenable: isHovered,
padding: const EdgeInsets.only(left: 8.0), builder: (context, value, child) {
child: Opacity( return Padding(
opacity: value ? 1.0 : 0.0, padding: const EdgeInsets.only(left: 8.0),
child: child, child: Opacity(
), opacity: value ? 1.0 : 0.0,
); child: child,
}, ),
child: WorkspaceMoreActionList(workspace: widget.workspace), );
), },
child: WorkspaceMoreActionList(workspace: widget.workspace),
),
const HSpace(8.0), const HSpace(8.0),
if (widget.isSelected) ...[ if (widget.isSelected) ...[
const Padding( const Padding(
@ -244,43 +241,38 @@ class _WorkspaceInfo extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<WorkspaceMemberBloc, WorkspaceMemberState>( final memberCount = workspace.memberCount.toInt();
builder: (context, state) { return FlowyButton(
final members = state.members; onTap: () => _openWorkspace(context),
return FlowyButton( iconPadding: 10.0,
onTap: () => _openWorkspace(context), leftIconSize: const Size.square(32),
iconPadding: 10.0, leftIcon: const SizedBox.square(dimension: 32),
leftIconSize: const Size.square(32), rightIcon: const HSpace(32.0),
leftIcon: const SizedBox.square(dimension: 32), text: Column(
rightIcon: const HSpace(32.0), crossAxisAlignment: CrossAxisAlignment.start,
text: Column( mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, children: [
mainAxisAlignment: MainAxisAlignment.center, // workspace name
children: [ FlowyText.medium(
// workspace name workspace.name,
FlowyText.medium( fontSize: 14.0,
workspace.name, figmaLineHeight: 17.0,
fontSize: 14.0, overflow: TextOverflow.ellipsis,
figmaLineHeight: 17.0, withTooltip: true,
overflow: TextOverflow.ellipsis,
withTooltip: true,
),
// workspace members count
FlowyText.regular(
state.isLoading
? ''
: LocaleKeys.settings_appearance_members_membersCount
.plural(
members.length,
),
fontSize: 10.0,
figmaLineHeight: 12.0,
color: Theme.of(context).hintColor,
),
],
), ),
); // workspace members count
}, FlowyText.regular(
memberCount == 0
? ''
: LocaleKeys.settings_appearance_members_membersCount.plural(
memberCount,
),
fontSize: 10.0,
figmaLineHeight: 12.0,
color: Theme.of(context).hintColor,
),
],
),
); );
} }

View File

@ -204,9 +204,12 @@ class _SidebarSwitchWorkspaceButtonState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppFlowyPopover( return AppFlowyPopover(
direction: PopoverDirection.bottomWithLeftAligned, direction: PopoverDirection.bottomWithCenterAligned,
offset: const Offset(0, 5), offset: const Offset(0, 5),
constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600),
animationDuration: Durations.short3,
beginScaleFactor: 1.0,
beginOpacity: 0.8,
controller: _popoverController, controller: _popoverController,
triggerActions: PopoverTriggerFlags.none, triggerActions: PopoverTriggerFlags.none,
onOpen: () { onOpen: () {

View File

@ -679,6 +679,7 @@ pub fn save_user_workspace(
user_workspace_table::created_at.eq(&user_workspace.created_at), user_workspace_table::created_at.eq(&user_workspace.created_at),
user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id), user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id),
user_workspace_table::icon.eq(&user_workspace.icon), user_workspace_table::icon.eq(&user_workspace.icon),
user_workspace_table::member_count.eq(&user_workspace.member_count),
)) ))
.execute(conn)?; .execute(conn)?;
@ -729,6 +730,7 @@ pub fn save_all_user_workspaces(
user_workspace_table::created_at.eq(&user_workspace.created_at), user_workspace_table::created_at.eq(&user_workspace.created_at),
user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id), user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id),
user_workspace_table::icon.eq(&user_workspace.icon), user_workspace_table::icon.eq(&user_workspace.icon),
user_workspace_table::member_count.eq(&user_workspace.member_count),
)) ))
.execute(conn)?; .execute(conn)?;