fix: launch 0.7.4 review issues on desktop (#6792)

* chore: enable document test runner

* fix: double menu showing in workspace menu

* feat: only display copy in ai message

* test: add test

* test: fix integration test

* fix: rust ci
This commit is contained in:
Lucas 2024-11-15 11:12:27 +08:00 committed by GitHub
parent bced9327b1
commit 6ffb9e4d0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 117 additions and 16 deletions

View File

@ -1,6 +1,8 @@
import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy/shared/feature_flags.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_icon.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart'; import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
@ -38,6 +40,10 @@ void main() {
await tester.changeWorkspaceIcon(icon); await tester.changeWorkspaceIcon(icon);
await tester.changeWorkspaceName(name); await tester.changeWorkspaceName(name);
await tester.pumpUntilNotFound(
find.text(LocaleKeys.workspace_renameSuccess.tr()),
);
workspaceIcon = tester.widget<WorkspaceIcon>( workspaceIcon = tester.widget<WorkspaceIcon>(
find.byType(WorkspaceIcon), find.byType(WorkspaceIcon),
); );

View File

@ -101,5 +101,52 @@ void main() {
final memberCount = find.text('1 member'); final memberCount = find.text('1 member');
expect(memberCount, findsNWidgets(2)); expect(memberCount, findsNWidgets(2));
}); });
testWidgets('only display one menu item in the workspace menu',
(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();
// hover on the workspace and click the more button
final workspaceItem = find.byWidgetPredicate(
(w) => w is WorkspaceMenuItem && w.workspace.name == name,
);
await tester.hoverOnWidget(
workspaceItem,
onHover: () async {
final moreButton = find.byWidgetPredicate(
(w) => w is WorkspaceMoreActionList && w.workspace.name == name,
);
expect(moreButton, findsOneWidget);
await tester.tapButton(moreButton);
// click it again
await tester.tapButton(moreButton);
// nothing should happen
expect(
find.text(LocaleKeys.button_rename.tr()),
findsOneWidget,
);
},
);
});
}); });
} }

View File

@ -1,7 +1,7 @@
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'mobile/document/page_style_test.dart' as page_style_test; import 'mobile/document/document_test_runner.dart' as document_test_runner;
import 'mobile/home_page/create_new_page_test.dart' as create_new_page_test; import 'mobile/home_page/create_new_page_test.dart' as create_new_page_test;
import 'mobile/sign_in/anonymous_sign_in_test.dart' as anonymous_sign_in_test; import 'mobile/sign_in/anonymous_sign_in_test.dart' as anonymous_sign_in_test;
@ -16,5 +16,5 @@ Future<void> runIntegration1OnMobile() async {
anonymous_sign_in_test.main(); anonymous_sign_in_test.main();
create_new_page_test.main(); create_new_page_test.main();
page_style_test.main(); document_test_runner.main();
} }

View File

@ -40,9 +40,13 @@ extension AppFlowyWorkspace on WidgetTester {
moreButton, moreButton,
onHover: () async { onHover: () async {
await tapButton(moreButton); await tapButton(moreButton);
await tapButton( // wait for the menu to open
find.findTextInFlowyText(LocaleKeys.button_rename.tr()), final renameButton = find.findTextInFlowyText(
LocaleKeys.button_rename.tr(),
); );
await pumpUntilFound(renameButton);
expect(renameButton, findsOneWidget);
await tapButton(renameButton);
final input = find.byType(TextFormField); final input = find.byType(TextFormField);
expect(input, findsOneWidget); expect(input, findsOneWidget);
await enterText(input, name); await enterText(input, name);

View File

@ -1,3 +1,4 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart'; import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_configuration.dart'; import 'package:appflowy/plugins/document/presentation/editor_configuration.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
@ -5,6 +6,7 @@ import 'package:appflowy/shared/markdown_to_document.dart';
import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/util/theme_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.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';
@ -126,6 +128,15 @@ class _AppFlowyEditorMarkdownState extends State<_AppFlowyEditorMarkdown> {
commandShortcutEvents: [customCopyCommand], commandShortcutEvents: [customCopyCommand],
disableAutoScroll: true, disableAutoScroll: true,
editorState: editorState, editorState: editorState,
contextMenuItems: [
[
ContextMenuItem(
getName: LocaleKeys.document_plugins_contextMenu_copy.tr,
onPressed: (editorState) =>
customCopyCommand.execute(editorState),
),
]
],
), ),
); );
} }

View File

