Skip to content
Snippets Groups Projects
main.js 10.4 KiB
Newer Older
const fs = require('fs');
const {spawn} = require('child_process');

azubieta's avatar
azubieta committed
const {app, BrowserWindow, BrowserView, ipcMain} = require('electron');
const ElectronStore = require('electron-store');
const request = require('request');

const appPackage = require('../package.json');
const appConfig = require('./configs/application.json');
const ocsManagerConfig = require('./configs/ocs-manager.json');

const isDebugMode = process.argv.includes('--debug');
const previewpicDirectory = `${app.getPath('userData')}/previewpic`;
azubieta's avatar
azubieta committed
const windowIcon = `${__dirname}/images/app-icons/ocs-store.png`;
const windowIndexFileUrl = `file://${__dirname}/index.html`;
const viewSessionPartition = 'persist:opendesktop';
const viewPreloadScript = `${__dirname}/scripts/renderers/browser-view.js`;
const appConfigStoreStorage = 'application';

let mainWindow = null;
azubieta's avatar
azubieta committed
let mainView = null;
let ocsManager = null;
let ocsManagerUrl = '';

async function startOcsManager() {
    return new Promise((resolve) => {
        const resolveOcsManagerUrl = (data) => {
            const matches = data.toString().match(/Websocket server started at: "(wss?:\/\/.+)"/);
            if (matches) {
                ocsManagerUrl = matches[1];
                resolve(true);
            }
        };

        ocsManager = spawn(ocsManagerConfig.bin, ['-p', ocsManagerConfig.port]);

        ocsManager.stdout.on('data', (data) => {
            console.log(`[${ocsManagerConfig.bin}] ${data}`);
            if (!ocsManagerUrl) {
                resolveOcsManagerUrl(data);
            }
        });

        ocsManager.stderr.on('data', (data) => {
            console.error(`[${ocsManagerConfig.bin}] ${data}`);
            if (!ocsManagerUrl) {
                resolveOcsManagerUrl(data);
            }
        });

        ocsManager.on('close', (code) => {
            console.log(`${ocsManagerConfig.bin} exited with code ${code}`);
        });

        ocsManager.on('error', () => {
            console.error(`Failed to start ${ocsManagerConfig.bin}`);
            resolve(false);
        });
    });
}

function stopOcsManager() {
    if (ocsManager) {
        ocsManager.kill();
azubieta's avatar
azubieta committed
        ocsManager = null;
        ocsManagerUrl = '';
    }
}

