Merge branch 'adjust/themes' into 'main'
Merge Theme Adjustments See merge request neshura-websites/www!10
This commit is contained in:
commit
7c7d60bab7
19 changed files with 280 additions and 185 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -1,9 +1,9 @@
|
|||
# do not track installed modules
|
||||
node_modules/
|
||||
.vscode/
|
||||
/node_modules/
|
||||
/.vscode/
|
||||
|
||||
# do not track built files
|
||||
.next/
|
||||
/.next/
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
|
@ -14,6 +14,6 @@ yarn-error.log*
|
|||
.pnpm-debug.log*
|
||||
|
||||
# production
|
||||
build/
|
||||
data/
|
||||
confs/
|
||||
/build/
|
||||
/data/
|
||||
/confs/
|
1
components/fetcher.tsx
Normal file
1
components/fetcher.tsx
Normal file
|
@ -0,0 +1 @@
|
|||
export const fetcher = (url:string) => fetch(url).then((res) => res.json());
|
|
@ -1,15 +1,14 @@
|
|||
import PageFooter from './footer'
|
||||
import PageNavbar from './navbar'
|
||||
import Script from 'next/script'
|
||||
import { Page, Main } from './styles/generic'
|
||||
|
||||
import PageFooter from './footer';
|
||||
import PageNavbar from './navbar';
|
||||
import Script from 'next/script';
|
||||
import { Page, Main } from './styles/generic';
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<Page>
|
||||
<Script id="matomo_analytics"
|
||||
defer src='https://static.cloudflareinsights.com/beacon.min.js'
|
||||
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
|
||||
<Script id="matomo_analytics"
|
||||
defer src='https://static.cloudflareinsights.com/beacon.min.js'
|
||||
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
|
||||
</Script>
|
||||
<Script id="matomo_analytics">
|
||||
{`
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
import { usePathname } from 'next/navigation'
|
||||
import { NavBar, NavLink, NavWrap } from './styles/navbar';
|
||||
import { StyleSelector, StyleSelectorPlaceholder } from './themeselector';
|
||||
|
||||
const navLinks = [
|
||||
{ name: "Home", href: "/" },
|
||||
{ name: "About", href: "/about" },
|
||||
{ name: "Games", href: "/games" },
|
||||
{ name: "Services", href: "/services" }
|
||||
]
|
||||
import Links from '../public/data/navbar.json';
|
||||
|
||||
const PageNavbar = () => {
|
||||
const path = usePathname();
|
||||
|
@ -16,8 +10,8 @@ const PageNavbar = () => {
|
|||
<NavWrap>
|
||||
<StyleSelector></StyleSelector>
|
||||
<NavBar>
|
||||
{navLinks.map((item) => (
|
||||
<NavLink active={path == item.href ? true : false} key={item.name} href={item.href}>
|
||||
{Links.links.map((item) => (
|
||||
<NavLink active={path === item.href ? +true : +false} key={item.name} href={item.href}>
|
||||
{item.name}
|
||||
</NavLink>
|
||||
))}
|
||||
|
|
|
@ -12,17 +12,23 @@ interface OnlinePropType {
|
|||
// replaces .title
|
||||
export const PageTitle = styled.h1`
|
||||
margin: 0;
|
||||
padding: 0.25rem 1rem;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
text-align: center;
|
||||
background-color: ${({ theme }) => theme.colors.background ? theme.colors.background : ""};
|
||||
border-radius: 15px;
|
||||
`;
|
||||
|
||||
// replaces .description
|
||||
export const PageDescription = styled.p`
|
||||
background-color: ${({ theme }) => theme.colors.background ? theme.colors.background : ""};
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 4rem 0;
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
`;
|
||||
|
||||
// replaces .grid
|
||||
|
@ -43,25 +49,26 @@ const CardStyle = css`
|
|||
height: 240px;
|
||||
`;
|
||||
|
||||
const CardLink = styled(Link)`
|
||||
export const CardLink = styled(Link)`
|
||||
${CardStyle}
|
||||
`;
|
||||
|
||||
const CardStyleWrap = styled.div`
|
||||
export const CardStyleWrap = styled.div`
|
||||
${CardStyle}
|
||||
`;
|
||||
|
||||
// replaces .card & .contentcard
|
||||
export const PageCard = styled.div`
|
||||
margin: 1rem;
|
||||
padding: 1.5rem 0.7rem 1.5rem 0.7rem;
|
||||
padding: 23px 10px;
|
||||
text-align: center;
|
||||
color: ${({ theme }) => theme.colors.primary};
|
||||
background-color: ${({ theme }) => theme.colors.background};
|
||||
text-decoration: none;
|
||||
border: 1px solid;
|
||||
border: 2px solid;
|
||||
border-radius: 10px;
|
||||
border-color: ${({ theme }) => theme.colors.primary};
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
transition: all 0.1s linear;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
|
@ -82,11 +89,15 @@ export const PageCard = styled.div`
|
|||
${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & {
|
||||
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 & {
|
||||
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;
|
||||
}};
|
||||
padding: 0.2rem;
|
||||
background-color: ${({ theme }) => theme.colors.background};
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.colors.primary};
|
||||
border-radius: 5px;
|
||||
|
@ -119,13 +129,25 @@ const OnlineStatus = styled.p<OnlinePropType>`
|
|||
top: 100; right: 50; bottom: 0; left: 50;
|
||||
offset-position: bottom 10px;
|
||||
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 & {
|
||||
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 & {
|
||||
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
|
||||
const CardContentTitleIcon = styled.div`
|
||||
const CardContentTitleIcon = styled(Image)`
|
||||
object-fit: "contain";
|
||||
margin-right: 0.4rem;
|
||||
position: relative;
|
||||
margin-right: 8px;
|
||||
aspect-ratio: 1;
|
||||
height: 1.5rem;
|
||||
height: 28px;
|
||||
`;
|
||||
|
||||
// replaces .contentTitle
|
||||
const CardContentTitleWrap = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
@ -163,9 +185,7 @@ const CardContentTitle = ({ content }: { content: Service | Game }) => {
|
|||
<CardContentTitleWrap>
|
||||
{
|
||||
content.icon ? (
|
||||
<CardContentTitleIcon>
|
||||
<Image alt="icon" src={content.icon} fill></Image>
|
||||
</CardContentTitleIcon>
|
||||
<CardContentTitleIcon alt="icon" src={content.icon} width="28" height="28" sizes='10vw'/>
|
||||
) : (<></>)
|
||||
}
|
||||
<h2>{content.name}</h2>
|
||||
|
|
|
@ -10,6 +10,10 @@ export const StyledBody = styled.body`
|
|||
export const Page = styled.div`
|
||||
width: 100%;
|
||||
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`
|
||||
|
|
|
@ -2,7 +2,7 @@ import styled from 'styled-components'
|
|||
import Link from 'next/link';
|
||||
|
||||
interface ActivePropType {
|
||||
active?: false | true;
|
||||
active?: number;
|
||||
}
|
||||
|
||||
export const NavWrap = styled.div`
|
||||
|
@ -23,19 +23,34 @@ export const NavBar = styled.nav`
|
|||
align-items: center;
|
||||
`
|
||||
|
||||
export const NavLink = styled(Link)<ActivePropType>`
|
||||
color: ${props => props.active ? ({ theme }) => theme.colors.secondary : ({ theme }) => theme.colors.primary};
|
||||
export const NavLink = styled(Link) <ActivePropType>`
|
||||
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;
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
padding: 0.2rem 0.5rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&: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};
|
||||
}
|
||||
`
|
|
@ -1,36 +1,46 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
interface DisplayPropType {
|
||||
show?: false | true;
|
||||
focus?: number,
|
||||
show?: number;
|
||||
}
|
||||
|
||||
interface ActivePropType {
|
||||
active?: false | true;
|
||||
active?: number;
|
||||
}
|
||||
|
||||
export const ThemeDropDown = styled.div`
|
||||
margin-left: 1%;
|
||||
min-width: 180px;
|
||||
color: ${({ theme }) => theme.colors.primary}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: ${({ theme }) => theme.colors.primary};
|
||||
`
|
||||
|
||||
export const ThemeDropDownButton = styled.button<DisplayPropType>`
|
||||
width: 160px;
|
||||
border: 1px solid;
|
||||
border: 2px solid;
|
||||
border-radius: 5px;
|
||||
background-color: ${({ theme }) => theme.colors.background};
|
||||
padding: 0.2rem 0.5rem;
|
||||
background-color: ${ props => props.focus ?
|
||||
({ theme }) => theme.invertButtons ? theme.colors.secondary : theme.colors.background :
|
||||
({ theme }) => theme.colors.background};
|
||||
padding: 2px 6px;
|
||||
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-duration: 0.15s;
|
||||
transition-delay: 0s, ${ props => props.show ? "0s, 0s" : "0.6s, 0.6s" }, 0s;
|
||||
|
||||
&: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" : "" };
|
||||
|
@ -40,7 +50,14 @@ export const ThemeDropDownButton = styled.button<DisplayPropType>`
|
|||
export const ThemeDropDownOptions = styled.div<DisplayPropType>`
|
||||
position: absolute;
|
||||
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;
|
||||
flex-direction: column;
|
||||
min-width: 160px;
|
||||
|
@ -49,7 +66,7 @@ export const ThemeDropDownOptions = styled.div<DisplayPropType>`
|
|||
border-radius: 5px;
|
||||
border-top-left-radius: 0px; border-top-right-radius: 0px;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
overflow: ${ props => props.show ? "scroll" : "hidden" };
|
||||
max-height: ${ props => props.show ? "20%" : "0%" };
|
||||
visibility: ${ props => props.show ? "visible" : "hidden" };
|
||||
|
||||
|
@ -61,7 +78,8 @@ export const ThemeDropDownOptions = styled.div<DisplayPropType>`
|
|||
|
||||
export const ThemeDropDownOption = styled.button<ActivePropType>`
|
||||
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;
|
||||
border: 0px solid;
|
||||
padding: 0.2rem 0.5rem;
|
||||
|
|
|
@ -56,17 +56,4 @@ export const amoledTheme: DefaultTheme = {
|
|||
loading: '#ff5300',
|
||||
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',
|
||||
},
|
||||
}
|
|
@ -1,17 +1,12 @@
|
|||
import { useUpdateTheme } from "../pages/_app";
|
||||
import { Fragment, useContext, useState } from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
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";
|
||||
|
||||
const themes = [
|
||||
lightTheme,
|
||||
darkTheme,
|
||||
amoledTheme,
|
||||
amoled2Theme,
|
||||
]
|
||||
import Themes from '../public/data/themes.json';
|
||||
|
||||
export const StyleSelector = () => {
|
||||
const themes: DefaultTheme[] = Themes.themes;
|
||||
const updateTheme = useUpdateTheme();
|
||||
const currentTheme = useContext(ThemeContext);
|
||||
const [selectedTheme, setSelectedTheme] = useState(themes[currentTheme.themeId]);
|
||||
|
@ -21,7 +16,6 @@ export const StyleSelector = () => {
|
|||
}
|
||||
|
||||
const updateThemeWithStorage = (newTheme: DefaultTheme) => {
|
||||
|
||||
if (newTheme.themeId === lightTheme.themeId) {
|
||||
updateLightTheme(newTheme);
|
||||
}
|
||||
|
@ -38,27 +32,25 @@ export const StyleSelector = () => {
|
|||
localStorage.setItem("theme", newTheme.themeId.toString());
|
||||
updateTheme(newTheme)
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const [test, setTest] = useState(false);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [buttonFocus, setButtonFocus] = useState(visible);
|
||||
|
||||
function handleBlur(event:any) {
|
||||
console.log(event)
|
||||
if (!event.currentTarget.contains(event.relatedTarget)) {
|
||||
setTest(false);
|
||||
setButtonFocus(false);
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<ThemeDropDown onBlur={(event) => handleBlur(event)}>
|
||||
<ThemeDropDownButton show={test} onClick={() => setTest(test => !test)}>{selectedTheme.themeName}</ThemeDropDownButton>
|
||||
<ThemeDropDownOptions id="themesDropdown" show={test}>
|
||||
<ThemeDropDownButton focus={+buttonFocus} show={+visible} onFocus={() => setButtonFocus(true)} onClick={() => setVisible(visible => !visible)}>{selectedTheme.themeName}
|
||||
</ThemeDropDownButton>
|
||||
<ThemeDropDownOptions id="themesDropdown" show={+visible}>
|
||||
{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}
|
||||
</ThemeDropDownOption>
|
||||
))}
|
||||
|
@ -73,27 +65,14 @@ export const StyleSelectorPlaceholder = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export function getTheme(themeId: number): DefaultTheme {
|
||||
let theme: DefaultTheme;
|
||||
export function getTheme(themeId: number, themes: DefaultTheme[]): DefaultTheme {
|
||||
let retTheme: DefaultTheme = darkTheme;
|
||||
|
||||
switch (themeId) {
|
||||
case 0:
|
||||
theme = lightTheme;
|
||||
break;
|
||||
case 1:
|
||||
theme = darkTheme;
|
||||
break;
|
||||
case 2:
|
||||
theme = amoledTheme;
|
||||
break;
|
||||
case 3:
|
||||
theme = amoled2Theme;
|
||||
break;
|
||||
default:
|
||||
theme = darkTheme
|
||||
}
|
||||
themes.forEach((theme) => {
|
||||
if (theme.themeId === themeId) { retTheme = theme};
|
||||
})
|
||||
|
||||
return theme;
|
||||
return retTheme;
|
||||
}
|
||||
|
||||
export default StyleSelector;
|
|
@ -7,6 +7,7 @@ import { DefaultTheme, ThemeProvider } from 'styled-components';
|
|||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { darkTheme, GlobalStyle } from '../components/themes';
|
||||
import { getTheme } from '../components/themeselector';
|
||||
import Themes from '../public/data/themes.json';
|
||||
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (page: ReactElement) => ReactNode
|
||||
|
@ -16,8 +17,7 @@ export type AppPropsWithLayout = AppProps & {
|
|||
Component: NextPageWithLayout
|
||||
}
|
||||
|
||||
|
||||
const ThemeUpdateContext = createContext(
|
||||
export const ThemeUpdateContext = createContext(
|
||||
(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) {
|
||||
const loadedThemes = Themes.themes;
|
||||
const [selectedTheme, setselectedTheme] = useState(darkTheme);
|
||||
const [themes, setThemes] = useState(loadedThemes);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -34,10 +36,9 @@ export default function Website({ Component, pageProps }: AppPropsWithLayout) {
|
|||
// if theme data differs set it
|
||||
// if not just exit
|
||||
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
|
||||
const getLayout = Component.getLayout ?? ((page) => (
|
||||
|
@ -46,12 +47,12 @@ export default function Website({ Component, pageProps }: AppPropsWithLayout) {
|
|||
return (
|
||||
<Fragment>
|
||||
<GlobalStyle />
|
||||
<ThemeProvider theme={selectedTheme}>
|
||||
<ThemeUpdateContext.Provider value={setselectedTheme}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ThemeUpdateContext.Provider>
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme={selectedTheme}>
|
||||
<ThemeUpdateContext.Provider value={setselectedTheme}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ThemeUpdateContext.Provider>
|
||||
</ThemeProvider>
|
||||
</Fragment>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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' });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import Head from 'next/head'
|
||||
import fsPromises from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { EntryList, Game } from '../interfaces/CardTypes';
|
||||
import { Game } from '../interfaces/CardTypes';
|
||||
import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content'
|
||||
import Link from 'next/link'
|
||||
import GameList from '../public/pages.json';
|
||||
|
||||
function Servers(props: EntryList) {
|
||||
const serverList = props.games
|
||||
function Servers() {
|
||||
// TODO: unuggly this shit
|
||||
const serverList: Game[] = JSON.parse(JSON.stringify(GameList.games));
|
||||
return (
|
||||
<>
|
||||
<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
|
|
@ -1,6 +1,6 @@
|
|||
import Head from 'next/head'
|
||||
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() {
|
||||
return (
|
||||
|
@ -18,26 +18,26 @@ export default function Home() {
|
|||
Feel free to look around
|
||||
</PageDescription>
|
||||
<PageContentBox>
|
||||
<PageCard key="about">
|
||||
<Link href="/about">
|
||||
<CardLink key="about" href="/about">
|
||||
<PageCard>
|
||||
<h2>About →</h2>
|
||||
<p>Useless Info, don't bother</p>
|
||||
</Link>
|
||||
</PageCard>
|
||||
</PageCard>
|
||||
</CardLink>
|
||||
|
||||
<PageCard key="servers">
|
||||
<Link href="/games">
|
||||
<CardLink key="servers" href="/games">
|
||||
<PageCard>
|
||||
<h2>Games →</h2>
|
||||
<p>List of all available Servers</p>
|
||||
</Link>
|
||||
</PageCard>
|
||||
</PageCard>
|
||||
</CardLink>
|
||||
|
||||
<PageCard key="services">
|
||||
<Link href="/services">
|
||||
<CardLink key="services" href="/services">
|
||||
<PageCard>
|
||||
<h2>Services →</h2>
|
||||
<p>List of available Services</p>
|
||||
</Link>
|
||||
</PageCard>
|
||||
</PageCard>
|
||||
</CardLink>
|
||||
</PageContentBox>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes';
|
||||
import Dockerode from 'dockerode';
|
||||
import { ReactElement } from 'react'
|
||||
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())
|
||||
|
||||
function Services() {
|
||||
const { initialData, fullData, loadingInitial, loadingFull, error } = useServices();
|
||||
const { initialData, fullData, loadingFull, error } = useServices();
|
||||
|
||||
let content: ReactElement = <></>;
|
||||
|
||||
if (error) { content = <div>Error loading data</div> }
|
||||
else if (loadingInitial) { content = <div>Loading</div> }
|
||||
else if (loadingFull) {
|
||||
content =
|
||||
<PageContentBox>
|
||||
|
@ -22,7 +21,6 @@ function Services() {
|
|||
<CardContentService key={item.name} content={item} />
|
||||
))}
|
||||
</PageContentBox>
|
||||
|
||||
}
|
||||
else if (fullData) {
|
||||
content =
|
||||
|
@ -39,7 +37,7 @@ function Services() {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Neshura Servers</title>
|
||||
<title>Neshweb - Services</title>
|
||||
<meta charSet='utf-8' />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<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() {
|
||||
const { data: containerData, error: containerError } = useSWR('/api/containers', fetcher);
|
||||
const { data: initialData, error: initialError } = useSWR('/api/services', fetcher);
|
||||
const loadingInitial = !initialData && !initialError
|
||||
const { data: fullData, error: fullError } = useSWR((initialData && containerData) ? [containerData, initialData] : null, fetchFullDataArray)
|
||||
// TODO: unfuck this
|
||||
const initialData: Service[] = JSON.parse(JSON.stringify(ServiceList.services));
|
||||
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
|
||||
|
||||
return {
|
||||
initialData,
|
||||
fullData,
|
||||
loadingInitial,
|
||||
loadingFull,
|
||||
error: initialError || fullError || containerError,
|
||||
error: fullError || containerError,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
20
public/data/navbar.json
Normal file
20
public/data/navbar.json
Normal 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
84
public/data/themes.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
{
|
||||
"name": "Nextcloud",
|
||||
"icon": "/icons/nextcloud-logo.svg",
|
||||
"href": "https://nextcloud.neshweb.net",
|
||||
"href": "https://nextcloud.neshweb.net/",
|
||||
"desc": "Self-hosted Cloud Storage Service",
|
||||
"warn": "Note: Registration requires approval",
|
||||
"type": "docker",
|
||||
|
@ -13,7 +13,7 @@
|
|||
{
|
||||
"name": "Komga",
|
||||
"icon": "/icons/komga-logo.png",
|
||||
"href": "https://komga.neshweb.net",
|
||||
"href": "https://komga.neshweb.net/",
|
||||
"desc": "Self-hosted Comic Library",
|
||||
"warn": "Note: Registration only via Admin",
|
||||
"type": "docker",
|
||||
|
@ -32,7 +32,7 @@
|
|||
{
|
||||
"name": "PeerTube",
|
||||
"icon": "/icons/peertube-logo.svg",
|
||||
"href": "https://tube.neshweb.net",
|
||||
"href": "https://tube.neshweb.net/",
|
||||
"desc": "Self-hosted PeerTube Instance",
|
||||
"warn": "Note: Registration only via Admin",
|
||||
"type": "docker",
|
||||
|
@ -42,7 +42,7 @@
|
|||
{
|
||||
"name": "Mastodon",
|
||||
"icon": "/icons/mastodon-logo.svg",
|
||||
"href": "https://mastodon.neshweb.net",
|
||||
"href": "https://mastodon.neshweb.net/",
|
||||
"desc": "Self-hosted Mastodon Instance",
|
||||
"warn": "Note: Registration requires approval",
|
||||
"type": "docker",
|
||||
|
|
4
styled.d.ts
vendored
4
styled.d.ts
vendored
|
@ -4,10 +4,14 @@ declare module 'styled-components' {
|
|||
export interface DefaultTheme {
|
||||
themeName: string,
|
||||
themeId: number,
|
||||
backgroundImage?: string,
|
||||
invertButtons?: boolean,
|
||||
colors: {
|
||||
background: string,
|
||||
backgroundAlt?: string,
|
||||
primary: string,
|
||||
secondary: string,
|
||||
text?: string,
|
||||
online: string,
|
||||
loading: string,
|
||||
offline: string,
|
||||
|
|
Loading…
Reference in a new issue