4
0
mirror of https://github.com/AltarikMC/Launcher synced 2024-11-24 06:39:50 +01:00

Move renderer to Vite + React

This commit is contained in:
Quentin Legot 2024-01-23 19:16:32 +01:00
parent c0f6ace012
commit b9f6dba214
48 changed files with 2530 additions and 1221 deletions

View File

@ -1,29 +1,9 @@
module.exports = { module.exports = {
env: {
browser: true,
es2021: true
},
extends: [ extends: [
'standard' 'eslint:recommended',
], 'plugin:react/recommended',
overrides: [ 'plugin:react/jsx-runtime',
{ '@electron-toolkit',
env: { '@electron-toolkit/eslint-config-prettier'
node: true ]
},
files: [
'.eslintrc.{js,cjs}'
],
parserOptions: {
sourceType: 'script'
}
}
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [],
rules: {
}
} }

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ out/
.vscode/ .vscode/
*.code-workspace *.code-workspace
.idea .idea
dist

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json

5
.prettierrc.yaml Normal file
View File

@ -0,0 +1,5 @@
singleQuote: true
semi: false
printWidth: 100
trailingComma: none
endOfLine: auto

View File

@ -1,13 +1,17 @@
const path = require('path'); const path = require('path')
const pkg = require('./package.json') const pkg = require('./package.json')
module.exports = { module.exports = {
packagerConfig: { packagerConfig: {
packageName: "altarik-launcher", packageName: 'altarik-launcher',
name: "Altarik Launcher", name: 'Altarik Launcher',
productName: "altarik-launcher", productName: 'altarik-launcher',
icon: path.resolve(__dirname, 'icon.ico'), icon: path.resolve(__dirname, 'icon.ico'),
asar: true, asar: true,
ignore: [
/^\/src/,
/(.eslintrc.json)|(.gitignore)|(electron.vite.config.ts)|(forge.config.cjs)|(tsconfig.*)/
]
}, },
plugins: [ plugins: [
{ {
@ -17,7 +21,7 @@ module.exports = {
], ],
makers: [ makers: [
{ {
name: "@electron-forge/maker-squirrel", name: '@electron-forge/maker-squirrel',
platforms: ['darwin', 'win32'], platforms: ['darwin', 'win32'],
config: { config: {
name: pkg.name, name: pkg.name,

View File

@ -3,7 +3,7 @@
"author": "Altarik", "author": "Altarik",
"version": "2.1.4", "version": "2.1.4",
"description": "Altarik Launcher", "description": "Altarik Launcher",
"main": "src/server/main.js", "main": "./dist/main/index.js",
"type": "module", "type": "module",
"homepage": "https://altarik.fr/", "homepage": "https://altarik.fr/",
"private": true, "private": true,
@ -23,11 +23,11 @@
], ],
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"scripts": { "scripts": {
"start": "electron-forge start", "start": "electron-vite preview --outDir=dist",
"test": "electron-forge start --enable-logging --inspect-electron", "dev": "electron-vite dev --outDir=dist",
"package": "electron-forge package", "package": "electron-vite build --outDir=dist && electron-forge package",
"make": "electron-forge make", "make": "electron-vite build --outDir=dist && electron-forge make",
"publish": "electron-forge publish" "publish": "electron-vite build --outDir=dist && electron-forge publish"
}, },
"devDependencies": { "devDependencies": {
"@electron-forge/cli": "^7.1.0", "@electron-forge/cli": "^7.1.0",
@ -35,25 +35,38 @@
"@electron-forge/maker-zip": "^7.2.0", "@electron-forge/maker-zip": "^7.2.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.2.0", "@electron-forge/plugin-auto-unpack-natives": "^7.2.0",
"@electron-forge/publisher-github": "^7.2.0", "@electron-forge/publisher-github": "^7.2.0",
"@electron-toolkit/eslint-config": "^1.0.2",
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
"autoprefixer": "^10.4.17",
"cssnano": "^6.0.3",
"electron": "28.1.3", "electron": "28.1.3",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-standard": "^17.1.0", "eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2", "eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.0.0" "eslint-plugin-promise": "^6.0.0",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1"
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@vitejs/plugin-react": "^4.2.1",
"decompress": "^4.2.1", "decompress": "^4.2.1",
"electron-is-dev": "^3.0.1",
"electron-log": "^5.0.3", "electron-log": "^5.0.3",
"electron-squirrel-startup": "^1.0.0", "electron-squirrel-startup": "^1.0.0",
"electron-vite": "^2.0.0",
"eslint-plugin-react": "^7.33.2",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"hasha": "^6.0.0", "hasha": "^6.0.0",
"izitoast": "^1.4.0", "izitoast": "^1.4.0",
"minecraft-launcher-core": "^3.17.3", "minecraft-launcher-core": "^3.17.3",
"msmc": "^4.1.0", "msmc": "^4.1.0",
"node-fetch": "^3.0.0", "node-fetch": "^3.0.0",
"vue": "^3.4.11" "prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^5.0.12"
}, },
"config": { "config": {
"forge": "./config.forge.cjs" "forge": "./config.forge.cjs"

7
postcss.config.js Normal file
View File

@ -0,0 +1,7 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
}
}

View File

@ -1,32 +0,0 @@
const vue = require('vue/dist/vue.cjs.js')
app = vue.createApp({
data () {
return {
displayFullscreen: 'block',
fullscreenText: 'Recherche de mise à jour...',
downloadLink: null
}
},
mounted () {
this.sendCheckingUpdate()
},
methods: {
sendCheckingUpdate () {
ipcRenderer.send('checking-update')
},
openLinkExternal () {
shell.openExternal(this.downloadLink)
}
}
})
const root = app.mount('#vue')
ipcRenderer.on('update-available', (event, arg) => {
root.fullscreenText = 'Mise à jour disponible, téléchargement...'
})
ipcRenderer.on('please-download-update', (event, args) => {
root.fullscreenText = 'Veuillez télécharger la mise à jour en cliquant sur le lien suivant :'
root.downloadLink = `${args.url}`
})

View File

@ -1,177 +0,0 @@
const os = require('os')
const totalMem = os.totalmem() / (1.049 * Math.pow(10, 6))
const vue = require('vue/dist/vue.cjs.js')
app = vue.createApp({
data () {
return {
minMemValue: localStorage.getItem('minMem') != null ? localStorage.getItem('minMem') : 1024,
maxMemValue: localStorage.getItem('maxMem') != null ? localStorage.getItem('maxMem') : 2048,
memStep: 128,
memMax: totalMem,
invalidateButtonText: 'Supprimer et retélécharger les bibliothèques',
invalidateButtonDisabled: false,
displayFullscreen: 'none',
displaySettings: 'none',
displayCredits: 'none',
nick: 'Chargement',
launchBtnText: 'Selectionnez un chapitre',
launchBtnDisable: true,
launchBtnHidden: false,
loadingMessageHidden: true,
loadingMessageText: 'Téléchargement de Minecraft en cours...',
fullprogressbarHidden: true,
progressbarWidth: 0,
sidebarContent: '<hr><p>Chargement en cours</p>',
modsInformations: [],
modsInformationsLoaded: true,
selectedChapter: -1,
gameLaunching: false
}
},
mounted () {
iziToast.settings({
close: false,
closeOnClick: true,
timeout: 5000,
position: 'topRight',
resetOnHover: true
})
setInterval(() => {
ipcRenderer.send('pageReady')
}, 500)
},
methods: {
invalidateData () {
this.invalidateButtonDisabled = true
this.invalidateButtonText = 'Opération en cours'
this.showInfo('Opération en cours', 'Suppression des données du jeu en cours')
ipcRenderer.send('invalidateData')
},
launchBtnClick () {
this.launchBtnHidden = true
this.fullprogressbarHidden = false
this.loadingMessageHidden = false
if (Number(this.minMemValue) <= Number(this.maxMemValue)) {
ipcRenderer.send('launch', {
minMem: this.minMemValue + 'M',
maxMem: this.maxMemValue + 'M',
chapter: this.selectedChapter
})
this.launchBtnDisable = true
localStorage.setItem('minMem', this.minMemValue)
localStorage.setItem('maxMem', this.maxMemValue)
this.gameLaunching = true
} else {
this.showError('Erreur de lancement', 'La mémoire minimale doit être inférieure ou égale à la mémoire maximale.')
}
},
changeSelectedChapter (index) {
this.selectedChapter = parseInt(index)
root.launchBtnText = 'JOUER'
root.launchBtnDisable = false
},
disconnectBtn () {
ipcRenderer.send('disconnect')
},
options () {
if (!this.gameLaunching) {
this.displayFullscreen = 'block'
this.displaySettings = 'block'
this.displayCredits = 'none'
}
},
discord () {
shell.openExternal('https://discord.gg/p3EnE6Jumg')
},
web () {
shell.openExternal('https://altarik.fr')
},
closeFullscreen () {
this.displayFullscreen = 'none'
this.displaySettings = 'none'
this.displayCredits = 'none'
},
credits () {
this.displayFullscreen = 'block'
this.displaySettings = 'none'
this.displayCredits = 'block'
},
updateModsInformations (content) {
this.modsInformations = content
},
getModsInformations () {
return this.modsInformations
},
showInfo (title, body) {
iziToast.info({
title,
message: body,
color: 'blue'
})
},
showError (title, body) {
iziToast.error({
title,
message: body,
color: 'red'
})
},
showWarning (title, body) {
iziToast.warning({
title,
message: body,
color: 'yellow'
})
},
showSuccess (title, body) {
iziToast.success({
title,
message: body,
color: 'green'
})
},
isSelected (index) {
return this.selectedChapter === index
}
}
})
const root = app.mount('#vue')
ipcRenderer.on('invalidated', () => {
root.invalidateButtonDisabled = false
root.invalidateButtonText = 'Supprimer et retélécharger les bibliothèques'
root.showSuccess('Opération terminée', 'Les données du jeu ont été supprimé avec succès')
})
ipcRenderer.on('progress', (e, args) => {
root.progressbarWidth = (args.task / Math.max(args.total, args.task)) * 100
root.loadingMessageText = 'Téléchargement de ' + args.type + ': ' + args.task + ' sur ' + Math.max(args.total, args.task)
})
ipcRenderer.on('close', (_e, _args) => {
root.launchBtnHidden = false
root.fullprogressbarHidden = true
root.loadingMessageHidden = true
root.loadingMessageText = 'Chargement de Minecraft en cours...'
root.progressbarWidth = 0
root.launchBtnDisable = false
root.gameLaunching = false
})
ipcRenderer.on('launch', (_e, _args) => {
root.fullprogressbarHidden = true
root.loadingMessageHidden = true
})
ipcRenderer.on('modsInformations', (_e, args) => {
if (args === null) {
root.modsInformationsLoaded = false
} else {
root.modsInformationsLoaded = true
}
root.updateModsInformations(args)
})
ipcRenderer.on('nick', (_e, args) => root.nick = args.name)

View File

@ -1,87 +0,0 @@
const vue = require('vue/dist/vue.cjs.js')
app = vue.createApp({
data () {
return {
login: 'Connexion',
email: 'Email',
password: 'Mot de passe',
send_credentials: 'Se connecter',
microsoft_button: 'Connexion avec un compte Microsoft'
}
},
mounted () {
iziToast.settings({
close: false,
closeOnClick: true,
timeout: 5000,
position: 'topRight',
resetOnHover: true
})
},
methods: {
formSubmit (e) {
e.preventDefault()
if (!microsoftButton.disabled) {
form.disabled = true
if (user.value) {
ipcRenderer.send('login', {
user: user.value,
pass: password.value
})
} else {
this.showWarning('Erreur de connexion', 'Veuillez entrer des identifiants')
form.disabled = false
}
}
},
microsoftButton (e) {
e.preventDefault()
if (!form.disabled) {
microsoftButton.disabled = true
form.disabled = true
ipcRenderer.send('microsoft-login')
}
},
showInfo (title, body) {
iziToast.info({
title,
message: body,
color: 'blue'
})
},
showError (title, body) {
iziToast.error({
title,
message: body,
color: 'red'
})
},
showWarning (title, body) {
iziToast.warning({
title,
message: body,
color: 'yellow'
})
},
showSuccess (title, body) {
iziToast.success({
title,
message: body,
color: 'green'
})
}
}
})
app.mount('#vue')
// theirs const are declared after vue cause vue modify them when declaring new vue instance
const form = document.querySelector('#login-form')
const user = document.querySelector('#nickname')
const password = document.querySelector('#password')
const microsoftButton = document.querySelector('#microsoft-button')
ipcRenderer.on('loginError', () => {
form.disabled = false
microsoftButton.disabled = false
})

View File

@ -1,29 +0,0 @@
'use strict'
const { ipcRenderer, shell } = require('electron')
let app
window.addEventListener('DOMContentLoaded', () => {
const minimizeButton = document.getElementById('minimize-btn')
const closeButton = document.getElementById('close-btn')
minimizeButton.addEventListener('click', () => ipcRenderer.send('minimizeWindow'))
closeButton.addEventListener('click', () => ipcRenderer.send('closeWindow'))
})
ipcRenderer.on('notification', (_e, args) => {
app.notificationTitle = args.title
app.notificationMessage = args.body
switch (args.class) {
case 'success':
app._component.methods.showSuccess(args.title, args.body)
break
case 'warning':
app._component.methods.showWarning(args.title, args.body)
break
case 'error':
app._component.methods.showError(args.title, args.body)
break
case 'info':default:
app._component.methods.showInfo(args.title, args.body)
}
})

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Altarik Launcher</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval';" />
<link rel="stylesheet" href="../../node_modules/izitoast/dist/css/iziToast.min.css">
<link href="assets/css/fonts.css" rel="stylesheet" />
<link href="assets/css/checkingUpdate.css" rel="stylesheet" />
<link href="assets/css/menubar.css" rel="stylesheet" />
<!-- <link rel="shortcut icon" type="image/png" href="assets/images/icon.png"/> -->
</head>
<body>
<div id="vue">
<div id="menubar">
<ul class="left">
<img src="../../icon.ico">
</ul>
<ul class="right">
<!-- Mettre ce code en ligne pour éviter que chrome ne met un espace automatiquement entre les éléments -->
<li id="minimize-btn"><i class="material-icons">minimize</i></li><!--<li id="max-unmax-btn"><i class="material-icons">crop_square</i></li>--><li id="close-btn"><i class="material-icons">close</i></li>
</ul>
</div>
<div id="fullscreen" :style="{ display: displayFullscreen }">
<div id="fullscreen-content">
<p>{{ fullscreenText }} <br /><a @click="openLinkExternal" v-if="downloadLink !== null" href="#">{{ downloadLink }}</a></p>
<div class="dots-3"></div>
</div>
</div>
</div>
<script src="../../node_modules/izitoast/dist/js/iziToast.js" type="text/javascript"></script>
<script src="assets/js/preload.js"></script>
<script src="assets/js/checkingUpdate.js"></script>
</body>
</html>

View File

@ -1,112 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Altarik Launcher</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval';" />
<link rel="stylesheet" href="../../node_modules/izitoast/dist/css/iziToast.min.css">
<link href="assets/css/fonts.css" rel="stylesheet" />
<link href="assets/css/index.css" rel="stylesheet" />
<link href="assets/css/menubar.css" rel="stylesheet" />
<!--<link rel="shortcut icon" type="image/png" href="assets/images/icon.png"/> -->
</head>
<body>
<div id="vue">
<div id="menubar">
<ul class="left">
<img src="../../icon.ico">
</ul>
<ul class="right">
<!-- Mettre ce code en ligne pour éviter que chrome ne met un espace automatiquement entre les éléments -->
<li id="minimize-btn"><i class="material-icons">minimize</i></li><!--<li id="max-unmax-btn"><i class="material-icons">crop_square</i></li>--><li id="close-btn"><i class="material-icons">close</i></li>
</ul>
</div>
<div id="fullscreen" :style="{ display: displayFullscreen }">
<div @click="closeFullscreen" id="close"><i class="material-icons">close</i></div>
<div id="settings" :style="{ display: displaySettings }">
<h2>Paramètres</h2>
<span href="" id="disconnect-btn" @click="disconnectBtn">Se déconnecter</span>
<h4>Allocation mémoire</h4>
<label for="minMem">mémoire minimale : <span id="outputMinMem">{{ minMemValue }}</span></label><br />
<input type="number" min="1024" :max="memMax" :step="memStep" v-model="minMemValue" class="slider" id="minMem"><br />
<label for="maxMem">mémoire maximale : <span id="outputMaxMem">{{ maxMemValue }}</span></label><br />
<input type="number" min="1024" :max="memMax" :step="memStep" v-model="maxMemValue" class="slider" id="maxMem"><br />
<h4>Au secours, mon jeu ne démarre pas</h4>
<button @click="invalidateData" :disabled="invalidateButtonDisabled">{{ invalidateButtonText }}</button><br />
<span @click="credits">Voir crédits</span>
</div>
<div id="credits" :style="{ display: displayCredits }">
<div class="content">
<p>BSD 3-Clause License</p>
<p>Copyright (c) 2021, Altarik<br />
All rights reserved.</p>
<p>Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:</p>
<ol>
<li>Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.</li>
<li>Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.</li>
<li>Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.</li>
</ol>
<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
</div>
</div>
</div>
<div id="content">
<div id="sidebar">
<h2>Chapitres</h2>
<div id="sidebar-content" @change="modsInformations">
<div v-if="modsInformationsLoaded === false">Une erreur est survenue lors de la récupération des informations, vérifiez votre connexion internet puis cliquez sur réessayez</div>
<div v-for="(item, index) in modsInformations" v-else-if="modsInformations.length !== 0" v-on:click="changeSelectedChapter(index)" :class="{ selected: isSelected(index) }">
<h3>{{ item.title }}</h3>
<p>{{ item.description}}</p>
</div>
<div v-else>Chargement en cours</div>
</div>
</div>
<div id="media">
<div @click="options" title="Paramètres">
<img src="assets/images/settings.png">
</div>
<div @click="discord" title="Rejoingnez notre Discord">
<img src="assets/images/discord.png">
</div>
<div @click="web"title="Visitez notre site web">
<img src="assets/images/web.png">
</div>
</div>
<div id="main">
<div id="account">
<div id="nick">{{ nick }}</div><!-- <img src=""> Head du joueur -->
</div>
<button @click="launchBtnClick" id="launch-btn" :disabled="launchBtnDisable">
<div id="launch-text" :class="[{hidden: launchBtnHidden}]">{{ launchBtnText }}</div>
<div id="loading-message" :class="[{hidden: loadingMessageHidden}]">{{ loadingMessageText }}</div>
<div id="fullprogressbar" :class="[{hidden: fullprogressbarHidden}]"><div id="progressbar" :style="{ width: progressbarWidth + '%' }"></div></div>
</button>
</div>
</div>
</div>
<script src="../../node_modules/izitoast/dist/js/iziToast.js" type="text/javascript"></script>
<script src="assets/js/preload.js"></script>
<script src="assets/js/index.js"></script>
</body>
</html>

View File

@ -1,44 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Altarik Launcher</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval';" />
<link rel="stylesheet" href="../../node_modules/izitoast/dist/css/iziToast.min.css">
<link href="assets/css/fonts.css" rel="stylesheet" />
<link href="assets/css/login.css" rel="stylesheet" />
<link href="assets/css/menubar.css" rel="stylesheet" />
<!-- <link rel="shortcut icon" type="image/png" href="assets/images/icon.png"/> -->
</head>
<body>
<div id="vue">
<div id="menubar">
<ul class="left">
<img src="../../icon.ico">
</ul>
<ul class="right">
<!-- Mettre ce code en ligne pour éviter que chrome ne met un espace automatiquement entre les éléments -->
<li id="minimize-btn"><i class="material-icons">minimize</i></li><!--<li id="max-unmax-btn"><i class="material-icons">crop_square</i></li>--><li id="close-btn"><i class="material-icons">close</i></li>
</ul>
</div>
<div id="content">
<div id="login">
<h3>{{ login }}</h3>
<hr>
<form v-on:submit="formSubmit" id="login-form">
<label for="nickname">{{ email }}:</label><br />
<input type="text" name="nickname" id="nickname"><br />
<label for="password">{{ password }}:</label><br />
<input type="password" name="password" id="password"><br />
<input type="submit" v-bind:value="send_credentials">
</form>
</div>
<button id="microsoft-button" v-on:click="microsoftButton">{{ microsoft_button }}</button>
</div>
</div>
<script src="../../node_modules/izitoast/dist/js/iziToast.js" type="text/javascript"></script>
<script src="assets/js/preload.js"></script>
<script src="assets/js/login.js"></script>
</body>
</html>

107
src/main/index.js Normal file
View File

@ -0,0 +1,107 @@
import { app, shell, BrowserWindow, autoUpdater, dialog, Menu, ipcMain } from 'electron'
import { dirname, join } from 'path'
import { optimizer, is } from '@electron-toolkit/utils'
import logger from 'electron-log'
import Updater from './updater.js'
import electronStartup from 'electron-squirrel-startup'
import install from './install.js'
import Mc from './minecraft.js'
import { minimizeWindow, closeWindow } from './menubar.js'
// import icon from '../../resources/icon.png?asset'
const updaterInstance = new Updater(app, autoUpdater, dialog, logger, showNotification)
updaterInstance.configUpdater()
const minecraft = new Mc()
minecraft.showNotification = showNotification
const __dirname = dirname(__filename)
const iconPath = join(__dirname, 'icon.ico')
let mainWindow
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1000,
height: 600,
resizable: false,
autoHideMenuBar: true,
icon: iconPath,
// ...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.mjs'),
sandbox: false,
nodeIntegration: true,
contextIsolation: false
},
frame: false
})
if (!is.dev) {
Menu.setApplicationMenu(null)
}
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
mainWindow.on('close', () => {
app.quit()
})
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
function showNotification(title, body = '', clazz = 'info') {
mainWindow.webContents.send('notification', { title, body, class: clazz })
}
ipcMain.on('minimizeWindow', () => {
minimizeWindow(mainWindow)
})
ipcMain.on('closeWindow', () => {
closeWindow(mainWindow)
})
ipcMain.on('microsoft-login', (event) => {
minecraft.microsoftLogin(event, mainWindow)
})
ipcMain.on('invalidateData', (event) => {
minecraft.invalidateData(event)
})
ipcMain.on('launch', (event, args) => {
minecraft.launch(event, args)
})
function main() {
if (electronStartup) {
install(app)
app.quit()
return null
}
app.whenReady().then(() => {
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
mainWindow = createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
}
main()

View File

@ -1,4 +1,4 @@
import isDev from 'electron-is-dev' import { is } from '@electron-toolkit/utils'
import mlc from 'minecraft-launcher-core' import mlc from 'minecraft-launcher-core'
import fetch from 'node-fetch' import fetch from 'node-fetch'
import { hashFile } from 'hasha' import { hashFile } from 'hasha'
@ -32,7 +32,7 @@ export default class Minecraft {
*/ */
login (event, win, username, password) { login (event, win, username, password) {
this.auth = null this.auth = null
if (isDev || password.trim() !== '') { if (is.dev || password.trim() !== '') {
this.auth = Authenticator.getAuth(username, password) this.auth = Authenticator.getAuth(username, password)
this.auth.then(v => { this.auth.then(v => {
win.loadFile('src/client/index.html') win.loadFile('src/client/index.html')

View File

@ -1,4 +1,4 @@
import isDev from 'electron-is-dev' import { is } from '@electron-toolkit/utils'
import fetch from 'node-fetch' import fetch from 'node-fetch'
import pkg from '../../package.json' assert {type: 'json'} import pkg from '../../package.json' assert {type: 'json'}
@ -19,7 +19,7 @@ export default class Updater {
this.logger.info(`Node version: ${process.versions.node}`) this.logger.info(`Node version: ${process.versions.node}`)
this.logger.info(`platform: ${process.platform}`) this.logger.info(`platform: ${process.platform}`)
this.logger.info(`arch: ${process.arch}`) this.logger.info(`arch: ${process.arch}`)
if (isDev) { if (is.dev) {
this.logger.info(`developpement version ${this.app.getVersion()}`) this.logger.info(`developpement version ${this.app.getVersion()}`)
return return
} }
@ -33,7 +33,7 @@ export default class Updater {
} }
checkForUpdates (win, showNotification) { checkForUpdates (win, showNotification) {
if (isDev) { if (is.dev) {
win.loadFile('src/client/login.html') win.loadFile('src/client/login.html')
return return
} }

20
src/preload/index.js Normal file
View File

@ -0,0 +1,20 @@
import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}

17
src/renderer/index.html Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Electron</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
/>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

17
src/renderer/src/App.jsx Normal file
View File

@ -0,0 +1,17 @@
/* eslint-disable no-unused-vars */
import React, { useState } from 'react'
import CheckingUpdate from './components/CheckingUpdate'
import Login from './components/Login'
import Main from './components/Main'
import Menubar from './components/Menubar'
export default function App() {
const [checkingUpdate, setCheckingUpdate] = useState(true)
const [login, setLogin] = useState(false)
return (
<>
<Menubar />
{checkingUpdate ? <CheckingUpdate /> : login ? <Login /> : <Main />}
</>
)
}

View File

@ -32,3 +32,8 @@
font-family: "French-Press"; font-family: "French-Press";
src: url("../fonts/Frenchpress.otf") format("OpenType"); src: url("../fonts/Frenchpress.otf") format("OpenType");
} }
.material-icons{
position: relative;
top: 2px;
}

View File

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

View File

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

View File

Before

Width:  |  Height:  |  Size: 375 KiB

After

Width:  |  Height:  |  Size: 375 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -0,0 +1,6 @@
/* eslint-disable no-unused-vars */
import React, { useState } from 'react'
export default function CheckingUpdate() {
return <>Bonjour</>
}

View File

@ -0,0 +1,6 @@
/* eslint-disable no-unused-vars */
import React from "react"
export default function Fullscreen() {
return <></>
}

View File

@ -0,0 +1,6 @@
/* eslint-disable no-unused-vars */
import React from 'react'
export default function Login() {
return <></>
}

View File

@ -0,0 +1,6 @@
/* eslint-disable no-unused-vars */
import React from 'react'
export default function Main() {
return <></>
}

View File

@ -0,0 +1,24 @@
/* eslint-disable no-unused-vars */
import React from 'react'
import icon from '../../../../icon.ico'
export default function Menubar() {
return (
<div
className="fixed top-0 left-0 w-full h-7 bg-slate-900 text-white cursor-move select-none z-50 *:m-0 *:p-0"
style={{ 'webkit-app-region': 'drag' }}
>
<ul className="float-left">
<img src={icon} className="pl-4 h-7 w-auto" />
</ul>
<ul className="float-right *:inline-block *:h-7 *:hover:cursor-pointer *:hover:transition-all *:ease-in *:duration-300">
<li className="hover:bg-slate-800">
<i className="material-icons">minimize</i>
</li>
<li className="hover:bg-red-600">
<i className="material-icons">close</i>
</li>
</ul>
</div>
)
}

12
src/renderer/src/main.jsx Normal file
View File

@ -0,0 +1,12 @@
/* eslint-disable no-unused-vars */
import React from 'react'
import ReactDOM from 'react-dom/client'
import './assets/index.css'
import './assets/css/fonts.css'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)

View File

@ -1,112 +0,0 @@
import { app, BrowserWindow, Menu, ipcMain, autoUpdater, dialog } from 'electron'
import isDev from 'electron-is-dev'
import logger from 'electron-log'
import { join, dirname } from 'path'
import Updater from './updater.js'
import electronStartup from 'electron-squirrel-startup'
import install from './install.js'
import Mc from './minecraft.js'
import { minimizeWindow, closeWindow } from './menubar.js'
import { fileURLToPath } from 'url'
const updaterInstance = new Updater(app, autoUpdater, dialog, logger, showNotification)
updaterInstance.configUpdater()
const minecraft = new Mc()
minecraft.showNotification = showNotification
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const iconPath = join(__dirname, 'icon.ico')
let win = null
function createWindow () {
win = new BrowserWindow({
width: 1000,
height: 600,
resizable: false,
icon: iconPath,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
frame: false
})
if (!isDev) {
Menu.setApplicationMenu(null)
}
win.loadFile('src/client/checkingUpdate.html')
win.on('close', () => {
app.quit()
})
}
function showNotification (title, body = '', clazz = 'info') {
win.webContents.send('notification', { title, body, class: clazz })
}
ipcMain.on('disconnect', () => {
minecraft.auth = null
win.loadFile('src/client/login.html').then(() => showNotification('Déconnecté', 'Vous avez été déconnecté de votre compte', 'success'))
})
ipcMain.on('pageReady', (event) => {
event.sender.send('nick', { name: minecraft.auth.name })
minecraft.getModsInformations(event)
})
ipcMain.on('checking-update', () => {
updaterInstance.checkForUpdates(win, showNotification)
})
function main () {
if (electronStartup) {
install(app)
app.quit()
return
}
app.whenReady().then(() => {
createWindow()
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
ipcMain.on('minimizeWindow', () => {
minimizeWindow(win)
})
ipcMain.on('closeWindow', () => {
closeWindow(win)
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
ipcMain.on('login', (event, args) => {
minecraft.login(event, win, args.user, args.pass)
})
ipcMain.on('microsoft-login', (event) => {
minecraft.microsoftLogin(event, win)
})
ipcMain.on('invalidateData', event => {
minecraft.invalidateData(event)
})
ipcMain.on('launch', (event, args) => {
minecraft.launch(event, args)
})
}
main()

9
tailwind.config.js Normal file
View File

@ -0,0 +1,9 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {}
},
plugins: [],
darkMode: 'class'
}

1955
yarn.lock

File diff suppressed because it is too large Load Diff