Skip to content

Commit

Permalink
Locale detection and language selection (#45)
Browse files Browse the repository at this point in the history
* 🐞 fix: Fix title in base layout

* ✨feature: Chair Page Setup, Navigation and Speakers List

* ✨feature: Attendance Page

* ✨feature: i18n

* ✨feature: Whiteboard Update and Chair Editing Support

* 🐞 fix: Formatting

* 🔵 other: Color Pallet update

* 🐞 fix: any -> unknown

* 📄 docs: Clearer Documentation

* ✨feature: Dark Mode

* ✨feature: System Color Mode detection & High Contrast Mode

* ✨feature: Changing Speech times

* ✨feature: Navbar Button Titles & New Chair Presentation Window Button in Navbar

* ✨feature: Navbar Button Titles & New Chair Presentation Window Button in Navbar

* 🐞 fix: Few Theme related design changes

* ✨feature: Fuse.js Fuzzy Search for Countries

* ✨feature: Add Country to List Toast and other minor changes

* ✨feature: Change Speakers Time: Show warning if wrong format and show success toast on success

* ✨feature: Chair Voting Page

* 🐞 fix: I18n Type Error fix

* 🐞 fix: I18n Type Error fix

* 🐞 fix: install fuse in the frontend project

* ✨ feat (CHASE Frontend): Local Storage Locale Detection and Settings option for selecting language / Local Storing of current theme preferance
branch: locale-detection-and-language-selection

* 🧼 format & lint (CHASE Frontend)
branch: locale-detection-and-language-selection

* 🐞 fix (CHASE Frontend): Window Build Error
branch: locale-detection-and-language-selection

* 🧼 format & lint (CHASE Frontend): Removing (or ignoring) all console.log to make linter test pass
branch: locale-detection-and-language-selection

* Revert "🐞 fix (CHASE Frontend): Window Build Error"

This reverts commit 51ceeef.

* Revert "Revert "🐞 fix (CHASE Frontend): Window Build Error""

This reverts commit 95c04e6.

* Revert "🧼 format & lint (CHASE Frontend): Removing (or ignoring) all console.log to make linter test pass"

This reverts commit 4920427.

* 🧼 format & lint (CHASE Frontend): Removing (or ignoring) all console.log to make linter test pass
branch: locale-detection-and-language-selection

* 🐞 fix (CHASE Frontend): using useState and useEffect in layout file
branch: locale-detection-and-language-selection

* 🐞 fix (CHASE-Frontend): SSR and localStorage
branch: locale-detection-and-language-selection

* 🧼 format & lint (CHASE-Frontend): lint
branch: locale-detection-and-language-selection

* 🐞 fix (CHASE-Frontend): Adding Localisation
branch: locale-detection-and-language-selection

---------

Co-authored-by: m1212e <[email protected]>
  • Loading branch information
Strehk and m1212e authored Jul 10, 2023
1 parent b84491b commit 5605c74
Show file tree
Hide file tree
Showing 17 changed files with 253 additions and 76 deletions.
3 changes: 3 additions & 0 deletions chase/backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export let server: FastifyInstance;
},
});

