diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2e72b66..7968297 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,6 +1,6 @@ module.exports = { root: true, - env: { node: true, es2020: true }, + env: { node: true, browser: true, es2020: true }, extends: [ 'eslint:recommended', 'plugin:react/recommended', @@ -16,5 +16,6 @@ module.exports = { 'warn', { allowConstantExport: true }, ], + 'react/prop-types': 'off', }, } diff --git a/dev-app-update.yml b/dev-app-update.yml new file mode 100644 index 0000000..320007f --- /dev/null +++ b/dev-app-update.yml @@ -0,0 +1,3 @@ +owner: lambor590 +repo: Packager +provider: github \ No newline at end of file diff --git a/electron/main/index.js b/electron/main/index.js index 8f22236..025ddef 100644 --- a/electron/main/index.js +++ b/electron/main/index.js @@ -29,8 +29,11 @@ const indexHtml = join(process.env.DIST, 'index.html') async function createWindow() { win = new BrowserWindow({ - title: 'Main window', + title: 'Packager', icon: join(process.env.VITE_PUBLIC, 'favicon.ico'), + width: 1200, + height: 720, + autoHideMenuBar: true, webPreferences: { preload, nodeIntegration: true, @@ -46,23 +49,16 @@ async function createWindow() { win.loadFile(indexHtml) } - // Test actively push message to the Electron-Renderer - win.webContents.on('did-finish-load', () => { - win?.webContents.send('main-process-message', new Date().toLocaleString()) - }) - // Make all links open with the browser, not with the application win.webContents.setWindowOpenHandler(({ url }) => { if (url.startsWith('https:')) shell.openExternal(url) return { action: 'deny' } }) - - // Apply electron-updater - update(win) } app.whenReady().then(createWindow) + app.on('window-all-closed', () => { win = null if (process.platform !== 'darwin') app.quit() @@ -85,20 +81,16 @@ app.on('activate', () => { } }) -// New window example arg: new windows url -ipcMain.handle('open-win', (_, arg) => { - const childWindow = new BrowserWindow({ - webPreferences: { - preload, - nodeIntegration: true, - contextIsolation: false, - }, - }) - - if (process.env.VITE_DEV_SERVER_URL) { - childWindow.loadURL(`${url}#${arg}`) - } else { - childWindow.loadFile(indexHtml, { hash: arg }) - } +ipcMain.handle("get-app-version", () => { + return app.getVersion() }) +ipcMain.on("do-action", (event, arg, data) => { + switch (arg) { + case "initAutoUpdater": + update(event) + break + default: + break + } +}) \ No newline at end of file diff --git a/electron/main/update.js b/electron/main/update.js index 090a0fa..c56f4c3 100644 --- a/electron/main/update.js +++ b/electron/main/update.js @@ -1,66 +1,24 @@ -import { app, ipcMain } from 'electron' import { autoUpdater } from 'electron-updater' -export function update(win) { +export function update(event) { - // When set to false, the update download will be triggered through the API - autoUpdater.autoDownload = false - autoUpdater.disableWebInstaller = false + autoUpdater.autoDownload = true autoUpdater.allowDowngrade = false + autoUpdater.disableWebInstaller = true + autoUpdater.autoInstallOnAppQuit = true + autoUpdater.forceDevUpdateConfig = true - // start check - autoUpdater.on('checking-for-update', function () { }) - // update available - autoUpdater.on('update-available', (arg) => { - win.webContents.send('update-can-available', { update: true, version: app.getVersion(), newVersion: arg?.version }) + autoUpdater.on('update-available', (_event) => { + event.sender.send('update-available', _event) }) - // update not available - autoUpdater.on('update-not-available', (arg) => { - win.webContents.send('update-can-available', { update: false, version: app.getVersion(), newVersion: arg?.version }) - }) - - // Checking for updates - ipcMain.handle('check-update', async () => { - if (!app.isPackaged) { - const error = new Error('The update feature is only available after the package.') - return { message: error.message, error } - } - try { - return await autoUpdater.checkForUpdatesAndNotify() - } catch (error) { - return { message: 'Network error', error } - } + autoUpdater.on('download-progress', (_event, progressInfo) => { + event.sender.send('download-progress', _event, progressInfo) }) - // Start downloading and feedback on progress - ipcMain.handle('start-download', (event) => { - startDownload( - (error, progressInfo) => { - if (error) { - // feedback download error message - event.sender.send('update-error', { message: error.message, error }) - } else { - // feedback update progress message - event.sender.send('download-progress', progressInfo) - } - }, - () => { - // feedback update downloaded message - event.sender.send('update-downloaded') - } - ) - }) - - // Install now - ipcMain.handle('quit-and-install', () => { - autoUpdater.quitAndInstall(false, true) - }) -} + autoUpdater.on('update-downloaded', () => { + autoUpdater.quitAndInstall(true, true) + }); -function startDownload(callback, complete) { - autoUpdater.on('download-progress', info => callback(null, info)) - autoUpdater.on('error', error => callback(error, null)) - autoUpdater.on('update-downloaded', complete) - autoUpdater.downloadUpdate() -} + autoUpdater.checkForUpdates() +} \ No newline at end of file diff --git a/index.html b/index.html index caf31aa..f8ed9a4 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,16 @@ - - - - - Vite + React - - -
- - - + + + + + + Packager + + + +
+ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5bb37e3..3237d27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "packager", - "version": "0.0.1", + "version": "0.0.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "packager", - "version": "0.0.1", + "version": "0.0.11", "license": "MIT", "dependencies": { "electron-updater": "^6.1.7" @@ -16,7 +16,7 @@ "@types/react-dom": "18.2.18", "@vitejs/plugin-react-swc": "3.5.0", "autoprefixer": "10.4.16", - "daisyui": "^4.4.24", + "daisyui": "4.4.24", "electron": "28.1.0", "electron-builder": "24.9.1", "eslint": "8.56.0", @@ -27,6 +27,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "tailwindcss": "3.4.0", + "tree-kill": "^1.2.2", "vite": "5.0.10", "vite-plugin-electron": "0.15.5", "vite-plugin-electron-renderer": "0.14.5" @@ -2866,15 +2867,6 @@ "node": ">=6.0.0" } }, - "node_modules/dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", @@ -5766,6 +5758,15 @@ "node": ">=12.0.0" } }, + "node_modules/read-config-file/node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6676,6 +6677,15 @@ "node": ">=8.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", diff --git a/package.json b/package.json index af32e6e..b673cc2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "Packager", "version": "0.0.1", "main": "dist-electron/main/index.js", - "description": "Gestor de paquetes para Parcel Shops para gestionar todo en un mismo lugar de forma rápida y sencilla.", + "description": "Gestor de paquetes para Parcel Shops con el objetivo de gestionar todo en un mismo lugar de forma rápida y sencilla.", "author": "Marc Correale", "license": "MIT", "private": true, @@ -37,6 +37,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "tailwindcss": "3.4.0", + "tree-kill": "^1.2.2", "vite": "5.0.10", "vite-plugin-electron": "0.15.5", "vite-plugin-electron-renderer": "0.14.5" diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.css b/src/App.css index b9d355d..bd6213e 100644 --- a/src/App.css +++ b/src/App.css @@ -1,42 +1,3 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index b8b8473..45807b0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,33 +1,29 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' +import { useEffect, useState } from 'react' +import { ipcRenderer } from 'electron' +import UpdateModal from './components/UpdateModal' import './App.css' function App() { - const [count, setCount] = useState(0) + const [appVersion, setAppVersion] = useState('') + + useEffect(() => { + ipcRenderer.invoke('get-app-version').then(version => { + setAppVersion(version) + }) + }, []) return ( <> +

Packager

- - Vite logo - - - React logo - -
-

Vite + React

-
-

- Edit src/App.jsx and save to test HMR + Version {appVersion}

-

- Click on the Vite and React logos to learn more -

+ ) } diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx new file mode 100644 index 0000000..53d8cde --- /dev/null +++ b/src/components/Modal.jsx @@ -0,0 +1,21 @@ +import '../App.css' + +function Modal({ isOpen, title = null, children, canBeDismissed = true, onClose }) { + return ( + +
+

{title}

+

{children}

+
+
+ {canBeDismissed && + + } +
+
+
+
+ ) +} + +export default Modal \ No newline at end of file diff --git a/src/components/ProgressBar.jsx b/src/components/ProgressBar.jsx new file mode 100644 index 0000000..43ff0a6 --- /dev/null +++ b/src/components/ProgressBar.jsx @@ -0,0 +1,9 @@ +import '../App.css' + +function ProgressBar({ percentage }) { + return ( + + ) +} + +export default ProgressBar \ No newline at end of file diff --git a/src/components/UpdateModal.jsx b/src/components/UpdateModal.jsx new file mode 100644 index 0000000..7e8aa48 --- /dev/null +++ b/src/components/UpdateModal.jsx @@ -0,0 +1,35 @@ +import { useState, useEffect } from 'react' +import { ipcRenderer } from 'electron' +import Modal from './Modal' +import ProgressBar from './ProgressBar' + +function UpdateModal() { + const [isOpen, setIsOpen] = useState(false) + const [progress, setProgress] = useState(0) + + useEffect(() => { + + ipcRenderer.on('update-available', () => { + setIsOpen(true) + }) + ipcRenderer.on('download-progress', (_event, progressInfo) => { + setProgress(progressInfo.percent) + }) + + return () => { + ipcRenderer.removeListener('update-available', setIsOpen(true)) + ipcRenderer.removeListener('download-progress', (_event, progressInfo) => { + setProgress(progressInfo.percent) + }) + } + }, []) + + return ( + + {progress >= 90 ? 'Instalando actualización...' : 'Descargando actualización...'} + + + ) +} + +export default UpdateModal \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6119ad9..e59a1eb 100644 --- a/src/index.css +++ b/src/index.css @@ -1,68 +1,17 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; + font-family: system-ui, -apple-system, Ubuntu, sans-serif; + user-select: none; } body { margin: 0; - display: flex; + display: grid; place-items: center; + text-align: center; min-width: 320px; min-height: 100vh; } -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} +.modal { + border: none; +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 54b39dd..9fda639 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,10 @@ -import React from 'react' import ReactDOM from 'react-dom/client' +import { ipcRenderer } from 'electron' import App from './App.jsx' import './index.css' +ipcRenderer.send('do-action', 'initAutoUpdater') + ReactDOM.createRoot(document.getElementById('root')).render( - - - , + )