add language selection menu

This commit is contained in:
piradata 2024-05-16 02:51:51 -03:00
parent 29d9c43834
commit 72aa822655
11 changed files with 10071 additions and 76 deletions

9853
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@
"@reduxjs/toolkit": "^2.2.3",
"@vanilla-extract/css": "^1.14.2",
"@vanilla-extract/recipes": "^0.5.2",
"iso-639-1": "3.1.2",
"auto-launch": "^5.0.6",
"axios": "^1.6.8",
"better-sqlite3": "^9.5.0",

View File

@ -153,7 +153,8 @@
"enable_real_debrid": "Enable Real Debrid",
"real_debrid": "Real Debrid",
"real_debrid_api_token_hint": "You can get your API key <0>here</0>.",
"save_changes": "Save changes"
"save_changes": "Save changes",
"language": "Language"
},
"notifications": {
"download_complete": "Download complete",

View File

@ -153,7 +153,8 @@
"enable_real_debrid": "Activar Real Debrid",
"real_debrid": "Real Debrid",
"real_debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí</0>.",
"save_changes": "Guardar cambios"
"save_changes": "Guardar cambios",
"language": "Idioma"
},
"notifications": {
"download_complete": "Descarga completada",

View File

@ -115,7 +115,8 @@
"enable_download_notifications": "Quand un téléchargement est terminé",
"enable_repack_list_notifications": "Quand un nouveau repack est ajouté",
"telemetry": "Télémétrie",
"telemetry_description": "Activer les statistiques d'utilisation anonymes"
"telemetry_description": "Activer les statistiques d'utilisation anonymes",
"language": "Langue"
},
"notifications": {
"download_complete": "Téléchargement terminé",

View File

@ -149,7 +149,8 @@
"enable_real_debrid": "Habilitar Real Debrid",
"real_debrid": "Real Debrid",
"real_debrid_api_token_hint": "Você pode obter sua chave de API <0>aqui</0>.",
"save_changes": "Salvar mudanças"
"save_changes": "Salvar mudanças",
"language": "Idioma"
},
"notifications": {
"download_complete": "Download concluído",

View File

@ -8,3 +8,4 @@ export * from "./sidebar/sidebar";
export * from "./text-field/text-field";
export * from "./checkbox-field/checkbox-field";
export * from "./link/link";
export * from "./select/select";

View File

@ -0,0 +1,60 @@
import { SPACING_UNIT, vars } from "../../theme.css";
import { style } from "@vanilla-extract/css";
import { recipe } from "@vanilla-extract/recipes";
export const select = recipe({
base: {
display: "inline-flex",
transition: "all ease 0.2s",
width: "fit-content",
alignItems: "center",
borderRadius: "8px",
border: `1px solid ${vars.color.border}`,
height: "40px",
minHeight: "40px",
},
variants: {
focused: {
true: {
borderColor: "#DADBE1",
},
false: {
":hover": {
borderColor: "rgba(255, 255, 255, 0.5)",
},
},
},
theme: {
primary: {
backgroundColor: vars.color.darkBackground,
},
dark: {
backgroundColor: vars.color.background,
},
},
},
});
export const option = style({
backgroundColor: vars.color.darkBackground,
borderColor: "transparent",
borderRadius: "8px",
width: "fit-content",
height: "100%",
outline: "none",
color: "#DADBE1",
cursor: "default",
fontFamily: "inherit",
fontSize: vars.size.bodyFontSize,
textOverflow: "ellipsis",
padding: `${SPACING_UNIT}px`,
":focus": {
cursor: "text",
},
});
export const label = style({
marginBottom: `${SPACING_UNIT}px`,
display: "block",
color: vars.color.bodyText,
});

View File

@ -0,0 +1,46 @@
import { useId, useState } from "react";
import type { RecipeVariants } from "@vanilla-extract/recipes";
import * as styles from "./select.css";
export interface SelectProps
extends React.DetailedHTMLProps<
React.SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
> {
theme?: NonNullable<RecipeVariants<typeof styles.select>>["theme"];
label?: string;
}
export function Select({
value,
theme = "primary",
label,
children,
onChange,
}: SelectProps) {
const [isFocused, setIsFocused] = useState(false);
const id = useId();
return (
<div style={{ flex: 1 }}>
{label && (
<label htmlFor={id} className={styles.label}>
{label}
</label>
)}
<div className={styles.select({ focused: isFocused, theme })}>
<select
id={id}
value={value}
className={styles.option}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChange={onChange}
>
{children}
</select>
</div>
</div>
);
}

View File

@ -1,11 +1,14 @@
import { useEffect, useState } from "react";
import ISO6391 from 'iso-639-1'
import { TextField, Button, CheckboxField } from "@renderer/components";
import { TextField, Button, CheckboxField, Select} from "@renderer/components";
import { useTranslation } from "react-i18next";
import * as styles from "./settings-general.css";
import type { UserPreferences } from "@types";
import { changeLanguage } from "i18next";
import * as languageResources from "@locales";
export interface SettingsGeneralProps {
userPreferences: UserPreferences | null;
updateUserPreferences: (values: Partial<UserPreferences>) => void;
@ -15,34 +18,41 @@ export function SettingsGeneral({
userPreferences,
updateUserPreferences,
}: SettingsGeneralProps) {
const [form, setForm] = useState({
downloadsPath: "",
downloadNotificationsEnabled: false,
repackUpdatesNotificationsEnabled: false,
});
useEffect(() => {
if (userPreferences) {
const {
downloadsPath,
downloadNotificationsEnabled,
repackUpdatesNotificationsEnabled,
} = userPreferences;
window.electron.getDefaultDownloadsPath().then((defaultDownloadsPath) => {
setForm((prev) => ({
...prev,
downloadsPath: downloadsPath ?? defaultDownloadsPath,
downloadNotificationsEnabled,
repackUpdatesNotificationsEnabled,
}));
});
}
}, [userPreferences]);
const { t } = useTranslation("settings");
const [form, setForm] = useState(
{
downloadsPath: '',
downloadNotificationsEnabled: false,
repackUpdatesNotificationsEnabled: false,
language: '',
}
);
const [defaultDownloadsPath, setdefaultDownloadsPath] = useState('');
useEffect(() => {
async function fetchdefaultDownloadsPath() {
setdefaultDownloadsPath(await window.electron.getDefaultDownloadsPath())
}
fetchdefaultDownloadsPath();
}, []);
useEffect(updateFormWithUserPreferences, [userPreferences, defaultDownloadsPath]);
const handleLanguageChange = (event) => {
const value = event.target.value;
handleChange({ language: value });
changeLanguage(value);
};
const handleChange = (values: Partial<typeof form>) => {
// TODO: why is the setForm needed if updateUserPreferences already changes the
// UserPreferences and useEffect(updateFormWithUserPreferences)
// does the setForm((prev) in the callback function?
setForm((prev) => ({ ...prev, ...values }));
updateUserPreferences(values);
};
@ -56,10 +66,21 @@ export function SettingsGeneral({
if (filePaths && filePaths.length > 0) {
const path = filePaths[0];
handleChange({ downloadsPath: path });
updateUserPreferences({ downloadsPath: path });
}
};
function updateFormWithUserPreferences(){
if (userPreferences) {
setForm((prev) => ({
...prev,
downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath,
downloadNotificationsEnabled: userPreferences.downloadNotificationsEnabled,
repackUpdatesNotificationsEnabled: userPreferences.repackUpdatesNotificationsEnabled,
language: userPreferences.language
}));
}
}
return (
<>
<div className={styles.downloadsPathField}>
@ -79,28 +100,38 @@ export function SettingsGeneral({
</Button>
</div>
<h3>{t("language")}</h3>
<>
<Select value={form.language} onChange={handleLanguageChange}>
{Object.keys(languageResources).map(language => (
<option key={language} value={language}>{ISO6391.getName(language)}</option>
))}
</Select>
</>
<h3>{t("notifications")}</h3>
<>
<CheckboxField
label={t("enable_download_notifications")}
checked={form.downloadNotificationsEnabled}
onChange={() =>
handleChange({
downloadNotificationsEnabled: !form.downloadNotificationsEnabled,
})
}
/>
<CheckboxField
label={t("enable_download_notifications")}
checked={form.downloadNotificationsEnabled}
onChange={() =>
handleChange({
downloadNotificationsEnabled: !form.downloadNotificationsEnabled,
})
}
/>
<CheckboxField
label={t("enable_repack_list_notifications")}
checked={form.repackUpdatesNotificationsEnabled}
onChange={() =>
handleChange({
repackUpdatesNotificationsEnabled:
!form.repackUpdatesNotificationsEnabled,
})
}
/>
<CheckboxField
label={t("enable_repack_list_notifications")}
checked={form.repackUpdatesNotificationsEnabled}
onChange={() =>
handleChange({
repackUpdatesNotificationsEnabled:
!form.repackUpdatesNotificationsEnabled,
})
}
/>
</>
</>
);
}

View File

@ -27,32 +27,31 @@ export function Settings() {
window.electron.updateUserPreferences(values);
};
const renderCategory = () => {
if (currentCategory === "general") {
return (
<SettingsGeneral
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
function renderCategory() {
switch (currentCategory) {
case "general":
return (
<SettingsGeneral
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
case "real_debrid":
return (
<SettingsRealDebrid
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
default:
return (
<SettingsBehavior
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
}
if (currentCategory === "real_debrid") {
return (
<SettingsRealDebrid
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
}
return (
<SettingsBehavior
userPreferences={userPreferences}
updateUserPreferences={handleUpdateUserPreferences}
/>
);
};
}
return (
<section className={styles.container}>