anix/refactor: navbar

This commit is contained in:
Kentai Radiquum 2025-08-25 04:35:32 +05:00
parent 48345244f3
commit 6b84a312f7
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
11 changed files with 332 additions and 374 deletions

View file

@ -0,0 +1,168 @@
"use client";
import {
Avatar,
Dropdown,
DropdownDivider,
DropdownItem,
} from "flowbite-react";
import { useUserStore } from "#/store/auth";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { usePreferencesStore } from "#/store/preferences";
const NavbarItems = [
{
title: "Домашняя",
icon: "mdi--home",
href: "/",
auth: false,
},
{
title: "Поиск",
icon: "mdi--search",
href: "/search",
auth: false,
},
{
title: "Закладки",
icon: "mdi--bookmark-multiple",
href: "/bookmarks",
auth: true,
},
];
const FifthButton = {
favorites: {
title: "Избранное",
icon: "mdi--favorite",
href: "/favorites",
auth: true,
},
collections: {
title: "Коллекции",
icon: "mdi--collections-bookmark",
href: "/collections",
auth: true,
},
history: {
title: "История",
icon: "mdi--history",
href: "/history",
auth: true,
},
discovery: {
title: "Обзор",
icon: "mdi--compass",
href: "/discovery",
auth: true,
},
};
export const NavBarMobile = (props: { setIsSettingModalOpen: any }) => {
const userStore = useUserStore();
const router = useRouter();
const pathname = usePathname();
const preferenceStore = usePreferencesStore();
return (
<>
<footer className="fixed bottom-0 left-0 right-0 z-50 block w-full h-[70px] font-medium bg-black rounded-t-lg lg:hidden">
<div className="flex items-center justify-center h-full gap-4">
{NavbarItems.map((item) => {
if (item.auth && !userStore.isAuth) return <></>;
return (
<Link
href={item.href}
key={`navbar-mobile-${item.title}`}
className="flex flex-col items-center justify-center gap-1"
>
<span className={`iconify ${item.icon} w-6 h-6`}></span>
<span className="text-sm">{item.title}</span>
</Link>
);
})}
{userStore.isAuth && preferenceStore.flags.showFifthButton ?
<Link
href={FifthButton[preferenceStore.flags.showFifthButton].href}
key={`navbar-mobile-${FifthButton[preferenceStore.flags.showFifthButton].title}`}
className="flex flex-col items-center justify-center gap-1"
>
<span
className={`iconify ${FifthButton[preferenceStore.flags.showFifthButton].icon} w-6 h-6`}
></span>
<span className="text-sm">
{FifthButton[preferenceStore.flags.showFifthButton].title}
</span>
</Link>
: ""}
<Dropdown
arrowIcon={false}
inline
label={
<div className="flex flex-col items-center justify-center gap-1">
<Avatar
alt=""
img={
userStore.isAuth ?
userStore.user.avatar
: "https://s.anixmirai.com/avatars/no_avatar.jpg"
}
rounded
size="xs"
/>
<p className="text-sm">
{userStore.isAuth ? userStore.user.login : "Аноним"}
</p>
</div>
}
>
{userStore.isAuth && (
<>
<DropdownItem
onClick={() => router.push(`/profile/${userStore.user.id}`)}
>
<span className="w-4 h-4 iconify mdi--user"></span>
<span className="ml-2">Профиль</span>
</DropdownItem>
{Object.entries(FifthButton).map(([key, item]) => {
if (item.auth && !userStore.isAuth) return <></>;
if (preferenceStore.flags.showFifthButton === key)
return <></>;
return (
<DropdownItem
key={`navbar-mobile-${item.title}`}
onClick={() => router.push(item.href)}
>
<span className={`w-4 h-4 iconify ${item.icon}`}></span>
<span className="ml-2">{item.title}</span>
</DropdownItem>
);
})}
<DropdownDivider />
</>
)}
<DropdownItem
onClick={() => props.setIsSettingModalOpen(true)}
className="relative flex"
>
<span className="w-4 h-4 iconify mdi--settings"></span>
<span className="ml-2">Настройки</span>
</DropdownItem>
{userStore.isAuth ?
<DropdownItem onClick={() => userStore.logout()}>
<span className="w-4 h-4 iconify mdi--logout"></span>
<span className="ml-2">Выйти</span>
</DropdownItem>
: <DropdownItem
onClick={() => router.push(`/login?redirect=${pathname}`)}
>
<span className="w-4 h-4 iconify mdi--login"></span>
<span className="ml-2">Войти</span>
</DropdownItem>
}
</Dropdown>
</div>
</footer>
</>
);
};

