Implement 🥚

This commit is contained in:
grimsi
2024-09-27 11:04:13 +02:00
parent e47ab8405f
commit c8eaf0fb54
4 changed files with 102 additions and 23 deletions
+56
View File
@@ -44,6 +44,7 @@
"moment-timezone": "^0.5.45", "moment-timezone": "^0.5.45",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "18.3.1", "react": "18.3.1",
"react-confetti-boom": "^1.0.0",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-router-dom": "6.26.1", "react-router-dom": "6.26.1",
"sonner": "^1.5.0", "sonner": "^1.5.0",
@@ -1959,6 +1960,25 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@emotion/is-prop-valid": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz",
"integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@emotion/memoize": "0.7.1"
}
},
"node_modules/@emotion/memoize": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
"integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==",
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -12213,6 +12233,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-confetti-boom": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-confetti-boom/-/react-confetti-boom-1.0.0.tgz",
"integrity": "sha512-JPCs1tx36xduFdMORaRLLvFJABAQUgjEqKvqZiBZr8bUkHsCzbHeesnfQwZU1LExmGiYVFGIJS/tiR4OUTktww==",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@@ -16299,6 +16332,23 @@
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
"@emotion/is-prop-valid": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz",
"integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==",
"optional": true,
"peer": true,
"requires": {
"@emotion/memoize": "0.7.1"
}
},
"@emotion/memoize": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
"integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==",
"optional": true,
"peer": true
},
"@esbuild/aix-ppc64": { "@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -23397,6 +23447,12 @@
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
} }
}, },
"react-confetti-boom": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-confetti-boom/-/react-confetti-boom-1.0.0.tgz",
"integrity": "sha512-JPCs1tx36xduFdMORaRLLvFJABAQUgjEqKvqZiBZr8bUkHsCzbHeesnfQwZU1LExmGiYVFGIJS/tiR4OUTktww==",
"requires": {}
},
"react-dom": { "react-dom": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+4 -2
View File
@@ -39,6 +39,7 @@
"moment-timezone": "^0.5.45", "moment-timezone": "^0.5.45",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "18.3.1", "react": "18.3.1",
"react-confetti-boom": "^1.0.0",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-router-dom": "6.26.1", "react-router-dom": "6.26.1",
"sonner": "^1.5.0", "sonner": "^1.5.0",
@@ -122,7 +123,8 @@
"@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles", "@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles",
"cron-validator": "$cron-validator", "cron-validator": "$cron-validator",
"moment": "$moment", "moment": "$moment",
"moment-timezone": "$moment-timezone" "moment-timezone": "$moment-timezone",
"react-confetti-boom": "$react-confetti-boom"
}, },
"vaadin": { "vaadin": {
"dependencies": { "dependencies": {
@@ -181,6 +183,6 @@
"workbox-core": "7.1.0", "workbox-core": "7.1.0",
"workbox-precaching": "7.1.0" "workbox-precaching": "7.1.0"
}, },
"hash": "9af3f915316df3b36e94b855108f7148a48217645c62f7b0e5a970f3030981b5" "hash": "e5da4862f0edd6fde265ae72b8f5ec20ca6276687df8b437c1ebf9324053fb01"
} }
} }
+4 -8
View File
@@ -18,22 +18,18 @@ export default function LoginView() {
const [url, setUrl] = useState<string>(); const [url, setUrl] = useState<string>();
const [signUpAllowed, setSignUpAllowed] = useState<boolean>(false); const [signUpAllowed, setSignUpAllowed] = useState<boolean>(false);
useEffect(() => {
RegistrationEndpoint.isSelfRegistrationAllowed().then(setSignUpAllowed);
}, []);
useEffect(() => { useEffect(() => {
if (state.user) { if (state.user) {
const path = url ? new URL(url, document.baseURI).pathname : '/' const path = url ? new URL(url, document.baseURI).pathname : '/'
navigate(path, {replace: true}); navigate(path, {replace: true});
} else {
RegistrationEndpoint.isSelfRegistrationAllowed().then(setSignUpAllowed);
} }
}, [state.user]); }, [state.user]);
async function tryLogin(values: any, formik: any) { async function tryLogin(values: any, formik: any) {
const {defaultUrl, error, redirectUrl} = await login(values.username, values.password); const {error} = await login(values.username, values.password);
if (!error) { if (error) {
setUrl(redirectUrl ?? defaultUrl ?? '/');
} else {
formik.setFieldError("username", " "); // Mark the field red, but don't show an error message formik.setFieldError("username", " "); // Mark the field red, but don't show an error message
formik.setFieldError("password", "Invalid username and/or password."); formik.setFieldError("password", "Invalid username and/or password.");
} }
+37 -12
View File
@@ -1,24 +1,46 @@
import {useRouteMetadata} from 'Frontend/util/routing.js'; import {useRouteMetadata} from 'Frontend/util/routing.js';
import {useEffect} from 'react'; import {useEffect, useState} from 'react';
import ProfileMenu from "Frontend/components/ProfileMenu"; import ProfileMenu from "Frontend/components/ProfileMenu";
import {Divider, Link, Navbar, NavbarBrand, NavbarContent, NavbarItem} from "@nextui-org/react"; import {Divider, Link, Navbar, NavbarBrand, NavbarContent, NavbarItem} from "@nextui-org/react";
import GameyfinLogo from "Frontend/components/theming/GameyfinLogo"; import GameyfinLogo from "Frontend/components/theming/GameyfinLogo";
import * as PackageJson from "../../../../package.json"; import * as PackageJson from "../../../../package.json";
import {Outlet, useNavigate} from "react-router-dom"; import {Outlet, useNavigate} from "react-router-dom";
import {useAuth} from "Frontend/util/auth"; import {useAuth} from "Frontend/util/auth";
import {Heart} from "@phosphor-icons/react";
import Confetti, {ConfettiProps} from "react-confetti-boom";
export default function MainLayout() { export default function MainLayout() {
const navigate = useNavigate(); const navigate = useNavigate();
const auth = useAuth(); const auth = useAuth();
const routeMetadata = useRouteMetadata(); const routeMetadata = useRouteMetadata();
const [isExploding, setIsExploding] = useState(false);
useEffect(() => { useEffect(() => {
let newTitle = `Gameyfin - ${routeMetadata?.title}` ?? 'Gameyfin'; let newTitle = `Gameyfin - ${routeMetadata?.title}` ?? 'Gameyfin';
window.addEventListener('popstate', () => document.title = newTitle); window.addEventListener('popstate', () => document.title = newTitle);
}, []); }, []);
const confettiProps: ConfettiProps = {
mode: 'boom',
x: 0.5,
y: 1,
particleCount: 1000,
spreadDeg: 90,
launchSpeed: 4,
effectInterval: 10000
}
function easterEgg() {
if (isExploding) return;
setIsExploding(true);
if (confettiProps.mode === "boom") {
setTimeout(() => setIsExploding(false), confettiProps.effectInterval);
}
}
return ( return (
<div className="flex flex-col min-h-svh"> <div className="flex flex-col min-h-svh">
{isExploding ? <Confetti {...confettiProps}/> : <></>}
<div className="flex flex-col flex-grow w-full 2xl:w-3/4 m-auto"> <div className="flex flex-col flex-grow w-full 2xl:w-3/4 m-auto">
<Navbar maxWidth="full"> <Navbar maxWidth="full">
<NavbarBrand as="button" onClick={() => navigate('/')}> <NavbarBrand as="button" onClick={() => navigate('/')}>
@@ -44,17 +66,20 @@ export default function MainLayout() {
</div> </div>
<Divider/> <Divider/>
<footer className="flex flex-row items-center justify-between py-4 px-12"> <div className="flex flex-col w-full 2xl:w-3/4 m-auto">
<p>Gameyfin {PackageJson.version}</p> <footer className="flex flex-row items-center justify-between py-4 px-12">
<p> <p>Gameyfin {PackageJson.version}</p>
&copy; {(new Date()).getFullYear()}&ensp; <p className="flex flex-row gap-1 items-baseline">
<Link href="https://github.com/gameyfin/gameyfin/graphs/contributors" target="_blank"> Made with
Gameyfin contributors <Heart size={16} weight="fill" className="text-primary" onClick={easterEgg}/>
</Link> by
</p> <Link href="https://github.com/grimsi" target="_blank">grimsi</Link> and
</footer> <Link href="https://github.com/gameyfin/gameyfin/graphs/contributors" target="_blank">
contributors
</Link>
</p>
</footer>
</div>
</div> </div>
); );
} }
/**/