diff --git a/main.js b/main.js deleted file mode 100644 index 573b0cc..0000000 --- a/main.js +++ /dev/null @@ -1,162 +0,0 @@ -// Modules -const electron = require('electron') -const windowStateKeeper = require('electron-window-state') -const { - app, - BrowserWindow, - session, - globalShortcut, - Tray, - ipcMain -} = electron -const readItem = require('./readItem') -const updater = require('./updater') - -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let mainWindow -let monitorWindow - -// Listen for new item request -ipcMain.on('new-item', (e, itemUrl) => { - // console.log(itemUrl) - - // Get new item and send back to renderer - readItem(itemUrl, item => { - e.sender.send('new-item-success', item) - }) -}) - -// Create a new BrowserWindow when `app` is ready -function createWindow() { - let ses = session.defaultSession - - // Check for app updates 3 seconds after launch - setTimeout( updater, 3000 ) - - let windowState = windowStateKeeper({ - defaultWidth: 500, defaultHeight: 650 - }) - - mainWindow = new BrowserWindow({ - width: windowState.width, height: windowState.height, - x: windowState.x, y: windowState.y, - minWidth: 350, maxWidth: 650, minHeight: 300, - webPreferences: {nodeIntegration: true}, - titleBarStyle: 'hidden' - }) - - windowState.manage(mainWindow) - - // Load main.html into the new BrowserWindow - mainWindow.loadFile('renderer/main.html') - - mainWindow.webContents.openDevTools() - - // Listen for window being closed - mainWindow.on('closed', () => { - mainWindow = null - }) - - let wc = mainWindow.webContents - /* - wc.on('before-input-event', (e, input) => { - console.log('Before ' + input.key + ' ' + input.type) - }) - */ - wc.on('did-finish-load', () => { - console.log('main window fully loaded') - }) - wc.on('dom-ready', () => { - console.log('DOM ready') - }) - - //***// - globalShortcut.register('f5', function() { - console.log('f5 is pressed') - mainWindow.reload() - }) - globalShortcut.register('CommandOrControl+R', function() { - console.log('CommandOrControl+R is pressed') - mainWindow.reload() - }) -} - -function createMonitorWindow() { - let windowState = windowStateKeeper({ - defaultWidth: 500, defaultHeight: 400 - }) - - // Create a new window - monitorWindow = new BrowserWindow({ - width: windowState.width, height: windowState.height, - x: windowState.x, y: windowState.y, - minWidth: 300, maxWidth: 800, minHeight: 300, maxHeight: 800, - // set the title bar style - titleBarStyle: 'hiddenInset', - // set the background color to black - backgroundColor: "#111", - // Don't show the window until it's ready, this prevents any white flickering - show: false, - webPreferences: {nodeIntegration: true} - }) - - windowState.manage(monitorWindow) - - monitorWindow.loadFile("renderer/monitor.html") - - monitorWindow.webContents.openDevTools() - - monitorWindow.once('ready-to-show', () => { - monitorWindow.show() - }) - - let wc = monitorWindow.webContents - wc.on('did-finish-load', () => { - console.log('monitor window fully loaded') - monitorWindow.webContents.send('init-monitor', true) - }) -} - -/* -app.on('browser-window-blur', () => { - console.log('App unfocused') - setTimeout(app.quit, 3000) -}) - -app.on('browser-window-focus', () => { - console.log('App focused') -}) -*/ - -// Electron `app` is ready -app.on('ready', () => { - console.log('App is ready') - console.log(app.getPath('desktop')) - console.log(app.getPath('music')) - console.log(app.getPath('temp')) - console.log(app.getPath('userData')) - createWindow() - createMonitorWindow() - // setTimeout(app.focus, 1000) -}) - -// Quit when all windows are closed - (Not macOS - Darwin) -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') app.quit() -}) - -// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow -app.on('activate', () => { - if (mainWindow === null) { - createWindow() - } - if (monitorWindow === null) { - createMonitorWindow() - } -}) - - - - - diff --git a/package.json b/package.json index d7b5e02..0a1e9b5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "rb-electron-test", "version": "1.1.0", "description": "Resource Monitor", - "main": "main.js", + "main": "src/main/main.js", "scripts": { "start": "electron .", "watch": "nodemon --exec \"electron .\"", diff --git a/renderer/app.js b/renderer/app.js deleted file mode 100644 index 9635f43..0000000 --- a/renderer/app.js +++ /dev/null @@ -1,119 +0,0 @@ - -// Modules -const {ipcRenderer} = require('electron') -const monitor = require('./monitor') -const items = require('./items') - -// Dom Nodes -let showModal = document.getElementById('show-modal'), - closeModal = document.getElementById('close-modal'), - modal = document.getElementById('modal'), - addItem = document.getElementById('add-item'), - itemUrl = document.getElementById('url'), - search = document.getElementById('search') - -// Open new item modal -window.newItem = () => { - showModal.click() -} - -// Open selected item -window.openItem = () => { - items.open() -} - -// Delete selected item -window.deleteItem = () => { - let selectedItem = items.getSelectedItem(); - items.delete(selectedItem.index) -} - -// Open selected item in native Browser -window.openItemNative = () => { - items.openNative() -} - -// Search Items -window.searchItems = () => { - search.focus() -} - -// Filter items with "search" -search.addEventListener('keyup', e => { - - // Loop items - Array.from( document.getElementsByClassName('read-item') ).forEach( item => { - - // Hide items that don't match search value - let hasMatch = item.innerText.toLowerCase().includes(search.value) - item.style.display = hasMatch ? 'flex' : 'none' - }) -}) - -// Navigate item selection with up/down arrows -document.addEventListener('keydown', e => { - if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { - items.changeSelection(e.key) - } -}) - -// Disable & Enable modal buttons -const toggleModalButtons = () => { - - // Check state of buttons - if (addItem.disabled === true) { - addItem.disabled = false - addItem.style.opacity = 1 - addItem.innerText = 'Add Item' - closeModal.style.display = 'inline' - } else { - addItem.disabled = true - addItem.style.opacity = 0.5 - addItem.innerText = 'Adding...' - closeModal.style.display = 'none' - } -} - -// Show modal -showModal.addEventListener('click', e => { - modal.style.display = 'flex' - itemUrl.focus() -}) - -// Hide modal -closeModal.addEventListener('click', e => { - modal.style.display = 'none' -}) - -// Handle new item -addItem.addEventListener('click', e => { - - // Check a url exists - if (itemUrl.value) { - - // Send new item url to main process - ipcRenderer.send('new-item', itemUrl.value) - - // Disable buttons - toggleModalButtons() - } -}) - -// Listen for new item from main process -ipcRenderer.on('new-item-success', (e, newItem) => { - - // Add new item to "items" node - items.addItem(newItem, true) - - // Enable buttons - toggleModalButtons() - - // Hide modal and clear value - modal.style.display = 'none' - itemUrl.value = '' -}) - -// Listen for keyboard submit -itemUrl.addEventListener('keyup', e => { - if( e.key === 'Enter' ) addItem.click() -}) diff --git a/renderer/items.js b/renderer/items.js deleted file mode 100644 index 552d62e..0000000 --- a/renderer/items.js +++ /dev/null @@ -1,181 +0,0 @@ - -// Modules -const fs = require('fs') -const {shell} = require('electron') - -// DOM nodes -let items = document.getElementById('items') - -// Get readerJS contents -let readerJS -fs.readFile(`${__dirname}/reader.js`, (err, data) => { - readerJS = data.toString() -}) - -// Track items in storage -exports.storage = JSON.parse(localStorage.getItem('readit-items')) || [] - -// Listen for "Done" message from reader window -window.addEventListener('message', e => { - - // Check for correct message - if (e.data.action === 'delete-reader-item') { - - // Delete item at given index - this.delete(e.data.itemIndex) - - // Close the reader window - e.source.close() - } -}) - -// Delete item -exports.delete = itemIndex => { - - // Remove item from DOM - items.removeChild( items.childNodes[itemIndex] ) - - // Remove from storage - this.storage.splice(itemIndex, 1) - - // Persist - this.save() - - // Select previous item or new first item if first was deleted - if (this.storage.length) { - - // Get new selected item index - let newSelectedItemIndex = (itemIndex === 0) ? 0 : itemIndex - 1 - - // Set item at new index as selected - document.getElementsByClassName('read-item')[newSelectedItemIndex].classList.add('selected') - } -} - -// Get selected item index -exports.getSelectedItem = () => { - - // Get selected node - let currentItem = document.getElementsByClassName('read-item selected')[0] - - // Get item index - let itemIndex = 0 - let child = currentItem - while( (child = child.previousSibling) != null ) itemIndex++ - - // Return selected item and index - return { node: currentItem, index: itemIndex } -} - -// Persist storage -exports.save = () => { - localStorage.setItem('readit-items', JSON.stringify(this.storage)) -} - -// Set item as selected -exports.select = e => { - - // Remove currently selected item class - this.getSelectedItem().node.classList.remove('selected') - - // Add to clicked item - e.currentTarget.classList.add('selected') -} - -// Move to newly selected item -exports.changeSelection = direction => { - - // Get selected item - let currentItem = this.getSelectedItem() - - // Handle up/down - if (direction === 'ArrowUp' && currentItem.node.previousSibling) { - currentItem.node.classList.remove('selected') - currentItem.node.previousSibling.classList.add('selected') - - } else if (direction === 'ArrowDown' && currentItem.node.nextSibling) { - currentItem.node.classList.remove('selected') - currentItem.node.nextSibling.classList.add('selected') - } -} - -// Open selected item -exports.open = () => { - - // Only if we have items (in case of menu open) - if( !this.storage.length ) return - - // Get selected item - let selectedItem = this.getSelectedItem() - - // Get item's url - let contentURL = selectedItem.node.dataset.url - - // Open item in proxy BrowserWindow - let readerWin = window.open(contentURL, '', ` - maxWidth=2000, - maxHeight=2000, - width=1200, - height=800, - backgroundColor=#DEDEDE, - nodeIntegration=0, - contextIsolation=1 - `) - - // Inject JavaScript with specific item index (selectedItem.index) - readerWin.eval( readerJS.replace('{{index}}', selectedItem.index) ) -} - -// Open selected item in native Browser -exports.openNative = () => { - - // Only if we have items (in case of menu open) - if( !this.storage.length ) return - - // Get selected item - let selectedItem = this.getSelectedItem() - - // Open in system browser - shell.openExternal(selectedItem.node.dataset.url) -} - -// Add new item -exports.addItem = (item, isNew = false) => { - - // Create a new DOM node - let itemNode = document.createElement('div') - - // Assign "read-item" class - itemNode.setAttribute('class', 'read-item') - - // Set item url as data attribute - itemNode.setAttribute('data-url', item.url) - - // Add inner HTML - itemNode.innerHTML = `

