forked from mirror/misskey
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(),
|
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,
|
||||||
|
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_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[][];
|
||||||
|
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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>
|
||||||
|
@ -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
995
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user