1
0
forked from mirror/misskey

Feat: VirtualScrollをTLに実装した

This commit is contained in:
mattyatea 2024-07-17 22:53:47 +09:00
parent d80f37328d
commit 0e23ac3bb9
6 changed files with 519 additions and 660 deletions

View File

@ -470,7 +470,6 @@ export class UserEntityService implements OnModuleInit {
createdAt: this.idService.parse(announcement.id).date.toISOString(), createdAt: this.idService.parse(announcement.id).date.toISOString(),
...announcement, ...announcement,
})) : null; })) : null;
console.log(user.getPoints);
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null; const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
const packed = { const packed = {
id: user.id, id: user.id,

View File

@ -14,6 +14,8 @@ declare const _DATA_TRANSFER_DRIVE_FILE_: string;
declare const _DATA_TRANSFER_DRIVE_FOLDER_: string; declare const _DATA_TRANSFER_DRIVE_FOLDER_: string;
declare const _DATA_TRANSFER_DECK_COLUMN_: string; declare const _DATA_TRANSFER_DECK_COLUMN_: string;
declare module 'vue-virtual-scroller'
// for dev-mode // for dev-mode
declare const _LANGS_FULL_: string[][]; declare const _LANGS_FULL_: string[][];

View File

@ -74,6 +74,7 @@
"v-code-diff": "1.12.0", "v-code-diff": "1.12.0",
"vite": "5.3.2", "vite": "5.3.2",
"vue": "3.4.31", "vue": "3.4.31",
"vue-virtual-scroller": "2.0.0-beta.8",
"vuedraggable": "next", "vuedraggable": "next",
"wavesurfer.js": "^7.7.14" "wavesurfer.js": "^7.7.14"
}, },

View File

