Skip to content

Commit

Permalink
feat(notifier): basic support
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed May 14, 2024
1 parent 566e078 commit bd477e5
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Official plugins

- @cordisjs/plugin-config
- @cordisjs/plugin-insight
- @cordisjs/plugin-manager
- @cordisjs/plugin-notifier
- @cordisjs/plugin-webui
59 changes: 59 additions & 0 deletions plugins/notifier/client/config.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<template>
<k-comment v-for="item in notifiers" :type="item.type">
<render :children="Element.parse(item.content)"></render>
</k-comment>
</template>

<script setup lang="ts">
import { Element } from '@cordisjs/element'
import {} from '@cordisjs/plugin-config/client'
import { useContext, useRpc, send } from '@cordisjs/client'
import type NotifierService from '../src'
import { h, computed, resolveComponent, FunctionalComponent } from 'vue'
const data = useRpc<NotifierService.Data>()
const ctx = useContext()
const notifiers = computed(() => {
return data.value.notifiers.filter((item) => {
return item.paths?.includes(ctx.manager.current.value!.path) && item.content
})
})
const forward = ['div', 'ul', 'ol', 'li', 'br', 'span', 'p', 'img', 'audio', 'video', 'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'del', 'code']
const render: FunctionalComponent<{ children: Element[] }> = ({ children }, ctx) => {
return children.map(({ type, attrs, children }) => {
if (type === 'text') {
return attrs.content
} else if (forward.includes(type)) {
return h(type, attrs, {
default: () => render({ children }, ctx),
})
} else if (type === 'spl') {
return h('span', { class: 'spoiler', ...attrs }, {
default: () => render({ children }, ctx),
})
} else if (type === 'button') {
return h(resolveComponent('el-button'), {
...attrs,
onClick: () => send('notifier/button', attrs.onClick),
}, {
default: () => render({ children }, ctx),
})
} else if (type === 'progress') {
return h(resolveComponent('el-progress'), attrs, {
default: () => render({ children }, ctx),
})
} else if (type === 'template') {
return render({ children }, ctx)
}
})
}
</script>

<style scoped lang="scss">
</style>
32 changes: 32 additions & 0 deletions plugins/notifier/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Context, message } from '@cordisjs/client'
import {} from '../src'
import Config from './config.vue'

interface NotifierMessage {
content: string
type: 'success' | 'warning' | 'error' | 'primary'
}

declare module '@cordisjs/client' {
interface Events<C> {
'notifier/message'(this: C, payload: NotifierMessage): void
}
}

export default (ctx: Context) => {
ctx.slot({
type: 'plugin-details',
component: Config,
order: 0,
})

ctx.on('notifier/message', ({ content, type }) => {
ctx.effect(() => {
const handler = message({
message: content,
type: type === 'primary' ? 'info' : type,
})
return () => handler.close()
})
})
}
11 changes: 11 additions & 0 deletions plugins/notifier/client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../../tsconfig.client",
"include": [
".",
],
"references": [
{
"path": "../tsconfig.json",
},
],
}
71 changes: 71 additions & 0 deletions plugins/notifier/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@cordisjs/plugin-notifier",
"description": "Notifier service for Koishi",
"version": "0.1.0",
"main": "lib/index.cjs",
"types": "lib/index.d.ts",
"exports": {
".": {
"import": "./lib/index.mjs",
"require": "./lib/index.cjs",
"types": "./lib/index.d.ts"
},
"./src/*": "./src/*",
"./client": "./client/index.ts",
"./package.json": "./package.json"
},
"files": [
"lib",
"dist",
"src"
],
"author": "Shigma <shigma10826@gmail.com>",
"license": "MIT",
"scripts": {
"lint": "eslint src --ext .ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cordiverse/webui.git",
"directory": "plugins/notifier"
},
"bugs": {
"url": "https://github.com/cordiverse/webui/issues"
},
"keywords": [
"cordis",
"plugin",
"notifier",
"webui"
],
"cordis": {
"public": [
"dist"
],
"description": {
"en": "Notifier service for Cordis WebUI",
"zh": "Cordis WebUI 通知服务"
},
"service": {
"implements": [
"notifier"
],
"optional": [
"manager",
"webui"
]
}
},
"peerDependencies": {
"@cordisjs/plugin-webui": "^0.1.1",
"cordis": "^3.15.0"
},
"devDependencies": {
"@cordisjs/client": "^0.1.1",
"@cordisjs/plugin-config": "^2.8.6"
},
"dependencies": {
"@cordisjs/element": "^0.1.0",
"cosmokit": "^1.6.2"
}
}
150 changes: 150 additions & 0 deletions plugins/notifier/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Context, Schema, Service } from 'cordis'
import { Dict, isNullable, remove } from 'cosmokit'
import { h } from '@cordisjs/element'
import type { Entry } from '@cordisjs/plugin-webui'
import { resolve } from 'path'

