mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-23 15:07:43 +01:00
170 lines
5.2 KiB
TypeScript
170 lines
5.2 KiB
TypeScript
import classNames from "classnames";
|
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useHistory } 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 history = useHistory();
|
|
|
|
const goTo = (href: string) => {
|
|
if (href.startsWith("http")) window.open(href, "_blank");
|
|
else history.push(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}
|
|
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="cursor-pointer tabbable rounded-full flex gap-2 text-white items-center py-2 px-3 bg-pill-background bg-opacity-50 hover:bg-pill-backgroundHover transition-[background,transform] duration-100 hover:scale-105"
|
|
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>
|
|
<DropdownLink href="/about" icon={Icons.EPISODES}>
|
|
{t("navigation.menu.about")}
|
|
</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={conf().GITHUB_LINK} icon={Icons.GITHUB} />
|
|
<CircleDropdownLink
|
|
href={conf().DONATION_LINK}
|
|
icon={Icons.COINS}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
);
|
|
}
|