Skip to content

Commit

Permalink
fix: navigation bar issue on linked pages(AppFlowy-IO#7111)
Browse files Browse the repository at this point in the history
  • Loading branch information
asjqkkkk committed Jan 4, 2025
1 parent 552c592 commit 0b0fc52
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_folder.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

Expand Down Expand Up @@ -44,5 +48,82 @@ void main() {
);
expect(isExpanded(type: FolderSpaceType.private), true);
});

testWidgets('Expanding with subpage', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
const page1 = 'SubPageBloc', page2 = '$page1 2';
await tester.createNewPageWithNameUnderParent(name: page1);
await tester.createNewPageWithNameUnderParent(
name: page2,
parentName: page1,
);

await tester.expandOrCollapsePage(
pageName: gettingStarted,
layout: ViewLayoutPB.Document,
);

await tester.tapNewPageButton();

await tester.editor.tapLineOfEditorAt(0);
await tester.pumpAndSettle();
await tester.editor.showSlashMenu();
await tester.pumpAndSettle();

final slashMenu = find
.ancestor(
of: find.byType(SelectionMenuItemWidget),
matching: find.byWidgetPredicate(
(widget) => widget is Scrollable,
),
)
.first;
final slashMenuItem = find.text(
LocaleKeys.document_slashMenu_name_linkedDoc.tr(),
);
await tester.scrollUntilVisible(
slashMenuItem,
100,
scrollable: slashMenu,
duration: const Duration(milliseconds: 250),
);

final menuItemFinder = find.byWidgetPredicate(
(w) =>
w is SelectionMenuItemWidget &&
w.item.name == LocaleKeys.document_slashMenu_name_linkedDoc.tr(),
);

final menuItem =
menuItemFinder.evaluate().first.widget as SelectionMenuItemWidget;

/// tapSlashMenuItemWithName is not working, so invoke this function directly
menuItem.item.handler(
menuItem.editorState,
menuItem.menuService,
menuItemFinder.evaluate().first,
);

await tester.pumpAndSettle();
final actionHandler = find.byType(InlineActionsHandler);
final subPage = find.descendant(
of: actionHandler,
matching: find.text(page2, findRichText: true),
);
await tester.tapButton(subPage);

final subpageBlock = find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.text(page2, findRichText: true),
);

expect(find.text(page2, findRichText: true), findsOneWidget);
await tester.tapButton(subpageBlock);

/// one is in SectionFolder, another one is in CoverTitle
/// the last one is in FlowyNavigation
expect(find.text(page2, findRichText: true), findsNWidgets(3));
});
});
}
5 changes: 5 additions & 0 deletions frontend/appflowy_flutter/lib/core/config/kv_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,9 @@ class KVKeys {
///
/// The value is a json string of [RecentIcons]
static const String recentIcons = 'kRecentIcons';

/// The key for saving the space view id
///
/// The value is the space view id
static const String spaceViewId = 'spacePages';
}
27 changes: 27 additions & 0 deletions frontend/appflowy_flutter/lib/util/expand_views.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:appflowy/workspace/application/view/view_bloc.dart';

/// the key is view id
final Map<String, Set<ViewBloc>> _recordBlocs = {};

bool isViewExpanded(String id) => getViewBloc(id)?.state.isExpanded ?? false;

void recordBloc(String id, ViewBloc bloc) {
final set = _recordBlocs[id] ?? {};
set.add(bloc);
_recordBlocs[id] = set;
}

void eraseBloc(String id, ViewBloc bloc) {
final set = _recordBlocs[id] ?? {};
set.remove(bloc);
if (set.isEmpty) {
_recordBlocs.remove(id);
} else {
_recordBlocs[id] = set;
}
}

