Merge branch 'preview-fixes' into preview-features

This commit is contained in:
the1812 2025-07-22 23:10:49 +08:00
commit fba8849f46
5 changed files with 95 additions and 105 deletions

View File

@ -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 |

View File

@ -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"
},

View File

@ -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`

View File

@ -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<number, FollowTimeInfo> = {}
const insertFollowTimeToCard = (mid: number, dateStr: string, label: string) => {
const anchor = document.querySelector<HTMLAnchorElement>(
`${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<HTMLAnchorElement>(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<Element>(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': '在粉丝/关注列表及用户主页显示关注的具体时间。',
},
})

View File

@ -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",