From 9f246e3dc7f8d71e7435960c348e32747fda7bdc Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 07:53:56 +0000 Subject: [PATCH 001/102] test --- .../frontend/src/components/MkPagination.vue | 73 ++++++++++++++----- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 661b04c365..1033c9701a 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -54,7 +54,17 @@ const APPEAR_MINIMUM_INTERVAL = 600; export type Paging = { endpoint: E; + + /** + * 一度にAPIへ取得する件数 + */ limit: number; + + /** + * タイムラインに表示する最大件数 + */ + displayLimit?: number; + params?: misskey.Endpoints[E]['req'] | ComputedRef; /** @@ -89,9 +99,7 @@ import { infoImageUrl } from '@/instance'; const props = withDefaults(defineProps<{ pagination: Paging; disableAutoLoad?: boolean; - displayLimit?: number; }>(), { - displayLimit: 20, }); const emit = defineEmits<{ @@ -124,17 +132,23 @@ const offset = ref(0); */ const fetching = ref(true); +/** + * onActivatedでtrue, onDeactivatedでfalseになる + */ +const active = ref(false); + const moreFetching = ref(false); const more = ref(false); const preventAppearFetchMore = ref(false); const preventAppearFetchMoreTimer = ref(null); -const isBackTop = ref(false); const empty = computed(() => items.value.size === 0); const error = ref(false); const { enableInfiniteScroll, } = defaultStore.reactiveState; +const displayLimit = computed(() => props.pagination.displayLimit ?? props.pagination.limit * 2); + const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body); @@ -339,9 +353,9 @@ const appearFetchMoreAhead = async (): Promise => { fetchMoreAppearTimeout(); }; -const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE); +const isTop = (): boolean => (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE); -watch(visibility, () => { +function visibilityChange() { if (visibility.value === 'hidden') { timerForSetPause = window.setTimeout(() => { isPausingUpdate = true; @@ -354,13 +368,24 @@ watch(visibility, () => { timerForSetPause = null; } else { isPausingUpdate = false; - if (isTop()) { + if (isTop() && active.value) { executeQueue(); } } } +} + +onActivated(() => { + active.value = true; + visibilityChange(); }); +onDeactivated(() => { + active.value = false; +}); + +watch(visibility, visibilityChange); + /** * 最新のものとして1つだけアイテムを追加する * ストリーミングから降ってきたアイテムはこれで追加する @@ -373,19 +398,28 @@ const prepend = (item: MisskeyEntity): void => { return; } - if (isTop() && !isPausingUpdate) unshiftItems([item]); - else prependQueue(item); + if ( + isTop() && // 先頭に表示されていない時はキューに追加する + !isPausingUpdate && // タブがバックグラウンドの時はキューに追加する + active.value // keepAliveで隠されている間はキューに追加する + ) { + if (!items.value.has(item.id)) return; // 既にタイムラインにある場合は何もしない + unshiftItems([item]); + } else { + prependQueue(item); + } }; /** - * 新着アイテムをitemsの先頭に追加し、displayLimitを適用する + * 新着アイテムをitemsの先頭に追加し、limitを適用する * @param newItems 新しいアイテムの配列 + * @param limit デフォルトはdisplayLimit */ -function unshiftItems(newItems: MisskeyEntity[]) { +function unshiftItems(newItems: MisskeyEntity[], limit = displayLimit.value) { const length = newItems.length + items.value.size; - items.value = new Map([...arrayToEntries(newItems), ...items.value].slice(0, props.displayLimit)); + items.value = new Map([...arrayToEntries(newItems), ...items.value].slice(0, limit)); - if (length >= props.displayLimit) more.value = true; + if (length >= displayLimit.value) more.value = true; } /** @@ -394,18 +428,19 @@ function unshiftItems(newItems: MisskeyEntity[]) { */ function concatItems(oldItems: MisskeyEntity[]) { const length = oldItems.length + items.value.size; - items.value = new Map([...items.value, ...arrayToEntries(oldItems)].slice(0, props.displayLimit)); + items.value = new Map([...items.value, ...arrayToEntries(oldItems)].slice(0, displayLimit.value)); - if (length >= props.displayLimit) more.value = true; + if (length >= displayLimit.value) more.value = true; } function executeQueue() { - unshiftItems(Array.from(queue.value.values())); - queue.value = new Map(); + const queueArr = Array.from(queue.value.entries()); + unshiftItems(queueArr.slice(-1 * props.pagination.limit).map(v => v[1])); + queue.value = new Map(queueArr.slice(0, -1 * props.pagination.limit)); } function prependQueue(newItem: MisskeyEntity) { - queue.value = new Map([[newItem.id, newItem], ...queue.value].slice(0, props.displayLimit) as [string, MisskeyEntity][]); + queue.value = new Map([[newItem.id, newItem], ...queue.value] as [string, MisskeyEntity][]); } /* @@ -431,11 +466,11 @@ const updateItem = (id: MisskeyEntity['id'], replacer: (old: MisskeyEntity) => M const inited = init(); onActivated(() => { - isBackTop.value = false; + // nothing to do }); onDeactivated(() => { - isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl ? rootEl.scrollHeight - window.innerHeight : 0) : window.scrollY === 0; + // nothing to do }); function toBottom() { From 5f1cd1e532ae920a405e2b38c7d0789192bb60c9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 12:36:45 +0000 Subject: [PATCH 002/102] wip --- .../frontend/src/components/MkPagination.vue | 32 ++++++++++++------- packages/frontend/src/scripts/scroll.ts | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 1033c9701a..ac95b68b8e 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -41,7 +41,7 @@ import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'; import * as misskey from 'misskey-js'; import * as os from '@/os'; -import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll'; +import { onScrollTop, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll } from '@/scripts/scroll'; import { useDocumentVisibility } from '@/scripts/use-document-visibility'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store'; @@ -49,7 +49,7 @@ import { MisskeyEntity } from '@/types/date-separated-list'; import { i18n } from '@/i18n'; const SECOND_FETCH_LIMIT = 30; -const TOLERANCE = 16; +const TOLERANCE = 64; const APPEAR_MINIMUM_INTERVAL = 600; export type Paging = { @@ -108,7 +108,10 @@ const emit = defineEmits<{ let rootEl = $shallowRef(); -// 遡り中かどうか +/** + * スクロールが先頭にある場合はfalse + * スクロールが先頭にない場合にtrue + */ let backed = $ref(false); let scrollRemove = $ref<(() => void) | null>(null); @@ -158,8 +161,11 @@ let isPausingUpdate = false; let timerForSetPause: number | null = null; const BACKGROUND_PAUSE_WAIT_SEC = 10; -// 先頭が表示されているかどうかを検出 -// https://qiita.com/mkataigi/items/0154aefd2223ce23398e +//#region scrolling +/** + * IntersectionObserverで大まかに検出 + * https://qiita.com/mkataigi/items/0154aefd2223ce23398e + */ let scrollObserver = $ref(); watch([() => props.pagination.reversed, $$(scrollableElement)], () => { @@ -170,7 +176,7 @@ watch([() => props.pagination.reversed, $$(scrollableElement)], () => { }, { root: scrollableElement, rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px', - threshold: 0.01, + threshold: 0.1, // 10%ぐらいになったらqueueを読む }); }, { immediate: true }); @@ -181,16 +187,20 @@ watch($$(rootEl), () => { }); }); +/** + * onScrollTop/onScrollBottomで細かく検出する + */ watch([$$(backed), $$(contentEl)], () => { - if (!backed) { - if (!contentEl) return; + if (!contentEl) return; + if (!backed) { scrollRemove = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl, executeQueue, TOLERANCE); } else { if (scrollRemove) scrollRemove(); scrollRemove = null; } }); +//#endregion if (props.pagination.params && isRef(props.pagination.params)) { watch(props.pagination.params, init, { deep: true }); @@ -353,8 +363,6 @@ const appearFetchMoreAhead = async (): Promise => { fetchMoreAppearTimeout(); }; -const isTop = (): boolean => (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE); - function visibilityChange() { if (visibility.value === 'hidden') { timerForSetPause = window.setTimeout(() => { @@ -368,7 +376,7 @@ function visibilityChange() { timerForSetPause = null; } else { isPausingUpdate = false; - if (isTop() && active.value) { + if (!backed && active.value) { executeQueue(); } } @@ -399,7 +407,7 @@ const prepend = (item: MisskeyEntity): void => { } if ( - isTop() && // 先頭に表示されていない時はキューに追加する + !backed && // 先頭に表示されていない時はキューに追加する !isPausingUpdate && // タブがバックグラウンドの時はキューに追加する active.value // keepAliveで隠されている間はキューに追加する ) { diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts index a002f02b5a..5826d4d31f 100644 --- a/packages/frontend/src/scripts/scroll.ts +++ b/packages/frontend/src/scripts/scroll.ts @@ -25,7 +25,7 @@ export function getScrollPosition(el: HTMLElement | null): number { export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { // とりあえず評価してみる - if (isTopVisible(el)) { + if (isTopVisible(el, tolerance)) { cb(); if (once) return null; } From 2481123972f5813f40e350129bb79062a5d5b1ae Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 12:47:06 +0000 Subject: [PATCH 003/102] 128 --- packages/frontend/src/components/MkPagination.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index ac95b68b8e..8c3f76916b 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -49,7 +49,7 @@ import { MisskeyEntity } from '@/types/date-separated-list'; import { i18n } from '@/i18n'; const SECOND_FETCH_LIMIT = 30; -const TOLERANCE = 64; +const TOLERANCE = 128; const APPEAR_MINIMUM_INTERVAL = 600; export type Paging = { From 28f914f67f839d66ea73b1b141857dc131e4323e Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 13:11:25 +0000 Subject: [PATCH 004/102] wip --- .../frontend/src/components/MkPagination.vue | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 8c3f76916b..db834310e4 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -41,7 +41,7 @@ import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'; import * as misskey from 'misskey-js'; import * as os from '@/os'; -import { onScrollTop, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll } from '@/scripts/scroll'; +import { isBottomVisible, isTopVisible, getBodyScrollHeight, getScrollContainer, scrollToBottom, scroll } from '@/scripts/scroll'; import { useDocumentVisibility } from '@/scripts/use-document-visibility'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store'; @@ -113,6 +113,7 @@ let rootEl = $shallowRef(); * スクロールが先頭にない場合にtrue */ let backed = $ref(false); +let weakBacked = $ref(false); let scrollRemove = $ref<(() => void) | null>(null); @@ -153,7 +154,7 @@ const { const displayLimit = computed(() => props.pagination.displayLimit ?? props.pagination.limit * 2); const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); -const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body); +const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) ?? document.body : document.body); const visibility = useDocumentVisibility(); @@ -172,7 +173,8 @@ watch([() => props.pagination.reversed, $$(scrollableElement)], () => { if (scrollObserver) scrollObserver.disconnect(); scrollObserver = new IntersectionObserver(entries => { - backed = entries[0].isIntersecting; + weakBacked = entries[0].isIntersecting; + if (weakBacked) backed = true; }, { root: scrollableElement, rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px', @@ -190,15 +192,46 @@ watch($$(rootEl), () => { /** * onScrollTop/onScrollBottomで細かく検出する */ -watch([$$(backed), $$(contentEl)], () => { - if (!contentEl) return; +function onHead() { + console.log('onHead'); + backed = false; + executeQueue(); +} - if (!backed) { - scrollRemove = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl, executeQueue, TOLERANCE); - } else { +function onBacked() { + backed = true; +} + +watch([$$(weakBacked), $$(contentEl)], () => { + if (weakBacked || !contentEl) { if (scrollRemove) scrollRemove(); scrollRemove = null; + return; } + + scrollRemove = (() => { + const checkFn = props.pagination.reversed ? isBottomVisible : isTopVisible; + const el = contentEl; + const tolerance = TOLERANCE; + + const onScroll = () => { + if (!document.body.contains(el)) return; + if (checkFn(el, tolerance)) { + onHead(); + } else { + onBacked(); + } + }; + + // とりあえず評価してみる + onScroll(); + + const container = scrollableElement; + + function removeListener() { container.removeEventListener('scroll', onScroll); } + container.addEventListener('scroll', onScroll, { passive: true }); + return removeListener; + })(); }); //#endregion @@ -516,7 +549,6 @@ onBeforeUnmount(() => { defineExpose({ items, queue, - backed, more, inited, reload, From b4d532efb43c980917c0fd722fc0cf03ad36e400 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 13:23:56 +0000 Subject: [PATCH 005/102] fix --- .../frontend/src/components/MkPagination.vue | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index db834310e4..c91655138a 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -192,15 +192,11 @@ watch($$(rootEl), () => { /** * onScrollTop/onScrollBottomで細かく検出する */ -function onHead() { - console.log('onHead'); - backed = false; - executeQueue(); -} - -function onBacked() { - backed = true; -} +watch($$(backed), () => { + if (!backed) { + executeQueue(); + } +}); watch([$$(weakBacked), $$(contentEl)], () => { if (weakBacked || !contentEl) { @@ -217,9 +213,9 @@ watch([$$(weakBacked), $$(contentEl)], () => { const onScroll = () => { if (!document.body.contains(el)) return; if (checkFn(el, tolerance)) { - onHead(); + backed = false; } else { - onBacked(); + backed = true; } }; @@ -444,7 +440,7 @@ const prepend = (item: MisskeyEntity): void => { !isPausingUpdate && // タブがバックグラウンドの時はキューに追加する active.value // keepAliveで隠されている間はキューに追加する ) { - if (!items.value.has(item.id)) return; // 既にタイムラインにある場合は何もしない + if (items.value.has(item.id)) return; // 既にタイムラインにある場合は何もしない unshiftItems([item]); } else { prependQueue(item); From 035c98dc15707fdc5638112cf9999e874599551b Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 11 Jul 2023 14:14:15 +0000 Subject: [PATCH 006/102] :v: --- locales/index.d.ts | 2 +- locales/ja-JP.yml | 2 +- .../frontend/src/components/MkPagination.vue | 79 ++++++++++++------- .../frontend/src/components/MkTimeline.vue | 6 ++ .../frontend/src/pages/antenna-timeline.vue | 4 +- packages/frontend/src/pages/timeline.vue | 4 +- .../frontend/src/pages/user-list-timeline.vue | 4 +- 7 files changed, 63 insertions(+), 38 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 8697b22e21..453465839f 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -527,7 +527,7 @@ export interface Locale { "deleteAll": string; "showFixedPostForm": string; "showFixedPostFormInChannel": string; - "newNoteRecived": string; + "goToTheHeadOfTimeline": string; "sounds": string; "sound": string; "listen": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 82efc8a469..2febef0a9e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -524,7 +524,7 @@ serverLogs: "サーバーログ" deleteAll: "全て削除" showFixedPostForm: "タイムライン上部に投稿フォームを表示する" showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)" -newNoteRecived: "新しいノートがあります" +goToTheHeadOfTimeline: "最新のノートに移動" sounds: "サウンド" sound: "サウンド" listen: "聴く" diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index c91655138a..985b9a4973 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -8,7 +8,7 @@ > - +
@@ -41,7 +41,7 @@ import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'; import * as misskey from 'misskey-js'; 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 MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store'; @@ -125,7 +125,7 @@ const items = ref(new Map()); /** * タブが非アクティブなどの場合に更新を貯めておく - * 最新が0番目 + * 最新が最後(パフォーマンス上の理由でitemsと逆にした) */ const queue = ref(new Map()); @@ -231,15 +231,17 @@ watch([$$(weakBacked), $$(contentEl)], () => { }); //#endregion -if (props.pagination.params && isRef(props.pagination.params)) { - watch(props.pagination.params, init, { deep: true }); -} - watch(queue, (a, b) => { if (a.size === 0 && b.size === 0) return; emit('queue', queue.value.size); }, { deep: true }); +/** + * 初期化 + * scrollAfterInitなどの後処理もあるので、reload関数を使うべき + * + * 注意: moreFetchingをtrueにするのでfalseにする必要がある + */ async function init(): Promise { items.value = new Map(); queue.value = new Map(); @@ -258,7 +260,7 @@ async function init(): Promise { concatItems(res); more.value = false; } else { - if (props.pagination.reversed) moreFetching.value = true; + moreFetching.value = true; concatItems(res); more.value = true; } @@ -272,10 +274,43 @@ async function init(): Promise { }); } -const reload = (): Promise => { - 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 => { + await init(); + scrollAfterInit(); }; +if (props.pagination.params && isRef(props.pagination.params)) { + watch(props.pagination.params, reload, { deep: true }); +} + const fetchMore = async (): Promise => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; moreFetching.value = true; @@ -472,12 +507,12 @@ function concatItems(oldItems: MisskeyEntity[]) { function executeQueue() { const queueArr = Array.from(queue.value.entries()); - unshiftItems(queueArr.slice(-1 * props.pagination.limit).map(v => v[1])); - queue.value = new Map(queueArr.slice(0, -1 * props.pagination.limit)); + unshiftItems(queueArr.slice(0, props.pagination.limit).map(v => v[1]).reverse()); + queue.value = new Map(queueArr.slice(props.pagination.limit)); } 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 }); -function toBottom() { - scrollToBottom(contentEl!); -} - onMounted(() => { - inited.then(() => { - if (props.pagination.reversed) { - nextTick(() => { - setTimeout(toBottom, 800); - - // scrollToBottomでmoreFetchingボタンが画面外まで出るまで - // more = trueを遅らせる - setTimeout(() => { - moreFetching.value = false; - }, 2000); - }); - } - }); + inited.then(scrollAfterInit); }); onBeforeUnmount(() => { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 2595ebc45d..13d5b9f88d 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -164,4 +164,10 @@ const timetravel = (date?: Date) => { this.$refs.tl.reload(); }; */ + +defineExpose({ + reload: () => { + tlComponent.pagingComponent?.reload(); + }, +}); diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index a22714791f..b2bc889cfb 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -3,7 +3,7 @@
-
+
-
+
{ diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index f66670e1f6..67a576ce83 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -3,7 +3,7 @@
-
+
Date: Tue, 11 Jul 2023 14:39:59 +0000 Subject: [PATCH 007/102] :v: --- .../frontend/src/components/MkPagination.vue | 25 +++++++++++-------- .../frontend/src/components/MkTimeline.vue | 6 +++-- .../frontend/src/pages/antenna-timeline.vue | 8 +----- packages/frontend/src/pages/timeline.vue | 9 +------ .../frontend/src/pages/user-list-timeline.vue | 9 +------ 5 files changed, 21 insertions(+), 36 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 985b9a4973..52a2c3af37 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -102,10 +102,6 @@ const props = withDefaults(defineProps<{ }>(), { }); -const emit = defineEmits<{ - (ev: 'queue', count: number): void; -}>(); - let rootEl = $shallowRef(); /** @@ -128,6 +124,7 @@ const items = ref(new Map()); * 最新が最後(パフォーマンス上の理由でitemsと逆にした) */ const queue = ref(new Map()); +const queueSize = computed(() => queue.value.size); const offset = ref(0); @@ -231,11 +228,6 @@ watch([$$(weakBacked), $$(contentEl)], () => { }); //#endregion -watch(queue, (a, b) => { - if (a.size === 0 && b.size === 0) return; - emit('queue', queue.value.size); -}, { deep: true }); - /** * 初期化 * scrollAfterInitなどの後処理もあるので、reload関数を使うべき @@ -283,7 +275,12 @@ function scrollAfterInit() { if (props.pagination.reversed) { nextTick(() => { setTimeout(() => { - if (contentEl) scrollToBottom(contentEl); + if (contentEl) { + scrollToBottom(contentEl); + // scrollToしてもbacked周りがうまく動かないので手動で戻す必要がある + weakBacked = false; + backed = false; + } }, 200); // scrollToBottomでmoreFetchingボタンが画面外まで出るまで @@ -295,7 +292,11 @@ function scrollAfterInit() { } else { nextTick(() => { setTimeout(() => { - if (contentEl) scrollToTop(contentEl); + scrollToTop(scrollableElement); + // scrollToしてもbacked周りがうまく動かないので手動で戻す必要がある + weakBacked = false; + backed = false; + moreFetching.value = false; }, 200); }); @@ -471,6 +472,7 @@ const prepend = (item: MisskeyEntity): void => { } if ( + queueSize.value === 0 && // キューに残っている場合はキューに追加する !backed && // 先頭に表示されていない時はキューに追加する !isPausingUpdate && // タブがバックグラウンドの時はキューに追加する active.value // keepAliveで隠されている間はキューに追加する @@ -566,6 +568,7 @@ defineExpose({ queue, more, inited, + queueSize, reload, prepend, append: appendItem, diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 13d5b9f88d..bc15ebfcaf 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -1,5 +1,5 @@ diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index b2bc889cfb..693259a0e5 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -3,14 +3,13 @@
-
+
@@ -34,17 +33,12 @@ const props = defineProps<{ }>(); let antenna = $ref(null); -let queue = $ref(0); let rootEl = $shallowRef(); let tlEl = $shallowRef>(); const keymap = $computed(() => ({ 't': focus, })); -function queueUpdated(q) { - queue = q; -} - function top() { tlEl?.reload(); } diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 20b35c9a25..f045d53a69 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -6,14 +6,13 @@ -
+
@@ -26,7 +25,6 @@ import { defineAsyncComponent, computed, watch, provide } from 'vue'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import MkTimeline from '@/components/MkTimeline.vue'; import MkPostForm from '@/components/MkPostForm.vue'; -import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; @@ -47,16 +45,11 @@ const keymap = { const tlComponent = $shallowRef>(); const rootEl = $shallowRef(); -let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); watch ($$(src), () => queue = 0); -function queueUpdated(q: number): void { - queue = q; -} - function top(): void { tlComponent?.reload(); } diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 67a576ce83..e297e1747e 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -3,14 +3,13 @@
-
+
@@ -21,7 +20,6 @@ diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 3a03444de2..5a92507bce 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -49,24 +49,30 @@ function parsePath(path: string): ParsedPath { return res; } +export type NiraxChangeEvent = { + beforePath: string; + path: string; + resolved: Resolved; + key: string; +}; + +export type NiraxExportEvent = { + path: string; + key: string; +}; + +export type NiraxPushEvent = { + beforePath: string; + path: string; + route: RouteDef | null; + props: Map | null; + key: string; +}; + export class Router extends EventEmitter<{ - change: (ctx: { - beforePath: string; - path: string; - resolved: Resolved; - key: string; - }) => void; - replace: (ctx: { - path: string; - key: string; - }) => void; - push: (ctx: { - beforePath: string; - path: string; - route: RouteDef | null; - props: Map | null; - key: string; - }) => void; + change: (ctx: NiraxChangeEvent) => void; + replace: (ctx: NiraxExportEvent) => void; + push: (ctx: NiraxExportEvent) => void; same: () => void; }> { private routes: RouteDef[]; @@ -271,29 +277,3 @@ export class Router extends EventEmitter<{ this.navigate(path, key); } } - -export function useScrollPositionManager(getScrollContainer: () => HTMLElement, router: Router) { - const scrollPosStore = new Map(); - - onMounted(() => { - const scrollContainer = getScrollContainer(); - - scrollContainer.addEventListener('scroll', () => { - scrollPosStore.set(router.getCurrentKey(), scrollContainer.scrollTop); - }, { passive: true }); - - router.addListener('change', ctx => { - const scrollPos = scrollPosStore.get(ctx.key) ?? 0; - scrollContainer.scroll({ top: scrollPos, behavior: 'instant' }); - if (scrollPos !== 0) { - window.setTimeout(() => { // 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール - scrollContainer.scroll({ top: scrollPos, behavior: 'instant' }); - }, 100); - } - }); - - router.addListener('same', () => { - scrollContainer.scroll({ top: 0, behavior: 'smooth' }); - }); - }); -} diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index 0413307955..9d730171eb 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -8,7 +8,7 @@
- +
@@ -21,8 +21,6 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; -import { useScrollPositionManager } from '@/nirax'; -import { getScrollContainer } from '@/scripts/scroll'; defineProps<{ column: Column; @@ -66,6 +64,4 @@ function onContextmenu(ev: MouseEvent) { }, }], ev); } - -useScrollPositionManager(() => getScrollContainer(contents.value), mainRouter); diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 9ae43c39d3..4f80e8cbbb 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -4,7 +4,7 @@ - +
@@ -95,7 +95,6 @@ import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { deviceKind } from '@/scripts/device-kind'; import { miLocalStorage } from '@/local-storage'; import { CURRENT_STICKY_BOTTOM } from '@/const'; -import { useScrollPositionManager } from '@/nirax'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); @@ -214,8 +213,6 @@ watch($$(navFooter), () => { }, { immediate: true, }); - -useScrollPositionManager(() => contents.value.rootEl, mainRouter); diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 9566cc651f..a176da821d 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -35,9 +35,7 @@
{{ i18n.ts.letsLookAtTimeline }}
-
- -
+
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 327f1b4b14..705ebb72c3 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -3,15 +3,13 @@
-
-
- -
+
@@ -84,25 +82,6 @@ definePageMetadata(computed(() => antenna ? { diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 3d497c2e23..a8297f386f 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -15,33 +15,20 @@ -
-

- - {{ i18n.ts._disabledTimeline.title }} -

-

{{ i18n.ts._disabledTimeline.description }}

-
-
- -
+ - - From e2f3091778523931c67166b92731d35a6c47ac79 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 05:14:42 +0000 Subject: [PATCH 077/102] :art: --- packages/frontend/src/components/MkTimeline.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index a667f5d79f..c3f6e3ab09 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -198,7 +198,7 @@ defineExpose({ From 05042a0697ed6ac80e48c7a50775c3dd9c379322 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 06:39:39 +0000 Subject: [PATCH 079/102] =?UTF-8?q?perf(backend):=20createPerson=E3=81=A7?= =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92=E7=A9=8D?= =?UTF-8?q?=E6=A5=B5=E7=9A=84=E3=81=AB=E5=88=A9=E7=94=A8=E3=81=99=E3=82=8B?= =?UTF-8?q?,=20=E3=83=88=E3=83=A9=E3=83=B3=E3=82=B6=E3=82=AF=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E5=9B=9E=E6=95=B0=E3=82=92=E6=B8=9B=E3=82=89?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/models/ApPersonService.ts | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index e89ee4632c..22ef0b139e 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -43,6 +43,7 @@ import type { ApLoggerService } from '../ApLoggerService.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import type { ApImageService } from './ApImageService.js'; import type { IActor, IObject } from '../type.js'; +import { ca } from 'date-fns/locale'; const nameLength = 128; const summaryLength = 2048; @@ -259,13 +260,49 @@ export class ApPersonService implements OnModuleInit { // Create user let user: RemoteUser | null = null; + const userAdditionalInfo: Pick = { + avatarId: null, + bannerId: null, + avatarUrl: null, + bannerUrl: null, + avatarBlurhash: null, + bannerBlurhash: null, + emojis: [], + }; + + try { + //#region アバターとヘッダー画像をフェッチ + const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { + if (img == null) return null; + if (user == null) throw new Error('failed to create user: user is null'); + return this.apImageService.resolveImage(user, img).catch(() => null); + })); + + userAdditionalInfo.avatarId = avatar?.id ?? null; + userAdditionalInfo.bannerId = banner?.id ?? null; + userAdditionalInfo.avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null; + userAdditionalInfo.bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null; + userAdditionalInfo.avatarBlurhash = avatar?.blurhash ?? null; + userAdditionalInfo.bannerBlurhash = banner?.blurhash ?? null; + //#endregion + } catch (err) { + this.logger.error('error occured while fetching user avatar/banner', { stack: err }); + } + + //#region カスタム絵文字取得 + userAdditionalInfo.emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) + .then(_emojis => _emojis.map(emoji => emoji.name)) + .catch(err => { + this.logger.error(`error occured while fetching user emojis`, { stack: err }); + return []; + }); + //#endregion + try { // Start transaction await this.db.transaction(async transactionalEntityManager => { user = await transactionalEntityManager.save(new User({ id: this.idService.genId(), - avatarId: null, - bannerId: null, createdAt: new Date(), lastFetchedAt: new Date(), name: truncate(person.name, nameLength), @@ -285,6 +322,7 @@ export class ApPersonService implements OnModuleInit { tags, isBot, isCat: (person as any).isCat === true, + ...userAdditionalInfo, })) as RemoteUser; await transactionalEntityManager.save(new UserProfile({ @@ -321,6 +359,9 @@ export class ApPersonService implements OnModuleInit { if (user == null) throw new Error('failed to create user: user is null'); + // Register to the cache + this.cacheService.uriPersonCache.set(user.uri, user); + // Register host this.federatedInstanceService.fetch(host).then(async i => { this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); @@ -335,48 +376,6 @@ export class ApPersonService implements OnModuleInit { // ハッシュタグ更新 this.hashtagService.updateUsertags(user, tags); - //#region アバターとヘッダー画像をフェッチ - const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { - if (img == null) return null; - if (user == null) throw new Error('failed to create user: user is null'); - return this.apImageService.resolveImage(user, img).catch(() => null); - })); - - const avatarId = avatar?.id ?? null; - const bannerId = banner?.id ?? null; - const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null; - const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null; - const avatarBlurhash = avatar?.blurhash ?? null; - const bannerBlurhash = banner?.blurhash ?? null; - - await this.usersRepository.update(user.id, { - avatarId, - bannerId, - avatarUrl, - bannerUrl, - avatarBlurhash, - bannerBlurhash, - }); - - user.avatarId = avatarId; - user.bannerId = bannerId; - user.avatarUrl = avatarUrl; - user.bannerUrl = bannerUrl; - user.avatarBlurhash = avatarBlurhash; - user.bannerBlurhash = bannerBlurhash; - //#endregion - - //#region カスタム絵文字取得 - const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { - this.logger.info(`extractEmojis: ${err}`); - return []; - }); - - const emojiNames = emojis.map(emoji => emoji.name); - - await this.usersRepository.update(user.id, { emojis: emojiNames }); - //#endregion - await this.updateFeatured(user.id, resolver).catch(err => this.logger.error(err)); return user; @@ -400,7 +399,7 @@ export class ApPersonService implements OnModuleInit { if (uri.startsWith(`${this.config.url}/`)) return; //#region このサーバーに既に登録されているか - const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null; + const exist = await this.fetchPerson(uri) as RemoteUser | null; if (exist === null) return; //#endregion From fc50dc7a67b0ddb996dca0f3c5cd687257152022 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 06:43:09 +0000 Subject: [PATCH 080/102] move comment --- .../backend/src/core/activitypub/models/ApPersonService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 22ef0b139e..aa3580599e 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -270,8 +270,8 @@ export class ApPersonService implements OnModuleInit { emojis: [], }; + //#region アバターとヘッダー画像をフェッチ try { - //#region アバターとヘッダー画像をフェッチ const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { if (img == null) return null; if (user == null) throw new Error('failed to create user: user is null'); @@ -284,10 +284,10 @@ export class ApPersonService implements OnModuleInit { userAdditionalInfo.bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null; userAdditionalInfo.avatarBlurhash = avatar?.blurhash ?? null; userAdditionalInfo.bannerBlurhash = banner?.blurhash ?? null; - //#endregion } catch (err) { this.logger.error('error occured while fetching user avatar/banner', { stack: err }); } + //#endregion //#region カスタム絵文字取得 userAdditionalInfo.emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) From 660b0302338d912e39bdd3f8d95cad7e58a734b0 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 06:59:12 +0000 Subject: [PATCH 081/102] fix --- .../activitypub/models/ApPersonService.ts | 82 ++++++++----------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index aa3580599e..37177a83e2 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -44,6 +44,7 @@ import type { ApLoggerService } from '../ApLoggerService.js'; import type { ApImageService } from './ApImageService.js'; import type { IActor, IObject } from '../type.js'; import { ca } from 'date-fns/locale'; +import { users } from 'systeminformation'; const nameLength = 128; const summaryLength = 2048; @@ -221,6 +222,23 @@ export class ApPersonService implements OnModuleInit { return null; } + private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise> { + const [avatar, banner] = await Promise.all([icon, image].map(img => { + if (img == null) return null; + if (user == null) throw new Error('failed to create user: user is null'); + return this.apImageService.resolveImage(user, img).catch(() => null); + })); + + return { + avatarId: avatar?.id ?? null, + bannerId: banner?.id ?? null, + avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, + bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, + avatarBlurhash: avatar?.blurhash ?? null, + bannerBlurhash: banner?.blurhash ?? null, + }; + } + /** * Personを作成します。 */ @@ -260,37 +278,9 @@ export class ApPersonService implements OnModuleInit { // Create user let user: RemoteUser | null = null; - const userAdditionalInfo: Pick = { - avatarId: null, - bannerId: null, - avatarUrl: null, - bannerUrl: null, - avatarBlurhash: null, - bannerBlurhash: null, - emojis: [], - }; - - //#region アバターとヘッダー画像をフェッチ - try { - const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { - if (img == null) return null; - if (user == null) throw new Error('failed to create user: user is null'); - return this.apImageService.resolveImage(user, img).catch(() => null); - })); - - userAdditionalInfo.avatarId = avatar?.id ?? null; - userAdditionalInfo.bannerId = banner?.id ?? null; - userAdditionalInfo.avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null; - userAdditionalInfo.bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null; - userAdditionalInfo.avatarBlurhash = avatar?.blurhash ?? null; - userAdditionalInfo.bannerBlurhash = banner?.blurhash ?? null; - } catch (err) { - this.logger.error('error occured while fetching user avatar/banner', { stack: err }); - } - //#endregion //#region カスタム絵文字取得 - userAdditionalInfo.emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) + const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) .then(_emojis => _emojis.map(emoji => emoji.name)) .catch(err => { this.logger.error(`error occured while fetching user emojis`, { stack: err }); @@ -322,7 +312,7 @@ export class ApPersonService implements OnModuleInit { tags, isBot, isCat: (person as any).isCat === true, - ...userAdditionalInfo, + emojis, })) as RemoteUser; await transactionalEntityManager.save(new UserProfile({ @@ -362,6 +352,19 @@ export class ApPersonService implements OnModuleInit { // Register to the cache this.cacheService.uriPersonCache.set(user.uri, user); + //#region アバターとヘッダー画像をフェッチ + try { + const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); + await this.usersRepository.update(user.id, updates); + user = { ...user, ...updates }; + + // Register to the cache + this.cacheService.uriPersonCache.set(user.uri, user); + } catch (err) { + this.logger.error('error occured while fetching user avatar/banner', { stack: err }); + } + //#endregion + // Register host this.federatedInstanceService.fetch(host).then(async i => { this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); @@ -412,12 +415,6 @@ export class ApPersonService implements OnModuleInit { this.logger.info(`Updating the Person: ${person.id}`); - // アバターとヘッダー画像をフェッチ - const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { - if (img == null) return null; - return this.apImageService.resolveImage(exist, img).catch(() => null); - })); - // カスタム絵文字取得 const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => { this.logger.info(`extractEmojis: ${e}`); @@ -453,6 +450,7 @@ export class ApPersonService implements OnModuleInit { movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, + ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), } as Partial & Pick; const moving = ((): boolean => { @@ -475,18 +473,6 @@ export class ApPersonService implements OnModuleInit { if (moving) updates.movedAt = new Date(); - if (avatar) { - updates.avatarId = avatar.id; - updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar'); - updates.avatarBlurhash = avatar.blurhash; - } - - if (banner) { - updates.bannerId = banner.id; - updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner); - updates.bannerBlurhash = banner.blurhash; - } - // Update user await this.usersRepository.update(exist.id, updates); From 4e7a26e6d5faa9760077062752940ded9662f50d Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:00:50 +0000 Subject: [PATCH 082/102] oops --- packages/backend/src/core/activitypub/models/ApPersonService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 37177a83e2..8f2997ac84 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -43,8 +43,6 @@ import type { ApLoggerService } from '../ApLoggerService.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import type { ApImageService } from './ApImageService.js'; import type { IActor, IObject } from '../type.js'; -import { ca } from 'date-fns/locale'; -import { users } from 'systeminformation'; const nameLength = 128; const summaryLength = 2048; From 4c83663597e424364e3592ad00f12f3141d79236 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:03:07 +0000 Subject: [PATCH 083/102] fix --- packages/backend/src/core/activitypub/models/ApPersonService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 8f2997ac84..4f83983be1 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -291,6 +291,8 @@ export class ApPersonService implements OnModuleInit { await this.db.transaction(async transactionalEntityManager => { user = await transactionalEntityManager.save(new User({ id: this.idService.genId(), + avatarId: null, + bannerId: null, createdAt: new Date(), lastFetchedAt: new Date(), name: truncate(person.name, nameLength), From a2f6bf3d5c93d02d36c37bdae0235a4ade2620f5 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:03:30 +0000 Subject: [PATCH 084/102] fix --- .../core/activitypub/models/ApPersonService.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 4f83983be1..028b5148b5 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -352,6 +352,15 @@ export class ApPersonService implements OnModuleInit { // Register to the cache this.cacheService.uriPersonCache.set(user.uri, user); + // Register host + this.federatedInstanceService.fetch(host).then(async i => { + this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); + this.fetchInstanceMetadataService.fetchInstanceMetadata(i); + if ((await this.metaService.fetch()).enableChartsForFederatedInstances) { + this.instanceChart.newUser(i.host); + } + }); + //#region アバターとヘッダー画像をフェッチ try { const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); @@ -365,15 +374,6 @@ export class ApPersonService implements OnModuleInit { } //#endregion - // Register host - this.federatedInstanceService.fetch(host).then(async i => { - this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); - this.fetchInstanceMetadataService.fetchInstanceMetadata(i); - if ((await this.metaService.fetch()).enableChartsForFederatedInstances) { - this.instanceChart.newUser(i.host); - } - }); - this.usersChart.update(user, true); // ハッシュタグ更新 From ee1e2aa200f1b0ebc8b7337048b1c3d538961ea8 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:05:02 +0000 Subject: [PATCH 085/102] fix --- .../src/core/activitypub/models/ApPersonService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 028b5148b5..8fc083719d 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -361,6 +361,11 @@ export class ApPersonService implements OnModuleInit { } }); + this.usersChart.update(user, true); + + // ハッシュタグ更新 + this.hashtagService.updateUsertags(user, tags); + //#region アバターとヘッダー画像をフェッチ try { const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); @@ -374,11 +379,6 @@ export class ApPersonService implements OnModuleInit { } //#endregion - this.usersChart.update(user, true); - - // ハッシュタグ更新 - this.hashtagService.updateUsertags(user, tags); - await this.updateFeatured(user.id, resolver).catch(err => this.logger.error(err)); return user; From e6ee5704e8e35dfc966f445470e885ab41320f3f Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:07:58 +0000 Subject: [PATCH 086/102] beta.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a62ef7c185..dc619b7606 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.14.0-beta.6", + "version": "13.14.0-beta.7", "codename": "nasubi", "repository": { "type": "git", From 4e775a670f23179a06ddeaaa2c034e002c7fabb6 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:08:15 +0000 Subject: [PATCH 087/102] Revert "beta.7" This reverts commit e6ee5704e8e35dfc966f445470e885ab41320f3f. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc619b7606..a62ef7c185 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.14.0-beta.7", + "version": "13.14.0-beta.6", "codename": "nasubi", "repository": { "type": "git", From 3cc22e5e1c3f3d351fbec4679d8701ba4c8d06c6 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 19 Jul 2023 07:08:28 +0000 Subject: [PATCH 088/102] Revert "Merge branch 'use-uri-cache' into pag-back" This reverts commit d0a119c2eab99216afd03d4664bff6bab02ce9c9, reversing changes made to 5bfb98df006d4729c7bfe38507b8c82907906b0e. --- .../activitypub/models/ApPersonService.ts | 99 +++++++++++-------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 8fc083719d..e89ee4632c 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -220,23 +220,6 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise> { - const [avatar, banner] = await Promise.all([icon, image].map(img => { - if (img == null) return null; - if (user == null) throw new Error('failed to create user: user is null'); - return this.apImageService.resolveImage(user, img).catch(() => null); - })); - - return { - avatarId: avatar?.id ?? null, - bannerId: banner?.id ?? null, - avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, - bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, - avatarBlurhash: avatar?.blurhash ?? null, - bannerBlurhash: banner?.blurhash ?? null, - }; - } - /** * Personを作成します。 */ @@ -276,16 +259,6 @@ export class ApPersonService implements OnModuleInit { // Create user let user: RemoteUser | null = null; - - //#region カスタム絵文字取得 - const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) - .then(_emojis => _emojis.map(emoji => emoji.name)) - .catch(err => { - this.logger.error(`error occured while fetching user emojis`, { stack: err }); - return []; - }); - //#endregion - try { // Start transaction await this.db.transaction(async transactionalEntityManager => { @@ -312,7 +285,6 @@ export class ApPersonService implements OnModuleInit { tags, isBot, isCat: (person as any).isCat === true, - emojis, })) as RemoteUser; await transactionalEntityManager.save(new UserProfile({ @@ -349,9 +321,6 @@ export class ApPersonService implements OnModuleInit { if (user == null) throw new Error('failed to create user: user is null'); - // Register to the cache - this.cacheService.uriPersonCache.set(user.uri, user); - // Register host this.federatedInstanceService.fetch(host).then(async i => { this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); @@ -367,16 +336,45 @@ export class ApPersonService implements OnModuleInit { this.hashtagService.updateUsertags(user, tags); //#region アバターとヘッダー画像をフェッチ - try { - const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); - await this.usersRepository.update(user.id, updates); - user = { ...user, ...updates }; + const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { + if (img == null) return null; + if (user == null) throw new Error('failed to create user: user is null'); + return this.apImageService.resolveImage(user, img).catch(() => null); + })); - // Register to the cache - this.cacheService.uriPersonCache.set(user.uri, user); - } catch (err) { - this.logger.error('error occured while fetching user avatar/banner', { stack: err }); - } + const avatarId = avatar?.id ?? null; + const bannerId = banner?.id ?? null; + const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null; + const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null; + const avatarBlurhash = avatar?.blurhash ?? null; + const bannerBlurhash = banner?.blurhash ?? null; + + await this.usersRepository.update(user.id, { + avatarId, + bannerId, + avatarUrl, + bannerUrl, + avatarBlurhash, + bannerBlurhash, + }); + + user.avatarId = avatarId; + user.bannerId = bannerId; + user.avatarUrl = avatarUrl; + user.bannerUrl = bannerUrl; + user.avatarBlurhash = avatarBlurhash; + user.bannerBlurhash = bannerBlurhash; + //#endregion + + //#region カスタム絵文字取得 + const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { + this.logger.info(`extractEmojis: ${err}`); + return []; + }); + + const emojiNames = emojis.map(emoji => emoji.name); + + await this.usersRepository.update(user.id, { emojis: emojiNames }); //#endregion await this.updateFeatured(user.id, resolver).catch(err => this.logger.error(err)); @@ -402,7 +400,7 @@ export class ApPersonService implements OnModuleInit { if (uri.startsWith(`${this.config.url}/`)) return; //#region このサーバーに既に登録されているか - const exist = await this.fetchPerson(uri) as RemoteUser | null; + const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null; if (exist === null) return; //#endregion @@ -415,6 +413,12 @@ export class ApPersonService implements OnModuleInit { this.logger.info(`Updating the Person: ${person.id}`); + // アバターとヘッダー画像をフェッチ + const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => { + if (img == null) return null; + return this.apImageService.resolveImage(exist, img).catch(() => null); + })); + // カスタム絵文字取得 const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => { this.logger.info(`extractEmojis: ${e}`); @@ -450,7 +454,6 @@ export class ApPersonService implements OnModuleInit { movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, - ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), } as Partial & Pick; const moving = ((): boolean => { @@ -473,6 +476,18 @@ export class ApPersonService implements OnModuleInit { if (moving) updates.movedAt = new Date(); + if (avatar) { + updates.avatarId = avatar.id; + updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar'); + updates.avatarBlurhash = avatar.blurhash; + } + + if (banner) { + updates.bannerId = banner.id; + updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner); + updates.bannerBlurhash = banner.blurhash; + } + // Update user await this.usersRepository.update(exist.id, updates); From ef69eee155d4840d355d3b4d7e3913968f4dcf8e Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 21 Jul 2023 11:04:58 +0000 Subject: [PATCH 089/102] =?UTF-8?q?active=E3=81=A7=E3=82=82executeQueue?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkPagination.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index df2b423f39..cae08c8f4b 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -433,7 +433,7 @@ onDeactivated(() => { active.value = false; }); -watch(visibility, () => { +watch([active, visibility], () => { if (!backed && active.value && visibility.value === 'visible') { executeQueue(); } From d6e57059e42c28d855aecd71f6bbe47551874642 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 24 Jul 2023 07:19:22 +0000 Subject: [PATCH 090/102] =?UTF-8?q?backed=E3=81=8Cfalse=E3=81=AB=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=9F=E3=82=89=E9=80=9A=E7=9F=A5=E3=82=92=E6=97=A2?= =?UTF-8?q?=E8=AA=AD=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNotifications.vue | 10 ++++++++-- packages/frontend/src/components/MkPagination.vue | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 9b1aa6230e..4498f0f113 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -17,7 +17,7 @@