import * as glob from 'glob' ; import { remote } from 'electron' ; import { AppContext } from './context' ; import SELECTORS from './selectors' ; import KeymapsHandler from './keymaps_handler' ; export interface Plugin { onKeymap ? : { [ keymapName ] : ( context : AppContext ) => void ; } ; onStart ? ( context ) : void ; onTweetStatus ? ( tweetElement , context ) : void ; } export default class PluginManger { public readonly plugins : { [ absolutePath ] : Plugin ; } = { } ; static create ( globPaths , ctx , keymaps ) { if ( globPaths . length === 0 ) { return Promise . resolve ( new PluginManger ( [ ] , keymaps , ctx ) ) ; } const options = { cwd : remote . app . getPath ( 'userData' ) , absolute : true , } ; return Promise . all ( globPaths . map ( gp => new Promise < string [ ] > ( ( resolve , reject ) => { glob ( gp , options , ( err , paths ) => err ? reject ( err ) : resolve ( paths ) ) ; } ) ) ) . then ( ( pathsList ) => new PluginManger ( pathsList . reduce ( ( acc , p ) => { Array . prototype . push . apply ( acc , p ) ; return acc ; } , [ ] ) , keymaps , ctx ) ) ; } constructor ( pluginPaths , private keymaps , private ctx ) { console . log ( 'Tui: Plugin manager constructed with paths:' , pluginPaths ) ; const existingTweets = ctx . timelineRoot === null ? [ ] : ctx . timelineRoot . querySelectorAll ( SELECTORS . tweet ) as NodeListOf < HTMLElement > ; for ( const p of pluginPaths ) { const plugin = this . loadPlugin ( p ) ; if ( plugin !== null ) { this . registerPlugin ( p , plugin , existingTweets ) ; } } ctx . tweetWatcher . on ( 'added' , this . onTweetAdded ) ; } registerPlugin ( pluginPath , plugin , currentTimeline ) { this . plugins [ pluginPath ] = plugin ; if ( plugin . onStart ) { try { plugin . onStart ( this . ctx ) ; } catch ( e ) { console . error ( 'Tui: onTweetStatus: Error while executing plugin:' , pluginPath , e ) ; } } for ( const tw of currentTimeline ) { this . runOnTweetHook ( pluginPath , tw as HTMLDivElement ) ; } if ( plugin . onKeymap ) { for ( const name in plugin . onKeymap ) { const callback = plugin . onKeymap [ name ] ; this . keymaps . registerCustomHandler ( name , callback ) ; } } } loadPlugin ( absolutePath ) { try { const p = require ( absolutePath ) as Plugin ; console . log ( 'Tui: Plugin loaded:' , p ) ; return p ; } catch ( e ) { console . error ( 'Tui: Failed to load a plugin from ' + absolutePath , e ) ; return null ; } } private onTweetAdded = ( tw ) => { for ( const key in this . plugins ) { this . runOnTweetHook ( key , tw ) ; } } ; private runOnTweetHook ( pluginPath , tw ) { const plugin = this . plugins [ pluginPath ] ; if ( ! plugin . onTweetStatus ) { return ; } try { plugin . onTweetStatus ( tw as HTMLDivElement , this . ctx ) ; } catch ( e ) { console . error ( 'Tui: onTweetStatus: Error while executing plugin:' , pluginPath , e ) ; } } }