Added styled-components + various
Includes changes due to NextJS version bump and attempts at storing theme via cookie
This commit is contained in:
parent
8a763ee72e
commit
21e613891e
13 changed files with 325 additions and 102 deletions
76
app/layout.tsx
Normal file
76
app/layout.tsx
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
'use client'
|
||||||
|
import Script from "next/script"
|
||||||
|
import Footer from "../components/footer"
|
||||||
|
import Navbar from "../components/navbar"
|
||||||
|
import StyleSelector from "../components/themeselector"
|
||||||
|
import styles from "../styles/Home.module.css"
|
||||||
|
import { Page } from '../components/styles/generic'
|
||||||
|
import { DefaultTheme, ThemeProvider } from 'styled-components';
|
||||||
|
import { createContext, useContext, useEffect, useState } from "react"
|
||||||
|
import { setCookie } from "cookies-next"
|
||||||
|
import { darkTheme } from "../components/themes"
|
||||||
|
|
||||||
|
const ThemeUpdateContext = createContext(
|
||||||
|
(theme: DefaultTheme) => console.error("attempted to set theme outside of a ThemeUpdateContext.Provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
export const useUpdateTheme = () => useContext(ThemeUpdateContext);
|
||||||
|
|
||||||
|
export default function Layout({ children, }: { children: React.ReactNode }) {
|
||||||
|
const [selectedTheme, setselectedTheme] = useState(darkTheme);
|
||||||
|
console.log("Selected Theme: ", selectedTheme); // DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<ThemeProvider theme={selectedTheme}>
|
||||||
|
<ThemeUpdateContext.Provider value={setselectedTheme}>
|
||||||
|
<Page>
|
||||||
|
|
||||||
|
<Navbar />
|
||||||
|
<StyleSelector></StyleSelector>
|
||||||
|
<body className={styles.main}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</Page>
|
||||||
|
</ThemeUpdateContext.Provider>
|
||||||
|
</ThemeProvider>
|
||||||
|
|
||||||
|
|
||||||
|
</html >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{/* <Page>
|
||||||
|
<Navbar />
|
||||||
|
<StyleSelector></StyleSelector>
|
||||||
|
<body className={styles.main}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
<Footer />
|
||||||
|
</Page> */}
|
||||||
|
|
||||||
|
/* <html>
|
||||||
|
<head />
|
||||||
|
<body>{children}</body>
|
||||||
|
</html> */
|
||||||
|
|
||||||
|
{/* <Script id="matomo_analytics">
|
||||||
|
{`
|
||||||
|
var _paq = window._paq = window._paq || []; */}
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
/* _paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
|
||||||
|
_paq.push(["setCookieDomain", "www.neshweb.net"]);
|
||||||
|
_paq.push(["disableCookies"]);
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//tracking.neshweb.net/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', '2']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
`}
|
||||||
|
</Script> */
|
|
@ -2,10 +2,13 @@ import Footer from './footer'
|
||||||
import Navbar from './navbar'
|
import Navbar from './navbar'
|
||||||
import styles from '/styles/Home.module.css'
|
import styles from '/styles/Home.module.css'
|
||||||
import Script from 'next/script'
|
import Script from 'next/script'
|
||||||
|
import { Page } from './styles/generic'
|
||||||
|
import StyleSelector from './themeselector'
|
||||||
|
|
||||||
|
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<Page>
|
||||||
<Script id="matomo_analytics">
|
<Script id="matomo_analytics">
|
||||||
{`
|
{`
|
||||||
var _paq = window._paq = window._paq || [];
|
var _paq = window._paq = window._paq || [];
|
||||||
|
@ -26,11 +29,12 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
</Script>
|
</Script>
|
||||||
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
<StyleSelector></StyleSelector>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import styles from '/styles/Home.module.css'
|
import styles from '/styles/Home.module.css'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { usePathname } from 'next/navigation'
|
||||||
|
|
||||||
const navLinks = [
|
const navLinks = [
|
||||||
{ name: "Home", href: "/" },
|
{ name: "Home", href: "/" },
|
||||||
|
@ -10,19 +10,16 @@ const navLinks = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const router = useRouter();
|
const path = usePathname();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.navbar}>
|
<nav className={styles.navbar}>
|
||||||
{navLinks.map((item, index) => (
|
{navLinks.map((item, index) => (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link className={path == item.href ? styles.navelem_active : styles.navelem} key={item.name} href={item.href}>
|
||||||
<a className={router.pathname == item.href ? styles.navelem_active : styles.navelem}>{item.name}</a>
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
<Link key="Mastodon_Verify" href="https://mastodon.neshweb.net/@neshura">
|
<Link className={styles.navelem} key="Mastodon_Verify" rel="me" href="https://mastodon.neshweb.net/@neshura">Mastodon</Link>
|
||||||
<a className={styles.navelem} rel="me" href="https://mastodon.neshweb.net/@neshura">Mastodon</a>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
8
components/styles/generic.tsx
Normal file
8
components/styles/generic.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const Page = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
background-color: ${({ theme }) => theme.colors.background};
|
||||||
|
`
|
||||||
|
|
||||||
|
export { Page }
|
51
components/themes.tsx
Normal file
51
components/themes.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Probably a good idea to spread this out into multiple files under a folder once it gets bigger
|
||||||
|
import { createGlobalStyle, DefaultTheme } from 'styled-components'
|
||||||
|
|
||||||
|
// unsure whether needed
|
||||||
|
/* const GlobalStyle = createGlobalStyle`
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
color: ${({ theme }) => theme.colors.primary};
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default GlobalStyle; */
|
||||||
|
|
||||||
|
export const lightTheme: DefaultTheme = {
|
||||||
|
themeId: 1,
|
||||||
|
colors: {
|
||||||
|
background: '#ffffff',
|
||||||
|
primary: '#00aaff',
|
||||||
|
secondary:'#ffaa00',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const darkTheme: DefaultTheme = {
|
||||||
|
themeId: 2,
|
||||||
|
colors: {
|
||||||
|
background: '#1f1f1f',
|
||||||
|
primary: '#00aaff',
|
||||||
|
secondary:'#ffaa00',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const amoledTheme: DefaultTheme = {
|
||||||
|
themeId: 3,
|
||||||
|
colors: {
|
||||||
|
background: '#000000',
|
||||||
|
primary: '#00aaff',
|
||||||
|
secondary:'#ffaa00',
|
||||||
|
},
|
||||||
|
}
|
52
components/themeselector.tsx
Normal file
52
components/themeselector.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { useUpdateTheme } from "../pages/_app";
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { ThemeContext, DefaultTheme } from "styled-components";
|
||||||
|
import { darkTheme, amoledTheme, lightTheme } from './themes';
|
||||||
|
|
||||||
|
const StyleSelector = () => {
|
||||||
|
const updateTheme = useUpdateTheme();
|
||||||
|
const currentTheme = useContext(ThemeContext);
|
||||||
|
|
||||||
|
//console.log(currentTheme); // DEBUG
|
||||||
|
|
||||||
|
let newTheme: DefaultTheme;
|
||||||
|
switch(currentTheme.themeId) {
|
||||||
|
case 1:
|
||||||
|
newTheme = darkTheme;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
newTheme = amoledTheme;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
newTheme = lightTheme;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newTheme = currentTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button onClick={() => updateTheme(newTheme)}>Switch Style</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTheme(themeId: number): DefaultTheme {
|
||||||
|
let theme: DefaultTheme;
|
||||||
|
|
||||||
|
switch(themeId) {
|
||||||
|
case 1:
|
||||||
|
theme = lightTheme;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
theme = darkTheme;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
theme = amoledTheme;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
theme = darkTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StyleSelector;
|
|
@ -3,7 +3,10 @@ import type { ReactElement, ReactNode } from 'react'
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import { AppProps } from 'next/app';
|
import { AppProps } from 'next/app';
|
||||||
|
import { DefaultTheme, ThemeProvider } from 'styled-components';
|
||||||
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
|
import { getCookie, getCookies, setCookie } from 'cookies-next'
|
||||||
|
import { darkTheme } from '../components/themes';
|
||||||
|
|
||||||
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
|
||||||
|
@ -13,10 +16,41 @@ export type AppPropsWithLayout = AppProps & {
|
||||||
Component: NextPageWithLayout
|
Component: NextPageWithLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ThemeUpdateContext = createContext(
|
||||||
|
(theme: DefaultTheme) => console.error("attempted to set theme outside of a ThemeUpdateContext.Provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
export const useUpdateTheme = () => useContext(ThemeUpdateContext);
|
||||||
|
|
||||||
|
|
||||||
export default function Website({ Component, pageProps }: AppPropsWithLayout) {
|
export default function Website({ Component, pageProps }: AppPropsWithLayout) {
|
||||||
|
const [selectedTheme, setselectedTheme] = useState(darkTheme);
|
||||||
|
//console.log("Selected Theme: ", selectedTheme); // DEBUG
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCookie('theme', selectedTheme.themeId);
|
||||||
|
}, [selectedTheme])
|
||||||
|
|
||||||
|
|
||||||
// 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) => (
|
||||||
<Layout>{page}</Layout>))
|
<Layout>{page}</Layout>))
|
||||||
|
|
||||||
return getLayout(<Component {...pageProps} />)
|
return (
|
||||||
|
<ThemeProvider theme={selectedTheme}>
|
||||||
|
<ThemeUpdateContext.Provider value={setselectedTheme}>
|
||||||
|
{getLayout(<Component {...pageProps} />)}
|
||||||
|
</ThemeUpdateContext.Provider>
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
token: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { Html, Head, Main, NextScript } from 'next/document'
|
import { getCookie, getCookies, setCookie } from 'cookies-next';
|
||||||
|
import { Html, Head, Main, NextScript, DocumentContext } from 'next/document'
|
||||||
|
import { useUpdateTheme } from './_app';
|
||||||
|
import { darkTheme } from '../components/themes';
|
||||||
|
import { GetServerSidePropsContext } from 'next';
|
||||||
|
|
||||||
|
export default function Document(ctx: DocumentContext) {
|
||||||
|
const updateTheme = useUpdateTheme();
|
||||||
|
|
||||||
export default function Document() {
|
|
||||||
return (
|
return (
|
||||||
<Html lang='en'>
|
<Html lang='en'>
|
||||||
<Head />
|
<Head />
|
||||||
|
|
|
@ -26,20 +26,18 @@ function Servers(props: EntryList) {
|
||||||
{Object.values(serverList).map((item: CustomLink) => {
|
{Object.values(serverList).map((item: CustomLink) => {
|
||||||
if (item.href != null) {
|
if (item.href != null) {
|
||||||
return (
|
return (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link className={styles.contentcard} key={item.name} href={item.href}>
|
||||||
<a className={styles.contentcard}>
|
<div className={styles.contenttitle}><h2>{item.name}</h2></div>
|
||||||
<div className={styles.contenttitle}><h2>{item.name }</h2></div>
|
|
||||||
<div><p>{item.desc}</p></div>
|
<div><p>{item.desc}</p></div>
|
||||||
<div><p>{item.ip }</p></div>
|
<div><p>{item.ip}</p></div>
|
||||||
<div className={item.status == "Online" ? styles.contentonline : styles.contentoffline}><p>{item.status}</p></div>
|
<div className={item.status == "Online" ? styles.contentonline : styles.contentoffline}><p>{item.status}</p></div>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (
|
return (
|
||||||
<a key={item.name} className={styles.contentcardstatic}>
|
<a key={item.name} className={styles.contentcardstatic}>
|
||||||
<div className={styles.contenttitle}><h2>{item.name }</h2></div>
|
<div className={styles.contenttitle}><h2>{item.name}</h2></div>
|
||||||
<div><p>{item.desc}</p></div>
|
<div><p>{item.desc}</p></div>
|
||||||
<div><p>{item.ip}</p></div>
|
<div><p>{item.ip}</p></div>
|
||||||
<div className={item.status == "Online" ? styles.contentonline : styles.contentoffline}><p>{item.status}</p></div>
|
<div className={item.status == "Online" ? styles.contentonline : styles.contentoffline}><p>{item.status}</p></div>
|
||||||
|
|
|
@ -6,11 +6,10 @@ export default function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Neshura Servers</title>
|
<title>Neshweb - Home</title>
|
||||||
<meta charSet='utf-8' />
|
<meta charSet='utf-8' />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
Welcome to my Servers Webpage
|
Welcome to my Servers Webpage
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -19,25 +18,19 @@ export default function Home() {
|
||||||
Feel free to look around
|
Feel free to look around
|
||||||
</p>
|
</p>
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<Link key="about" href="/about">
|
<Link className={styles.card} key="about" href="/about">
|
||||||
<a className={styles.card}>
|
|
||||||
<h2>About →</h2>
|
<h2>About →</h2>
|
||||||
<p>Useless Info, don't bother</p>
|
<p>Useless Info, don't bother</p>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link key="servers" href="/games">
|
<Link className={styles.card} key="servers" href="/games">
|
||||||
<a className={styles.card}>
|
|
||||||
<h2>Games →</h2>
|
<h2>Games →</h2>
|
||||||
<p>List of all available Servers</p>
|
<p>List of all available Servers</p>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link key="services" href="/services">
|
<Link className={styles.card} key="services" href="/services">
|
||||||
<a className={styles.card}>
|
|
||||||
<h2>Services →</h2>
|
<h2>Services →</h2>
|
||||||
<p>List of available Services</p>
|
<p>List of available Services</p>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,8 +20,7 @@ function Services() {
|
||||||
content =
|
content =
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{initialData?.map((item: Service) => (
|
{initialData?.map((item: Service) => (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link className={styles.contentcard} key={item.name} href={item.href}>
|
||||||
<a className={styles.contentcard}>
|
|
||||||
<div className={styles.contentTitle}>
|
<div className={styles.contentTitle}>
|
||||||
{
|
{
|
||||||
item.icon ? (
|
item.icon ? (
|
||||||
|
@ -37,7 +36,6 @@ function Services() {
|
||||||
</div>
|
</div>
|
||||||
<div><p>{item.desc}</p></div>
|
<div><p>{item.desc}</p></div>
|
||||||
<div className={styles.cardwarn}><p>{item.warn}</p></div>
|
<div className={styles.cardwarn}><p>{item.warn}</p></div>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,13 +44,12 @@ function Services() {
|
||||||
content =
|
content =
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{fullData.map((item: Service) => (
|
{fullData.map((item: Service) => (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link className={styles.contentcard} key={item.name} href={item.href}>
|
||||||
<a className={styles.contentcard}>
|
|
||||||
<div className={styles.contentTitle}>
|
<div className={styles.contentTitle}>
|
||||||
{
|
{
|
||||||
item.icon ? (
|
item.icon ? (
|
||||||
<div className={styles.contentIcon}>
|
<div className={styles.contentIcon}>
|
||||||
<Image alt="icon" src={item.icon} layout="fill" objectFit="contain"></Image>
|
<Image alt="icon" src={item.icon} fill></Image>
|
||||||
</div>
|
</div>
|
||||||
) : (<></>)
|
) : (<></>)
|
||||||
}
|
}
|
||||||
|
@ -63,7 +60,6 @@ function Services() {
|
||||||
</div>
|
</div>
|
||||||
<div><p>{item.desc}</p></div>
|
<div><p>{item.desc}</p></div>
|
||||||
<div className={styles.cardwarn}><p>{item.warn}</p></div>
|
<div className={styles.cardwarn}><p>{item.warn}</p></div>
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
12
styled.d.ts
vendored
Normal file
12
styled.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'styled-components'
|
||||||
|
|
||||||
|
declare module 'styled-components' {
|
||||||
|
export interface DefaultTheme {
|
||||||
|
themeId: number,
|
||||||
|
colors: {
|
||||||
|
background: string,
|
||||||
|
primary: string
|
||||||
|
secondary: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,3 @@
|
||||||
.page {
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--black-0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0 2rem;
|
padding: 0 2rem;
|
||||||
}
|
}
|
||||||
|
@ -175,6 +170,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentIcon {
|
.contentIcon {
|
||||||
|
object-fit: "contain";
|
||||||
margin-right: 0.4rem;
|
margin-right: 0.4rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
|
|
Loading…
Reference in a new issue