"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const fs_1 = require("fs");
const electron_1 = require("electron");
const electron_better_ipc_1 = require("electron-better-ipc");
const electron_updater_1 = require("electron-updater");
const electronDl = require("electron-dl");
const electronContextMenu = require("electron-context-menu");
const electronLocalshortcut = require("electron-localshortcut");
const electronDebug = require("electron-debug");
const electron_util_1 = require("electron-util");
const facebook_locales_1 = require("facebook-locales");
const doNotDisturb = require("@sindresorhus/do-not-disturb");
const menu_1 = require("./menu");
const config_1 = require("./config");
const tray_1 = require("./tray");
const util_1 = require("./util");
const emoji_1 = require("./emoji");
const ensure_online_1 = require("./ensure-online");
const menu_bar_mode_1 = require("./menu-bar-mode");
const constants_1 = require("./constants");
electron_better_ipc_1.ipcMain.setMaxListeners(100);
electronDebug({
    isEnabled: true,
    showDevTools: false
});
electronDl();
electronContextMenu({
    prepend: defaultActions => {
        /*
        TODO: Use menu option or use replacement of options (https://github.com/sindresorhus/electron-context-menu/issues/70)
        See explanation for this hacky solution here: https://github.com/sindresorhus/caprine/pull/1169
        */
        defaultActions.copyLink({
            transform: util_1.stripTrackingFromUrl
        });
        return [];
    }
});
electron_1.app.setAppUserModelId('com.sindresorhus.caprine');
if (!config_1.default.get('hardwareAcceleration')) {
    electron_1.app.disableHardwareAcceleration();
}
if (!electron_util_1.is.development && !electron_util_1.is.linux) {
    (async () => {
        const FOUR_HOURS = 1000 * 60 * 60 * 4;
        setInterval(async () => {
            await electron_updater_1.autoUpdater.checkForUpdates();
        }, FOUR_HOURS);
        await electron_updater_1.autoUpdater.checkForUpdates();
    })();
}
let mainWindow;
let isQuitting = false;
let previousMessageCount = 0;
let dockMenu;
let isDNDEnabled = false;
if (!electron_1.app.requestSingleInstanceLock()) {
    electron_1.app.quit();
}
electron_1.app.on('second-instance', () => {
    if (mainWindow) {
        if (mainWindow.isMinimized()) {
            mainWindow.restore();
        }
        mainWindow.show();
    }
});
// Preserves the window position when a display is removed and Caprine is moved to a different screen.
electron_1.app.on('ready', () => {
    electron_1.screen.on('display-removed', () => {
        const [x, y] = mainWindow.getPosition();
        mainWindow.setPosition(x, y);
    });
});
function getMessageCount(conversations) {
    return conversations.filter(({ unread }) => unread).length;
}
async function updateBadge(conversations) {
    // Ignore `Sindre messaged you` blinking
    if (!Array.isArray(conversations)) {
        return;
    }
    const messageCount = getMessageCount(conversations);
    if (electron_util_1.is.macos || electron_util_1.is.linux) {
        if (config_1.default.get('showUnreadBadge') && !isDNDEnabled) {
            electron_1.app.badgeCount = messageCount;
        }
        if (electron_util_1.is.macos &&
            !isDNDEnabled &&
            config_1.default.get('bounceDockOnMessage') &&
            previousMessageCount !== messageCount) {
            electron_1.app.dock.bounce('informational');
            previousMessageCount = messageCount;
        }
    }
    if (electron_util_1.is.linux || electron_util_1.is.windows) {
        if (config_1.default.get('showUnreadBadge')) {
            tray_1.default.setBadge(messageCount > 0);
        }
        if (config_1.default.get('flashWindowOnMessage')) {
            mainWindow.flashFrame(messageCount !== 0);
        }
    }
    tray_1.default.update(messageCount);
    if (electron_util_1.is.windows) {
        if (!config_1.default.get('showUnreadBadge') || messageCount === 0) {
            mainWindow.setOverlayIcon(null, '');
        }
        else {
            // Delegate drawing of overlay icon to renderer process
            updateOverlayIcon(await electron_better_ipc_1.ipcMain.callRenderer(mainWindow, 'render-overlay-icon', messageCount));
        }
    }
}
function updateOverlayIcon({ data, text }) {
    const img = electron_1.nativeImage.createFromDataURL(data);
    mainWindow.setOverlayIcon(img, text);
}
function updateTrayIcon() {
    if (!config_1.default.get('showTrayIcon') || config_1.default.get('quitOnWindowClose')) {
        tray_1.default.destroy();
    }
    else {
        tray_1.default.create(mainWindow);
    }
}
electron_better_ipc_1.ipcMain.answerRenderer('update-tray-icon', updateTrayIcon);
function enableHiresResources() {
    const scaleFactor = Math.max(...electron_1.screen.getAllDisplays().map(display => display.scaleFactor));
    if (scaleFactor === 1) {
        return;
    }
    const filter = { urls: [`*://*.${util_1.messengerDomain}/`] };
    electron_1.session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
        let cookie = details.requestHeaders.Cookie;
        if (cookie && details.method === 'GET') {
            cookie = /(?:; )?dpr=\d/.test(cookie) ? cookie.replace(/dpr=\d/, `dpr=${scaleFactor}`) : `${cookie}; dpr=${scaleFactor}`;
            details.requestHeaders.Cookie = cookie;
        }
        callback({
            cancel: false,
            requestHeaders: details.requestHeaders
        });
    });
}
function initRequestsFiltering() {
    const filter = {
        urls: [
            `*://*.${util_1.messengerDomain}/*typ.php*`,
            `*://*.${util_1.messengerDomain}/*change_read_status.php*`,
            `*://*.${util_1.messengerDomain}/*delivery_receipts*`,
            `*://*.${util_1.messengerDomain}/*unread_threads*`,
            '*://*.fbcdn.net/images/emoji.php/v9/*',
            '*://*.facebook.com/images/emoji.php/v9/*' // Emoji
        ]
    };
    electron_1.session.defaultSession.webRequest.onBeforeRequest(filter, async ({ url }, callback) => {
        if (url.includes('emoji.php')) {
            callback(await emoji_1.process(url));
        }
        else if (url.includes('typ.php')) {
            callback({ cancel: config_1.default.get('block.typingIndicator') });
        }
        else if (url.includes('change_read_status.php')) {
            callback({ cancel: config_1.default.get('block.chatSeen') });
        }
        else if (url.includes('delivery_receipts') || url.includes('unread_threads')) {
            callback({ cancel: config_1.default.get('block.deliveryReceipt') });
        }
    });
    electron_1.session.defaultSession.webRequest.onHeadersReceived({
        urls: ['*://static.xx.fbcdn.net/rsrc.php/*']
    }, ({ responseHeaders }, callback) => {
        if (!config_1.default.get('callRingtoneMuted') || !responseHeaders) {
            callback({});
            return;
        }
        const callRingtoneHash = '2NAu/QVqg211BbktgY5GkA==';
        callback({
            cancel: responseHeaders['content-md5'][0] === callRingtoneHash
        });
    });
}
function setUserLocale() {
    const userLocale = facebook_locales_1.bestFacebookLocaleFor(electron_1.app.getLocale().replace('-', '_'));
    const cookie = {
        url: 'https://www.messenger.com/',
        name: 'locale',
        secure: true,
        value: userLocale
    };
    electron_1.session.defaultSession.cookies.set(cookie);
}
function setNotificationsMute(status) {
    const label = 'Mute Notifications';
    const muteMenuItem = electron_1.Menu.getApplicationMenu().getMenuItemById('mute-notifications');
    config_1.default.set('notificationsMuted', status);
    muteMenuItem.checked = status;
    if (electron_util_1.is.macos) {
        const item = dockMenu.items.find(x => x.label === label);
        item.checked = status;
    }
}
function createMainWindow() {
    const lastWindowState = config_1.default.get('lastWindowState');
    const isDarkMode = config_1.default.get('darkMode');
    // Messenger or Work Chat
    const mainURL = config_1.default.get('useWorkChat') ?
        'https://work.facebook.com/chat' :
        'https://www.messenger.com/login/';
    const win = new electron_1.BrowserWindow({
        title: electron_1.app.name,
        show: false,
        x: lastWindowState.x,
        y: lastWindowState.y,
        width: lastWindowState.width,
        height: lastWindowState.height,
        icon: electron_util_1.is.linux ? constants_1.caprineIconPath : undefined,
        minWidth: 400,
        minHeight: 200,
        alwaysOnTop: config_1.default.get('alwaysOnTop'),
        titleBarStyle: 'hiddenInset',
        autoHideMenuBar: config_1.default.get('autoHideMenuBar'),
        darkTheme: isDarkMode,
        webPreferences: {
            preload: path.join(__dirname, 'browser.js'),
            nativeWindowOpen: true,
            contextIsolation: true,
            spellcheck: config_1.default.get('isSpellCheckerEnabled'),
            plugins: true,
            enableRemoteModule: true
        }
    });
    setUserLocale();
    initRequestsFiltering();
    let previousDarkMode = electron_util_1.darkMode.isEnabled;
    electron_util_1.darkMode.onChange(() => {
        if (electron_util_1.darkMode.isEnabled !== previousDarkMode) {
            previousDarkMode = electron_util_1.darkMode.isEnabled;
            win.webContents.send('set-dark-mode');
        }
    });
    if (electron_util_1.is.macos) {
        win.setSheetOffset(40);
    }
    win.loadURL(mainURL);
    win.on('close', event => {
        if (config_1.default.get('quitOnWindowClose')) {
            electron_1.app.quit();
            return;
        }
        // Workaround for https://github.com/electron/electron/issues/20263
        // Closing the app window when on full screen leaves a black screen
        // Exit fullscreen before closing
        if (electron_util_1.is.macos && mainWindow.isFullScreen()) {
            mainWindow.once('leave-full-screen', () => {
                mainWindow.hide();
            });
            mainWindow.setFullScreen(false);
        }
        if (!isQuitting) {
            event.preventDefault();
            // Workaround for https://github.com/electron/electron/issues/10023
            win.blur();
            if (electron_util_1.is.macos) {
                // On macOS we're using `app.hide()` in order to focus the previous window correctly
                electron_1.app.hide();
            }
            else {
                win.hide();
            }
        }
    });
    win.on('focus', () => {
        if (config_1.default.get('flashWindowOnMessage')) {
            // This is a security in the case where messageCount is not reset by page title update
            win.flashFrame(false);
        }
    });
    win.on('resize', () => {
        const { isMaximized } = config_1.default.get('lastWindowState');
        config_1.default.set('lastWindowState', { ...win.getNormalBounds(), isMaximized });
    });
    win.on('maximize', () => {
        config_1.default.set('lastWindowState.isMaximized', true);
    });
    win.on('unmaximize', () => {
        config_1.default.set('lastWindowState.isMaximized', false);
    });
    return win;
}
(async () => {
    await Promise.all([ensure_online_1.default(), electron_1.app.whenReady()]);
    await menu_1.default();
    mainWindow = createMainWindow();
    // Workaround for https://github.com/electron/electron/issues/5256
    electronLocalshortcut.register(mainWindow, 'CommandOrControl+=', () => {
        util_1.sendAction('zoom-in');
    });
    // Start in menu bar mode if enabled, otherwise start normally
    menu_bar_mode_1.setUpMenuBarMode(mainWindow);
    if (electron_util_1.is.macos) {
        const firstItem = {
            label: 'Mute Notifications',
            type: 'checkbox',
            checked: config_1.default.get('notificationsMuted'),
            async click() {
                setNotificationsMute(await electron_better_ipc_1.ipcMain.callRenderer(mainWindow, 'toggle-mute-notifications'));
            }
        };
        dockMenu = electron_1.Menu.buildFromTemplate([firstItem]);
        electron_1.app.dock.setMenu(dockMenu);
        // Dock icon is hidden initially on macOS
        if (config_1.default.get('showDockIcon')) {
            electron_1.app.dock.show();
        }
        electron_better_ipc_1.ipcMain.once('conversations', () => {
            // Messenger sorts the conversations by unread state.
            // We select the first conversation from the list.
            util_1.sendAction('jump-to-conversation', 1);
        });
        electron_better_ipc_1.ipcMain.answerRenderer('conversations', (conversations) => {
            if (conversations.length === 0) {
                return;
            }
            const items = conversations.map(({ label, icon }, index) => {
                return {
                    label: `${label}`,
                    icon: electron_1.nativeImage.createFromDataURL(icon),
                    click: () => {
                        mainWindow.show();
                        util_1.sendAction('jump-to-conversation', index + 1);
                    }
                };
            });
            electron_1.app.dock.setMenu(electron_1.Menu.buildFromTemplate([firstItem, { type: 'separator' }, ...items]));
        });
    }
    // Update badge on conversations change
    electron_better_ipc_1.ipcMain.answerRenderer('conversations', async (conversations) => {
        updateBadge(conversations);
    });
    enableHiresResources();
    const { webContents } = mainWindow;
    webContents.on('dom-ready', async () => {
        await menu_1.default();
        webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'browser.css'), 'utf8'));
        webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'dark-mode.css'), 'utf8'));
        webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'vibrancy.css'), 'utf8'));
        webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'code-blocks.css'), 'utf8'));
        webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'autoplay.css'), 'utf8'));
        if (config_1.default.get('useWorkChat')) {
            webContents.insertCSS(fs_1.readFileSync(path.join(__dirname, '..', 'css', 'workchat.css'), 'utf8'));
        }
        if (fs_1.existsSync(path.join(electron_1.app.getPath('userData'), 'custom.css'))) {
            webContents.insertCSS(fs_1.readFileSync(path.join(electron_1.app.getPath('userData'), 'custom.css'), 'utf8'));
        }
        if (config_1.default.get('launchMinimized') || electron_1.app.getLoginItemSettings().wasOpenedAsHidden) {
            mainWindow.hide();
            tray_1.default.create(mainWindow);
        }
        else {
            if (config_1.default.get('lastWindowState').isMaximized) {
                mainWindow.maximize();
            }
            mainWindow.show();
        }
        if (electron_util_1.is.macos) {
            electron_better_ipc_1.ipcMain.answerRenderer('update-dnd-mode', async (initialSoundsValue) => {
                doNotDisturb.on('change', (doNotDisturb) => {
                    isDNDEnabled = doNotDisturb;
                    electron_better_ipc_1.ipcMain.callRenderer(mainWindow, 'toggle-sounds', isDNDEnabled ? false : initialSoundsValue);
                });
                isDNDEnabled = await doNotDisturb.isEnabled();
                return isDNDEnabled ? false : initialSoundsValue;
            });
        }
        setNotificationsMute(await electron_better_ipc_1.ipcMain.callRenderer(mainWindow, 'toggle-mute-notifications', config_1.default.get('notificationsMuted')));
        electron_better_ipc_1.ipcMain.callRenderer(mainWindow, 'toggle-message-buttons', config_1.default.get('showMessageButtons'));
        await webContents.executeJavaScript(fs_1.readFileSync(path.join(__dirname, 'notifications-isolated.js'), 'utf8'));
        if (electron_util_1.is.macos) {
            // eslint-disable-next-line node/no-unsupported-features/es-syntax
            await Promise.resolve().then(() => require('./touch-bar'));
        }
    });
    webContents.on('new-window', async (event, url, frameName, _disposition, options) => {
        var _a;
        event.preventDefault();
        if (url === 'about:blank' || url === 'about:blank#blocked') {
            if (frameName !== 'about:blank') {
                // Voice/video call popup
                options.show = true;
                options.titleBarStyle = 'default';
                options.webPreferences = (_a = options.webPreferences) !== null && _a !== void 0 ? _a : {};
                options.webPreferences.nodeIntegration = false;
                options.webPreferences.preload = path.join(__dirname, 'browser-call.js');
                event.newGuest = new electron_1.BrowserWindow(options);
            }
        }
        else {
            url = util_1.stripTrackingFromUrl(url);
            await electron_1.shell.openExternal(url);
        }
    });
    webContents.on('will-navigate', async (event, url) => {
        const isMessengerDotCom = (url) => {
            const { hostname } = new URL(url);
            return hostname.endsWith('.messenger.com');
        };
        const isTwoFactorAuth = (url) => {
            const twoFactorAuthURL = 'https://www.facebook.com/checkpoint';
            return url.startsWith(twoFactorAuthURL);
        };
        const isWorkChat = (url) => {
            const { hostname, pathname } = new URL(url);
            if (hostname === 'work.facebook.com' || hostname === 'work.workplace.com') {
                return true;
            }
            if (
            // Example: https://company-name.facebook.com/login or
            //   		https://company-name.workplace.com/login
            (hostname.endsWith('.facebook.com') || hostname.endsWith('.workplace.com')) &&
                (pathname.startsWith('/login') || pathname.startsWith('/chat'))) {
                return true;
            }
            if (hostname === 'login.microsoftonline.com') {
                return true;
            }
            return false;
        };
        if (isMessengerDotCom(url) || isTwoFactorAuth(url) || isWorkChat(url)) {
            return;
        }
        event.preventDefault();
        await electron_1.shell.openExternal(url);
    });
})();
if (electron_util_1.is.macos) {
    electron_better_ipc_1.ipcMain.answerRenderer('set-vibrancy', () => {
        mainWindow.setBackgroundColor('#80FFFFFF'); // Transparent, workaround for vibrancy issue.
        mainWindow.setVibrancy('sidebar');
    });
}
function toggleMaximized() {
    if (mainWindow.isMaximized()) {
        mainWindow.unmaximize();
    }
    else {
        mainWindow.maximize();
    }
}
electron_better_ipc_1.ipcMain.answerRenderer('titlebar-doubleclick', () => {
    if (electron_util_1.is.macos) {
        const doubleClickAction = electron_1.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
        if (doubleClickAction === 'Minimize') {
            mainWindow.minimize();
        }
        else if (doubleClickAction === 'Maximize') {
            toggleMaximized();
        }
    }
    else {
        toggleMaximized();
    }
});
electron_1.app.on('activate', () => {
    if (mainWindow) {
        mainWindow.show();
    }
});
electron_1.app.on('before-quit', () => {
    isQuitting = true;
    // Checking whether the window exists to work around an Electron race issue:
    // https://github.com/sindresorhus/caprine/issues/809
    if (mainWindow) {
        const { isMaximized } = config_1.default.get('lastWindowState');
        config_1.default.set('lastWindowState', { ...mainWindow.getNormalBounds(), isMaximized });
    }
});
const notifications = new Map();
electron_better_ipc_1.ipcMain.answerRenderer('notification', ({ id, title, body, icon, silent }) => {
    const notification = new electron_1.Notification({
        title,
        body: config_1.default.get('notificationMessagePreview') ? body : 'You have a new message',
        hasReply: true,
        icon: electron_1.nativeImage.createFromDataURL(icon),
        silent
    });
    notifications.set(id, notification);
    notification.on('click', () => {
        util_1.sendAction('notification-callback', { callbackName: 'onclick', id });
        notifications.delete(id);
    });
    notification.on('reply', (_event, reply) => {
        // We use onclick event used by messenger to go to the right convo
        util_1.sendBackgroundAction('notification-reply-callback', { callbackName: 'onclick', id, reply });
        notifications.delete(id);
    });
    notification.on('close', () => {
        util_1.sendBackgroundAction('notification-callback', { callbackName: 'onclose', id });
        notifications.delete(id);
    });
    notification.show();
});
