From 0a4642addd5d1d0ef2c61ca14a2d78903f6b31cc Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:28:29 +0900 Subject: [PATCH 01/85] wip --- packages/frontend/src/boot/main-boot.ts | 142 +++++++++--------- .../src/components/MkNotifications.vue | 24 +-- .../frontend/src/components/MkTimeline.vue | 5 +- packages/frontend/src/lib/pizzax.ts | 20 --- packages/frontend/src/store.ts | 4 + packages/frontend/src/ui/_common_/common.vue | 11 +- .../src/ui/_common_/stream-indicator.vue | 11 +- packages/frontend/src/use/use-note-capture.ts | 5 +- 8 files changed, 112 insertions(+), 110 deletions(-) diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index bac7128603..d4522a4ae5 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -82,39 +82,6 @@ export async function mainBoot() { } } - const stream = useStream(); - - let reloadDialogShowing = false; - stream.on('_disconnected_', async () => { - if (prefer.s.serverDisconnectedBehavior === 'reload') { - window.location.reload(); - } else if (prefer.s.serverDisconnectedBehavior === 'dialog') { - if (reloadDialogShowing) return; - reloadDialogShowing = true; - const { canceled } = await confirm({ - type: 'warning', - title: i18n.ts.disconnectedFromServer, - text: i18n.ts.reloadConfirm, - }); - reloadDialogShowing = false; - if (!canceled) { - window.location.reload(); - } - } - }); - - stream.on('emojiAdded', emojiData => { - addCustomEmoji(emojiData.emoji); - }); - - stream.on('emojiUpdated', emojiData => { - updateCustomEmojis(emojiData.emojis); - }); - - stream.on('emojiDeleted', emojiData => { - removeCustomEmojis(emojiData.emojis); - }); - launchPlugins(); try { @@ -172,8 +139,6 @@ export async function mainBoot() { } } - stream.on('announcementCreated', onAnnouncementCreated); - if ($i.isDeleted) { alert({ type: 'warning', @@ -351,50 +316,87 @@ export async function mainBoot() { } } - const main = markRaw(stream.useChannel('main', null, 'System')); + if (store.s.realtimeMode) { + const stream = useStream(); - // 自分の情報が更新されたとき - main.on('meUpdated', i => { - updateCurrentAccountPartial(i); - }); - - main.on('readAllNotifications', () => { - updateCurrentAccountPartial({ - hasUnreadNotification: false, - unreadNotificationsCount: 0, + let reloadDialogShowing = false; + stream.on('_disconnected_', async () => { + if (prefer.s.serverDisconnectedBehavior === 'reload') { + window.location.reload(); + } else if (prefer.s.serverDisconnectedBehavior === 'dialog') { + if (reloadDialogShowing) return; + reloadDialogShowing = true; + const { canceled } = await confirm({ + type: 'warning', + title: i18n.ts.disconnectedFromServer, + text: i18n.ts.reloadConfirm, + }); + reloadDialogShowing = false; + if (!canceled) { + window.location.reload(); + } + } }); - }); - main.on('unreadNotification', () => { - const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1; - updateCurrentAccountPartial({ - hasUnreadNotification: true, - unreadNotificationsCount, + stream.on('emojiAdded', emojiData => { + addCustomEmoji(emojiData.emoji); }); - }); - main.on('unreadAntenna', () => { - updateCurrentAccountPartial({ hasUnreadAntenna: true }); - sound.playMisskeySfx('antenna'); - }); + stream.on('emojiUpdated', emojiData => { + updateCustomEmojis(emojiData.emojis); + }); - main.on('newChatMessage', () => { - updateCurrentAccountPartial({ hasUnreadChatMessages: true }); - sound.playMisskeySfx('chatMessage'); - }); + stream.on('emojiDeleted', emojiData => { + removeCustomEmojis(emojiData.emojis); + }); - main.on('readAllAnnouncements', () => { - updateCurrentAccountPartial({ hasUnreadAnnouncement: false }); - }); + stream.on('announcementCreated', onAnnouncementCreated); - // 個人宛てお知らせが発行されたとき - main.on('announcementCreated', onAnnouncementCreated); + const main = markRaw(stream.useChannel('main', null, 'System')); - // トークンが再生成されたとき - // このままではMisskeyが利用できないので強制的にサインアウトさせる - main.on('myTokenRegenerated', () => { - signout(); - }); + // 自分の情報が更新されたとき + main.on('meUpdated', i => { + updateCurrentAccountPartial(i); + }); + + main.on('readAllNotifications', () => { + updateCurrentAccountPartial({ + hasUnreadNotification: false, + unreadNotificationsCount: 0, + }); + }); + + main.on('unreadNotification', () => { + const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1; + updateCurrentAccountPartial({ + hasUnreadNotification: true, + unreadNotificationsCount, + }); + }); + + main.on('unreadAntenna', () => { + updateCurrentAccountPartial({ hasUnreadAntenna: true }); + sound.playMisskeySfx('antenna'); + }); + + main.on('newChatMessage', () => { + updateCurrentAccountPartial({ hasUnreadChatMessages: true }); + sound.playMisskeySfx('chatMessage'); + }); + + main.on('readAllAnnouncements', () => { + updateCurrentAccountPartial({ hasUnreadAnnouncement: false }); + }); + + // 個人宛てお知らせが発行されたとき + main.on('announcementCreated', onAnnouncementCreated); + + // トークンが再生成されたとき + // このままではMisskeyが利用できないので強制的にサインアウトさせる + main.on('myTokenRegenerated', () => { + signout(); + }); + } } // shortcut diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 21f1967bfa..8a4679046f 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -36,6 +36,7 @@ import { i18n } from '@/i18n.js'; import { infoImageUrl } from '@/instance.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import { prefer } from '@/preferences.js'; +import { store } from '@/store.js'; const props = defineProps<{ excludeTypes?: typeof notificationTypes[number][]; @@ -60,7 +61,9 @@ const pagination = computed(() => prefer.r.useGroupedNotifications.value ? { function onNotification(notification) { const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false; if (isMuted || window.document.visibilityState === 'visible') { - useStream().send('readNotification'); + if (store.s.realtimeMode) { + useStream().send('readNotification'); + } } if (!isMuted) { @@ -76,19 +79,22 @@ function reload() { }); } -let connection: Misskey.ChannelConnection; +let connection: Misskey.ChannelConnection | null = null; onMounted(() => { - connection = useStream().useChannel('main'); - connection.on('notification', onNotification); - connection.on('notificationFlushed', reload); + if (store.s.realtimeMode) { + connection = useStream().useChannel('main'); + connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); + } }); onActivated(() => { - pagingComponent.value?.reload(); - connection = useStream().useChannel('main'); - connection.on('notification', onNotification); - connection.on('notificationFlushed', reload); + if (store.s.realtimeMode) { + connection = useStream().useChannel('main'); + connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); + } }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 73057e4644..63bfb6a66d 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -28,6 +28,7 @@ import * as sound from '@/utility/sound.js'; import { $i } from '@/i.js'; import { instance } from '@/instance.js'; import { prefer } from '@/preferences.js'; +import { store } from '@/store.js'; const props = withDefaults(defineProps<{ src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; @@ -94,7 +95,7 @@ let connection: Misskey.ChannelConnection | null = null; let connection2: Misskey.ChannelConnection | null = null; let paginationQuery: Paging | null = null; -const stream = useStream(); +const stream = store.s.realtimeMode ? useStream() : null; function connectChannel() { if (props.src === 'antenna') { @@ -239,7 +240,7 @@ function updatePaginationQuery() { } function refreshEndpointAndChannel() { - if (!prefer.s.disableStreamingTimeline) { + if (!prefer.s.disableStreamingTimeline && store.s.realtimeMode) { disconnectChannel(); connectChannel(); } diff --git a/packages/frontend/src/lib/pizzax.ts b/packages/frontend/src/lib/pizzax.ts index a232ced75e..20d44032df 100644 --- a/packages/frontend/src/lib/pizzax.ts +++ b/packages/frontend/src/lib/pizzax.ts @@ -12,7 +12,6 @@ import { $i } from '@/i.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { get, set } from '@/utility/idb-proxy.js'; import { store } from '@/store.js'; -import { useStream } from '@/stream.js'; import { deepClone } from '@/utility/clone.js'; import { deepMerge } from '@/utility/merge.js'; @@ -129,25 +128,6 @@ export class Pizzax { if (where === 'deviceAccount' && !($i && userId !== $i.id)) return; this.r[key].value = this.s[key] = value; }); - - if ($i) { - const connection = useStream().useChannel('main'); - - // streamingのuser storage updateイベントを監視して更新 - connection.on('registryUpdated', ({ scope, key, value }: { scope?: string[], key: keyof T, value: T[typeof key]['default'] }) => { - if (!scope || scope.length !== 2 || scope[0] !== 'client' || scope[1] !== this.key || this.s[key] === value) return; - - this.r[key].value = this.s[key] = value; - - this.addIdbSetJob(async () => { - const cache = await get(this.registryCacheKeyName); - if (cache[key] !== value) { - cache[key] = value; - await set(this.registryCacheKeyName, cache); - } - }); - }); - } } private load(): Promise { diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index fc1d463674..3396780e14 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -76,6 +76,10 @@ export const store = markRaw(new Pizzax('base', { where: 'device', default: false, }, + realtimeMode: { + where: 'device', + default: false, + }, recentlyUsedEmojis: { where: 'device', default: [] as string[], diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index 930f633c9f..4ed68bf6e2 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -58,6 +58,7 @@ import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { prefer } from '@/preferences.js'; import { globalEvents } from '@/events.js'; +import { store } from '@/store.js'; const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue')); const XUpload = defineAsyncComponent(() => import('./upload.vue')); @@ -70,7 +71,9 @@ function onNotification(notification: Misskey.entities.Notification, isClient = if (window.document.visibilityState === 'visible') { if (!isClient && notification.type !== 'test') { // サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので) - useStream().send('readNotification'); + if (store.s.realtimeMode) { + useStream().send('readNotification'); + } } notifications.value.unshift(notification); @@ -87,8 +90,10 @@ function onNotification(notification: Misskey.entities.Notification, isClient = } if ($i) { - const connection = useStream().useChannel('main', null, 'UI'); - connection.on('notification', onNotification); + if (store.s.realtimeMode) { + const connection = useStream().useChannel('main', null, 'UI'); + connection.on('notification', onNotification); + } globalEvents.on('clientNotification', notification => onNotification(notification, true)); //#region Listen message from SW diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue index 5f7600881f..35508b7ce6 100644 --- a/packages/frontend/src/ui/_common_/stream-indicator.vue +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -20,6 +20,7 @@ import { i18n } from '@/i18n.js'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { prefer } from '@/preferences.js'; +import { store } from '@/store.js'; const zIndex = os.claimZIndex('high'); @@ -37,11 +38,13 @@ function reload() { window.location.reload(); } -useStream().on('_disconnected_', onDisconnected); +if (store.s.realtimeMode) { + useStream().on('_disconnected_', onDisconnected); -onUnmounted(() => { - useStream().off('_disconnected_', onDisconnected); -}); + onUnmounted(() => { + useStream().off('_disconnected_', onDisconnected); + }); +} diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 89ab1bf99a..3e0ef4155d 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -6,17 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -29,7 +29,6 @@ import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js'; import { useTooltip } from '@/use/use-tooltip.js'; import { $i } from '@/i.js'; import MkReactionEffect from '@/components/MkReactionEffect.vue'; -import { claimAchievement } from '@/utility/achievements.js'; import { i18n } from '@/i18n.js'; import * as sound from '@/utility/sound.js'; import { checkReactionPermissions } from '@/utility/check-reaction-permissions.js'; @@ -39,10 +38,12 @@ import { DI } from '@/di.js'; import { noteEvents } from '@/use/use-note-capture.js'; const props = defineProps<{ + noteId: Misskey.entities.Note['id']; reaction: string; + reactionEmojis: Misskey.entities.Note['reactionEmojis']; + myReaction: Misskey.entities.Note['myReaction']; count: number; isInitial: boolean; - note: Misskey.entities.Note; }>(); const mock = inject(DI.mock, false); @@ -57,14 +58,16 @@ const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction)); const canToggle = computed(() => { - return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value); + // TODO + //return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value); + return !props.reaction.match(/@\w/) && $i && emoji.value; }); const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); async function toggleReaction() { if (!canToggle.value) return; - const oldReaction = props.note.myReaction; + const oldReaction = props.myReaction; if (oldReaction) { const confirm = await os.confirm({ type: 'warning', @@ -82,19 +85,19 @@ async function toggleReaction() { } misskeyApi('notes/reactions/delete', { - noteId: props.note.id, + noteId: props.noteId, }).then(() => { - noteEvents.emit(`unreacted:${props.note.id}`, { + noteEvents.emit(`unreacted:${props.noteId}`, { userId: $i!.id, reaction: props.reaction, emoji: emoji.value, }); if (oldReaction !== props.reaction) { misskeyApi('notes/reactions/create', { - noteId: props.note.id, + noteId: props.noteId, reaction: props.reaction, }).then(() => { - noteEvents.emit(`reacted:${props.note.id}`, { + noteEvents.emit(`reacted:${props.noteId}`, { userId: $i!.id, reaction: props.reaction, emoji: emoji.value, @@ -120,18 +123,19 @@ async function toggleReaction() { } misskeyApi('notes/reactions/create', { - noteId: props.note.id, + noteId: props.noteId, reaction: props.reaction, }).then(() => { - noteEvents.emit(`reacted:${props.note.id}`, { + noteEvents.emit(`reacted:${props.noteId}`, { userId: $i!.id, reaction: props.reaction, emoji: emoji.value, }); }); - if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) { - claimAchievement('reactWithoutRead'); - } + // TODO: 上位コンポーネントでやる + //if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) { + // claimAchievement('reactWithoutRead'); + //} } } @@ -175,7 +179,7 @@ onMounted(() => { if (!mock) { useTooltip(buttonEl, async (showing) => { const reactions = await misskeyApiGet('notes/reactions', { - noteId: props.note.id, + noteId: props.noteId, type: props.reaction, limit: 10, _cacheKey_: props.count, diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index e8cf6c36db..725978179e 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -13,7 +13,17 @@ SPDX-License-Identifier: AGPL-3.0-only :moveClass="$style.transition_x_move" tag="div" :class="$style.root" > - + @@ -27,7 +37,10 @@ import { prefer } from '@/preferences.js'; import { DI } from '@/di.js'; const props = withDefaults(defineProps<{ - note: Misskey.entities.Note; + noteId: Misskey.entities.Note['id']; + reactions: Misskey.entities.Note['reactions']; + reactionEmojis: Misskey.entities.Note['reactionEmojis']; + myReaction: Misskey.entities.Note['myReaction']; maxNumber?: number; }>(), { maxNumber: Infinity, @@ -39,33 +52,33 @@ const emit = defineEmits<{ (ev: 'mockUpdateMyReaction', emoji: string, delta: number): void; }>(); -const initialReactions = new Set(Object.keys(props.note.reactions)); +const initialReactions = new Set(Object.keys(props.reactions)); -const reactions = ref<[string, number][]>([]); +const _reactions = ref<[string, number][]>([]); const hasMoreReactions = ref(false); -if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) { - reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction]; +if (props.myReaction && !Object.keys(_reactions.value).includes(props.myReaction)) { + _reactions.value[props.myReaction] = props.reactions[props.myReaction]; } function onMockToggleReaction(emoji: string, count: number) { if (!mock) return; - const i = reactions.value.findIndex((item) => item[0] === emoji); + const i = _reactions.value.findIndex((item) => item[0] === emoji); if (i < 0) return; - emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1])); + emit('mockUpdateMyReaction', emoji, (count - _reactions.value[i][1])); } -watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => { +watch([() => props.reactions, () => props.maxNumber], ([newSource, maxNumber]) => { let newReactions: [string, number][] = []; hasMoreReactions.value = Object.keys(newSource).length > maxNumber; - for (let i = 0; i < reactions.value.length; i++) { - const reaction = reactions.value[i][0]; + for (let i = 0; i < _reactions.value.length; i++) { + const reaction = _reactions.value[i][0]; if (reaction in newSource && newSource[reaction] !== 0) { - reactions.value[i][1] = newSource[reaction]; - newReactions.push(reactions.value[i]); + _reactions.value[i][1] = newSource[reaction]; + newReactions.push(_reactions.value[i]); } } @@ -79,11 +92,11 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe newReactions = newReactions.slice(0, props.maxNumber); - if (props.note.myReaction && !newReactions.map(([x]) => x).includes(props.note.myReaction)) { - newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]); + if (props.myReaction && !newReactions.map(([x]) => x).includes(props.myReaction)) { + newReactions.push([props.myReaction, newSource[props.myReaction]]); } - reactions.value = newReactions; + _reactions.value = newReactions; }, { immediate: true, deep: true }); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 5800beb3a0..9b28573712 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -321,6 +321,7 @@ refreshEndpointAndChannel(); const paginator = usePagination({ ctx: paginationQuery, + useShallowRef: true, }); onUnmounted(() => { diff --git a/packages/frontend/src/pages/welcome.timeline.note.vue b/packages/frontend/src/pages/welcome.timeline.note.vue index 680fe08c14..62a220d2f1 100644 --- a/packages/frontend/src/pages/welcome.timeline.note.vue +++ b/packages/frontend/src/pages/welcome.timeline.note.vue @@ -27,7 +27,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- + +
diff --git a/packages/frontend/src/use/use-note-capture.ts b/packages/frontend/src/use/use-note-capture.ts index c0d7bbfc65..4d5cb6be0f 100644 --- a/packages/frontend/src/use/use-note-capture.ts +++ b/packages/frontend/src/use/use-note-capture.ts @@ -6,7 +6,7 @@ import { onUnmounted } from 'vue'; import * as Misskey from 'misskey-js'; import { EventEmitter } from 'eventemitter3'; -import type { Ref, ShallowRef } from 'vue'; +import type { Ref } from 'vue'; import { useStream } from '@/stream.js'; import { $i } from '@/i.js'; import { store } from '@/store.js'; @@ -28,7 +28,7 @@ const pollingQueue = new Map(); -function pollingEnqueue(note: Misskey.entities.Note) { +function pollingEnqueue(note: Pick) { if (pollingQueue.has(note.id)) { const data = pollingQueue.get(note.id)!; pollingQueue.set(note.id, { @@ -44,7 +44,7 @@ function pollingEnqueue(note: Misskey.entities.Note) { } } -function pollingDequeue(note: Misskey.entities.Note) { +function pollingDequeue(note: Pick) { const data = pollingQueue.get(note.id); if (data == null) return; @@ -85,28 +85,31 @@ window.setInterval(() => { }, POLLING_INTERVAL); function pollingSubscribe(props: { - note: Ref; + note: Pick; + reactionsRef: Ref; + reactionCountRef: Ref; + reactionEmojisRef: Ref; isDeletedRef: Ref; }) { - const note = props.note; + const { note, reactionsRef, reactionCountRef, reactionEmojisRef } = props; function onFetched(data: Pick): void { - note.value.reactions = data.reactions; - note.value.reactionCount = Object.values(data.reactions).reduce((a, b) => a + b, 0); - note.value.reactionEmojis = data.reactionEmojis; + reactionsRef.value = data.reactions; + reactionCountRef.value = Object.values(data.reactions).reduce((a, b) => a + b, 0); + reactionEmojisRef.value = data.reactionEmojis; } - pollingEnqueue(note.value); - fetchEvent.on(note.value.id, onFetched); + pollingEnqueue(note); + fetchEvent.on(note.id, onFetched); onUnmounted(() => { - pollingDequeue(note.value); - fetchEvent.off(note.value.id, onFetched); + pollingDequeue(note); + fetchEvent.off(note.id, onFetched); }); } function realtimeSubscribe(props: { - note: Ref; + note: Pick; isDeletedRef: Ref; }): void { const note = props.note; @@ -115,7 +118,7 @@ function realtimeSubscribe(props: { function onStreamNoteUpdated(noteData): void { const { type, id, body } = noteData; - if (id !== note.value.id) return; + if (id !== note.id) return; switch (type) { case 'reacted': { @@ -152,12 +155,12 @@ function realtimeSubscribe(props: { } function capture(withHandler = false): void { - connection.send('sr', { id: note.value.id }); + connection.send('sr', { id: note.id }); if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated); } function decapture(withHandler = false): void { - connection.send('un', { id: note.value.id }); + connection.send('un', { id: note.id }); if (withHandler) connection.off('noteUpdated', onStreamNoteUpdated); } @@ -175,39 +178,42 @@ function realtimeSubscribe(props: { } export function useNoteCapture(props: { - note: Ref; - parentNote: Ref | null; + note: Pick; + parentNote: Misskey.entities.Note | null; + reactionsRef: Ref; + reactionCountRef: Ref; + reactionEmojisRef: Ref; + myReactionRef: Ref; + pollChoicesRef: Ref['choices'] | null>; isDeletedRef: Ref; }) { - const note = props.note; - const parentNote = props.parentNote; + const { note, parentNote, reactionsRef, reactionCountRef, reactionEmojisRef, myReactionRef, pollChoicesRef } = props; - noteEvents.on(`reacted:${note.value.id}`, onReacted); - noteEvents.on(`unreacted:${note.value.id}`, onUnreacted); - noteEvents.on(`pollVoted:${note.value.id}`, onPollVoted); - noteEvents.on(`deleted:${note.value.id}`, onDeleted); + noteEvents.on(`reacted:${note.id}`, onReacted); + noteEvents.on(`unreacted:${note.id}`, onUnreacted); + noteEvents.on(`pollVoted:${note.id}`, onPollVoted); + noteEvents.on(`deleted:${note.id}`, onDeleted); let latestReactedKey: string | null = null; let latestUnreactedKey: string | null = null; let latestPollVotedKey: string | null = null; function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void { - console.log('reacted', ctx); const newReactedKey = `${ctx.userId}:${ctx.reaction}`; if (newReactedKey === latestReactedKey) return; latestReactedKey = newReactedKey; - if (ctx.emoji && !(ctx.emoji.name in note.value.reactionEmojis)) { - note.value.reactionEmojis[ctx.emoji.name] = ctx.emoji.url; + if (ctx.emoji && !(ctx.emoji.name in reactionEmojisRef.value)) { + reactionEmojisRef.value[ctx.emoji.name] = ctx.emoji.url; } - const currentCount = note.value.reactions[ctx.reaction] || 0; + const currentCount = reactionsRef.value[ctx.reaction] || 0; - note.value.reactions[ctx.reaction] = currentCount + 1; - note.value.reactionCount += 1; + reactionsRef.value[ctx.reaction] = currentCount + 1; + reactionCountRef.value += 1; if ($i && (ctx.userId === $i.id)) { - note.value.myReaction = ctx.reaction; + myReactionRef.value = ctx.reaction; } } @@ -216,14 +222,14 @@ export function useNoteCapture(props: { if (newUnreactedKey === latestUnreactedKey) return; latestUnreactedKey = newUnreactedKey; - const currentCount = note.value.reactions[ctx.reaction] || 0; + const currentCount = reactionsRef.value[ctx.reaction] || 0; - note.value.reactions[ctx.reaction] = Math.max(0, currentCount - 1); - note.value.reactionCount = Math.max(0, note.value.reactionCount - 1); - if (note.value.reactions[ctx.reaction] === 0) delete note.value.reactions[ctx.reaction]; + reactionsRef.value[ctx.reaction] = Math.max(0, currentCount - 1); + reactionCountRef.value = Math.max(0, reactionCountRef.value - 1); + if (reactionsRef.value[ctx.reaction] === 0) delete reactionsRef.value[ctx.reaction]; if ($i && (ctx.userId === $i.id)) { - note.value.myReaction = null; + myReactionRef.value = null; } } @@ -232,7 +238,7 @@ export function useNoteCapture(props: { if (newPollVotedKey === latestPollVotedKey) return; latestPollVotedKey = newPollVotedKey; - const choices = [...note.value.poll.choices]; + const choices = [...pollChoicesRef.value]; choices[ctx.choice] = { ...choices[ctx.choice], votes: choices[ctx.choice].votes + 1, @@ -241,7 +247,7 @@ export function useNoteCapture(props: { } : {}), }; - note.value.poll.choices = choices; + pollChoicesRef.value = choices; } function onDeleted(): void { @@ -249,22 +255,22 @@ export function useNoteCapture(props: { } onUnmounted(() => { - noteEvents.off(`reacted:${note.value.id}`, onReacted); - noteEvents.off(`unreacted:${note.value.id}`, onUnreacted); - noteEvents.off(`pollVoted:${note.value.id}`, onPollVoted); - noteEvents.off(`deleted:${note.value.id}`, onDeleted); + noteEvents.off(`reacted:${note.id}`, onReacted); + noteEvents.off(`unreacted:${note.id}`, onUnreacted); + noteEvents.off(`pollVoted:${note.id}`, onPollVoted); + noteEvents.off(`deleted:${note.id}`, onDeleted); }); // 投稿からある程度経過している(=タイムラインを遡って表示した)ノートは、イベントが発生する可能性が低いためそもそも購読しない // ただし「リノートされたばかりの過去のノート」(= parentNoteが存在し、かつparentNoteの投稿日時が最近)はイベント発生が考えられるため購読する // TODO: デバイスとサーバーの時計がズレていると不具合の元になるため、ズレを検知して警告を表示するなどのケアが必要かもしれない if (parentNote == null) { - if ((Date.now() - new Date(note.value.createdAt).getTime()) > 1000 * 60 * 5) { // 5min + if ((Date.now() - new Date(note.createdAt).getTime()) > 1000 * 60 * 5) { // 5min // リノートで表示されているノートでもないし、投稿からある程度経過しているので購読しない return; } } else { - if ((Date.now() - new Date(parentNote.value.createdAt).getTime()) > 1000 * 60 * 5) { // 5min + if ((Date.now() - new Date(parentNote.createdAt).getTime()) > 1000 * 60 * 5) { // 5min // リノートで表示されているノートだが、リノートされてからある程度経過しているので購読しない return; } diff --git a/packages/frontend/src/use/use-pagination.ts b/packages/frontend/src/use/use-pagination.ts index e8d25c3473..12ce2a7f7d 100644 --- a/packages/frontend/src/use/use-pagination.ts +++ b/packages/frontend/src/use/use-pagination.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, isRef, onMounted, ref, watch } from 'vue'; +import { computed, isRef, onMounted, ref, shallowRef, triggerRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import type { ComputedRef, Ref, ShallowRef } from 'vue'; import { misskeyApi } from '@/utility/misskey-api.js'; @@ -40,9 +40,10 @@ export type PagingCtx(props: { ctx: PagingCtx; + useShallowRef?: boolean; }) { - const items = ref([]); - const queue = ref([]); + const items = props.useShallowRef ? shallowRef([]) : ref([]); + const queue = props.useShallowRef ? shallowRef([]) : ref([]); const fetching = ref(true); const moreFetching = ref(false); const canFetchMore = ref(false); @@ -142,8 +143,10 @@ export function usePagination(props: { }).then(res => { if (options.toQueue) { queue.value.unshift(...res.toReversed()); + if (props.useShallowRef) triggerRef(queue); } else { items.value.unshift(...res.toReversed()); + if (props.useShallowRef) triggerRef(items); } }); } @@ -155,18 +158,22 @@ export function usePagination(props: { function unshiftItems(newItems: T[]) { items.value.unshift(...newItems); + if (props.useShallowRef) triggerRef(items); } function pushItems(oldItems: T[]) { items.value.push(...oldItems); + if (props.useShallowRef) triggerRef(items); } function prepend(item: T) { items.value.unshift(item); + if (props.useShallowRef) triggerRef(items); } function enqueue(item: T) { queue.value.unshift(item); + if (props.useShallowRef) triggerRef(queue); } function releaseQueue() { From 5ac2116449cbe7973ccf83b64871acf3d4dbb14a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 1 May 2025 21:46:16 +0900 Subject: [PATCH 35/85] wip --- packages/frontend/src/components/MkNote.vue | 45 +++++++------- .../src/components/MkNoteDetailed.vue | 41 ++++++------- packages/frontend/src/use/use-note-capture.ts | 60 +++++++++---------- 3 files changed, 70 insertions(+), 76 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 936c17d55b..6e35d1dba9 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only :noteId="appearNote.id" :multiple="appearNote.poll.multiple" :expiresAt="appearNote.poll.expiresAt" - :choices="pollChoices" + :choices="$appearNote.pollChoices" :author="appearNote.user" :emojiUrls="appearNote.emojis" :class="$style.poll" @@ -113,9 +113,9 @@ SPDX-License-Identifier: AGPL-3.0-only From dcaf020afb58b495b7f95596cee92fdc040d0341 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 15:03:37 +0900 Subject: [PATCH 53/85] Update MkTimeline.vue --- 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 2c3b8c48ce..5fc619e169 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -456,7 +456,7 @@ defineExpose({ margin: auto; background: var(--MI_THEME-accent); color: var(--MI_THEME-fgOnAccent); - font-size: 85%; + font-size: 90%; &:hover { background: hsl(from var(--MI_THEME-accent) h s calc(l + 5)); From ef82d103ff552a6e8ffa9fceb870156a2db7d49b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 15:42:28 +0900 Subject: [PATCH 54/85] Update MkTimeline.vue --- .../frontend/src/components/MkTimeline.vue | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 5fc619e169..7b083831b1 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only 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 { isHeadVisible, scrollToTop } from '@@/js/scroll.js'; +import { getScrollContainer, 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'; @@ -98,7 +98,35 @@ provide('inTimeline', true); provide('tl_withSensitive', computed(() => props.withSensitive)); provide('inChannel', computed(() => props.src === 'channel')); +function isTop() { + if (scrollContainer == null) return false; + if (rootEl.value == null) return false; + const scrollTop = scrollContainer.scrollTop; + const tlTop = rootEl.value.offsetTop - scrollContainer.offsetTop; + return scrollTop <= tlTop; +} + +let scrollContainer: HTMLElement | null = null; + +function onScrollContainerScroll() { + if (isTop()) { + paginator.releaseQueue(); + } +} + const rootEl = useTemplateRef('rootEl'); +watch(rootEl, (el) => { + if (el && scrollContainer == null) { + scrollContainer = getScrollContainer(el)!; + scrollContainer.addEventListener('scroll', onScrollContainerScroll, { passive: true }); // ほんとはscrollendにしたいけどiosが非対応 + } +}, { immediate: true }); + +onUnmounted(() => { + if (scrollContainer) { + scrollContainer.removeEventListener('scroll', onScrollContainerScroll); + } +}); type TimelineQueryType = { antennaId?: string, @@ -122,9 +150,8 @@ const POLLING_INTERVAL = if (!store.s.realtimeMode) { useInterval(async () => { - const isTop = rootEl.value == null ? false : isHeadVisible(rootEl.value, 16); paginator.fetchNewer({ - toQueue: !isTop, + toQueue: !isTop(), }); }, POLLING_INTERVAL, { immediate: false, @@ -133,9 +160,8 @@ if (!store.s.realtimeMode) { } globalEvents.on('notePosted', (note: Misskey.entities.Note) => { - const isTop = rootEl.value == null ? false : isHeadVisible(rootEl.value, 16); paginator.fetchNewer({ - toQueue: !isTop, + toQueue: !isTop(), }); }); @@ -151,8 +177,7 @@ function prepend(note: Misskey.entities.Note) { note._shouldInsertAd_ = true; } - const isTop = isHeadVisible(rootEl.value, 16); - if (isTop) { + if (isTop()) { paginator.prepend(note); } else { paginator.enqueue(note); From 7b5634908bd154888a7a11a85f434ed3e83dd667 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 16:13:16 +0900 Subject: [PATCH 55/85] =?UTF-8?q?=E2=9C=8C=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkTimeline.vue | 1 + packages/frontend/src/use/use-pagination.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 7b083831b1..dc2dc330dd 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -149,6 +149,7 @@ const POLLING_INTERVAL = MIN_POLLING_INTERVAL; if (!store.s.realtimeMode) { + // TODO: 先頭のノートの作成日時が1日以上前であれば流速が遅いTLと見做してインターバルを通常より延ばす useInterval(async () => { paginator.fetchNewer({ toQueue: !isTop(), diff --git a/packages/frontend/src/use/use-pagination.ts b/packages/frontend/src/use/use-pagination.ts index 645433290d..ec34d38901 100644 --- a/packages/frontend/src/use/use-pagination.ts +++ b/packages/frontend/src/use/use-pagination.ts @@ -54,7 +54,7 @@ export function usePagination(props: { // パラメータに何らかの変更があった際、再読込したい(チャンネル等のIDが変わったなど) watch(() => [props.ctx.endpoint, props.ctx.params], init, { deep: true }); - function getNewestId() { + function getNewestId(): string | null | undefined { // 様々な要因により並び順は保証されないのでソートが必要 if (aheadQueue.length > 0) { return aheadQueue.map(x => x.id).sort().at(-1); @@ -62,7 +62,7 @@ export function usePagination(props: { return items.value.map(x => x.id).sort().at(-1); } - function getOldestId() { + function getOldestId(): string | null | undefined { // 様々な要因により並び順は保証されないのでソートが必要 return items.value.map(x => x.id).sort().at(0); } From 85679b33b64140318fb37dce4d0dbd89cf9b7d09 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 20:28:13 +0900 Subject: [PATCH 56/85] Update MkTimeline.vue --- packages/frontend/src/components/MkTimeline.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index dc2dc330dd..54562c7529 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -91,9 +91,6 @@ const props = withDefaults(defineProps<{ onlyFiles: false, }); -const emit = defineEmits<{ -}>(); - provide('inTimeline', true); provide('tl_withSensitive', computed(() => props.withSensitive)); provide('inChannel', computed(() => props.src === 'channel')); From 668fd7b4cdd7b9b0750a19dab707820e87a58f64 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 20:47:50 +0900 Subject: [PATCH 57/85] wip --- packages/frontend/src/components/MkRange.vue | 7 +++++++ packages/frontend/src/pages/settings/preferences.vue | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 4b2e6910db..f36e68b687 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
@@ -25,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only @touchstart="onMousedown" >
+
@@ -224,12 +226,17 @@ function onMousedown(ev: MouseEvent | TouchEvent) { $thumbWidth: 20px; > .body { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; padding: 7px 12px; background: var(--MI_THEME-panel); border: solid 1px var(--MI_THEME-panel); border-radius: 6px; > .container { + flex: 1; position: relative; height: $thumbHeight; diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 8480ff4ddb..7e72d81bcd 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -51,9 +51,11 @@ SPDX-License-Identifier: AGPL-3.0-only - + + + From 6c45aeec325a2f3d9a58cb4a338f0e56919885bc Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 2 May 2025 20:54:03 +0900 Subject: [PATCH 58/85] wip --- packages/misskey-js/etc/misskey-js.api.md | 8 +++ .../misskey-js/src/autogen/apiClientJSDoc.ts | 11 ++++ packages/misskey-js/src/autogen/endpoint.ts | 3 + packages/misskey-js/src/autogen/entities.ts | 2 + packages/misskey-js/src/autogen/types.ts | 62 +++++++++++++++++++ 5 files changed, 86 insertions(+) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 7069d32317..329a12531c 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1956,6 +1956,8 @@ declare namespace entities { NotesSearchByTagResponse, NotesShowRequest, NotesShowResponse, + NotesShowPartialBulkRequest, + NotesShowPartialBulkResponse, NotesStateRequest, NotesStateResponse, NotesThreadMutingCreateRequest, @@ -3050,6 +3052,12 @@ type NotesSearchRequest = operations['notes___search']['requestBody']['content'] // @public (undocumented) type NotesSearchResponse = operations['notes___search']['responses']['200']['content']['application/json']; +// @public (undocumented) +type NotesShowPartialBulkRequest = operations['notes___show-partial-bulk']['requestBody']['content']['application/json']; + +// @public (undocumented) +type NotesShowPartialBulkResponse = operations['notes___show-partial-bulk']['responses']['200']['content']['application/json']; + // @public (undocumented) type NotesShowRequest = operations['notes___show']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index b607c93e1e..12b51a4ac0 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -3758,6 +3758,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *No* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 56224cdbaf..13adaa9efd 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -512,6 +512,8 @@ import type { NotesSearchByTagResponse, NotesShowRequest, NotesShowResponse, + NotesShowPartialBulkRequest, + NotesShowPartialBulkResponse, NotesStateRequest, NotesStateResponse, NotesThreadMutingCreateRequest, @@ -971,6 +973,7 @@ export type Endpoints = { 'notes/search': { req: NotesSearchRequest; res: NotesSearchResponse }; 'notes/search-by-tag': { req: NotesSearchByTagRequest; res: NotesSearchByTagResponse }; 'notes/show': { req: NotesShowRequest; res: NotesShowResponse }; + 'notes/show-partial-bulk': { req: NotesShowPartialBulkRequest; res: NotesShowPartialBulkResponse }; 'notes/state': { req: NotesStateRequest; res: NotesStateResponse }; 'notes/thread-muting/create': { req: NotesThreadMutingCreateRequest; res: EmptyResponse }; 'notes/thread-muting/delete': { req: NotesThreadMutingDeleteRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index b5370e99fa..2030e8ae5d 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -515,6 +515,8 @@ export type NotesSearchByTagRequest = operations['notes___search-by-tag']['reque export type NotesSearchByTagResponse = operations['notes___search-by-tag']['responses']['200']['content']['application/json']; export type NotesShowRequest = operations['notes___show']['requestBody']['content']['application/json']; export type NotesShowResponse = operations['notes___show']['responses']['200']['content']['application/json']; +export type NotesShowPartialBulkRequest = operations['notes___show-partial-bulk']['requestBody']['content']['application/json']; +export type NotesShowPartialBulkResponse = operations['notes___show-partial-bulk']['responses']['200']['content']['application/json']; export type NotesStateRequest = operations['notes___state']['requestBody']['content']['application/json']; export type NotesStateResponse = operations['notes___state']['responses']['200']['content']['application/json']; export type NotesThreadMutingCreateRequest = operations['notes___thread-muting___create']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index c83e1f1fbe..4fb625ef5f 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3247,6 +3247,15 @@ export type paths = { */ post: operations['notes___show']; }; + '/notes/show-partial-bulk': { + /** + * notes/show-partial-bulk + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['notes___show-partial-bulk']; + }; '/notes/state': { /** * notes/state @@ -25736,6 +25745,59 @@ export type operations = { }; }; }; + /** + * notes/show-partial-bulk + * @description No description provided. + * + * **Credential required**: *No* + */ + 'notes___show-partial-bulk': { + requestBody: { + content: { + 'application/json': { + noteIds: string[]; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': Record[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * notes/state * @description No description provided. From 53948b54698189b05a21f65002ed5d72022b5a3b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 08:20:21 +0900 Subject: [PATCH 59/85] test --- .../src/components/MkPullToRefresh.vue | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index 22ae563d13..bf49b6ec1e 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -39,7 +39,6 @@ const isPullEnd = ref(false); const isRefreshing = ref(false); const pullDistance = ref(0); -let supportPointerDesktop = false; let startScreenY: number | null = null; const rootEl = useTemplateRef('rootEl'); @@ -58,10 +57,11 @@ const emit = defineEmits<{ }>(); function getScreenY(event) { - if (supportPointerDesktop) { + if (event.touches && event.touches[0] && event.touches[0].screenY != null) { + return event.touches[0].screenY; + } else { return event.screenY; } - return event.touches[0].screenY; } function moveStart(event) { @@ -72,6 +72,16 @@ function moveStart(event) { } } +function moveStartByMouse(event: MouseEvent) { + if (!isPullStart.value && !isRefreshing.value && !disabled) { + isPullStart.value = true; + startScreenY = event.screenY; + pullDistance.value = 0; + window.addEventListener('mousemove', moving, { passive: false }); + window.addEventListener('mouseup', moveEnd, { passive: true, once: true }); + } +} + function moveBySystem(to: number): Promise { return new Promise(r => { const startHeight = pullDistance.value; @@ -129,7 +139,7 @@ function moveEnd() { function moving(event: TouchEvent | PointerEvent) { if (!isPullStart.value || isRefreshing.value || disabled) return; - if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value) || isHorizontalSwipeSwiping.value) { + if ((scrollEl?.scrollTop ?? 0) > SCROLL_STOP + pullDistance.value || isHorizontalSwipeSwiping.value) { pullDistance.value = 0; isPullEnd.value = false; moveEnd(); @@ -186,14 +196,17 @@ function onScrollContainerScroll() { function registerEventListenersForReadyToPull() { if (rootEl.value == null) return; + rootEl.value.addEventListener('mousedown', moveStartByMouse, { passive: true }); rootEl.value.addEventListener('touchstart', moveStart, { passive: true }); rootEl.value.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない } function unregisterEventListenersForReadyToPull() { if (rootEl.value == null) return; + rootEl.value.removeEventListener('mousedown', moveStartByMouse); rootEl.value.removeEventListener('touchstart', moveStart); rootEl.value.removeEventListener('touchmove', moving); + window.removeEventListener('pointermove', moving); } onMounted(() => { From 4ee5811c2f219a2fe44448910101b82a855b58f2 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 08:28:26 +0900 Subject: [PATCH 60/85] Update MkPullToRefresh.vue --- packages/frontend/src/components/MkPullToRefresh.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index bf49b6ec1e..99ebc25077 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -136,7 +136,7 @@ function moveEnd() { } } -function moving(event: TouchEvent | PointerEvent) { +function moving(event: TouchEvent | PointerEvent | MouseEvent) { if (!isPullStart.value || isRefreshing.value || disabled) return; if ((scrollEl?.scrollTop ?? 0) > SCROLL_STOP + pullDistance.value || isHorizontalSwipeSwiping.value) { @@ -206,7 +206,7 @@ function unregisterEventListenersForReadyToPull() { rootEl.value.removeEventListener('mousedown', moveStartByMouse); rootEl.value.removeEventListener('touchstart', moveStart); rootEl.value.removeEventListener('touchmove', moving); - window.removeEventListener('pointermove', moving); + window.removeEventListener('mousemove', moving); } onMounted(() => { From 84fe933863a70def9402b9a45340ec299162718d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 09:52:06 +0900 Subject: [PATCH 61/85] Update MkPullToRefresh.vue --- .../src/components/MkPullToRefresh.vue | 161 ++++++++---------- 1 file changed, 72 insertions(+), 89 deletions(-) diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index 99ebc25077..d1e2368a70 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -111,15 +116,16 @@ const router = useRouter(); const props = defineProps<{ showWidgetButton?: boolean; + asDrawer?: boolean; }>(); const emit = defineEmits<{ (ev: 'widgetButtonClick'): void; }>(); -const forceIconOnly = ref(window.innerWidth <= 1279); +const forceIconOnly = ref(!props.asDrawer && window.innerWidth <= 1279); const iconOnly = computed(() => { - return forceIconOnly.value || (store.r.menuDisplay.value === 'sideIcon'); + return !props.asDrawer && (forceIconOnly.value || (store.r.menuDisplay.value === 'sideIcon')); }); const otherMenuItemIndicated = computed(() => { @@ -208,13 +214,51 @@ function menuEdit() { overscroll-behavior: contain; background: var(--MI_THEME-navBg); contain: strict; - display: flex; - flex-direction: column; direction: rtl; // スクロールバーを左に表示したいため } .top { direction: ltr; + + /* 疑似progressive blur */ + &::before { + position: absolute; + z-index: -1; + inset: 0; + content: ""; + backdrop-filter: blur(8px); + mask-image: linear-gradient( + to top, + rgb(0 0 0 / 0%) 0%, + rgb(0 0 0 / 4.9%) 7.75%, + rgb(0 0 0 / 10.4%) 11.25%, + rgb(0 0 0 / 45%) 23.55%, + rgb(0 0 0 / 55%) 26.45%, + rgb(0 0 0 / 89.6%) 38.75%, + rgb(0 0 0 / 95.1%) 42.25%, + rgb(0 0 0 / 100%) 50% + ); + } + + &::after { + position: absolute; + z-index: -1; + inset: 0; + bottom: 25%; + content: ""; + backdrop-filter: blur(16px); + mask-image: linear-gradient( + to top, + rgb(0 0 0 / 0%) 0%, + rgb(0 0 0 / 4.9%) 15.5%, + rgb(0 0 0 / 10.4%) 22.5%, + rgb(0 0 0 / 45%) 47.1%, + rgb(0 0 0 / 55%) 52.9%, + rgb(0 0 0 / 89.6%) 77.5%, + rgb(0 0 0 / 95.1%) 91.9%, + rgb(0 0 0 / 100%) 100% + ); + } } .middle { @@ -223,6 +267,47 @@ function menuEdit() { .bottom { direction: ltr; + + /* 疑似progressive blur */ + &::before { + position: absolute; + z-index: -1; + inset: -30px 0 0 0; + content: ""; + backdrop-filter: blur(8px); + mask-image: linear-gradient( + to bottom, + rgb(0 0 0 / 0%) 0%, + rgb(0 0 0 / 4.9%) 7.75%, + rgb(0 0 0 / 10.4%) 11.25%, + rgb(0 0 0 / 45%) 23.55%, + rgb(0 0 0 / 55%) 26.45%, + rgb(0 0 0 / 89.6%) 38.75%, + rgb(0 0 0 / 95.1%) 42.25%, + rgb(0 0 0 / 100%) 50% + ); + pointer-events: none; + } + + &::after { + position: absolute; + z-index: -1; + inset: 0; + top: 25%; + content: ""; + backdrop-filter: blur(16px); + mask-image: linear-gradient( + to bottom, + rgb(0 0 0 / 0%) 0%, + rgb(0 0 0 / 4.9%) 15.5%, + rgb(0 0 0 / 10.4%) 22.5%, + rgb(0 0 0 / 45%) 47.1%, + rgb(0 0 0 / 55%) 52.9%, + rgb(0 0 0 / 89.6%) 77.5%, + rgb(0 0 0 / 95.1%) 91.9%, + rgb(0 0 0 / 100%) 100% + ); + } } .subButtons { @@ -307,29 +392,18 @@ function menuEdit() { } .top { + --top-height: 80px; + position: sticky; top: 0; z-index: 1; - padding: 20px 0; - background: var(--nav-bg-transparent); - -webkit-backdrop-filter: var(--MI-blur, blur(8px)); - backdrop-filter: var(--MI-blur, blur(8px)); + display: flex; + height: var(--top-height); } .instance { position: relative; - display: block; - text-align: center; - width: 100%; - - &:focus-visible { - outline: none; - - > .instanceIcon { - outline: 2px solid var(--MI_THEME-focus); - outline-offset: 2px; - } - } + width: var(--top-height); } .instanceIcon { @@ -339,26 +413,22 @@ function menuEdit() { border-radius: 8px; } - .bottom { - position: sticky; - bottom: 0; - background: var(--nav-bg-transparent); - -webkit-backdrop-filter: var(--MI-blur, blur(8px)); - backdrop-filter: var(--MI-blur, blur(8px)); - } - .realtimeMode { - display: block; - position: relative; - width: 100%; - height: 52px; - text-align: center; + display: inline-block; + width: var(--top-height); + margin-left: auto; &.on { color: var(--MI_THEME-accent); } } + .bottom { + position: sticky; + bottom: 0; + padding-top: 20px; + } + .post { position: relative; display: block; @@ -548,9 +618,6 @@ function menuEdit() { top: 0; z-index: 1; padding: 20px 0; - background: var(--nav-bg-transparent); - -webkit-backdrop-filter: var(--MI-blur, blur(8px)); - backdrop-filter: var(--MI-blur, blur(8px)); } .instance { @@ -579,9 +646,6 @@ function menuEdit() { position: sticky; bottom: 0; padding-top: 20px; - background: var(--nav-bg-transparent); - -webkit-backdrop-filter: var(--MI-blur, blur(8px)); - backdrop-filter: var(--MI-blur, blur(8px)); } .widget { @@ -690,7 +754,7 @@ function menuEdit() { .item { display: block; position: relative; - padding: 18px 0; + padding: 16px 0; width: 100%; text-align: center; From dafc6b2d455cd2bd340d5456772207da0b1391cd Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 12:42:34 +0900 Subject: [PATCH 67/85] wip --- packages/frontend/src/components/MkPullToRefresh.vue | 6 ++---- packages/frontend/src/components/MkTimeline.vue | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index ad828c50b6..ff310233db 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -204,16 +204,14 @@ function refreshFinished() { onMounted(() => { if (rootEl.value == null) return; - scrollEl = getScrollContainer(rootEl.value); - rootEl.value.addEventListener('pointerdown', moveStart, { passive: true }); rootEl.value.addEventListener('touchend', toggleScrollLockOnTouchEnd, { passive: true }); }); onUnmounted(() => { - rootEl.value.removeEventListener('pointerdown', moveStart); - rootEl.value.removeEventListener('touchend', toggleScrollLockOnTouchEnd); + if (rootEl.value) rootEl.value.removeEventListener('pointerdown', moveStart); + if (rootEl.value) rootEl.value.removeEventListener('touchend', toggleScrollLockOnTouchEnd); }); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index da31761e63..bc23497d54 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -96,8 +96,8 @@ provide('tl_withSensitive', computed(() => props.withSensitive)); provide('inChannel', computed(() => props.src === 'channel')); function isTop() { - if (scrollContainer == null) return false; - if (rootEl.value == null) return false; + if (scrollContainer == null) return true; + if (rootEl.value == null) return true; const scrollTop = scrollContainer.scrollTop; const tlTop = rootEl.value.offsetTop - scrollContainer.offsetTop; return scrollTop <= tlTop; From 8cbd3dcd428127e6edd390c095ecaa5a13b0276b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 13:16:59 +0900 Subject: [PATCH 68/85] wip --- packages/frontend/src/components/MkNotes.vue | 51 +++++++++++-------- packages/frontend/src/pages/tag.vue | 2 +- .../frontend/src/ui/deck/direct-column.vue | 2 +- .../frontend/src/ui/deck/mentions-column.vue | 4 +- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index 9d862a4eac..763664765e 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -4,37 +4,40 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index e1dffd4f2d..6ce5d276b5 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -48,7 +48,7 @@ async function post() { await os.post(); store.set('postFormHashtags', ''); store.set('postFormWithHashtags', false); - notes.value?.pagingComponent?.reload(); + notes.value?.reload(); } const headerActions = computed(() => [{ diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue index 772188d773..3a90b1cfb5 100644 --- a/packages/frontend/src/ui/deck/direct-column.vue +++ b/packages/frontend/src/ui/deck/direct-column.vue @@ -35,7 +35,7 @@ const tlComponent = ref>(); function reloadTimeline() { return new Promise((res) => { - tlComponent.value?.pagingComponent?.reload().then(() => { + tlComponent.value?.reload().then(() => { res(); }); }); diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue index ffd0307940..0888812db9 100644 --- a/packages/frontend/src/ui/deck/mentions-column.vue +++ b/packages/frontend/src/ui/deck/mentions-column.vue @@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import XColumn from './column.vue'; import type { Column } from '@/deck.js'; +import { i18n } from '@/i18n.js'; import MkNotes from '@/components/MkNotes.vue'; -import { i18n } from '../../i18n.js'; defineProps<{ column: Column; @@ -27,7 +27,7 @@ const tlComponent = ref>(); function reloadTimeline() { return new Promise((res) => { - tlComponent.value?.pagingComponent?.reload().then(() => { + tlComponent.value?.reload().then(() => { res(); }); }); From 39f718c510378d9355e2e93a8aa7fddc962aea22 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 13:20:43 +0900 Subject: [PATCH 69/85] wip --- .../{MkTimeline.vue => MkStreamingNotesTimeline.vue} | 0 packages/frontend/src/components/MkVisitorDashboard.vue | 4 ++-- packages/frontend/src/pages/antenna-timeline.vue | 4 ++-- packages/frontend/src/pages/channel.vue | 4 ++-- packages/frontend/src/pages/role.vue | 4 ++-- packages/frontend/src/pages/timeline.vue | 4 ++-- packages/frontend/src/pages/user-list-timeline.vue | 4 ++-- packages/frontend/src/ui/deck/antenna-column.vue | 4 ++-- packages/frontend/src/ui/deck/channel-column.vue | 4 ++-- packages/frontend/src/ui/deck/list-column.vue | 4 ++-- packages/frontend/src/ui/deck/role-timeline-column.vue | 4 ++-- packages/frontend/src/ui/deck/tl-column.vue | 4 ++-- packages/frontend/src/widgets/WidgetTimeline.vue | 4 ++-- 13 files changed, 24 insertions(+), 24 deletions(-) rename packages/frontend/src/components/{MkTimeline.vue => MkStreamingNotesTimeline.vue} (100%) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue similarity index 100% rename from packages/frontend/src/components/MkTimeline.vue rename to packages/frontend/src/components/MkStreamingNotesTimeline.vue diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 1a4d14a3f0..a809e9040d 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.letsLookAtTimeline }}
- +
@@ -58,7 +58,7 @@ import * as Misskey from 'misskey-js'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import MkButton from '@/components/MkButton.vue'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import MkInfo from '@/components/MkInfo.vue'; import { instanceName } from '@@/js/config.js'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 3e0ef4155d..7d2393dba5 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- import { computed, watch, ref, useTemplateRef } from 'vue'; import * as Misskey from 'misskey-js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { definePage } from '@/page.js'; diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 5ead3b5fe6..0d95bd8ced 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only - +
@@ -76,7 +76,7 @@ import { url } from '@@/js/config.js'; import { useInterval } from '@@/js/use-interval.js'; import type { PageHeaderItem } from '@/types/page-header.js'; import MkPostForm from '@/components/MkPostForm.vue'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import XChannelFollowButton from '@/components/MkChannelFollowButton.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue index 82e5999406..2ee8a03933 100644 --- a/packages/frontend/src/pages/role.vue +++ b/packages/frontend/src/pages/role.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
{{ i18n.ts.nothing }}
@@ -42,7 +42,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import MkUserList from '@/components/MkUserList.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import { serverErrorImageUrl, infoImageUrl } from '@/instance.js'; const props = withDefaults(defineProps<{ diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 65e2d71900..453a48d1bc 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._timelineDescription[src] }} -
- import { computed, watch, ref, useTemplateRef } from 'vue'; import * as Misskey from 'misskey-js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue index 9d4beac028..716f0ba995 100644 --- a/packages/frontend/src/ui/deck/antenna-column.vue +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ column.name || antennaName || i18n.ts._deck._columns.antenna }} - + @@ -21,7 +21,7 @@ import type { Column } from '@/deck.js'; import type { MenuItem } from '@/types/menu.js'; import type { SoundStore } from '@/preferences/def.js'; import { updateColumn } from '@/deck.js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 7de4203d02..3439a2a56e 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- + @@ -26,7 +26,7 @@ import type { Column } from '@/deck.js'; import type { MenuItem } from '@/types/menu.js'; import type { SoundStore } from '@/preferences/def.js'; import { updateColumn } from '@/deck.js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { favoritedChannelsCache } from '@/cache.js'; diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue index 077b39b219..5b7390b1b2 100644 --- a/packages/frontend/src/ui/deck/list-column.vue +++ b/packages/frontend/src/ui/deck/list-column.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ (column.name || listName) ?? i18n.ts._deck._columns.list }} - + @@ -21,7 +21,7 @@ import type { Column } from '@/deck.js'; import type { MenuItem } from '@/types/menu.js'; import type { SoundStore } from '@/preferences/def.js'; import { updateColumn } from '@/deck.js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue index 5ce5cbfc61..ff00dfa6e0 100644 --- a/packages/frontend/src/ui/deck/role-timeline-column.vue +++ b/packages/frontend/src/ui/deck/role-timeline-column.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ column.name || roleName || i18n.ts._deck._columns.roleTimeline }} - + @@ -20,7 +20,7 @@ import type { Column } from '@/deck.js'; import type { MenuItem } from '@/types/menu.js'; import type { SoundStore } from '@/preferences/def.js'; import { updateColumn } from '@/deck.js'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 291bcc49ff..97208f1c6a 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only

{{ i18n.ts._disabledTimeline.description }}

- {{ i18n.ts._disabledTimeline.description }}

- +
@@ -38,7 +38,7 @@ import type { GetFormResultType } from '@/utility/form.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; -import MkTimeline from '@/components/MkTimeline.vue'; +import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; import { i18n } from '@/i18n.js'; import { availableBasicTimelines, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; import type { MenuItem } from '@/types/menu.js'; From 69edb2046534092a67ae0e7128e3fdf2672dbd0b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 13:23:42 +0900 Subject: [PATCH 70/85] wip --- .../src/components/{MkNotes.vue => MkNotesTimeline.vue} | 0 packages/frontend/src/pages/channel.vue | 6 +++--- packages/frontend/src/pages/clip.vue | 4 ++-- packages/frontend/src/pages/drive.file.notes.vue | 4 ++-- packages/frontend/src/pages/explore.featured.vue | 6 +++--- packages/frontend/src/pages/note.vue | 6 +++--- packages/frontend/src/pages/notifications.vue | 6 +++--- packages/frontend/src/pages/search.note.vue | 4 ++-- packages/frontend/src/pages/tag.vue | 4 ++-- packages/frontend/src/pages/user/home.vue | 2 +- packages/frontend/src/pages/user/index.timeline.vue | 4 ++-- packages/frontend/src/ui/deck/direct-column.vue | 4 ++-- packages/frontend/src/ui/deck/mentions-column.vue | 4 ++-- 13 files changed, 27 insertions(+), 27 deletions(-) rename packages/frontend/src/components/{MkNotes.vue => MkNotesTimeline.vue} (100%) diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotesTimeline.vue similarity index 100% rename from packages/frontend/src/components/MkNotes.vue rename to packages/frontend/src/components/MkNotesTimeline.vue diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 0d95bd8ced..6eb390f743 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.search }}
- +
{{ i18n.ts.notesSearchNotAvailable }} @@ -84,7 +84,7 @@ import { $i, iAmModerator } from '@/i.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { deviceKind } from '@/utility/device-kind.js'; -import MkNotes from '@/components/MkNotes.vue'; +import MkNotesTimeline from '@/components/MkNotesTimeline.vue'; import { favoritedChannelsCache } from '@/cache.js'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index 68c5d6c270..dc043e2ce1 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
@@ -34,7 +34,7 @@ import { computed, watch, provide, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { url } from '@@/js/config.js'; import type { MenuItem } from '@/types/menu.js'; -import MkNotes from '@/components/MkNotes.vue'; +import MkNotesTimeline from '@/components/MkNotesTimeline.vue'; import { $i } from '@/i.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/drive.file.notes.vue b/packages/frontend/src/pages/drive.file.notes.vue index d7519896cc..5eaf84224b 100644 --- a/packages/frontend/src/pages/drive.file.notes.vue +++ b/packages/frontend/src/pages/drive.file.notes.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -15,7 +15,7 @@ import { ref, computed } from 'vue'; import { i18n } from '@/i18n.js'; import type { Paging } from '@/components/MkPagination.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkNotes from '@/components/MkNotes.vue'; +import MkNotesTimeline from '@/components/MkNotesTimeline.vue'; const props = defineProps<{ fileId: string; diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index a47e3efbc8..b8eb7eb8d5 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -9,14 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only - - + + From c192d0512e07e4b78b04da49c4b683c23b273f54 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 3 May 2025 14:30:33 +0900 Subject: [PATCH 74/85] Update home.vue --- packages/frontend/src/pages/user/home.vue | 269 +++++++++++----------- 1 file changed, 138 insertions(+), 131 deletions(-) diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 19cf2c79e3..23f740ddd0 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -4,158 +4,160 @@ SPDX-License-Identifier: AGPL-3.0-only -->