Skip to content

Commit

Permalink
refa: refactor client into plugins, fix #305
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 30, 2024
1 parent 1451c26 commit 275d7c8
Show file tree
Hide file tree
Showing 20 changed files with 382 additions and 324 deletions.
4 changes: 2 additions & 2 deletions packages/client/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ root.plugin(status)
root.plugin(styles)
root.plugin(theme)

root.app.mount('#app')
root.start()

if (!global.static) {
const endpoint = new URL(global.endpoint, location.origin).toString()
connect(() => new WebSocket(endpoint.replace(/^http/, 'ws')))
connect(root, () => new WebSocket(endpoint.replace(/^http/, 'ws')))
}
2 changes: 1 addition & 1 deletion packages/client/app/status/loading.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { computed } from 'vue'
const ctx = useContext()
const progress = computed(() => {
const states = Object.values(ctx.internal.extensions)
const states = Object.values(ctx.$loader.extensions)
return states.filter(state => state.done.value).length / states.length
})
Expand Down
5 changes: 3 additions & 2 deletions packages/client/app/theme/activity/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
import { computed, provide } from 'vue'
import { useWindowSize } from '@vueuse/core'
import { activities, Activity, useConfig, useMenu } from '@koishijs/client'
import { Activity, useConfig, useContext, useMenu } from '@koishijs/client'
import ActivityItem from './item.vue'
import ActivitySeparator from './separator.vue'
const ctx = useContext()
const config = useConfig()
const trigger = useMenu('theme.activity')
const { height, width } = useWindowSize()
Expand All @@ -34,7 +35,7 @@ const groups = computed(() => {
const unit = width.value <= 768 ? 52 : 56
const total = height.value - (width.value <= 768 ? 4 : 8)
const available = Object.fromEntries(Object
.entries(activities)
.entries(ctx.$router.pages)
.filter(([, data]) => !data.disabled())
.map(([key, data]) => [key, [data]]))
for (const id of Object.keys(available)) {
Expand Down
5 changes: 3 additions & 2 deletions packages/client/app/theme/activity/separator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<script lang="ts" setup>
import { ComputedRef, ref, inject } from 'vue'
import { activities, Activity, useConfig } from '@koishijs/client'
import { Activity, useConfig, useContext } from '@koishijs/client'
type Position = 'top' | 'bottom'
Expand All @@ -34,6 +34,7 @@ function handleDragLeave(event: DragEvent) {
}
const config = useConfig()
const ctx = useContext()
function handleDrop(event: DragEvent) {
hasDragOver.value = false
Expand All @@ -46,7 +47,7 @@ function handleDrop(event: DragEvent) {
event.preventDefault()
let index = props.index
const item = activities[id]
const item = ctx.$router.pages[id]
if (oldIndex < 0) {
list.splice(index, 0, item)
} else {
Expand Down
95 changes: 0 additions & 95 deletions packages/client/client/activity.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/client/client/components/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const KActivityLink = defineComponent({
setup(props, { slots }) {
const ctx = useContext()
return () => {
const activity = ctx.internal.activities[props.id]
const activity = ctx.$router.pages[props.id]
return h(RouterLink, {
to: ctx.internal.routeCache[activity?.id] || activity?.path.replace(/:.+/, ''),
to: ctx.$router.cache[activity?.id] || activity?.path.replace(/:.+/, ''),
}, {
default: () => slots.default?.() ?? activity?.name,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/client/client/components/slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const KSlot = defineComponent({
const internal = props.single ? [] : [...slots.default?.() || []]
.filter(node => node.type === KSlotItem)
.map(node => ({ node, order: node.props?.order || 0 }))
const external = [...ctx.internal.views[props.name] || []]
const external = [...ctx.$router.views[props.name] || []]
.filter(item => !item.disabled?.())
.map(item => ({
node: h(item.component, { ...props.data }, slots),
Expand Down
72 changes: 19 additions & 53 deletions packages/client/client/context.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import * as cordis from 'cordis'
import { Dict, remove } from 'cosmokit'
import {
App, Component, createApp, defineComponent, h, inject, markRaw,
onBeforeUnmount, provide, reactive, Ref, resolveComponent,
onBeforeUnmount, provide, Ref, resolveComponent,
} from 'vue'
import { createI18n } from 'vue-i18n'
import { activities, Activity } from './activity'
import { SlotOptions } from './components'
import { extensions, LoadResult } from './loader'
import ActionService from './plugins/action'
import I18nService from './plugins/i18n'
import LoaderService from './plugins/loader'
import RouterService from './plugins/router'
import SettingService from './plugins/setting'
import ThemeService from './plugins/theme'
import { insert } from './utils'

// layout api

export interface Events<C extends Context> extends cordis.Events<C> {
'activity'(activity: Activity): boolean
}
export interface Events<C extends Context> extends cordis.Events<C> {}

export interface Context {
[Context.events]: Events<this>
internal: Internal
}

export function useContext() {
Expand All @@ -35,18 +31,7 @@ export function useRpc<T>(): Ref<T> {
return parent.extension?.data
}

export class Internal {
extensions = extensions
activities = activities
routeCache = routeCache
views = reactive<Dict<SlotOptions[]>>({})
i18n = createI18n({
legacy: false,
fallbackLocale: 'zh-CN',
})
}

export const routeCache = reactive<Record<keyof any, string>>({})
export interface Internal {}

export class Context extends cordis.Context {
// workaround injection check
Expand All @@ -55,25 +40,31 @@ export class Context extends cordis.Context {

app: App

extension?: LoadResult
internal = new Internal()

constructor() {
super()
this.provide('extension')

this.extension = null
this.internal = {} as Internal
this.app = createApp(defineComponent({
setup: () => () => [
h(resolveComponent('k-slot'), { name: 'root', single: true }),
h(resolveComponent('k-slot'), { name: 'global' }),
],
}))
this.app.use(this.internal.i18n)
this.app.provide('cordis', this)

this.plugin(ActionService)
this.plugin(I18nService)
this.plugin(LoaderService)
this.plugin(RouterService)
this.plugin(SettingService)
this.plugin(ThemeService)

this.on('ready', async () => {
await this.$loader.initTask
this.app.use(this.$i18n.i18n)
this.app.use(this.$router.router)
this.app.mount('#app')
})
}

addEventListener<K extends keyof WindowEventMap>(
Expand All @@ -96,31 +87,6 @@ export class Context extends cordis.Context {
return () => h(component, props, slots)
})
}

/** @deprecated */
addView(options: SlotOptions) {
return this.slot(options)
}

/** @deprecated */
addPage(options: Activity.Options) {
return this.page(options)
}

slot(options: SlotOptions) {
options.order ??= 0
options.component = this.wrapComponent(options.component)
if (options.when) options.disabled = () => !options.when()
const list = this.internal.views[options.type] ||= []
insert(list, options)
return this.scope.collect('view', () => remove(list, options))
}

page(options: Activity.Options) {
options.component = this.wrapComponent(options.component)
const activity = new Activity(this, options)
return this.scope.collect('page', () => activity.dispose())
}
}

markRaw(cordis.Context.prototype)
Expand Down
8 changes: 4 additions & 4 deletions packages/client/client/data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ClientConfig, Console, DataService, Events } from '@koishijs/plugin-console'
import type { Promisify, Universal } from 'koishi'
import { markRaw, reactive, ref } from 'vue'
import { root } from '.'
import { Context } from './context'

export type Store = {
[K in keyof Console.Services]?: Console.Services[K] extends DataService<infer T> ? T : never
Expand Down Expand Up @@ -61,7 +61,7 @@ receive('response', ({ id, value, error }) => {
}
})

export function connect(callback: () => Universal.WebSocket) {
export function connect(ctx: Context, callback: () => Universal.WebSocket) {
const value = callback()

let sendTimer: number
Expand All @@ -81,7 +81,7 @@ export function connect(callback: () => Universal.WebSocket) {
}
console.log('[koishi] websocket disconnected, will retry in 1s...')
setTimeout(() => {
connect(callback).then(location.reload, () => {
connect(ctx, callback).then(location.reload, () => {
console.log('[koishi] websocket disconnected, will retry in 1s...')
})
}, 1000)
Expand All @@ -94,7 +94,7 @@ export function connect(callback: () => Universal.WebSocket) {
if (data.type in listeners) {
listeners[data.type](data.body)
}
root.emit(data.type, data.body)
ctx.emit(data.type, data.body)
})

value.addEventListener('close', reconnect)
Expand Down
Loading

0 comments on commit 275d7c8

Please sign in to comment.