Added language preference system
This commit is contained in:
parent
3cf890d70d
commit
fb88e97825
|
@ -14,6 +14,7 @@ import {
|
|||
import { useEffect, useState } from "react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { ImageQuality } from "./Img";
|
||||
import OrderableList from "./OrderableList";
|
||||
import MainPanel from "./Panels/MainPanel";
|
||||
import Popup from "./Popup";
|
||||
import Select from "./Select";
|
||||
|
@ -250,33 +251,48 @@ export default function AppLayout(props: Props): JSX.Element {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<Popup
|
||||
state={appLayout.languagePanelOpen}
|
||||
setState={appLayout.setLanguagePanelOpen}
|
||||
>
|
||||
<h2 className="text-2xl">{langui.select_language}</h2>
|
||||
<div className="flex flex-wrap flex-row gap-2 mobile:flex-col">
|
||||
{router.locales?.map((locale) => (
|
||||
<Button
|
||||
key={locale}
|
||||
active={locale === router.locale}
|
||||
href={router.asPath}
|
||||
locale={locale}
|
||||
onClick={() => appLayout.setLanguagePanelOpen(false)}
|
||||
>
|
||||
{prettyLanguage(locale, languages)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</Popup>
|
||||
|
||||
<Popup
|
||||
state={appLayout.configPanelOpen}
|
||||
setState={appLayout.setConfigPanelOpen}
|
||||
>
|
||||
<h2 className="text-2xl">{langui.settings}</h2>
|
||||
|
||||
<div className="mt-4 grid gap-8 place-items-center text-center desktop:grid-cols-2">
|
||||
<div className="mt-4 grid gap-16 justify-items-center text-center desktop:grid-cols-[auto_auto]">
|
||||
{router.locales && (
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.languages}</h3>
|
||||
{appLayout.preferredLanguages && (
|
||||
<OrderableList
|
||||
items={
|
||||
appLayout.preferredLanguages.length > 0
|
||||
? new Map(
|
||||
appLayout.preferredLanguages.map((locale) => [
|
||||
locale,
|
||||
prettyLanguage(locale, languages),
|
||||
])
|
||||
)
|
||||
: new Map(
|
||||
router.locales.map((locale) => [
|
||||
locale,
|
||||
prettyLanguage(locale, languages),
|
||||
])
|
||||
)
|
||||
}
|
||||
onChange={(items) => {
|
||||
const preferredLanguages = [...items].map(
|
||||
([code]) => code
|
||||
);
|
||||
console.log(router.asPath);
|
||||
appLayout.setPreferredLanguages(preferredLanguages);
|
||||
router.push(router.asPath, router.asPath, {
|
||||
locale: preferredLanguages[0],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="grid gap-8 place-items-center text-center desktop:grid-cols-2">
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.theme}</h3>
|
||||
<div className="flex flex-row">
|
||||
|
@ -337,7 +353,9 @@ export default function AppLayout(props: Props): JSX.Element {
|
|||
className="rounded-r-none"
|
||||
onClick={() =>
|
||||
appLayout.setFontSize(
|
||||
appLayout.fontSize ? appLayout.fontSize / 1.05 : 1 / 1.05
|
||||
appLayout.fontSize
|
||||
? appLayout.fontSize / 1.05
|
||||
: 1 / 1.05
|
||||
)
|
||||
}
|
||||
>
|
||||
|
@ -347,16 +365,21 @@ export default function AppLayout(props: Props): JSX.Element {
|
|||
className="rounded-l-none rounded-r-none border-x-0"
|
||||
onClick={() => appLayout.setFontSize(1)}
|
||||
>
|
||||
{((appLayout.fontSize ?? 1) * 100).toLocaleString(undefined, {
|
||||
{((appLayout.fontSize ?? 1) * 100).toLocaleString(
|
||||
undefined,
|
||||
{
|
||||
maximumFractionDigits: 0,
|
||||
})}
|
||||
}
|
||||
)}
|
||||
%
|
||||
</Button>
|
||||
<Button
|
||||
className="rounded-l-none"
|
||||
onClick={() =>
|
||||
appLayout.setFontSize(
|
||||
appLayout.fontSize ? appLayout.fontSize * 1.05 : 1 * 1.05
|
||||
appLayout.fontSize
|
||||
? appLayout.fontSize * 1.05
|
||||
: 1 * 1.05
|
||||
)
|
||||
}
|
||||
>
|
||||
|
@ -399,6 +422,7 @@ export default function AppLayout(props: Props): JSX.Element {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@ interface Props {
|
|||
locale?: string;
|
||||
target?: "_blank";
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
draggable?: boolean
|
||||
draggable?: boolean;
|
||||
}
|
||||
|
||||
export default function Button(props: Props): JSX.Element {
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import { arrayMove } from "queries/helpers";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
items: Map<string, string>;
|
||||
onChange?: (items: Map<string, string>) => void;
|
||||
}
|
||||
|
||||
export default function LanguageSwitcher(props: Props): JSX.Element {
|
||||
const [items, setItems] = useState<Map<string, string>>(props.items);
|
||||
|
||||
useEffect(() => {
|
||||
props.onChange?.(items);
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
{[...items].map(([key, value], index) => (
|
||||
<>
|
||||
{index === 0 ? (
|
||||
<p>Primary language</p>
|
||||
) : index === 1 ? (
|
||||
<p>Secondary languages</p>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<div
|
||||
onDragStart={(event) => {
|
||||
const source = event.target as HTMLElement;
|
||||
const sourceIndex = source.parentElement
|
||||
? Array.from(source.parentElement.children)
|
||||
.filter((element) => element.tagName === "DIV")
|
||||
.indexOf(source)
|
||||
: -1;
|
||||
event.dataTransfer.setData("text", sourceIndex.toString());
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
const target = event.target as HTMLElement;
|
||||
const targetIndex = target.parentElement
|
||||
? Array.from(target.parentElement.children)
|
||||
.filter((element) => element.tagName === "DIV")
|
||||
.indexOf(target)
|
||||
: -1;
|
||||
const sourceIndex = parseInt(
|
||||
event.dataTransfer.getData("text"),
|
||||
10
|
||||
);
|
||||
const newItems = arrayMove([...items], sourceIndex, targetIndex);
|
||||
setItems(new Map(newItems));
|
||||
}}
|
||||
className="grid place-content-center place-items-center
|
||||
border-[1px] transition-all hover:text-light hover:bg-dark
|
||||
hover:drop-shadow-shade-lg border-dark bg-light text-dark
|
||||
rounded-full px-4 pt-[0.4rem] pb-[0.5rem] cursor-grab select-none"
|
||||
key={key}
|
||||
draggable
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -89,26 +89,6 @@ export default function MainPanel(props: Props): JSX.Element {
|
|||
</Button>
|
||||
</ToolTip>
|
||||
|
||||
{router.locale && (
|
||||
<ToolTip
|
||||
content={<h3 className="text-2xl">{langui.change_language}</h3>}
|
||||
placement="right"
|
||||
className="text-left"
|
||||
disabled={!appLayout.mainPanelReduced}
|
||||
>
|
||||
<Button
|
||||
onClick={() => appLayout.setLanguagePanelOpen(true)}
|
||||
className={
|
||||
appLayout.mainPanelReduced && isDesktop
|
||||
? ""
|
||||
: "!py-0.5 !px-2.5 !text-sm"
|
||||
}
|
||||
>
|
||||
{router.locale.toUpperCase()}
|
||||
</Button>
|
||||
</ToolTip>
|
||||
)}
|
||||
|
||||
{/* <ToolTip
|
||||
content={<h3 className="text-2xl">{langui.open_search}</h3>}
|
||||
placement="right"
|
||||
|
|
|
@ -4,7 +4,6 @@ import React, { ReactNode, useContext } from "react";
|
|||
|
||||
interface AppLayoutState {
|
||||
subPanelOpen: boolean | undefined;
|
||||
languagePanelOpen: boolean | undefined;
|
||||
configPanelOpen: boolean | undefined;
|
||||
mainPanelReduced: boolean | undefined;
|
||||
mainPanelOpen: boolean | undefined;
|
||||
|
@ -14,10 +13,8 @@ interface AppLayoutState {
|
|||
dyslexic: boolean | undefined;
|
||||
currency: string | undefined;
|
||||
playerName: string | undefined;
|
||||
preferredLanguages: string[] | undefined;
|
||||
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean | undefined>>;
|
||||
setLanguagePanelOpen: React.Dispatch<
|
||||
React.SetStateAction<boolean | undefined>
|
||||
>;
|
||||
setConfigPanelOpen: React.Dispatch<React.SetStateAction<boolean | undefined>>;
|
||||
setMainPanelReduced: React.Dispatch<
|
||||
React.SetStateAction<boolean | undefined>
|
||||
|
@ -31,12 +28,14 @@ interface AppLayoutState {
|
|||
setDyslexic: React.Dispatch<React.SetStateAction<boolean | undefined>>;
|
||||
setCurrency: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setPlayerName: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setPreferredLanguages: React.Dispatch<
|
||||
React.SetStateAction<string[] | undefined>
|
||||
>;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
const initialState: AppLayoutState = {
|
||||
subPanelOpen: false,
|
||||
languagePanelOpen: false,
|
||||
configPanelOpen: false,
|
||||
mainPanelReduced: false,
|
||||
mainPanelOpen: false,
|
||||
|
@ -46,8 +45,8 @@ const initialState: AppLayoutState = {
|
|||
dyslexic: false,
|
||||
currency: "USD",
|
||||
playerName: "",
|
||||
preferredLanguages: [],
|
||||
setSubPanelOpen: () => {},
|
||||
setLanguagePanelOpen: () => {},
|
||||
setMainPanelReduced: () => {},
|
||||
setMainPanelOpen: () => {},
|
||||
setDarkMode: () => {},
|
||||
|
@ -57,6 +56,7 @@ const initialState: AppLayoutState = {
|
|||
setDyslexic: () => {},
|
||||
setCurrency: () => {},
|
||||
setPlayerName: () => {},
|
||||
setPreferredLanguages: () => {},
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/no-empty-function */
|
||||
|
||||
|
@ -77,10 +77,6 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
boolean | undefined
|
||||
>("subPanelOpen", initialState.subPanelOpen);
|
||||
|
||||
const [languagePanelOpen, setLanguagePanelOpen] = useStateWithLocalStorage<
|
||||
boolean | undefined
|
||||
>("languagePanelOpen", initialState.languagePanelOpen);
|
||||
|
||||
const [configPanelOpen, setConfigPanelOpen] = useStateWithLocalStorage<
|
||||
boolean | undefined
|
||||
>("configPanelOpen", initialState.configPanelOpen);
|
||||
|
@ -115,11 +111,14 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
string | undefined
|
||||
>("playerName", initialState.playerName);
|
||||
|
||||
const [preferredLanguages, setPreferredLanguages] = useStateWithLocalStorage<
|
||||
string[] | undefined
|
||||
>("preferredLanguages", initialState.preferredLanguages);
|
||||
|
||||
return (
|
||||
<AppContext.Provider
|
||||
value={{
|
||||
subPanelOpen,
|
||||
languagePanelOpen,
|
||||
configPanelOpen,
|
||||
mainPanelReduced,
|
||||
mainPanelOpen,
|
||||
|
@ -129,8 +128,8 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
dyslexic,
|
||||
currency,
|
||||
playerName,
|
||||
preferredLanguages,
|
||||
setSubPanelOpen,
|
||||
setLanguagePanelOpen,
|
||||
setConfigPanelOpen,
|
||||
setMainPanelReduced,
|
||||
setMainPanelOpen,
|
||||
|
@ -140,6 +139,7 @@ export function AppContextProvider(props: Props): JSX.Element {
|
|||
setDyslexic,
|
||||
setCurrency,
|
||||
setPlayerName,
|
||||
setPreferredLanguages,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
|
|
@ -274,6 +274,18 @@ export function prettyLanguage(
|
|||
return result;
|
||||
}
|
||||
|
||||
export function prettyLanguageToCode(
|
||||
prettyLanguage: string,
|
||||
languages: AppStaticProps["languages"]
|
||||
): string {
|
||||
let result = prettyLanguage;
|
||||
languages.forEach((language) => {
|
||||
if (language?.attributes?.localized_name === prettyLanguage)
|
||||
result = language.attributes.code;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function prettyTestWarning(
|
||||
router: NextRouter,
|
||||
message: string,
|
||||
|
@ -463,3 +475,8 @@ export function getVideoThumbnailURL(uid: string): string {
|
|||
export function getVideoFile(uid: string): string {
|
||||
return `${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.mp4`;
|
||||
}
|
||||
|
||||
export function arrayMove<T>(arr: T[], old_index: number, new_index: number) {
|
||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||
return arr;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue