feat: scroll to block after selecting notification item (#6667)

* fix: scrollbar's ScrollController has no ScrollPosition attached

* feat: support scrolling to block after selecting notification item

* chore: remove debug print

* fix: unable to cancel block selection
This commit is contained in:
Lucas 2024-10-31 10:29:34 +08:00 committed by GitHub
parent af6736d352
commit 873ab6cdc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 83 additions and 17 deletions

View File

@ -20,6 +20,7 @@ extension MobileRouter on BuildContext {
bool addInRecent = true,
bool showMoreButton = true,
String? fixedTitle,
String? blockId,
}) async {
// set the current view before pushing the new view
getIt<MenuSharedState>().latestOpenView = view;
@ -32,6 +33,9 @@ extension MobileRouter on BuildContext {
if (fixedTitle != null) {
queryParameters[MobileDocumentScreen.viewFixedTitle] = fixedTitle;
}
if (blockId != null) {
queryParameters[MobileDocumentScreen.viewBlockId] = blockId;
}
}
final uri = Uri(

View File

@ -61,6 +61,7 @@ class NotificationReminderBloc
reminderContent: node.delta?.toPlainText() ?? '',
nodes: [node],
status: NotificationReminderStatus.loaded,
blockId: reminder.meta[ReminderMetaKeys.blockId],
),
);
}
@ -205,6 +206,7 @@ class NotificationReminderState with _$NotificationReminderState {
@Default(NotificationReminderStatus.initial)
NotificationReminderStatus status,
@Default([]) List<Node> nodes,
String? blockId,
ViewPB? view,
}) = _NotificationReminderState;

View File

@ -30,6 +30,7 @@ class MobileViewPage extends StatefulWidget {
this.arguments,
this.fixedTitle,
this.showMoreButton = true,
this.blockId,
});
/// view id
@ -38,6 +39,7 @@ class MobileViewPage extends StatefulWidget {
final String? title;
final Map<String, dynamic>? arguments;
final bool showMoreButton;
final String? blockId;
// only used in row page
final String? fixedTitle;
@ -177,6 +179,7 @@ class _MobileViewPageState extends State<MobileViewPage> {
context: PluginContext(userProfile: state.userProfilePB),
data: {
MobileDocumentScreen.viewFixedTitle: widget.fixedTitle,
MobileDocumentScreen.viewBlockId: widget.blockId,
},
);
},

View File

