2021-01-12 01:33:10 +02:00
import ViewController , { EmitMessageEvent } from './ViewController' ;
2020-11-05 18:58:23 +02:00
import shim from '../../shim' ;
2021-02-07 12:09:28 +02:00
import { ButtonSpec , DialogResult , ViewHandle } from './api/types' ;
2020-11-05 18:58:23 +02:00
const { toSystemSlashes } = require ( '../../path-utils' ) ;
2021-11-09 17:50:50 +02:00
import PostMessageService , { MessageParticipant } from '../PostMessageService' ;
2020-10-09 19:35:46 +02:00
export enum ContainerType {
Panel = 'panel' ,
Dialog = 'dialog' ,
}
export interface Options {
2020-11-12 21:29:22 +02:00
containerType : ContainerType ;
2020-10-09 19:35:46 +02:00
}
interface CloseResponse {
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2020-10-09 19:35:46 +02:00
resolve : Function ;
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2020-10-09 19:35:46 +02:00
reject : Function ;
}
2021-01-02 15:32:15 +02:00
// TODO: Copied from:
// packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.ts
function findItemByKey ( layout : any , key : string ) : any {
if ( ! layout ) throw new Error ( 'Layout cannot be null' ) ;
function recurseFind ( item : any ) : any {
if ( item . key === key ) return item ;
if ( item . children ) {
for ( const child of item . children ) {
const found = recurseFind ( child ) ;
if ( found ) return found ;
}
}
return null ;
}
return recurseFind ( layout ) ;
}
2020-10-09 19:35:46 +02:00
export default class WebviewController extends ViewController {
2020-11-12 21:13:28 +02:00
private baseDir_ : string ;
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2020-11-12 21:13:28 +02:00
private messageListener_ : Function = null ;
private closeResponse_ : CloseResponse = null ;
2020-10-09 19:35:46 +02:00
2024-03-09 13:03:57 +02:00
// True if a **panel** is shown in a modal window.
private panelInModalMode_ = false ;
2021-02-07 12:09:28 +02:00
public constructor ( handle : ViewHandle , pluginId : string , store : any , baseDir : string , containerType : ContainerType ) {
super ( handle , pluginId , store ) ;
2020-10-09 19:35:46 +02:00
this . baseDir_ = toSystemSlashes ( baseDir , 'linux' ) ;
this . store . dispatch ( {
type : 'PLUGIN_VIEW_ADD' ,
pluginId : pluginId ,
view : {
id : this.handle ,
type : this . type ,
2020-11-13 19:09:28 +02:00
containerType : containerType ,
2020-10-09 19:35:46 +02:00
html : '' ,
scripts : [ ] ,
opened : false ,
buttons : null ,
2021-08-18 13:09:45 +02:00
fitToContent : true ,
2020-10-09 19:35:46 +02:00
} ,
} ) ;
}
2020-11-12 21:13:28 +02:00
public get type ( ) : string {
2020-10-09 19:35:46 +02:00
return 'webview' ;
}
2020-11-12 21:13:28 +02:00
private setStoreProp ( name : string , value : any ) {
2020-10-09 19:35:46 +02:00
this . store . dispatch ( {
type : 'PLUGIN_VIEW_PROP_SET' ,
pluginId : this.pluginId ,
id : this.handle ,
name : name ,
value : value ,
} ) ;
}
2020-11-12 21:13:28 +02:00
public get html ( ) : string {
2020-10-09 19:35:46 +02:00
return this . storeView . html ;
}
2020-11-12 21:13:28 +02:00
public set html ( html : string ) {
2020-10-09 19:35:46 +02:00
this . setStoreProp ( 'html' , html ) ;
}
2020-11-12 21:13:28 +02:00
public get containerType ( ) : ContainerType {
2020-10-09 19:35:46 +02:00
return this . storeView . containerType ;
}
2020-11-12 21:13:28 +02:00
public async addScript ( path : string ) {
2020-10-09 19:35:46 +02:00
const fullPath = toSystemSlashes ( shim . fsDriver ( ) . resolve ( ` ${ this . baseDir_ } / ${ path } ` ) , 'linux' ) ;
if ( fullPath . indexOf ( this . baseDir_ ) !== 0 ) throw new Error ( ` Script appears to be outside of plugin base directory: ${ fullPath } (Base dir: ${ this . baseDir_ } ) ` ) ;
this . store . dispatch ( {
type : 'PLUGIN_VIEW_PROP_PUSH' ,
pluginId : this.pluginId ,
id : this.handle ,
name : 'scripts' ,
value : fullPath ,
} ) ;
}
2021-11-09 17:50:50 +02:00
public postMessage ( message : any ) {
const messageId = ` plugin_ ${ Date . now ( ) } ${ Math . random ( ) } ` ;
void PostMessageService . instance ( ) . postMessage ( {
pluginId : this.pluginId ,
viewId : this.handle ,
contentScriptId : null ,
from : MessageParticipant . Plugin ,
to : MessageParticipant.UserWebview ,
id : messageId ,
content : message ,
} ) ;
}
2021-01-12 01:33:10 +02:00
public async emitMessage ( event : EmitMessageEvent ) : Promise < any > {
2021-11-09 17:50:50 +02:00
2020-10-09 19:35:46 +02:00
if ( ! this . messageListener_ ) return ;
2021-01-12 01:33:10 +02:00
return this . messageListener_ ( event . message ) ;
2020-10-09 19:35:46 +02:00
}
2020-11-12 21:13:28 +02:00
public onMessage ( callback : any ) {
2020-10-09 19:35:46 +02:00
this . messageListener_ = callback ;
}
2021-01-02 15:32:15 +02:00
// ---------------------------------------------
// Specific to panels
// ---------------------------------------------
2023-06-30 10:11:26 +02:00
public async show ( show = true ) : Promise < void > {
2021-01-02 15:32:15 +02:00
this . store . dispatch ( {
type : 'MAIN_LAYOUT_SET_ITEM_PROP' ,
itemKey : this.handle ,
propName : 'visible' ,
propValue : show ,
} ) ;
}
public async hide ( ) : Promise < void > {
return this . show ( false ) ;
}
2024-03-09 13:03:57 +02:00
// This method allows us to determine whether a panel is shown in dialog mode,
// which is used on mobile.
public setIsShownInModal ( shown : boolean ) {
this . panelInModalMode_ = shown ;
}
2021-01-02 15:32:15 +02:00
public get visible ( ) : boolean {
2024-03-09 13:03:57 +02:00
const appState = this . store . getState ( ) ;
if ( this . panelInModalMode_ ) {
return true ;
}
const mainLayout = appState . mainLayout ;
// Mobile: There is no appState.mainLayout
if ( ! mainLayout ) {
return false ;
}
2021-01-02 15:32:15 +02:00
const item = findItemByKey ( mainLayout , this . handle ) ;
return item ? item.visible : false ;
}
2020-10-09 19:35:46 +02:00
// ---------------------------------------------
// Specific to dialogs
// ---------------------------------------------
2020-11-13 20:48:42 +02:00
public async open ( ) : Promise < DialogResult > {
2021-02-07 12:09:28 +02:00
this . store . dispatch ( {
type : 'VISIBLE_DIALOGS_ADD' ,
name : this.handle ,
} ) ;
2020-10-09 19:35:46 +02:00
this . setStoreProp ( 'opened' , true ) ;
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2020-11-12 21:13:28 +02:00
return new Promise ( ( resolve : Function , reject : Function ) = > {
2020-10-09 19:35:46 +02:00
this . closeResponse_ = { resolve , reject } ;
} ) ;
}
2020-11-25 16:40:25 +02:00
public close() {
2021-02-07 12:09:28 +02:00
this . store . dispatch ( {
type : 'VISIBLE_DIALOGS_REMOVE' ,
name : this.handle ,
} ) ;
2020-10-09 19:35:46 +02:00
this . setStoreProp ( 'opened' , false ) ;
}
2020-11-13 20:48:42 +02:00
public closeWithResponse ( result : DialogResult ) {
2020-10-09 19:35:46 +02:00
this . close ( ) ;
this . closeResponse_ . resolve ( result ) ;
}
2020-11-12 21:13:28 +02:00
public get buttons ( ) : ButtonSpec [ ] {
2020-10-09 19:35:46 +02:00
return this . storeView . buttons ;
}
2020-11-12 21:13:28 +02:00
public set buttons ( buttons : ButtonSpec [ ] ) {
2020-10-09 19:35:46 +02:00
this . setStoreProp ( 'buttons' , buttons ) ;
}
2021-08-18 13:09:45 +02:00
public get fitToContent ( ) : boolean {
return this . storeView . fitToContent ;
}
public set fitToContent ( fitToContent : boolean ) {
this . setStoreProp ( 'fitToContent' , fitToContent ) ;
}
2020-10-09 19:35:46 +02:00
}