mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-19 12:02:06 +00:00
Add Featured Modal Removed Individual Carousels for each genre Recommended Carousel View More page for viewing all Improve several minor visuals Update search and navigation Full Commit Log: add more carousel skeleton dots bug fix and languages remove provider translations Add change button for recommended more content add buttons to moreContent page dropdown for changing recommended Increase genres and providers add home/search button to discover Update FeaturedCarousel.tsx fix recommended load more pages increase number of featured items clean up featured image fetch maybe fix ff bug? add dynamic blur to header Update Dropdown.tsx fix dropdown add recommended carousel animate dropdown fix some visuals random button fix padding reset timer when manually switching slides fix editor picks more titles add store for discover fix editor picks Update FeaturedCarousel.tsx add view more card move view more link update carousel buttons and dropdown finish 5 carousels use 5 carousels init carousel nav buttons update dropdown update featured sizing update blurs add clear blur to navigation update padding and sizing Update FeaturedCarousel.tsx add loading skeleton update discover navigation again simplify featured media Update SearchBar.tsx tweak some minor visual stuff fix button sizes update carousel gradient fix sticky fix safari overlay bug make search transparent use secondary buttons on featured fix up negative margins fix searching classes fix buttons because of the overlay make it shorter add featured section to home page add toggle for image logos fix details modal title overlay position clean up some buttons improve fed setup status check update grid Update FeaturedCarousel.tsx dont show more content for providers more stuff clean and bugfix update editor picks more content page Update DetailsModal.tsx more more more! shuffle editor picks discover update part 2 fix more info button init discover v3
186 lines
5.7 KiB
TypeScript
186 lines
5.7 KiB
TypeScript
import classNames from "classnames";
|
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto";
|
|
import { UserAvatar } from "@/components/Avatar";
|
|
import { Icon, Icons } from "@/components/Icon";
|
|
import { Transition } from "@/components/utils/Transition";
|
|
import { useAuth } from "@/hooks/auth/useAuth";
|
|
import { conf } from "@/setup/config";
|
|
import { useAuthStore } from "@/stores/auth";
|
|
|
|
function Divider() {
|
|
return <hr className="border-0 w-full h-px bg-dropdown-border" />;
|
|
}
|
|
|
|
function GoToLink(props: {
|
|
children: React.ReactNode;
|
|
href?: string;
|
|
className?: string;
|
|
onClick?: () => void;
|
|
}) {
|
|
const navigate = useNavigate();
|
|
|
|
const goTo = (href: string) => {
|
|
if (href.startsWith("http")) {
|
|
window.open(href, "_blank");
|
|
} else {
|
|
window.scrollTo(0, 0);
|
|
navigate(href);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<a
|
|
tabIndex={0}
|
|
href={props.href}
|
|
onClick={(evt) => {
|
|
evt.preventDefault();
|
|
if (props.href) goTo(props.href);
|
|
else props.onClick?.();
|
|
}}
|
|
className={props.className}
|
|
>
|
|
{props.children}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
function DropdownLink(props: {
|
|
children: React.ReactNode;
|
|
href?: string;
|
|
icon?: Icons;
|
|
highlight?: boolean;
|
|
className?: string;
|
|
onClick?: () => void;
|
|
}) {
|
|
return (
|
|
<GoToLink
|
|
onClick={props.onClick}
|
|
href={props.href}
|
|
className={classNames(
|
|
"tabbable cursor-pointer flex gap-3 items-center m-3 p-1 rounded font-medium transition-colors duration-100",
|
|
props.highlight
|
|
? "text-dropdown-highlight hover:text-dropdown-highlightHover"
|
|
: "text-dropdown-text hover:text-white",
|
|
props.className,
|
|
)}
|
|
>
|
|
{props.icon ? <Icon icon={props.icon} className="text-xl" /> : null}
|
|
{props.children}
|
|
</GoToLink>
|
|
);
|
|
}
|
|
|
|
function CircleDropdownLink(props: { icon: Icons; href: string }) {
|
|
return (
|
|
<GoToLink
|
|
href={props.href}
|
|
onClick={() => window.scrollTo(0, 0)}
|
|
className="tabbable w-11 h-11 rounded-full bg-dropdown-contentBackground text-dropdown-text hover:text-white transition-colors duration-100 flex justify-center items-center"
|
|
>
|
|
<Icon className="text-2xl" icon={props.icon} />
|
|
</GoToLink>
|
|
);
|
|
}
|
|
|
|
export function LinksDropdown(props: { children: React.ReactNode }) {
|
|
const { t } = useTranslation();
|
|
const [open, setOpen] = useState(false);
|
|
const deviceName = useAuthStore((s) => s.account?.deviceName);
|
|
const seed = useAuthStore((s) => s.account?.seed);
|
|
const bufferSeed = useMemo(
|
|
() => (seed ? base64ToBuffer(seed) : null),
|
|
[seed],
|
|
);
|
|
const { logout } = useAuth();
|
|
|
|
useEffect(() => {
|
|
function onWindowClick(evt: MouseEvent) {
|
|
if ((evt.target as HTMLElement).closest(".is-dropdown")) return;
|
|
setOpen(false);
|
|
}
|
|
|
|
window.addEventListener("click", onWindowClick);
|
|
return () => window.removeEventListener("click", onWindowClick);
|
|
}, []);
|
|
|
|
const toggleOpen = useCallback(() => {
|
|
setOpen((s) => !s);
|
|
}, []);
|
|
|
|
return (
|
|
<div className="relative is-dropdown">
|
|
<div
|
|
className={classNames(
|
|
"cursor-pointer tabbable rounded-full flex gap-2 text-white items-center py-2 px-3 bg-pill-background hover:bg-pill-backgroundHover backdrop-blur-lg transition-all duration-100 hover:scale-105",
|
|
open ? "bg-opacity-100" : "bg-opacity-50",
|
|
)}
|
|
tabIndex={0}
|
|
onClick={toggleOpen}
|
|
onKeyUp={(evt) => evt.key === "Enter" && toggleOpen()}
|
|
>
|
|
{props.children}
|
|
<Icon
|
|
className={classNames(
|
|
"text-xl transition-transform duration-100",
|
|
open ? "rotate-180" : "",
|
|
)}
|
|
icon={Icons.CHEVRON_DOWN}
|
|
/>
|
|
</div>
|
|
<Transition animation="slide-down" show={open}>
|
|
<div className="rounded-lg absolute w-64 bg-dropdown-altBackground top-full mt-3 right-0">
|
|
{deviceName && bufferSeed ? (
|
|
<DropdownLink className="text-white" href="/settings">
|
|
<UserAvatar />
|
|
{decryptData(deviceName, bufferSeed)}
|
|
</DropdownLink>
|
|
) : (
|
|
<DropdownLink href="/login" icon={Icons.RISING_STAR} highlight>
|
|
{t("navigation.menu.register")}
|
|
</DropdownLink>
|
|
)}
|
|
<Divider />
|
|
<DropdownLink href="/settings" icon={Icons.SETTINGS}>
|
|
{t("navigation.menu.settings")}
|
|
</DropdownLink>
|
|
{process.env.NODE_ENV === "development" ? (
|
|
<DropdownLink href="/dev" icon={Icons.COMPRESS}>
|
|
{t("navigation.menu.development")}
|
|
</DropdownLink>
|
|
) : null}
|
|
<DropdownLink href="/about" icon={Icons.CIRCLE_QUESTION}>
|
|
{t("navigation.menu.about")}
|
|
</DropdownLink>
|
|
<DropdownLink href="/discover" icon={Icons.RISING_STAR}>
|
|
{t("navigation.menu.discover")}
|
|
</DropdownLink>
|
|
{deviceName ? (
|
|
<DropdownLink
|
|
className="!text-type-danger opacity-75 hover:opacity-100"
|
|
icon={Icons.LOGOUT}
|
|
onClick={logout}
|
|
>
|
|
{t("navigation.menu.logout")}
|
|
</DropdownLink>
|
|
) : null}
|
|
<Divider />
|
|
<div className="my-4 flex justify-center items-center gap-4">
|
|
<CircleDropdownLink
|
|
href={conf().DISCORD_LINK}
|
|
icon={Icons.DISCORD}
|
|
/>
|
|
<CircleDropdownLink href="/support" icon={Icons.SUPPORT} />
|
|
<CircleDropdownLink
|
|
href="https://rentry.co/h5mypdfs"
|
|
icon={Icons.TIP_JAR}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
);
|
|
}
|