View file

@ -0,0 +1,139 @@
"use client";
import {
Avatar,
Dropdown,
DropdownDivider,
DropdownItem,
} from "flowbite-react";
import { useUserStore } from "#/store/auth";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
const NavbarItems = [
{
title: "Домашняя",
icon: "mdi--home",
href: "/",
auth: false,
},
{
title: "Поиск",
icon: "mdi--search",
href: "/search",
auth: false,
},
{
title: "Закладки",
icon: "mdi--bookmark-multiple",
href: "/bookmarks",
auth: true,
},
{
title: "Избранное",
icon: "mdi--favorite",
href: "/favorites",
auth: true,
},
{
title: "Коллекции",
icon: "mdi--collections-bookmark",
href: "/collections",
auth: true,
},
{
title: "История",
icon: "mdi--history",
href: "/history",
auth: true,
},
{
title: "Обзор",
icon: "mdi--compass",
href: "/discovery",
auth: true,
},
];
export const NavBarPc = (props: { setIsSettingModalOpen: any }) => {
const userStore = useUserStore();
const router = useRouter();
const pathname = usePathname();
return (
<>
<header className="sticky top-0 left-0 right-0 z-50 hidden w-full h-16 font-medium bg-black rounded-b-lg lg:block">
<div className="container flex items-center justify-between h-full px-2 mx-auto">
<div className="flex items-center h-full gap-3">
{NavbarItems.map((item) => {
if (item.auth && !userStore.isAuth) return <></>;
return (
<Link
href={item.href}
key={`navbar-pc-${item.title}`}
className="flex items-center"
>
<span className={`iconify ${item.icon} w-6 h-6`}></span>
<span className="ml-1">{item.title}</span>
</Link>
);
})}
</div>
<Dropdown
arrowIcon={false}
inline
label={
<div className="flex items-center">
<Avatar
alt=""
img={
userStore.isAuth ?
userStore.user.avatar
: "https://s.anixmirai.com/avatars/no_avatar.jpg"
}
rounded
size="xs"
/>
<p className="ml-2">
{userStore.isAuth ? userStore.user.login : "Аноним"}
</p>
</div>
}
>
{userStore.isAuth ?
<DropdownItem
onClick={() => router.push(`/profile/${userStore.user.id}`)}
className="relative flex"
>
<span className="w-4 h-4 iconify mdi--user"></span>
<span className="ml-2">Профиль</span>
</DropdownItem>
: ""}
<DropdownItem
onClick={() => props.setIsSettingModalOpen(true)}
className="relative flex"
>
<span className="w-4 h-4 iconify mdi--settings"></span>
<span className="ml-2">Настройки</span>
</DropdownItem>
{userStore.isAuth ?
<DropdownItem
onClick={() => userStore.logout()}
className="relative flex"
>
<span className="w-4 h-4 iconify mdi--logout"></span>
<span className="ml-2">Выйти</span>
</DropdownItem>
: <DropdownItem
onClick={() => router.push(`/login?redirect=${pathname}`)}
className="relative flex"
>
<span className="w-4 h-4 iconify mdi--login"></span>
<span className="ml-2">Войти</span>
</DropdownItem>
}
</Dropdown>
</div>
</header>
</>
);
};

View file

