import * as Types from '@meetalva/types' ; import * as Store from '../store' ; import * as React from 'react' ; import * as MobxReact from 'mobx-react' ; import * as ReactDOM from 'react-dom' ; import * as Menu from '../container/menu' ; import * as Path from 'path' ; import * as Mobx from 'mobx' ; import * as Fs from 'fs' ; import { Message } from '@meetalva/message' ; import * as Model from '@meetalva/model' ; export class BrowserHost implements Types . Host < Model . AlvaApp < Message > , Model . Project , Message > { public type = Types . HostType . Browser ; private fs ? : typeof Fs ; private container : HTMLElement ; private menuStore : Store . MenuStore ; private store : Store . ViewStore ; @ Mobx . observable private apps : Map < string , Model . AlvaApp < Message > > = new Map ( ) ; private sender ? : Types . Sender < Message > ; constructor ( init ) { this . container = document . createElement ( 'div' ) ; this . container . style . position = 'fixed' ; this . container . style . top = '100vh' ; this . container . style . zIndex = '97' ; this . menuStore = new Store . MenuStore ( [ ] ) ; this . store = init . store ; this . fs = init . fs ; } public start ( ) { document . body . appendChild ( this . container ) ; ReactDOM . render ( < MobxReact . Provider store = { this . store } menuStore = { this . menuStore } > < Menu . ContextMenu /> < /MobxReact.Provider>, this.container); } public async getFlags ( ) { return { _ : [ ] } ; } public async getPort ( ) { return 1337 ; } public async resolveFrom ( base , ... paths ) { const getBasePath = ( b ) : string => { switch ( b ) { case Types . HostBase . AppPath : case Types . HostBase . Source : return Path . resolve ( '/source' ) ; case Types . HostBase . AppData : return Path . resolve ( '/app_data' ) ; case Types . HostBase . UserData : return Path . resolve ( '/user_data' ) ; case Types . HostBase . UserHome : return Path . resolve ( '/user_home' ) ; } } ; return Path . resolve ( ... [ getBasePath ( base ) , ... paths ] ) ; } public exists ( path ) { return new Promise ( resolve => { this . fs ! . exists ( path , exists => resolve ( exists ) ) ; } ) ; } public access ( path , mode ) { return this . exists ( path ) ; } public async stat ( path ) { return new Promise ( ( resolve , reject ) => { this . fs ! . stat ( path , ( err , stats ) => { if ( err ) { return reject ( err ) ; } resolve ( stats ) ; } ) ; } ) ; } public async mkdir ( path ) { const _mkdir = ( p ) => new Promise ( ( resolve , reject ) => { this . fs ! . mkdir ( p , err => { if ( err ) { reject ( err ) ; } resolve ( ) ; } ) ; } ) ; const fragments = [ ] ; await Promise . all ( path . split ( '/' ) . map ( async ( f ) => { fragments . push ( f ) ; const p = fragments . map ( f => `${ f }/` ) . join ( '' ) ; if ( ! await this . exists ( p ) ) { await _mkdir ( p ) ; } } ) ) ; } public readFile ( path ) { return new Promise ( ( resolve , reject ) => { this . fs ! . readFile ( path , ( err , contents ) => { if ( err ) { return reject ( err ) ; } resolve ( { path , buffer : contents , contents : contents . toString ( ) } ) ; } ) ; } ) ; } public writeFile ( path , data ) { return new Promise ( ( resolve , reject ) => { this . fs ! . writeFile ( path , data , err => { if ( err ) { return reject ( err ) ; } resolve ( ) ; } ) ; } ) ; } public async log ( message ? , ... optionalParams ) { console . log ( message , ... optionalParams ) ; } public download ( name , url ) { return new Promise ( resolve => { const a = document . createElement ( 'a' ) ; a . download = name ; a . href = url ; document . body . appendChild ( a ) ; a . click ( ) ; window . requestIdleCallback ( ( ) => { document . body . removeChild ( a ) ; URL . revokeObjectURL ( url ) ; resolve ( ) ; } ) ; } ) ; } public async open ( url ) { window . open ( url , '_blank' ) ; } public async reload ( ) { window . location . reload ( ) ; } public async showMessage ( opts ) { alert ( [ opts . message , opts . detail ] . filter ( Boolean ) . join ( '\n' ) ) ; return ; } public async saveFile ( path , contents ) { const blob = new Blob ( [ contents ] , { type : 'text/plain;charset=utf-8' } ) ; const url = URL . createObjectURL ( blob ) ; await this . download ( Path . basename ( path ) , url ) ; URL . revokeObjectURL ( url ) ; } public async showContextMenu ( opts ) { opts . items . forEach ( item => this . menuStore . add ( item , { depth : 0 , active : false } ) ) ; this . menuStore . position = opts . position ; return ; } public async writeClipboard ( input ) { const clipboard = ( navigator as any ) . clipboard ; if ( ! clipboard ) { return ; } clipboard . writeText ( input ) ; } public async readClipboard ( ) { const clipboard = ( navigator as any ) . clipboard ; if ( ! clipboard ) { return ; } return clipboard . readText ( ) ; } public async createWindow ( options ) { window . open ( options . address , '_blank' ) ; return ; } public async selectFile ( ) { return ; } public async selectSaveFile ( ) { return ; } public async toggleDevTools ( ) { return ; } @ Mobx . action public async addApp ( app ) { this . apps . set ( app . getId ( ) , app ) ; return ; } public async getApp ( id ) { return this . apps . get ( id ) ; } public async getSender ( ) { return this . sender ! ; } public setSender ( sender ) { this . sender = sender ; } }