✌️
This commit is contained in:
parent
b4d532efb4
commit
035c98dc15
7 changed files with 63 additions and 38 deletions
2
locales/index.d.ts
vendored
2
locales/index.d.ts
vendored
|
@ -527,7 +527,7 @@ export interface Locale {
|
||||||
"deleteAll": string;
|
"deleteAll": string;
|
||||||
"showFixedPostForm": string;
|
"showFixedPostForm": string;
|
||||||
"showFixedPostFormInChannel": string;
|
"showFixedPostFormInChannel": string;
|
||||||
"newNoteRecived": string;
|
"goToTheHeadOfTimeline": string;
|
||||||
"sounds": string;
|
"sounds": string;
|
||||||
"sound": string;
|
"sound": string;
|
||||||
"listen": string;
|
"listen": string;
|
||||||
|
|
|
@ -524,7 +524,7 @@ serverLogs: "サーバーログ"
|
||||||
deleteAll: "全て削除"
|
deleteAll: "全て削除"
|
||||||
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
|
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
|
||||||
showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)"
|
showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)"
|
||||||
newNoteRecived: "新しいノートがあります"
|
goToTheHeadOfTimeline: "最新のノートに移動"
|
||||||
sounds: "サウンド"
|
sounds: "サウンド"
|
||||||
sound: "サウンド"
|
sound: "サウンド"
|
||||||
listen: "聴く"
|
listen: "聴く"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
>
|
>
|
||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
|
|
||||||
<MkError v-else-if="error" @retry="init()"/>
|
<MkError v-else-if="error" @retry="reload()"/>
|
||||||
|
|
||||||
<div v-else-if="empty" key="_empty_" class="empty">
|
<div v-else-if="empty" key="_empty_" class="empty">
|
||||||
<slot name="empty">
|
<slot name="empty">
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
|
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
|
||||||
import * as misskey from 'misskey-js';
|
import * as misskey from 'misskey-js';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { isBottomVisible, isTopVisible, getBodyScrollHeight, getScrollContainer, scrollToBottom, scroll } from '@/scripts/scroll';
|
import { isBottomVisible, isTopVisible, getBodyScrollHeight, getScrollContainer, scrollToBottom, scroll, scrollToTop } from '@/scripts/scroll';
|
||||||
import { useDocumentVisibility } from '@/scripts/use-document-visibility';
|
import { useDocumentVisibility } from '@/scripts/use-document-visibility';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
@ -125,7 +125,7 @@ const items = ref<MisskeyEntityMap>(new Map());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* タブが非アクティブなどの場合に更新を貯めておく
|
* タブが非アクティブなどの場合に更新を貯めておく
|
||||||
* 最新が0番目
|
* 最新が最後(パフォーマンス上の理由でitemsと逆にした)
|
||||||
*/
|
*/
|
||||||
const queue = ref<MisskeyEntityMap>(new Map());
|
const queue = ref<MisskeyEntityMap>(new Map());
|
||||||
|
|
||||||
|
@ -231,15 +231,17 @@ watch([$$(weakBacked), $$(contentEl)], () => {
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (props.pagination.params && isRef(props.pagination.params)) {
|
|
||||||
watch(props.pagination.params, init, { deep: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(queue, (a, b) => {
|
watch(queue, (a, b) => {
|
||||||
if (a.size === 0 && b.size === 0) return;
|
if (a.size === 0 && b.size === 0) return;
|
||||||
emit('queue', queue.value.size);
|
emit('queue', queue.value.size);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初期化
|
||||||
|
* scrollAfterInitなどの後処理もあるので、reload関数を使うべき
|
||||||
|
*
|
||||||
|
* 注意: moreFetchingをtrueにするのでfalseにする必要がある
|
||||||
|
*/
|
||||||
async function init(): Promise<void> {
|
async function init(): Promise<void> {
|
||||||
items.value = new Map();
|
items.value = new Map();
|
||||||
queue.value = new Map();
|
queue.value = new Map();
|
||||||
|
@ -258,7 +260,7 @@ async function init(): Promise<void> {
|
||||||
concatItems(res);
|
concatItems(res);
|
||||||
more.value = false;
|
more.value = false;
|
||||||
} else {
|
} else {
|
||||||
if (props.pagination.reversed) moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
concatItems(res);
|
concatItems(res);
|
||||||
more.value = true;
|
more.value = true;
|
||||||
}
|
}
|
||||||
|
@ -272,10 +274,43 @@ async function init(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const reload = (): Promise<void> => {
|
/**
|
||||||
return init();
|
* initの後に呼ぶ
|
||||||
|
* コンポーネント作成直後でinitが呼ばれた時はonMountedで呼ばれる
|
||||||
|
* reloadでinitが呼ばれた時はreload内でinitの後に呼ばれる
|
||||||
|
*/
|
||||||
|
function scrollAfterInit() {
|
||||||
|
if (props.pagination.reversed) {
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (contentEl) scrollToBottom(contentEl);
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
// scrollToBottomでmoreFetchingボタンが画面外まで出るまで
|
||||||
|
// more = trueを遅らせる
|
||||||
|
setTimeout(() => {
|
||||||
|
moreFetching.value = false;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (contentEl) scrollToTop(contentEl);
|
||||||
|
moreFetching.value = false;
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const reload = async (): Promise<void> => {
|
||||||
|
await init();
|
||||||
|
scrollAfterInit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (props.pagination.params && isRef(props.pagination.params)) {
|
||||||
|
watch(props.pagination.params, reload, { deep: true });
|
||||||
|
}
|
||||||
|
|
||||||
const fetchMore = async (): Promise<void> => {
|
const fetchMore = async (): Promise<void> => {
|
||||||
if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;
|
if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;
|
||||||
moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
|
@ -472,12 +507,12 @@ function concatItems(oldItems: MisskeyEntity[]) {
|
||||||
|
|
||||||
function executeQueue() {
|
function executeQueue() {
|
||||||
const queueArr = Array.from(queue.value.entries());
|
const queueArr = Array.from(queue.value.entries());
|
||||||
unshiftItems(queueArr.slice(-1 * props.pagination.limit).map(v => v[1]));
|
unshiftItems(queueArr.slice(0, props.pagination.limit).map(v => v[1]).reverse());
|
||||||
queue.value = new Map(queueArr.slice(0, -1 * props.pagination.limit));
|
queue.value = new Map(queueArr.slice(props.pagination.limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
function prependQueue(newItem: MisskeyEntity) {
|
function prependQueue(newItem: MisskeyEntity) {
|
||||||
queue.value = new Map([[newItem.id, newItem], ...queue.value] as [string, MisskeyEntity][]);
|
queue.value.set(newItem.id, newItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -510,24 +545,8 @@ onDeactivated(() => {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
});
|
});
|
||||||
|
|
||||||
function toBottom() {
|
|
||||||
scrollToBottom(contentEl!);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
inited.then(() => {
|
inited.then(scrollAfterInit);
|
||||||
if (props.pagination.reversed) {
|
|
||||||
nextTick(() => {
|
|
||||||
setTimeout(toBottom, 800);
|
|
||||||
|
|
||||||
// scrollToBottomでmoreFetchingボタンが画面外まで出るまで
|
|
||||||
// more = trueを遅らせる
|
|
||||||
setTimeout(() => {
|
|
||||||
moreFetching.value = false;
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|
|
@ -164,4 +164,10 @@ const timetravel = (date?: Date) => {
|
||||||
this.$refs.tl.reload();
|
this.$refs.tl.reload();
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
reload: () => {
|
||||||
|
tlComponent.pagingComponent?.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div ref="rootEl" v-hotkey.global="keymap">
|
<div ref="rootEl" v-hotkey.global="keymap">
|
||||||
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.goToTheHeadOfTimeline }}</button></div>
|
||||||
<div :class="$style.tl">
|
<div :class="$style.tl">
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlEl" :key="antennaId"
|
ref="tlEl" :key="antennaId"
|
||||||
|
@ -46,7 +46,7 @@ function queueUpdated(q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top() {
|
function top() {
|
||||||
scroll(rootEl, { top: 0 });
|
tlEl?.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function timetravel() {
|
async function timetravel() {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/>
|
<XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/>
|
||||||
<MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/>
|
<MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/>
|
||||||
|
|
||||||
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.goToTheHeadOfTimeline }}</button></div>
|
||||||
<div :class="$style.tl">
|
<div :class="$style.tl">
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlComponent"
|
ref="tlComponent"
|
||||||
|
@ -58,7 +58,7 @@ function queueUpdated(q: number): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top(): void {
|
function top(): void {
|
||||||
if (rootEl) scroll(rootEl, { top: 0 });
|
tlComponent?.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function chooseList(ev: MouseEvent): Promise<void> {
|
async function chooseList(ev: MouseEvent): Promise<void> {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div ref="rootEl">
|
<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 v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.goToTheHeadOfTimeline }}</button></div>
|
||||||
<div :class="$style.tl">
|
<div :class="$style.tl">
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlEl" :key="listId"
|
ref="tlEl" :key="listId"
|
||||||
|
@ -49,7 +49,7 @@ function queueUpdated(q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top() {
|
function top() {
|
||||||
scroll(rootEl, { top: 0 });
|
tlEl?.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function settings() {
|
function settings() {
|
||||||
|
|
Loading…
Reference in a new issue