@ -1,215 +0,0 @@
"use client";
import Link from "next/link";
import Image from "next/image";
import { useUserStore } from "#/store/auth";
import { usePathname } from "next/navigation";
import { useState } from "react";
import { SettingsModal } from "#/components/SettingsModal/SettingsModal";
import { usePreferencesStore } from "#/store/preferences";
export const Navbar = () => {
const pathname = usePathname();
const userStore = useUserStore();
const [isSettingModalOpen, setIsSettingModalOpen] = useState(false);
const preferenceStore = usePreferencesStore();
const menuItems = [
{
id: 1,
title: "Домашняя",
href: "/",
hrefInCategory: "/home",
icon: {
default: "material-symbols--home-outline",
active: "material-symbols--home",
},
isAuthRequired: false,
isShownOnMobile: true,
},
{
id: 2,
title: "Поиск",
href: "/search",
icon: {
default: "material-symbols--search",
active: "material-symbols--search",
},
isAuthRequired: false,
isShownOnMobile: true,
},
{
id: 3,
title: "Закладки",
href: "/bookmarks",
icon: {
default: "material-symbols--bookmarks-outline",
active: "material-symbols--bookmarks",
},
isAuthRequired: true,
isShownOnMobile: true,
},
{
id: 4,
title: "Избранное",
href: "/favorites",
icon: {
default: "material-symbols--favorite-outline",
active: "material-symbols--favorite",
},
isAuthRequired: true,
isShownOnMobile: false,
},
{
id: 5,
title: "Коллекции",
href: "/collections",
icon: {
default: "material-symbols--collections-bookmark-outline",
active: "material-symbols--collections-bookmark",
},
isAuthRequired: true,
isShownOnMobile: false,
},
{
id: 6,
title: "История",
href: "/history",
icon: {
default: "material-symbols--history",
active: "material-symbols--history",
},
isAuthRequired: true,
isShownOnMobile: false,
},
];
return (
<>
<header className="fixed bottom-0 left-0 z-50 w-full text-white bg-black rounded-t-lg sm:sticky sm:top-0 sm:rounded-t-none sm:rounded-b-lg">
<div className={`container flex items-center min-h-[76px] justify-center ${preferenceStore.flags.showFifthButton && preferenceStore.flags.showNavbarTitles == "always" ? "gap-0" : "gap-4"} mx-auto sm:gap-0 sm:justify-between`}>
<div className={`flex items-center ${preferenceStore.flags.showFifthButton && preferenceStore.flags.showNavbarTitles == "always" ? "gap-4" : "gap-8"} px-2 py-4 sm:gap-4`}>
{menuItems.map((item) => {
return (
<Link
href={item.href}
key={`navbar__${item.id}`}
className={`flex-col items-center justify-center gap-1 lg:flex-row ${
item.isAuthRequired && !userStore.isAuth ? "hidden"
: item.isShownOnMobile ? "flex"
: "hidden sm:flex"
} ${[item.href, item.hrefInCategory].includes("/" + pathname.split("/")[1]) ? "font-bold" : "font-medium"} transition-all`}
>
<span
className={`w-6 h-6 iconify ${[item.href, item.hrefInCategory].includes("/" + pathname.split("/")[1]) ? item.icon.active : item.icon.default}`}
></span>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" || preferenceStore.flags.showNavbarTitles == "links" || (preferenceStore.flags.showNavbarTitles == "selected" && [item.href, item.hrefInCategory].includes("/" + pathname.split("/")[1])) ? "block" : "hidden"}`}
>
{item.title}
</span>
</Link>
);
})}
</div>
<div className={`flex items-center ${preferenceStore.flags.showFifthButton && preferenceStore.flags.showNavbarTitles == "always" ? "gap-4" : "gap-8"} px-2 py-4 sm:gap-4`}>
{!userStore.isAuth ?
<Link
href={
pathname != "/login" ? `/login?redirect=${pathname}` : "#"
}
className={`flex items-center flex-col lg:flex-row gap-1 ${pathname == "/login" ? "font-bold" : "font-medium"} transition-all`}
>
<span className="w-6 h-6 iconify material-symbols--login"></span>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" || (preferenceStore.flags.showNavbarTitles == "selected" && pathname == "/login") ? "block" : "hidden"}`}
>
Войти
</span>
</Link>
: <>
<Link
href={`/profile/${userStore.user.id}`}
className={`hidden lg:flex flex-col lg:flex-row items-center gap-1 ${pathname == `/profile/${userStore.user.id}` ? "font-bold" : "font-medium"} transition-all`}
>
<Image
src={userStore.user.avatar}
alt=""
className="w-6 h-6 rounded-full"
width={24}
height={24}
/>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" || preferenceStore.flags.showNavbarTitles == "links" || (preferenceStore.flags.showNavbarTitles == "selected" && pathname == `/profile/${userStore.user.id}`) ? "block" : "hidden"}`}
>
{userStore.user.login}
</span>
</Link>
{preferenceStore.flags.showFifthButton ?
<Link
href={menuItems[preferenceStore.flags.showFifthButton].href}
className={`flex flex-col sm:hidden items-center gap-1 ${pathname == menuItems[preferenceStore.flags.showFifthButton].href ? "font-bold" : "font-medium"} transition-all`}
>
<span
className={`w-6 h-6 iconify ${pathname == menuItems[preferenceStore.flags.showFifthButton].href ? menuItems[preferenceStore.flags.showFifthButton].icon.active : menuItems[preferenceStore.flags.showFifthButton].icon.default}`}
></span>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" || preferenceStore.flags.showNavbarTitles == "links" || (preferenceStore.flags.showNavbarTitles == "selected" && pathname == menuItems[preferenceStore.flags.showFifthButton].href) ? "block" : "hidden"}`}
>
{menuItems[preferenceStore.flags.showFifthButton].title}
</span>
</Link>
: ""}
<Link
href={`/menu`}
className={`flex flex-col lg:hidden items-center gap-1 ${pathname == `/menu` || pathname == `/profile/${userStore.user.id}` ? "font-bold" : "font-medium"} transition-all`}
>
<Image
src={userStore.user.avatar}
alt=""
className="w-6 h-6 rounded-full"
width={24}
height={24}
/>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" || preferenceStore.flags.showNavbarTitles == "links" || (preferenceStore.flags.showNavbarTitles == "selected" && (pathname == `/menu` || pathname == `/profile/${userStore.user.id}`)) ? "block" : "hidden"}`}
>
{userStore.user.login}
</span>
</Link>
</>
}
<button
className={`${userStore.isAuth ? "hidden lg:flex" : "flex"} flex-col items-center gap-1 lg:flex-row`}
onClick={() => setIsSettingModalOpen(true)}
>
<span className="w-6 h-6 iconify material-symbols--settings-outline-rounded"></span>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" ? "block" : "hidden"}`}
>
Настройки
</span>
</button>
{userStore.isAuth && (
<button
className="flex-col items-center hidden gap-1 lg:flex-row lg:flex"
onClick={() => userStore.logout()}
>
<span className="w-6 h-6 iconify material-symbols--logout"></span>
<span
className={`text-xs sm:text-base ${preferenceStore.flags.showNavbarTitles == "always" ? "lg:hidden xl:block" : "hidden"}`}
>
Выйти
</span>
</button>
)}
</div>
</div>
</header>
<SettingsModal
isOpen={isSettingModalOpen}
setIsOpen={setIsSettingModalOpen}
/>
</>
);
};

