mirror of
https://github.com/linkwarden/linkwarden.git
synced 2026-06-30 07:46:13 +00:00
190 lines
5.8 KiB
TypeScript
190 lines
5.8 KiB
TypeScript
import React, { useState } from "react";
|
|
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types/global";
|
|
import { useTranslation } from "next-i18next";
|
|
import { useDeleteLink } from "@linkwarden/router/links";
|
|
import Drawer from "../Drawer";
|
|
import LinkDetails from "../LinkDetails";
|
|
import Link from "next/link";
|
|
import usePermissions from "@/hooks/usePermissions";
|
|
import { useRouter } from "next/router";
|
|
import toast from "react-hot-toast";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuTrigger,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { Button } from "../ui/button";
|
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
|
type Props = {
|
|
onClose: Function;
|
|
onDelete: Function;
|
|
onUpdateArchive: Function;
|
|
onPin: Function;
|
|
link: LinkIncludingShortenedCollectionAndTags;
|
|
activeMode?: "view" | "edit";
|
|
};
|
|
|
|
export default function LinkModal({
|
|
onClose,
|
|
onDelete,
|
|
onUpdateArchive,
|
|
onPin,
|
|
link,
|
|
activeMode,
|
|
}: Props) {
|
|
const { t } = useTranslation();
|
|
|
|
const permissions = usePermissions(link.collection.id as number);
|
|
|
|
const router = useRouter();
|
|
|
|
const isPublicRoute = router.pathname.startsWith("/public") ? true : false;
|
|
|
|
const deleteLink = useDeleteLink({ toast, t });
|
|
|
|
const [mode, setMode] = useState<"view" | "edit">(activeMode || "view");
|
|
|
|
const handleDelete = async (e: React.MouseEvent) => {
|
|
setTimeout(() => (document.body.style.pointerEvents = ""), 0);
|
|
|
|
if (e.shiftKey && link.id) {
|
|
deleteLink.mutateAsync(link.id);
|
|
|
|
onClose();
|
|
} else {
|
|
onDelete();
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Drawer
|
|
toggleDrawer={onClose}
|
|
className="sm:h-screen items-center relative"
|
|
>
|
|
<div className="absolute top-3 left-0 right-0 flex justify-between px-3">
|
|
<Button
|
|
variant="simple"
|
|
size="icon"
|
|
className="bi-x text-xl rounded-full text-base-content opacity-50 hover:opacity-100 z-10"
|
|
onClick={() => onClose()}
|
|
></Button>
|
|
|
|
{(permissions === true || permissions?.canUpdate) && !isPublicRoute && (
|
|
<Tabs
|
|
value={(mode === "view" ? "view" : "edit").toString()}
|
|
className="w-fit absolute left-1/2 -translate-x-1/2 rounded-full bg-base-100/50 text-sm shadow-md z-10 opacity-90"
|
|
onValueChange={(index: any) =>
|
|
setMode(index === "view" ? "view" : "edit")
|
|
}
|
|
>
|
|
<TabsList className="rounded-full h-8">
|
|
<TabsTrigger value="view" className="rounded-full py-0">
|
|
View
|
|
</TabsTrigger>
|
|
<TabsTrigger value="edit" className="rounded-full py-0">
|
|
Edit
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
</Tabs>
|
|
)}
|
|
|
|
<div className="flex gap-2">
|
|
{!isPublicRoute && (
|
|
<DropdownMenu modal={true}>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
variant="simple"
|
|
size="icon"
|
|
className="rounded-full text-base-content opacity-50 hover:opacity-100 z-10"
|
|
title={t("more")}
|
|
>
|
|
<i title="More" className="bi-three-dots text-xl" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
|
|
<DropdownMenuContent
|
|
align="end"
|
|
className="bg-base-200 border border-neutral-content rounded-box p-1"
|
|
>
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
(document.activeElement as HTMLElement)?.blur();
|
|
onPin();
|
|
}}
|
|
>
|
|
<i className="bi-pin" />
|
|
{link.pinnedBy?.[0] ? t("unpin") : t("pin_to_dashboard")}
|
|
</DropdownMenuItem>
|
|
|
|
{link.type === "url" &&
|
|
(permissions === true || permissions?.canUpdate) && (
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
setTimeout(
|
|
() => (document.body.style.pointerEvents = ""),
|
|
0
|
|
);
|
|
|
|
onUpdateArchive();
|
|
onClose();
|
|
}}
|
|
>
|
|
<i className="bi-arrow-clockwise"></i>
|
|
{t("refresh_preserved_formats")}
|
|
</DropdownMenuItem>
|
|
)}
|
|
|
|
{(permissions === true || permissions?.canDelete) && (
|
|
<>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem
|
|
onClick={handleDelete}
|
|
className="text-error"
|
|
>
|
|
<i className="bi-trash"></i>
|
|
{t("delete")}
|
|
</DropdownMenuItem>
|
|
</>
|
|
)}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)}
|
|
{link.url && (
|
|
<Button
|
|
asChild
|
|
variant="simple"
|
|
size="icon"
|
|
className="rounded-full"
|
|
>
|
|
<Link
|
|
href={link.url}
|
|
target="_blank"
|
|
className="bi-box-arrow-up-right text-base-content opacity-50 hover:opacity-100 select-none z-10"
|
|
></Link>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="w-full">
|
|
<LinkDetails
|
|
activeLink={link}
|
|
className="sm:mt-0 -mt-11"
|
|
mode={mode}
|
|
setMode={(mode: "view" | "edit") => setMode(mode)}
|
|
onUpdateArchive={() => {
|
|
setTimeout(() => (document.body.style.pointerEvents = ""), 0);
|
|
|
|
onUpdateArchive();
|
|
onClose();
|
|
}}
|
|
/>
|
|
</div>
|
|
</Drawer>
|
|
);
|
|
}
|