@owdproject/core
Version:
<p align="center"> <img width="160" height="160" src="https://avatars.githubusercontent.com/u/65117737?s=160&v=4" /> </p> <h1 align="center">Open Web Desktop</h1> <h3 align="center"> A modular framework for building web-based desktop experiences. </h3
256 lines (201 loc) • 6.68 kB
text/typescript
import { nanoid } from 'nanoid'
import { WindowController } from './WindowController'
import { useApplicationManager } from '../../composables/useApplicationManager'
import { useApplicationWindowsStore } from '../../stores/storeApplicationWindows'
import { useApplicationMetaStore } from '../../stores/storeApplicationMeta'
import { useTerminalManager } from '../../composables/useTerminalManager'
import { debugLog, debugError } from '../../utils/utilDebug'
import { useDesktopDefaultAppsStore } from '../../stores/storeDesktopDefaultApps'
import { useDesktopWorkspaceStore } from '../../stores/storeDesktopWorkspace'
import { reactive } from '@vue/reactivity'
export class ApplicationController implements IApplicationController {
private readonly applicationManager: IApplicationManager
private readonly terminalManager: ITerminalManager
public readonly id
public readonly config
public readonly storeWindows
public readonly storeMeta
public windows = reactive(new Map<string, IWindowController>())
public isRunning = false
constructor(id: string, config: ApplicationConfig) {
this.applicationManager = useApplicationManager()
this.terminalManager = useTerminalManager()
this.id = id
this.config = config
this.storeWindows = useApplicationWindowsStore(id)
this.storeMeta = useApplicationMetaStore(id)
}
public async initApplication(): Promise<void> {
const desktopDefaultAppsStore = useDesktopDefaultAppsStore()
// provides
// set as default app for specific purposes
// todo improve this and move it in a store
if (this.config.provides) {
const existingDefault = desktopDefaultAppsStore.getDefaultApp(
this.config.provides.name,
)
if (!existingDefault) {
desktopDefaultAppsStore.setDefaultApp(
this.config.provides.name,
this,
this.config.provides.entry,
)
debugLog(
`${this.config.title} has been set as predefined app for "${this.config.provides.name}"`,
)
}
}
// terminal
if (this.config.commands) {
for (const commandKey of Object.keys(this.config.commands)) {
this.terminalManager.addCommand({
applicationId: this.id,
name: commandKey,
})
}
}
// store
if (this.storeWindows.$persistedState) {
await this.storeWindows.$persistedState.isReady()
}
if (this.storeMeta.$persistedState) {
await this.storeMeta.$persistedState.isReady()
}
// set default meta values
// this.storeMeta.meta = this.config.meta ?? {}
// restore application state
await this.restoreApplication()
// once app is defined, always run "onReady"
if (typeof this.config.onReady === 'function') {
// todo transform in hook
this.config.onReady(this)
}
}
/**
* App always tries to restore previous windows
* and returns a boolean if it succeeded
*/
public async restoreApplication() {
if (typeof this.config.onRestore === 'function') {
// todo transform in hook
await this.config.onRestore(this)
}
if (
!this.storeWindows.windows ||
Object.keys(this.storeWindows.windows).length === 0
) {
return false
}
this.restoreWindows()
this.setRunning(true)
return true
}
private restoreWindows() {
Object.keys(this.storeWindows.windows).map((windowId) => {
const windowStore: WindowStoredState | undefined =
this.storeWindows.windows[windowId]
if (windowStore) {
this.openWindow(windowStore.model, windowStore, {
isRestoring: true,
})
}
})
debugLog('Windows have been restored', this.windows)
}
public openWindow(
model: string,
windowStoredState: WindowStoredState | undefined,
meta?: any,
) {
const desktopWorkspaceStore = useDesktopWorkspaceStore()
if (!this.config.windows || !this.config.windows.hasOwnProperty(model)) {
debugError(`Window model "${model}" not found`)
return
}
let windowId: string
if (!windowStoredState) {
windowId = `${model}-${nanoid(6)}`
const windowConfig: WindowConfig = this.config.windows[
model
] as WindowConfig
const screenHeight = window.innerHeight
const centerY = (screenHeight - Number(windowConfig.size.height)) / 2
const positionY =
windowConfig.position?.y !== undefined
? window.scrollY + windowConfig.position.y
: window.scrollY + centerY
this.storeWindows.windows[windowId] = {
model,
state: {
id: windowId,
active: true,
focused: false,
position: {
x: windowConfig.position?.x,
y: positionY,
},
createdAt: +new Date(),
workspace: desktopWorkspaceStore.active,
},
meta,
}
windowStoredState = this.storeWindows.windows[windowId]
} else {
// restore previous id if state is defined
windowId = windowStoredState.state.id
}
const windowConfig = this.config.windows[model] as WindowConfig
const windowController = new WindowController(
this,
model,
windowConfig,
windowStoredState!,
)
if (!meta?.isRestoring) {
windowController.actions.bringToFront()
}
this.windows.set(windowId, windowController)
this.setRunning(true)
return windowController
}
public closeWindow(windowId: string) {
delete this.storeWindows.windows[windowId]
this.windows.delete(windowId)
if (this.windows.size === 0) {
this.applicationManager.closeApp(this.id)
}
}
public closeAllWindows() {
this.storeWindows.windows = {}
this.windows.clear()
}
get windowsOpened() {
return this.windows
}
public getWindowById(id: string): IWindowController[] {
return this.windows.get(id)
}
public getWindowsByModel(model: string): IWindowController[] {
return Array.from(this.windows.values()).filter((w) => w.model === model)
}
public getFirstWindowByModel(model: string): IWindowController | undefined {
return Array.from(this.windows.values()).find((w) => w.model === model)
}
public setRunning(value: boolean): void {
this.isRunning = value
}
// meta
get meta() {
return this.storeMeta.meta
}
getMeta(key: string) {
return this.meta[key]
}
setMeta(key: string, value: any) {
this.meta[key] = value
}
// commands
async execCommand(command: string): Promise<CommandOutput | void> {
return this.applicationManager.execAppCommand(this.id, command)
}
}