${item.title}

` - - // Append new node to "items" - items.appendChild(itemNode) - - // Attach click handler to select - itemNode.addEventListener('click', this.select) - - // Attach open doubleclick handler - itemNode.addEventListener('dblclick', this.open) - - // If this is the first item, select it - if (document.getElementsByClassName('read-item').length === 1) { - itemNode.classList.add('selected') - } - - // Add item to storage and persist - if(isNew) { - this.storage.push(item) - this.save() - } -} - -// Add items from storage when app loads -this.storage.forEach( item => { - this.addItem(item) -}) diff --git a/renderer/main.css b/renderer/main.css deleted file mode 100644 index 555a05c..0000000 --- a/renderer/main.css +++ /dev/null @@ -1,122 +0,0 @@ - -html, body { - height: 100%; -} - -body { - font: caption; - margin: 0; - display: flex; - flex-flow: column; -} - -button { - background: dodgerblue; - color: white; - border-radius: 5px; - border: none; - font-size: 20px; - outline: none; -} - -input { - font-size: 20px; - border-radius: 5px; - border: 1px solid silver; - padding: 0 10px; -} - -input::placeholder { - color: lightgray; -} - -header { - background: lightgray; - display: flex; - padding: 10px; - font-weight: bold; - border-bottom: 1px solid silver; - box-shadow: 0px 10px 10px rgba(0,0,0,0.1); -} - -#show-modal { - padding: 0px 12px 5px; - margin-right: 10px; - font-size: 30px; -} - -#search { - flex-grow: 1; -} - -main { - flex-grow: 1; - overflow-y: scroll; -} - -#no-items { - font-weight: bold; - color: silver; - text-align: center; - width: 100%; - position: absolute; - top: 100px; - z-index: -1; -} - -#modal { - position: absolute; - width: 100%; - height: 100%; - background: rgba(0,0,0,0.85); - display: flex; - align-items: center; - align-content: center; - flex-wrap: wrap; - display: none; -} - -#url { - flex-grow: 1; - width: 100%; - margin: 0 25px 15px; - padding: 10px; -} - -#modal button { - padding: 10px; -} - -#close-modal { - background: white; - color: black; - margin-left: 15px; -} - -#add-item { - margin-left: 25px; -} - -.read-item { - display: flex; - align-items: center; - align-content: center; - border-bottom: lightgray 2px solid; - background: #FAFAFA; - padding: 10px; - border-left: 10px solid lightgray; - -webkit-user-select: none; -} - -.read-item:hover { - background-color: #EEE; -} - -.read-item.selected { - border-left-color: dodgerblue; -} - -.read-item img { - width: 20%; - margin-right: 25px; -} diff --git a/src/main/main.js b/src/main/main.js new file mode 100644 index 0000000..3b72c35 --- /dev/null +++ b/src/main/main.js @@ -0,0 +1,49 @@ +// Modules +const electron = require('electron') +const windowStateKeeper = require('electron-window-state') +const {app} = electron +const mainWindow = require('./mainWindow') +const monitorWindow = require('./monitorWindow') +const updater = require('./updater') + +/* +app.on('browser-window-blur', () => { + console.log('App unfocused') + setTimeout(app.quit, 3000) +}) + +app.on('browser-window-focus', () => { + console.log('App focused') +}) +*/ + +// Electron `app` is ready +app.on('ready', () => { + console.log('App is ready') + console.log(app.getPath('desktop')) + console.log(app.getPath('music')) + console.log(app.getPath('temp')) + console.log(app.getPath('userData')) + + mainWindow.activate() + // monitorWindow.activate() + + // Check for app updates 3 seconds after launch + // setTimeout( updater, 3000 ) +}) + +// Quit when all windows are closed - (Not macOS - Darwin) +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit() +}) + +// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow +app.on('activate', () => { + mainWindow.activate() + // montitorWindow.activate() +}) + + + + + diff --git a/src/main/mainWindow.js b/src/main/mainWindow.js new file mode 100644 index 0000000..8af7bc6 --- /dev/null +++ b/src/main/mainWindow.js @@ -0,0 +1,88 @@ +// Modules +const electron = require('electron') +const windowStateKeeper = require('electron-window-state') +const { + BrowserWindow, + globalShortcut, + ipcMain +} = electron +const readItem = require('./readItem') + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let mainBrowserwindow = null + +// Listen for new item request +ipcMain.on('new-item', (e, itemUrl) => { + console.log('Received new-item via ipcMain: ' + itemUrl) + + // Get new item and send back to renderer + readItem(itemUrl, item => { + console.log('send new-item-success back to e.sender: ' + item) + e.sender.send('new-item-success', item) + }) +}) + +exports.activate = () => { + + console.log('activateMain') + console.log(mainBrowserwindow) + if (mainBrowserwindow === null) { + createMainWindow() + } +} + +// Create a new BrowserWindow when `app` is ready +function createMainWindow() { + + let windowState = windowStateKeeper({ + defaultWidth: 500, defaultHeight: 650 + }) + + mainBrowserwindow = new BrowserWindow({ + width: windowState.width, height: windowState.height, + x: windowState.x, y: windowState.y, + minWidth: 350, maxWidth: 650, minHeight: 300, + webPreferences: {nodeIntegration: true/*, webSecurity: false*/}, + titleBarStyle: 'hidden' + }) + + windowState.manage(mainBrowserwindow) + + // Load main.html into the new BrowserWindow + mainBrowserwindow.loadFile('src/renderer/html/main.html') + + mainBrowserwindow.webContents.openDevTools() + + // Listen for window being closed + mainBrowserwindow.on('closed', () => { + mainBrowserwindow = null + }) + + let wc = mainBrowserwindow.webContents + /* + wc.on('before-input-event', (e, input) => { + console.log('Before ' + input.key + ' ' + input.type) + }) + */ + wc.on('did-finish-load', () => { + console.log('main window fully loaded') + }) + wc.on('dom-ready', () => { + console.log('DOM ready') + }) + + //***// + globalShortcut.register('f5', function () { + console.log('f5 is pressed') + mainBrowserwindow.reload() + }) + globalShortcut.register('CommandOrControl+R', function () { + console.log('CommandOrControl+R is pressed') + mainBrowserwindow.reload() + }) +} + + + + diff --git a/src/main/monitorWindow.js b/src/main/monitorWindow.js new file mode 100644 index 0000000..ed3a783 --- /dev/null +++ b/src/main/monitorWindow.js @@ -0,0 +1,52 @@ +// Modules +const electron = require('electron') +const windowStateKeeper = require('electron-window-state') +const {BrowserWindow} = electron + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let monitorBrowserWindow = null + +exports.activate = () => { + if (monitorBrowserWindow === null) { + createMonitorWindow() + } +} + +function createMonitorWindow() { + + let windowState = windowStateKeeper({ + defaultWidth: 500, defaultHeight: 400 + }) + + // Create a new window + monitorBrowserWindow = new BrowserWindow({ + width: windowState.width, height: windowState.height, + x: windowState.x, y: windowState.y, + minWidth: 300, maxWidth: 800, minHeight: 300, maxHeight: 800, + // set the title bar style + titleBarStyle: 'hiddenInset', + // set the background color to black + backgroundColor: "#111", + // Don't show the window until it's ready, this prevents any white flickering + show: false, + webPreferences: {nodeIntegration: true/*, webSecurity: false*/} + }) + + windowState.manage(monitorBrowserWindow) + + monitorBrowserWindow.loadFile("src/renderer/html/monitor.html") + + monitorBrowserWindow.webContents.openDevTools() + + monitorBrowserWindow.once('ready-to-show', () => { + monitorBrowserWindow.show() + }) + + let wc = monitorBrowserWindow.webContents + wc.on('did-finish-load', () => { + console.log('monitor window fully loaded') + console.log('Send init-monitor via monitorBrowserWindow.webContents') + monitorBrowserWindow.webContents.send('init-monitor', true) + }) +} diff --git a/readItem.js b/src/main/readItem.js similarity index 100% rename from readItem.js rename to src/main/readItem.js diff --git a/updater.js b/src/main/updater.js similarity index 100% rename from updater.js rename to src/main/updater.js diff --git a/src/renderer/app.js b/src/renderer/app.js new file mode 100644 index 0000000..17a25b7 --- /dev/null +++ b/src/renderer/app.js @@ -0,0 +1,3 @@ +// Modules +const main = require('./script/main') +const monitor = require('./script/monitor') diff --git a/renderer/main.html b/src/renderer/html/main.html similarity index 76% rename from renderer/main.html rename to src/renderer/html/main.html index cd420df..2e95003 100644 --- a/renderer/main.html +++ b/src/renderer/html/main.html @@ -1,21 +1,22 @@ + Readit - + - +
-

No Items

-
+

No Items

+
+ diff --git a/renderer/monitor.html b/src/renderer/html/monitor.html similarity index 78% rename from renderer/monitor.html rename to src/renderer/html/monitor.html index 93dbe38..6d3aaf1 100644 --- a/renderer/monitor.html +++ b/src/renderer/html/monitor.html @@ -3,15 +3,10 @@ - Activity Monitor - - - - - - + + diff --git a/src/renderer/script/main.js b/src/renderer/script/main.js new file mode 100644 index 0000000..b101c9d --- /dev/null +++ b/src/renderer/script/main.js @@ -0,0 +1,122 @@ +// Modules +const {ipcRenderer} = require('electron') +const items = require('./mainItems') + +// Dom Nodes +let showModal = document.getElementById('show-modal'), + closeModal = document.getElementById('close-modal'), + modal = document.getElementById('modal'), + addItem = document.getElementById('add-item'), + itemUrl = document.getElementById('url'), + search = document.getElementById('search') + +// Open new item modal +window.newItem = () => { + showModal.click() +} + +// Open selected item +window.openItem = () => { + items.open() +} + +// Delete selected item +window.deleteItem = () => { + let selectedItem = items.getSelectedItem(); + items.delete(selectedItem.index) +} + +// Open selected item in native Browser +window.openItemNative = () => { + items.openNative() +} + +// Search Items +window.searchItems = () => { + search.focus() +} + +// Filter items with "search" +search.addEventListener('keyup', e => { + + // Loop items + Array.from(document.getElementsByClassName('read-item')).forEach(item => { + + // Hide items that don't match search value + let hasMatch = item.innerText.toLowerCase().includes(search.value) + item.style.display = hasMatch ? 'flex' : 'none' + }) +}) + +// Navigate item selection with up/down arrows +document.addEventListener('keydown', e => { + if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + items.changeSelection(e.key) + } +}) + +// Disable & Enable modal buttons +const toggleModalButtons = () => { + + // Check state of buttons + if (addItem.disabled === true) { + addItem.disabled = false + addItem.style.opacity = 1 + addItem.innerText = 'Add Item' + closeModal.style.display = 'inline' + } else { + addItem.disabled = true + addItem.style.opacity = 0.5 + addItem.innerText = 'Adding...' + closeModal.style.display = 'none' + } +} + +// Show modal +showModal.addEventListener('click', e => { + modal.style.display = 'flex' + itemUrl.focus() +}) + +// Hide modal +closeModal.addEventListener('click', e => { + modal.style.display = 'none' +}) + +// Handle new item +addItem.addEventListener('click', e => { + + // Check a url exists + if (itemUrl.value) { + + console.log('Send new-item via ipcRenderer: ' + itemUrl.value) + + // Send new item url to main process + ipcRenderer.send('new-item', itemUrl.value) + + // Disable buttons + toggleModalButtons() + } +}) + +// Listen for new item from main process +ipcRenderer.on('new-item-success', (e, newItem) => { + + console.log('Received new-item-success via ipcRenderer') + console.log(newItem) + + // Add new item to "items" node + items.addItem(newItem, true) + + // Enable buttons + toggleModalButtons() + + // Hide modal and clear value + modal.style.display = 'none' + itemUrl.value = '' +}) + +// Listen for keyboard submit +itemUrl.addEventListener('keyup', e => { + if (e.key === 'Enter') addItem.click() +}) diff --git a/src/renderer/script/mainItems.js b/src/renderer/script/mainItems.js new file mode 100644 index 0000000..95c78ab --- /dev/null +++ b/src/renderer/script/mainItems.js @@ -0,0 +1,185 @@ +// Modules +const fs = require('fs') +const {shell} = require('electron') + +// Dom Nodes +let main = document.getElementById('main'), + noMain = document.getElementById('no-main') + +// Get readerJS contents +let readerJS +fs.readFile(`${__dirname}/mainReader.js`, (err, data) => { + readerJS = data.toString() +}) + +// Track main in storage +exports.storage = JSON.parse(localStorage.getItem('readit-main')) || [] + +// Listen for "Done" message from reader window +window.addEventListener('message', e => { + + // Check for correct message + if (e.data.action === 'delete-reader-item') { + + // Delete item at given index + this.delete(e.data.itemIndex) + + // Close the reader window + e.source.close() + } +}) + +// Delete item +exports.delete = itemIndex => { + + // Remove item from DOM + main.removeChild(main.childNodes[itemIndex]) + if (main.childNodes.length === 0) { + noMain.style.display = 'inline' + } + + // Remove from storage + this.storage.splice(itemIndex, 1) + + // Persist + this.save() + + // Select previous item or new first item if first was deleted + if (this.storage.length) { + + // Get new selected item index + let newSelectedItemIndex = (itemIndex === 0) ? 0 : itemIndex - 1 + + // Set item at new index as selected + document.getElementsByClassName('read-item')[newSelectedItemIndex].classList.add('selected') + } +} + +// Get selected item index +exports.getSelectedItem = () => { + + // Get selected node + let currentItem = document.getElementsByClassName('read-item selected')[0] + + // Get item index + let itemIndex = 0 + let child = currentItem + while ((child = child.previousSibling) != null) itemIndex++ + + // Return selected item and index + return {node: currentItem, index: itemIndex} +} + +// Persist storage +exports.save = () => { + localStorage.setItem('readit-main', JSON.stringify(this.storage)) +} + +// Set item as selected +exports.select = e => { + + // Remove currently selected item class + this.getSelectedItem().node.classList.remove('selected') + + // Add to clicked item + e.currentTarget.classList.add('selected') +} + +// Move to newly selected item +exports.changeSelection = direction => { + + // Get selected item + let currentItem = this.getSelectedItem() + + // Handle up/down + if (direction === 'ArrowUp' && currentItem.node.previousSibling) { + currentItem.node.classList.remove('selected') + currentItem.node.previousSibling.classList.add('selected') + + } else if (direction === 'ArrowDown' && currentItem.node.nextSibling) { + currentItem.node.classList.remove('selected') + currentItem.node.nextSibling.classList.add('selected') + } +} + +// Open selected item +exports.open = () => { + + // Only if we have main (in case of menu open) + if (!this.storage.length) return + + // Get selected item + let selectedItem = this.getSelectedItem() + + // Get item's url + let contentURL = selectedItem.node.dataset.url + + // Open item in proxy BrowserWindow + let readerWin = window.open(contentURL, '', ` + maxWidth=2000, + maxHeight=2000, + width=1200, + height=800, + backgroundColor=#DEDEDE, + nodeIntegration=0, + contextIsolation=1 + `) + + // Inject JavaScript with specific item index (selectedItem.index) + readerWin.eval(readerJS.replace('{{index}}', selectedItem.index)) +} + +// Open selected item in native Browser +exports.openNative = () => { + + // Only if we have main (in case of menu open) + if (!this.storage.length) return + + // Get selected item + let selectedItem = this.getSelectedItem() + + // Open in system browser + shell.openExternal(selectedItem.node.dataset.url) +} + +// Add new item +exports.addItem = (item, isNew = false) => { + + // Create a new DOM node + let itemNode = document.createElement('div') + + // Assign "read-item" class + itemNode.setAttribute('class', 'read-item') + + // Set item url as data attribute + itemNode.setAttribute('data-url', item.url) + + // Add inner HTML + itemNode.innerHTML = `

