wip
This commit is contained in:
parent
445e52214a
commit
ccfd0ed0ea
|
@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else ref="rootEl">
|
<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
|
<component
|
||||||
:is="prefer.s.animation ? TransitionGroup : 'div'"
|
:is="prefer.s.animation ? TransitionGroup : 'div'"
|
||||||
:class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: paginationQuery.reversed }]"
|
:class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: paginationQuery.reversed }]"
|
||||||
|
@ -50,9 +51,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 * as Misskey from 'misskey-js';
|
||||||
import { useInterval } from '@@/js/use-interval.js';
|
import { useInterval } from '@@/js/use-interval.js';
|
||||||
|
import { scrollToTop } from '@@/js/scroll.js';
|
||||||
import type { BasicTimelineType } from '@/timelines.js';
|
import type { BasicTimelineType } from '@/timelines.js';
|
||||||
import type { PagingCtx } from '@/use/use-pagination.js';
|
import type { PagingCtx } from '@/use/use-pagination.js';
|
||||||
import { usePagination } 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 { prefer } from '@/preferences.js';
|
||||||
import { store } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -87,13 +90,14 @@ const props = withDefaults(defineProps<{
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'queue', count: number): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
provide('inTimeline', true);
|
provide('inTimeline', true);
|
||||||
provide('tl_withSensitive', computed(() => props.withSensitive));
|
provide('tl_withSensitive', computed(() => props.withSensitive));
|
||||||
provide('inChannel', computed(() => props.src === 'channel'));
|
provide('inChannel', computed(() => props.src === 'channel'));
|
||||||
|
|
||||||
|
const rootEl = useTemplateRef('rootEl');
|
||||||
|
|
||||||
type TimelineQueryType = {
|
type TimelineQueryType = {
|
||||||
antennaId?: string,
|
antennaId?: string,
|
||||||
withRenotes?: boolean,
|
withRenotes?: boolean,
|
||||||
|
@ -105,7 +109,7 @@ type TimelineQueryType = {
|
||||||
roleId?: string
|
roleId?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
let tlNotesCount = 0;
|
let noteCounterForAd = 0;
|
||||||
|
|
||||||
const POLLING_INTERVAL = 1000 * 15;
|
const POLLING_INTERVAL = 1000 * 15;
|
||||||
|
|
||||||
|
@ -124,14 +128,28 @@ if (!store.s.realtimeMode) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepend(note) {
|
const notesQueue = ref<Misskey.entities.Note[]>([]);
|
||||||
tlNotesCount++;
|
|
||||||
|
|
||||||
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;
|
note._shouldInsertAd_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isTop = false;
|
||||||
|
if (isTop) {
|
||||||
paginator.prepend(note);
|
paginator.prepend(note);
|
||||||
|
} else {
|
||||||
|
notesQueue.value.unshift(note);
|
||||||
|
}
|
||||||
|
|
||||||
if (props.sound) {
|
if (props.sound) {
|
||||||
sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
||||||
|
@ -315,7 +333,7 @@ onUnmounted(() => {
|
||||||
|
|
||||||
function reloadTimeline() {
|
function reloadTimeline() {
|
||||||
return new Promise<void>((res) => {
|
return new Promise<void>((res) => {
|
||||||
tlNotesCount = 0;
|
noteCounterForAd = 0;
|
||||||
|
|
||||||
paginator.reload().then(() => {
|
paginator.reload().then(() => {
|
||||||
res();
|
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 {
|
.ad:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.more {
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,26 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<div class="_spacer" style="--MI_SPACER-w: 800px;">
|
<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">
|
<div :class="$style.tl">
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlEl" :key="antennaId"
|
ref="tlEl" :key="antennaId"
|
||||||
src="antenna"
|
src="antenna"
|
||||||
:antenna="antennaId"
|
:antenna="antennaId"
|
||||||
:sound="true"
|
:sound="true"
|
||||||
@queue="queueUpdated"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, ref, useTemplateRef } from 'vue';
|
import { computed, watch, ref, useTemplateRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { scrollInContainer } from '@@/js/scroll.js';
|
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -40,18 +35,8 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const antenna = ref<Misskey.entities.Antenna | null>(null);
|
const antenna = ref<Misskey.entities.Antenna | null>(null);
|
||||||
const queue = ref(0);
|
|
||||||
const rootEl = useTemplateRef('rootEl');
|
|
||||||
const tlEl = useTemplateRef('tlEl');
|
const tlEl = useTemplateRef('tlEl');
|
||||||
|
|
||||||
function queueUpdated(q) {
|
|
||||||
queue.value = q;
|
|
||||||
}
|
|
||||||
|
|
||||||
function top() {
|
|
||||||
scrollInContainer(rootEl.value, { top: 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function timetravel() {
|
async function timetravel() {
|
||||||
const { canceled, result: date } = await os.inputDate({
|
const { canceled, result: date } = await os.inputDate({
|
||||||
title: i18n.ts.date,
|
title: i18n.ts.date,
|
||||||
|
@ -94,25 +79,6 @@ definePage(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<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 {
|
.tl {
|
||||||
background: var(--MI_THEME-bg);
|
background: var(--MI_THEME-bg);
|
||||||
border-radius: var(--MI-radius);
|
border-radius: var(--MI-radius);
|
||||||
|
|
|
@ -4,13 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<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;">
|
<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()">
|
<MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
|
||||||
{{ i18n.ts._timelineDescription[src] }}
|
{{ i18n.ts._timelineDescription[src] }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
<MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/>
|
<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
|
<MkTimeline
|
||||||
ref="tlComponent"
|
ref="tlComponent"
|
||||||
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
||||||
|
@ -22,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:withSensitive="withSensitive"
|
:withSensitive="withSensitive"
|
||||||
:onlyFiles="onlyFiles"
|
:onlyFiles="onlyFiles"
|
||||||
:sound="true"
|
:sound="true"
|
||||||
@queue="queueUpdated"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
|
@ -51,11 +49,9 @@ import { prefer } from '@/preferences.js';
|
||||||
provide('shouldOmitHeaderTitle', true);
|
provide('shouldOmitHeaderTitle', true);
|
||||||
|
|
||||||
const tlComponent = useTemplateRef('tlComponent');
|
const tlComponent = useTemplateRef('tlComponent');
|
||||||
const pageComponent = useTemplateRef('pageComponent');
|
|
||||||
|
|
||||||
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
|
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
|
||||||
|
|
||||||
const queue = ref(0);
|
|
||||||
const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global');
|
const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global');
|
||||||
const src = computed<TimelinePageSrc>({
|
const src = computed<TimelinePageSrc>({
|
||||||
get: () => ($i ? store.r.tl.value.src : srcWhenNotSignin.value),
|
get: () => ($i ? store.r.tl.value.src : srcWhenNotSignin.value),
|
||||||
|
@ -110,18 +106,6 @@ const withSensitive = computed<boolean>({
|
||||||
set: (x) => saveTlFilter('withSensitive', x),
|
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> {
|
async function chooseList(ev: MouseEvent): Promise<void> {
|
||||||
const lists = await userListsCache.fetch();
|
const lists = await userListsCache.fetch();
|
||||||
const items: MenuItem[] = [
|
const items: MenuItem[] = [
|
||||||
|
|
|
@ -6,26 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<div class="_spacer" style="--MI_SPACER-w: 800px;">
|
<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">
|
<div :class="$style.tl">
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlEl" :key="listId"
|
ref="tlEl" :key="listId"
|
||||||
src="list"
|
src="list"
|
||||||
:list="listId"
|
:list="listId"
|
||||||
:sound="true"
|
:sound="true"
|
||||||
@queue="queueUpdated"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, ref, useTemplateRef } from 'vue';
|
import { computed, watch, ref, useTemplateRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { scrollInContainer } from '@@/js/scroll.js';
|
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
|
@ -39,9 +34,6 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const list = ref<Misskey.entities.UserList | null>(null);
|
const list = ref<Misskey.entities.UserList | null>(null);
|
||||||
const queue = ref(0);
|
|
||||||
const tlEl = useTemplateRef('tlEl');
|
|
||||||
const rootEl = useTemplateRef('rootEl');
|
|
||||||
|
|
||||||
watch(() => props.listId, async () => {
|
watch(() => props.listId, async () => {
|
||||||
list.value = await misskeyApi('users/lists/show', {
|
list.value = await misskeyApi('users/lists/show', {
|
||||||
|
@ -49,14 +41,6 @@ watch(() => props.listId, async () => {
|
||||||
});
|
});
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
function queueUpdated(q) {
|
|
||||||
queue.value = q;
|
|
||||||
}
|
|
||||||
|
|
||||||
function top() {
|
|
||||||
scrollInContainer(rootEl.value, { top: 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
function settings() {
|
function settings() {
|
||||||
router.push(`/my/lists/${props.listId}`);
|
router.push(`/my/lists/${props.listId}`);
|
||||||
}
|
}
|
||||||
|
@ -76,25 +60,6 @@ definePage(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<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 {
|
.tl {
|
||||||
background: var(--MI_THEME-bg);
|
background: var(--MI_THEME-bg);
|
||||||
border-radius: var(--MI-radius);
|
border-radius: var(--MI-radius);
|
||||||
|
|
Loading…
Reference in New Issue