mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
* 【新增】支持固定全局倍速值和设置倍速菜单最大高度(后者不显示选项) * 【新增】支持隐藏倍速菜单滚动条 * 【优化】改变选项实时更新 * 【优化】使用了更稳妥的方式获取菜单项元素 * 【优化】使用自实现的 mini-rxjs 处理事件流,避免重复操作,实现更改选项值实时响应 * 【优化】为切换倍速等操作加上了防抖处理 * 【修复】添加空倍速会报倍速值太小的错误信息 * 【迁移】添加了迁移提示和自动迁移配置的功能 * 【更名】「倍速记忆」/「倍速增强」->「记忆倍速」 * 【更名】「扩展倍速菜单」->「扩展倍速」
143 lines
3.6 KiB
TypeScript
143 lines
3.6 KiB
TypeScript
import { getGeneralSettings } from '@/core/settings'
|
||
|
||
export type EmptyFunction = () => void
|
||
export type TeardownLogic = EmptyFunction
|
||
export type Unsubscribe = EmptyFunction
|
||
|
||
export interface StandardizedObserver<T = any> {
|
||
next(value?: T): void
|
||
error?(err: Error): void
|
||
complete?(): void
|
||
}
|
||
|
||
export type Observer<T = any> = ((value: T) => void) | StandardizedObserver<T>
|
||
|
||
export interface PublishContext<T = any, R = T>
|
||
extends Required<Omit<StandardizedObserver<R>, 'completed'>> {
|
||
subscribe?(observer?: Observer<T>): Unsubscribe | undefined
|
||
}
|
||
|
||
export interface Publisher<T = any, R = T> {
|
||
(context: PublishContext<T, R>): TeardownLogic | void
|
||
}
|
||
|
||
export interface Operator<T = any, R = T> {
|
||
(context: PublishContext<T, R>): TeardownLogic | void
|
||
}
|
||
|
||
export interface Subject<T> extends Required<PublishContext<T>> {
|
||
connect(): void
|
||
pipe<R = T>(...operators: Operator[]): Subject<R>
|
||
}
|
||
|
||
export const toStandardizedObserver = <T>(
|
||
observer: Observer<T>,
|
||
): StandardizedObserver<T> => (typeof observer === 'function' ? { next: observer } : observer)
|
||
|
||
export const subject = <T, R = T>(
|
||
publisher?: Publisher<T, R>,
|
||
): Subject<R> => (function internalSubject(publisher_, parent = undefined, root = undefined) {
|
||
let connected = false
|
||
|
||
const teardownLogicList: TeardownLogic[] = []
|
||
const observers = []
|
||
let completed = false
|
||
|
||
const cleanup = () => {
|
||
while (teardownLogicList.length) { teardownLogicList.pop()() }
|
||
observers.length = 0
|
||
completed = true
|
||
}
|
||
|
||
const error = (err: Error) => {
|
||
if (completed) {
|
||
return
|
||
}
|
||
observers.forEach(observer => {
|
||
observer.error?.(err)
|
||
getGeneralSettings().devMode && console.error(err)
|
||
})
|
||
cleanup()
|
||
}
|
||
|
||
const next = (value: R) => {
|
||
if (completed) {
|
||
return
|
||
}
|
||
observers.forEach(observer => {
|
||
try {
|
||
observer.next(value)
|
||
} catch (err) {
|
||
error(err)
|
||
}
|
||
})
|
||
}
|
||
|
||
const complete = () => {
|
||
observers.forEach(observer => {
|
||
observer.complete?.()
|
||
})
|
||
cleanup()
|
||
}
|
||
|
||
const connect = () => {
|
||
if (connected) {
|
||
return
|
||
}
|
||
const teardownLogic = publisher_?.({ next, error, complete })
|
||
teardownLogic && teardownLogicList.push(teardownLogic)
|
||
connected = true
|
||
}
|
||
|
||
const subscribe = (observer?: StandardizedObserver<T>) => {
|
||
if (observer == null) {
|
||
return null
|
||
}
|
||
observers.push(observer)
|
||
return () => {
|
||
lodash.pull(observers, observer)
|
||
}
|
||
}
|
||
|
||
const pipe = (...publishers: Publisher<any>[]) => {
|
||
if (publishers.length === 0) {
|
||
return {
|
||
subscribe: observer => {
|
||
const unsubscribe = subscribe(toStandardizedObserver(observer));
|
||
(root?.connect ?? connect)()
|
||
return unsubscribe
|
||
},
|
||
pipe,
|
||
next,
|
||
error,
|
||
complete,
|
||
...root,
|
||
}
|
||
}
|
||
return internalSubject(
|
||
publishers[0],
|
||
{ subscribe },
|
||
root || {
|
||
connect,
|
||
next,
|
||
},
|
||
).pipe(...publishers.slice(1))
|
||
}
|
||
|
||
if (parent) {
|
||
const teardownLogic = publisher_?.({
|
||
subscribe: observer => parent.subscribe(
|
||
// 默认传递本级的 error 和 complete,这样实现操作符时,将简单许多,一般情况下,错误和完成信号都能沿着链式调用传递下去
|
||
{ error, complete, ...toStandardizedObserver(observer) },
|
||
),
|
||
next,
|
||
error,
|
||
complete,
|
||
})
|
||
teardownLogic
|
||
&& teardownLogicList.push(teardownLogic)
|
||
}
|
||
|
||
return pipe()
|
||
}(publisher))
|