From: Daniel Karbach Date: Tue, 10 May 2022 10:51:51 +0000 (+0200) Subject: client side aos base rom select X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=8f8c68f00151100f2a2fa535492847ddb6b7029a;p=alttp.git client side aos base rom select --- diff --git a/.env.example b/.env.example index 071eccf..3774044 100644 --- a/.env.example +++ b/.env.example @@ -60,3 +60,5 @@ LARASCORD_SCOPE=identify DISCORD_BOT_TOKEN= DISCORD_BOT_CREATE_COMMANDS= DISCORD_BOT_ENABLE_COMMANDS= + +AOS_HOSTNAME=aos.localhorst.tv diff --git a/config/aos.php b/config/aos.php new file mode 100644 index 0000000..d093775 --- /dev/null +++ b/config/aos.php @@ -0,0 +1,5 @@ + env('AOS_HOSTNAME', 'aos.localhorst.tv'), +]; diff --git a/package-lock.json b/package-lock.json index 181e184..bd34127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", "laravel-echo": "^1.11.3", + "localforage": "^1.10.0", "moment": "^2.29.1", "numeral": "^2.0.6", "pusher-js": "^7.0.6", @@ -6624,6 +6625,11 @@ "imagemin": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, "node_modules/immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", @@ -7437,6 +7443,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", @@ -7487,6 +7501,14 @@ "json5": "lib/cli.js" } }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -17132,6 +17154,11 @@ "loader-utils": "^1.1.0" } }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, "immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", @@ -17710,6 +17737,14 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "requires": { + "immediate": "~3.0.5" + } + }, "lilconfig": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", @@ -17750,6 +17785,14 @@ } } }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "requires": { + "lie": "3.1.1" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", diff --git a/package.json b/package.json index d494074..a71398d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "eslintConfig": { "env": { "browser": true, - "es6": true, + "es6": true, "node": true }, "extends": [ @@ -81,6 +81,7 @@ "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", "laravel-echo": "^1.11.3", + "localforage": "^1.10.0", "moment": "^2.29.1", "numeral": "^2.0.6", "pusher-js": "^7.0.6", diff --git a/resources/js/app.js b/resources/js/app.js index 47b7703..16e3e65 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -18,6 +18,13 @@ toastr.options.positionClass = 'toast-bottom-right'; * or customize the JavaScript scaffolding to fit your unique needs. */ +import AosApp from './components/aos/App'; import App from './components/App'; -ReactDOM.render(, document.getElementById('react-root')); +if (document.getElementById('aos-root')) { + ReactDOM.render(, document.getElementById('aos-root')); +} + +if (document.getElementById('react-root')) { + ReactDOM.render(, document.getElementById('react-root')); +} diff --git a/resources/js/components/aos/App.js b/resources/js/components/aos/App.js new file mode 100644 index 0000000..82f177c --- /dev/null +++ b/resources/js/components/aos/App.js @@ -0,0 +1,64 @@ +import axios from 'axios'; +import React, { useEffect, useState } from 'react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; + +import Header from './Header'; +import Front from '../pages/Front'; +import User from '../pages/User'; +import AosBaseRomProvider from '../../helpers/AosBaseRomContext'; +import UserContext from '../../helpers/UserContext'; + +const App = () => { + const [user, setUser] = useState(null); + + const checkAuth = async () => { + try { + const response = await axios.get('/api/user'); + setUser(response.data); + } catch (e) { + setUser(null); + } + }; + + const doLogout = async () => { + await axios.post('/logout'); + await checkAuth(); + }; + + useEffect(() => { + let timer = null; + axios + .get('/sanctum/csrf-cookie') + .then(() => { + checkAuth(); + timer = setInterval(checkAuth, 15 * 60 * 1000); + }); + return () => { + if (timer) clearInterval(timer); + }; + }, []); + + useEffect(() => { + window.Echo.channel('App.Control') + .listen('PleaseRefresh', () => { + location.reload(); + }); + return () => { + window.Echo.leave('App.Control'); + }; + }, []); + + return + + +
+ + } /> + } /> + + + + ; +}; + +export default App; diff --git a/resources/js/components/aos/BaseRomButton.js b/resources/js/components/aos/BaseRomButton.js new file mode 100644 index 0000000..fe9d930 --- /dev/null +++ b/resources/js/components/aos/BaseRomButton.js @@ -0,0 +1,39 @@ +import React from 'react'; +import { Button } from 'react-bootstrap'; +import { withTranslation } from 'react-i18next'; + +import i18n from '../../i18n'; + +import { useAosBaseRom } from '../../helpers/AosBaseRomContext'; + +const BaseRomButton = () => { + const { rom, setRom } = useAosBaseRom(); + + const handleFile = React.useCallback(async e => { + if (e.target.files.length != 1) { + setRom(null); + } else { + const buf = await e.target.files[0].arrayBuffer(); + setRom(buf); + } + }, [setRom]); + + if (rom) return null; + + return + + + ; +}; + +export default withTranslation()(BaseRomButton); diff --git a/resources/js/components/aos/Header.js b/resources/js/components/aos/Header.js new file mode 100644 index 0000000..8d9415b --- /dev/null +++ b/resources/js/components/aos/Header.js @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Button, Container, Nav, Navbar } from 'react-bootstrap'; +import { LinkContainer } from 'react-router-bootstrap'; +import { withTranslation } from 'react-i18next'; + +import BaseRomButton from './BaseRomButton'; +import Icon from '../common/Icon'; +import LanguageSwitcher from '../common/LanguageSwitcher'; +import { getAvatarUrl } from '../../helpers/User'; +import { withUser } from '../../helpers/UserContext'; +import i18n from '../../i18n'; + +const Header = ({ doLogout, user }) => + + + + + AoS + + + + + + + + + +; + +Header.propTypes = { + doLogout: PropTypes.func, + user: PropTypes.shape({ + avatar: PropTypes.string, + discriminator: PropTypes.string, + id: PropTypes.string, + username: PropTypes.string, + }), +}; + +export default withTranslation()(withUser(Header)); diff --git a/resources/js/helpers/AosBaseRomContext.js b/resources/js/helpers/AosBaseRomContext.js new file mode 100644 index 0000000..f187c73 --- /dev/null +++ b/resources/js/helpers/AosBaseRomContext.js @@ -0,0 +1,52 @@ +import CRC32 from 'crc-32'; +import localforage from 'localforage'; +import PropTypes from 'prop-types'; +import React from 'react'; +import toastr from 'toastr'; + +import i18n from '../i18n'; + +const AosBaseRomContext = React.createContext(null); + +const AosBaseRomProvider = ({ children }) => { + const [rom, setRom] = React.useState(null); + + const setRomCallback = React.useCallback(buffer => { + if (buffer) { + const crc = CRC32.buf(new Uint8Array(buffer)); + if (crc === 0x35536183) { + setRom(buffer); + localforage.setItem('aosBaseRom', buffer); + toastr.success(i18n.t('aos.baseRomSet')); + } else { + toastr.error(i18n.t('aos.baseRomInvalid')); + } + } else { + setRom(null); + localforage.removeItem('aosBaseRom'); + toastr.success(i18n.t('aos.baseRomRemoved')); + } + }, [setRom]); + + React.useEffect(async () => { + const stored = await localforage.getItem('aosBaseRom'); + if (stored) { + const crc = CRC32.buf(new Uint8Array(stored)); + if (crc == 0x35536183) { + setRom(stored); + } + } + }, []); + + return + {children} + ; +}; + +AosBaseRomProvider.propTypes = { + children: PropTypes.node, +}; + +export const useAosBaseRom = () => React.useContext(AosBaseRomContext); + +export default AosBaseRomProvider; diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 331346e..dcc3d68 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -1,6 +1,12 @@ /* eslint-disable max-len */ export default { translation: { + aos: { + baseRomInvalid: 'CRC32 Check fehlgeschlagen (brauche 35:53:61:83). Falsche ROM Datei?', + baseRomRemoved: 'Base ROM entfernt.', + baseRomSet: 'Base ROM gespeichert.', + setBaseRom: 'Base ROM auswählen', + }, applications: { accept: 'Annehmen', acceptError: 'Fehler beim Annehmen', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index de9b303..5cc5ed1 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -1,6 +1,12 @@ /* eslint-disable max-len */ export default { translation: { + aos: { + baseRomInvalid: 'CRC32 mismatch (need 35:53:61:83). Wrong ROM file?', + baseRomRemoved: 'Base ROM removed.', + baseRomSet: 'Base ROM set.', + setBaseRom: 'Set base ROM', + }, applications: { accept: 'Accept', acceptError: 'Error accepting', diff --git a/resources/views/aos.blade.php b/resources/views/aos.blade.php new file mode 100644 index 0000000..37bb874 --- /dev/null +++ b/resources/views/aos.blade.php @@ -0,0 +1,20 @@ + + + + + + + AoS + + + + + + + + + +
+
+ + diff --git a/routes/web.php b/routes/web.php index 450d21f..375962d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,6 +17,10 @@ use Illuminate\Support\Facades\Route; Route::get('/sitemap.xml', [SitemapXmlController::class, 'index']); +Route::domain(config('aos.hostname'))->group(function() { + Route::view('/{path?}', 'aos')->where('path', '.*'); +}); + Route::view('/{path?}', 'app')->where('path', '.*'); Route::group(['prefix' => config('larascord.prefix'), 'middleware' => ['web']], function() {