diff --git a/oaer/lib/api/index.ts b/oaer/lib/api/index.ts index c896b0d..4f99f41 100644 --- a/oaer/lib/api/index.ts +++ b/oaer/lib/api/index.ts @@ -12,8 +12,10 @@ const info = () => { }>('/', {}) } +const set_base_host = webapi.set_base_host export { - apitoken + apitoken, + set_base_host } export default { diff --git a/oaer/lib/api/webapi.ts b/oaer/lib/api/webapi.ts index a68a6a2..4495792 100644 --- a/oaer/lib/api/webapi.ts +++ b/oaer/lib/api/webapi.ts @@ -175,6 +175,12 @@ export const webapi = { 'content-type': 'application/json;charset=UTF-8', }, })), + set_base_host(host: string) { + if (host === '' || host === '/') { + return + } + webapi.client.defaults.baseURL = host + '/api/' + }, Get(url: string, req: data): Promise { return webapi.client.request(Object.assign({ method: 'get', url: url }, transData(req))) }, diff --git a/oaer/lib/assets/css/oaer.scss b/oaer/lib/assets/css/oaer.scss index bef718a..0f8599d 100644 --- a/oaer/lib/assets/css/oaer.scss +++ b/oaer/lib/assets/css/oaer.scss @@ -228,6 +228,24 @@ div[voa] { } +.voa-fs { + padding: 0 1rem; + + .voa-fs-header { + display: flex; + justify-content: space-between; + align-items: center; + } + + .fsdir { + .fsdir-header { + display: flex; + justify-content: space-between; + align-items: center; + } + } +} + .voa-logo { background-image: url("../favicon.svg"); background-size: cover; diff --git a/oaer/lib/components/account.ts b/oaer/lib/components/account.ts index 4457708..7cb4381 100644 --- a/oaer/lib/components/account.ts +++ b/oaer/lib/components/account.ts @@ -19,7 +19,7 @@ export default v('voa-account', [ v('voa-ah-2')] ), v('voa-account-body', [ - v('voa-ab-ico', [v({ typ: 'img', attrs: { 'src': () => `${logic.user.icon}` } })]), + v('voa-ab-ico', [v({ typ: 'img', attrs: { 'src': () => `${logic.urlwarp(logic.user.icon)}` } })]), v('voa-ab-info', [ v('voa-abi-1', [v('span', '昵称:'), v('span', () => logic.user.nickname)]), v('voa-abi-2', [v('span', '账户:'), v('span', () => logic.user.username)]), diff --git a/oaer/lib/components/app.ts b/oaer/lib/components/app.ts index bf67caf..896dcc5 100644 --- a/oaer/lib/components/app.ts +++ b/oaer/lib/components/app.ts @@ -32,7 +32,7 @@ export default () => { vfor(apps, (data) => v({ class: 'voa-apps-box', - children: [v('img', '', { src: () => data.icon })], + children: [v('img', '', { src: () => logic.urlwarp(data.icon) })], onclick: () => { logic.goto(`/app/${data.id}`) } diff --git a/oaer/lib/components/fsdom.ts b/oaer/lib/components/fsdom.ts new file mode 100644 index 0000000..5f8f893 --- /dev/null +++ b/oaer/lib/components/fsdom.ts @@ -0,0 +1,43 @@ +/* + * fs.ts + * Copyright (C) 2024 veypi + * 2024-11-04 17:11 + * Distributed under terms of the GPL license. + */ + + +import { v } from "../v2dom"; +import logic from '../logic' +import api from "../api"; +import fstree from "./fstree"; + + +export default () => { + return v({ + class: 'voa-fs', + children: [ + v('voa-fs-header', + [ + v({ + class: 'voa-item-title', + children: ['我的文件'], + onclick: () => { + logic.goto('/') + } + }), + v({ + class: 'voa-fs-subtxt', children: '获取密钥', onclick: () => { + api.token.Post({ + refresh: logic.token.refresh.raw(), + user_id: logic.token.refresh.uid! + }).then(e => { + console.log(e) + }) + } + }) + ] + ), + v('voa-fs-body', fstree('/')) + ] + }) +} diff --git a/oaer/lib/components/fstree.ts b/oaer/lib/components/fstree.ts new file mode 100644 index 0000000..15577bf --- /dev/null +++ b/oaer/lib/components/fstree.ts @@ -0,0 +1,37 @@ +/* + * fstree.ts + * Copyright (C) 2024 veypi + * 2024-11-04 17:18 + * Distributed under terms of the GPL license. + */ + +import v, { proxy, vfor } from "../v2dom" +import fs, { FileStat } from '../fs' +import { vif } from "../v2dom/v2dom" + +const dirTree = (url: string) => { + let treeItems = proxy.Watch([] as FileStat[]) + let expand = proxy.Watch({ value: false }) + let child = v('', + () => + expand.value && treeItems.length ? vfor(treeItems, (item) => v('div', item.basename), undefined, '123') : []) + return v('fsdir', [ + v({ + class: 'fsdir-header', + children: [v('div', () => expand.value + ''), v('div', url)], + onclick: () => { + expand.value = !expand.value + if (expand.value) { + fs.user.getDirectoryContents(url).then((e: any) => { + treeItems.splice(0) + treeItems.push(...e) + }) + } + } + }), + vif([() => expand.value && treeItems.length, () => child], [1, () => v('div', 123)]) + ]) +} + + +export default dirTree diff --git a/oaer/lib/components/index.ts b/oaer/lib/components/index.ts index aada22b..fe35690 100644 --- a/oaer/lib/components/index.ts +++ b/oaer/lib/components/index.ts @@ -22,7 +22,7 @@ export default class { let frame_user = v({ class: 'voa-on', vclass: [() => logic.ready ? 'voa-scale-in' : 'voa-scale-off'], - children: [() => ``], + children: [() => ``], onclick: () => { logic.getDetailUser() if (!this.slide) { diff --git a/oaer/lib/components/slide.ts b/oaer/lib/components/slide.ts index 380b4b5..5ab9320 100644 --- a/oaer/lib/components/slide.ts +++ b/oaer/lib/components/slide.ts @@ -9,6 +9,7 @@ import account from './account' import app from './app' import logic from '../logic' import bus from '../bus' +import fsdom from './fsdom' /* mask @@ -37,14 +38,15 @@ export default class { bus.emit('logout') } }) - console.log(logic.user) this.main = v({ class: 'voa-slide-main', children: [ account, v({ class: 'voa-sm-separate' }), app(), - v({ class: 'voa-sm-separate' }) + v({ class: 'voa-sm-separate' }), + fsdom(), + v({ class: 'voa-sm-separate' }), ] }) this.body = v({ diff --git a/oaer/lib/fs.ts b/oaer/lib/fs.ts index a56ad28..86d698c 100644 --- a/oaer/lib/fs.ts +++ b/oaer/lib/fs.ts @@ -7,18 +7,10 @@ import * as webdav from 'webdav' +export { type FileStat } from 'webdav' import { proxy } from "./v2dom"; import logic from "./logic"; -export interface fileProps { - filename: string, - basename: string, - lastmod: string, - size: number, - type: "directory" | "file", - etag: string -} - function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -32,19 +24,20 @@ class davWraper { this.host = host this.prefix = prefix this.token = token - this.client = webdav.createClient(host + prefix) + this.client = webdav.createClient(this.host + this.prefix) + this.set(host, prefix, token) + } + set(host: string, prefix: string, token: string) { + this.host = host + this.prefix = prefix + this.token = token + this.client = webdav.createClient(this.host + this.prefix) if (this.prefix.endsWith('/')) { this.prefix = this.prefix.slice(0, -1) } - } - set(k: 'host' | 'token' | 'prefix', value: string) { - if (value !== this[k]) { - this[k] = value - this.client = webdav.createClient(this.host + this.prefix) - this.client.setHeaders({ - authorization: "bearer " + this.token - }) - } + this.client.setHeaders({ + authorization: "bearer " + this.token + }) } putFileContents(filename: string, data: string | webdav.BufferLike, options?: webdav.PutFileContentsOptions) { return this.retry(() => this.client.putFileContents(filename, data, options)) @@ -95,17 +88,12 @@ const user = new davWraper(logic.Host(), '/fs/u/', token) const app = new davWraper(logic.Host(), '/fs/a/' + logic.oa_id, token) -export const set_host = (h: string) => { - user.set('host', h) - app.set('host', h) -} const sync = () => { if (logic.token.oa.isVaild()) { let t = logic.token.oa.raw() // console.warn('sync oafs token: ' + t) - user.set('token', t) - app.set('token', t) - app.set('prefix', '/fs/a/' + logic.app_id) + user.set(logic.Host(), '/fs/u/', t) + app.set(logic.Host(), '/fs/a/' + logic.app_id, t) } } proxy.Listen(() => { diff --git a/oaer/lib/logic.ts b/oaer/lib/logic.ts index 9ca0c9f..7207083 100644 --- a/oaer/lib/logic.ts +++ b/oaer/lib/logic.ts @@ -152,17 +152,23 @@ const logic = proxy.Watch({ }, host: window.location.origin, Host() { - return this.host + return logic.host }, - goto(url: string) { + urlwarp(url: string) { + let h = logic.host + if (!url) { + return '' + } if (url.startsWith('http')) { - window.location.href = url - return + return url } if (!url.startsWith('/')) { url = '/' + url } - window.location.href = this.host + url + return h + url + }, + goto(url: string) { + window.location.href = logic.urlwarp(url) }, }) diff --git a/oaer/lib/main.ts b/oaer/lib/main.ts index e10a3ea..fece225 100644 --- a/oaer/lib/main.ts +++ b/oaer/lib/main.ts @@ -9,7 +9,7 @@ import './assets/css/oaer.scss' import bus from './bus' import logic from './logic' import ui from './components' -import api from './api' +import api, { set_base_host } from './api' import fs from './fs' @@ -33,6 +33,7 @@ export default new class { init(host?: string, code?: string) { if (host) { logic.host = host + set_base_host(host) } if (code) { logic.token.refresh.set(code) diff --git a/oaer/lib/v2dom/proxy.ts b/oaer/lib/v2dom/proxy.ts index bb3b263..73433c6 100644 --- a/oaer/lib/v2dom/proxy.ts +++ b/oaer/lib/v2dom/proxy.ts @@ -85,7 +85,15 @@ function DomListen(dom: HTMLElement, fn: () => void) { const isProxy = Symbol("isProxy") const DataID = Symbol("DataID") -function Watch(data: T) { +// @ts-ignore +window.$u = (id: number) => { + callbackCache[id][1]() +} + +function Ref(data: T): { value: T } { + return Watch({ value: data }) +} +function Watch(data: T, debug = false) { const did = GenUniqueID() let isArray = false if (Object.prototype.toString.call(data) === '[object Array]') { @@ -106,7 +114,7 @@ function Watch(data: T) { if (value[isProxy]) { return value } else { - let newValue = Watch(value) + let newValue = Watch(value, debug) Reflect.set(target, key, newValue, receiver) return newValue } @@ -124,12 +132,16 @@ function Watch(data: T) { listeners[lkey].push(idx) } } - // console.log(`${did} get ${key.toString()}:${value} ${idx}`) + if (debug) { + console.log(`${did} get ${key.toString()}:${value} ${idx}`) + } return value; }, set(target: Object, key: string | symbol, newValue: any, receiver: any) { - // console.log(`${did} set ${key.toString()} ${newValue}`) const result = Reflect.set(target, key, newValue, receiver); + if (debug) { + console.log(`${did} set ${key.toString()} ${newValue}`, listeners, result) + } if (result && listen_tags.length === 0) { let lkey = key if (isArray) { @@ -158,4 +170,4 @@ function Watch(data: T) { return res } -export default { Watch, Listen, DomListen, ForceUpdate, DataID, GenUniqueID, } +export default { Watch, Listen, Ref, DomListen, ForceUpdate, DataID, GenUniqueID, } diff --git a/oaer/lib/v2dom/v2dom.ts b/oaer/lib/v2dom/v2dom.ts index 86df019..7abf8c3 100644 --- a/oaer/lib/v2dom/v2dom.ts +++ b/oaer/lib/v2dom/v2dom.ts @@ -24,8 +24,11 @@ interface buildOpts { const typs = ['div', 'img', 'span', 'p', 'a', 'input'] type vforChild = { obj: T[], fn: (d: T, idx?: number) => HTMLElement, iterID: string, ifnone: HTMLElement } -type computedFn = () => string | HTMLElement -type childTyp = HTMLElement | vforChild | string | computedFn +type vIfCondition = boolean | number | string | undefined | null | (() => vIfCondition) +type vIfStatment = HTMLElement | (() => HTMLElement) +type vIfChild = { ifID: string, rules: [vIfCondition, vIfStatment][] } +type computedFn = () => number | string | HTMLElement | vforChild | [] +type childTyp = HTMLElement | vforChild | string | number | computedFn | vIfChild type attrs = { [key: string]: string | number | (() => string) } export const v = (opts: buildOpts | string, children?: childTyp | childTyp[], attrs?: attrs) => { @@ -128,6 +131,8 @@ function handleChild(dom: HTMLElement, c: any, is_only = false, is_listen = fals } } else if (c && c.iterID) { handleChildVfor(dom, c as vforChild, is_listen) + } else if (c && c.ifID) { + handleChildVif(dom, c as vIfChild, is_listen) } else if (c instanceof Element) { dom.appendChild(c) } else if (typeof c === 'undefined' || c === null) { @@ -136,8 +141,55 @@ function handleChild(dom: HTMLElement, c: any, is_only = false, is_listen = fals } } -export const vfor = (obj: T[], fn: (d: T, idx?: number) => HTMLElement, ifnone?: () => HTMLElement): vforChild => { - let iterID = proxy.GenUniqueID() +function handleChildVif(dom: HTMLElement, data: vIfChild, is_listen = false) { + let fn = () => { + let target = emptyDom() + for (let r of data.rules) { + let c = Boolean(r[0]) + if (typeof r[0] === 'function') { + c = Boolean(r[0]()) + } + if (c) { + if (typeof r[1] === 'function') { + target = r[1]() + } else { + target = r[1] + } + break + } + } + let before: Element | null = null + for (let domc of dom.children) { + if (domc.getAttribute('vbind-if') === data.ifID) { + before = domc + } + } + target.setAttribute('vbind-if', data.ifID) + if (before) { + dom.replaceChild(target, before) + } else { + dom.appendChild(target) + } + } + if (is_listen) { + fn() + } else { + proxy.DomListen(dom, fn) + } +} + +export const vif = (...rules: [vIfCondition, vIfStatment][]): vIfChild => { + let ifID = proxy.GenUniqueID() + return { + ifID, + rules + } +} + +export const vfor = (obj: T[], fn: (d: T, idx?: number) => HTMLElement, ifnone?: () => HTMLElement, iterID?: string): vforChild => { + if (!iterID) { + iterID = proxy.GenUniqueID() + } let dom = document.createElement('div') as HTMLElement dom.style.display = 'none' dom.style.visibility = 'hidden' @@ -238,3 +290,11 @@ function handleChildVfor(dom: HTMLElement, data: vforChild, is_listen = fal } +function emptyDom() { + let dom = document.createElement('div') as HTMLElement + dom.style.display = 'none' + dom.style.visibility = 'hidden' + dom.style.height = '0' + dom.style.width = '0' + return dom +}