View file

@ -15,7 +15,7 @@ export const RelatedSection = (props: any) => {
<div className="flex items-center justify-center p-4">
{props.images.map((item, index) => {
return (
<div key={`related-img-${index}`} className="w-[100px] lg:w-[300px] aspect-[9/12] even:scale-110 shadow-md even:[box-shadow:_0px_0px_16px_black;] even:z-30 origin-center first:[transform:translateX(25%)] last:[transform:translateX(-25%)] rounded-lg overflow-hidden">
<div key={`related-img-${index}`} className="w-[100px] lg:w-[300px] aspect-[9/12] even:scale-110 shadow-md even:[box-shadow:_0px_0px_16px_black;] even:z-20 origin-center first:[transform:translateX(25%)] last:[transform:translateX(-25%)] rounded-lg overflow-hidden">
<Image
fill={true}
src={item}

View file

@ -23,8 +23,8 @@ export const ReleaseSection = (props: {
chipsSettings={{
enabled: true,
lastWatchedHidden:
(props.sectionTitle &&
props.sectionTitle.toLowerCase() != "история")
props.sectionTitle &&
props.sectionTitle.toLowerCase() != "история",
}}
/>
</div>

View file

@ -43,9 +43,10 @@ const NavbarTitles = {
};
const FifthButton = {
3: "Избранное",
4: "Коллекции",
5: "История",
favorites: "Избранное",
collections: "Коллекции",
history: "История",
discovery: "Обзор",
};
export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
@ -56,7 +57,8 @@ export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
const [isPlayerConfigured, setIsPlayerConfigured] = useState(false);
useEffect(() => {
const NEXT_PUBLIC_PLAYER_PARSER_URL = env("NEXT_PUBLIC_PLAYER_PARSER_URL") || null;
const NEXT_PUBLIC_PLAYER_PARSER_URL =
env("NEXT_PUBLIC_PLAYER_PARSER_URL") || null;
if (NEXT_PUBLIC_PLAYER_PARSER_URL) {
setIsPlayerConfigured(true);
}
@ -204,7 +206,7 @@ export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
</Dropdown>
</div>
{userStore.isAuth ?
<div className="flex items-center justify-between sm:hidden">
<div className="flex items-center justify-between lg:hidden">
<p className=" dark:text-white max-w-96">
Пятый пункт в навигации
</p>
@ -229,11 +231,7 @@ export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
return (
<DropdownItem
key={`navbar-fifthbutton-${key}`}
onClick={() =>
preferenceStore.setFlags({
showFifthButton: Number(key) as 3 | 4 | 5,
})
}
onClick={() => preferenceStore.setFlags({showFifthButton: key})}
>
{FifthButton[key]}
</DropdownItem>