// rome-ignore lint: console output is intended
console.log(`
╔══════════════════════════════════════════════════════════════════╗
║ Serving API documentation on http://localhost:${PORT}/documentation ║
Expand Down Expand Up @@ -155,6 +156,7 @@ export let server: FastifyInstance;
if (!process.env.PRODUCTION) {
server.swagger();
}
// rome-ignore lint: console output is intended
console.log(`Running on port ${PORT}`);
await server.listen({ port: PORT, host: "0.0.0.0" });
db?.$disconnect();
Expand All @@ -164,4 +166,5 @@ export let server: FastifyInstance;
}
})();

// rome-ignore lint: console output is intended
console.log("Starting server...");
1 change: 0 additions & 1 deletion chase/frontend/app/chair/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export default function ParticipantDashboard() {

useEffect(() => {
const intervalAPICall = setInterval(() => {
console.log("API Call");
setData(apiTestData);
}, 1000);
return () => clearInterval(intervalAPICall);
Expand Down
1 change: 0 additions & 1 deletion chase/frontend/app/chair/speakers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export default function ChairSpeakersList() {

useEffect(() => {
const intervalAPICall = setInterval(() => {
console.log("API Call");
setData(apiTestData);
}, 1000);
return () => clearInterval(intervalAPICall);
Expand Down
4 changes: 1 addition & 3 deletions chase/frontend/app/chair/voting/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ export default function ChairVoting() {
label={LL.chairs.voting.BUTTON_NEW_MOTION()}
icon={<FontAwesomeIcon icon={faGavel} className="mr-2" />}
className="w-full"
onClick={() => {
console.log("API call to create a new motion");
}}
onClick={() => {}}
model={[]}
/>
<Motions
Expand Down
5 changes: 1 addition & 4 deletions chase/frontend/app/chair/whiteboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ export default function ChairWhiteboard() {
const [whiteboardContent, setWhiteboardContent] =
React.useState<string>(whiteboardTestData);

const pushWhiteboardContent = () => {
console.log("Pushing whiteboard content to server...", whiteboardContent);
};
const pushWhiteboardContent = () => {};

const resetWhiteboardContent = () => {
console.log("Resetting whiteboard content...");
setWhiteboardContent(whiteboardTestData);
};

Expand Down
42 changes: 22 additions & 20 deletions chase/frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,47 @@ import "primereact/resources/primereact.min.css";
//icons
import "primeicons/primeicons.css";

import Head from 'next/head';
import Head from "next/head";

import { detectLocale, navigatorDetector } from "typesafe-i18n/detectors";
import {
detectLocale,
navigatorDetector,
localStorageDetector,
} from "typesafe-i18n/detectors";
import { loadLocale } from "@/i18n/i18n-util.sync";
import { baseLocale, locales } from "@/i18n/i18n-util";
import TypesafeI18n from "@/i18n/i18n-react";

const inter = Inter({ subsets: ["latin"] });


export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// TODO inspect the way locale is detected and loaded.
// The Problem was that the locale was not loaded on the client side. There was a build error because the navigator was not defined on the server side.
// // https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/detectors)
// const locale = detectLocale(baseLocale, locales, navigatorDetector);
// loadLocale(locale);

// TadeSF 2023-06-14 suggested solution:
let locale = baseLocale; // default to baseLocale
if (typeof window !== "undefined") {
// Check if window is defined to ensure running on client side
locale = detectLocale(baseLocale, locales, navigatorDetector);
loadLocale(locale);
const isBrowser = typeof window !== "undefined";
let locale = baseLocale;

if (isBrowser) {
// Only run this code in a browser context.
locale = detectLocale(
baseLocale,
locales,
localStorageDetector,
navigatorDetector
);
}

loadLocale(locale);

return (
<TypesafeI18n locale={locale}>
<html lang="de">
<html lang="en">
<Head>
<title>Chase</title> {/* TODO Make title work */}
</Head>
<body className={inter.className}>
{children}
</body>
<body className={inter.className}>{children}</body>
</html>
</TypesafeI18n>
);
}
}
1 change: 0 additions & 1 deletion chase/frontend/app/participant/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export default function participant_dashboard() {

useEffect(() => {
const intervalAPICall = setInterval(() => {
console.log("API Call");
setData(apiTestData);
}, 1000);
return () => clearInterval(intervalAPICall);
Expand Down
1 change: 0 additions & 1 deletion chase/frontend/app/participant/speakers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export default function SpeakersList() {

useEffect(() => {
const intervalAPICall = setInterval(() => {
console.log("API Call");
setData(apiTestData);
}, 1000);
return () => clearInterval(intervalAPICall);
Expand Down
4 changes: 1 addition & 3 deletions chase/frontend/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ export default function Navbar({ children }: { children: React.ReactNode }) {
router.push("/login/participant");
};

const rejectLogout = () => {
console.log("Logout rejected");
};
const rejectLogout = () => {};

const confirmLogout = () => {
confirmDialog({
Expand Down
117 changes: 91 additions & 26 deletions chase/frontend/components/navbar/settings_sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use client";
import React, { useEffect, useState } from "react";
import { Sidebar } from "primereact/sidebar";
import { SelectButton } from "primereact/selectbutton";
Expand All @@ -9,6 +10,9 @@ import {
faMoon,
faSun,
} from "@fortawesome/free-solid-svg-icons";
import { Dropdown } from "primereact/dropdown";
import { SmallFlag } from "../flag_templates";
import { useI18nContext } from "@/i18n/i18n-react";

interface ColormodeOption {
name: string;
Expand All @@ -26,43 +30,50 @@ interface SettingsSidebarProps {
* It displays the settings of the user, which are:
* - Colortheme
* - Language
* - ...
* TODO: add more settings
* TODO: add functionality to change settings
* TODO: add functionality to save settings
* TODO: add functionality to reset settings
*/

export default function SettingsSidebar({
settingsSidebarVisible,
setSettingsSidebarVisible,
}: SettingsSidebarProps) {
const { LL } = useI18nContext();

const colortheme_items: ColormodeOption[] = [
{
name: "Hell",
name: LL.settings.colorTheme.SYSTEM(),
icon: faDisplay,
value: "system",
},
{
name: LL.settings.colorTheme.LIGHT(),
icon: faSun,
value: "light",
},
{
name: "Dunkel",
name: LL.settings.colorTheme.DARK(),
icon: faMoon,
value: "dark",
},
{
name: "System",
icon: faDisplay,
value: "system",
},
{
name: "Hoher Kontrast",
name: LL.settings.colorTheme.CONTRAST(),
icon: faCircleHalfStroke,
value: "contrast",
},
];

const [colortheme, setColortheme] = useState("system");
const [colortheme, setColortheme] = useState(
typeof window !== "undefined"
? localStorage.getItem("theme") ?? "system"
: "system",
);
const [isDarkMode, setIsDarkMode] = useState(false);

const [language, setLanguage] = useState(
typeof window !== "undefined"
? localStorage.getItem("lang") ?? "system"
: "system",
);

useEffect(() => {
setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);

Expand All @@ -85,10 +96,8 @@ export default function SettingsSidebar({
if (colortheme === "system") {
document.documentElement.classList.remove("contrast");
if (isDarkMode) {
console.log("dark");
document.documentElement.classList.add("dark");
} else {
console.log("light");
document.documentElement.classList.remove("dark");
}
} else if (colortheme === "light") {
Expand All @@ -105,25 +114,81 @@ export default function SettingsSidebar({
document.documentElement.classList.remove("contrast");
console.error(`Invalid colortheme: ${colortheme}`);
}

localStorage.setItem("theme", colortheme);
}, [colortheme, isDarkMode]);

useEffect(() => {
if (language === "system") {
localStorage.removeItem("lang");
} else if (language === "de") {
localStorage.setItem("lang", "de");
} else if (language === "en") {
localStorage.setItem("lang", "en");
} else {
console.error(`Invalid language: ${language}`);
return;
}
}, [language]);

return (
<Sidebar
visible={settingsSidebarVisible}
onHide={() => setSettingsSidebarVisible(false)}
position="top"
>
{/* TODO Settings */}
<div className="flex w-full justify-center items-center pt-2">
<SelectButton
value={colortheme}
onChange={(e) => {
setColortheme(e.value);
}}
itemTemplate={colorModeTemplate}
optionLabel="Color Theme"
options={colortheme_items}
/>
<div className="flex w-full justify-center items-center pt-2 gap-2">
<div className="flex flex-col">
<div className="text-sm text-gray-500">
{LL.settings.colorTheme.HEADLINE()}
</div>
<SelectButton
value={colortheme}
onChange={(e) => {
setColortheme(e.value);
}}
itemTemplate={colorModeTemplate}
optionLabel={LL.settings.colorTheme.HEADLINE()}
options={colortheme_items}
unselectable={false}
/>
</div>
<div className="flex flex-col">
<div className="text-sm text-gray-500">
{LL.settings.language.HEADLINE()}
</div>
<Dropdown
value={language}
options={[
{
flag: "uno",
label: LL.settings.language.SYSTEM(),
value: "system",
},
{
flag: "deu",
label: LL.settings.language.GERMAN(),
value: "de",
},
{
flag: "usa",
label: LL.settings.language.ENGLISH(),
value: "en",
},
]}
itemTemplate={(option) => (
<div className="flex items-center">
<SmallFlag countryCode={option.flag} />
<span className="ml-4">{option.label}</span>
</div>
)}
onChange={(e) => {
setLanguage(e.value);
if (window) window.location.reload();
}}
/>
</div>
</div>
</Sidebar>
);
Expand Down
1 change: 0 additions & 1 deletion chase/frontend/components/speakers_list/add_speaker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export default function AddSpeakerOverlay({
}) {
const { LL, locale } = useI18nContext();
const { showToast } = useContext(ToastContext);
console.log(showToast);

const [countries, setCountries] = useState<CountryData[] | null>(null);
const [query, setQuery] = useState<string>("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,6 @@ export default function ChangeSpeechTimeOverlay({
return;
}

console.log(
`API call to change the speech time (in seconds): ${calculateSecondsFromTime(
time,
)}`,
);

showToast({
severity: "success",
summary: LL.chairs.speakersList.changeSpeechTimeOverlay.TOAST_SUCCESS(
Expand Down
12 changes: 3 additions & 9 deletions chase/frontend/components/voting/voting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,18 @@ export default function VotingArea({
<Button
label={LL.chairs.voting.BUTTON_CHANGE_INFO()}
faIcon={faInfo}
onClick={() => {
console.log("Change Info");
}}
onClick={() => {}}
/>
<Button
label={LL.chairs.voting.BUTTON_RESET()}
faIcon={faUndo}
onClick={() => {
console.log("Reset");
}}
onClick={() => {}}
severity="warning"
/>
<Button
label={LL.chairs.voting.BUTTON_DELETE()}
faIcon={faTrash}
onClick={() => {
console.log("Delete");
}}
onClick={() => {}}
severity="danger"
/>
</div>
Expand Down
Loading

0 comments on commit 5605c74

Please sign in to comment.