@ -1,9 +1,10 @@
<!-- <!--
SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License-Identifier: AGPL-3.0-only SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-project
SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad"> <MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad" :virtualScrollOn="true">
<template #empty> <template #empty>
<div class="_fullinfo"> <div class="_fullinfo">
<img :src="infoImageUrl" class="_ghost"/> <img :src="infoImageUrl" class="_ghost"/>
@ -11,20 +12,24 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
</div> </div>
</template> </template>
<template #default="{ items: notes }"> <template #default="{ item: note, index, items }">
<div :class="[$style.root, { [$style.noGap]: noGap }]"> <div :class="[$style.root, { [$style.noGap]: noGap },{ [$style['date-separated-list']]: noGap}]">
<MkDateSeparatedList <div :class="$style.notes,{ [$style['date-separated-list-nogap']]: noGap}" >
ref="notes" <p :style="{margin: 0, borderBottom: 'solid 1px var(--divider)'}"></p>
v-slot="{ item: note }"
:items="notes"
:direction="pagination.reversed ? 'up' : 'down'"
:reversed="pagination.reversed"
:noGap="noGap"
:ad="true"
:class="$style.notes"
>
<MkNote v-if="props.withCw && !note.cw || !props.withCw" :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/> <MkNote v-if="props.withCw && !note.cw || !props.withCw" :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/>
</MkDateSeparatedList> <div v-if="index !== items.length - 1 && note?.createdAt && items[index + 1]?.createdAt && (new Date(note?.createdAt).getDate()) !== ( new Date(items[index + 1]?.createdAt).getDate())" :key="note.id" :class="$style.separator">
<p :class="$style.date">
<span :class="$style['date-1']">
<i class="ti ti-chevron-up"></i>
{{ getDateText(note.createdAt) }}
</span>
<span :class="$style['date-2']">
{{ getDateText(items[index + 1].createdAt) }}
<i class="ti ti-chevron-down"></i>
</span>
</p>
</div>
</div>
</div> </div>
</template> </template>
</MkPagination> </MkPagination>
@ -33,10 +38,10 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from 'vue'; import { shallowRef } from 'vue';
import MkNote from '@/components/MkNote.vue'; import MkNote from '@/components/MkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js'; import { infoImageUrl } from '@/instance.js';
const dateTextCache = new Map<string, string>();
const props = defineProps<{ const props = defineProps<{
pagination: Paging; pagination: Paging;
@ -46,6 +51,20 @@ const props = defineProps<{
}>(); }>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
function getDateText(time: string) {
if (dateTextCache.has(time)) {
return dateTextCache.get(time)!;
}
const date = new Date(time).getDate();
const month = new Date(time).getMonth() + 1;
const text = i18n.tsx.monthAndDay({
month: month.toString(),
day: date.toString(),
});
dateTextCache.set(time, text);
return text;
}
defineExpose({ defineExpose({
pagingComponent, pagingComponent,
}); });
@ -57,12 +76,16 @@ defineExpose({
> .notes { > .notes {
background: var(--panel); background: var(--panel);
} }
.note{
&:not(:last-child) {
border-bottom: solid 0.5px var(--divider);
}
}
} }
&:not(.noGap) { &:not(.noGap) {
> .notes { > .notes {
background: var(--bg); background: var(--bg);
.note { .note {
background: var(--panel); background: var(--panel);
border-radius: var(--radius); border-radius: var(--radius);
@ -70,4 +93,103 @@ defineExpose({
} }
} }
} }
.date-separated-list {
container-type: inline-size;
&:global {
> .list-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
&.deny-move-transition > .list-move {
transition: none !important;
}
> .list-enter-active {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
> *:empty {
display: none;
}
}
&:not(.date-separated-list-nogap) > *:not(:last-child) {
margin-bottom: var(--margin);
}
}
.date-separated-list-nogap {
> * {
margin: 0 !important;
border: none;
border-radius: 0;
box-shadow: none;
&:not(:last-child) {
border-bottom: solid 0.5px var(--divider);
}
}
}
.direction-up {
&:global {
> .list-enter-from,
> .list-leave-to {
opacity: 0;
transform: translateY(64px);
}
}
}
.direction-down {
&:global {
> .list-enter-from,
> .list-leave-to {
opacity: 0;
transform: translateY(-64px);
}
}
}
.reversed {
display: flex;
flex-direction: column-reverse;
}
.separator {
border-bottom: solid 1px var(--divider);
text-align: center;
}
.date {
display: inline-block;
position: relative;
margin: 0;
padding: 0 16px;
line-height: 32px;
text-align: center;
font-size: 12px;
color: var(--dateLabelFg);
}
.date-1 {
margin-right: 8px;
}
.date-1-icon {
margin-right: 8px;
}
.date-2 {
margin-left: 8px;
}
.date-2-icon {
margin-left: 8px;
}
.before-leave {
position: absolute !important;
}
</style> </style>

View File

@ -30,7 +30,24 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
</MkButton> </MkButton>
<MkLoading v-else class="loading"/> <MkLoading v-else class="loading"/>
</div> </div>
<slot :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot> <DynamicScroller
v-if="props.virtualScrollOn"
:items="Array.from(items.values())"
:minItemSize="110"
:pageMode="true"
:style="{height: '100%'}"
>
<template #default="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:data-index="index"
>
<slot :item="item" :index="index" :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot>
</DynamicScrollerItem>
</template>
</DynamicScroller>
<slot v-else :items="Array.from(items.values())" item="none" :fetching="fetching || moreFetching"></slot>
<div v-show="!pagination.reversed && more" key="_more_" class="_margin"> <div v-show="!pagination.reversed && more" key="_more_" class="_margin">
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary rounded @click="fetchMore"> <MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary rounded @click="fetchMore">
{{ i18n.ts.loadMore }} {{ i18n.ts.loadMore }}
@ -44,7 +61,7 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
<script lang="ts"> <script lang="ts">
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue'; import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
import { useDocumentVisibility } from '@/scripts/use-document-visibility.js'; import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
@ -91,9 +108,12 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M
<script lang="ts" setup> <script lang="ts" setup>
import { infoImageUrl } from '@/instance.js'; import { infoImageUrl } from '@/instance.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
pagination: Paging; pagination: Paging;
virtualScrollOn?: boolean;
disableAutoLoad?: boolean; disableAutoLoad?: boolean;
displayLimit?: number; displayLimit?: number;
}>(), { }>(), {

995
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff