first working packed version

This commit is contained in:
rnsrk 2022-09-25 13:08:57 +02:00
parent 11b0d9fb6c
commit 8fc852b2b4
14 changed files with 227 additions and 197 deletions

View file

@ -1,7 +1,7 @@
# Marvin # Marvin
<div style="text-align: center;"> <div style="text-align: center;">
<img src="marvin.ico"/> <img src="src/assets/images/marvin.ico"/>
<p> <p>
Document manager for Institute for Art Technology and Conservation at <a href="https://gnm.de" _targer="blank">Germanic National Museum Nuremberg </a> Document manager for Institute for Art Technology and Conservation at <a href="https://gnm.de" _targer="blank">Germanic National Museum Nuremberg </a>
</p> </p>

View file

@ -2,18 +2,18 @@
"appId": "org.nasarek.marvin", "appId": "org.nasarek.marvin",
"win": { "win": {
"target": "NSIS", "target": "NSIS",
"icon": "marvin256x256.ico" "icon": "src/assets/images/marvin256x256.ico"
}, },
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,
"installerIcon": "marvin256x256.ico", "installerIcon": "src/assets/images/marvin256x256.ico",
"uninstallerIcon": "marvin256x256.ico", "uninstallerIcon": "src/assets/images/marvin256x256.ico",
"allowToChangeInstallationDirectory": true "allowToChangeInstallationDirectory": true
}, },
"linux": { "linux": {
"target": "deb", "target": "deb",
"category": "Utility", "category": "Utility",
"icon": "marvin256x256.png" "icon": "src/assets/images/marvin256x256.png"
}, },
"files": [ "files": [
"*.js", "*.js",
@ -23,10 +23,5 @@
"./dist/main.js", "./dist/main.js",
"./dist/main.js.LICENSE.txt", "./dist/main.js.LICENSE.txt",
"node_modules" "node_modules"
],
"extraResources": [{
"from": "resources/files",
"to": "files"
}
] ]
} }

62
main.js
View file

