Feat: VirtualScrollをTLに実装した
This commit is contained in:
parent
d80f37328d
commit
0e23ac3bb9
|
@ -470,7 +470,6 @@ export class UserEntityService implements OnModuleInit {
|
|||
createdAt: this.idService.parse(announcement.id).date.toISOString(),
|
||||
...announcement,
|
||||
})) : null;
|
||||
console.log(user.getPoints);
|
||||
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
|
||||
const packed = {
|
||||
id: user.id,
|
||||
|
|
2
packages/frontend/@types/global.d.ts
vendored
2
packages/frontend/@types/global.d.ts
vendored
|
@ -14,6 +14,8 @@ declare const _DATA_TRANSFER_DRIVE_FILE_: string;
|
|||
declare const _DATA_TRANSFER_DRIVE_FOLDER_: string;
|
||||
declare const _DATA_TRANSFER_DECK_COLUMN_: string;
|
||||
|
||||
declare module 'vue-virtual-scroller'
|
||||
|
||||
// for dev-mode
|
||||
declare const _LANGS_FULL_: string[][];
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
"v-code-diff": "1.12.0",
|
||||
"vite": "5.3.2",
|
||||
"vue": "3.4.31",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8",
|
||||
"vuedraggable": "next",
|
||||
"wavesurfer.js": "^7.7.14"
|
||||
},
|
||||
|
|
|
@ -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>
|
||||
<MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad">
|
||||
<MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad" :virtualScrollOn="true">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
<img :src="infoImageUrl" class="_ghost"/>
|
||||
|
@ -11,20 +12,24 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template #default="{ items: notes }">
|
||||
<div :class="[$style.root, { [$style.noGap]: noGap }]">
|
||||
<MkDateSeparatedList
|
||||
ref="notes"
|
||||
v-slot="{ item: note }"
|
||||
:items="notes"
|
||||
:direction="pagination.reversed ? 'up' : 'down'"
|
||||
:reversed="pagination.reversed"
|
||||
:noGap="noGap"
|
||||
:ad="true"
|
||||
:class="$style.notes"
|
||||
>
|
||||
<template #default="{ item: note, index, items }">
|
||||
<div :class="[$style.root, { [$style.noGap]: noGap },{ [$style['date-separated-list']]: noGap}]">
|
||||
<div :class="$style.notes,{ [$style['date-separated-list-nogap']]: noGap}" >
|
||||
<p :style="{margin: 0, borderBottom: 'solid 1px var(--divider)'}"></p>
|
||||
<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>
|
||||
</template>
|
||||
</MkPagination>
|
||||
|
@ -33,10 +38,10 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
|
|||
<script lang="ts" setup>
|
||||
import { shallowRef } from 'vue';
|
||||
import MkNote from '@/components/MkNote.vue';
|
||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
const dateTextCache = new Map<string, string>();
|
||||
|
||||
const props = defineProps<{
|
||||
pagination: Paging;
|
||||
|
@ -46,6 +51,20 @@ const props = defineProps<{
|
|||
}>();
|
||||
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({
|
||||
pagingComponent,
|
||||
});
|
||||
|
@ -57,12 +76,16 @@ defineExpose({
|
|||
> .notes {
|
||||
background: var(--panel);
|
||||
}
|
||||
.note{
|
||||
&:not(:last-child) {
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.noGap) {
|
||||
> .notes {
|
||||
background: var(--bg);
|
||||
|
||||
.note {
|
||||
background: var(--panel);
|
||||
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>
|
||||
|
|
|
@ -30,7 +30,24 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
|
|||
</MkButton>
|
||||
<MkLoading v-else class="loading"/>
|
||||
</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">
|
||||
<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 }}
|
||||
|
@ -44,7 +61,7 @@ SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-projectSPDX-License
|
|||
<script lang="ts">
|
||||
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os.js';
|
||||
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
|
||||
import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
|
||||
|
@ -91,9 +108,12 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M
|
|||
<script lang="ts" setup>
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
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<{
|
||||
pagination: Paging;
|
||||
virtualScrollOn?: boolean;
|
||||
disableAutoLoad?: boolean;
|
||||
displayLimit?: number;
|
||||
}>(), {
|
||||
|
|
995
pnpm-lock.yaml
995
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue