Skip to content

Commit

Permalink
feat(client): support arbitrary union type
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 23, 2022
1 parent e76c1ca commit 5842477
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 46 deletions.
38 changes: 24 additions & 14 deletions plugins/a11y/verifier/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { Awaitable, Context, Schema, Session } from 'koishi'

type RequestHandler = string | boolean | ((session: Session) => Awaitable<string | boolean | void>)
type Response = [boolean, string?]
type RequestHandler = number | GeneralHandler
type GeneralHandler = string | boolean | ((session: Session) => Awaitable<string | boolean | void>)
type Response = [approve: boolean, comment?: string]

async function useRequestHandler(handler: RequestHandler, session: Session, prefer: boolean): Promise<Response> {
const RequestHandler: Schema<RequestHandler> = Schema.union([
Schema.const(undefined).description('无操作'),
Schema.const(true).description('全部通过'),
Schema.const(false).description('全部拒绝'),
Schema.natural().description('权限等级').default(0),
Schema.string().hidden(),
Schema.function().hidden(),
])

async function useGeneralHandler(handler: GeneralHandler, session: Session, prefer: boolean): Promise<Response> {
const result = typeof handler === 'function' ? await handler(session) : handler
if (typeof result === 'string') {
return [prefer, result]
Expand All @@ -28,18 +38,18 @@ async function checkChannelAuthority(session: Session, authority: number): Promi
}
}

export const name = 'verifier'

export interface Config {
onFriendRequest?: number | RequestHandler
onGuildMemberRequest?: number | RequestHandler
onGuildRequest?: number | RequestHandler
onFriendRequest?: RequestHandler
onGuildMemberRequest?: RequestHandler
onGuildRequest?: RequestHandler
}

export const name = 'verifier'

export const Config: Schema<Config> = Schema.object({
onFriendRequest: Schema.union([Number, String, Boolean, Function]).description('通过好友请求所需的权限等级。'),
onGuildMemberRequest: Schema.union([Number, String, Boolean, Function]).description('通过入群申请所需的权限等级。'),
onGuildRequest: Schema.union([Number, String, Boolean, Function]).description('通过入群邀请所需的权限等级。'),
onFriendRequest: RequestHandler.description('如何响应好友请求?'),
onGuildMemberRequest: RequestHandler.description('如何响应入群申请?'),
onGuildRequest: RequestHandler.description('如何响应入群邀请?'),
})

export function apply(ctx: Context, config: Config = {}) {
Expand All @@ -48,21 +58,21 @@ export function apply(ctx: Context, config: Config = {}) {
ctx.on('friend-request', async (session) => {
const result = typeof onFriendRequest === 'number'
? await checkUserAuthority(session, onFriendRequest)
: await useRequestHandler(onFriendRequest, session, true)
: await useGeneralHandler(onFriendRequest, session, true)
if (result) return session.bot.handleFriendRequest(session.messageId, ...result)
})

ctx.on('guild-request', async (session) => {
const result = typeof onGuildRequest === 'number'
? await checkChannelAuthority(session, onGuildRequest)
: await useRequestHandler(onGuildRequest, session, false)
: await useGeneralHandler(onGuildRequest, session, false)
if (result) return session.bot.handleGuildRequest(session.messageId, ...result)
})

ctx.on('guild-member-request', async (session) => {
const result = typeof onGuildMemberRequest === 'number'
? await checkUserAuthority(session, onGuildMemberRequest)
: await useRequestHandler(onGuildMemberRequest, session, false)
: await useGeneralHandler(onGuildMemberRequest, session, false)
if (result) return session.bot.handleGuildMemberRequest(session.messageId, ...result)
})
}
1 change: 1 addition & 0 deletions plugins/frontend/client/client/components/form/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<k-comment v-if="!validate(resolved)" type="warning">
部分配置项无法正常显示,这可能并非预期行为<slot name="hint"></slot>。
</k-comment>

<form class="k-form">
<h2 v-if="showHeader ?? !hasTitle(resolved, true)">基础设置</h2>
<slot name="prolog"></slot>
Expand Down
74 changes: 51 additions & 23 deletions plugins/frontend/client/client/components/form/schema.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<template>
<template v-if="!schema || schema.meta.hidden"/>

<!--
Schema.object(dict).description(desc)
desc will be displayed as h2 title
-->
<template v-else-if="schema.type === 'object'">
<h2 v-if="schema.meta.description">{{ schema.meta.description }}</h2>
<k-schema v-for="(item, key) in schema.dict" :key="key"
Expand Down Expand Up @@ -46,29 +42,29 @@
</template>

<template #right>
<template v-if="isPrimitive(schema)">
<template v-if="schema.type === 'union' && !isRadio">
<el-select v-model="selectModel" :disabled="disabled">
<el-option
v-for="(item, index) in choices"
:key="index"
:value="index"
:label="item.meta.description || item.value"
></el-option>
</el-select>
</template>

<template v-if="isPrimitive(active) && (schema.type !== 'union' || active.type !== 'const')">
<schema-primitive
v-model="config"
:initial="initial"
:schema="schema"
:schema="active"
:disabled="disabled"
></schema-primitive>
</template>

<template v-else-if="isComposite">
<k-button solid @click="signal = true" :disabled="disabled">添加项</k-button>
</template>

<template v-else-if="isSelect">
<el-select v-model="config" :disabled="disabled">
<el-option
v-for="item in choices"
:key="item.value"
:label="item.value"
:value="item.value"
></el-option>
</el-select>
</template>
</template>

<ul v-if="isRadio">
Expand Down Expand Up @@ -145,12 +141,31 @@ const required = computed(() => {
&& isNullable(props.modelValue)
})
const choices = computed(() => getChoices(props.schema))
const choices = ref<Schema[]>()
const cache = ref<any[]>()
const active = ref<Schema>()
const isSelect = computed(() => {
return props.schema.type === 'union'
&& choices.value.every(item => item.type === 'const')
&& choices.value.some(item => !item.meta.description)
watch(() => props.schema, (value) => {
if (value?.type !== 'union') {
choices.value = []
return
}
choices.value = getChoices(props.schema)
cache.value = choices.value.map((item) => {
if (item.type === 'const') return item.value
return getFallback(item)
})
}, { immediate: true })
const selectModel = computed({
get() {
if (active.value === props.schema) return
return active.value.meta.description || active.value.value
},
set(index) {
config.value = cache.value[index]
active.value = choices.value[index]
},
})
const isRadio = computed(() => {
Expand All @@ -160,13 +175,21 @@ const isRadio = computed(() => {
})
const isComposite = computed(() => {
return ['array', 'dict'].includes(props.schema.type) && validate(props.schema.inner)
return ['array', 'dict'].includes(active.value.type) && validate(active.value.inner)
})
const config = ref()
const signal = ref(false)
watch(() => props.modelValue, (value) => {
active.value = props.schema
for (const item of choices.value) {
try {
item(value)
active.value = item
break
} catch {}
}
config.value = value ?? getFallback(props.schema)
}, { immediate: true })
Expand Down Expand Up @@ -246,6 +269,11 @@ function handleCommand(action: string) {
height: 1.375rem;
}
}
.el-select:not(:last-child) {
width: 10rem;
margin-right: 1rem;
}
}
</style>
5 changes: 2 additions & 3 deletions plugins/frontend/client/client/components/form/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function isObjectSchema(schema: Schema) {
}

export function getChoices(schema: Schema) {
return schema.list.filter(item => !['function', 'transform'].includes(item.type))
return schema.list.filter(item => !item.meta.hidden && !dynamic.includes(item.type))
}

export function getFallback(schema: Schema) {
Expand All @@ -37,8 +37,7 @@ export function validate(schema: Schema): boolean {
} else if (schema.type === 'intersect') {
return schema.list.every(isObjectSchema)
} else if (schema.type === 'union') {
const choices = schema.list.filter(item => !dynamic.includes(item.type))
return choices.length === 1 && validate(choices[0]) || choices.every(item => item.type === 'const')
return getChoices(schema).every(item => validate(item) && (item.type === 'const' || item.meta.description))
} else if (composite.includes(schema.type)) {
return validate(schema.inner)
} else {
Expand Down
8 changes: 2 additions & 6 deletions plugins/frontend/manager/client/bots/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<script lang="ts" setup>
import { computed, watch, ref, reactive } from 'vue'
import { store, send, clone } from '@koishijs/client'
import { store, send, clone, Schema } from '@koishijs/client'
import { BotProvider } from '@koishijs/plugin-manager'
const props = defineProps<{
Expand All @@ -38,11 +38,7 @@ const props = defineProps<{
const createSchema = (values: string[]) => ({
type: 'union',
meta: { required: true },
list: values.map((value) => ({
type: 'const',
value,
meta: {},
})),
list: values.map((value) => Schema.const(value)),
})
const adapterSchema = computed(() => {
Expand Down

0 comments on commit 5842477

Please sign in to comment.