This commit is contained in:
syuilo 2025-05-03 19:58:19 +09:00
parent 6ca9cfb24d
commit 74cc831332
6 changed files with 71 additions and 69 deletions

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #default="{ items: notes }">
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]">
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]">
<template v-for="(note, i) in notes" :key="note.id">
<div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id">
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
@ -62,11 +62,6 @@ defineExpose({
</script>
<style lang="scss" module>
.reverse {
display: flex;
flex-direction: column-reverse;
}
.root {
container-type: inline-size;

View File

@ -27,15 +27,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else ref="rootEl" class="_gaps">
<div v-show="pagination.reversed && paginator.canFetchMore.value" key="_more_">
<MkButton v-if="!paginator.moreFetching.value" v-appear="(prefer.s.enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="paginator.moreFetching.value" primary rounded @click="paginator.fetchNewer">
<div v-show="pagination.reversed && paginator.canFetchOlder.value" key="_more_">
<MkButton v-if="!paginator.fetchingOlder.value" v-appear="(prefer.s.enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="paginator.fetchingOlder.value" primary rounded @click="paginator.fetchNewer">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else/>
</div>
<slot :items="paginator.items.value" :fetching="paginator.fetching.value || paginator.moreFetching.value"></slot>
<div v-show="!pagination.reversed && paginator.canFetchMore.value" key="_more_">
<MkButton v-if="!paginator.moreFetching.value" v-appear="(prefer.s.enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :wait="paginator.moreFetching.value" primary rounded @click="paginator.fetchOlder">
<slot :items="paginator.items.value" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
<div v-show="!pagination.reversed && paginator.canFetchOlder.value" key="_more_">
<MkButton v-if="!paginator.fetchingOlder.value" v-appear="(prefer.s.enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :wait="paginator.fetchingOlder.value" primary rounded @click="paginator.fetchOlder">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else/>

View File

@ -44,8 +44,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkNote v-else :class="$style.note" :note="note" :withHardMute="true" :data-scroll-anchor="note.id"/>
</template>
</component>
<button v-show="paginator.canFetchMore.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.moreFetching.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.moreFetching.value">{{ i18n.ts.loadMore }}</div>
<button v-show="paginator.canFetchOlder.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.fetchingOlder.value">{{ i18n.ts.loadMore }}</div>
<MkLoading v-else :inline="true"/>
</button>
</div>

View File

@ -33,8 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true" :data-scroll-anchor="notification.id"/>
</template>
</component>
<button v-show="paginator.canFetchMore.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.moreFetching.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.moreFetching.value">{{ i18n.ts.loadMore }}</div>
<button v-show="paginator.canFetchOlder.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.fetchingOlder.value">{{ i18n.ts.loadMore }}</div>
<MkLoading v-else/>
</button>
</div>

View File

@ -6,42 +6,40 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<div>
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="note">
<div v-if="showNext" class="_margin">
<MkNotesTimeline :pullToRefresh="false" class="" :pagination="showNext === 'channel' ? nextChannelPagination : nextUserPagination" :noGap="true" :disableAutoLoad="true"/>
</div>
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="note">
<div v-if="showNext" class="_margin">
<MkNotesTimeline :pullToRefresh="false" class="" :pagination="showNext === 'channel' ? nextChannelPagination : nextUserPagination" :noGap="true" :disableAutoLoad="true"/>
</div>
<div class="_margin">
<div v-if="!showNext" class="_buttons" :class="$style.loadNext">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showNext = 'channel'"><i class="ti ti-chevron-up"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showNext = 'user'"><i class="ti ti-chevron-up"></i> <i class="ti ti-user"></i></MkButton>
</div>
<div class="_margin _gaps_s">
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
<MkNoteDetailed :key="note.id" v-model:note="note" :initialTab="initialTab" :class="$style.note"/>
</div>
<div v-if="clips && clips.length > 0" class="_margin">
<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
<div class="_gaps">
<MkClipPreview v-for="item in clips" :key="item.id" :clip="item"/>
</div>
</div>
<div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showPrev = 'channel'"><i class="ti ti-chevron-down"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showPrev = 'user'"><i class="ti ti-chevron-down"></i> <i class="ti ti-user"></i></MkButton>
<div class="_margin">
<div v-if="!showNext" class="_buttons" :class="$style.loadNext">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showNext = 'channel'"><i class="ti ti-chevron-up"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showNext = 'user'"><i class="ti ti-chevron-up"></i> <i class="ti ti-user"></i></MkButton>
</div>
<div class="_margin _gaps_s">
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
<MkNoteDetailed :key="note.id" v-model:note="note" :initialTab="initialTab" :class="$style.note"/>
</div>
<div v-if="clips && clips.length > 0" class="_margin">
<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
<div class="_gaps">
<MkClipPreview v-for="item in clips" :key="item.id" :clip="item"/>
</div>
</div>
<div v-if="showPrev" class="_margin">
<MkNotesTimeline :pullToRefresh="false" class="" :pagination="showPrev === 'channel' ? prevChannelPagination : prevUserPagination" :noGap="true"/>
<div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showPrev = 'channel'"><i class="ti ti-chevron-down"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showPrev = 'user'"><i class="ti ti-chevron-down"></i> <i class="ti ti-user"></i></MkButton>
</div>
</div>
<MkError v-else-if="error" @retry="fetchNote()"/>
<MkLoading v-else/>
</Transition>
</div>
<div v-if="showPrev" class="_margin">
<MkNotesTimeline :pullToRefresh="false" class="" :pagination="showPrev === 'channel' ? prevChannelPagination : prevUserPagination" :noGap="true"/>
</div>
</div>
<MkError v-else-if="error" @retry="fetchNote()"/>
<MkLoading v-else/>
</Transition>
</div>
</PageWithHeader>
</template>
@ -83,19 +81,20 @@ const error = ref();
const prevUserPagination: PagingCtx = {
endpoint: 'users/notes',
limit: 10,
baseId: props.noteId,
direction: 'older',
params: computed(() => note.value ? ({
userId: note.value.userId,
untilId: note.value.id,
}) : undefined),
};
const nextUserPagination: PagingCtx = {
reversed: true,
endpoint: 'users/notes',
limit: 10,
baseId: props.noteId,
direction: 'newer',
params: computed(() => note.value ? ({
userId: note.value.userId,
sinceId: note.value.id,
}) : undefined),
};

View File

@ -31,12 +31,10 @@ export type PagingCtx<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoint
*/
noPaging?: boolean;
/**
* items ()
*/
reversed?: boolean;
offsetMode?: boolean;
baseId?: MisskeyEntity['id'];
direction?: 'newer' | 'older';
};
export function usePagination<T extends MisskeyEntity>(props: {
@ -47,8 +45,8 @@ export function usePagination<T extends MisskeyEntity>(props: {
let aheadQueue: T[] = [];
const queuedAheadItemsCount = ref(0);
const fetching = ref(true);
const moreFetching = ref(false);
const canFetchMore = ref(false);
const fetchingOlder = ref(false);
const canFetchOlder = ref(false);
const error = ref(false);
// パラメータに何らかの変更があった際、再読込したいチャンネル等のIDが変わったなど
@ -73,11 +71,22 @@ export function usePagination<T extends MisskeyEntity>(props: {
queuedAheadItemsCount.value = 0;
fetching.value = true;
const params = props.ctx.params ? isRef(props.ctx.params) ? props.ctx.params.value : props.ctx.params : {};
await misskeyApi<T[]>(props.ctx.endpoint, {
...params,
limit: props.ctx.limit ?? FIRST_FETCH_LIMIT,
allowPartial: true,
...(props.ctx.baseId && props.ctx.direction === 'newer' ? {
sinceId: props.ctx.baseId,
} : props.ctx.baseId && props.ctx.direction === 'older' ? {
untilId: props.ctx.baseId,
} : {}),
}).then(res => {
// 逆順で返ってくるので
if (props.ctx.baseId && props.ctx.direction === 'newer') {
res.reverse();
}
for (let i = 0; i < res.length; i++) {
const item = res[i];
if (i === 3) item._shouldInsertAd_ = true;
@ -85,11 +94,10 @@ export function usePagination<T extends MisskeyEntity>(props: {
if (res.length === 0 || props.ctx.noPaging) {
pushItems(res);
canFetchMore.value = false;
canFetchOlder.value = false;
} else {
if (props.ctx.reversed) moreFetching.value = true;
pushItems(res);
canFetchMore.value = true;
canFetchOlder.value = true;
}
error.value = false;
@ -105,8 +113,8 @@ export function usePagination<T extends MisskeyEntity>(props: {
}
async function fetchOlder(): Promise<void> {
if (!canFetchMore.value || fetching.value || moreFetching.value || items.value.length === 0) return;
moreFetching.value = true;
if (!canFetchOlder.value || fetching.value || fetchingOlder.value || items.value.length === 0) return;
fetchingOlder.value = true;
const params = props.ctx.params ? isRef(props.ctx.params) ? props.ctx.params.value : props.ctx.params : {};
await misskeyApi<T[]>(props.ctx.endpoint, {
...params,
@ -123,16 +131,16 @@ export function usePagination<T extends MisskeyEntity>(props: {
}
if (res.length === 0) {
canFetchMore.value = false;
moreFetching.value = false;
canFetchOlder.value = false;
fetchingOlder.value = false;
} else {
items.value.push(...res);
if (props.useShallowRef) triggerRef(items);
canFetchMore.value = true;
moreFetching.value = false;
canFetchOlder.value = true;
fetchingOlder.value = false;
}
}, err => {
moreFetching.value = false;
fetchingOlder.value = false;
});
}
@ -163,7 +171,7 @@ export function usePagination<T extends MisskeyEntity>(props: {
}
function trim() {
if (items.value.length >= MAX_ITEMS) canFetchMore.value = true;
if (items.value.length >= MAX_ITEMS) canFetchOlder.value = true;
items.value = items.value.slice(0, MAX_ITEMS);
}
@ -225,8 +233,8 @@ export function usePagination<T extends MisskeyEntity>(props: {
items,
queuedAheadItemsCount,
fetching,
moreFetching,
canFetchMore,
fetchingOlder,
canFetchOlder,
init,
reload,
fetchOlder,