Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions main/helpers/db/connectDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,29 +154,50 @@ export const getLibraryStats = async () => {
};

export const getSettings = async () => {
const settings = await db.select().from(schema.settings).limit(1);
return settings[0];
const defaults = {
name: null,
profilePicture: null,
musicFolder: null,
lastFmUsername: null,
lastFmSessionKey: null,
enableLastFm: false,
scrobbleThreshold: 50,
language: "en",
};

const settingsRow = await db.select().from(settings).limit(1);
if (settingsRow.length === 0) return defaults;

return {
...defaults,
...settingsRow[0],
language: settingsRow[0].language || "en",
};
};

export const updateSettings = async (data: any) => {
const currentSettings = await db.select().from(settings);

if (currentSettings[0].profilePicture) {
if (currentSettings[0]?.profilePicture && data.profilePicture && data.profilePicture !== currentSettings[0].profilePicture) {
try {
fs.unlinkSync(currentSettings[0].profilePicture);
} catch (error) {
console.error("Error deleting old profile picture:", error);
}
}

await db.update(settings).set({
name: data.name,
profilePicture: data.profilePicture,
});
// Construir objeto de actualización, solo con los campos que vienen en data
const updateObj: any = {};
if (data.name !== undefined) updateObj.name = data.name;
if (data.profilePicture !== undefined) updateObj.profilePicture = data.profilePicture;
if (data.language !== undefined) updateObj.language = data.language;

await db.update(settings).set(updateObj);

return true;
};


export const getSongs = async (page: number = 1, limit: number = 30) => {
return await db.query.songs.findMany({
with: { album: true },
Expand Down
1 change: 1 addition & 0 deletions main/helpers/db/createDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const initDatabase = async () => {
name TEXT,
profilePicture TEXT,
musicFolder TEXT
language TEXT DEFAULT 'en'
);
CREATE TABLE IF NOT EXISTS albums (
id INTEGER PRIMARY KEY,
Expand Down
1 change: 1 addition & 0 deletions main/helpers/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const settings = sqliteTable("settings", {
lastFmSessionKey: text("lastFmSessionKey"),
enableLastFm: integer("enableLastFm", { mode: "boolean" }).default(false),
scrobbleThreshold: integer("scrobbleThreshold").default(50),
language: text("language").default("en"),
});

export const albums = sqliteTable("albums", {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@
"embla-carousel-react": "^8.6.0",
"eslint-config-next": "^15.3.4",
"howler": "^2.2.4",
"i18next": "^25.5.2",
"i18next-http-backend": "^3.0.2",
"last-fm": "^5.3.0",
"music-metadata": "^7.14.0",
"next-themes": "^0.4.6",
"node-fetch": "2",
"react-hook-form": "^7.58.1",
"react-i18next": "^15.7.3",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"seamless-scroll-polyfill": "^2.3.4",
Expand Down
Binary file modified renderer/.DS_Store
Binary file not shown.
127 changes: 103 additions & 24 deletions renderer/components/main/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IconSun,
IconVinyl,
IconUser,
IconLanguage,
} from "@tabler/icons-react";
import {
Tooltip,
Expand All @@ -32,10 +33,12 @@ import { useRouter } from "next/router";
import { usePlayer } from "@/context/playerContext";
import Spinner from "@/components/ui/spinner";
import { useTheme } from "next-themes";
import { useTranslation } from "react-i18next";

type Settings = {
name: string;
profilePicture: string;
language?: string;
};

type NavLink = {
Expand All @@ -44,6 +47,11 @@ type NavLink = {
label: string;
};

const supportedLanguages = [
{ code: "en", label: "English", flag: "🇬🇧" },
{ code: "es", label: "Español", flag: "🇪🇸" },
];

const Navbar = () => {
const router = useRouter();
const [open, setOpen] = useState(false);
Expand All @@ -54,6 +62,12 @@ const Navbar = () => {
const { setQueueAndPlay } = usePlayer();
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
const { t, i18n } = useTranslation();

// --- Language states ---
const [language, setLanguage] = useState<string>("en");
const [openLanguageDialog, setOpenLanguageDialog] = useState(false);
const [langSearch, setLangSearch] = useState("");

useEffect(() => {
setMounted(true);
Expand All @@ -63,22 +77,22 @@ const Navbar = () => {
{
href: "/home",
icon: <IconInbox stroke={2} className="w-5" />,
label: "Home",
label: t("navbar.links.home"),
},
{
href: "/playlists",
icon: <IconVinyl stroke={2} size={20} />,
label: "Playlists",
label: t("navbar.links.playlists"),
},
{
href: "/songs",
icon: <IconList stroke={2} size={20} />,
label: "Songs",
label: t("navbar.links.songs"),
},
{
href: "/albums",
icon: <IconFocusCentered stroke={2} size={20} />,
label: "Albums",
label: t("navbar.links.albums"),
},
];

Expand Down Expand Up @@ -106,7 +120,6 @@ const Navbar = () => {
if (href === "/home" && router.pathname === "/") {
return true;
}

return (
router.pathname === href ||
(href !== "/home" && router.pathname.startsWith(href))
Expand Down Expand Up @@ -139,14 +152,34 @@ const Navbar = () => {
[router],
);

// ---- Language Setting Load ----
useEffect(() => {
window.ipc.invoke("getSettings").then((response) => {
setSettings(response);
if (response?.language && supportedLanguages.find(l => l.code === response.language)) {
setLanguage(response.language);
i18n.changeLanguage(response.language);
}
});
window.ipc.on("confirmSettingsUpdate", () => {
window.ipc.invoke("getSettings").then((response) => {
setSettings(response);
});
});
// Sync on manual change (rarely needed, for IPC listeners)
window.ipc.on("languageChanged", (_event, lang) => {
setLanguage(lang as string);
i18n.changeLanguage(lang as string);
});
}, [i18n]);

useEffect(() => {
const down = (e) => {
if (e.key === "f" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((open) => !open);
}
};

document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down);
}, []);
Expand Down Expand Up @@ -199,17 +232,12 @@ const Navbar = () => {
setOpen(false);
};

useEffect(() => {
window.ipc.invoke("getSettings").then((response) => {
setSettings(response);
});

window.ipc.on("confirmSettingsUpdate", () => {
window.ipc.invoke("getSettings").then((response) => {
setSettings(response);
});
});
}, []);
const handleLanguageChange = (lang: string) => {
setLanguage(lang);
i18n.changeLanguage(lang);
window.ipc.invoke("updateSettings", { language: lang });
window.ipc.send("languageChanged", lang);
};

return (
<>
Expand All @@ -226,7 +254,7 @@ const Navbar = () => {
</Link>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={25}>
<p>{settings && settings.name ? settings.name : "Wora User"}</p>
<p>{settings && settings.name ? settings.name : t("settings.default_name")}</p>
</TooltipContent>
</Tooltip>
<div className="wora-border flex w-18 flex-col items-center gap-10 rounded-2xl p-8">
Expand Down Expand Up @@ -259,7 +287,19 @@ const Navbar = () => {
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={50}>
<p>Search</p>
<p>{t("navbar.search.open")}</p>
</TooltipContent>
</Tooltip>

{/* --- Idioma: Botón y Dialog --- */}
<Tooltip delayDuration={0}>
<TooltipTrigger>
<Button variant="ghost" onClick={() => setOpenLanguageDialog(true)}>
<IconLanguage stroke={2} className="w-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={25}>
<p>{t("navbar.language")}</p>
</TooltipContent>
</Tooltip>
</div>
Expand All @@ -270,16 +310,55 @@ const Navbar = () => {
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={25}>
<p className="capitalize">Theme: {theme}</p>
<p className="capitalize">{t("navbar.theme")}: {theme}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>

{/* --- Language CommandDialog --- */}
<CommandDialog open={openLanguageDialog} onOpenChange={setOpenLanguageDialog}>
<Command>
<CommandInput
placeholder={t("navbar.language_search") || "Search language..."}
value={langSearch}
onValueChange={setLangSearch}
/>
<CommandList>
<CommandGroup heading={t("navbar.available_languages") || "Available Languages"}>
{supportedLanguages
.filter(lang =>
lang.label.toLowerCase().includes(langSearch.toLowerCase())
)
.map((lang) => (
<CommandItem
key={lang.code}
value={lang.label.toLowerCase()}
onSelect={() => {
handleLanguageChange(lang.code);
setOpenLanguageDialog(false);
setLangSearch("");
}}
className={language === lang.code ? "font-bold" : ""}
>
<span className="mr-2 text-lg">{lang.flag}</span>
{lang.label}
{language === lang.code && (
<span className="ml-auto text-xs px-2 py-0.5 rounded bg-black/10 dark:bg-white/10 opacity-70">
{t("navbar.selected") || "Selected"}
</span>
)}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</CommandDialog>

<CommandDialog open={open} onOpenChange={setOpen}>
<Command>
<CommandInput
placeholder="Search for a song, album or playlist..."
placeholder={t("navbar.search.placeholder")}
value={search}
onValueChange={setSearch}
/>
Expand All @@ -290,7 +369,7 @@ const Navbar = () => {
</div>
)}
{search && !loading ? (
<CommandGroup heading="Search Results" className="pb-2">
<CommandGroup heading={t("navbar.search.results")} className="pb-2">
{searchResults.map((item) => (
<CommandItem
key={`${item.type}-${item.id || item.name}`}
Expand All @@ -317,13 +396,13 @@ const Navbar = () => {
<div>
<p className="w-full overflow-hidden text-xs text-nowrap">
{item.name}
<span className="ml-1 opacity-50">({item.type})</span>
<span className="ml-1 opacity-50">({t(`navbar.search.types.${item.type.toLowerCase()}`)})</span>
</p>
<p className="w-full text-xs opacity-50">
{item.type === "Playlist"
? item.description
: item.type === "Artist"
? "Artist"
? t("navbar.search.types.artist")
: item.artist}
</p>
</div>
Expand Down
Loading