diff --git a/locales/en-US.yml b/locales/en-US.yml index ad81376f89..b1455430f8 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1266,6 +1266,8 @@ fromX: "From {x}" genEmbedCode: "Generate embed code" noteOfThisUser: "Notes by this user" clipNoteLimitExceeded: "No more notes can be added to this clip." +timeTravel: "Time Travel" +timeTravelDescription: "Show posts before this date." _delivery: status: "Delivery status" stop: "Suspended" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 0d76361d6f..4d2d1f653d 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1277,6 +1277,8 @@ signinWithPasskey: "使用通行密钥登录" unknownWebAuthnKey: "此通行密钥未注册。" passkeyVerificationFailed: "验证通行密钥失败。" passkeyVerificationSucceededButPasswordlessLoginDisabled: "通行密钥验证成功,但账户未开启无密码登录。" +timeTravel: "时光机" +timeTravelDescription: "显示该日期以前的帖子" _delivery: status: "投递状态" stop: "停止投递" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 74c03befd1..26420e7a56 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1283,6 +1283,8 @@ signinWithPasskey: "使用密碼金鑰登入" unknownWebAuthnKey: "未註冊的金鑰。" passkeyVerificationFailed: "驗證金鑰失敗。" passkeyVerificationSucceededButPasswordlessLoginDisabled: "雖然驗證金鑰成功,但是無密碼登入的方式是停用的。" +timeTravel: "時光機" +timeTravelDescription: "回到指定的日期" _delivery: status: "傳送狀態" stop: "停止發送" diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index ca87316bf7..7cb27e1c36 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -61,7 +61,8 @@ type TimelineQueryType = { visibility?: string, listId?: string, channelId?: string, - roleId?: string + roleId?: string, + untilDate?: number, } const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); @@ -89,7 +90,7 @@ function prepend(note) { let connection: Misskey.ChannelConnection | null = null; let connection2: Misskey.ChannelConnection | null = null; -let paginationQuery: Paging | null = null; +const paginationQuery = ref<Paging | null>(null); const stream = useStream(); @@ -124,7 +125,7 @@ function connectChannel() { }); } else if (props.src === 'mentions') { connection = stream.useChannel('main'); - connection.on('mention', prepend); + connection?.on('mention', prepend); } else if (props.src === 'directs') { const onNote = note => { if (note.visibility === 'specified') { @@ -132,7 +133,7 @@ function connectChannel() { } }; connection = stream.useChannel('main'); - connection.on('mention', onNote); + connection?.on('mention', onNote); } else if (props.src === 'list') { if (props.list == null) return; connection = stream.useChannel('userList', { @@ -159,7 +160,7 @@ function disconnectChannel() { if (connection2) connection2.dispose(); } -function updatePaginationQuery() { +function updatePaginationQuery(untilDate?: Date) { let endpoint: keyof Misskey.Endpoints | null; let query: TimelineQueryType | null; @@ -224,14 +225,19 @@ function updatePaginationQuery() { query = null; } + if (untilDate && Number(untilDate)) { + query = query ?? {}; + query.untilDate = Number(untilDate); + } + if (endpoint && query) { - paginationQuery = { + paginationQuery.value = { endpoint: endpoint, limit: 10, params: query, }; } else { - paginationQuery = null; + paginationQuery.value = null; } } @@ -267,7 +273,12 @@ function reloadTimeline() { }); } +function timetravel(date: Date) { + updatePaginationQuery(date); +} + defineExpose({ reloadTimeline, + timetravel, }); </script> diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index f1a451808f..ff58a8f5fc 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -56,6 +56,7 @@ const props = withDefaults(defineProps<{ actions?: PageHeaderItem[] | null; thin?: boolean; displayMyAvatar?: boolean; + hideTitle?: boolean; }>(), { tabs: () => ([] as Tab[]), }); @@ -66,7 +67,7 @@ const emit = defineEmits<{ const pageMetadata = injectReactiveMetadata(); -const hideTitle = inject('shouldOmitHeaderTitle', false); +const hideTitle = props.hideTitle || inject('shouldOmitHeaderTitle', false); const thin_ = props.thin || inject('shouldHeaderThin', false); const el = shallowRef<HTMLElement | undefined>(undefined); diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 22c5231dd9..f1be5de13a 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -55,11 +55,12 @@ function top() { async function timetravel() { const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, + title: i18n.ts.timeTravel as string, + text: i18n.ts.timeTravelDescription as string, }); if (canceled) return; - tlEl.value.timetravel(date); + tlEl.value?.timetravel(date); } function settings() { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 12e2db2293..b8b4203809 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -221,11 +221,12 @@ function saveTlFilter(key: keyof typeof defaultStore.state.tl.filter, newValue: async function timetravel(): Promise<void> { const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, + title: i18n.ts.timeTravel as string, + text: i18n.ts.timeTravelDescription as string, }); if (canceled) return; - tlComponent.value.timetravel(date); + tlComponent.value?.timetravel(date); } function focus(): void { @@ -323,10 +324,10 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList iconOnly: true, onClick: chooseAntenna, }, { - icon: 'ti ti-device-tv', - title: i18n.ts.channel, + icon: 'ti ti-calendar-time', + title: i18n.ts.timeTravel, iconOnly: true, - onClick: chooseChannel, + onClick: timetravel, }] as Tab[]); const headerTabsWhenNotLogin = computed(() => [...availableBasicTimelines().map(tl => ({