@ -22,9 +22,11 @@ class WorkspaceMoreActionList extends StatelessWidget {
const WorkspaceMoreActionList({ const WorkspaceMoreActionList({
super.key, super.key,
required this.workspace, required this.workspace,
required this.isShowingMoreActions,
}); });
final UserWorkspacePB workspace; final UserWorkspacePB workspace;
final ValueNotifier<bool> isShowingMoreActions;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -46,6 +48,13 @@ class WorkspaceMoreActionList extends StatelessWidget {
.map((e) => _WorkspaceMoreActionWrapper(e, workspace)) .map((e) => _WorkspaceMoreActionWrapper(e, workspace))
.toList(), .toList(),
constraints: const BoxConstraints(minWidth: 220), constraints: const BoxConstraints(minWidth: 220),
animationDuration: Durations.short3,
slideDistance: 2,
beginScaleFactor: 1.0,
beginOpacity: 0.8,
onClosed: () {
isShowingMoreActions.value = false;
},
buildChild: (controller) { buildChild: (controller) {
return SizedBox.square( return SizedBox.square(
dimension: 24.0, dimension: 24.0,
@ -55,7 +64,11 @@ class WorkspaceMoreActionList extends StatelessWidget {
FlowySvgs.workspace_three_dots_s, FlowySvgs.workspace_three_dots_s,
), ),
onTap: () { onTap: () {
if (!isShowingMoreActions.value) {
controller.show(); controller.show();
}
isShowingMoreActions.value = true;
}, },
), ),
); );

View File

@ -25,7 +25,7 @@ const createWorkspaceButtonKey = ValueKey('createWorkspaceButton');
@visibleForTesting @visibleForTesting
const importNotionButtonKey = ValueKey('importNotinoButton'); const importNotionButtonKey = ValueKey('importNotinoButton');
class WorkspacesMenu extends StatelessWidget { class WorkspacesMenu extends StatefulWidget {
const WorkspacesMenu({ const WorkspacesMenu({
super.key, super.key,
required this.userProfile, required this.userProfile,
@ -37,6 +37,19 @@ class WorkspacesMenu extends StatelessWidget {
final UserWorkspacePB currentWorkspace; final UserWorkspacePB currentWorkspace;
final List<UserWorkspacePB> workspaces; final List<UserWorkspacePB> workspaces;
@override
State<WorkspacesMenu> createState() => _WorkspacesMenuState();
}
class _WorkspacesMenuState extends State<WorkspacesMenu> {
final ValueNotifier<bool> isShowingMoreActions = ValueNotifier(false);
@override
void dispose() {
isShowingMoreActions.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
@ -72,13 +85,14 @@ class WorkspacesMenu extends StatelessWidget {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
for (final workspace in workspaces) ...[ for (final workspace in widget.workspaces) ...[
WorkspaceMenuItem( WorkspaceMenuItem(
key: ValueKey(workspace.workspaceId), key: ValueKey(workspace.workspaceId),
workspace: workspace, workspace: workspace,
userProfile: userProfile, userProfile: widget.userProfile,
isSelected: isSelected: workspace.workspaceId ==
workspace.workspaceId == currentWorkspace.workspaceId, widget.currentWorkspace.workspaceId,
isShowingMoreActions: isShowingMoreActions,
), ),
const VSpace(6.0), const VSpace(6.0),
], ],
@ -99,12 +113,12 @@ class WorkspacesMenu extends StatelessWidget {
} }
String _getUserInfo() { String _getUserInfo() {
if (userProfile.email.isNotEmpty) { if (widget.userProfile.email.isNotEmpty) {
return userProfile.email; return widget.userProfile.email;
} }
if (userProfile.name.isNotEmpty) { if (widget.userProfile.name.isNotEmpty) {
return userProfile.name; return widget.userProfile.name;
} }
return LocaleKeys.defaultUsername.tr(); return LocaleKeys.defaultUsername.tr();
@ -117,11 +131,13 @@ class WorkspaceMenuItem extends StatefulWidget {
required this.workspace, required this.workspace,
required this.userProfile, required this.userProfile,
required this.isSelected, required this.isSelected,
required this.isShowingMoreActions,
}); });
final UserProfilePB userProfile; final UserProfilePB userProfile;
final UserWorkspacePB workspace; final UserWorkspacePB workspace;
final bool isSelected; final bool isSelected;
final ValueNotifier<bool> isShowingMoreActions;
@override @override
State<WorkspaceMenuItem> createState() => _WorkspaceMenuItemState(); State<WorkspaceMenuItem> createState() => _WorkspaceMenuItemState();
@ -211,7 +227,10 @@ class _WorkspaceMenuItemState extends State<WorkspaceMenuItem> {
), ),
); );
}, },
child: WorkspaceMoreActionList(workspace: widget.workspace), child: WorkspaceMoreActionList(
workspace: widget.workspace,
isShowingMoreActions: widget.isShowingMoreActions,
),
), ),
const HSpace(8.0), const HSpace(8.0),
if (widget.isSelected) ...[ if (widget.isSelected) ...[

View File

@ -5,13 +5,14 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
use tracing::{error, trace}; use tracing::{error, trace};
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
pub struct WatchContext {
#[allow(dead_code)] #[allow(dead_code)]
pub struct WatchContext {
watcher: notify::RecommendedWatcher, watcher: notify::RecommendedWatcher,
pub path: PathBuf, pub path: PathBuf,
} }
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
#[allow(dead_code)]
pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver<WatchDiskEvent>)> { pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver<WatchDiskEvent>)> {
use notify::{Event, Watcher}; use notify::{Event, Watcher};