diff --git a/contextAPI.js b/contextAPI.js
index 568d22c..f371b6e 100644
--- a/contextAPI.js
+++ b/contextAPI.js
@@ -3,5 +3,7 @@
const {contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld("api", {
- openFile : async () => ipcRenderer.invoke("WindowManager::openFile")
+ openFileDialog : () => ipcRenderer.send("WindowManager::openFileDialog"),
+ 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/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/Ation.js b/src/Ation.js
index 43852cb..a77ab50 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(/^\./, "");
@@ -28,28 +36,32 @@ class Ation {
});
}
- async openFile() {
+ async openFile(filePath = null) {
if (!this.windowManager.mainWindow)
return null;
- const result = await dialog.showOpenDialog(this.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);
- 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 11a6967..5a0ca7b 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");
@@ -18,7 +17,8 @@ class WindowManager {
app.whenReady().then(() => this.init());
- ipcMain.handle("WindowManager::openFile", async () => this.app.openFile());
+ ipcMain.on("WindowManager::openFileDialog", () => this.app.openFile());
+ ipcMain.on("WindowManager::openFile", (_, path) => this.app.openFile(path));
}
init() {
@@ -28,6 +28,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/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/Ation.js b/src/ui/src/components/Ation.js
index bb3562f..b2ae917 100644
--- a/src/ui/src/components/Ation.js
+++ b/src/ui/src/components/Ation.js
@@ -18,31 +18,39 @@ 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.onFileOpen(presentation => {
+ const [basePath, slideDeck] = presentation;
+ if (!slideDeck)
+ return;
+ setBasePath(basePath);
+ setDeck(slideDeck);
+ });
+ }, []);
+
+ const openFile = () => {
+ window.api.openFileDialog();
}
- if (deck.length < 1)
- return ;
-
return (
-
-
-
-
-
+ <>
+ {deck.length < 1 ?
+
+ : (
+
+
+
+
+
+ )}
+ >
);
};
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;
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 (
);
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