fix(frontend): ページネーションの進行方向を指定できるように
This commit is contained in:
parent
8c433d2706
commit
9cd51ca89d
|
@ -25,15 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
|
||||
<div v-else key="_root_" class="_gaps">
|
||||
<slot :items="unref(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
|
||||
<div v-if="paginator.order.value === 'oldest'">
|
||||
<MkButton v-if="!paginator.fetchingNewer.value" :class="$style.more" :wait="paginator.fetchingNewer.value" primary rounded @click="paginator.fetchNewer()">
|
||||
<div v-if="paginator.direction === 'up' || paginator.direction === 'both'" v-show="upButtonVisible">
|
||||
<MkButton v-if="!upButtonLoading" :class="$style.more" primary rounded @click="upButtonClick">
|
||||
{{ i18n.ts.loadMore }}
|
||||
</MkButton>
|
||||
<MkLoading v-else/>
|
||||
</div>
|
||||
<div v-else v-show="paginator.canFetchOlder.value">
|
||||
<MkButton v-if="!paginator.fetchingOlder.value" :class="$style.more" :wait="paginator.fetchingOlder.value" primary rounded @click="paginator.fetchOlder()">
|
||||
<slot :items="unref(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
|
||||
<div v-if="paginator.direction === 'down' || paginator.direction === 'both'" v-show="downButtonVisible">
|
||||
<MkButton v-if="!downButtonLoading" :class="$style.more" primary rounded @click="downButtonClick">
|
||||
{{ i18n.ts.loadMore }}
|
||||
</MkButton>
|
||||
<MkLoading v-else/>
|
||||
|
@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup generic="T extends IPaginator">
|
||||
import { isLink } from '@@/js/is-link.js';
|
||||
import { onMounted, watch, unref } from 'vue';
|
||||
import { onMounted, computed, watch, unref } from 'vue';
|
||||
import type { UnwrapRef } from 'vue';
|
||||
import type { IPaginator } from '@/utility/paginator.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
@ -93,6 +93,34 @@ if (props.paginator.computedParams) {
|
|||
}, { immediate: false, deep: true });
|
||||
}
|
||||
|
||||
const upButtonVisible = computed(() => {
|
||||
return props.paginator.order.value === 'oldest' ? props.paginator.canFetchOlder.value : props.paginator.canFetchNewer.value;
|
||||
});
|
||||
const upButtonLoading = computed(() => {
|
||||
return props.paginator.order.value === 'oldest' ? props.paginator.fetchingOlder.value : props.paginator.fetchingNewer.value;
|
||||
});
|
||||
function upButtonClick() {
|
||||
if (props.paginator.order.value === 'oldest') {
|
||||
props.paginator.fetchOlder();
|
||||
} else {
|
||||
props.paginator.fetchNewer({ noTrim: true });
|
||||
}
|
||||
}
|
||||
|
||||
const downButtonVisible = computed(() => {
|
||||
return props.paginator.order.value === 'oldest' ? props.paginator.canFetchNewer.value : props.paginator.canFetchOlder.value;
|
||||
});
|
||||
const downButtonLoading = computed(() => {
|
||||
return props.paginator.order.value === 'oldest' ? props.paginator.fetchingNewer.value : props.paginator.fetchingOlder.value;
|
||||
});
|
||||
function downButtonClick() {
|
||||
if (props.paginator.order.value === 'oldest') {
|
||||
props.paginator.fetchNewer({ noTrim: true });
|
||||
} else {
|
||||
props.paginator.fetchOlder();
|
||||
}
|
||||
}
|
||||
|
||||
defineSlots<{
|
||||
empty: () => void;
|
||||
default: (props: { items: UnwrapRef<T['items']> }) => void;
|
||||
|
|
|
@ -64,7 +64,6 @@ const q = ref<string | null>(null);
|
|||
|
||||
watch(order, () => {
|
||||
props.paginator.order.value = order.value;
|
||||
props.paginator.initialDirection = order.value === 'oldest' ? 'newer' : 'older';
|
||||
props.paginator.reload();
|
||||
});
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ const error = ref();
|
|||
const prevUserPaginator = markRaw(new Paginator('users/notes', {
|
||||
limit: 10,
|
||||
initialId: props.noteId,
|
||||
initialDirection: 'older',
|
||||
computedParams: computed(() => note.value ? ({
|
||||
userId: note.value.userId,
|
||||
}) : undefined),
|
||||
|
@ -91,6 +90,7 @@ const nextUserPaginator = markRaw(new Paginator('users/notes', {
|
|||
limit: 10,
|
||||
initialId: props.noteId,
|
||||
initialDirection: 'newer',
|
||||
direction: 'up',
|
||||
computedParams: computed(() => note.value ? ({
|
||||
userId: note.value.userId,
|
||||
}) : undefined),
|
||||
|
@ -99,7 +99,6 @@ const nextUserPaginator = markRaw(new Paginator('users/notes', {
|
|||
const prevChannelPaginator = markRaw(new Paginator('channels/timeline', {
|
||||
limit: 10,
|
||||
initialId: props.noteId,
|
||||
initialDirection: 'older',
|
||||
computedParams: computed(() => note.value && note.value.channelId != null ? ({
|
||||
channelId: note.value.channelId,
|
||||
}) : undefined),
|
||||
|
@ -109,6 +108,7 @@ const nextChannelPaginator = markRaw(new Paginator('channels/timeline', {
|
|||
limit: 10,
|
||||
initialId: props.noteId,
|
||||
initialDirection: 'newer',
|
||||
direction: 'up',
|
||||
computedParams: computed(() => note.value && note.value.channelId != null ? ({
|
||||
channelId: note.value.channelId,
|
||||
}) : undefined),
|
||||
|
|
|
@ -37,6 +37,7 @@ export interface IPaginator<T = unknown, _T = T & MisskeyEntity> {
|
|||
fetchingOlder: Ref<boolean>;
|
||||
fetchingNewer: Ref<boolean>;
|
||||
canFetchOlder: Ref<boolean>;
|
||||
canFetchNewer: Ref<boolean>;
|
||||
canSearch: boolean;
|
||||
error: Ref<boolean>;
|
||||
computedParams: ComputedRef<Misskey.Endpoints[PaginatorCompatibleEndpointPaths]['req'] | null | undefined> | null;
|
||||
|
@ -45,6 +46,7 @@ export interface IPaginator<T = unknown, _T = T & MisskeyEntity> {
|
|||
initialDirection: 'newer' | 'older';
|
||||
noPaging: boolean;
|
||||
searchQuery: Ref<null | string>;
|
||||
direction: 'up' | 'down' | 'both';
|
||||
order: Ref<'newest' | 'oldest'>;
|
||||
|
||||
init(): Promise<void>;
|
||||
|
@ -77,6 +79,7 @@ export class Paginator<
|
|||
public fetchingOlder = ref(false);
|
||||
public fetchingNewer = ref(false);
|
||||
public canFetchOlder = ref(false);
|
||||
public canFetchNewer = ref(false);
|
||||
public canSearch = false;
|
||||
public error = ref(false);
|
||||
private endpoint: Endpoint;
|
||||
|
@ -85,7 +88,12 @@ export class Paginator<
|
|||
public computedParams: ComputedRef<E['req'] | null | undefined> | null;
|
||||
public initialId: MisskeyEntity['id'] | null = null;
|
||||
public initialDate: number | null = null;
|
||||
|
||||
// 初回読み込み時、initialIdを基準にそれより新しいものを取得するか古いものを取得するか
|
||||
// newer: initialIdより新しいものを取得する
|
||||
// older: initialIdより古いものを取得する (default)
|
||||
public initialDirection: 'newer' | 'older';
|
||||
|
||||
private offsetMode: boolean;
|
||||
public noPaging: boolean;
|
||||
public searchQuery = ref<null | string>('');
|
||||
|
@ -94,6 +102,13 @@ export class Paginator<
|
|||
private aheadQueue: T[] = [];
|
||||
private useShallowRef: SRef;
|
||||
|
||||
// ページネーションを進める方向
|
||||
// up: 上方向
|
||||
// down: 下方向 (default)
|
||||
// both: 双方向
|
||||
// NOTE: この方向はページネーションの方向であって、アイテムの並び順ではない
|
||||
public direction: 'up' | 'down' | 'both' = 'down';
|
||||
|
||||
// 配列内の要素をどのような順序で並べるか
|
||||
// newest: 新しいものが先頭 (default)
|
||||
// oldest: 古いものが先頭
|
||||
|
@ -116,6 +131,8 @@ export class Paginator<
|
|||
initialId?: MisskeyEntity['id'];
|
||||
initialDate?: number | null;
|
||||
initialDirection?: 'newer' | 'older';
|
||||
|
||||
direction?: 'up' | 'down' | 'both';
|
||||
order?: 'newest' | 'oldest';
|
||||
|
||||
// 一部のAPIはさらに遡れる場合でもパフォーマンス上の理由でlimit以下の結果を返す場合があり、その場合はsafe、それ以外はlimitにすることを推奨
|
||||
|
@ -138,6 +155,7 @@ export class Paginator<
|
|||
this.params = props.params ?? {};
|
||||
this.computedParams = props.computedParams ?? null;
|
||||
this.order = ref(props.order ?? 'newest');
|
||||
this.direction = props.direction ?? 'down';
|
||||
this.initialId = props.initialId ?? null;
|
||||
this.initialDate = props.initialDate ?? null;
|
||||
this.initialDirection = props.initialDirection ?? 'older';
|
||||
|
@ -222,15 +240,15 @@ export class Paginator<
|
|||
|
||||
if (this.canFetchDetection === 'limit') {
|
||||
if (apiRes.length < FIRST_FETCH_LIMIT) {
|
||||
this.canFetchOlder.value = false;
|
||||
(this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = false;
|
||||
} else {
|
||||
this.canFetchOlder.value = true;
|
||||
(this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = true;
|
||||
}
|
||||
} else if (this.canFetchDetection === 'safe' || this.canFetchDetection == null) {
|
||||
if (apiRes.length === 0 || this.noPaging) {
|
||||
this.canFetchOlder.value = false;
|
||||
(this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = false;
|
||||
} else {
|
||||
this.canFetchOlder.value = true;
|
||||
(this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,7 +291,11 @@ export class Paginator<
|
|||
if (i === 10) item._shouldInsertAd_ = true;
|
||||
}
|
||||
|
||||
if (this.order.value === 'oldest') {
|
||||
this.unshiftItems(apiRes.toReversed(), true);
|
||||
} else {
|
||||
this.pushItems(apiRes);
|
||||
}
|
||||
|
||||
if (this.canFetchDetection === 'limit') {
|
||||
if (apiRes.length < FIRST_FETCH_LIMIT) {
|
||||
|
@ -325,7 +347,21 @@ export class Paginator<
|
|||
if (this.order.value === 'oldest') {
|
||||
this.pushItems(apiRes);
|
||||
} else {
|
||||
this.unshiftItems(apiRes.toReversed());
|
||||
this.unshiftItems(apiRes.toReversed(), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.canFetchDetection === 'limit') {
|
||||
if (apiRes.length < FIRST_FETCH_LIMIT) {
|
||||
this.canFetchNewer.value = false;
|
||||
} else {
|
||||
this.canFetchNewer.value = true;
|
||||
}
|
||||
} else if (this.canFetchDetection === 'safe' || this.canFetchDetection == null) {
|
||||
if (apiRes.length === 0) {
|
||||
this.canFetchNewer.value = false;
|
||||
} else {
|
||||
this.canFetchNewer.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -336,10 +372,10 @@ export class Paginator<
|
|||
if (this.useShallowRef && trigger) triggerRef(this.items);
|
||||
}
|
||||
|
||||
public unshiftItems(newItems: T[]): void {
|
||||
public unshiftItems(newItems: T[], noTrim = false): void {
|
||||
if (newItems.length === 0) return; // これやらないと余計なre-renderが走る
|
||||
this.items.value.unshift(...newItems.filter(x => !this.items.value.some(y => y.id === x.id))); // ストリーミングやポーリングのタイミングによっては重複することがあるため
|
||||
this.trim(false);
|
||||
if (!noTrim) this.trim(true);
|
||||
if (this.useShallowRef) triggerRef(this.items);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue