mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-11-03 03:24:08 +00:00
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:
parent
57933736ea
commit
cd3be696dc
5
.github/workflows/rust_ci.yaml
vendored
5
.github/workflows/rust_ci.yaml
vendored
@ -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..."
|
||||||
|
|||||||
@ -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));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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: () {
|
||||||
|
|||||||
@ -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)?;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user