This commit is contained in:
syuilo 2025-04-29 12:16:32 +09:00
parent 445e52214a
commit ccfd0ed0ea
4 changed files with 67 additions and 108 deletions

View File

@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else ref="rootEl">
<div v-if="notesQueue.length > 0" :class="$style.new" @click="releaseQueue()"><button class="_button" :class="$style.newButton">{{ i18n.ts.newNoteRecived }}</button></div>
<component
:is="prefer.s.animation ? TransitionGroup : 'div'"
:class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: paginationQuery.reversed }]"
@ -50,9 +51,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup, onMounted } from 'vue';
import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup, onMounted, shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { useInterval } from '@@/js/use-interval.js';
import { scrollToTop } from '@@/js/scroll.js';
import type { BasicTimelineType } from '@/timelines.js';
import type { PagingCtx } from '@/use/use-pagination.js';
import { usePagination } from '@/use/use-pagination.js';
@ -64,6 +66,7 @@ import { instance } from '@/instance.js';
import { prefer } from '@/preferences.js';
import { store } from '@/store.js';
import MkNote from '@/components/MkNote.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@ -87,13 +90,14 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
(ev: 'queue', count: number): void;
}>();
provide('inTimeline', true);
provide('tl_withSensitive', computed(() => props.withSensitive));
provide('inChannel', computed(() => props.src === 'channel'));
const rootEl = useTemplateRef('rootEl');
type TimelineQueryType = {
antennaId?: string,
withRenotes?: boolean,
@ -105,7 +109,7 @@ type TimelineQueryType = {
roleId?: string
};
let tlNotesCount = 0;
let noteCounterForAd = 0;
const POLLING_INTERVAL = 1000 * 15;
@ -124,14 +128,28 @@ if (!store.s.realtimeMode) {
});
}
function prepend(note) {
tlNotesCount++;
const notesQueue = ref<Misskey.entities.Note[]>([]);
if (instance.notesPerOneAd > 0 && tlNotesCount % instance.notesPerOneAd === 0) {
function releaseQueue() {
paginator.unshiftItems(notesQueue.value);
notesQueue.value = [];
scrollToTop(rootEl.value);
}
function prepend(note: Misskey.entities.Note) {
noteCounterForAd++;
if (instance.notesPerOneAd > 0 && noteCounterForAd % instance.notesPerOneAd === 0) {
note._shouldInsertAd_ = true;
}
paginator.prepend(note);
const isTop = false;
if (isTop) {
paginator.prepend(note);
} else {
notesQueue.value.unshift(note);
}
if (props.sound) {
sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note');
@ -315,7 +333,7 @@ onUnmounted(() => {
function reloadTimeline() {
return new Promise<void>((res) => {
tlNotesCount = 0;
noteCounterForAd = 0;
paginator.reload().then(() => {
res();
@ -376,7 +394,33 @@ defineExpose({
}
}
.new {
position: sticky;
top: var(--MI-stickyTop, 0px);
z-index: 1000;
width: 100%;
box-sizing: border-box;
padding: 8px 0;
-webkit-backdrop-filter: var(--MI-blur, blur(4px));
backdrop-filter: var(--MI-blur, blur(4px));
}
.newButton {
display: block;
padding: 8px 16px;
border-radius: 999px;
width: max-content;
margin: auto;
background: var(--MI_THEME-accent);
color: var(--MI_THEME-fgOnAccent);
font-size: 90%;
}
.ad:empty {
display: none;
}
.more {
margin: 16px auto;
}
</style>

View File

@ -6,17 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<div ref="rootEl">
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div :class="$style.tl">
<MkTimeline
ref="tlEl" :key="antennaId"
src="antenna"
:antenna="antennaId"
:sound="true"
@queue="queueUpdated"
/>
</div>
<div :class="$style.tl">
<MkTimeline
ref="tlEl" :key="antennaId"
src="antenna"
:antenna="antennaId"
:sound="true"
/>
</div>
</div>
</PageWithHeader>
@ -25,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, watch, ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { scrollInContainer } from '@@/js/scroll.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@ -40,18 +35,8 @@ const props = defineProps<{
}>();
const antenna = ref<Misskey.entities.Antenna | null>(null);
const queue = ref(0);
const rootEl = useTemplateRef('rootEl');
const tlEl = useTemplateRef('tlEl');
function queueUpdated(q) {
queue.value = q;
}
function top() {
scrollInContainer(rootEl.value, { top: 0 });
}
async function timetravel() {
const { canceled, result: date } = await os.inputDate({
title: i18n.ts.date,
@ -94,25 +79,6 @@ definePage(() => ({
</script>
<style lang="scss" module>
.new {
position: sticky;
top: calc(var(--MI-stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
margin: calc(-0.675em - 8px) 0;
&:first-child {
margin-top: calc(-0.675em - 8px - var(--MI-margin));
}
}
.newButton {
display: block;
margin: var(--MI-margin) auto 0 auto;
padding: 8px 16px;
border-radius: 32px;
}
.tl {
background: var(--MI_THEME-bg);
border-radius: var(--MI-radius);

View File

@ -4,13 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<PageWithHeader ref="pageComponent" v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true">
<PageWithHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
{{ i18n.ts._timelineDescription[src] }}
</MkInfo>
<MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/>
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<MkTimeline
ref="tlComponent"
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
@ -22,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:withSensitive="withSensitive"
:onlyFiles="onlyFiles"
:sound="true"
@queue="queueUpdated"
/>
</div>
</PageWithHeader>
@ -51,11 +49,9 @@ import { prefer } from '@/preferences.js';
provide('shouldOmitHeaderTitle', true);
const tlComponent = useTemplateRef('tlComponent');
const pageComponent = useTemplateRef('pageComponent');
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
const queue = ref(0);
const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global');
const src = computed<TimelinePageSrc>({
get: () => ($i ? store.r.tl.value.src : srcWhenNotSignin.value),
@ -110,18 +106,6 @@ const withSensitive = computed<boolean>({
set: (x) => saveTlFilter('withSensitive', x),
});
watch(src, () => {
queue.value = 0;
});
function queueUpdated(q: number): void {
queue.value = q;
}
function top(): void {
if (pageComponent.value) pageComponent.value.scrollToTop();
}
async function chooseList(ev: MouseEvent): Promise<void> {
const lists = await userListsCache.fetch();
const items: MenuItem[] = [

View File

@ -6,17 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<div ref="rootEl">
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div :class="$style.tl">
<MkTimeline
ref="tlEl" :key="listId"
src="list"
:list="listId"
:sound="true"
@queue="queueUpdated"
/>
</div>
<div :class="$style.tl">
<MkTimeline
ref="tlEl" :key="listId"
src="list"
:list="listId"
:sound="true"
/>
</div>
</div>
</PageWithHeader>
@ -25,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, watch, ref, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { scrollInContainer } from '@@/js/scroll.js';
import MkTimeline from '@/components/MkTimeline.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
@ -39,9 +34,6 @@ const props = defineProps<{
}>();
const list = ref<Misskey.entities.UserList | null>(null);
const queue = ref(0);
const tlEl = useTemplateRef('tlEl');
const rootEl = useTemplateRef('rootEl');
watch(() => props.listId, async () => {
list.value = await misskeyApi('users/lists/show', {
@ -49,14 +41,6 @@ watch(() => props.listId, async () => {
});
}, { immediate: true });
function queueUpdated(q) {
queue.value = q;
}
function top() {
scrollInContainer(rootEl.value, { top: 0 });
}
function settings() {
router.push(`/my/lists/${props.listId}`);
}
@ -76,25 +60,6 @@ definePage(() => ({
</script>
<style lang="scss" module>
.new {
position: sticky;
top: calc(var(--MI-stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
margin: calc(-0.675em - 8px) 0;
&:first-child {
margin-top: calc(-0.675em - 8px - var(--MI-margin));
}
}
.newButton {
display: block;
margin: var(--MI-margin) auto 0 auto;
padding: 8px 16px;
border-radius: 32px;
}
.tl {
background: var(--MI_THEME-bg);
border-radius: var(--MI-radius);