From f54ba7c242c069f2b281e8604ed7a5d46e2969fd Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Sun, 2 Oct 2022 11:04:13 +0200 Subject: [PATCH 1/5] added aiton logo to toolbar --- package-lock.json | 2 +- package.json | 13 +++++++++++-- src/ui/src/assets/css/_toolbar.scss | 6 ++++++ src/ui/src/assets/images/logo_ation.svg | 16 ++++++++++++++++ src/ui/src/components/Toolbar.js | 5 ++++- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 src/ui/src/assets/images/logo_ation.svg diff --git a/package-lock.json b/package-lock.json index 0ce05b1..ea128c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ation", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b5538a1..34f337a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ation", - "version": "0.1.0", + "version": "0.2.0", "description": "a simple presentation software", "main": "main.js", "scripts": { @@ -35,7 +35,16 @@ "dmg" ], "icon": "assets/app.icns", - "darkModeSupport": true + "darkModeSupport": true, + "fileAssociations": [ + { + "ext": [ + "md" + ], + "description": "Markdown files", + "role": "Viewer" + } + ] }, "dmg": { "background": "assets/dmg_background.png", diff --git a/src/ui/src/assets/css/_toolbar.scss b/src/ui/src/assets/css/_toolbar.scss index 52f02dd..36d4a37 100644 --- a/src/ui/src/assets/css/_toolbar.scss +++ b/src/ui/src/assets/css/_toolbar.scss @@ -23,4 +23,10 @@ margin: 0; } } + + > svg { + max-height: 90%; + width: auto; + margin: 0 2rem; + } } \ No newline at end of file diff --git a/src/ui/src/assets/images/logo_ation.svg b/src/ui/src/assets/images/logo_ation.svg new file mode 100644 index 0000000..4681f8f --- /dev/null +++ b/src/ui/src/assets/images/logo_ation.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/ui/src/components/Toolbar.js b/src/ui/src/components/Toolbar.js index 0cfc5c5..1ee8616 100644 --- a/src/ui/src/components/Toolbar.js +++ b/src/ui/src/components/Toolbar.js @@ -5,6 +5,8 @@ import {Folder2Open, Cast, InfoCircle} from "react-bootstrap-icons"; import SlideContext from "../shared/SlideContext"; import Mode from "../models/Mode"; +import {ReactComponent as Logo} from "../assets/images/logo_ation.svg"; + const Toolbar = ({openFile, setShowTips}) => { const {setMode, setSlide} = useContext(SlideContext); @@ -16,8 +18,9 @@ const Toolbar = ({openFile, setShowTips}) => { return ( ); From 4cbc2bf38408133487746e558c7eb666b2713dff Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Sun, 2 Oct 2022 11:13:43 +0200 Subject: [PATCH 2/5] made opening files asynchronous between render and bg process addresses #2 --- contextAPI.js | 3 ++- src/Ation.js | 8 ++++++++ src/WindowManager.js | 16 ++++++++++++---- src/ui/src/components/Ation.js | 19 ++++++++++++------- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/contextAPI.js b/contextAPI.js index 568d22c..c8d17fb 100644 --- a/contextAPI.js +++ b/contextAPI.js @@ -3,5 +3,6 @@ const {contextBridge, ipcRenderer} = require("electron"); contextBridge.exposeInMainWorld("api", { - openFile : async () => ipcRenderer.invoke("WindowManager::openFile") + openFileDialog : () => ipcRenderer.send("WindowManager::openFileDialog"), + openFile : callback => ipcRenderer.on("Ation::openFile", (_, presentation) => callback(presentation)) }); \ No newline at end of file diff --git a/src/Ation.js b/src/Ation.js index 43852cb..78caad7 100644 --- a/src/Ation.js +++ b/src/Ation.js @@ -10,14 +10,22 @@ const {parser} = require("./Parser"); class Ation { windowManager; mainMenu; + fileToOpen; constructor() { if (Ation.Instances > 0) throw new Error("Only one Instance of Ation possible"); + this.fileToOpen = null; this.windowManager = new WindowManager(this); this.mainMenu = new MainMenu(this); + app.on("open-file", (_, path) => { + this.fileToOpen = path; + }); + app.whenReady().then(async () => { + if (this.fileToOpen) + dialog.showMessageBox(this.windowManager.mainWindow, {message : this.fileToOpen}); protocol.registerFileProtocol("slideimg", (request, callback) => { const uri = request.url.replace(/^slideimg:\/\//, ""); const extension = path.extname(uri).replace(/^\./, ""); diff --git a/src/WindowManager.js b/src/WindowManager.js index 11a6967..558e510 100644 --- a/src/WindowManager.js +++ b/src/WindowManager.js @@ -1,9 +1,8 @@ "use strict"; -const {app, BrowserWindow, ipcMain, dialog, Menu} = require("electron"); -const path = require("path"); -const fs = require("fs/promises"); -const util = require("util"); +const {app, BrowserWindow, ipcMain, globalShortcut} = require("electron"); +const path = require("path"); +const util = require("util"); const {isDevelopment} = require("./Util"); @@ -28,6 +27,15 @@ class WindowManager { titleBarStyle : "hiddenInset" }); + if (isDevelopment) { + globalShortcut.register("CommandOrControl+I", () => { + this.mainWindow.toggleDevTools(); + }); + globalShortcut.register("CommandOrControl+R", () => { + this.mainWindow.reload(); + }); + } + if (isDevelopment()) this.mainWindow.loadURL("http://localhost:3000"); else diff --git a/src/ui/src/components/Ation.js b/src/ui/src/components/Ation.js index bb3562f..5945a0f 100644 --- a/src/ui/src/components/Ation.js +++ b/src/ui/src/components/Ation.js @@ -18,13 +18,18 @@ const Ation = () => { const [basePath, setBasePath] = useState(""); const [showTips, setShowTips] = useState(false); - const openFile = async () => { - const [basePath, slideDeck] = await window.api.openFile(); - - if (!slideDeck) - return; - setBasePath(basePath); - setDeck(slideDeck); + useEffect(() => { + window.api.openFile(presentation => { + const [basePath, slideDeck] = presentation; + if (!slideDeck) + return; + setBasePath(basePath); + setDeck(slideDeck); + }); + }, []); + + const openFile = () => { + window.api.openFileDialog(); } if (deck.length < 1) From e6449e78dd0eea8c226659e00ee5ba8ca4605b6c Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Sun, 2 Oct 2022 11:17:48 +0200 Subject: [PATCH 3/5] added "File -> Open" menu item to main menu addresses #2 --- src/Ation.js | 5 +++-- src/MainMenu.js | 10 ++++++++++ src/WindowManager.js | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Ation.js b/src/Ation.js index 78caad7..9b58aa3 100644 --- a/src/Ation.js +++ b/src/Ation.js @@ -40,7 +40,7 @@ class Ation { if (!this.windowManager.mainWindow) return null; - const result = await dialog.showOpenDialog(this.mainWindow, { + const result = await dialog.showOpenDialog(this.windowManager.mainWindow, { title : "open file", filters : [ { @@ -57,7 +57,8 @@ class Ation { const basePath = path.dirname(result.filePaths[0]); const data = parser(fileContents); - return [basePath, data]; + + this.windowManager.mainWindow.send("Ation::openFile", [basePath, data]); } } Ation.Instances = 0; diff --git a/src/MainMenu.js b/src/MainMenu.js index ba37ac9..86ddf79 100644 --- a/src/MainMenu.js +++ b/src/MainMenu.js @@ -15,6 +15,16 @@ class MainMenu { ...(process.platform === "darwin" ? [{ role : "appMenu" }] : []), + { + label : "File", + submenu : [ + { + label : "Open", + accelerator : "CommandOrControl+O", + click : () => this.app.openFile() + } + ] + }, { role : "windowMenu" } diff --git a/src/WindowManager.js b/src/WindowManager.js index 558e510..e87e035 100644 --- a/src/WindowManager.js +++ b/src/WindowManager.js @@ -17,7 +17,7 @@ class WindowManager { app.whenReady().then(() => this.init()); - ipcMain.handle("WindowManager::openFile", async () => this.app.openFile()); + ipcMain.on("WindowManager::openFileDialog", () => this.app.openFile()); } init() { From 5022bdf3186f12c1e9af4df0a11d529fe88f8810 Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Sun, 2 Oct 2022 11:19:21 +0200 Subject: [PATCH 4/5] open file keyboard shortcut is now handled by a main menu accelerator --- src/ui/src/components/KeyboardControl.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ui/src/components/KeyboardControl.js b/src/ui/src/components/KeyboardControl.js index 4f1212f..4dfb240 100644 --- a/src/ui/src/components/KeyboardControl.js +++ b/src/ui/src/components/KeyboardControl.js @@ -19,11 +19,6 @@ const KeyboardControl = ({openFile, mode, setMode, deck, setShowTips}) => { const keyHandler = event => { switch(event.key) { - case "o": - if (!event.metaKey && !event.ctrlKey) - return; - openFile(); - break; case "Escape": setShowTips(false); break; From ca9754cd10679ed47711e2b97be1061a7ddb0958 Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Sun, 2 Oct 2022 12:03:09 +0200 Subject: [PATCH 5/5] added file dropping support through `withDrop` hoc addresses #2 --- contextAPI.js | 3 +- src/Ation.js | 31 ++++++++------- src/WindowManager.js | 1 + src/ui/src/components/Ation.js | 35 ++++++++-------- src/ui/src/higherOrderComponents/withDrop.js | 42 ++++++++++++++++++++ src/ui/src/index.js | 7 +++- 6 files changed, 86 insertions(+), 33 deletions(-) create mode 100644 src/ui/src/higherOrderComponents/withDrop.js diff --git a/contextAPI.js b/contextAPI.js index c8d17fb..f371b6e 100644 --- a/contextAPI.js +++ b/contextAPI.js @@ -4,5 +4,6 @@ const {contextBridge, ipcRenderer} = require("electron"); contextBridge.exposeInMainWorld("api", { openFileDialog : () => ipcRenderer.send("WindowManager::openFileDialog"), - openFile : callback => ipcRenderer.on("Ation::openFile", (_, presentation) => callback(presentation)) + onFileOpen : callback => ipcRenderer.on("Ation::openFile", (_, presentation) => callback(presentation)), + openFile : filePath => ipcRenderer.send("WindowManager::openFile", filePath) }); \ No newline at end of file diff --git a/src/Ation.js b/src/Ation.js index 9b58aa3..a77ab50 100644 --- a/src/Ation.js +++ b/src/Ation.js @@ -36,25 +36,28 @@ class Ation { }); } - async openFile() { + async openFile(filePath = null) { if (!this.windowManager.mainWindow) return null; - const result = await dialog.showOpenDialog(this.windowManager.mainWindow, { - title : "open file", - filters : [ - { - name : "Markdown files", - extensions : [".md"] - } - ] - }); + if (!filePath) { + const result = await dialog.showOpenDialog(this.windowManager.mainWindow, { + title : "open file", + filters : [ + { + name : "Markdown files", + extensions : [".md"] + } + ] + }); - if (result.canceled || result.filePaths.length < 1) - return null; + if (result.canceled || result.filePaths.length < 1) + return; + filePath = result.filePaths[0]; + } - const fileContents = await fs.readFile(result.filePaths[0], {encoding : "utf-8"}); - const basePath = path.dirname(result.filePaths[0]); + const fileContents = await fs.readFile(filePath, {encoding : "utf-8"}); + const basePath = path.dirname(filePath); const data = parser(fileContents); diff --git a/src/WindowManager.js b/src/WindowManager.js index e87e035..5a0ca7b 100644 --- a/src/WindowManager.js +++ b/src/WindowManager.js @@ -18,6 +18,7 @@ class WindowManager { app.whenReady().then(() => this.init()); ipcMain.on("WindowManager::openFileDialog", () => this.app.openFile()); + ipcMain.on("WindowManager::openFile", (_, path) => this.app.openFile(path)); } init() { diff --git a/src/ui/src/components/Ation.js b/src/ui/src/components/Ation.js index 5945a0f..b2ae917 100644 --- a/src/ui/src/components/Ation.js +++ b/src/ui/src/components/Ation.js @@ -19,7 +19,7 @@ const Ation = () => { const [showTips, setShowTips] = useState(false); useEffect(() => { - window.api.openFile(presentation => { + window.api.onFileOpen(presentation => { const [basePath, slideDeck] = presentation; if (!slideDeck) return; @@ -32,22 +32,25 @@ const Ation = () => { window.api.openFileDialog(); } - if (deck.length < 1) - return ; - return ( - -
- - -
- -
- -
- - -
+ <> + {deck.length < 1 ? + + : ( + +
+ + +
+ +
+ +
+ + +
+ )} + ); }; diff --git a/src/ui/src/higherOrderComponents/withDrop.js b/src/ui/src/higherOrderComponents/withDrop.js new file mode 100644 index 0000000..ed5734c --- /dev/null +++ b/src/ui/src/higherOrderComponents/withDrop.js @@ -0,0 +1,42 @@ +import React, {useState} from "react"; + +const extension = filename => { + const parts = filename.split('.'); + + return parts[parts.length - 1]; +}; + +const withDrop = Component => () => { + const [drag, setDrag] = useState(false); + + const handleDragOver = event => { + event.preventDefault(); + event.stopPropagation(); + setDrag(true); + }; + + const handleDragLeave = event => { + event.preventDefault(); + event.stopPropagation(); + setDrag(false); + } + + const handleDrop = event => { + event.preventDefault(); + event.stopPropagation(); + const file = event.dataTransfer?.items[0].getAsFile(); + if (extension(file.name.toLowerCase()) === "md") { + window.api.openFile(file.path); + } + setDrag(false); + }; + + return ( +
+ +
+ ); +}; + + +export default withDrop; \ No newline at end of file diff --git a/src/ui/src/index.js b/src/ui/src/index.js index 2db8a76..a545a68 100644 --- a/src/ui/src/index.js +++ b/src/ui/src/index.js @@ -1,13 +1,16 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import Ation from "./components/Ation"; +import Ation from "./components/Ation"; +import withDrop from "./higherOrderComponents/withDrop"; import "./assets/css/ation.scss"; +const App = withDrop(Ation); + const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + ); \ No newline at end of file