Merge pull request #3154 from timongh/component-option-types

Change buildin components to define api
This commit is contained in:
Grant Howard 2022-03-22 22:42:30 +08:00 committed by GitHub
commit d98dd98f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 225 additions and 170 deletions

View File

@ -1,11 +1,16 @@
import { ComponentMetadata, componentsTags } from '@/components/types'
import { ComponentEntry, componentsTags } from '@/components/types'
import { meta } from '@/core/meta'
import {
getGeneralSettings,
getComponentSettings,
getGeneralSettings,
isUserComponent,
} from '@/core/settings'
import { Version } from '@/core/version'
import {
defineComponentMetadata,
defineOptionsMetadata,
OptionsOfMetadata,
} from '@/components/define'
import { LaunchBarActionProvider } from '../launch-bar/launch-bar-action'
import { ComponentAction } from '../settings-panel/component-actions/component-actions'
import { isLocalItem, name, UpdateCheckItem } from './utils'
@ -19,68 +24,78 @@ const {
silentCheckUpdate,
} = checkerMethods
export const component: ComponentMetadata = {
const optionsMetadata = defineOptionsMetadata({
lastUpdateCheck: {
displayName: '最后检查更新日期',
defaultValue: 0,
hidden: true,
},
lastInstalledVersion: {
displayName: '最后安装版本',
defaultValue: '2.0.0',
hidden: true,
},
localPortOverride: {
displayName: '本地组件链接端口',
defaultValue: '',
hidden: true,
},
minimumDuration: {
displayName: '更新间隔 (ms)',
defaultValue: 1000 * 3600 * 24,
},
urls: {
displayName: '更新链接',
defaultValue: {
components: {} as Record<string, UpdateCheckItem>,
plugins: {} as Record<string, UpdateCheckItem>,
styles: {} as Record<string, UpdateCheckItem>,
},
hidden: true,
},
maxUpdateCount: {
displayName: '单次最大更新量 (个)',
defaultValue: 4,
// hidden: true,
},
})
const entry: ComponentEntry<typeof optionsMetadata> = async ({
settings: { options: opt },
}) => {
const now = Number(new Date())
const duration = now - opt.lastUpdateCheck
const isDurationExceeded = duration >= opt.minimumDuration
const isVersionOutdated = new Version(meta.version).greaterThan(
new Version(opt.lastInstalledVersion),
)
if (isDurationExceeded) {
coreApis.lifeCycle.fullyLoaded(() => silentCheckUpdate())
} else if (isVersionOutdated) {
coreApis.lifeCycle.fullyLoaded(() => forceCheckUpdate())
}
return checkerMethods
}
type Options = OptionsOfMetadata<typeof optionsMetadata>
export const component = defineComponentMetadata({
name,
displayName: '自动更新器',
description: {
'zh-CN': '自动检查组件, 插件和样式的更新. (仅限从设置面板中安装的)',
},
tags: [componentsTags.utils],
options: {
lastUpdateCheck: {
displayName: '最后检查更新日期',
defaultValue: 0,
hidden: true,
},
lastInstalledVersion: {
displayName: '最后安装版本',
defaultValue: '2.0.0',
hidden: true,
},
localPortOverride: {
displayName: '本地组件链接端口',
defaultValue: '',
hidden: true,
},
minimumDuration: {
displayName: '更新间隔 (ms)',
defaultValue: 1000 * 3600 * 24,
},
urls: {
displayName: '更新链接',
defaultValue: {
components: {},
plugins: {},
styles: {},
},
hidden: true,
},
maxUpdateCount: {
displayName: '单次最大更新量 (个)',
defaultValue: 4,
// hidden: true,
},
},
options: optionsMetadata,
extraOptions: () => import('./ExtraOptions.vue').then(m => m.default),
entry: async ({ settings: { options } }) => {
const now = Number(new Date())
const duration = now - options.lastUpdateCheck
const isDurationExceeded = duration >= options.minimumDuration
const isVersionOutdated = new Version(meta.version)
.greaterThan(new Version(options.lastInstalledVersion))
if (isDurationExceeded) {
coreApis.lifeCycle.fullyLoaded(() => silentCheckUpdate())
} else if (isVersionOutdated) {
coreApis.lifeCycle.fullyLoaded(() => forceCheckUpdate())
}
return checkerMethods
},
entry,
plugin: {
displayName: '自动更新器 - 功能扩展',
description: {
'zh-CN': '记录在设置面板中的功能安装/卸载数据供自动更新使用; 并在组件详情中支持手动检查该组件的更新.',
'zh-CN':
'记录在设置面板中的功能安装/卸载数据供自动更新使用; 并在组件详情中支持手动检查该组件的更新.',
},
// 非设置面板的组件如果也对能功能进行修改, 建议调用此处的 Hooks 同步状态.
setup: ({ addData, addHook }) => {
@ -90,7 +105,9 @@ export const component: ComponentMetadata = {
after: (_, url: string, metadata: { name: string }) => {
// console.log('hook', `user${lodash.startCase(type)}.add`, metadata.name, url)
const { options } = getComponentSettings('autoUpdate')
const existingItem = options.urls[type][metadata.name] as UpdateCheckItem
const existingItem = options.urls[type][
metadata.name
] as UpdateCheckItem
if (!existingItem) {
options.urls[type][metadata.name] = {
url,
@ -116,32 +133,36 @@ export const component: ComponentMetadata = {
},
})
})
addData('settingsPanel.componentActions', (actions: ComponentAction[]) => {
const { options } = getComponentSettings('autoUpdate')
actions.push(metadata => {
const item = options.urls.components[metadata.name] as UpdateCheckItem
if (!item) {
return undefined
}
return {
name: 'checkUpdate',
displayName: '检查更新',
icon: isLocalItem(item.url) ? 'mdi-file-download-outline' : 'mdi-cloud-download-outline',
condition: () => isUserComponent(metadata),
title: item.url,
action: async () => {
const { Toast } = await import('@/core/toast')
const toast = Toast.info('检查更新中...', '检查更新')
const result = await checkComponentsUpdate({
filterNames: [metadata.name],
force: true,
})
toast.message = result
toast.duration = 3000
},
}
})
})
addData(
'settingsPanel.componentActions',
(actions: ComponentAction[]) => {
const { options } = getComponentSettings<Options>('autoUpdate')
actions.push(metadata => {
const item = options.urls.components[metadata.name]
if (!item) {
return undefined
}
return {
name: 'checkUpdate',
displayName: '检查更新',
icon: isLocalItem(item.url)
? 'mdi-file-download-outline'
: 'mdi-cloud-download-outline',
condition: () => isUserComponent(metadata),
title: item.url,
action: async () => {
const { Toast } = await import('@/core/toast')
const toast = Toast.info('检查更新中...', '检查更新')
toast.message = await checkComponentsUpdate({
filterNames: [metadata.name],
force: true,
})
toast.duration = 3000
},
}
})
},
)
const icon = 'mdi-cloud-sync-outline'
addData('launchBar.actions', (actions: LaunchBarActionProvider[]) => {
@ -172,7 +193,10 @@ export const component: ComponentMetadata = {
description: 'Check Last Update',
action: async () => {
const { Toast } = await import('@/core/toast')
const toast = Toast.info('正在检查更新...', '检查最近更新的功能')
const toast = Toast.info(
'正在检查更新...',
'检查最近更新的功能',
)
await checkLastFeature()
toast.dismiss()
},
@ -184,4 +208,4 @@ export const component: ComponentMetadata = {
}
},
},
}
})

View File

@ -1,9 +1,13 @@
import { defaultLanguageCode, languageCodeToName } from '@/core/utils/i18n'
import { ComponentMetadata, componentsTags } from '../types'
import { translateProviderNames, translateProviders } from './machine-translator/translators'
import { defineComponentMetadata } from '@/components/define'
import { componentsTags } from '../types'
import {
translateProviderNames,
translateProviders,
} from './machine-translator/translators'
import { startTranslate } from './dom-translator'
export const component: ComponentMetadata = {
export const component = defineComponentMetadata({
name: 'i18n',
displayName: '多语言',
configurable: false,
@ -14,7 +18,8 @@ export const component: ComponentMetadata = {
componentsTags.general,
],
description: {
'zh-CN': '安装其他语言包可以更换界面语言, 机器翻译选择可以设定其他一些功能如`动态翻译`, `评论翻译`使用的翻译器. 机器翻译的选择不影响界面语言.',
'zh-CN':
'安装其他语言包可以更换界面语言, 机器翻译选择可以设定其他一些功能如`动态翻译`, `评论翻译`使用的翻译器. 机器翻译的选择不影响界面语言.',
},
options: {
language: {
@ -30,4 +35,4 @@ export const component: ComponentMetadata = {
},
// 相关功能好像全挂了, 先隐藏了
hidden: true,
}
})

View File

@ -1,16 +1,14 @@
import { none } from '@/core/utils'
import { ComponentMetadata, componentsTags } from '../types'
import { defineComponentMetadata } from '@/components/define'
import { componentsTags } from '../types'
import { plugin } from './plugin'
export const component: ComponentMetadata = {
export const component = defineComponentMetadata({
name: 'launchBar',
displayName: '搜索栏',
configurable: false,
entry: none,
plugin,
hidden: true,
tags: [
componentsTags.general,
componentsTags.utils,
],
}
tags: [componentsTags.general, componentsTags.utils],
})

View File

@ -1,13 +1,12 @@
import { cdnRoots } from '@/core/cdn-types'
import { ComponentMetadata, componentsTags } from '../types'
import { defineComponentMetadata } from '@/components/define'
import { componentsTags } from '../types'
export const component: ComponentMetadata = {
export const component = defineComponentMetadata({
name: 'notifyNewVersion',
displayName: '新版本提示',
description: '定期检查脚本本体的更新, 并弹出提示.',
tags: [
componentsTags.utils,
],
tags: [componentsTags.utils],
options: {
lastUpdateCheck: {
displayName: '最后检查更新日期',
@ -28,19 +27,28 @@ export const component: ComponentMetadata = {
const { getGeneralSettings } = await import('@/core/settings')
const now = Number(new Date())
const duration = now - options.lastUpdateCheck
if (duration < options.minimumDuration) { // 未到间隔期
if (duration < options.minimumDuration) {
// 未到间隔期
return
}
// 本地调试版不检查
if (!GM_info.scriptUpdateURL) {
return
}
const updateUrl = `${cdnRoots[getGeneralSettings().cdnRoot](meta.compilationInfo.branch)}dist/${meta.originalFilename}`
const scriptText: string = await monkey({ url: updateUrl, responseType: 'text' })
const updateUrl = `${cdnRoots[getGeneralSettings().cdnRoot](
meta.compilationInfo.branch,
)}dist/${meta.originalFilename}`
const scriptText: string = await monkey({
url: updateUrl,
responseType: 'text',
})
options.lastUpdateCheck = Number(new Date())
const versionMatch = scriptText.match(/^\/\/ @version\s*([\d\.]+)$/m)
const versionMatch = scriptText.match(/^\/\/ @version\s*([\d.]+)$/m)
if (!versionMatch?.[1]) {
console.warn('[新版本提示] 未能检测出脚本版本, scriptText.length =', scriptText.length)
console.warn(
'[新版本提示] 未能检测出脚本版本, scriptText.length =',
scriptText.length,
)
return
}
const latestVersion = new Version(versionMatch[1])
@ -48,9 +56,12 @@ export const component: ComponentMetadata = {
if (!latestVersion.greaterThan(currentVersion)) {
return
}
Toast.info(/* html */`新版本 <span>${latestVersion.versionString}</span> 已发布. <a href="https://github.com/the1812/Bilibili-Evolved/releases" target="_blank" class="link">更新说明</a><a href="${updateUrl}" target="_blank" class="link">安装</a>`, '检查更新')
Toast.info(
/* html */ `新版本 <span>${latestVersion.versionString}</span> 已发布. <a href='https://github.com/the1812/Bilibili-Evolved/releases' target='_blank' class='link'>更新说明</a><a href='${updateUrl}' target='_blank' class='link'>安装</a>`,
'检查更新',
)
} catch (error) {
console.warn('[新版本提示] 检查更新时发生错误: ', error)
}
},
}
})

View File

@ -4,85 +4,103 @@ import { TextColor } from '@/core/text-color'
import { CdnTypes } from '@/core/cdn-types'
import { addComponentListener } from '@/core/settings'
import { DownloadPackageEmitMode } from '@/core/download-mode'
import { ComponentEntry, ComponentMetadata, componentsTags } from '../types'
import { ComponentEntry, componentsTags } from '../types'
import {
defineComponentMetadata,
defineOptionsMetadata,
OptionsOfMetadata,
} from '../define'
import { provideActions } from './external-actions'
import description from './desc.md'
export const WidgetsPlugin = 'widgets'
export enum SettingsPanelDockSide {
Left = '左侧',
Right = '右侧',
}
const entry: ComponentEntry = async ({ metadata }) => {
const options = defineOptionsMetadata({
themeColor: {
defaultValue: '#00A0D8',
displayName: '主题颜色',
color: true,
},
scriptLoadingMode: {
defaultValue: LoadingMode.Delay,
displayName: '功能加载模式',
dropdownEnum: LoadingMode,
},
styleLoadingMode: {
defaultValue: LoadingMode.Race,
displayName: '样式加载模式',
dropdownEnum: LoadingMode,
},
textColor: {
defaultValue: TextColor.Auto,
displayName: '文本颜色',
dropdownEnum: TextColor,
},
cdnRoot: {
defaultValue: CdnTypes.jsDelivr,
displayName: '更新源',
dropdownEnum: CdnTypes,
},
dockSide: {
defaultValue: SettingsPanelDockSide.Left,
displayName: '设置面板停靠',
dropdownEnum: SettingsPanelDockSide,
},
filenameFormat: {
defaultValue: '[title][ - ep]',
displayName: '文件命名格式',
},
batchFilenameFormat: {
defaultValue: '[n - ][ep]',
displayName: '批量命名格式',
},
downloadPackageEmitMode: {
defaultValue: DownloadPackageEmitMode.Packed,
displayName: '文件下载模式',
dropdownEnum: DownloadPackageEmitMode,
},
devMode: {
defaultValue: false,
displayName: '开发者模式',
},
})
export type Options = OptionsOfMetadata<typeof options>
const entry: ComponentEntry<typeof options> = async ({ metadata }) => {
const { isIframe } = await import('@/core/utils')
if (isIframe()) {
return
}
addComponentListener(`${metadata.name}.dockSide`, (value: SettingsPanelDockSide) => {
document.body.classList.toggle('settings-panel-dock-right', value === SettingsPanelDockSide.Right)
}, true)
addComponentListener(
`${metadata.name}.dockSide`,
(value: SettingsPanelDockSide) => {
document.body.classList.toggle(
'settings-panel-dock-right',
value === SettingsPanelDockSide.Right,
)
},
true,
)
requestIdleCallback(async () => {
const Container = await import('./SettingsContainer.vue')
const instance = mountVueComponent(Container)
document.body.insertAdjacentElement('beforeend', instance.$el)
})
}
export const component: ComponentMetadata = {
export const component = defineComponentMetadata({
name: 'settingsPanel',
displayName: '通用设置',
configurable: false,
description,
entry,
options: {
themeColor: {
defaultValue: '#00A0D8',
displayName: '主题颜色',
color: true,
},
scriptLoadingMode: {
defaultValue: LoadingMode.Delay,
displayName: '功能加载模式',
dropdownEnum: LoadingMode,
},
styleLoadingMode: {
defaultValue: LoadingMode.Race,
displayName: '样式加载模式',
dropdownEnum: LoadingMode,
},
textColor: {
defaultValue: TextColor.Auto,
displayName: '文本颜色',
dropdownEnum: TextColor,
},
cdnRoot: {
defaultValue: CdnTypes.jsDelivr,
displayName: '更新源',
dropdownEnum: CdnTypes,
},
dockSide: {
defaultValue: SettingsPanelDockSide.Left,
displayName: '设置面板停靠',
dropdownEnum: SettingsPanelDockSide,
},
filenameFormat: {
defaultValue: '[title][ - ep]',
displayName: '文件命名格式',
},
batchFilenameFormat: {
defaultValue: '[n - ][ep]',
displayName: '批量命名格式',
},
downloadPackageEmitMode: {
defaultValue: DownloadPackageEmitMode.Packed,
displayName: '文件下载模式',
dropdownEnum: DownloadPackageEmitMode,
},
devMode: {
defaultValue: false,
displayName: '开发者模式',
},
},
options,
tags: [componentsTags.general],
i18n: {
'en-US': {
@ -103,4 +121,4 @@ export const component: ComponentMetadata = {
provideActions()
},
},
}
})

View File

@ -8,7 +8,8 @@ import {
} from '@/components/component'
import { PluginMetadata } from '@/plugins/plugin'
// import serialize from 'serialize-javascript'
import { settings, ComponentSettings } from '../settings'
import type { Options as SettingsPanelOptions } from '@/components/settings-panel'
import { ComponentSettings, settings } from '../settings'
import { matchUrlPattern } from '../utils'
/**
@ -23,11 +24,9 @@ export const metadataToOptions = <O extends UnknownOptions>(
*
* @param component
*/
export const componentToSettings = <
Om extends OptionalOptionsMetadata
>(component: ComponentMetadata<Om>): (
ComponentSettings<OptionsFromOptionalMetadata<Om>>
) => {
export const componentToSettings = <Om extends OptionalOptionsMetadata>(
component: ComponentMetadata<Om>,
): ComponentSettings<OptionsFromOptionalMetadata<Om>> => {
const { options: meta } = component
return {
enabled: component.enabledByDefault ?? true,
@ -99,7 +98,7 @@ export const getComponentSettings = <R extends UnknownOptions = UnknownOptions>(
/**
* (`settingsPanel``options`)
*/
export const getGeneralSettings = () => getComponentSettings('settingsPanel').options
export const getGeneralSettings = () => getComponentSettings<SettingsPanelOptions>('settingsPanel').options
/**
* , :
* - , URL必须不匹配其排除列表中任意一项(`Component.urlExclude`)