From b76c04428c8b6dd29f2d24a2a301382958ee254d Mon Sep 17 00:00:00 2001 From: Ryo Hilmawan <54142180+CloudHill@users.noreply.github.com> Date: Sat, 5 Dec 2020 06:19:34 +0700 Subject: [PATCH] new extension: panel-sites (#313) --- mods/panel-sites/app.css | 12 +++ mods/panel-sites/mod.js | 28 ++++++ mods/panel-sites/panel.js | 45 ++++++++++ mods/side-panel/mod.js | 176 +++++++++++++++++++++++++------------- 4 files changed, 201 insertions(+), 60 deletions(-) create mode 100644 mods/panel-sites/app.css create mode 100644 mods/panel-sites/mod.js create mode 100644 mods/panel-sites/panel.js diff --git a/mods/panel-sites/app.css b/mods/panel-sites/app.css new file mode 100644 index 0000000..c4aaf3e --- /dev/null +++ b/mods/panel-sites/app.css @@ -0,0 +1,12 @@ +/* + * panel sites + * (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) + * (c) 2020 CloudHill + * under the MIT license + */ + +.panel-site { + border: none; + flex: 1; + background: white; +} diff --git a/mods/panel-sites/mod.js b/mods/panel-sites/mod.js new file mode 100644 index 0000000..fb83182 --- /dev/null +++ b/mods/panel-sites/mod.js @@ -0,0 +1,28 @@ +/* + * panel sites + * (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) + * (c) 2020 CloudHill + * under the MIT license + */ + +'use strict'; + +module.exports = { + id: '0d541743-eb2c-4d77-83a8-3b2f5e8e5dff', + tags: ['extension', 'panel'], + name: 'panel sites', + desc: 'embed sites on the site panel.', + version: '1.0.0', + author: 'CloudHill', + options: [ + { + key: 'sites', + label: 'list of sites', + type: 'file', + extensions: ['json'], + }, + ], + panel: { + js: 'panel.js' + } +}; diff --git a/mods/panel-sites/panel.js b/mods/panel-sites/panel.js new file mode 100644 index 0000000..04d9ff4 --- /dev/null +++ b/mods/panel-sites/panel.js @@ -0,0 +1,45 @@ +/* + * panel sites + * (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) + * (c) 2020 CloudHill + * under the MIT license + */ + +const electron = require('electron') + +module.exports = (store) => { + let iframe; + const mainWindow = electron.remote.getCurrentWindow(); + const originalUserAgent = mainWindow.webContents.getUserAgent(); + const mobileUserAgent = + 'Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36' + + // bypass x-frame-options + mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { + const responseHeaders = Object.entries(details.responseHeaders) + .filter( h => !/x-frame-options/i.test(h[0]) ); + callback({ + responseHeaders: Object.fromEntries(responseHeaders) + }); + }); + + // handle opening mobile sites + function setUserAgent(userAgent) { + mainWindow.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => { + details.requestHeaders['User-Agent'] = userAgent; + callback({ cancel: false, requestHeaders: details.requestHeaders }); + }); + } + + return { + onLoad() { + iframe = document.querySelector('.panel-site'); + if (iframe.hasAttribute('mobile-user-agent')) + setUserAgent(mobileUserAgent); + }, + onSwitch() { + if (iframe.hasAttribute('mobile-user-agent')) + setUserAgent(originalUserAgent); + } + } +} diff --git a/mods/side-panel/mod.js b/mods/side-panel/mod.js index 8aa88c5..3e35589 100644 --- a/mods/side-panel/mod.js +++ b/mods/side-panel/mod.js @@ -29,61 +29,16 @@ module.exports = { })(); // Load panel mods - let panelMods = getEnhancements().loaded.filter(mod => - (mod.panel && (store('mods')[mod.id] || {}).enabled) - ); - - // Get panel info - panelMods.forEach(mod => initMod(mod)); - async function initMod(mod) { - try { - if (typeof mod.panel === 'object') { - // html - mod.panel.html = await fs.readFile( - path.resolve(__dirname, `../${mod.dir}/${mod.panel.html}`) - ); - // name - if (!mod.panel.name) mod.panel.name = mod.name; - // icon - if (mod.panel.icon) { - const iconPath = path.resolve(__dirname, `../${mod.dir}/${mod.panel.icon}`); - if (await fs.pathExists(iconPath)) - mod.panel.icon = await fs.readFile(iconPath); - } else { - mod.panel.icon = mod.panel.name[0]; - } - // js - if (mod.panel.js) { - const jsPath = `../${mod.dir}/${mod.panel.js}`; - if (await fs.pathExists(path.resolve(__dirname, jsPath))) { - mod.panel.js = require(jsPath)((...args) => { - if (!args.length) return store(mod.id, mod.defaults); - if (args.length === 1 && typeof args[0] === 'object') - return store(mod.id, { ...mod.defaults, ...args[0] }); - const other_mod = modules.find((m) => m.id === args[0]); - return store(args[0], { - ...(other_mod ? other_mod.defaults : {}), - ...(args[1] || {}) - }) - }, __exports); - } - } - } else if (typeof mod.panel === 'string') { - mod.panel.icon = mod.name[0]; - mod.panel.html = await fs.readFile( - path.resolve(__dirname, `../${mod.dir}/${mod.panel}`) - ); - } else throw Error; - } catch (err) { - console.log('invalid panel mod: ' + mod.name); - panelMods = panelMods.filter(panelMod => panelMod !== mod); - } - } + let panelMods = + getEnhancements().loaded.filter( + mod => (mod.panel && (store('mods')[mod.id] || {}).enabled) + ); + panelMods.forEach(mod => initMod(mod)); document.addEventListener('readystatechange', (event) => { if (document.readyState !== 'complete') return false; if (panelMods.length < 1) return; - + const attempt_interval = setInterval(enhance, 500); function enhance() { if (!store().width) store().width = 220; @@ -167,11 +122,9 @@ module.exports = { loadContent(mod); loaded = true; } - }) - } - if (!loaded) { - loadContent(panelMods[0]); + }); } + if (!loaded) loadContent(panelMods[0]); function loadContent(mod) { if (curPanel.js && curPanel.js.onSwitch) curPanel.js.onSwitch(); @@ -181,7 +134,7 @@ module.exports = { panel.querySelector('.enhancer-panel--title').innerHTML = mod.panel.name || mod.name; // Reload button - let reloadButton = document.querySelector('.enhancer-panel--reload-button'); + let reloadButton = panel.querySelector('.enhancer-panel--reload-button'); if (reloadButton) reloadButton.remove(); if (mod.panel.reload) { reloadButton = createElement( @@ -282,17 +235,19 @@ module.exports = { } function renderSwitcher() { - if (document.querySelector('.enhancer-panel--overlay-container')) return; + if (panel.querySelector('.enhancer-panel--overlay-container')) return; // Layer to close switcher const overlayContainer = createElement( '<div class="enhancer-panel--overlay-container"></div>' ); overlayContainer.addEventListener('click', hideSwitcher) - document.querySelector('.notion-app-inner').appendChild(overlayContainer); + document + .querySelector('.notion-app-inner') + .appendChild(overlayContainer); // Position switcher below header - const rect = header.getBoundingClientRect(); + const rect = panel.querySelector('.enhancer-panel--header').getBoundingClientRect(); const div = createElement(` <div style="position: fixed; top: ${rect.top}px; left: ${rect.left}px; width: ${rect.width}px; height: ${rect.height}px "> <div style="position: relative; top: 100%; pointer-events: auto;"></div> @@ -349,7 +304,7 @@ module.exports = { } function enableResize() { - const handle = resize.firstElementChild; + const handle = panel.querySelector('.enhancer-panel--resize div'); handle.addEventListener('mousedown', initDrag); let startX, startWidth; @@ -410,6 +365,107 @@ module.exports = { } } }); + + // INITIALIZATION FUNCTIONS + + async function initMod(mod) { + // load panel sites + if (mod.id === '0d541743-eb2c-4d77-83a8-3b2f5e8e5dff') { + panelMods = panelMods.filter(panelMod => panelMod !== mod); + return panelMods.push(...initPanelSites(mod)); + } + try { + if (typeof mod.panel === 'object') { + // html + mod.panel.html = await fs.readFile( + path.resolve(__dirname, `../${mod.dir}/${mod.panel.html}`) + ); + // name + if (!mod.panel.name) mod.panel.name = mod.name; + // icon + if (mod.panel.icon) { + const iconPath = path.resolve(__dirname, `../${mod.dir}/${mod.panel.icon}`); + if (await fs.pathExists(iconPath)) + mod.panel.icon = await fs.readFile(iconPath); + } else { + mod.panel.icon = mod.panel.name[0]; + } + // js + if (mod.panel.js) { + const jsPath = `../${mod.dir}/${mod.panel.js}`; + if (await fs.pathExists(path.resolve(__dirname, jsPath))) { + mod.panel.js = require(jsPath)(loadStore(mod), __exports); + } + } + } else if (typeof mod.panel === 'string') { + mod.panel.icon = mod.name[0]; + mod.panel.html = await fs.readFile( + path.resolve(__dirname, `../${mod.dir}/${mod.panel}`) + ); + } else throw Error; + } catch (err) { + console.log('invalid panel mod: ' + mod.name); + panelMods = panelMods.filter(panelMod => panelMod !== mod); + } + } + + function initPanelSites(mod) { + let panelSites = []; + const sitesPath = store(mod.id).sites; + if (sitesPath) { + try { + const sites = require(sitesPath); + const invalid = false; + const sitePanelJs = require('../panel-sites/panel.js')(loadStore(mod), __exports); + + const frameUrl = function(url, mobile) { + if (!/(^https?:\/\/)/.test(url)) url = 'https://' + url; + return `<iframe src=${url} class="panel-site" ${mobile ? 'mobile-user-agent' : ''}></iframe>`; + } + + sites.forEach(site => { + if (site.url && site.name) { + + // get url and icon + const iframe = frameUrl(site.url, site.mobile); + const icon = `<img style="width: 100%; height: 100%;" + src="${site.icon || `https://www.google.com/s2/favicons?domain=${site.url}`}" />`; + + const panelMod = { + id: `${mod.id}-${site.url}`, + panel: { + name: site.name, + html: iframe, + icon: icon, + js: sitePanelJs, + fullHeight: true, + reload: true, + }, + } + panelSites.push(panelMod); + } else invalid = true; + }); + if (invalid) throw Error; + } + catch (err) { + console.log('panel site error'); + } + } + return panelSites; + } + + function loadStore(mod) { + return (...args) => { + if (!args.length) return store(mod.id, mod.defaults); + if (args.length === 1 && typeof args[0] === 'object') + return store(mod.id, { ...mod.defaults, ...args[0] }); + const other_mod = modules.find((m) => m.id === args[0]); + return store(args[0], { + ...(other_mod ? other_mod.defaults : {}), + ...(args[1] || {}) + }) + } + } }, }, };