Merge branch 'adjust/themes' into 'main'

Merge Theme Adjustments

See merge request neshura-websites/www!10
This commit is contained in:
Neshura 2022-12-18 22:20:42 +00:00
commit 7c7d60bab7
19 changed files with 280 additions and 185 deletions

12
.gitignore vendored
View file

@ -1,9 +1,9 @@
# do not track installed modules # do not track installed modules
node_modules/ /node_modules/
.vscode/ /.vscode/
# do not track built files # do not track built files
.next/ /.next/
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
@ -14,6 +14,6 @@ yarn-error.log*
.pnpm-debug.log* .pnpm-debug.log*
# production # production
build/ /build/
data/ /data/
confs/ /confs/

1
components/fetcher.tsx Normal file
View file

@ -0,0 +1 @@
export const fetcher = (url:string) => fetch(url).then((res) => res.json());

View file

@ -1,15 +1,14 @@
import PageFooter from './footer' import PageFooter from './footer';
import PageNavbar from './navbar' import PageNavbar from './navbar';
import Script from 'next/script' import Script from 'next/script';
import { Page, Main } from './styles/generic' import { Page, Main } from './styles/generic';
const Layout = ({ children }: { children: React.ReactNode }) => { const Layout = ({ children }: { children: React.ReactNode }) => {
return ( return (
<Page> <Page>
<Script id="matomo_analytics" <Script id="matomo_analytics"
defer src='https://static.cloudflareinsights.com/beacon.min.js' defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'> data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
</Script> </Script>
<Script id="matomo_analytics"> <Script id="matomo_analytics">
{` {`

View file

@ -1,13 +1,7 @@
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { NavBar, NavLink, NavWrap } from './styles/navbar'; import { NavBar, NavLink, NavWrap } from './styles/navbar';
import { StyleSelector, StyleSelectorPlaceholder } from './themeselector'; import { StyleSelector, StyleSelectorPlaceholder } from './themeselector';
import Links from '../public/data/navbar.json';
const navLinks = [
{ name: "Home", href: "/" },
{ name: "About", href: "/about" },
{ name: "Games", href: "/games" },
{ name: "Services", href: "/services" }
]
const PageNavbar = () => { const PageNavbar = () => {
const path = usePathname(); const path = usePathname();
@ -16,8 +10,8 @@ const PageNavbar = () => {
<NavWrap> <NavWrap>
<StyleSelector></StyleSelector> <StyleSelector></StyleSelector>
<NavBar> <NavBar>
{navLinks.map((item) => ( {Links.links.map((item) => (
<NavLink active={path == item.href ? true : false} key={item.name} href={item.href}> <NavLink active={path === item.href ? +true : +false} key={item.name} href={item.href}>
{item.name} {item.name}
</NavLink> </NavLink>
))} ))}

View file

@ -12,17 +12,23 @@ interface OnlinePropType {
// replaces .title // replaces .title
export const PageTitle = styled.h1` export const PageTitle = styled.h1`
margin: 0; margin: 0;
padding: 0.25rem 1rem;
line-height: 1.15; line-height: 1.15;
font-size: 4rem; font-size: 4rem;
text-align: center; text-align: center;
background-color: ${({ theme }) => theme.colors.background ? theme.colors.background : ""};
border-radius: 15px;
`; `;
// replaces .description // replaces .description
export const PageDescription = styled.p` export const PageDescription = styled.p`
background-color: ${({ theme }) => theme.colors.background ? theme.colors.background : ""};
padding: 0.25rem 0.5rem;
margin: 4rem 0; margin: 4rem 0;
line-height: 1.5; line-height: 1.5;
font-size: 1.5rem; font-size: 1.5rem;
text-align: center; text-align: center;
border-radius: 10px;
`; `;
// replaces .grid // replaces .grid
@ -43,25 +49,26 @@ const CardStyle = css`
height: 240px; height: 240px;
`; `;
const CardLink = styled(Link)` export const CardLink = styled(Link)`
${CardStyle} ${CardStyle}
`; `;
const CardStyleWrap = styled.div` export const CardStyleWrap = styled.div`
${CardStyle} ${CardStyle}
`; `;
// replaces .card & .contentcard // replaces .card & .contentcard
export const PageCard = styled.div` export const PageCard = styled.div`
margin: 1rem; margin: 1rem;
padding: 1.5rem 0.7rem 1.5rem 0.7rem; padding: 23px 10px;
text-align: center; text-align: center;
color: ${({ theme }) => theme.colors.primary}; color: ${({ theme }) => theme.colors.primary};
background-color: ${({ theme }) => theme.colors.background};
text-decoration: none; text-decoration: none;
border: 1px solid; border: 2px solid;
border-radius: 10px; border-radius: 10px;
border-color: ${({ theme }) => theme.colors.primary}; border-color: ${({ theme }) => theme.colors.primary};
transition: color 0.15s ease, border-color 0.15s ease; transition: all 0.1s linear;
width: 300px; width: 300px;
height: 200px; height: 200px;
display: flex; display: flex;
@ -82,11 +89,15 @@ export const PageCard = styled.div`
${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & { ${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.secondary};
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
${CardLink}:focus,${CardLink}:active,${CardLink}:hover & { ${CardLink}:focus,${CardLink}:active,${CardLink}:hover & {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.secondary};
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
`; `;
@ -110,7 +121,6 @@ const OnlineStatus = styled.p<OnlinePropType>`
return ret; return ret;
}}; }};
padding: 0.2rem; padding: 0.2rem;
background-color: ${({ theme }) => theme.colors.background};
border: 1px solid; border: 1px solid;
border-color: ${({ theme }) => theme.colors.primary}; border-color: ${({ theme }) => theme.colors.primary};
border-radius: 5px; border-radius: 5px;
@ -119,13 +129,25 @@ const OnlineStatus = styled.p<OnlinePropType>`
top: 100; right: 50; bottom: 0; left: 50; top: 100; right: 50; bottom: 0; left: 50;
offset-position: bottom 10px; offset-position: bottom 10px;
transition: color 0.15s ease, border-color 0.15s ease; transition: color 0.15s ease, border-color 0.15s ease;
background-color: ${({ theme }) => theme.colors.background};
background-image: ${({ theme }) => theme.backgroundImage ?
"linear-gradient("
+ theme.colors.background + "," + theme.colors.background +
"), url(" + theme.backgroundImage + ")" : ""};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & { ${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & {
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
${CardLink}:focus,${CardLink}:active,${CardLink}:hover & { ${CardLink}:focus,${CardLink}:active,${CardLink}:hover & {
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
`; `;
@ -136,16 +158,16 @@ const CardContentWarning = styled.p`
`; `;
// replaces .contentIcon // replaces .contentIcon
const CardContentTitleIcon = styled.div` const CardContentTitleIcon = styled(Image)`
object-fit: "contain"; object-fit: "contain";
margin-right: 0.4rem; margin-right: 8px;
position: relative;
aspect-ratio: 1; aspect-ratio: 1;
height: 1.5rem; height: 28px;
`; `;
// replaces .contentTitle // replaces .contentTitle
const CardContentTitleWrap = styled.div` const CardContentTitleWrap = styled.div`
position: relative;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@ -163,9 +185,7 @@ const CardContentTitle = ({ content }: { content: Service | Game }) => {
<CardContentTitleWrap> <CardContentTitleWrap>
{ {
content.icon ? ( content.icon ? (
<CardContentTitleIcon> <CardContentTitleIcon alt="icon" src={content.icon} width="28" height="28" sizes='10vw'/>
<Image alt="icon" src={content.icon} fill></Image>
</CardContentTitleIcon>
) : (<></>) ) : (<></>)
} }
<h2>{content.name}</h2> <h2>{content.name}</h2>

View file

@ -10,6 +10,10 @@ export const StyledBody = styled.body`
export const Page = styled.div` export const Page = styled.div`
width: 100%; width: 100%;
background-color: ${({ theme }) => theme.colors.background}; background-color: ${({ theme }) => theme.colors.background};
background-image: ${({ theme }) => theme.backgroundImage ? "url(" + theme.backgroundImage + ")" : ""};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
` `
export const Main = styled.main` export const Main = styled.main`

View file

@ -2,7 +2,7 @@ import styled from 'styled-components'
import Link from 'next/link'; import Link from 'next/link';
interface ActivePropType { interface ActivePropType {
active?: false | true; active?: number;
} }
export const NavWrap = styled.div` export const NavWrap = styled.div`
@ -23,19 +23,34 @@ export const NavBar = styled.nav`
align-items: center; align-items: center;
` `
export const NavLink = styled(Link)<ActivePropType>` export const NavLink = styled(Link) <ActivePropType>`
color: ${props => props.active ? ({ theme }) => theme.colors.secondary : ({ theme }) => theme.colors.primary}; color: ${props => props.active ?
({ theme }) => theme.invertButtons ?
theme.colors.text ? theme.colors.text : theme.colors.secondary : theme.colors.secondary :
({ theme }) => theme.colors.primary};
background-color: ${props => props.active ?
({ theme }) => theme.invertButtons ?
theme.colors.secondary : theme.colors.background :
({ theme }) => theme.colors.background};
padding: 2px 6px;
border: 2px solid;
margin: 0.2rem; margin: 0.2rem;
border: 1px solid;
border-radius: 5px; border-radius: 5px;
padding: 0.2rem 0.5rem;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
transition: color 0.15s ease, border-color 0.15s ease; transition: all 0.1s ease;
&:hover { &:hover {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.invertButtons ?
} theme.colors.text ? theme.colors.text : theme.colors.primary :
theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.secondary :
theme.colors.background};
}
` `

View file

@ -1,36 +1,46 @@
import styled from 'styled-components'; import styled from 'styled-components';
interface DisplayPropType { interface DisplayPropType {
show?: false | true; focus?: number,
show?: number;
} }
interface ActivePropType { interface ActivePropType {
active?: false | true; active?: number;
} }
export const ThemeDropDown = styled.div` export const ThemeDropDown = styled.div`
margin-left: 1%; margin-left: 1%;
min-width: 180px; min-width: 180px;
color: ${({ theme }) => theme.colors.primary} color: ${({ theme }) => theme.colors.primary};
display: flex;
flex-direction: column;
` `
export const ThemeDropDownButton = styled.button<DisplayPropType>` export const ThemeDropDownButton = styled.button<DisplayPropType>`
width: 160px; width: 160px;
border: 1px solid; border: 2px solid;
border-radius: 5px; border-radius: 5px;
background-color: ${({ theme }) => theme.colors.background}; background-color: ${ props => props.focus ?
padding: 0.2rem 0.5rem; ({ theme }) => theme.invertButtons ? theme.colors.secondary : theme.colors.background :
({ theme }) => theme.colors.background};
padding: 2px 6px;
cursor: pointer; cursor: pointer;
color: ${({ theme }) => theme.colors.primary}; color: ${props => props.focus ? ({ theme }) => theme.invertButtons ?
theme.colors.text ? theme.colors.text : theme.colors.primary : theme.colors.secondary :
({ theme }) => theme.colors.primary};
transition-property: color, border-bottom-left-radius, border-bottom-right-radius; transition-property: color, border-bottom-left-radius, border-bottom-right-radius, background-color;
transition-timing-function: ease; transition-timing-function: ease;
transition-duration: 0.15s; transition-duration: 0.15s;
transition-delay: 0s, ${ props => props.show ? "0s, 0s" : "0.6s, 0.6s" }, 0s;
&:focus,:hover { &:focus,:hover {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.invertButtons ?
theme.colors.text ? theme.colors.text : theme.colors.primary :
theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.secondary :
theme.colors.background};
} }
border-bottom-left-radius: ${ props => props.show ? "0" : "" }; border-bottom-left-radius: ${ props => props.show ? "0" : "" };
@ -40,7 +50,14 @@ export const ThemeDropDownButton = styled.button<DisplayPropType>`
export const ThemeDropDownOptions = styled.div<DisplayPropType>` export const ThemeDropDownOptions = styled.div<DisplayPropType>`
position: absolute; position: absolute;
color: ${({ theme }) => theme.colors.primary}; color: ${({ theme }) => theme.colors.primary};
background-color: ${({ theme }) => theme.colors.background}; background-image: ${({ theme }) => theme.backgroundImage ?
"linear-gradient("
+ theme.colors.background + "," + theme.colors.background +
"), url(" + theme.backgroundImage + ")" : ""};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 160px; min-width: 160px;
@ -49,7 +66,7 @@ export const ThemeDropDownOptions = styled.div<DisplayPropType>`
border-radius: 5px; border-radius: 5px;
border-top-left-radius: 0px; border-top-right-radius: 0px; border-top-left-radius: 0px; border-top-right-radius: 0px;
z-index: 1; z-index: 1;
overflow: hidden; overflow: ${ props => props.show ? "scroll" : "hidden" };
max-height: ${ props => props.show ? "20%" : "0%" }; max-height: ${ props => props.show ? "20%" : "0%" };
visibility: ${ props => props.show ? "visible" : "hidden" }; visibility: ${ props => props.show ? "visible" : "hidden" };
@ -61,7 +78,8 @@ export const ThemeDropDownOptions = styled.div<DisplayPropType>`
export const ThemeDropDownOption = styled.button<ActivePropType>` export const ThemeDropDownOption = styled.button<ActivePropType>`
color: ${ props => props.active ? ({ theme }) => theme.colors.secondary : ({ theme }) => theme.colors.primary }; color: ${ props => props.active ? ({ theme }) => theme.colors.secondary : ({ theme }) => theme.colors.primary };
background-color: ${({ theme }) => theme.colors.background}; background-color: transparent;
cursor: pointer;
align-self: center; align-self: center;
border: 0px solid; border: 0px solid;
padding: 0.2rem 0.5rem; padding: 0.2rem 0.5rem;

View file

@ -56,17 +56,4 @@ export const amoledTheme: DefaultTheme = {
loading: '#ff5300', loading: '#ff5300',
offline: '#ff0000', offline: '#ff0000',
}, },
}
export const amoled2Theme: DefaultTheme = {
themeName: "AMOLED Theme 2",
themeId: 3,
colors: {
background: '#000000',
primary: '#00ffaa',
secondary:'#aa00ff',
online: '#00ff00',
loading: '#ff5300',
offline: '#ff0000',
},
} }

View file

@ -1,17 +1,12 @@
import { useUpdateTheme } from "../pages/_app"; import { useUpdateTheme } from "../pages/_app";
import { Fragment, useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { ThemeContext, DefaultTheme } from "styled-components"; import { ThemeContext, DefaultTheme } from "styled-components";
import { darkTheme, amoledTheme, lightTheme, amoled2Theme } from './themes'; import { darkTheme, lightTheme } from './themes';
import { ThemeDropDown, ThemeDropDownButton, ThemeDropDownOption, ThemeDropDownOptions } from "./styles/themedropdown"; import { ThemeDropDown, ThemeDropDownButton, ThemeDropDownOption, ThemeDropDownOptions } from "./styles/themedropdown";
import Themes from '../public/data/themes.json';
const themes = [
lightTheme,
darkTheme,
amoledTheme,
amoled2Theme,
]
export const StyleSelector = () => { export const StyleSelector = () => {
const themes: DefaultTheme[] = Themes.themes;
const updateTheme = useUpdateTheme(); const updateTheme = useUpdateTheme();
const currentTheme = useContext(ThemeContext); const currentTheme = useContext(ThemeContext);
const [selectedTheme, setSelectedTheme] = useState(themes[currentTheme.themeId]); const [selectedTheme, setSelectedTheme] = useState(themes[currentTheme.themeId]);
@ -21,7 +16,6 @@ export const StyleSelector = () => {
} }
const updateThemeWithStorage = (newTheme: DefaultTheme) => { const updateThemeWithStorage = (newTheme: DefaultTheme) => {
if (newTheme.themeId === lightTheme.themeId) { if (newTheme.themeId === lightTheme.themeId) {
updateLightTheme(newTheme); updateLightTheme(newTheme);
} }
@ -38,27 +32,25 @@ export const StyleSelector = () => {
localStorage.setItem("theme", newTheme.themeId.toString()); localStorage.setItem("theme", newTheme.themeId.toString());
updateTheme(newTheme) updateTheme(newTheme)
} }
else {
}
} }
const [test, setTest] = useState(false); const [visible, setVisible] = useState(false);
const [buttonFocus, setButtonFocus] = useState(visible);
function handleBlur(event:any) { function handleBlur(event:any) {
console.log(event)
if (!event.currentTarget.contains(event.relatedTarget)) { if (!event.currentTarget.contains(event.relatedTarget)) {
setTest(false); setButtonFocus(false);
setVisible(false);
} }
} }
return ( return (
<ThemeDropDown onBlur={(event) => handleBlur(event)}> <ThemeDropDown onBlur={(event) => handleBlur(event)}>
<ThemeDropDownButton show={test} onClick={() => setTest(test => !test)}>{selectedTheme.themeName}</ThemeDropDownButton> <ThemeDropDownButton focus={+buttonFocus} show={+visible} onFocus={() => setButtonFocus(true)} onClick={() => setVisible(visible => !visible)}>{selectedTheme.themeName}
<ThemeDropDownOptions id="themesDropdown" show={test}> </ThemeDropDownButton>
<ThemeDropDownOptions id="themesDropdown" show={+visible}>
{themes.map((theme) => ( {themes.map((theme) => (
<ThemeDropDownOption active={theme === selectedTheme} key={theme.themeId} onClick={() => updateThemeWithStorage(theme)}> <ThemeDropDownOption active={theme.themeId === selectedTheme.themeId ? 1 : 0} key={theme.themeId} onClick={() => updateThemeWithStorage(theme)}>
{theme.themeName} {theme.themeName}
</ThemeDropDownOption> </ThemeDropDownOption>
))} ))}
@ -73,27 +65,14 @@ export const StyleSelectorPlaceholder = () => {
) )
} }
export function getTheme(themeId: number): DefaultTheme { export function getTheme(themeId: number, themes: DefaultTheme[]): DefaultTheme {
let theme: DefaultTheme; let retTheme: DefaultTheme = darkTheme;
switch (themeId) { themes.forEach((theme) => {
case 0: if (theme.themeId === themeId) { retTheme = theme};
theme = lightTheme; })
break;
case 1:
theme = darkTheme;
break;
case 2:
theme = amoledTheme;
break;
case 3:
theme = amoled2Theme;
break;
default:
theme = darkTheme
}
return theme; return retTheme;
} }
export default StyleSelector; export default StyleSelector;

View file

@ -7,6 +7,7 @@ import { DefaultTheme, ThemeProvider } from 'styled-components';
import { createContext, useContext, useEffect, useState } from 'react' import { createContext, useContext, useEffect, useState } from 'react'
import { darkTheme, GlobalStyle } from '../components/themes'; import { darkTheme, GlobalStyle } from '../components/themes';
import { getTheme } from '../components/themeselector'; import { getTheme } from '../components/themeselector';
import Themes from '../public/data/themes.json';
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & { export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode getLayout?: (page: ReactElement) => ReactNode
@ -16,8 +17,7 @@ export type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout Component: NextPageWithLayout
} }
export const ThemeUpdateContext = createContext(
const ThemeUpdateContext = createContext(
(theme: DefaultTheme) => console.error("attempted to set theme outside of a ThemeUpdateContext.Provider") (theme: DefaultTheme) => console.error("attempted to set theme outside of a ThemeUpdateContext.Provider")
) )
@ -25,7 +25,9 @@ export const useUpdateTheme = () => useContext(ThemeUpdateContext);
export default function Website({ Component, pageProps }: AppPropsWithLayout) { export default function Website({ Component, pageProps }: AppPropsWithLayout) {
const loadedThemes = Themes.themes;
const [selectedTheme, setselectedTheme] = useState(darkTheme); const [selectedTheme, setselectedTheme] = useState(darkTheme);
const [themes, setThemes] = useState(loadedThemes);
useEffect(() => { useEffect(() => {
@ -34,10 +36,9 @@ export default function Website({ Component, pageProps }: AppPropsWithLayout) {
// if theme data differs set it // if theme data differs set it
// if not just exit // if not just exit
if (storedThemeIdTemp && parseInt(storedThemeIdTemp) !== selectedTheme.themeId) { if (storedThemeIdTemp && parseInt(storedThemeIdTemp) !== selectedTheme.themeId) {
setselectedTheme(getTheme(parseInt(storedThemeIdTemp))) setselectedTheme(getTheme(parseInt(storedThemeIdTemp), themes))
} }
}, [selectedTheme]) }, [selectedTheme, themes])
// Use the layout defined at the page level, if available // Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => ( const getLayout = Component.getLayout ?? ((page) => (
@ -46,12 +47,12 @@ export default function Website({ Component, pageProps }: AppPropsWithLayout) {
return ( return (
<Fragment> <Fragment>
<GlobalStyle /> <GlobalStyle />
<ThemeProvider theme={selectedTheme}> <ThemeProvider theme={selectedTheme}>
<ThemeUpdateContext.Provider value={setselectedTheme}> <ThemeUpdateContext.Provider value={setselectedTheme}>
{getLayout(<Component {...pageProps} />)} {getLayout(<Component {...pageProps} />)}
</ThemeUpdateContext.Provider> </ThemeUpdateContext.Provider>
</ThemeProvider> </ThemeProvider>
</Fragment> </Fragment>
) )
} }

View file

@ -1,21 +0,0 @@
import fsPromises from 'fs/promises'
import path from 'path'
import { Service, Status } from '../../interfaces/CardTypes';
export default async function ServicesAPI(req: any, res: any) {
try {
const filePath = path.join(process.cwd(), '/public/pages.json')
const data = await fsPromises.readFile(filePath)
.then((file) => JSON.parse(file.toString()));
data.services.forEach((service: Service) => {
service.status = Status.loading;
});
res.status(200).json(data.services);
}
catch (error) {
console.log(error);
res.status(500).json({ error: 'Error reading data' });
}
}

View file

@ -1,12 +1,11 @@
import Head from 'next/head' import Head from 'next/head'
import fsPromises from 'fs/promises' import { Game } from '../interfaces/CardTypes';
import path from 'path'
import { EntryList, Game } from '../interfaces/CardTypes';
import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content' import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content'
import Link from 'next/link' import GameList from '../public/pages.json';
function Servers(props: EntryList) { function Servers() {
const serverList = props.games // TODO: unuggly this shit
const serverList: Game[] = JSON.parse(JSON.stringify(GameList.games));
return ( return (
<> <>
<Head> <Head>
@ -32,12 +31,4 @@ function Servers(props: EntryList) {
) )
} }
export async function getServerSideProps() {
const filePath = path.join(process.cwd(), '/public/pages.json')
const jsonData = await fsPromises.readFile(filePath)
const list = JSON.parse(jsonData.toString())
return { props: list }
}
export default Servers export default Servers

View file

@ -1,6 +1,6 @@
import Head from 'next/head' import Head from 'next/head'
import Link from 'next/link'; import Link from 'next/link';
import { PageTitle, PageDescription, PageContentBox, PageCard } from '../components/styles/content'; import { PageTitle, PageDescription, PageContentBox, PageCard, CardLink } from '../components/styles/content';
export default function Home() { export default function Home() {
return ( return (
@ -18,26 +18,26 @@ export default function Home() {
Feel free to look around Feel free to look around
</PageDescription> </PageDescription>
<PageContentBox> <PageContentBox>
<PageCard key="about"> <CardLink key="about" href="/about">
<Link href="/about"> <PageCard>
<h2>About &rarr;</h2> <h2>About &rarr;</h2>
<p>Useless Info, don&apos;t bother</p> <p>Useless Info, don&apos;t bother</p>
</Link> </PageCard>
</PageCard> </CardLink>
<PageCard key="servers"> <CardLink key="servers" href="/games">
<Link href="/games"> <PageCard>
<h2>Games &rarr;</h2> <h2>Games &rarr;</h2>
<p>List of all available Servers</p> <p>List of all available Servers</p>
</Link> </PageCard>
</PageCard> </CardLink>
<PageCard key="services"> <CardLink key="services" href="/services">
<Link href="/services"> <PageCard>
<h2>Services &rarr;</h2> <h2>Services &rarr;</h2>
<p>List of available Services</p> <p>List of available Services</p>
</Link> </PageCard>
</PageCard> </CardLink>
</PageContentBox> </PageContentBox>
</> </>
) )

View file

@ -1,20 +1,19 @@
import Head from 'next/head' import Head from 'next/head'
import Link from 'next/link'
import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes'; import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes';
import Dockerode from 'dockerode'; import Dockerode from 'dockerode';
import { ReactElement } from 'react' import { ReactElement } from 'react'
import useSWR from 'swr'; import useSWR from 'swr';
import { PageCard, CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content'; import { CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content';
import ServiceList from '../public/pages.json';
const fetcher = (url: string) => fetch(url).then((res) => res.json()) const fetcher = (url: string) => fetch(url).then((res) => res.json())
function Services() { function Services() {
const { initialData, fullData, loadingInitial, loadingFull, error } = useServices(); const { initialData, fullData, loadingFull, error } = useServices();
let content: ReactElement = <></>; let content: ReactElement = <></>;
if (error) { content = <div>Error loading data</div> } if (error) { content = <div>Error loading data</div> }
else if (loadingInitial) { content = <div>Loading</div> }
else if (loadingFull) { else if (loadingFull) {
content = content =
<PageContentBox> <PageContentBox>
@ -22,7 +21,6 @@ function Services() {
<CardContentService key={item.name} content={item} /> <CardContentService key={item.name} content={item} />
))} ))}
</PageContentBox> </PageContentBox>
} }
else if (fullData) { else if (fullData) {
content = content =
@ -39,7 +37,7 @@ function Services() {
return ( return (
<> <>
<Head> <Head>
<title>Neshura Servers</title> <title>Neshweb - Services</title>
<meta charSet='utf-8' /> <meta charSet='utf-8' />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="description" content="Lists all available Services, most likely up-to-date" /> <meta name="description" content="Lists all available Services, most likely up-to-date" />
@ -171,17 +169,18 @@ const fetchFullDataArray = (containerData: Dockerode.ContainerInfo[], dataSet: S
function useServices() { function useServices() {
const { data: containerData, error: containerError } = useSWR('/api/containers', fetcher); const { data: containerData, error: containerError } = useSWR('/api/containers', fetcher);
const { data: initialData, error: initialError } = useSWR('/api/services', fetcher); // TODO: unfuck this
const loadingInitial = !initialData && !initialError const initialData: Service[] = JSON.parse(JSON.stringify(ServiceList.services));
const { data: fullData, error: fullError } = useSWR((initialData && containerData) ? [containerData, initialData] : null, fetchFullDataArray) initialData.forEach((service) => {
if (service.status === undefined) service.status = Status.loading;
})
const { data: fullData, error: fullError } = useSWR((containerData) ? [containerData, initialData] : null, fetchFullDataArray)
const loadingFull = !fullData && !fullError const loadingFull = !fullData && !fullError
return { return {
initialData, initialData,
fullData, fullData,
loadingInitial,
loadingFull, loadingFull,
error: initialError || fullError || containerError, error: fullError || containerError,
}; };
} }

20
public/data/navbar.json Normal file
View file

@ -0,0 +1,20 @@
{
"links": [
{
"name": "Home",
"href": "/"
},
{
"name": "About",
"href": "/about"
},
{
"name": "Games",
"href": "/games"
},
{
"name": "Services",
"href": "/services"
}
]
}

84
public/data/themes.json Normal file
View file

@ -0,0 +1,84 @@
{
"themes": [
{
"themeName": "Cancer - Blue",
"themeId": 0,
"colors": {
"background": "#ffffff",
"primary": "#00AAFF",
"secondary": "#FF5500",
"online": "#2BFF00",
"loading": "#D400FF",
"offline": "#FF002B"
}
},
{
"themeName": "Dark - Blue",
"themeId": 1,
"colors": {
"background": "#161616",
"primary": "#00AAFF",
"secondary": "#FF5500",
"online": "#2BFF00",
"loading": "#D400FF",
"offline": "#FF002B"
}
},
{
"themeName": "AMOLED - Blue",
"themeId": 2,
"colors": {
"background": "#000000",
"primary": "#00AAFF",
"secondary": "#FF5500",
"online": "#2BFF00",
"loading": "#D400FF",
"offline": "#FF002B"
}
},
{
"themeName": "AMOLED - Purple",
"themeId": 3,
"colors": {
"background": "#000000",
"primary": "#886aff",
"secondary": "#E1FF6A",
"online": "#00ff00",
"loading": "#FF6A97",
"offline": "#ff0000"
}
},
{
"themeName": "Nordlys",
"themeId": 4,
"backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg",
"invertButtons": true,
"colors": {
"background": "#0008",
"backgroundAlt": "#000d",
"primary": "#ccc",
"secondary": "#00C7C7",
"text": "#000",
"online": "#00ff00",
"loading": "#0063C7",
"offline": "#ff0000"
}
},
{
"themeName": "dev",
"themeId": 5,
"backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg",
"invertButtons": true,
"colors": {
"background": "#0000",
"backgroundAlt": "#0000",
"primary": "#ccc",
"secondary": "#00C7C7",
"text": "#000",
"online": "#00ff00",
"loading": "#0063C7",
"offline": "#ff0000"
}
}
]
}

View file

@ -3,7 +3,7 @@
{ {
"name": "Nextcloud", "name": "Nextcloud",
"icon": "/icons/nextcloud-logo.svg", "icon": "/icons/nextcloud-logo.svg",
"href": "https://nextcloud.neshweb.net", "href": "https://nextcloud.neshweb.net/",
"desc": "Self-hosted Cloud Storage Service", "desc": "Self-hosted Cloud Storage Service",
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"type": "docker", "type": "docker",
@ -13,7 +13,7 @@
{ {
"name": "Komga", "name": "Komga",
"icon": "/icons/komga-logo.png", "icon": "/icons/komga-logo.png",
"href": "https://komga.neshweb.net", "href": "https://komga.neshweb.net/",
"desc": "Self-hosted Comic Library", "desc": "Self-hosted Comic Library",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
@ -32,7 +32,7 @@
{ {
"name": "PeerTube", "name": "PeerTube",
"icon": "/icons/peertube-logo.svg", "icon": "/icons/peertube-logo.svg",
"href": "https://tube.neshweb.net", "href": "https://tube.neshweb.net/",
"desc": "Self-hosted PeerTube Instance", "desc": "Self-hosted PeerTube Instance",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
@ -42,7 +42,7 @@
{ {
"name": "Mastodon", "name": "Mastodon",
"icon": "/icons/mastodon-logo.svg", "icon": "/icons/mastodon-logo.svg",
"href": "https://mastodon.neshweb.net", "href": "https://mastodon.neshweb.net/",
"desc": "Self-hosted Mastodon Instance", "desc": "Self-hosted Mastodon Instance",
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"type": "docker", "type": "docker",

4
styled.d.ts vendored
View file

@ -4,10 +4,14 @@ declare module 'styled-components' {
export interface DefaultTheme { export interface DefaultTheme {
themeName: string, themeName: string,
themeId: number, themeId: number,
backgroundImage?: string,
invertButtons?: boolean,
colors: { colors: {
background: string, background: string,
backgroundAlt?: string,
primary: string, primary: string,
secondary: string, secondary: string,
text?: string,
online: string, online: string,
loading: string, loading: string,
offline: string, offline: string,