import { ipcRenderer , remote } from 'electron' ; import log from 'electron-log' ; import { existsSync , lstatSync , readdirSync , readFileSync } from 'fs' ; import moment from 'moment' ; import os from 'os' ; import { join } from 'path' ; import React from 'react' ; import { IPCMessageNames } from '../../ipc/IPCMessageNames' ; import { IInstalledPlugin , IInstallNeededPlugin , IPluginProperties } from '../plugin/IPlugin' ; import { pluginInstalledLockFileName } from './constants' ; const settings = remote . require ( 'electron-settings' ) ; interface IWithSettingsProps { settingsKey : string ; } export interface IManipulateSettingsProps < TSettings = { } > { updateSettings : ( data : TSettings ) => void ; } export const withSettings = < Q extends object , P extends IManipulateSettingsProps < Q > > ( Comp , compSettings ? ) => { return class WithSettings extends React . Component < P & IWithSettingsProps , any > { private settingsObserver : any ; constructor ( props ) { super ( props ) ; this . updateSettings = this . updateSettings . bind ( this ) ; this . onKeyChanged = this . onKeyChanged . bind ( this ) ; } public componentDidMount ( ) { this . settingsObserver = settings . watch ( this . props . settingsKey , this . onKeyChanged ) ; } public componentWillUnmount ( ) { if ( this . settingsObserver ) { this . settingsObserver . dispose ( ) ; } } public render ( ) { const { settingsKey , ... props } = this . props as IWithSettingsProps ; const data = { ... props , ... ( compSettings || settings . get ( settingsKey ) ) } ; return < Comp updateSettings = { this . updateSettings } { ... data } />; } private updateSettings ( data ) { settings . set ( this . props . settingsKey , data , { prettify : true } ) ; log . verbose ( `updating settings: ${ this . props . settingsKey } = ${ JSON . stringify ( data ) }` ) ; } private onKeyChanged ( newVal , oldVal ) { this . forceUpdate ( ) ; } } ; } ; export const getPlugins = ( ) => { const rootSettings = settings . getAll ( ) ; const internalPlugins = loadPlugins ( `${ __dirname }/../plugin` , rootSettings ) ; const userPlugins = loadPlugins ( join ( os . homedir ( ) , '.overlayed' ) , rootSettings ) ; return internalPlugins . concat ( userPlugins ) ; } ; const isDirectory = ( source ) => lstatSync ( source ) . isDirectory ( ) ; const getDirectories = ( source ) => readdirSync ( source ) . map ( ( name ) => join ( source , name ) ) . filter ( isDirectory ) ; const loadPlugins = ( dir , rootSettings ) => { if ( ! existsSync ( dir ) ) { return [ ] ; } return getDirectories ( dir ) . map ( ( pluginDir ) => { const jsonData = readFileSync ( join ( pluginDir , 'package.json' ) ) . toString ( ) ; const pkg = JSON . parse ( jsonData ) ; const componentPath = join ( pluginDir , pkg . main as string ) ; let needsInstall = pkg . dependencies && Object . keys ( pkg . dependencies ) . filter ( k => k . toLowerCase ( ) !== 'react' ) . length > 0 ? true : false ; const installLockFilePath = join ( pluginDir , pluginInstalledLockFileName ) ; if ( existsSync ( installLockFilePath ) ) { const installLockFile = readFileSync ( installLockFilePath ) . toString ( ) ; if ( installLockFile ) { const installDate = moment ( installLockFile ) ; if ( installDate . add ( 1 , 'day' ) . isBefore ( moment ( ) ) ) { needsInstall = true ; } else { needsInstall = false ; } } } const plugin = { diskPath : pluginDir , name : pkg . name , requiresInstall : needsInstall , settings : rootSettings [ pkg . name ] || { } , version : pkg . version , } ; if ( plugin . requiresInstall ) { return { ... plugin , ... { component : componentPath } } as IInstallNeededPlugin ; } else { try { return { ... plugin , ... { component : require ( componentPath ) . default as React . ComponentType < any > } } as IInstalledPlugin ; } catch ( ex ) { const errorContents = `Failed to load ${ plugin . name } from ${ plugin . diskPath }` ; ipcRenderer . send ( IPCMessageNames . ShowTooltip , { content : errorContents , title : 'Plugin Load Error' , } ) ; log . error ( errorContents ) ; return plugin ; } } } ) ; } ;