function createWindow() {
    const appConfigStore = new ElectronStore({
        name: appConfigStoreStorage,
        defaults: appConfig.defaults
    });

    const windowBounds = appConfigStore.get('windowBounds');

    mainWindow = new BrowserWindow({
        title: appPackage.productName,
        icon: windowIcon,
azubieta's avatar
azubieta committed
        x: windowBounds.x,
        y: windowBounds.y,
        width: windowBounds.width,
        height: windowBounds.height,
        webPreferences: {
            nodeIntegration: true
        }
    });

    mainWindow.on('close', () => {
        const appConfigStore = new ElectronStore({name: appConfigStoreStorage});
        appConfigStore.set('windowBounds', mainWindow.getBounds());
    });

    mainWindow.on('closed', () => {
        mainWindow = null;
azubieta's avatar
azubieta committed
        if (mainView) {
            mainView.destroy();
            mainView = null;
        }
    });

    if (isDebugMode) {
azubieta's avatar
azubieta committed
        mainWindow.webContents.openDevTools({mode: 'detach'});
azubieta's avatar
azubieta committed
    } else {
azubieta's avatar
azubieta committed
        mainWindow.setMenu(null);
azubieta's avatar
azubieta committed

    mainWindow.loadURL(windowIndexFileUrl);
    mainWindow.maximize();
azubieta's avatar
azubieta committed
    createView();
}

function createView() {
    const detectOcsApiInfo = (url) => {
        // Detect provider key and content id from page url
        // https://www.opendesktop.org/s/Gnome/p/123456789/?key=val#hash
        //
        // providerKey = https://www.opendesktop.org/ocs/v1/
        // contentId = 123456789
        const info = {
            providerKey: '',
            contentId: ''
        };
        const matches = url.match(/(https?:\/\/[^/]+).*\/p\/([^/?#]+)/);
        if (matches) {
            info.providerKey = `${matches[1]}/ocs/v1/`;
            info.contentId = matches[2];
        }
        return info;
    };

    mainView = new BrowserView({
        webPreferences: {
            nodeIntegration: false,
            partition: viewSessionPartition,
            preload: viewPreloadScript
        }
    });

    mainWindow.setBrowserView(mainView);

    const windowBounds = mainWindow.getBounds();
    mainView.setBounds({
        x: 0,
azubieta's avatar
azubieta committed
        y: 40,
azubieta's avatar
azubieta committed
        width: windowBounds.width,
        height: windowBounds.height
    });

    mainView.setAutoResize({
        width: true,
        height: true
    });

    mainView.webContents.on('did-start-loading', () => {
        mainWindow.webContents.send('browserView_loading', {isLoading: true});
    });

    mainView.webContents.on('did-stop-loading', () => {
        mainWindow.webContents.send('browserView_loading', {isLoading: false});
    });

    mainView.webContents.on('dom-ready', () => {
        mainWindow.webContents.send('browserView_page', {
            url: mainView.webContents.getURL(),
            title: mainView.webContents.getTitle(),
            canGoBack: mainView.webContents.canGoBack(),
            canGoForward: mainView.webContents.canGoForward()
        });

        if (isDebugMode) {
            mainView.webContents.openDevTools({mode: 'detach'});
        }

        mainView.webContents.send('ipcMessage');
    });

    mainView.webContents.on('new-window', (event, url) => {
        if (url.startsWith('http://') || url.startsWith('https://')) {
            event.preventDefault();
            mainWindow.webContents.send('ocsManager_openUrl', {url: url});
        }
    });

    mainView.webContents.on('will-navigate', (event, url) => {
        if (url.startsWith('ocs://') || url.startsWith('ocss://')) {
            event.preventDefault();
            const info = detectOcsApiInfo(mainView.webContents.getURL());
            mainWindow.webContents.send('ocsManager_getItemByOcsUrl', {url: url, ...info});
        }
    });

    ipcMain.on('browserView_loadUrl', (event, url) => {
        mainView.webContents.loadURL(url);
        event.returnValue = undefined;
    });

    ipcMain.on('browserView_getUrl', (event) => {
        event.returnValue = mainView.webContents.getURL();
    });

    ipcMain.on('browserView_getTitle', (event) => {
        event.returnValue = mainView.webContents.getTitle();
    });

    ipcMain.on('browserView_goBack', (event) => {
        mainView.webContents.goBack();
        event.returnValue = undefined;
    });

    ipcMain.on('browserView_goForward', (event) => {
        mainView.webContents.goForward();
        event.returnValue = undefined;
    });

    ipcMain.on('browserView_reload', (event) => {
        mainView.webContents.reload();
        event.returnValue = undefined;
    });

    ipcMain.on('browserView_stop', (event) => {
        mainView.webContents.stop();
        event.returnValue = undefined;
    });

    //ipcMain.on('ipcMessage', (event) => {});

    const appConfigStore = new ElectronStore({name: appConfigStoreStorage});
    const startPage = appConfigStore.get('startPage');

    mainView.webContents.loadURL(startPage);
}

function isFile(path) {
    try {
        const stats = fs.statSync(path);
        return stats.isFile();
azubieta's avatar
azubieta committed
    } catch (error) {
        console.error(error);
        return false;
    }
}

function isDirectory(path) {
    try {
        const stats = fs.statSync(path);
        return stats.isDirectory();
azubieta's avatar
azubieta committed
    } catch (error) {
        console.error(error);
        return false;
    }
}

function btoa(string) {
    const buffer = (string instanceof Buffer) ? string : Buffer.from(string.toString(), 'binary');
    return buffer.toString('base64');
}

//function atob(string) {
//    return Buffer.from(string, 'base64').toString('binary');
//}

function previewpicFilename(itemKey) {
    // "itemKey" will be URL to product file
    return btoa(itemKey).slice(-255);
}

azubieta's avatar
azubieta committed
function downloadPreviewpic(itemKey) {
    const selector = 'meta[property="og:image"]';
    mainView.webContents.executeJavaScript(
        `document.querySelector('${selector}').content`,
        false,
        (result) => {
            const previewpicUrl = result || '';
            if (previewpicUrl) {
                if (!isDirectory(previewpicDirectory)) {
                    fs.mkdirSync(previewpicDirectory);
                }
                const path = `${previewpicDirectory}/${previewpicFilename(itemKey)}`;
                request.get(previewpicUrl).on('error', (error) => {
                    console.error(error);
                }).pipe(fs.createWriteStream(path));
            }
        }
    );
}

function removePreviewpic(itemKey) {
    const path = `${previewpicDirectory}/${previewpicFilename(itemKey)}`;
    if (isFile(path)) {
        fs.unlinkSync(path);
    }
}

app.on('ready', async () => {
    if (await startOcsManager()) {
        createWindow();
azubieta's avatar
azubieta committed
    } else {
        app.quit();
    }
});

app.on('quit', () => {
    stopOcsManager();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    if (mainWindow === null) {
        createWindow();
    }
});

azubieta's avatar
azubieta committed
/*app.on('web-contents-created', (event, webContents) => {
    if (webContents.getType() === 'webview') {
        webContents.on('will-navigate', (event, url) => {
            if (url.startsWith('ocs://') || url.startsWith('ocss://')) {
                // Cancel ocs protocol navigation
                event.preventDefault();
            }
        });
    }
azubieta's avatar
azubieta committed
});*/

ipcMain.on('app', (event, key) => {
    const data = {
        package: appPackage,
        config: appConfig,
        isDebugMode: isDebugMode
    };
    event.returnValue = key ? data[key] : data;
});

ipcMain.on('ocs-manager', (event, key) => {
    const data = {
        config: ocsManagerConfig,
        url: ocsManagerUrl
    };
    event.returnValue = key ? data[key] : data;
});

ipcMain.on('store', (event, key, value) => {
    const appConfigStore = new ElectronStore({name: appConfigStoreStorage});
    if (key && value) {
        appConfigStore.set(key, value);
    }
    event.returnValue = key ? appConfigStore.get(key) : appConfigStore.store;
});

ipcMain.on('previewpic', (event, kind, itemKey, url) => {
    if (kind === 'directory') {
        event.returnValue = previewpicDirectory;
azubieta's avatar
azubieta committed
    } else if (kind === 'path' && itemKey) {
        event.returnValue = `${previewpicDirectory}/${previewpicFilename(itemKey)}`;
azubieta's avatar
azubieta committed
    } else if (kind === 'download' && itemKey && url) {
        downloadPreviewpic(itemKey, url);
        event.returnValue = undefined;
azubieta's avatar
azubieta committed
    } else if (kind === 'remove' && itemKey) {
        removePreviewpic(itemKey);
        event.returnValue = undefined;
azubieta's avatar
azubieta committed
    } else {
        event.returnValue = false;
    }
});