${item.title}

` + + // Append new node to "main" + main.appendChild(itemNode) + noMain.style.display = 'none' + + // Attach click handler to select + itemNode.addEventListener('click', this.select) + + // Attach open doubleclick handler + itemNode.addEventListener('dblclick', this.open) + + // If this is the first item, select it + if (document.getElementsByClassName('read-item').length === 1) { + itemNode.classList.add('selected') + } + + // Add item to storage and persist + if (isNew) { + this.storage.push(item) + this.save() + } +} + +// Add main from storage when app loads +this.storage.forEach(item => { + this.addItem(item) +}) diff --git a/renderer/menu.js b/src/renderer/script/mainMenu.js similarity index 83% rename from renderer/menu.js rename to src/renderer/script/mainMenu.js index 428be80..0395cf1 100644 --- a/renderer/menu.js +++ b/src/renderer/script/mainMenu.js @@ -44,13 +44,15 @@ const template = [ submenu: [ { label: 'Learn more', - click: () => {shell.openExternal('https://github.com/ptaucher/rb-electron-test')} + click: () => { + shell.openExternal('https://github.com/ptaucher/rb-electron-test') + } } ] } ] -// Set Mac-specific first menu item +// Set Mac-specific first mainMenu item if (process.platform === 'darwin') { template.unshift({ label: remote.app.getName(), @@ -68,8 +70,8 @@ if (process.platform === 'darwin') { }) } -// Build menu -const menu = remote.Menu.buildFromTemplate(template) +// Build mainMenu +const mainMenu = remote.Menu.buildFromTemplate(template) -// Set as main menu -remote.Menu.setApplicationMenu(menu) +// Set as main mainMenu +remote.Menu.setApplicationMenu(mainMenu) diff --git a/renderer/reader.js b/src/renderer/script/mainReader.js similarity index 81% rename from renderer/reader.js rename to src/renderer/script/mainReader.js index d586086..442839c 100644 --- a/renderer/reader.js +++ b/src/renderer/script/mainReader.js @@ -1,4 +1,3 @@ - // Create button in remote content to mark item as "Done" let readitClose = document.createElement('div') readitClose.innerText = 'Done' @@ -19,11 +18,17 @@ readitClose.style.boxShadow = '2px 2px 2px rgba(0,0,0,0.2)' // Attach click handler readitClose.onclick = e => { - // Message parent (opener) window - window.opener.postMessage({ - action: 'delete-reader-item', - itemIndex: {{index}} - }, '*') + // Message parent (opener) window + window.opener.postMessage({ + action: 'delete-reader-item', + itemIndex: { + { + index + } +} +}, + '*' +) } // Append button to body diff --git a/renderer/monitor.js b/src/renderer/script/monitor.js similarity index 97% rename from renderer/monitor.js rename to src/renderer/script/monitor.js index 7243516..782f7eb 100644 --- a/renderer/monitor.js +++ b/src/renderer/script/monitor.js @@ -8,7 +8,7 @@ var lastMeasureTimes = []; // Initialize/Start monitor only after the window is ready to show (-> call this via IPC) ipcRenderer.on('init-monitor', (flag) => { - console.log('init monitor') + console.log('Received init-monitor via ipcRenderer') document.getElementById('info').innerText = 'Host: ' + os.hostname() + diff --git a/src/renderer/style/main.css b/src/renderer/style/main.css new file mode 100644 index 0000000..1810aa4 --- /dev/null +++ b/src/renderer/style/main.css @@ -0,0 +1,122 @@ + +html, body { + height: 100%; +} + +body { + font: caption; + margin: 0; + display: flex; + flex-flow: column; +} + +button { + background: dodgerblue; + color: white; + border-radius: 5px; + border: none; + font-size: 20px; + outline: none; +} + +input { + font-size: 20px; + border-radius: 5px; + border: 1px solid silver; + padding: 0 10px; +} + +input::placeholder { + color: lightgray; +} + +header { + background: lightgray; + display: flex; + padding: 10px; + font-weight: bold; + border-bottom: 1px solid silver; + box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.1); +} + +#show-modal { + padding: 0px 12px 5px; + margin-right: 10px; + font-size: 30px; +} + +#search { + flex-grow: 1; +} + +main { + flex-grow: 1; + overflow-y: scroll; +} + +#no-items { + font-weight: bold; + color: silver; + text-align: center; + width: 100%; + position: absolute; + top: 100px; + z-index: -1; +} + +#modal { + position: absolute; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.85); + display: flex; + align-items: center; + align-content: center; + flex-wrap: wrap; + display: none; +} + +#url { + flex-grow: 1; + width: 100%; + margin: 0 25px 15px; + padding: 10px; +} + +#modal button { + padding: 10px; +} + +#close-modal { + background: white; + color: black; + margin-left: 15px; +} + +#add-item { + margin-left: 25px; +} + +.read-item { + display: flex; + align-items: center; + align-content: center; + border-bottom: lightgray 2px solid; + background: #FAFAFA; + padding: 10px; + border-left: 10px solid lightgray; + -webkit-user-select: none; +} + +.read-item:hover { + background-color: #EEE; +} + +.read-item.selected { + border-left-color: dodgerblue; +} + +.read-item img { + width: 20%; + margin-right: 25px; +} diff --git a/renderer/monitor.css b/src/renderer/style/monitor.css similarity index 100% rename from renderer/monitor.css rename to src/renderer/style/monitor.css