mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-14 09:55:38 +02:00
refactor(frontend): improve pagination implementation
This commit is contained in:
@@ -41,13 +41,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" :spellcheck="false">
|
||||
<span>{{ i18n.ts.username }}</span>
|
||||
</MkInput>
|
||||
<MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params().origin === 'local'">
|
||||
<MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" :spellcheck="false" :disabled="paginator.computedParams.value.origin === 'local'">
|
||||
<span>{{ i18n.ts.host }}</span>
|
||||
</MkInput>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<MkPagination v-slot="{items}" ref="reports" :pagination="pagination">
|
||||
<MkPagination v-slot="{items}" :paginator="paginator">
|
||||
<div class="_gaps">
|
||||
<XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useTemplateRef, ref } from 'vue';
|
||||
import { computed, ref, markRaw } from 'vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import XAbuseReport from '@/components/MkAbuseReport.vue';
|
||||
@@ -66,8 +66,7 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { store } from '@/store.js';
|
||||
|
||||
const reports = useTemplateRef('reports');
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const state = ref('unresolved');
|
||||
const reporterOrigin = ref('combined');
|
||||
@@ -75,18 +74,17 @@ const targetUserOrigin = ref('combined');
|
||||
const searchUsername = ref('');
|
||||
const searchHost = ref('');
|
||||
|
||||
const pagination = {
|
||||
endpoint: 'admin/abuse-user-reports' as const,
|
||||
const paginator = markRaw(new Paginator('admin/abuse-user-reports', {
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
state: state.value,
|
||||
reporterOrigin: reporterOrigin.value,
|
||||
targetUserOrigin: targetUserOrigin.value,
|
||||
})),
|
||||
};
|
||||
}));
|
||||
|
||||
function resolved(reportId) {
|
||||
reports.value?.paginator.removeItem(reportId);
|
||||
paginator.removeItem(reportId);
|
||||
}
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</FormSplit>
|
||||
</div>
|
||||
|
||||
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
||||
<MkPagination v-slot="{items}" :key="host + state" :paginator="paginator">
|
||||
<div :class="$style.instances">
|
||||
<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`">
|
||||
<MkInstanceCardMini :instance="instance"/>
|
||||
@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, markRaw, ref } from 'vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
@@ -64,15 +64,15 @@ import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const host = ref('');
|
||||
const state = ref('federating');
|
||||
const sort = ref('+pubSub');
|
||||
const pagination = {
|
||||
endpoint: 'federation/instances' as const,
|
||||
const paginator = markRaw(new Paginator('federation/instances', {
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
sort: sort.value,
|
||||
host: host.value !== '' ? host.value : null,
|
||||
...(
|
||||
@@ -85,7 +85,7 @@ const pagination = {
|
||||
state.value === 'notResponding' ? { notResponding: true } :
|
||||
{}),
|
||||
})),
|
||||
};
|
||||
}));
|
||||
|
||||
function getStatus(instance: Misskey.entities.FederationInstance) {
|
||||
switch (instance.suspensionState) {
|
||||
|
||||
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<option value="local">{{ i18n.ts.local }}</option>
|
||||
<option value="remote">{{ i18n.ts.remote }}</option>
|
||||
</MkSelect>
|
||||
<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'">
|
||||
<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="paginator.computedParams.value.origin === 'local'">
|
||||
<template #label>{{ i18n.ts.host }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
@@ -26,14 +26,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>MIME type</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
<MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/>
|
||||
<MkFileListForAdmin :paginator="paginator" :viewMode="viewMode"/>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, markRaw, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
@@ -42,23 +42,22 @@ import * as os from '@/os.js';
|
||||
import { lookupFile } from '@/utility/admin-lookup.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import type { PagingCtx } from '@/composables/use-pagination.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const origin = ref<Misskey.entities.AdminDriveFilesRequest['origin']>('local');
|
||||
const type = ref<string | null>(null);
|
||||
const searchHost = ref('');
|
||||
const userId = ref('');
|
||||
const viewMode = ref<'grid' | 'list'>('grid');
|
||||
const pagination = {
|
||||
endpoint: 'admin/drive/files' as const,
|
||||
const paginator = markRaw(new Paginator('admin/drive/files', {
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
type: (type.value && type.value !== '') ? type.value : null,
|
||||
userId: (userId.value && userId.value !== '') ? userId.value : null,
|
||||
origin: origin.value,
|
||||
hostname: (searchHost.value && searchHost.value !== '') ? searchHost.value : null,
|
||||
})),
|
||||
} satisfies PagingCtx<'admin/drive/files'>;
|
||||
}));
|
||||
|
||||
function clear() {
|
||||
os.confirm({
|
||||
|
||||
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-if="!noExpirationDate" v-model="expiresAt" type="datetime-local">
|
||||
<template #label>{{ i18n.ts.expirationDate }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="createCount" type="number" min="1">
|
||||
<MkInput v-model="createCount" type="number" :min="1">
|
||||
<template #label>{{ i18n.ts.createCount }}</template>
|
||||
</MkInput>
|
||||
<MkButton primary rounded @click="createWithOptions">{{ i18n.ts.create }}</MkButton>
|
||||
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<option value="-usedAt">{{ i18n.ts.usedAt }} ({{ i18n.ts.descendingOrder }})</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<MkPagination ref="pagingComponent" :pagination="pagination">
|
||||
<MkPagination :paginator="paginator">
|
||||
<template #default="{ items }">
|
||||
<div class="_gaps_s">
|
||||
<MkInviteCode v-for="item in items" :key="item.id" :invite="(item as any)" :onDeleted="deleted" moderator/>
|
||||
@@ -54,8 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, useTemplateRef } from 'vue';
|
||||
import type { PagingCtx } from '@/composables/use-pagination.js';
|
||||
import { computed, markRaw, ref, useTemplateRef } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
@@ -67,21 +66,19 @@ import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import MkInviteCode from '@/components/MkInviteCode.vue';
|
||||
import { definePage } from '@/page.js';
|
||||
|
||||
const pagingComponent = useTemplateRef('pagingComponent');
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const type = ref('all');
|
||||
const sort = ref('+createdAt');
|
||||
|
||||
const pagination: PagingCtx = {
|
||||
endpoint: 'admin/invite/list' as const,
|
||||
const paginator = markRaw(new Paginator('admin/invite/list', {
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
type: type.value,
|
||||
sort: sort.value,
|
||||
})),
|
||||
offsetMode: true,
|
||||
};
|
||||
}));
|
||||
|
||||
const expiresAt = ref('');
|
||||
const noExpirationDate = ref(true);
|
||||
@@ -100,13 +97,11 @@ async function createWithOptions() {
|
||||
text: tickets.map(x => x.code).join('\n'),
|
||||
});
|
||||
|
||||
tickets.forEach(ticket => pagingComponent.value?.paginator.prepend(ticket));
|
||||
tickets.forEach(ticket => paginator.prepend(ticket));
|
||||
}
|
||||
|
||||
function deleted(id: string) {
|
||||
if (pagingComponent.value) {
|
||||
pagingComponent.value.paginator.removeItem(id);
|
||||
}
|
||||
paginator.removeItem(id);
|
||||
}
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<div class="_gaps">
|
||||
<MkPaginationControl v-model:order="order" v-model:date="date" v-model:q="q" canSearch canFilter @reload="paginator.reload()">
|
||||
<MkPaginationControl :paginator="paginator" canFilter>
|
||||
<MkSelect v-model="type" style="margin: 0; flex: 1;">
|
||||
<template #label>{{ i18n.ts.type }}</template>
|
||||
<option :value="null">{{ i18n.ts.all }}</option>
|
||||
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useTemplateRef, ref, watch } from 'vue';
|
||||
import { computed, ref, markRaw, onMounted } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XModLog from './modlog.ModLog.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
@@ -54,37 +54,25 @@ import MkTl from '@/components/MkTl.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { usePagination } from '@/composables/use-pagination.js';
|
||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkPaginationControl from '@/components/MkPaginationControl.vue';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const order = ref<'newest' | 'oldest'>('newest');
|
||||
const date = ref<number | null>(null);
|
||||
const type = ref<string | null>(null);
|
||||
const moderatorId = ref('');
|
||||
const q = ref<string | null>(null);
|
||||
|
||||
const paginator = usePagination({
|
||||
ctx: {
|
||||
endpoint: 'admin/show-moderation-logs',
|
||||
limit: 20,
|
||||
canFetchDetection: 'limit',
|
||||
params: computed(() => ({
|
||||
type: type.value,
|
||||
userId: moderatorId.value === '' ? null : moderatorId.value,
|
||||
search: q.value,
|
||||
})),
|
||||
},
|
||||
});
|
||||
const paginator = markRaw(new Paginator('admin/show-moderation-logs', {
|
||||
limit: 20,
|
||||
canFetchDetection: 'limit',
|
||||
canSearch: true,
|
||||
computedParams: computed(() => ({
|
||||
type: type.value,
|
||||
userId: moderatorId.value === '' ? null : moderatorId.value,
|
||||
})),
|
||||
}));
|
||||
|
||||
watch([order, date], () => {
|
||||
paginator.updateCtxPartial({
|
||||
order: order.value,
|
||||
initialDirection: order.value === 'oldest' ? 'newer' : 'older',
|
||||
initialDate: date.value,
|
||||
});
|
||||
}, { immediate: false });
|
||||
paginator.init();
|
||||
|
||||
const timeline = computed(() => {
|
||||
return paginator.items.value.map(x => ({
|
||||
@@ -95,7 +83,7 @@ const timeline = computed(() => {
|
||||
});
|
||||
|
||||
function fetchMore() {
|
||||
if (order.value === 'oldest') {
|
||||
if (paginator.order.value === 'oldest') {
|
||||
paginator.fetchNewer();
|
||||
} else {
|
||||
paginator.fetchOlder();
|
||||
|
||||
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_gaps">
|
||||
<MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
|
||||
|
||||
<MkPagination :pagination="usersPagination">
|
||||
<MkPagination :paginator="usersPaginator">
|
||||
<template #empty><MkResult type="empty" :text="i18n.ts.noUsers"/></template>
|
||||
|
||||
<template #default="{ items }">
|
||||
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { computed, markRaw, reactive, ref } from 'vue';
|
||||
import XEditor from './roles.editor.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import * as os from '@/os.js';
|
||||
@@ -66,6 +66,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -73,13 +74,12 @@ const props = defineProps<{
|
||||
id?: string;
|
||||
}>();
|
||||
|
||||
const usersPagination = {
|
||||
endpoint: 'admin/roles/users' as const,
|
||||
const usersPaginator = markRaw(new Paginator('admin/roles/users', {
|
||||
limit: 20,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
roleId: props.id,
|
||||
})),
|
||||
};
|
||||
}));
|
||||
|
||||
const expandedItems = ref([]);
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #prefix>@</template>
|
||||
<template #label>{{ i18n.ts.username }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'">
|
||||
<MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="paginator.computedParams.value.origin === 'local'">
|
||||
<template #prefix>@</template>
|
||||
<template #label>{{ i18n.ts.host }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
|
||||
<MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination">
|
||||
<MkPagination v-slot="{items}" :paginator="paginator">
|
||||
<div :class="$style.users">
|
||||
<MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" :class="$style.user" :to="`/admin/user/${user.id}`">
|
||||
<MkUserCardMini :user="user"/>
|
||||
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useTemplateRef, ref, watchEffect } from 'vue';
|
||||
import { computed, markRaw, ref, watchEffect } from 'vue';
|
||||
import { defaultMemoryStorage } from '@/memory-storage';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
@@ -69,6 +69,7 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
import { dateString } from '@/filters/date.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
type SearchQuery = {
|
||||
sort?: string;
|
||||
@@ -78,7 +79,6 @@ type SearchQuery = {
|
||||
hostname?: string;
|
||||
};
|
||||
|
||||
const paginationComponent = useTemplateRef('paginationComponent');
|
||||
const storedQuery = JSON.parse(defaultMemoryStorage.getItem('admin-users-query') ?? '{}') as SearchQuery;
|
||||
|
||||
const sort = ref(storedQuery.sort ?? '+createdAt');
|
||||
@@ -86,10 +86,9 @@ const state = ref(storedQuery.state ?? 'all');
|
||||
const origin = ref(storedQuery.origin ?? 'local');
|
||||
const searchUsername = ref(storedQuery.username ?? '');
|
||||
const searchHost = ref(storedQuery.hostname ?? '');
|
||||
const pagination = {
|
||||
endpoint: 'admin/show-users' as const,
|
||||
const paginator = markRaw(new Paginator('admin/show-users', {
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
computedParams: computed(() => ({
|
||||
sort: sort.value,
|
||||
state: state.value,
|
||||
origin: origin.value,
|
||||
@@ -97,7 +96,7 @@ const pagination = {
|
||||
hostname: searchHost.value,
|
||||
})),
|
||||
offsetMode: true,
|
||||
};
|
||||
}));
|
||||
|
||||
function searchUser() {
|
||||
os.selectUser({ includeSelf: true }).then(user => {
|
||||
@@ -121,7 +120,7 @@ async function addUser() {
|
||||
username: username,
|
||||
password: password,
|
||||
}).then(res => {
|
||||
paginationComponent.value?.paginator.reload();
|
||||
paginator.reload();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user