Skip to content

Commit

Permalink
feat: add apperance settings with time format
Browse files Browse the repository at this point in the history
  • Loading branch information
MrQuackDuck committed Jan 3, 2025
1 parent 7dad659 commit c6338f8
Show file tree
Hide file tree
Showing 17 changed files with 201 additions and 22 deletions.
5 changes: 4 additions & 1 deletion src/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AuthProvider } from "@/features/authorize";
import { UsersVolumeProvider } from "@/features/control-user-volume";
import { SettingsOpenCloseProvider } from "@/features/open-close-settings";
import { EncryptionKeysProvider, LanguageSettingsProvider, LoadingProvider, NotificationsSettingsProvider, ThemeProvider, TranslationProvider, VoiceSettingsProvider } from "@/shared/lib";
import { TimeFormatSettingsProvider } from "@/shared/lib/providers/TimeFormatProvider";
import { TooltipProvider } from "@/shared/ui";
import { ChatConnectionsProvider } from "@/widgets/chat-section";

Expand All @@ -30,7 +31,9 @@ createRoot(document.getElementById("root")!).render(
<SettingsOpenCloseProvider>
<VoiceSettingsProvider>
<NotificationsSettingsProvider>
<App />
<TimeFormatSettingsProvider>
<App />
</TimeFormatSettingsProvider>
</NotificationsSettingsProvider>
</VoiceSettingsProvider>
</SettingsOpenCloseProvider>
Expand Down
7 changes: 6 additions & 1 deletion src/assets/locale/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Kopieren",
"DOWNLOAD": "Herunterladen",
"ACCOUNT": "Konto",
"APPEARANCE": "Erscheinungsbild",
"COLIR_ID_AKA_HEX_ID": "Colir ID (oder Hex-ID)",
"IT_CANT_BE_CHANGED": "*Kann nicht geändert werden",
"VOICE_SETTINGS": "Spracheinstellungen",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Schließen",
"ROOM_OWNER": "Raumbesitzer",
"ERROR_CODE": "Fehlercode",
"SENDING": "Senden..."
"SENDING": "Senden...",
"TIME_FORMAT": "Zeitformat",
"EXAMPLE": "Beispiel",
"12_HOUR": "12-Stunden",
"24_HOUR": "24-Stunden"
}
7 changes: 6 additions & 1 deletion src/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Copy",
"DOWNLOAD": "Download",
"ACCOUNT": "Account",
"APPEARANCE": "Appearance",
"COLIR_ID_AKA_HEX_ID": "Colir Id (aka. Hex Id)",
"IT_CANT_BE_CHANGED": "*It can't be changed",
"VOICE_SETTINGS": "Voice Settings",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Close",
"ROOM_OWNER": "Room owner",
"ERROR_CODE": "Error code",
"SENDING": "Sending..."
"SENDING": "Sending...",
"TIME_FORMAT": "Time format",
"EXAMPLE": "Example",
"12_HOUR": "12-hour",
"24_HOUR": "24-hour"
}
7 changes: 6 additions & 1 deletion src/assets/locale/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Copiar",
"DOWNLOAD": "Descargar",
"ACCOUNT": "Cuenta",
"APPEARANCE": "Apariencia",
"COLIR_ID_AKA_HEX_ID": "Colir ID (o ID hexadecimal)",
"IT_CANT_BE_CHANGED": "*No se puede cambiar",
"VOICE_SETTINGS": "Configuración de voz",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Cerrar",
"ROOM_OWNER": "Propietario de la sala",
"ERROR_CODE": "Código de error",
"SENDING": "Enviando..."
"SENDING": "Enviando...",
"TIME_FORMAT": "Formato de hora",
"EXAMPLE": "Ejemplo",
"12_HOUR": "12 horas",
"24_HOUR": "24 horas"
}
7 changes: 6 additions & 1 deletion src/assets/locale/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Copier",
"DOWNLOAD": "Télécharger",
"ACCOUNT": "Compte",
"APPEARANCE": "Apparence",
"COLIR_ID_AKA_HEX_ID": "Colir ID (ou ID hexadécimal)",
"IT_CANT_BE_CHANGED": "*Ne peut pas être modifié",
"VOICE_SETTINGS": "Paramètres vocaux",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Échap] Fermer",
"ROOM_OWNER": "Propriétaire de la salle",
"ERROR_CODE": "Code d'erreur",
"SENDING": "Envoi en cours..."
"SENDING": "Envoi en cours...",
"TIME_FORMAT": "Format de l'heure",
"EXAMPLE": "Exemple",
"12_HOUR": "12 heures",
"24_HOUR": "24 heures"
}
7 changes: 6 additions & 1 deletion src/assets/locale/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Kopiuj",
"DOWNLOAD": "Pobierz",
"ACCOUNT": "Konto",
"APPEARANCE": "Wygląd",
"COLIR_ID_AKA_HEX_ID": "Colir ID (lub hex ID)",
"IT_CANT_BE_CHANGED": "*Nie można zmienić",
"VOICE_SETTINGS": "Ustawienia głosu",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Zamknij",
"ROOM_OWNER": "Właściciel pokoju",
"ERROR_CODE": "Kod błędu",
"SENDING": "Wysyłanie..."
"SENDING": "Wysyłanie...",
"TIME_FORMAT": "Format czasu",
"EXAMPLE": "Przykład",
"12_HOUR": "12-godzinny",
"24_HOUR": "24-godzinny"
}
7 changes: 6 additions & 1 deletion src/assets/locale/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Копировать",
"DOWNLOAD": "Скачать",
"ACCOUNT": "Аккаунт",
"APPEARANCE": "Внешний вид",
"COLIR_ID_AKA_HEX_ID": "Colir ID (или hex ID)",
"IT_CANT_BE_CHANGED": "*Это нельзя поменять",
"VOICE_SETTINGS": "Настройки голоса",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Закрыть",
"ROOM_OWNER": "Владелец комнаты",
"ERROR_CODE": "Код ошибки",
"SENDING": "Отправка..."
"SENDING": "Отправка...",
"TIME_FORMAT": "Формат времени",
"EXAMPLE": "Пример",
"12_HOUR": "12-часовой",
"24_HOUR": "24-часовой"
}
7 changes: 6 additions & 1 deletion src/assets/locale/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"COPY": "Копіювати",
"DOWNLOAD": "Завантажити",
"ACCOUNT": "Обліковий запис",
"APPEARANCE": "Зовнішній вигляд",
"COLIR_ID_AKA_HEX_ID": "Colir ID (або hex ID)",
"IT_CANT_BE_CHANGED": "*Не можна змінити",
"VOICE_SETTINGS": "Налаштування голосу",
Expand Down Expand Up @@ -229,5 +230,9 @@
"ESC_CLOSE": "[Esc] Закрити",
"ROOM_OWNER": "Власник кімнати",
"ERROR_CODE": "Код помилки",
"SENDING": "Надсилання..."
"SENDING": "Надсилання...",
"TIME_FORMAT": "Формат часу",
"EXAMPLE": "Приклад",
"12_HOUR": "12-годинний",
"24_HOUR": "24-годинний"
}
19 changes: 6 additions & 13 deletions src/entities/Message/ui/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useContextSelector } from "use-context-selector";
import { AttachmentsSection } from "@/entities/Attachment";
import { ReactionBar } from "@/entities/Reaction";
import { CurrentUserContext, UserModel, Username } from "@/entities/User";
import { cn, decryptString, encryptString, LanguageSettingsContext, replaceEmojis, useInfoToast, useTheme, useTranslation } from "@/shared/lib";
import { cn, decryptString, encryptString, LanguageSettingsContext, replaceEmojis, useFormatDate, useInfoToast, useTheme, useTranslation } from "@/shared/lib";
import { Button, ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, EmojiPicker, Separator, Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui";

import { formatText } from "../lib/formatText";
Expand Down Expand Up @@ -204,14 +204,7 @@ const Message = forwardRef(
setShiftPressed(event.shiftKey);
}

function formatDate(date) {
const now = Moment();
const givenDate = Moment(date);

if (givenDate.isSame(now, "day")) return givenDate.format("h:mm A");
if (givenDate.isSame(now.add(1, "day"), "day")) return givenDate.format("MMMM D, h:mm A");
return givenDate.format("MMMM D, h:mm A");
}
const { formatDateShortened, formatFullDate } = useFormatDate();

// Preventing context menu on the attachments and the reaction bar (because it's already handled by the ReactionBar component)
function validateContextMenu(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
Expand Down Expand Up @@ -274,9 +267,9 @@ const Message = forwardRef(
<div className="flex row items-center gap-1.5">
<Username user={sender} />
<Tooltip>
<TooltipTrigger asChild>{<span className="text-slate-400 cursor-default text-[0.625rem] translate-y-[1px]">{formatDate(message.postDate)}</span>}</TooltipTrigger>
<TooltipTrigger asChild>{<span className="text-slate-400 cursor-default text-[0.625rem] translate-y-[1px]">{formatDateShortened(message.postDate)}</span>}</TooltipTrigger>
<TooltipContent side="right">
<span className="text-[12px] capitalize">{Moment(message.postDate).format("LLLL")}</span>
<span className="text-[12px] capitalize">{formatFullDate(message.postDate)}</span>
</TooltipContent>
</Tooltip>
{message.editDate && (
Expand All @@ -286,12 +279,12 @@ const Message = forwardRef(
<TooltipTrigger asChild>
{
<span className="text-slate-400 cursor-default text-[0.625rem] translate-y-[1px] font-medium">
{t("EDITED")} {formatDate(message.editDate)}
{t("EDITED")} {formatDateShortened(message.editDate)}
</span>
}
</TooltipTrigger>
<TooltipContent side="right">
<span className="text-[12px] capitalize">{Moment(message.editDate).format("LLLL")}</span>
<span className="text-[12px] capitalize">{formatFullDate(message.editDate)}</span>
</TooltipContent>
</Tooltip>
</>
Expand Down
65 changes: 65 additions & 0 deletions src/pages/settings/ui/AppearanceSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useContextSelector } from "use-context-selector";

import { useFormatDate, useTheme, useTranslation } from "@/shared/lib";
import { TimeFormatSettingsContext } from "@/shared/lib/providers/TimeFormatProvider";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, Separator } from "@/shared/ui";

function AppearanceSettings() {
const t = useTranslation();
const { theme, setTheme } = useTheme();
const { timeFormat, setTimeFormat } = useContextSelector(TimeFormatSettingsContext, (c) => c);
const { formatFullDate } = useFormatDate();
const exampleTime: Date = new Date(2025, 11, 19, 13, 27);

function handleThemeChange(value: string) {
if (value == "dark" || value == "light") setTheme(value);
}

function handleTimeFormatChange(value: string) {
if (value == "12-hour" || value == "24-hour") setTimeFormat(value);
}

return (
<div className="flex flex-col gap-3.5">
<span className="text-3xl font-semibold">{t("APPEARANCE")}</span>
<Separator />
<div className="flex flex-row gap-4 max-w-[48rem]">
<div className="w-full flex flex-col gap-1.5">
<span className="text-sm font-medium">{t("THEME")}</span>
<Select value={theme} onValueChange={handleThemeChange}>
<SelectTrigger>
<SelectValue placeholder={t("DEFAULT")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>{t("THEME")}</SelectLabel>
<SelectItem value="light">{t("LIGHT")}</SelectItem>
<SelectItem value="dark">{t("DARK")}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="w-full flex flex-col gap-1.5">
<span className="text-sm font-medium">{t("TIME_FORMAT")}</span>
<Select value={timeFormat} onValueChange={handleTimeFormatChange}>
<SelectTrigger>
<SelectValue placeholder={t("DEFAULT")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>{t("TIME_FORMAT")}</SelectLabel>
<SelectItem value="12-hour">{t("12_HOUR")}</SelectItem>
<SelectItem value="24-hour">{t("24_HOUR")}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<span className="text-slate-500 text-sm">
<span className="font-semibold">{t("EXAMPLE")}</span>: {formatFullDate(exampleTime)}
</span>
</div>
</div>
</div>
);
}

export default AppearanceSettings;
2 changes: 2 additions & 0 deletions src/pages/settings/ui/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SettingsTabs as SettingsTabsEnum } from "@/shared/model";
import { Button, PopupWindow, ScrollArea, Separator, Sheet, SheetContent, SheetDescription, SheetTitle } from "@/shared/ui";

import AccountSettings from "./AccountSettings";
import AppearanceSettings from "./AppearanceSettings";
import ImportExportSettings from "./ImportExportSettings";
import LanguageSettings from "./LanguageSettings";
import NotificationsSettings from "./NotificationsSettings";
Expand Down Expand Up @@ -76,6 +77,7 @@ export function SettingsPage() {
<ScrollArea className="w-full">
<div className="w-full pl-4 pr-12 pt-5">
{selectedTab == SettingsTabsEnum.Account && <AccountSettings dialogOpenClosed={setIsAnyDialogOpen} />}
{selectedTab == SettingsTabsEnum.Appearance && <AppearanceSettings />}
{selectedTab == SettingsTabsEnum.VoiceSettings && <VoiceSettings />}
{selectedTab == SettingsTabsEnum.Notifications && <NotificationsSettings />}
{selectedTab == SettingsTabsEnum.Statistics && <StatisticsSettings />}
Expand Down
5 changes: 4 additions & 1 deletion src/pages/settings/ui/SettingsTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BarChart3Icon, GlobeIcon, ImportIcon, MegaphoneIcon, UserIcon, Volume2Icon } from "lucide-react";
import { BarChart3Icon, GlobeIcon, ImportIcon, MegaphoneIcon, Sparkles, UserIcon, Volume2Icon } from "lucide-react";

import { useTranslation } from "@/shared/lib";
import { SettingsTabs as SettingsTabsEnum } from "@/shared/model";
Expand All @@ -20,6 +20,9 @@ function SettingsTabs({ className, selectedTab, setSelectedTab }: SettingsTabsPr
<Tab className="w-full" isSelected={selectedTab == SettingsTabsEnum.Account} onClick={() => setSelectedTab(SettingsTabsEnum.Account)}>
<UserIcon className="shrink-0 text-popover-foreground mr-1.5 h-4 w-4" strokeWidth={2.5} /> {t("ACCOUNT")}
</Tab>
<Tab className="w-full" isSelected={selectedTab == SettingsTabsEnum.Appearance} onClick={() => setSelectedTab(SettingsTabsEnum.Appearance)}>
<Sparkles className="shrink-0 text-popover-foreground mr-1.5 h-4 w-4" strokeWidth={2.5} /> {t("APPEARANCE")}
</Tab>
<Tab className="w-full" isSelected={selectedTab == SettingsTabsEnum.VoiceSettings} onClick={() => setSelectedTab(SettingsTabsEnum.VoiceSettings)}>
<Volume2Icon className="shrink-0 text-popover-foreground mr-1.5 h-4 w-4" strokeWidth={2.5} /> {t("VOICE_SETTINGS")}
</Tab>
Expand Down
1 change: 1 addition & 0 deletions src/shared/lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { useAdaptiveColor } from "./useAdaptiveColor";
export { useErrorToast } from "./useErrorToast";
export { useFormatDate } from "./useFormatDate";
export { useImportExportSettings } from "./useImportExportSettings";
export { useInfoToast } from "./useInfoToast";
export { useInvertedScrollArea } from "./useInvertedScrollArea";
Expand Down
37 changes: 37 additions & 0 deletions src/shared/lib/hooks/useFormatDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import "moment/min/locales";

import Moment from "moment/min/moment-with-locales";
import { useContextSelector } from "use-context-selector";

import { TimeFormatSettingsContext } from "../providers/TimeFormatProvider";

export const useFormatDate = (): {
formatDateShortened: (date: Date) => string;
formatFullDate: (date: Date) => string;
} => {
const { timeFormat } = useContextSelector(TimeFormatSettingsContext, (c) => c);

const formatDateShortened = (date: Date): string => {
const now = Moment();
const givenDate = Moment(date);
const format = timeFormat === "12-hour" ? "h:mm A" : "HH:mm";

if (givenDate.isSame(now, "day")) {
return givenDate.format(format);
}

if (givenDate.isSame(now.clone().add(1, "day"), "day")) {
return givenDate.format(`MMM D, ${format}`);
}

return givenDate.format(`MMM D, ${format}`);
};

const formatFullDate = (date: Date): string => {
const givenDate = Moment(date);
const format = timeFormat === "12-hour" ? "h:mm A" : "HH:mm";
return givenDate.format(`dddd, MMMM D, YYYY ${format}`);
};

return { formatDateShortened, formatFullDate };
};
38 changes: 38 additions & 0 deletions src/shared/lib/providers/TimeFormatProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useEffect, useState } from "react";
import { createContext } from "use-context-selector";

import { useLocalStorage } from "../hooks/useLocalStorage";

type TimeFormat = "12-hour" | "24-hour";

export const TimeFormatSettingsContext = createContext<{
timeFormat: TimeFormat;
setTimeFormat: (format: TimeFormat) => void;
}>({
timeFormat: "12-hour",
setTimeFormat: () => {}
});

export const TimeFormatSettingsProvider = ({ children }) => {
const { getFromLocalStorage, setToLocalStorage } = useLocalStorage();
const [timeFormat, setTimeFormat] = useState<TimeFormat>(getFromLocalStorage("timeFormat") ?? "12-hour");

function saveAllToLocalStorage() {
setToLocalStorage("timeFormat", timeFormat);
}

useEffect(() => {
saveAllToLocalStorage();
}, [timeFormat]);

return (
<TimeFormatSettingsContext.Provider
value={{
timeFormat: timeFormat,
setTimeFormat: setTimeFormat
}}
>
{children}
</TimeFormatSettingsContext.Provider>
);
};
1 change: 1 addition & 0 deletions src/shared/lib/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export { LanguageSettingsContext, LanguageSettingsProvider } from "./LanguageSet
export { LoadingContext, LoadingProvider } from "./LoadingProvider";
export { NotificationsSettingsContext, NotificationsSettingsProvider } from "./NotificationsSettingsProvider";
export { ThemeProvider, useTheme } from "./ThemeProvider";
export { TimeFormatSettingsContext, TimeFormatSettingsProvider } from "./TimeFormatProvider";
export { TranslationContext, TranslationProvider } from "./TranslationProvider";
export { VoiceSettingsContext, VoiceSettingsProvider } from "./VoiceSettingsProvider";
Loading

0 comments on commit c6338f8

Please sign in to comment.