ViewBloc? getViewBloc(String id) {
final set = _recordBlocs[id] ?? {};
return set.isEmpty ? null : set.first;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import 'dart:convert';

import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/plugins/blank/blank.dart';
import 'package:appflowy/plugins/util.dart';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/expand_views.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_result/appflowy_result.dart';
import 'package:bloc/bloc.dart';
import 'package:collection/collection.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
Expand Down Expand Up @@ -71,6 +79,7 @@ class TabsBloc extends Bloc<TabsEvent, TabsState> {
emit(state.openPlugin(plugin: plugin, setLatest: setLatest));
if (setLatest) {
_setLatestOpenView(view);
if (view != null) _expandAncestors(view);
}
},
closeOtherTabs: (String pluginId) {
Expand Down Expand Up @@ -212,6 +221,24 @@ class TabsBloc extends Bloc<TabsEvent, TabsState> {
}
}

Future<void> _expandAncestors(ViewPB view) async {
if (isViewExpanded(view.parentViewId)) return;
final r = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
final Map map = r == null ? {} : jsonDecode(r);
final List<String> ancestors =
await ViewBackendService.getViewAncestors(view.id)
.fold((s) => s.items.map((e) => e.id).toList(), (f) => []);
ViewBloc? viewBloc;
for (final id in ancestors) {
map[id] = true;
final vb = getViewBloc(id);
if (vb == null) continue;
if (!vb.state.isExpanded && viewBloc == null) viewBloc = vb;
}
await getIt<KeyValueStorage>().set(KVKeys.expandedViews, jsonEncode(map));
viewBloc?.add(const ViewEvent.setIsExpanded(true));
}

int _adjustCurrentIndex({
required int currentIndex,
required int tabIndex,
Expand Down Expand Up @@ -249,26 +276,37 @@ class TabsBloc extends Bloc<TabsEvent, TabsState> {
@freezed
class TabsEvent with _$TabsEvent {
const factory TabsEvent.moveTab() = _MoveTab;

const factory TabsEvent.closeTab(String pluginId) = _CloseTab;

const factory TabsEvent.closeOtherTabs(String pluginId) = _CloseOtherTabs;

const factory TabsEvent.closeCurrentTab() = _CloseCurrentTab;

const factory TabsEvent.selectTab(int index) = _SelectTab;

const factory TabsEvent.togglePin(String pluginId) = _TogglePin;

const factory TabsEvent.openTab({
required Plugin plugin,
required ViewPB view,
}) = _OpenTab;

const factory TabsEvent.openPlugin({
required Plugin plugin,
ViewPB? view,
@Default(true) bool setLatest,
}) = _OpenPlugin;

const factory TabsEvent.openSecondaryPlugin({
required Plugin plugin,
ViewPB? view,
}) = _OpenSecondaryPlugin;

const factory TabsEvent.closeSecondaryPlugin() = _CloseSecondaryPlugin;

const factory TabsEvent.expandSecondaryPlugin() = _ExpandSecondaryPlugin;

const factory TabsEvent.switchWorkspace(String workspaceId) =
_SwitchWorkspace;
}
Expand All @@ -281,8 +319,11 @@ class TabsState {

final int currentIndex;
final List<PageManager> _pageManagers;

int get pages => _pageManagers.length;

PageManager get currentPageManager => _pageManagers[currentIndex];

List<PageManager> get pageManagers => _pageManagers;

bool get isAllPinned => _pageManagers.every((pm) => pm.isPinned);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/expand_views.dart';
import 'package:appflowy/workspace/application/favorite/favorite_listener.dart';
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart';
Expand All @@ -24,24 +25,30 @@ import 'package:protobuf/protobuf.dart';
part 'view_bloc.freezed.dart';

class ViewBloc extends Bloc<ViewEvent, ViewState> {
ViewBloc({required this.view, this.shouldLoadChildViews = true})
: viewBackendSvc = ViewBackendService(),
ViewBloc({
required this.view,
this.shouldLoadChildViews = true,
this.engagedInExpanding = false,
}) : viewBackendSvc = ViewBackendService(),
listener = ViewListener(viewId: view.id),
favoriteListener = FavoriteListener(),
super(ViewState.init(view)) {
_dispatch();
if (engagedInExpanding) recordBloc(view.id, this);
}

final ViewPB view;
final ViewBackendService viewBackendSvc;
final ViewListener listener;
final FavoriteListener favoriteListener;
final bool shouldLoadChildViews;
final bool engagedInExpanding;

@override
Future<void> close() async {
await listener.stop();
await favoriteListener.stop();
if (engagedInExpanding) eraseBloc(view.id, this);
return super.close();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class _SectionFolderState extends State<SectionFolder> {
(view) => ViewItem(
key: ValueKey('${widget.spaceType.name} ${view.id}'),
spaceType: widget.spaceType,
engagedInExpanding: true,
isFirstChild: view.id == widget.views.first.id,
view: view,
level: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ViewItem extends StatelessWidget {
this.extendBuilder,
this.disableSelectedStatus,
this.shouldIgnoreView,
this.engagedInExpanding = false,
this.enableRightClickContext = false,
});

Expand Down Expand Up @@ -136,12 +137,17 @@ class ViewItem extends StatelessWidget {
///
final bool enableRightClickContext;

/// to record the view id which is expanded or collapsed
final bool engagedInExpanding;

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) =>
ViewBloc(view: view, shouldLoadChildViews: shouldLoadChildViews)
..add(const ViewEvent.initial()),
create: (_) => ViewBloc(
view: view,
shouldLoadChildViews: shouldLoadChildViews,
engagedInExpanding: engagedInExpanding,
)..add(const ViewEvent.initial()),
child: BlocConsumer<ViewBloc, ViewState>(
listenWhen: (p, c) =>
c.lastCreatedView != null &&
Expand Down Expand Up @@ -183,6 +189,7 @@ class ViewItem extends StatelessWidget {
isExpandedNotifier: isExpandedNotifier,
extendBuilder: extendBuilder,
shouldIgnoreView: shouldIgnoreView,
engagedInExpanding: engagedInExpanding,
);

if (shouldIgnoreView?.call(view) == IgnoreViewType.disable) {
Expand Down Expand Up @@ -235,6 +242,7 @@ class InnerViewItem extends StatefulWidget {
this.isExpandedNotifier,
required this.extendBuilder,
this.disableSelectedStatus,
this.engagedInExpanding = false,
required this.shouldIgnoreView,
});

Expand Down Expand Up @@ -270,6 +278,7 @@ class InnerViewItem extends StatefulWidget {
final PropertyValueNotifier<bool>? isExpandedNotifier;
final List<Widget> Function(ViewPB view)? extendBuilder;
final IgnoreViewType Function(ViewPB view)? shouldIgnoreView;
final bool engagedInExpanding;

@override
State<InnerViewItem> createState() => _InnerViewItemState();
Expand Down Expand Up @@ -345,6 +354,7 @@ class _InnerViewItemState extends State<InnerViewItem> {
rightIconsBuilder: widget.rightIconsBuilder,
extendBuilder: widget.extendBuilder,
shouldIgnoreView: widget.shouldIgnoreView,
engagedInExpanding: widget.engagedInExpanding,
);
}).toList();

Expand Down

0 comments on commit 0b0fc52

Please sign in to comment.