@ -143,6 +143,7 @@ Future<T?> showMobileBottomSheet<T>(
) ??
Expanded(
child: Scrollbar(
controller: scrollController,
child: SingleChildScrollView(
controller: scrollController,
child: child,

View File

@ -9,6 +9,7 @@ class MobileDocumentScreen extends StatelessWidget {
this.title,
this.showMoreButton = true,
this.fixedTitle,
this.blockId,
});
/// view id
@ -16,12 +17,14 @@ class MobileDocumentScreen extends StatelessWidget {
final String? title;
final bool showMoreButton;
final String? fixedTitle;
final String? blockId;
static const routeName = '/docs';
static const viewId = 'id';
static const viewTitle = 'title';
static const viewShowMoreButton = 'show_more_button';
static const viewFixedTitle = 'fixed_title';
static const viewBlockId = 'block_id';
@override
Widget build(BuildContext context) {
@ -31,6 +34,7 @@ class MobileDocumentScreen extends StatelessWidget {
viewLayout: ViewLayoutPB.Document,
showMoreButton: showMoreButton,
fixedTitle: fixedTitle,
blockId: blockId,
);
}
}

View File

@ -64,11 +64,15 @@ class NotificationItem extends StatelessWidget {
child: child,
onTapUp: () async {
final view = state.view;
final blockId = state.blockId;
if (view == null) {
return;
}
await context.pushView(view);
await context.pushView(
view,
blockId: blockId,
);
if (!reminder.isRead && context.mounted) {
context.read<ReminderBloc>().add(

View File

@ -128,6 +128,7 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
});
final fixedTitle = data?[MobileDocumentScreen.viewFixedTitle];
final blockId = initialBlockId ?? data?[MobileDocumentScreen.viewBlockId];
return BlocProvider<ViewInfoBloc>.value(
value: bloc,
@ -137,7 +138,7 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
view: view,
onDeleted: () => context.onDeleted?.call(view, deletedViewIndex),
initialSelection: initialSelection,
initialBlockId: initialBlockId,
initialBlockId: blockId,
fixedTitle: fixedTitle,
),
),

View File

@ -46,6 +46,7 @@ class DocumentPage extends StatefulWidget {
class _DocumentPageState extends State<DocumentPage>
with WidgetsBindingObserver {
EditorState? editorState;
Selection? initialSelection;
late final documentBloc = DocumentBloc(documentId: widget.view.id)
..add(const DocumentEvent.initial());
@ -120,6 +121,9 @@ class _DocumentPageState extends State<DocumentPage>
final width = context.read<DocumentAppearanceCubit>().state.width;
// avoid the initial selection calculation change when the editorState is not changed
initialSelection ??= _calculateInitialSelection(editorState);
final Widget child;
if (UniversalPlatform.isMobile) {
child = BlocBuilder<DocumentPageStyleBloc, DocumentPageStyleState>(
@ -133,7 +137,7 @@ class _DocumentPageState extends State<DocumentPage>
padding: EditorStyleCustomizer.documentPadding,
),
header: buildCoverAndIcon(context, state),
initialSelection: widget.initialSelection,
initialSelection: initialSelection,
),
);
} else {
@ -151,7 +155,7 @@ class _DocumentPageState extends State<DocumentPage>
padding: EditorStyleCustomizer.documentPadding,
),
header: buildCoverAndIcon(context, state),
initialSelection: _calculateInitialSelection(editorState),
initialSelection: initialSelection,
),
);
}
@ -299,6 +303,9 @@ class _DocumentPageState extends State<DocumentPage>
final path = _findNodePathByBlockId(editorState, widget.initialBlockId!);
if (path != null) {
editorState.selectionType = SelectionType.block;
editorState.selectionExtraInfo = {
selectionExtraInfoDoNotAttachTextService: true,
};
return Selection.collapsed(
Position(
path: path,

View File

@ -181,15 +181,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
focusManager = AFFocusManager.maybeOf(context);
focusManager?.loseFocusNotifier.addListener(_loseFocus);
final initialSelection = widget.initialSelection;
final path = initialSelection?.start.path;
if (initialSelection != null && path != null && path.isNotEmpty) {
editorScrollController.itemScrollController.jumpTo(
index: path.first,
alignment: 0.5,
);
widget.editorState.updateSelectionWithReason(initialSelection);
}
_scrollToSelectionIfNeeded();
widget.editorState.service.keyboardService?.registerInterceptor(
editorKeyboardInterceptor,
@ -197,6 +189,48 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
});
}
void _scrollToSelectionIfNeeded() {
final initialSelection = widget.initialSelection;
final path = initialSelection?.start.path;
if (path == null) {
return;
}
// on desktop, using jumpTo to scroll to the selection.
// on mobile, using scrollTo to scroll to the selection, because using jumpTo will break the scroll notification metrics.
if (UniversalPlatform.isDesktop) {
editorScrollController.itemScrollController.jumpTo(
index: path.first,
alignment: 0.5,
);
widget.editorState.updateSelectionWithReason(
initialSelection,
);
} else {
const delayDuration = Duration(milliseconds: 250);
const animationDuration = Duration(milliseconds: 400);
Future.delayed(delayDuration, () {
editorScrollController.itemScrollController.scrollTo(
index: path.first,
duration: animationDuration,
curve: Curves.easeInOut,
);
widget.editorState.updateSelectionWithReason(
initialSelection,
extraInfo: {
selectionExtraInfoDoNotAttachTextService: true,
selectionExtraInfoDisableMobileToolbarKey: true,
},
);
}).then((_) {
Future.delayed(animationDuration, () {
widget.editorState.selectionType = SelectionType.inline;
widget.editorState.selectionExtraInfo = null;
});
});
}
}
void onSelectionChanged() {
if (widget.editorState.isDisposed) {
return;

View File

@ -327,7 +327,10 @@ Future<void> _handleTap(
if (UniversalPlatform.isMobile) {
if (context.mounted && currentViewId != view.id) {
await context.pushView(view);
await context.pushView(
view,
blockId: blockId,
);
}
} else {
final action = NavigationAction(

View File

@ -499,6 +499,8 @@ GoRoute _mobileEditorScreenRoute() {
);
final fixedTitle =
state.uri.queryParameters[MobileDocumentScreen.viewFixedTitle];
final blockId =
state.uri.queryParameters[MobileDocumentScreen.viewBlockId];
return MaterialExtendedPage(
child: MobileDocumentScreen(
@ -506,6 +508,7 @@ GoRoute _mobileEditorScreenRoute() {
title: title,
showMoreButton: showMoreButton ?? true,
fixedTitle: fixedTitle,
blockId: blockId,
),
);
},

View File

@ -61,8 +61,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: cca5c41
resolved-ref: cca5c413480048fbb7dbd4e58f9251e5cf3088e0
ref: "4081fb7"
resolved-ref: "4081fb756311c6d249f45efbeb59e4cd2a7db530"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "4.0.0"

View File

@ -172,7 +172,7 @@ dependency_overrides:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "cca5c41"
ref: "4081fb7"
appflowy_editor_plugins:
git: