diff --git a/doc/donate.md b/doc/donate.md index d04af99e4..0ff109007 100644 --- a/doc/donate.md +++ b/doc/donate.md @@ -30,6 +30,9 @@ https://afdian.com/a/the1812?tab=sponsor | 时间 | 用户名 | 单号后4位 | 金额 | | ------------------- | --------------------- | --------- | ------- | +| 2025.07.12 16:44:24 | 爱音allin | 3957 | ¥5.00 | +| 2025.06.29 10:53:44 | f*x | 9352 | ¥10.00 | +| 2025.06.10 01:22:23 | *匆 | 5332 | ¥5.00 | | 2025.05.21 19:55:02 | mobmaster69 | 7644 | ¥5.00 | | 2025.05.20 02:38:45 | Duane | 3937 | ¥5.00 | | 2025.05.15 22:12:22 | 匿名 | 3901 | ¥23.33 | diff --git a/doc/features/features.json b/doc/features/features.json index ad291e5c8..f26ced8f4 100644 --- a/doc/features/features.json +++ b/doc/features/features.json @@ -675,7 +675,7 @@ "type": "component", "name": "subscribeTimeShow", "displayName": "关注时间显示", - "description": "by [@Light_Quanta](https://github.com/LightQuanta)\n\n在粉丝/关注列表显示关注的具体时间", + "description": "by [@CNOCM](https://github.com/CNOCM)\n\n在粉丝/关注列表及用户主页显示关注的具体时间。", "fullRelativePath": "../../registry/dist/components/utils/subscribe-time-show.js", "fullAbsolutePath": "registry/dist/components/utils/subscribe-time-show.js" }, diff --git a/doc/features/features.md b/doc/features/features.md index 26341413b..b1b3a6759 100644 --- a/doc/features/features.md +++ b/doc/features/features.md @@ -946,9 +946,9 @@ by [@Light_Quanta](https://github.com/LightQuanta) **GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/subscribe-time-show.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/subscribe-time-show.js) -by [@Light_Quanta](https://github.com/LightQuanta) +by [@CNOCM](https://github.com/CNOCM) -在粉丝/关注列表显示关注的具体时间 +在粉丝/关注列表及用户主页显示关注的具体时间。 ### [网址参数清理](../../registry/dist/components/utils/url-params-clean.js) `urlParamsClean` diff --git a/registry/lib/components/utils/subscribe-time-show/index.ts b/registry/lib/components/utils/subscribe-time-show/index.ts index 2743ec93a..b90497ddf 100644 --- a/registry/lib/components/utils/subscribe-time-show/index.ts +++ b/registry/lib/components/utils/subscribe-time-show/index.ts @@ -1,6 +1,9 @@ import { defineComponentMetadata } from '@/components/define' import { useScopedConsole } from '@/core/utils/log' import { bilibiliApi, getJsonWithCredentials } from '@/core/ajax' +import { select } from '@/core/spin-query' +import { allMutations } from '@/core/observer' +import { getUserInfo } from '@/core/user-info' const displayName = '关注时间显示' const console = useScopedConsole(displayName) @@ -22,20 +25,16 @@ type FollowTimeInfo = { const midMap: Record = {} const insertFollowTimeToCard = (mid: number, dateStr: string, label: string) => { - const anchor = document.querySelector( - `${SELECTORS.relationCardAnchor}[href*="/${mid}"]`, - ) + const anchor = dq(`${SELECTORS.relationCardAnchor}[href*="/${mid}"]`) const card = anchor?.closest(SELECTORS.relationCardContainer) - if (!card) { - return - } - if (card.querySelector(`.${SELECTORS.relationCardTimeClass}`)) { + if (!card || card.querySelector(`.${SELECTORS.relationCardTimeClass}`)) { return } const div = document.createElement('div') div.className = SELECTORS.relationCardTimeClass div.textContent = `${label}:${dateStr}` + div.dataset.mid = mid.toString() const sign = card.querySelector(SELECTORS.relationCardSign) if (sign?.parentNode) { @@ -43,57 +42,37 @@ const insertFollowTimeToCard = (mid: number, dateStr: string, label: string) => } } -let fetchWrapped = false -let pendingUpdate = false - -const updateCards = () => { - if (pendingUpdate) { - return - } - pendingUpdate = true - requestAnimationFrame(() => { - pendingUpdate = false - const mids = Array.from( - document.querySelectorAll(SELECTORS.relationCardAnchor), - ) - .map(el => el.href.match(/\/(\d+)/)?.[1]) - .filter(Boolean) - .map(Number) - - mids.forEach(mid => { - const data = midMap[mid] - if (data) { - const dateStr = new Date(data.mtime * 1000).toLocaleString() - insertFollowTimeToCard(mid, dateStr, data.label) - } - }) - }) -} - -const waitForProfileContainer = () => - new Promise(resolve => { - const check = () => document.querySelector(SELECTORS.profileFollowContainer) - let container = check() - if (container) { - resolve(container) - } else { - const observer = new MutationObserver(() => { - container = check() - if (container) { - observer.disconnect() - resolve(container) +const updateCards = (() => { + let scheduled = false + return () => { + if (scheduled) { + return + } + scheduled = true + requestAnimationFrame(() => { + scheduled = false + dqa(SELECTORS.relationCardAnchor).forEach(el => { + const match = (el as HTMLAnchorElement).href.match(/\/(\d+)/) + if (!match) { + return + } + const mid = Number(match[1]) + if (Number.isNaN(mid)) { + return + } + const data = midMap[mid] + if (data) { + const dateStr = new Date(data.mtime * 1000).toLocaleString() + insertFollowTimeToCard(mid, dateStr, data.label) } }) - observer.observe(document.body, { childList: true, subtree: true }) - } - }) + }) + } +})() const insertSubscribeTime = (followTimeStr: string) => { - const container = document.querySelector(SELECTORS.profileFollowContainer) - if (!container) { - return - } - if (container.querySelector(`.${SELECTORS.profileTextClass}`)) { + const container = dq(SELECTORS.profileFollowContainer) + if (!container || container.querySelector(`.${SELECTORS.profileTextClass}`)) { return } @@ -107,25 +86,22 @@ const insertSubscribeTime = (followTimeStr: string) => { container.appendChild(infoEl) } -const entry = async () => { - try { - const { addImportantStyle } = await import('@/core/style') - const { default: style } = await import('./subscribe-time.scss') - addImportantStyle(style, 'subscribe-time-style') - } catch (e) { - console.error('样式加载失败:', e) - } +if (!(unsafeWindow as any).subscribeTimeHooked) { + ;(unsafeWindow as any).subscribeTimeHooked = true + const originalFetch = unsafeWindow.fetch + unsafeWindow.fetch = new Proxy(originalFetch, { + apply(target, thisArg, args) { + const url = typeof args[0] === 'string' ? args[0] : args[0].url + const urlObj = new URL(url, location.origin) - new MutationObserver(updateCards).observe(document.body, { childList: true, subtree: true }) - - if (!fetchWrapped) { - fetchWrapped = true - const originalFetch = unsafeWindow.fetch - unsafeWindow.fetch = new Proxy(originalFetch, { - apply(target, thisArg, args) { - const url = typeof args[0] === 'string' ? args[0] : args[0].url - if (url.includes('/x/relation/fans') || url.includes('/x/relation/followings')) { - return target.apply(thisArg, args).then(res => { + if ( + urlObj.hostname === 'api.bilibili.com' && + (urlObj.pathname.includes('/x/relation/fans') || + urlObj.pathname.includes('/x/relation/followings')) + ) { + return target + .apply(thisArg, args) + .then(res => { res .clone() .json() @@ -137,54 +113,63 @@ const entry = async () => { } list.forEach(user => { if (typeof user.mid === 'number' && typeof user.mtime === 'number') { - const label = url.includes('/x/relation/fans') - ? 'Ta 关注你的时间' - : '你关注 Ta 的时间' + const label = url.includes('/fans') ? 'Ta 关注你的时间' : '你关注 Ta 的时间' midMap[user.mid] = { mtime: user.mtime, label } - insertFollowTimeToCard( - user.mid, - new Date(user.mtime * 1000).toLocaleString(), - label, - ) + const dateStr = new Date(user.mtime * 1000).toLocaleString() + insertFollowTimeToCard(user.mid, dateStr, label) } }) }) - .catch(err => console.warn('JSON 解析失败:', err)) + .catch(err => { + console.warn('JSON 解析失败:', err) + }) return res }) - } - return target.apply(thisArg, args) - }, - }) + .catch(err => { + console.warn('fetch 请求失败:', err) + throw err + }) + } + + return target.apply(thisArg, args) + }, + }) +} + +const entry = async () => { + try { + const { addImportantStyle } = await import('@/core/style') + const { default: style } = await import('./subscribe-time.scss') + addImportantStyle(style, 'subscribe-time-style') + } catch (e) { + console.error('样式加载失败:', e) } + allMutations(updateCards) + + const userMid = (await getUserInfo()).mid const match = location.href.match(/space\.bilibili\.com\/(\d+)/) if (!match) { - console.warn('无法提取 mid') return } + const mid = Number(match[1]) + if (mid === userMid) { + console.log('当前为本人空间,跳过关注时间显示') + return + } const info = await bilibiliApi( getJsonWithCredentials(`https://api.bilibili.com/x/web-interface/relation?mid=${mid}`), ) - if (!info?.relation) { - console.warn('未获取到 relation 信息') - return - } - if (!info.relation.mtime) { - console.log('当前未关注或关注时间无效') + + const mtime = info?.relation?.mtime + if (!mtime) { return } - try { - await waitForProfileContainer() - } catch (e) { - console.warn('等待关注容器超时,跳过插入关注时间') - return - } - - insertSubscribeTime(new Date(info.relation.mtime * 1000).toLocaleString()) + await select(SELECTORS.profileFollowContainer) + insertSubscribeTime(new Date(mtime * 1000).toLocaleString()) } export const component = defineComponentMetadata({ @@ -200,5 +185,7 @@ export const component = defineComponentMetadata({ /https:\/\/space\.bilibili\.com\/\d+/, ], entry, - description: { 'zh-CN': '在粉丝/关注列表及用户主页显示关注的具体时间。' }, + description: { + 'zh-CN': '在粉丝/关注列表及用户主页显示关注的具体时间。', + }, }) diff --git a/src/client/common.meta.json b/src/client/common.meta.json index 8fdbfc24e..9db4499e9 100644 --- a/src/client/common.meta.json +++ b/src/client/common.meta.json @@ -1,5 +1,5 @@ { - "version": "2.10.2", + "version": "2.10.3", "author": "Grant Howard, Coulomb-G", "copyright": "[year], Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)", "license": "MIT",