declare module 'cordis' {
interface Context {
notifier: NotifierService
}
}

declare module '@cordisjs/plugin-webui' {
interface Events {
'notifier/button'(id: string): void
}
}

export class Notifier {
public options: Notifier.Config
public dispose: () => void

private actionKeys: string[] = []

constructor(public ctx: Context, options: h.Fragment | Notifier.Options) {
this.options = {
type: 'primary',
content: [],
}
ctx.notifier.store.push(this)
this.update(options)
ctx.notifier.entry?.refresh()
this.dispose = ctx.collect('entry', () => {
this.clearActions()
remove(ctx.notifier.store, this)
ctx.notifier.entry?.refresh()
})
}

clearActions() {
for (const key of this.actionKeys) {
delete this.ctx.notifier.actions[key]
}
this.actionKeys = []
}

update(options: h.Fragment | Notifier.Options) {
if (typeof options === 'string' || h.isElement(options) || Array.isArray(options)) {
options = { content: options }
}
if (!isNullable(options?.content)) {
this.clearActions()
const content = typeof options.content === 'string'
? [h('p', options.content)]
: h.toElementArray(options.content)
options.content = h.transform(content, ({ type, attrs }) => {
if (type === 'button' && typeof attrs.onClick === 'function') {
const key = Math.random().toString(36).slice(2)
this.ctx.notifier.actions[key] = attrs.onClick
this.actionKeys.push(key)
attrs.onClick = key
}
return true
})
}
Object.assign(this.options, options)
this.ctx.notifier.entry?.refresh()
}

toJSON(): Notifier.Data {
return {
...this.options,
content: this.options.content.join(''),
paths: this.ctx.get('loader')?.paths(this.ctx.scope),
}
}
}

export namespace Notifier {
export type Type = 'primary' | 'success' | 'warning' | 'danger'

export interface Options<T = h.Fragment> {
type?: Type
content?: T
}

export interface Config extends Required<Options> {
content: h[]
}

export interface Data extends Required<Options> {
content: string
paths?: string[]
}
}

class NotifierService extends Service {
public store: Notifier[] = []
public actions: Dict<() => void> = Object.create(null)
public entry?: Entry<NotifierService.Data>

constructor(ctx: Context, public config: NotifierService.Config) {
super(ctx, 'notifier', true)

ctx.inject(['webui'], (ctx) => {
ctx.on('dispose', () => this.entry = undefined)

this.entry = ctx.webui.addEntry(process.env.KOISHI_BASE ? [
process.env.KOISHI_BASE + '/dist/index.js',
process.env.KOISHI_BASE + '/dist/style.css',
] : process.env.KOISHI_ENV === 'browser' ? [
// @ts-ignore
import.meta.url.replace(/\/src\/[^/]+$/, '/client/index.ts'),
] : {
dev: resolve(__dirname, '../client/index.ts'),
prod: resolve(__dirname, '../dist'),
}, () => ({
notifiers: this.store.map(notifier => notifier.toJSON()),
}))

ctx.webui.addListener('notifier/button', (id: string) => {
return this.actions[id]()
})
})
}

message(options: string | Notifier.Options<string>) {
if (typeof options === 'string') {
options = { content: options }
}
options.type ||= 'primary'
this.ctx.get('console').broadcast('notifier/message', options)
}

create(options: h.Fragment | Notifier.Options) {
return new Notifier(this[Context.current], options)
}
}

namespace NotifierService {
export interface Data {
notifiers: Notifier.Data[]
}

export interface Config {}

export const Config: Schema<Config> = Schema.object({})
}

export default NotifierService
10 changes: 10 additions & 0 deletions plugins/notifier/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
},
"include": [
"src",
],
}

0 comments on commit bd477e5

Please sign in to comment.