This commit is contained in:
Philip 2023-10-23 18:56:29 +02:00
parent 375c4a212b
commit 12907e5b46
17 changed files with 112 additions and 85 deletions

View file

@ -178,13 +178,16 @@ const ProductSearch = () => {
>
<legend>{t("enterbarcode")}</legend>
<fieldset>
<legend>{t("enterbarcode")}</legend>
<Scan
onDetected={(barcode) => setBarcode(barcode)}
handleSubmit={(barcode) => handleSubmit(barcode)}
/>
<label htmlFor="barcodeInput" className="hidden">{t("enterbarcode")}</label>
<input
type="number"
name="barcode"
id="barcodeInput"
placeholder={t("enterbarcode")}
autoFocus={true}
value={barcode}

View file

@ -2,7 +2,7 @@ import Image from "next/image";
import { useTranslations } from "next-intl";
import { useState } from "react";
const SupportOption: React.FC = () => {
const SupportOption = () => {
const t = useTranslations("More");
const [icon, setIcon] = useState<string>("icon-paypal");
const [vendor, setVendor] = useState<string>("PayPal");

View file

@ -1,13 +1,15 @@
import { useTranslations } from 'next-intl';
import React, { useState, useEffect } from 'react';
import { useTranslations } from "next-intl";
import { useState, useEffect } from "react";
const OLEDMode: React.FC = () => {
const t = useTranslations('More');
const OLEDMode = () => {
const t = useTranslations("More");
const [isChecked, setIsChecked] = useState<boolean>(false);
const [error, setError] = useState<boolean>(false);
const setThemeColorAttribute = (color: string) => {
const themeColorElement = document.querySelector<HTMLMetaElement>('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]');
const themeColorElement = document.querySelector<HTMLMetaElement>(
'meta[name="theme-color"][media="(prefers-color-scheme: dark)"]'
);
if (themeColorElement) {
themeColorElement.setAttribute("content", color);
@ -16,7 +18,10 @@ const OLEDMode: React.FC = () => {
useEffect(() => {
const localStorageValue = localStorage.getItem("oled");
if (localStorageValue === "true" && window.matchMedia('(prefers-color-scheme: dark)').matches) {
if (
localStorageValue === "true" &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
document.documentElement.setAttribute("data-theme", "oled");
setThemeColorAttribute("#000");
setIsChecked(true);
@ -24,14 +29,17 @@ const OLEDMode: React.FC = () => {
}, []);
const handleClick = () => {
if (!isChecked && !window.matchMedia('(prefers-color-scheme: dark)').matches) {
if (
!isChecked &&
!window.matchMedia("(prefers-color-scheme: dark)").matches
) {
setError(true);
return;
}
if (!isChecked) {
document.documentElement.setAttribute("data-theme", "oled");
setThemeColorAttribute("#000");
localStorage.setItem('oled', 'true');
localStorage.setItem("oled", "true");
} else {
localStorage.clear();
document.documentElement.removeAttribute("data-theme");
@ -42,11 +50,19 @@ const OLEDMode: React.FC = () => {
};
return (
<span className="Grid switcher">
<label htmlFor="oled-switch" className="Grid switcher">
<div className="Grid-cell description">
OLED-Mode
<span className="info" id="cookieinfo">{t('thissetsacookie')}</span>
<span className={`info ${error ? "animated fadeIn" : ""}`} id="oledinfo" style={{display: error ? "block" : "none"}}>{t('activatedarkmode')}</span>
<span className="info" id="cookieinfo">
{t("thissetsacookie")}
</span>
<span
className={`info ${error ? "animated fadeIn" : ""}`}
id="oledinfo"
style={{ display: error ? "block" : "none" }}
>
{t("activatedarkmode")}
</span>
</div>
<div className="Grid-cell icons">
<input
@ -58,7 +74,7 @@ const OLEDMode: React.FC = () => {
onChange={handleClick}
/>
</div>
</span>
</label>
);
};

View file

@ -9,9 +9,18 @@ interface ModalProps {
children: React.ReactNode;
}
const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonClass, buttonText }) => {
const ModalWrapper = ({
children,
id,
buttonType,
buttonClass,
buttonText,
}: ModalProps) => {
const [isOpen, setIsOpen] = useState(false);
const modalRoot = typeof document !== 'undefined' ? document.getElementById("modal-root") : null;
const modalRoot =
typeof document !== "undefined"
? document.getElementById("modal-root")
: null;
useEffect(() => {
const handleEscapeKeyPress = (event: KeyboardEvent) => {
@ -35,7 +44,6 @@ const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonCl
}
};
document.addEventListener("keydown", handleEscapeKeyPress);
document.addEventListener("touchstart", handleTouchStart);
@ -91,15 +99,12 @@ const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonCl
{buttonText}
</div>
)}
{isOpen && modalRoot &&
{isOpen &&
modalRoot &&
createPortal(
<div className="modal_view animated fadeInUp open">
<div className="modal_close">
<a
className="btn-dark"
data-dismiss="modal"
onClick={closeModal}
>
<a className="btn-dark" data-dismiss="modal" onClick={closeModal}>
×
</a>
</div>

View file

@ -9,7 +9,7 @@ interface ShareButtonProps {
barcode: string;
}
const ShareButton: React.FC<ShareButtonProps> = ({ productName, barcode }) => {
const ShareButton = ({ productName, barcode }: ShareButtonProps) => {
const t = useTranslations("Check");
const [showButton, setShowButton] = useState<boolean>(false);
@ -91,7 +91,9 @@ const ShareButton: React.FC<ShareButtonProps> = ({ productName, barcode }) => {
text,
url,
})
.catch((err) => {console.error(err)});
.catch((err) => {
console.error(err);
});
}}
>
{t("share")}

View file

@ -1,13 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import Image from "next/image";
import { useTranslations } from "next-intl";
import { useState, useEffect, FC } from "react";
import { useState, useEffect } from "react";
interface ExtendedWindow extends Window {
MSStream?: any;
}
const Shortcut: FC = () => {
const Shortcut = () => {
const t = useTranslations("ShortcutPrompt");
const [showShortcut, setShowShortcut] = useState<boolean>(false);
@ -16,7 +16,8 @@ const Shortcut: FC = () => {
const windowWithMSStream = window as ExtendedWindow;
const isIOS: boolean =
/iPad|iPhone|iPod/.test(navigator.userAgent) && !windowWithMSStream.MSStream;
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
!windowWithMSStream.MSStream;
if (
!window.matchMedia("(display-mode: standalone)").matches &&

View file

@ -5,11 +5,6 @@ import React, { useState, FormEvent } from "react";
import ModalWrapper from "@/components/elements/modalwrapper";
export interface FlaggedItem {
item: string;
index: number;
}
const IngredientsCheck = () => {
const t = useTranslations("Ingredients");
const [flagged, setFlagged] = useState<string[]>([]);

View file

@ -0,0 +1,4 @@
export type FlaggedItem = {
item: string;
index: number;
};

View file

@ -1,17 +1,27 @@
import { GetStaticPropsContext } from 'next'
import { useTranslations } from 'next-intl';
import { GetStaticPropsContext } from "next";
import { useTranslations } from "next-intl";
import Container from "@/components/elements/container";
import Nav from "@/components/nav";
export default function NotFound() {
const t = useTranslations('404');
const t = useTranslations("404");
return (
<>
<Nav />
<Container heading={t('error404')}>
<p>{t('pagedoesnotexist')}</p>
<p dangerouslySetInnerHTML={{ __html: t('message', {statuspage: `<a href="https://stats.uptimerobot.com/LY1gRuP5j6/789004495">${t('statuspage')}</a>`, mastodon: '<a href="https://veganism.social/@vegancheck">Mastodon</a>'})}} />
<Container heading={t("error404")}>
<p>{t("pagedoesnotexist")}</p>
<p
dangerouslySetInnerHTML={{
__html: t("message", {
statuspage: `<a href="https://stats.uptimerobot.com/LY1gRuP5j6/789004495">${t(
"statuspage"
)}</a>`,
mastodon:
'<a href="https://veganism.social/@vegancheck">Mastodon</a>',
}),
}}
/>
</Container>
</>
);
@ -20,7 +30,7 @@ export default function NotFound() {
export async function getStaticProps({ locale }: GetStaticPropsContext) {
return {
props: {
messages: (await import(`../locales/${locale}.json`)).default
}
messages: (await import(`../locales/${locale}.json`)).default,
},
};
}

View file

@ -1,13 +1,8 @@
import "@/styles/style.scss";
import { init } from "@socialgouv/matomo-next";
import type { AppProps } from "next/app";
import { NextIntlProvider } from "next-intl";
import { useEffect } from "react";
export default function App({ Component, pageProps }: AppProps) {
useEffect(() => {
init({ url: "https://analytics.vegancheck.me", siteId: "1" });
}, []);
return (
<>
<NextIntlProvider messages={pageProps.messages}>

View file

@ -1,30 +1,28 @@
import { GetStaticPropsContext } from 'next'
import { useTranslations } from 'next-intl';
import React, { useState, useEffect } from 'react';
import { GetStaticPropsContext } from "next";
import { useTranslations } from "next-intl";
import React, { useState, useEffect } from "react";
import Container from "@/components/elements/container";
import Nav from "@/components/nav";
const Impressum = () => {
const t = useTranslations();
const [impressum, setImpressum] = useState('');
const [impressum, setImpressum] = useState("");
useEffect(() => {
fetch('https://philipbrembeck.com/impressum.txt', {method: "GET"})
.then(response => response.text())
.then(text => setImpressum(text))
.catch(error => console.error(error));
fetch("https://philipbrembeck.com/impressum.txt", { method: "GET" })
.then((response) => response.text())
.then((text) => setImpressum(text))
.catch((error) => console.error(error));
}, []);
return (
<>
<Nav />
<Container heading={t('More.imprint')}>
<p className="small">
{t('Privacy.germanonly')}
</p>
<div dangerouslySetInnerHTML={{ __html: impressum }} />
</Container>
<Nav />
<Container heading={t("More.imprint")}>
<p className="small">{t("Privacy.germanonly")}</p>
<div dangerouslySetInnerHTML={{ __html: impressum }} />
</Container>
</>
);
};
@ -34,7 +32,7 @@ export default Impressum;
export async function getStaticProps({ locale }: GetStaticPropsContext) {
return {
props: {
messages: (await import(`../locales/${locale}.json`)).default
}
messages: (await import(`../locales/${locale}.json`)).default,
},
};
}

View file

@ -1,7 +1,7 @@
import { GetStaticPropsContext } from "next";
import Container from "@/components/elements/container";
import IngredientsCheck from '@/components/ingredientscheck'
import IngredientsCheck from "@/components/ingredientscheck";
import Nav from "@/components/nav";
export default function ingredients() {
@ -9,10 +9,7 @@ export default function ingredients() {
<>
<div id="modal-root"></div>
<Nav />
<Container
logo={false}
backbutton={false}
>
<Container logo={false} backbutton={false}>
<IngredientsCheck />
</Container>
</>

View file

@ -108,7 +108,10 @@ export default function More() {
<span className="unknown icon-right-open"></span>
</div>
</Link>
<a href="https://frontendnet.work/vegancheck-api" className="Grid links">
<a
href="https://frontendnet.work/vegancheck-api"
className="Grid links"
>
<div className="Grid-cell description">{t("apidocumentation")}</div>
<div className="Grid-cell icons">
<span className="unknown icon-right-open"></span>

View file

@ -1,19 +1,17 @@
import { GetStaticPropsContext } from 'next'
import { useTranslations } from 'next-intl';
import { GetStaticPropsContext } from "next";
import { useTranslations } from "next-intl";
import Container from "@/components/elements/container";
import Nav from "@/components/nav";
export default function TOS() {
const t = useTranslations('TOS');
const t = useTranslations("TOS");
return (
<>
<Nav />
<Container heading={t('tos')}>
<p className="small">
{t('englishgermanonly')}
</p>
<div dangerouslySetInnerHTML={{__html: t.raw('tos_content')}} />
<Container heading={t("tos")}>
<p className="small">{t("englishgermanonly")}</p>
<div dangerouslySetInnerHTML={{ __html: t.raw("tos_content") }} />
</Container>
</>
);
@ -22,7 +20,7 @@ export default function TOS() {
export async function getStaticProps({ locale }: GetStaticPropsContext) {
return {
props: {
messages: (await import(`../locales/${locale}.json`)).default
}
messages: (await import(`../locales/${locale}.json`)).default,
},
};
}

View file

@ -1,3 +1,3 @@
/* Do use: Normalize.css as a CSS reset */
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}
button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}.hidden{display:none;}

View file

@ -118,7 +118,7 @@
display: block;
font-weight: 400;
font-size: rem(14.4px);
color: #ccc;
color: #595959;
margin-top: 0;
padding: 0;
line-height: rem(16px);