Bilibili-Evolved/registry/lib/components/style/dark-mode/schedule/index.ts
2021-12-05 20:52:01 +08:00

146 lines
4.4 KiB
TypeScript

import { ComponentMetadata } from '@/components/types'
import { fullyLoaded } from '@/core/life-cycle'
import { ComponentSettings, getComponentSettings } from '@/core/settings'
import { Range } from '@/ui/range'
import { darkExcludes } from '../dark-urls'
class ScheduleTime {
hour: number
minute: number
constructor(...args: [] | [string] | [number, number]) {
if (args.length === 0) {
const now = new Date()
this.hour = now.getHours()
this.minute = now.getMinutes()
} else if (args.length === 1) {
const [text] = args;
[this.hour, this.minute] = text
.split(':')
.slice(0, 2)
.map(it => ScheduleTime.validatePart(it))
this.normalize()
} else if (args.length === 2) {
[this.hour, this.minute] = args
}
}
normalize() {
while (this.minute < 0) {
this.minute += 60
this.hour -= 1
}
while (this.minute >= 60) {
this.minute -= 60
this.hour += 1
}
while (this.hour < 0) {
this.hour += 24
}
while (this.hour >= 24) {
this.hour -= 24
}
}
lessThan(other: ScheduleTime) {
if (this.hour < other.hour
|| (this.hour === other.hour && this.minute < other.minute)
) {
return true
}
return false
}
greaterThan(other: ScheduleTime) {
if (this.hour > other.hour
|| (this.hour === other.hour && this.minute > other.minute)
) {
return true
}
return false
}
equals(other: ScheduleTime) {
return this.hour === other.hour && this.minute === other.minute
}
isInRange(start: ScheduleTime, end: ScheduleTime) {
if (start.equals(end)) {
return false
}
let inRange = this.greaterThan(start) && this.lessThan(end)
if (start.greaterThan(end)) {
inRange = this.greaterThan(start) || this.lessThan(end)
}
const result = inRange || this.equals(start)
return result
}
toString() {
return `${this.hour.toString().padStart(2, '0')}:${this.minute.toString().padStart(2, '0')}`
}
static validatePart(text: string) {
const number = parseInt(text)
if (!Number.isNaN(number) && number >= 0 && number <= 59) {
return number
}
return null
}
static millisecondsBefore(time: ScheduleTime) {
const now = new ScheduleTime()
const nowSeconds = new Date().getSeconds()
const currentMilliseconds = 1000 * (now.hour * 3600 + now.minute * 60 + nowSeconds)
const targetMilliseconds = 1000 * (time.hour * 3600 + time.minute * 60)
let result = targetMilliseconds - currentMilliseconds
if (now.greaterThan(time) || (now.equals(time) && nowSeconds !== 0)) {
result += 24 * 3600 * 1000
}
return result
}
}
const checkTime = (settings: ComponentSettings) => {
const start = new ScheduleTime(settings.options.range.start)
const end = new ScheduleTime(settings.options.range.end)
const now = new ScheduleTime()
const useDarkMode = now.isInRange(start, end)
const darkModeSettings = getComponentSettings('darkMode')
if (darkModeSettings.enabled !== useDarkMode) {
darkModeSettings.enabled = useDarkMode
}
let timeout = 0
if (useDarkMode) {
timeout = ScheduleTime.millisecondsBefore(end)
} else {
timeout = ScheduleTime.millisecondsBefore(start)
}
if (timeout !== 0) {
setTimeout(() => checkTime(settings), timeout)
}
}
export const component: ComponentMetadata = {
name: 'darkModeSchedule',
displayName: '夜间模式计划时段',
description: '设置一个使用夜间模式的时间段, 进入 / 离开此时间段时, 会自动开启 / 关闭夜间模式. 结束时间小于起始时间时将视为次日, 如 `18:00` 至 `6:00` 表示晚上 18:00 到次日 6:00. 请勿和 \`夜间模式跟随系统\` 一同使用.',
tags: [
componentsTags.style,
componentsTags.general,
],
entry: ({ settings }) => fullyLoaded(() => checkTime(settings)),
urlExclude: darkExcludes,
options: {
range: {
defaultValue: {
start: '18:00',
end: '6:00',
},
displayName: '时间段',
validator: (range: Range<string>) => {
const { start, end } = range
const regex = /^(\d{1,2}):(\d{1,2})$/
if (!regex.test(start) || !regex.test(end)) {
return null
}
const startTime = new ScheduleTime(range.start)
const endTime = new ScheduleTime(range.end)
return {
start: startTime.toString(),
end: endTime.toString(),
}
},
},
},
}