diff --git a/README.md b/README.md index 731f8fc..ab3e672 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Marvin
- +

Document manager for Institute for Art Technology and Conservation at Germanic National Museum Nuremberg

diff --git a/electron-builder.json b/electron-builder.json index 6c17fbf..bd2f571 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -2,18 +2,18 @@ "appId": "org.nasarek.marvin", "win": { "target": "NSIS", - "icon": "marvin256x256.ico" + "icon": "src/assets/images/marvin256x256.ico" }, "nsis": { "oneClick": false, - "installerIcon": "marvin256x256.ico", - "uninstallerIcon": "marvin256x256.ico", + "installerIcon": "src/assets/images/marvin256x256.ico", + "uninstallerIcon": "src/assets/images/marvin256x256.ico", "allowToChangeInstallationDirectory": true }, "linux": { "target": "deb", "category": "Utility", - "icon": "marvin256x256.png" + "icon": "src/assets/images/marvin256x256.png" }, "files": [ "*.js", @@ -23,10 +23,5 @@ "./dist/main.js", "./dist/main.js.LICENSE.txt", "node_modules" - ], - "extraResources": [{ - "from": "resources/files", - "to": "files" - } ] } diff --git a/main.js b/main.js index 9331189..e2e299f 100644 --- a/main.js +++ b/main.js @@ -3,18 +3,41 @@ // Import parts of electron to use const {app, BrowserWindow, dialog, ipcMain, Menu, screen, Tray} = require('electron') const openAboutWindow = require('about-window').default + +// Modules + +const FileLoader = require('./src/FileLoader'); const path = require('path'); -const fs = require('fs'); +const Store = require('electron-store'); const url = require('url'); // 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, tray; let devBrowserProperties = {}; +let devWebPreferences = {}; -const ASSET_PATH = app.isPackaged - ? path.resolve(process.resourcesPath, 'app/src/assets') - : path.resolve(__dirname, 'src/assets'); +const fileLoader = new FileLoader(); + +const configDirPath = fileLoader.getConfigDirPath(); +const imageDirPath = fileLoader.getImageDirPath(); +const templateDirPath = fileLoader.getTemplateDirPath(); + +// Create store. +const store = new Store() +store.set({ + "configDirPath": configDirPath, + "imageDirPath": imageDirPath, + "templateDirPath": templateDirPath, +}); + + +// CONFIGJSON UNSET +/* +store.set( + "configJson", '', +) +*/ let trayMenu = Menu.buildFromTemplate([ { @@ -32,10 +55,10 @@ let trayMenu = Menu.buildFromTemplate([ } ]) - const RESSOURCE_PATH= fs.existsSync(ASSET_PATH) ? ASSET_PATH : 'resources/files' ; + function createTray() { - tray = new Tray(path.resolve(RESSOURCE_PATH, 'images/marvin16x16.png')); + tray = new Tray(path.resolve(imageDirPath, 'marvin16x16.png')); tray.setToolTip('Marvin') tray.setContextMenu(trayMenu); tray.on('click', function() { @@ -73,11 +96,6 @@ async function handleFileOpen() { } } -// Constants - -const getAssetPath = async (assetPath) => { - return path.resolve(ASSET_PATH, assetPath); -}; // Build main menu from file. let mainMenu = Menu.buildFromTemplate( @@ -140,7 +158,7 @@ let mainMenu = Menu.buildFromTemplate( label: 'Über Marvin', click: () => { openAboutWindow({ - icon_path: path.join(__dirname, 'marvin.ico'), + icon_path: path.resolve(imageDirPath, 'marvin.ico'), bug_report_url: 'mailto:r.nasarek@gnm.de', bug_link_text: 'Einen Fehler melden', license: 'MIT', @@ -159,14 +177,16 @@ function createWindow(dimensions) { if (dev !== true) { devBrowserProperties = { fullscreenable: false, - resizable: true, + resizable: false, + } + devWebPreferences = { + devTools: false } - } // Create tray icon. createTray() - let appWidth = 400; - let appHeight = 720; + let appWidth = 520; + let appHeight = 860; let xPosition = (dimensions.width - appWidth) // Create the browser window. mainWindow = new BrowserWindow({ @@ -175,12 +195,13 @@ function createWindow(dimensions) { y: 0, width: appWidth, height: appHeight, - icon: path.resolve(RESSOURCE_PATH, 'images/marvin.ico'), + icon: path.resolve(imageDirPath, 'marvin.ico'), show: false, webPreferences: { + ...devWebPreferences, preload: path.join(__dirname, 'preload.js'), nodeIntegration: true, - contextIsolation: false + contextIsolation: false, } }) @@ -223,7 +244,6 @@ function createWindow(dimensions) { .catch(err => console.log('Error loading React DevTools: ', err)) mainWindow.webContents.openDevTools() } - mainWindow.webContents.openDevTools() }) mainWindow.on('close', function (e) { @@ -247,10 +267,6 @@ function createWindow(dimensions) { // Some APIs can only be used after this event occurs. app.on('ready', () => { ipcMain.handle('dialog:openFile', handleFileOpen) - ipcMain.handle('assetPath:getAssetPath', async (event, assetPath) => { - return await getAssetPath(assetPath) - }) - const display = screen.getPrimaryDisplay(); const dimensions = display.size; createWindow(dimensions); diff --git a/package.json b/package.json index 242e438..78247e8 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .", "start": "cross-env NODE_ENV=development webpack serve --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development", "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production", - "pack:installer": "electron-builder build -l", + "pack:installer": "electron-builder build -w", "package": "npm run build", - "postpackage": "electron-packager ./ --ignore \"(resources|builds|installer)\" --out=./builds --overwrite --platform linux --icon marvin.ico" + "postpackage": "electron-packager ./ --ignore \"(resources|builds|installer)\" --out=./builds --overwrite --platform win32 --icon src/assets/images/marvin.ico" }, "dependencies": { "@emotion/react": "^11.10.0", @@ -44,6 +44,7 @@ "@mui/material": "^5.10.2", "about-window": "^1.15.2", "docx-templates": "^4.9.2", + "electron-store": "^8.1.0", "image-resize-compress": "^1.0.8", "postcss": "^8.4.16", "react": "^18.2.0", diff --git a/src/DocCreator.js b/src/DocCreator.js index a636a0d..c971a6d 100644 --- a/src/DocCreator.js +++ b/src/DocCreator.js @@ -3,9 +3,10 @@ import createReport from 'docx-templates'; import {mkdir} from 'node:fs/promises'; import path from 'path'; import replaceSpecialCharacters from "replace-special-characters"; +import Store from 'electron-store' import {parseString, Builder} from 'xml2js'; -import {FileLoader} from "./FileLoader"; +import fs from "fs"; //////////////////// @@ -24,12 +25,11 @@ class DocCreator { // Get DateTime. this.today = new Date(); - // FileLoader - this.fileLoader = new FileLoader() - - // Get config file and parse to json. - this.configFile = this.fileLoader.getConfigFile() + // Electron config storage + this.store = new Store(); + // Get config json + this.configJson = this.store.get('configJson') // Choose the template for selected document type. switch (objectData.dokumenttyp) { case 'rp': @@ -68,14 +68,12 @@ class DocCreator { const folderName = this.normObjectId + '__' + this.normTitle - this.objectPath = path.join(this.configFile.rootDir, folderName); - + this.objectPath = path.join(this.configJson.rootDir, folderName); this.documentPath = path.join(this.objectPath, this.documentInfo.documentType, objectData.datum); this.filename = this.normObjectId + '__' + this.normTitle + '__' + this.objectData.datum + '__' + this.objectData.dokumenttyp; this.filenameWithExtension = this.filename + '.docx' this.temporaryWorkDirPath = path.join(this.objectPath, 'werkstatt'); - } // Fills the docx template. @@ -87,7 +85,7 @@ class DocCreator { if (this.objectData.httpStatus === 200) { try { // Read template. - const template = fs.readFileSync(path.resolve(this.fileLoader.getTemplateDir(), this.documentInfo.templateFile)); + const template = fs.readFileSync(path.resolve(this.store.get('templateDirPath'), this.documentInfo.templateFile)); // Create report. buffer = await createReport({ diff --git a/src/FileLoader.js b/src/FileLoader.js index 3f65e72..1077690 100644 --- a/src/FileLoader.js +++ b/src/FileLoader.js @@ -1,35 +1,54 @@ -import fs from "fs"; -import path from "path"; - +const path = require('path'); +const fs = require('fs'); +const {app} = require('electron'); class FileLoader { assetPath; - devAssetPath = 'src/assets'; - buildAssetPath = 'resources/app/src/assets'; - packedAssetPath = 'resources/files/'; + assetBuildPath; + assetInstallerPath; + assetDevPath; - getPackedStructure() { - if (fs.existsSync(this.packedAssetPath)) { - this.assetPath = this.packedAssetPath; - } else if (fs.existsSync(this.buildAssetPath)) { - this.assetPath = this.packedAssetPath; + constructor(props) { + this.getAssetPath() + } + + getAssetPath() { + this.assetBuildPath = path.resolve(__dirname, '../assets'); + this.assetInstallerPath = path.resolve(__dirname, 'assets'); + this.assetDevPath = path.resolve(app.getAppPath(), 'assets'); + + if (fs.existsSync(this.assetInstallerPath)) { + this.assetPath = this.assetInstallerPath + } else if (fs.existsSync(this.assetBuildPath)) { + this.assetPath = this.assetBuildPath } else { - this.assetPath = this.devAssetPath; + this.assetPath = this.assetDevPath } } getConfigFile() { - this.getPackedStructure() - console.log(this.assetPath) - const ConfigFile = fs.readFileSync(path.resolve(this.assetPath, 'config/config.json')) - return JSON.parse(ConfigFile.toString()) + const fileBuffer = fs.readFileSync(path.resolve(this.assetPath, 'config/config.json')) + return JSON.parse(fileBuffer.toString()) } - getTemplateDir() { + getTemplateDirPath() { return path.resolve(this.assetPath, 'templates'); - } + getConfigDirPath() { + return path.resolve(this.assetPath, 'config'); + } + getImageDirPath() { + return path.resolve(this.assetPath, 'images'); + } + + whereAmI () { + console.log(__dirname , '__dirname'); + console.log(process.resourcesPath, 'resourcePath'); + console.log(process.cwd(), 'cwd'); + console.log(process.env.PWD, 'pwd'); + console.log(app.getAppPath(), 'getAppPath') + } + } -export {FileLoader} - +module.exports = FileLoader; diff --git a/src/ObjektkatalogApi.js b/src/ObjektkatalogApi.js index 798eed2..6c8aa85 100644 --- a/src/ObjektkatalogApi.js +++ b/src/ObjektkatalogApi.js @@ -86,7 +86,7 @@ class ObjektkatalogApi { httpStatus: 404, } this.visibility = false - return [this.receivedData, this.visibility] + return this.receivedData } case 408: // No response, no data form. @@ -95,7 +95,7 @@ class ObjektkatalogApi { httpStatus: 408, } this.visibility = false - return [this.receivedData, this.visibility] + return this.receivedData case 503: // Objektkatalog not reachable. this.receivedData = { @@ -103,7 +103,7 @@ class ObjektkatalogApi { httpStatus: 503, } this.visibility = false - return [this.receivedData, this.visibility] + return this.receivedData default: // Any other error this.receivedData = { @@ -111,7 +111,7 @@ class ObjektkatalogApi { httpStatus: 500, } this.visibility = false - return [this.receivedData, this.visibility] + return this.receivedData } } else { // No ObjectId, no data form. @@ -219,7 +219,7 @@ class ObjektkatalogApi { httpStatus: 200, } this.visibility = true - return [this.receivedData, this.visibility] + return this.receivedData } } diff --git a/src/assets/config/config.json b/src/assets/config/config.json deleted file mode 100644 index b0b532a..0000000 --- a/src/assets/config/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "/home/rbrt/Schreibtisch/marvin" -} \ No newline at end of file diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css index d23db39..cffc2ac 100644 --- a/src/assets/css/styles.css +++ b/src/assets/css/styles.css @@ -178,10 +178,12 @@ label { opacity: .5; } -.loading-icon + .log { font-size: 0.75em; height: 5em; + position: fixed; + top: 0 } .nav-icon svg { diff --git a/marvin.ico b/src/assets/images/marvin.ico similarity index 100% rename from marvin.ico rename to src/assets/images/marvin.ico diff --git a/marvin256x256.ico b/src/assets/images/marvin256x256.ico similarity index 100% rename from marvin256x256.ico rename to src/assets/images/marvin256x256.ico diff --git a/marvin256x256.png b/src/assets/images/marvin256x256.png similarity index 100% rename from marvin256x256.png rename to src/assets/images/marvin256x256.png diff --git a/src/components/DataForm.js b/src/components/DataForm.js index d97a112..1e2cc45 100644 --- a/src/components/DataForm.js +++ b/src/components/DataForm.js @@ -3,6 +3,7 @@ import {Galary} from './Galary' // Modules import {DocCreator} from "../DocCreator"; +import Store from 'electron-store'; // React import React from 'react' @@ -18,6 +19,9 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState // Variables let log; + // Store + const store = new Store() + // Handlers // Change handler response to every keystroke! @@ -101,15 +105,29 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState // Fill the template and save it. const fillTemplateClickHandler = async (e) => { e.preventDefault(); - const docCreator = new DocCreator(logState, objectData) - log = await docCreator.fillTemplate(); - const metsMods = await docCreator.createMetsMods(); + const configJson = store.get('configJson') + if (configJson.rootDir == null) { + setLogState( + { + log: { + status: 'red', + message: 'Keine Hauptverzeichnis für die Ordnerstruktur angegeben!', + tip: 'Bitte zu den Einstellungen wechseln, und dort ein Verzeichnis auswählen!' + }, + logStatus: 'active' + } + ) + } else { + const docCreator = new DocCreator(logState, objectData) + log = await docCreator.fillTemplate(); + await docCreator.createMetsMods(); - // Set new state of log div with message and visibility class - setLogState({ - log: log, - logStatus: 'active' - }); + // Set new state of log div with message and visibility class + setLogState({ + log: log, + logStatus: 'active' + }); + } } // Renders data form @@ -118,101 +136,102 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + -
-
- - -
-
- - -
+
+
+ + +
+
+ + +
+ >Dokument erstellen +
) diff --git a/src/routes/settings.js b/src/routes/settings.js index 6f5c787..2e6875a 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -1,9 +1,5 @@ // Modules -import {FileLoader} from "../FileLoader"; - -// Electron -const electron = window.require('electron'); -const ipcRenderer = electron.ipcRenderer; +import Store from 'electron-store'; // Icons import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; @@ -13,7 +9,6 @@ import React, {useState} from "react"; import {NavLink} from "react-router-dom"; import {Input} from "../components/Input"; import EditIcon from "@mui/icons-material/Edit"; -import {writeFile} from "fs"; //////////////////// @@ -21,16 +16,11 @@ import {writeFile} from "fs"; //////////////////// export default function Settings() { - const fileLoader = new FileLoader() - const initialConfig = fileLoader.getConfigFile() - console.log(initialConfig) - const getConfigPath = async (path) => { - return await ipcRenderer.invoke('assetPath:getAssetPath', path) - } - + const store = new Store() + const initialConfigJson = store.get('configJson') // States const [configFile, setConfigFile] = useState( - initialConfig + initialConfigJson ) const [restart, setRestart] = useState( false @@ -46,25 +36,18 @@ export default function Settings() { rootDir: rootDirFromWindow } }) - await saveInputClickHandler(rootDirFromWindow) + saveInputClickHandler(rootDirFromWindow) } // Handler - async function saveInputClickHandler(rootDirFromWindow) { + function saveInputClickHandler(rootDirFromWindow) { let config2Safe = { rootDir: rootDirFromWindow } if (rootDirFromWindow) { - let message; - const configPath = await getConfigPath('config/config.json') - await writeFile(configPath, JSON.stringify(config2Safe, null, 2), (err) => { - if (err) { - message = err; - } else { - message = 'saved file' - } - return message - }) + store.set( + 'configJson', config2Safe + ) setRestart(true) return restart; }