@ -3,18 +3,41 @@
// Import parts of electron to use // Import parts of electron to use
const {app, BrowserWindow, dialog, ipcMain, Menu, screen, Tray} = require('electron') const {app, BrowserWindow, dialog, ipcMain, Menu, screen, Tray} = require('electron')
const openAboutWindow = require('about-window').default const openAboutWindow = require('about-window').default
// Modules
const FileLoader = require('./src/FileLoader');
const path = require('path'); const path = require('path');
const fs = require('fs'); const Store = require('electron-store');
const url = require('url'); const url = require('url');
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow, tray; let mainWindow, tray;
let devBrowserProperties = {}; let devBrowserProperties = {};
let devWebPreferences = {};
const ASSET_PATH = app.isPackaged const fileLoader = new FileLoader();
? path.resolve(process.resourcesPath, 'app/src/assets')
: path.resolve(__dirname, 'src/assets'); 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([ 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() { function createTray() {
tray = new Tray(path.resolve(RESSOURCE_PATH, 'images/marvin16x16.png')); tray = new Tray(path.resolve(imageDirPath, 'marvin16x16.png'));
tray.setToolTip('Marvin') tray.setToolTip('Marvin')
tray.setContextMenu(trayMenu); tray.setContextMenu(trayMenu);
tray.on('click', function() { 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. // Build main menu from file.
let mainMenu = Menu.buildFromTemplate( let mainMenu = Menu.buildFromTemplate(
@ -140,7 +158,7 @@ let mainMenu = Menu.buildFromTemplate(
label: 'Über Marvin', label: 'Über Marvin',
click: () => { click: () => {
openAboutWindow({ openAboutWindow({
icon_path: path.join(__dirname, 'marvin.ico'), icon_path: path.resolve(imageDirPath, 'marvin.ico'),
bug_report_url: 'mailto:r.nasarek@gnm.de', bug_report_url: 'mailto:r.nasarek@gnm.de',
bug_link_text: 'Einen Fehler melden', bug_link_text: 'Einen Fehler melden',
license: 'MIT', license: 'MIT',
@ -159,14 +177,16 @@ function createWindow(dimensions) {
if (dev !== true) { if (dev !== true) {
devBrowserProperties = { devBrowserProperties = {
fullscreenable: false, fullscreenable: false,
resizable: true, resizable: false,
}
devWebPreferences = {
devTools: false
} }
} }
// Create tray icon. // Create tray icon.
createTray() createTray()
let appWidth = 400; let appWidth = 520;
let appHeight = 720; let appHeight = 860;
let xPosition = (dimensions.width - appWidth) let xPosition = (dimensions.width - appWidth)
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
@ -175,12 +195,13 @@ function createWindow(dimensions) {
y: 0, y: 0,
width: appWidth, width: appWidth,
height: appHeight, height: appHeight,
icon: path.resolve(RESSOURCE_PATH, 'images/marvin.ico'), icon: path.resolve(imageDirPath, 'marvin.ico'),
show: false, show: false,
webPreferences: { webPreferences: {
...devWebPreferences,
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false contextIsolation: false,
} }
}) })
@ -223,7 +244,6 @@ function createWindow(dimensions) {
.catch(err => console.log('Error loading React DevTools: ', err)) .catch(err => console.log('Error loading React DevTools: ', err))
mainWindow.webContents.openDevTools() mainWindow.webContents.openDevTools()
} }
mainWindow.webContents.openDevTools()
}) })
mainWindow.on('close', function (e) { mainWindow.on('close', function (e) {
@ -247,10 +267,6 @@ function createWindow(dimensions) {
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.on('ready', () => { app.on('ready', () => {
ipcMain.handle('dialog:openFile', handleFileOpen) ipcMain.handle('dialog:openFile', handleFileOpen)
ipcMain.handle('assetPath:getAssetPath', async (event, assetPath) => {
return await getAssetPath(assetPath)
})
const display = screen.getPrimaryDisplay(); const display = screen.getPrimaryDisplay();
const dimensions = display.size; const dimensions = display.size;
createWindow(dimensions); createWindow(dimensions);

View file

@ -33,9 +33,9 @@
"prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .", "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", "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", "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", "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": { "dependencies": {
"@emotion/react": "^11.10.0", "@emotion/react": "^11.10.0",
@ -44,6 +44,7 @@
"@mui/material": "^5.10.2", "@mui/material": "^5.10.2",
"about-window": "^1.15.2", "about-window": "^1.15.2",
"docx-templates": "^4.9.2", "docx-templates": "^4.9.2",
"electron-store": "^8.1.0",
"image-resize-compress": "^1.0.8", "image-resize-compress": "^1.0.8",
"postcss": "^8.4.16", "postcss": "^8.4.16",
"react": "^18.2.0", "react": "^18.2.0",

View file

@ -3,9 +3,10 @@ import createReport from 'docx-templates';
import {mkdir} from 'node:fs/promises'; import {mkdir} from 'node:fs/promises';
import path from 'path'; import path from 'path';
import replaceSpecialCharacters from "replace-special-characters"; import replaceSpecialCharacters from "replace-special-characters";
import Store from 'electron-store'
import {parseString, Builder} from 'xml2js'; import {parseString, Builder} from 'xml2js';
import {FileLoader} from "./FileLoader"; import fs from "fs";
//////////////////// ////////////////////
@ -24,12 +25,11 @@ class DocCreator {
// Get DateTime. // Get DateTime.
this.today = new Date(); this.today = new Date();
// FileLoader // Electron config storage
this.fileLoader = new FileLoader() this.store = new Store();
// Get config file and parse to json.
this.configFile = this.fileLoader.getConfigFile()
// Get config json
this.configJson = this.store.get('configJson')
// Choose the template for selected document type. // Choose the template for selected document type.
switch (objectData.dokumenttyp) { switch (objectData.dokumenttyp) {
case 'rp': case 'rp':
@ -68,14 +68,12 @@ class DocCreator {
const folderName = this.normObjectId + '__' + this.normTitle 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.documentPath = path.join(this.objectPath, this.documentInfo.documentType, objectData.datum);
this.filename = this.normObjectId + '__' + this.normTitle + '__' + this.objectData.datum + '__' + this.objectData.dokumenttyp; this.filename = this.normObjectId + '__' + this.normTitle + '__' + this.objectData.datum + '__' + this.objectData.dokumenttyp;
this.filenameWithExtension = this.filename + '.docx' this.filenameWithExtension = this.filename + '.docx'
this.temporaryWorkDirPath = path.join(this.objectPath, 'werkstatt'); this.temporaryWorkDirPath = path.join(this.objectPath, 'werkstatt');
} }
// Fills the docx template. // Fills the docx template.
@ -87,7 +85,7 @@ class DocCreator {
if (this.objectData.httpStatus === 200) { if (this.objectData.httpStatus === 200) {
try { try {
// Read template. // 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. // Create report.
buffer = await createReport({ buffer = await createReport({

View file

@ -1,35 +1,54 @@
import fs from "fs"; const path = require('path');
import path from "path"; const fs = require('fs');
const {app} = require('electron');
class FileLoader { class FileLoader {
assetPath; assetPath;
devAssetPath = 'src/assets'; assetBuildPath;
buildAssetPath = 'resources/app/src/assets'; assetInstallerPath;
packedAssetPath = 'resources/files/'; assetDevPath;
getPackedStructure() { constructor(props) {
if (fs.existsSync(this.packedAssetPath)) { this.getAssetPath()
this.assetPath = this.packedAssetPath; }
} else if (fs.existsSync(this.buildAssetPath)) {
this.assetPath = this.packedAssetPath; 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 { } else {
this.assetPath = this.devAssetPath; this.assetPath = this.assetDevPath
} }
} }
getConfigFile() { getConfigFile() {
this.getPackedStructure() const fileBuffer = fs.readFileSync(path.resolve(this.assetPath, 'config/config.json'))
console.log(this.assetPath) return JSON.parse(fileBuffer.toString())
const ConfigFile = fs.readFileSync(path.resolve(this.assetPath, 'config/config.json'))
return JSON.parse(ConfigFile.toString())
} }
getTemplateDir() { getTemplateDirPath() {
return path.resolve(this.assetPath, 'templates'); 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;

View file

@ -86,7 +86,7 @@ class ObjektkatalogApi {
httpStatus: 404, httpStatus: 404,
} }
this.visibility = false this.visibility = false
return [this.receivedData, this.visibility] return this.receivedData
} }
case 408: case 408:
// No response, no data form. // No response, no data form.
@ -95,7 +95,7 @@ class ObjektkatalogApi {
httpStatus: 408, httpStatus: 408,
} }
this.visibility = false this.visibility = false
return [this.receivedData, this.visibility] return this.receivedData
case 503: case 503:
// Objektkatalog not reachable. // Objektkatalog not reachable.
this.receivedData = { this.receivedData = {
@ -103,7 +103,7 @@ class ObjektkatalogApi {
httpStatus: 503, httpStatus: 503,
} }
this.visibility = false this.visibility = false
return [this.receivedData, this.visibility] return this.receivedData
default: default:
// Any other error // Any other error
this.receivedData = { this.receivedData = {
@ -111,7 +111,7 @@ class ObjektkatalogApi {
httpStatus: 500, httpStatus: 500,
} }
this.visibility = false this.visibility = false
return [this.receivedData, this.visibility] return this.receivedData
} }
} else { } else {
// No ObjectId, no data form. // No ObjectId, no data form.
@ -219,7 +219,7 @@ class ObjektkatalogApi {
httpStatus: 200, httpStatus: 200,
} }
this.visibility = true this.visibility = true
return [this.receivedData, this.visibility] return this.receivedData
} }
} }

View file

@ -1,3 +0,0 @@
{
"rootDir": "/home/rbrt/Schreibtisch/marvin"
}

View file

@ -178,10 +178,12 @@ label {
opacity: .5; opacity: .5;
} }
.loading-icon
.log { .log {
font-size: 0.75em; font-size: 0.75em;
height: 5em; height: 5em;
position: fixed;
top: 0
} }
.nav-icon svg { .nav-icon svg {

View file

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

View file

@ -3,6 +3,7 @@ import {Galary} from './Galary'
// Modules // Modules
import {DocCreator} from "../DocCreator"; import {DocCreator} from "../DocCreator";
import Store from 'electron-store';
// React // React
import React from 'react' import React from 'react'
@ -18,6 +19,9 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
// Variables // Variables
let log; let log;
// Store
const store = new Store()
// Handlers // Handlers
// Change handler response to every keystroke! // Change handler response to every keystroke!
@ -101,15 +105,29 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
// Fill the template and save it. // Fill the template and save it.
const fillTemplateClickHandler = async (e) => { const fillTemplateClickHandler = async (e) => {
e.preventDefault(); e.preventDefault();
const docCreator = new DocCreator(logState, objectData) const configJson = store.get('configJson')
log = await docCreator.fillTemplate(); if (configJson.rootDir == null) {
const metsMods = await docCreator.createMetsMods(); 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 // Set new state of log div with message and visibility class
setLogState({ setLogState({
log: log, log: log,
logStatus: 'active' logStatus: 'active'
}); });
}
} }
// Renders data form // Renders data form
@ -118,101 +136,102 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
<form className={'flex column pd-05rem'}> <form className={'flex column pd-05rem'}>
<Galary objectData={objectData}/> <Galary objectData={objectData}/>
<div> <div>
<div > <div>
<label htmlFor={'template-datum'} >Datum</label> <label htmlFor={'template-datum'}>Datum</label>
<input <input
id={"template-datum"} id={"template-datum"}
name={"datum"} name={"datum"}
type={"date"} type={"date"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.datum} defaultValue={objectData.datum}
onChange={datumChangeHandler} onChange={datumChangeHandler}
/> />
</div> </div>
<div> <div>
<label htmlFor={'template-inventarnummer'} >Inventarnummer</label> <label htmlFor={'template-inventarnummer'}>Inventarnummer</label>
<input <input
id={"template-inventarnummer"} id={"template-inventarnummer"}
name={"inventarnummer"} name={"inventarnummer"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.inventarnummer} defaultValue={objectData.inventarnummer}
onChange={inventarnummerChangeHandler} onChange={inventarnummerChangeHandler}
/> />
</div> </div>
<div > <div>
<label htmlFor={'template-titel'}>Titel</label> <label htmlFor={'template-titel'}>Titel</label>
<input <input
id={"template-titel"} id={"template-titel"}
name={"titel"} name={"titel"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.titel} defaultValue={objectData.titel}
onChange={titelChangeHandler} onChange={titelChangeHandler}
/> />
</div> </div>
<div> <div>
<label htmlFor={'template-hersteller'}>Hersteller</label> <label htmlFor={'template-hersteller'}>Hersteller</label>
<input <input
id={"template-hersteller"} id={"template-hersteller"}
name={"hersteller"} name={"hersteller"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.hersteller} defaultValue={objectData.hersteller}
onChange={herstellerChangeHandler} onChange={herstellerChangeHandler}
/> />
</div> </div>
<div> <div>
<label htmlFor={'template-herstellungsort'}>Herstellungsort</label> <label htmlFor={'template-herstellungsort'}>Herstellungsort</label>
<input <input
id={"template-herstellungsort"} id={"template-herstellungsort"}
name={"herstellungsort"} name={"herstellungsort"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.herstellungsort} defaultValue={objectData.herstellungsort}
onChange={herstellungsortChangeHandler} onChange={herstellungsortChangeHandler}
/> />
</div> </div>
<div > <div>
<label htmlFor={'template-herstellungsdatum'}>Herstellungsdatum</label> <label htmlFor={'template-herstellungsdatum'}>Herstellungsdatum</label>
<input <input
id={"template-herstellungsdatum"} id={"template-herstellungsdatum"}
name={"herstellungsdatum"} name={"herstellungsdatum"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.herstellungsdatum} defaultValue={objectData.herstellungsdatum}
onChange={herstellungsdatumChangeHandler} onChange={herstellungsdatumChangeHandler}
/> />
</div> </div>
<div > <div>
<label htmlFor={'template-materialTechnik'}>Material und Technik</label> <label htmlFor={'template-materialTechnik'}>Material und Technik</label>
<input <input
id={"template-materialTechnik"} id={"template-materialTechnik"}
name={"materialTechnik"} name={"materialTechnik"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.materialTechnik} defaultValue={objectData.materialTechnik}
onChange={materialTechnikChangeHandler} onChange={materialTechnikChangeHandler}
/> />
</div> </div>
<div> <div>
<label htmlFor={'template-masse'}>Maße</label> <label htmlFor={'template-masse'}>Maße</label>
<input <input
id={"template-masse"} id={"template-masse"}
name={"masse"} name={"masse"}
type={"text"} type={"text"}
className={"full input-field"} className={"full input-field"}
defaultValue={objectData.masse} defaultValue={objectData.masse}
onChange={masseChangeHandler} onChange={masseChangeHandler}
/> />
</div> </div>
</div> </div>
<button <button
type={"submit"} type={"submit"}
className={'send-button center top-distance'} className={'send-button center top-distance'}
onClick={fillTemplateClickHandler} onClick={fillTemplateClickHandler}
>Dokument erstellen</button> >Dokument erstellen
</button>
</form> </form>
</div> </div>
) )

View file

@ -1,9 +1,5 @@
// Modules // Modules
import {FileLoader} from "../FileLoader"; import Store from 'electron-store';
// Electron
const electron = window.require('electron');
const ipcRenderer = electron.ipcRenderer;
// Icons // Icons
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
@ -13,7 +9,6 @@ import React, {useState} from "react";
import {NavLink} from "react-router-dom"; import {NavLink} from "react-router-dom";
import {Input} from "../components/Input"; import {Input} from "../components/Input";
import EditIcon from "@mui/icons-material/Edit"; import EditIcon from "@mui/icons-material/Edit";
import {writeFile} from "fs";
//////////////////// ////////////////////
@ -21,16 +16,11 @@ import {writeFile} from "fs";
//////////////////// ////////////////////
export default function Settings() { export default function Settings() {
const fileLoader = new FileLoader() const store = new Store()
const initialConfig = fileLoader.getConfigFile() const initialConfigJson = store.get('configJson')
console.log(initialConfig)
const getConfigPath = async (path) => {
return await ipcRenderer.invoke('assetPath:getAssetPath', path)
}
// States // States
const [configFile, setConfigFile] = useState( const [configFile, setConfigFile] = useState(
initialConfig initialConfigJson
) )
const [restart, setRestart] = useState( const [restart, setRestart] = useState(
false false
@ -46,25 +36,18 @@ export default function Settings() {
rootDir: rootDirFromWindow rootDir: rootDirFromWindow
} }
}) })
await saveInputClickHandler(rootDirFromWindow) saveInputClickHandler(rootDirFromWindow)
} }
// Handler // Handler
async function saveInputClickHandler(rootDirFromWindow) { function saveInputClickHandler(rootDirFromWindow) {
let config2Safe = { let config2Safe = {
rootDir: rootDirFromWindow rootDir: rootDirFromWindow
} }
if (rootDirFromWindow) { if (rootDirFromWindow) {
let message; store.set(
const configPath = await getConfigPath('config/config.json') 'configJson', config2Safe
await writeFile(configPath, JSON.stringify(config2Safe, null, 2), (err) => { )
if (err) {
message = err;
} else {
message = 'saved file'
}
return message
})
setRestart(true) setRestart(true)
return restart; return restart;
} }