godoos/frontend/src/system/index.ts
2024-07-24 14:07:03 +08:00

540 lines
No EOL
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { initRootState, RootState } from './root';
import { SystemStateEnum } from './type/enum';
import { markRaw, nextTick } from 'vue';
import {
Saveablekey,
Setting,
SystemOptions,
SystemOptionsCertainly,
WinAppOptions,
} from './type/type';
import { initEventer, Eventer, initEventListener } from './event';
import { OsFileSystem } from './core/FileSystem';
import {useOsFile} from './core/FileOs';
import { version } from '../../package.json';
import { BrowserWindow, BrowserWindowOption } from './window/BrowserWindow';
import { extname } from './core/Path';
import { initBuiltinApp, initBuiltinFileOpener } from './initBuiltin';
import { defaultConfig } from './initConfig';
import { OsFileInterface } from './core/FIleInterface';
import { Notify, NotifyConstructorOptions } from './notification/Notification';
import { Dialog } from './window/Dialog';
import { pick } from '../util/modash';
import { Tray, TrayOptions } from './menu/Tary';
import { InitSystemFile, InitUserFile } from './core/SystemFileConfig';
import { createInitFile } from './core/createInitFile';
import { getSystemConfig, getSystemKey, setSystemKey, setSystemConfig, clearSystemConfig } from './config'
export type OsPlugin = (system: System) => void;
export type FileOpener = {
name?: string;
icon: string;
hiddenInChosen?: boolean;
func: (path: string, content: string) => void;
};
export class Bios {
public static _onOpen: ((system: System) => void) | null = null;
public static onOpen(func: (system: System) => void) {
this._onOpen = func;
}
constructor() {
//
}
}
/**
* @description: System 类,在初始化的过程中需要提供挂载点,以及一些配置
*/
export class System {
public static GLOBAL_SYSTEM: System;
readonly _options: SystemOptions;
_rootState: RootState;
private _eventer: Eventer;
private _ready: ((value: System) => void) | null = null;
private _error: ((reason: unknown) => void) | null = null;
private _flieOpenerMap: Map<string, FileOpener> = new Map();
version = version;
isFirstRun = true;
rootRef: HTMLElement | undefined = undefined;
fs!: any;
constructor(options?: SystemOptions) {
this._options = this.initOptions(options);
this._rootState = this.initRootState();
System.GLOBAL_SYSTEM = this; // 挂载全局系统
Bios._onOpen && Bios._onOpen(this);
this._eventer = this.initEvent();
this.firstRun();
this.initSystem();
}
/**
* @description: pure 初始化配置选项
*/
private initOptions(options?: SystemOptions) {
const tempOptions = Object.assign({}, defaultConfig, options);
return tempOptions;
}
/**
* @description: 获取系统配置
*/
private initRootState(): RootState {
return initRootState(this._options);
}
/**
* @description: 初始化系统
*/
private async initSystem() {
//await startApp();
/**
* 过程:激活屏幕,桥接事件。
*/
this._rootState.state = SystemStateEnum.opening;
initBuiltinFileOpener(this); // 注册内建文件打开器
await this.initFileSystem(); // 初始化文件系统
await this.initSavedConfig(); // 初始化保存的配置
initBuiltinApp(this); // 初始化内建应用
this.initApp(); // 初始化配置应用到app文件夹中
this.initAppList(); // 刷新app文件夹展示应用
// 判断是否登录
this.isLogin();
initEventListener(); // 初始化事件侦听
this._ready && this._ready(this);
//this.runPlugin(this); // 运行fs中插件
this.initBackground(); // 初始化壁纸
this.emit('start');
// setTimeout(() => {
// if (this._rootState.magnet?.length < 1) {
// this.refershAppList()
// }
// }, 6000);
}
/**
* @description: 判断是否登录
*/
private isLogin() {
if (!this._options.login) {
this._rootState.state = SystemStateEnum.open;
return;
} else {
if (this._options.login.init?.()) {
this._rootState.state = SystemStateEnum.open;
return;
}
this._rootState.state = SystemStateEnum.lock;
const tempCallBack = this._options.loginCallback;
if (!tempCallBack) {
throw new Error('没有设置登录回调函数');
}
this._options.loginCallback = async (username: string, password: string) => {
const res = await tempCallBack(username, password);
if (res) {
this._rootState.state = SystemStateEnum.open;
return true;
}
return false;
};
}
}
/**
* @description: 初始化壁纸
*/
initBackground() {
const background = getSystemKey('background');
if (background.type === 'image') {
this._rootState.options.background = background.url
}else{
this._rootState.options.background = background.color;
}
// this._rootState.options.background = '/image/bg/bg5.jpg';
}
/**
* @description: 初始化事件系统
*/
private initEvent() {
/**
* 过程:监听事件,处理事件
*/
return initEventer();
}
private initApp() {
this._rootState.options.desktop?.forEach((item) => {
this.addApp(item);
});
this._rootState.options.magnet?.forEach((item) => {
this.addMagnet(item);
});
this._rootState.options.menulist?.forEach((item) => {
this.addMenuList(item);
});
}
refershAppList() {
const APP_TYPE = ['apps', 'magnet', 'menulist'];
const system = useSystem();
for (let i = 0; i < APP_TYPE.length; i++) {
const element = APP_TYPE[i];
system?.fs
.readdir(
`${system._options.userLocation}${{
apps: 'Desktop',
magnet: 'Magnet',
menulist: 'Menulist',
}[element]
}`
)
.then((res:any) => {
if (res) {
const list = res;
const tempList:any = [];
for (let j = 0; j < list.length; j++) {
const item = list[j];
tempList.push(item);
}
switch (element) {
case 'apps':
useSystem()._rootState.apps.splice(0, useSystem()._rootState.apps.length, ...tempList);
break;
case 'magnet':
useSystem()._rootState.magnet.splice(0, useSystem()._rootState.magnet.length, ...tempList);
break;
case 'menulist':
useSystem()._rootState.menulist.splice(
0,
useSystem()._rootState.menulist.length,
...tempList
);
break;
default:
break;
}
}
});
}
}
isReadyUpdateAppList = false;
initAppList() {
this.isReadyUpdateAppList = true;
nextTick(() => {
if (this.isReadyUpdateAppList) {
this.isReadyUpdateAppList = false;
this.refershAppList();
}
});
}
private async initFileSystem() {
if (getSystemKey("storeType") == 'local'){
//this.fs = this._options.fs;
this.fs = useOsFile();
await this.initOutSystem()
} else {
this.fs = await new OsFileSystem().initFileSystem(this._options);
(this.fs as OsFileSystem).on('error', (err: string) => {
this.emitError(err);
});
this.fs.registerWatcher(new RegExp(`^${this._options.userLocation}`), () => {
this.initAppList();
});
}
}
async initOutSystem() {
if (!this.isFirstRun) {
return;
}
await this.fs.mkdir('/C');
await this.fs.mkdir('/D');
await this.fs.mkdir('/E');
await this.fs.mkdir('/B');
await createInitFile(this.fs, InitUserFile, this._options.userLocation);
await createInitFile(this.fs, InitSystemFile, this._options.systemLocation);
// await this.fs.chmod('/C', OsFileMode.ReadWrite);
// await this.fs.chmod('/D', OsFileMode.ReadWrite);
// await this.fs.chmod('/E', OsFileMode.ReadWrite);
// await this.fs.chmod('/B', OsFileMode.ReadWrite);
}
replaceFileSystem(fs: OsFileInterface) {
this.fs = fs;
this.initAppList();
}
mountVolume(path: string, fs: OsFileInterface) {
if (this.fs instanceof OsFileSystem) {
this.fs.mountVolume(path, fs);
} else {
console.error('自定义文件系统不支持挂载卷');
}
}
/**
* @description: 初始化保存的配置
*/
private async initSavedConfig() {
//const config = await this.fs.readFile(join(this._options.systemLocation || '', 'os/config.json'));
const config = getSystemConfig();
if (config) {
try {
this._rootState.options = Object.assign(this._rootState.options, config);
} catch {
new Notify({
title: '提示',
content: '配置文件格式错误',
});
}
}
}
setConfig<T extends keyof SystemOptionsCertainly>(key: T, value: SystemOptionsCertainly[T]): Promise<void>;
setConfig<T extends string>(
key: T,
value: T extends keyof SystemOptionsCertainly ? SystemOptionsCertainly[T] : unknown
): Promise<void>;
setConfig<T extends keyof SystemOptionsCertainly>(key: string, value: SystemOptionsCertainly[T]) {
this._rootState.options[key] = value;
if (Saveablekey.includes(key as any)) {
// return this.fs.writeFile(
// join(this._options.systemLocation || '', 'os/config.json'),
// JSON.stringify(pick(this._rootState.options, ...Saveablekey))
// );
return setSystemConfig(pick(this._rootState.options, ...Saveablekey))
} else {
return Promise.resolve();
}
}
getConfig<T extends keyof SystemOptionsCertainly>(key: T): SystemOptionsCertainly[T];
getConfig<T extends string>(key: T): unknown;
getConfig(key: string) {
return this._rootState.options[key];
}
private addWindowSysLink(loc: string, options: WinAppOptions, force = false) {
if (this.isFirstRun || force) {
this.fs.writeFile(
`${this._options.userLocation}${loc}/` + options.name + '.exe',
`link::${loc}::${options.name}::${options.icon}`
);
} else {
this.initAppList();
}
if (typeof options.window.content === 'string') {
// TODO: 当content是string的时候
} else {
options.window.content = markRaw(options.window.content);
}
this._rootState.windowMap[loc].set(options.name, options);
}
/**
* @description: 添加应用
* force 表示强制,在每次启动时都会添加
*/
addApp(options: WinAppOptions, force = false) {
this.addWindowSysLink('Desktop', options, force);
}
addMagnet(options: WinAppOptions, force = false) {
this.addWindowSysLink('Magnet', options, force);
}
addMenuList(options: WinAppOptions, force = false) {
this.addWindowSysLink('Menulist', options, force);
}
addBuiltInApp(options: WinAppOptions) {
this._rootState.windowMap['Builtin'].set(options.name, options);
}
whenReady(): Promise<System> {
return new Promise<System>((resolve, reject) => {
this._ready = resolve;
this._error = reject;
});
}
firstRun() {
if (getSystemKey('isFirstRun')) {
this.isFirstRun = false;
return false;
} else {
this.isFirstRun = true;
setSystemKey('isFirstRun', true)
this.emit('firstRun');
return true;
}
}
shutdown() {
this._rootState.state = SystemStateEnum.close;
}
reboot() {
this._rootState.state = SystemStateEnum.close;
window.location.reload();
}
recover() {
clearSystemConfig()
this._rootState.state = SystemStateEnum.close;
this.fs.removeFileSystem().then(() => {
window.indexedDB.deleteDatabase("GodoDatabase");
window.location.reload();
})
}
getEventer() {
return this._eventer;
}
emit(event: string, ...args: any[]) {
this.emitEvent(event, ...args);
}
emitEvent(event: string, ...args: any[]) {
const eventArray = event.split('.');
eventArray.forEach((_:any, index) => {
const tempEvent = eventArray.slice(0, index + 1).join('.');
this._eventer.emit(tempEvent, event, args);
});
this._eventer.emit('system', event, args);
}
on(event: string, callback: (...args: any[]) => void): void {
this.mountEvent(event, callback);
}
mountEvent(event: string | string[], callback: (...args: any[]) => void) {
if (Array.isArray(event)) {
event.forEach((item) => {
this.mountEvent(item, callback);
});
return;
} else {
this._eventer.on(event, callback);
}
}
offEvent(event?: string, callback?: (...args: any[]) => void): void {
this._eventer.off(event, callback);
}
/** 注册文件打开器 */
registerFileOpener(type: string | string[], opener: FileOpener) {
if (Array.isArray(type)) {
type.forEach((item) => {
this._flieOpenerMap.set(item, opener);
});
return;
}
this._flieOpenerMap.set(type, opener);
}
getOpener(type: string) {
return this._flieOpenerMap.get(type);
}
getAllFileOpener() {
return this._flieOpenerMap;
}
/** 注册设置app的设置页面 */
registerSettingPanel(setting: Setting) {
const temp = {
...setting,
content: markRaw(setting.content),
};
this._rootState.settings?.push(temp);
}
/**打开os 文件系统的文件 */
async openFile(path: string) {
const fileStat = await this.fs.stat(path);
if (!fileStat) {
throw new Error('文件不存在');
}
// 如果fileStat为目录
if (fileStat?.isDirectory) {
// 从_fileOpenerMap中获取'link'对应的函数并调用
this._flieOpenerMap.get('dir')?.func.call(this, path, '');
return;
} else {
// 读取文件内容
const fileContent = await this.fs.readFile(path);
// 从_fileOpenerMap中获取文件扩展名对应的函数并调用
this._flieOpenerMap
.get(extname(fileStat?.path || '') || 'link')
?.func.call(this, path, fileContent || '');
}
}
// 插件系统
use(func: OsPlugin): void {
return func(this);
}
// 状态序列化和反序列化
async serializeState(): Promise<string> {
const serializeFile = await this.fs.serializeFileSystem();
return JSON.stringify(serializeFile);
}
deserializeState(state: string) {
this.fs.deserializeFileSystem(JSON.parse(state));
}
outerFileDropCallback:
| ((path: string, list: FileList | undefined, process: (path: string) => void) => void)
| null = null;
// 当从外部拖入文件时
onOuterFileDrop(func: (path: string, list: FileList | undefined, process: (path: string) => void) => void) {
this.outerFileDropCallback = func;
}
/** 方便的通过system创建window */
createWindow(options: BrowserWindowOption) {
const win = new BrowserWindow(options);
return win;
}
/** 方便的通过system创建notify */
createNotify(options: NotifyConstructorOptions) {
return new Notify(options);
}
/** 方便的通过system创建Dialog */
createDialog() {
return Dialog;
}
/** 方便的通过system创建Tray */
createTray(options: TrayOptions) {
return new Tray(options);
}
errorHandler = 0;
emitError(error: string) {
this._error && this._error(error);
this._rootState.error = error;
this.errorHandler = Date.now();
setTimeout(() => {
if (Date.now() - this.errorHandler > 1000 * 3) {
this._rootState.error = '';
}
}, 1000 * 4);
}
}
export function useSystem() {
return System.GLOBAL_SYSTEM!;
}
export * from './core/Path';
export * from './core/FileSystem';
export { BrowserWindow } from './window/BrowserWindow';
export { Notify } from './notification/Notification';
export { Dialog } from './window/Dialog';
export type { SystemOptions, WinApp } from './type/type';
export { vDragable } from './window/MakeDragable';
export type { OsFileInterface } from './core/FIleInterface';
export { t } from '../i18n';
export { Tray } from './menu/Tary';
export { dealIcon } from '../util/Icon';
export { Menu } from './menu/Menu';
export { MenuItem } from './menu/MenuItem';