Compare commits
No commits in common. "1fd54e134ebdd666fe752c3aa66501c083c6d236" and "196fe0e044b678164053a2fca214e2630ce2d4d5" have entirely different histories.
1fd54e134e
...
196fe0e044
|
|
@ -5,16 +5,15 @@ import icon from '../../resources/icon.png?asset'
|
||||||
|
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
|
|
||||||
// import {
|
import {
|
||||||
// Game,
|
Game,
|
||||||
// addGame,
|
addGame,
|
||||||
// updateGame,
|
updateGame,
|
||||||
// deleteGame,
|
deleteGame,
|
||||||
// getAllGames,
|
getAllGames,
|
||||||
// getGame
|
getGame
|
||||||
// } from './services/database.js';
|
} from './services/Database.service.js';
|
||||||
import { appSettings } from './services/settings.js';
|
import { appSettings } from './settings.js';
|
||||||
import { init } from './services/api.js';
|
|
||||||
|
|
||||||
let mainWindow: BrowserWindow;
|
let mainWindow: BrowserWindow;
|
||||||
let splash: BrowserWindow;
|
let splash: BrowserWindow;
|
||||||
|
|
@ -141,6 +140,7 @@ app.whenReady().then(() => {
|
||||||
{
|
{
|
||||||
label: 'Quit',
|
label: 'Quit',
|
||||||
click: _ => {
|
click: _ => {
|
||||||
|
console.log('Menu/Quit was clicked')
|
||||||
app.exit()
|
app.exit()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -157,25 +157,23 @@ app.whenReady().then(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init()
|
|
||||||
|
|
||||||
|
|
||||||
|
ipcMain.handle('game:insert', async (_, game: Game) => {
|
||||||
// ipcMain.handle('game:insert', async (_, game: Game) => {
|
addGame(game);
|
||||||
// addGame(game);
|
});
|
||||||
// });
|
ipcMain.handle('game:update', async (_, game: Game) => {
|
||||||
// ipcMain.handle('game:update', async (_, game: Game) => {
|
updateGame(game);
|
||||||
// updateGame(game);
|
});
|
||||||
// });
|
ipcMain.handle('game:delete', async (_, id: number) => {
|
||||||
// ipcMain.handle('game:delete', async (_, id: number) => {
|
deleteGame(id);
|
||||||
// deleteGame(id);
|
});
|
||||||
// });
|
ipcMain.handle('game:getOne', async (_, id: number) => {
|
||||||
// ipcMain.handle('game:getOne', async (_, id: number) => {
|
return getGame(id);
|
||||||
// return getGame(id);
|
});
|
||||||
// });
|
ipcMain.handle('game:getAll', async () => {
|
||||||
// ipcMain.handle('game:getAll', async () => {
|
return getAllGames();
|
||||||
// return getAllGames();
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
ipcMain.on('launch-app', (event, { id, appPath }) => {
|
ipcMain.on('launch-app', (event, { id, appPath }) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export function updateGame(game: Game) {
|
||||||
const db = connect();
|
const db = connect();
|
||||||
const { game_id, title, formatted_title, path, img_cover, img_background, is_running } = game;
|
const { game_id, title, formatted_title, path, img_cover, img_background, is_running } = game;
|
||||||
const stm = db.prepare(
|
const stm = db.prepare(
|
||||||
'UPDATE games SET game_id = @game_id, title = @title, formatted_title = @formatted_title, path = @path, img_cover = @img_cover, img_background = @img_background, is_running = @is_running WHERE game_id = @game_id',
|
'UPDATE games SET game_id = @game_id, title = @title, formatted_title = @formatted_title, path = @path, img_cover = @img_cover, img_background = @img_background, is_running = @is_running, WHERE id = @id',
|
||||||
);
|
);
|
||||||
|
|
||||||
stm.run({ game_id, title, formatted_title, path, img_cover, img_background, is_running });
|
stm.run({ game_id, title, formatted_title, path, img_cover, img_background, is_running });
|
||||||
|
|
@ -82,7 +82,7 @@ export function deleteGame(id: number) {
|
||||||
stm.run({ id });
|
stm.run({ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllGames(args = {}) {
|
export function getAllGames() {
|
||||||
const db = connect();
|
const db = connect();
|
||||||
|
|
||||||
const stm = db.prepare('SELECT * FROM games');
|
const stm = db.prepare('SELECT * FROM games');
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
import { ipcMain } from "electron"
|
|
||||||
import {
|
|
||||||
Game,
|
|
||||||
addGame,
|
|
||||||
updateGame,
|
|
||||||
deleteGame,
|
|
||||||
getAllGames,
|
|
||||||
getGame
|
|
||||||
} from './database.js';
|
|
||||||
import { spawn } from "child_process";
|
|
||||||
|
|
||||||
export let umbraApi = {
|
|
||||||
getGames: "GET /games",
|
|
||||||
getRecentGames: "POST /games/recent",
|
|
||||||
addGame: "POST /games",
|
|
||||||
getGame: "GET /game",
|
|
||||||
updateGame: "PUT /game",
|
|
||||||
removeGame: "DELETE /game",
|
|
||||||
// startGame: "POST /game/launch",
|
|
||||||
getGameStatus: "GET /game/status",
|
|
||||||
stopGame: "POST /game/stop",
|
|
||||||
}
|
|
||||||
|
|
||||||
export function init() {
|
|
||||||
// Get all games { page, limit }
|
|
||||||
ipcMain.handle(umbraApi.getGames, async (args) => { return getAllGames(args); })
|
|
||||||
|
|
||||||
// Get recent games { page, limit, since }
|
|
||||||
ipcMain.handle(umbraApi.getRecentGames, async (args) => { return getAllGames(args); })
|
|
||||||
|
|
||||||
// Add Game
|
|
||||||
ipcMain.handle(umbraApi.addGame, async (_, game: Game) => { return addGame(game); })
|
|
||||||
|
|
||||||
// Get Game
|
|
||||||
ipcMain.handle(umbraApi.getGame, async (_, id: number) => { return getGame(id); })
|
|
||||||
|
|
||||||
// Update Game
|
|
||||||
ipcMain.handle(umbraApi.updateGame, async (_, game: Game) => { return updateGame(game); })
|
|
||||||
|
|
||||||
// Remove Game
|
|
||||||
ipcMain.handle(umbraApi.removeGame, async (_, id: number) => { return deleteGame(id); })
|
|
||||||
|
|
||||||
// Start Game
|
|
||||||
// ipcMain.handle(umbraApi.startGame, async (_, { id, path }) => {
|
|
||||||
// // console.log(getGame(id))
|
|
||||||
// try {
|
|
||||||
// let game = getGame(id);
|
|
||||||
// const appProcess = spawn(path, [], { detached: true, stdio: 'ignore' });
|
|
||||||
// await updateGame({ ...game, is_running: 1});
|
|
||||||
|
|
||||||
// appProcess.on('data', (data) => {
|
|
||||||
// console.log(`stdout: ${data}`);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Surveiller la fermeture de l'application
|
|
||||||
// appProcess.on('close', async (code) => {
|
|
||||||
// console.log(`child process close all stdio with code ${code}`);
|
|
||||||
// await updateGame({ ...game, is_running: 0});
|
|
||||||
// console.log(game, getGame(id))
|
|
||||||
// });
|
|
||||||
|
|
||||||
// /*appProcess.on('exit', async (code) => {
|
|
||||||
// console.log(`child process exited with code ${code}`);
|
|
||||||
// await updateGame({ ...game, is_running: 0});
|
|
||||||
// });*/
|
|
||||||
|
|
||||||
// appProcess.on('error', async (err) => {
|
|
||||||
// console.error(`Erreur avec l'application ${id} : ${err.message}`);
|
|
||||||
// await updateGame({ ...game, is_running: 0});
|
|
||||||
// });
|
|
||||||
// } catch (err: any) {
|
|
||||||
// console.error(`Erreur lors du lancement de l'application ${id} : ${err.message}`);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// ipcMain.handle(umbraApi.startGame, async (_, { id, path }) => { ipcRenderer.send('launch-app', { id, path }) })
|
|
||||||
ipcMain.on("startGame", async (event, { id, path }) => {
|
|
||||||
// ipcMain.on(umbraApi.startGame, async (event, { id, path }) => {
|
|
||||||
try {
|
|
||||||
let game = getGame(id);
|
|
||||||
const appProcess = spawn(path, [], { detached: true, stdio: 'ignore' });
|
|
||||||
await updateGame({ ...game, is_running: 1});
|
|
||||||
event.reply('app-status', { id, status: 1 });
|
|
||||||
|
|
||||||
appProcess.on('data', (data) => {
|
|
||||||
console.log(`stdout: ${data}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Surveiller la fermeture de l'application
|
|
||||||
appProcess.on('close', async (code) => {
|
|
||||||
console.log(`child process close all stdio with code ${code}`);
|
|
||||||
event.reply('app-status', { id, status: 0 });
|
|
||||||
await updateGame({ ...game, is_running: 0});
|
|
||||||
});
|
|
||||||
|
|
||||||
/*appProcess.on('exit', async (code) => {
|
|
||||||
console.log(`child process exited with code ${code}`);
|
|
||||||
event.reply('app-status', { id, status: 0 });
|
|
||||||
await updateGame({ ...game, is_running: 0});
|
|
||||||
});*/
|
|
||||||
|
|
||||||
appProcess.on('error', async (err) => {
|
|
||||||
console.error(`Erreur avec l'application ${id} : ${err.message}`);
|
|
||||||
event.reply('app-status', { id, status: 0 });
|
|
||||||
await updateGame({ ...game, is_running: 0});
|
|
||||||
});
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error(`Erreur lors du lancement de l'application ${id} : ${err.message}`);
|
|
||||||
// event.reply('app-status', { id, status: `${err.message}` });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get Game Status
|
|
||||||
ipcMain.handle(umbraApi.getGameStatus, async (_, id) => { return getGame(id).is_running; })
|
|
||||||
|
|
||||||
// Stop Game
|
|
||||||
ipcMain.handle(umbraApi.stopGame, async (args) => { return getAllGames(args); })
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Store from "electron-store";
|
import Store from "electron-store";
|
||||||
import { DEFAULT_SETTINGS } from "../data/default_settings.js";
|
import { DEFAULT_SETTINGS } from "./data/default_settings.js";
|
||||||
|
|
||||||
const schema = DEFAULT_SETTINGS;
|
const schema = DEFAULT_SETTINGS;
|
||||||
const store = new Store({schema});
|
const store = new Store({schema});
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { contextBridge, ipcRenderer } from 'electron'
|
import { contextBridge, ipcRenderer } from 'electron'
|
||||||
import { electronAPI } from '@electron-toolkit/preload'
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
import { umbraApi } from '../main/services/api.js'
|
|
||||||
|
|
||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
const api = {}
|
const api = {}
|
||||||
|
|
@ -16,18 +15,6 @@ if (process.contextIsolated) {
|
||||||
onAppStatus: (callback) => ipcRenderer.on('app-status', (event, status) => callback(status))
|
onAppStatus: (callback) => ipcRenderer.on('app-status', (event, status) => callback(status))
|
||||||
})
|
})
|
||||||
contextBridge.exposeInMainWorld('api', api)
|
contextBridge.exposeInMainWorld('api', api)
|
||||||
|
|
||||||
|
|
||||||
let safeUmbraApi = {};
|
|
||||||
for (let key in umbraApi) {
|
|
||||||
safeUmbraApi[key] = (args) => ipcRenderer.invoke(umbraApi[key], args);
|
|
||||||
}
|
|
||||||
safeUmbraApi = {
|
|
||||||
...safeUmbraApi,
|
|
||||||
"startGame": (id, path) => ipcRenderer.send('startGame', { id, path }),
|
|
||||||
"onAppStatus": (callback) => ipcRenderer.on('app-status', (event, status) => callback(status))
|
|
||||||
}
|
|
||||||
contextBridge.exposeInMainWorld('Umbra', safeUmbraApi);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,12 @@ import './styles/index.css'
|
||||||
import Versions from './components/Versions'
|
import Versions from './components/Versions'
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import GameArea, { Game } from './components/GameArea';
|
import GameArea, { Game } from './components/GameArea';
|
||||||
import { Dialog } from './components/Dialog';
|
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const [games, setGames] = useState<Game[]>([]);
|
const [games, setGames] = useState<Game[]>([]);
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
|
||||||
|
|
||||||
async function getAllGames() {
|
async function getAllGames() {
|
||||||
const data = await window["Umbra"].getGames();
|
const data = await window.electron.ipcRenderer.invoke('game:getAll');
|
||||||
|
|
||||||
console.log(data)
|
console.log(data)
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
@ -24,15 +22,6 @@ function App(): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button onClick={() => setShowDialog(true)}>Open</button>
|
|
||||||
{showDialog && (
|
|
||||||
<Dialog
|
|
||||||
showCloseButton={true}
|
|
||||||
onClose={() => setShowDialog(false)}
|
|
||||||
>
|
|
||||||
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam consequuntur, optio qui quas impedit magni eos, provident ducimus recusandae nostrum eius, ratione tempore ex in blanditiis? Accusamus omnis delectus repudiandae.</p>
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
<div className='appWrapper'>
|
<div className='appWrapper'>
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<div style={{ height: '600px' }}></div>
|
<div style={{ height: '600px' }}></div>
|
||||||
|
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
.modalClassName {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
max-width: 20rem;
|
|
||||||
padding: 2rem;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 0 0.5rem 0.25rem hsl(0 0% 0% / 10%);
|
|
||||||
}
|
|
||||||
.modalClassName::backdrop {
|
|
||||||
background: hsl(0 0% 0% / 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-close-btn {
|
|
||||||
font-size: .75em;
|
|
||||||
position: absolute;
|
|
||||||
top: .25em;
|
|
||||||
right: .25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.Dialog {
|
|
||||||
padding: 0;
|
|
||||||
text-align: start;
|
|
||||||
|
|
||||||
--dialog-margin-horizontal: 32px;
|
|
||||||
--dialog-margin-vertical: 24px;
|
|
||||||
--dialog-gap: 24px;
|
|
||||||
|
|
||||||
--modal-border: black;
|
|
||||||
--modal-background: rgb(25, 25, 25);
|
|
||||||
--text-default: white;
|
|
||||||
--modal-backdrop: hsl(0 0% 0% / 10%);
|
|
||||||
--space-xs: .8rem;
|
|
||||||
--space-md: 1rem;
|
|
||||||
--space-lg: 1.2rem;
|
|
||||||
--text-xl: 1.6rem;
|
|
||||||
--accent: blue;
|
|
||||||
--text-hover: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__element {
|
|
||||||
top: 0;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(calc(-50% + 50px), -50%);
|
|
||||||
z-index: 8;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0;
|
|
||||||
overflow: auto;
|
|
||||||
padding-top: var(--dialog-margin-vertical);
|
|
||||||
border: solid 1px var(--modal-border);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--modal-background);
|
|
||||||
color: var(--text-default);
|
|
||||||
opacity: 0;
|
|
||||||
/* transform: translateY(50px); */
|
|
||||||
transition:
|
|
||||||
opacity 500ms,
|
|
||||||
transform 500ms;
|
|
||||||
max-width: min(700px, 85vw);
|
|
||||||
max-height: 95vh;
|
|
||||||
|
|
||||||
/* remove padding top when there's a header element, it has its own padding */
|
|
||||||
&:has(.Dialog__header) {
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.Dialog__element::backdrop {
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__element:popover-open,
|
|
||||||
.Dialog__element[open] {
|
|
||||||
opacity: 1;
|
|
||||||
/* transform: translateY(0); */
|
|
||||||
box-shadow: 0px 0px 0px 100vmax var(--modal-backdrop);
|
|
||||||
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__header {
|
|
||||||
display: flex;
|
|
||||||
z-index: 2;
|
|
||||||
padding-bottom: var(--dialog-gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__headerTitle {
|
|
||||||
flex: 100% 1 1;
|
|
||||||
padding: var(--dialog-margin-vertical) 16px 0 var(--dialog-margin-horizontal);
|
|
||||||
font-size: var(--text-xl);
|
|
||||||
margin: var(--space-lg) 0 0;
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__Close {
|
|
||||||
padding: 0 var(--dialog-margin-horizontal) 0 0;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__Close,
|
|
||||||
.Dialog__header {
|
|
||||||
position: sticky;
|
|
||||||
top: 0px;
|
|
||||||
background: var(--modal-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__CloseButton {
|
|
||||||
border: none;
|
|
||||||
margin: calc(-1 * var(--space-xs));
|
|
||||||
padding: var(--space-xs);
|
|
||||||
border-radius: var(--space-xs);
|
|
||||||
background: none;
|
|
||||||
color: var(--text-default);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 250ms color;
|
|
||||||
position: absolute;
|
|
||||||
right: var(--space-lg);
|
|
||||||
top: var(--space-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__CloseButton:focus-visible {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: var(--accent) 0 0 0 2px inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__CloseButton:hover {
|
|
||||||
color: var(--text-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__CloseButton:active {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__CloseIcon {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__content {
|
|
||||||
padding: 0 var(--dialog-margin-horizontal) var(--dialog-gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.Dialog__footer {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
justify-content: end;
|
|
||||||
padding: 0 var(--dialog-margin-horizontal) var(--dialog-margin-vertical)
|
|
||||||
var(--dialog-margin-horizontal);
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import { ReactNode, SyntheticEvent, KeyboardEvent, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
||||||
import './index.css'
|
|
||||||
|
|
||||||
interface DialogProps {
|
|
||||||
className?: string
|
|
||||||
children: ReactNode
|
|
||||||
showCloseButton: boolean
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Dialog = ({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
showCloseButton = false,
|
|
||||||
onClose
|
|
||||||
}: DialogProps) => {
|
|
||||||
const dialogRef = useRef<HTMLDialogElement | null>(null)
|
|
||||||
const onCloseRef = useRef(onClose)
|
|
||||||
onCloseRef.current = onClose
|
|
||||||
const [focusOnClose, setFocusOnClose] = useState<HTMLElement | null>(null)
|
|
||||||
// const { disableDialogBackdropClose } = useContext(ContextProvider)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFocusOnClose(document.querySelector<HTMLElement>('*:focus'))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
onCloseRef.current()
|
|
||||||
if (focusOnClose) {
|
|
||||||
setTimeout(() => focusOnClose.focus(), 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const dialog = dialogRef.current
|
|
||||||
console.log(dialog)
|
|
||||||
if (dialog) {
|
|
||||||
const cancel = () => {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
dialog.addEventListener('cancel', cancel)
|
|
||||||
|
|
||||||
// if (disableDialogBackdropClose) {
|
|
||||||
// dialog['showPopover']()
|
|
||||||
|
|
||||||
// return () => {
|
|
||||||
// dialog.removeEventListener('cancel', cancel)
|
|
||||||
// dialog['hidePopover']()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
dialog.showModal()
|
|
||||||
console.log('dialog open')
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
dialog.removeEventListener('cancel', cancel)
|
|
||||||
dialog.close()
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}, [dialogRef.current/*, disableDialogBackdropClose*/])
|
|
||||||
|
|
||||||
const onDialogClick = useCallback(
|
|
||||||
(e: SyntheticEvent) => {
|
|
||||||
if (e.target === dialogRef.current) {
|
|
||||||
const ev = e.nativeEvent as MouseEvent
|
|
||||||
const tg = e.target as HTMLElement
|
|
||||||
if (
|
|
||||||
ev.offsetX < 0 ||
|
|
||||||
ev.offsetX > tg.offsetWidth ||
|
|
||||||
ev.offsetY < 0 ||
|
|
||||||
ev.offsetY > tg.offsetHeight
|
|
||||||
) {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onClose]
|
|
||||||
)
|
|
||||||
|
|
||||||
const closeIfEsc = (event: KeyboardEvent<HTMLDialogElement>) => {
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="Dialog">
|
|
||||||
<dialog
|
|
||||||
className={`Dialog__element ${className}`}
|
|
||||||
ref={dialogRef}
|
|
||||||
onClick={onDialogClick}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore, this feature is new and not yet typed
|
|
||||||
popover="manual"
|
|
||||||
onKeyUp={closeIfEsc}
|
|
||||||
>
|
|
||||||
{showCloseButton && (
|
|
||||||
<div className="Dialog__Close">
|
|
||||||
<button className="Dialog__CloseButton" onClick={close}>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</dialog>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -7,14 +7,14 @@ import { Game } from "@renderer/types";
|
||||||
const GameCard = ({ title, path, is_running, img_cover, id, }: Game) => {
|
const GameCard = ({ title, path, is_running, img_cover, id, }: Game) => {
|
||||||
const [status, setStatus] = useState<string>("");
|
const [status, setStatus] = useState<string>("");
|
||||||
|
|
||||||
async function startGame(id) {
|
function startGame(id) {
|
||||||
window["Umbra"].startGame(id, path);
|
window.electron.launchApp(id, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window["Umbra"].onAppStatus((status) => {
|
window.electron.onAppStatus((status) => {
|
||||||
if (status.id === id) {
|
if (status.id === id) {
|
||||||
let newStatus = status.status === 1 ? "Lancé" : status.status === 0 ? "" : status.status;
|
let newStatus = status.status === "started" ? "Lancé" : status.status === "closed" ? "" : status.status;
|
||||||
setStatus(newStatus);
|
setStatus(newStatus);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Reference in a new issue