enhance(frontend): タイムライン以外でもスクロール位置の保持を試みるように

This commit is contained in:
syuilo 2025-04-16 10:35:05 +09:00
parent 35d4b43c95
commit 6d90e09a58
3 changed files with 33 additions and 13 deletions

View File

@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
tag="div" tag="div"
> >
<template v-for="(notification, i) in notifications" :key="notification.id"> <template v-for="(notification, i) in notifications" :key="notification.id">
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true"/> <MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true" :data-scroll-anchor="notification.id"/>
<XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true"/> <XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true" :data-scroll-anchor="notification.id"/>
</template> </template>
</component> </component>
</template> </template>

View File

@ -20,6 +20,7 @@ import { useTemplateRef } from 'vue';
import { scrollInContainer } from '@@/js/scroll.js'; import { scrollInContainer } from '@@/js/scroll.js';
import type { PageHeaderItem } from '@/types/page-header.js'; import type { PageHeaderItem } from '@/types/page-header.js';
import type { Tab } from './MkPageHeader.tabs.vue'; import type { Tab } from './MkPageHeader.tabs.vue';
import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
tabs?: Tab[]; tabs?: Tab[];
@ -35,6 +36,8 @@ const props = withDefaults(defineProps<{
const tab = defineModel<string>('tab'); const tab = defineModel<string>('tab');
const rootEl = useTemplateRef('rootEl'); const rootEl = useTemplateRef('rootEl');
useScrollPositionKeeper(rootEl);
defineExpose({ defineExpose({
scrollToTop: () => { scrollToTop: () => {
if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' }); if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' });

View File

@ -4,19 +4,22 @@
*/ */
import { throttle } from 'throttle-debounce'; import { throttle } from 'throttle-debounce';
import { nextTick, onActivated, onUnmounted, watch } from 'vue'; import { nextTick, onActivated, onDeactivated, onUnmounted, watch } from 'vue';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
// note render skippingがオンだとズレるため、遷移直前にスクロール範囲に表示されているdata-scroll-anchor要素を特定して、復元時に当該要素までスクロールするようにする // note render skippingがオンだとズレるため、遷移直前にスクロール範囲に表示されているdata-scroll-anchor要素を特定して、復元時に当該要素までスクロールするようにする
export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | null | undefined>): void { export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | null | undefined>): void {
let anchorId: string | null = null; let anchorId: string | null = null;
let ready = true;
watch(scrollContainerRef, (el) => { watch(scrollContainerRef, (el) => {
if (!el) return; if (!el) return;
const onScroll = () => { const onScroll = () => {
if (!el) return; if (!el) return;
if (!ready) return;
const scrollContainerRect = el.getBoundingClientRect(); const scrollContainerRect = el.getBoundingClientRect();
const viewPosition = scrollContainerRect.height / 2; const viewPosition = scrollContainerRect.height / 2;
@ -41,8 +44,7 @@ export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | nu
immediate: true, immediate: true,
}); });
onActivated(() => { const restore = () => {
nextTick(() => {
if (!anchorId) return; if (!anchorId) return;
const scrollContainer = scrollContainerRef.value; const scrollContainer = scrollContainerRef.value;
if (!scrollContainer) return; if (!scrollContainer) return;
@ -53,6 +55,21 @@ export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | nu
block: 'center', block: 'center',
inline: 'center', inline: 'center',
}); });
};
onDeactivated(() => {
ready = false;
});
onActivated(() => {
restore();
nextTick(() => {
restore();
window.setTimeout(() => {
restore();
ready = true;
}, 100);
}); });
}); });
} }