improvements
This commit is contained in:
+65
-41
@@ -42,6 +42,7 @@ type Props = {
|
||||
standalone?: boolean;
|
||||
mode?: "view" | "edit";
|
||||
setMode?: Function;
|
||||
onUpdateArchive?: Function;
|
||||
};
|
||||
|
||||
export default function LinkDetails({
|
||||
@@ -50,6 +51,7 @@ export default function LinkDetails({
|
||||
standalone,
|
||||
mode = "view",
|
||||
setMode,
|
||||
onUpdateArchive,
|
||||
}: Props) {
|
||||
const [link, setLink] =
|
||||
useState<LinkIncludingShortenedCollectionAndTags>(activeLink);
|
||||
@@ -236,46 +238,50 @@ export default function LinkDetails({
|
||||
<div className="duration-100 h-40 skeleton rounded-none"></div>
|
||||
)}
|
||||
|
||||
{!standalone && (permissions === true || permissions?.canUpdate) && (
|
||||
<div className="absolute top-0 bottom-0 left-0 right-0 opacity-0 group-hover:opacity-100 duration-100 flex justify-end items-end">
|
||||
<label className="btn btn-xs mb-2 mr-3 opacity-50 hover:opacity-100">
|
||||
{t("upload_preview_image")}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/jpg, image/jpeg, image/png"
|
||||
onChange={async (e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
{!standalone &&
|
||||
(permissions === true || permissions?.canUpdate) &&
|
||||
!isPublicRoute && (
|
||||
<div className="absolute top-0 bottom-0 left-0 right-0 opacity-0 group-hover:opacity-100 duration-100 flex justify-end items-end">
|
||||
<label className="btn btn-xs mb-2 mr-3 opacity-50 hover:opacity-100">
|
||||
{t("upload_preview_image")}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/jpg, image/jpeg, image/png"
|
||||
onChange={async (e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const load = toast.loading(t("updating"));
|
||||
const load = toast.loading(t("updating"));
|
||||
|
||||
await updatePreview.mutateAsync(
|
||||
{
|
||||
linkId: link.id as number,
|
||||
file,
|
||||
},
|
||||
{
|
||||
onSettled: (data, error) => {
|
||||
toast.dismiss(load);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
} else {
|
||||
toast.success(t("updated"));
|
||||
setLink({ updatedAt: data.updatedAt, ...link });
|
||||
}
|
||||
await updatePreview.mutateAsync(
|
||||
{
|
||||
linkId: link.id as number,
|
||||
file,
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
className="hidden"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
onSettled: (data, error) => {
|
||||
toast.dismiss(load);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
} else {
|
||||
toast.success(t("updated"));
|
||||
setLink({ updatedAt: data.updatedAt, ...link });
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
className="hidden"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!standalone && (permissions === true || permissions?.canUpdate) ? (
|
||||
{!standalone &&
|
||||
(permissions === true || permissions?.canUpdate) &&
|
||||
!isPublicRoute ? (
|
||||
<div className="-mt-14 ml-8 relative w-fit pb-2">
|
||||
<div className="tooltip tooltip-bottom" data-tip={t("change_icon")}>
|
||||
<LinkIcon
|
||||
@@ -505,12 +511,30 @@ export default function LinkDetails({
|
||||
<div>
|
||||
<br />
|
||||
|
||||
<p
|
||||
className="text-sm mb-2 text-neutral"
|
||||
title={t("available_formats")}
|
||||
>
|
||||
{link.url ? t("preserved_formats") : t("file")}
|
||||
</p>
|
||||
<div className="flex gap-1 items-center mb-2">
|
||||
<p
|
||||
className="text-sm text-neutral"
|
||||
title={t("available_formats")}
|
||||
>
|
||||
{link.url ? t("preserved_formats") : t("file")}
|
||||
</p>
|
||||
|
||||
{onUpdateArchive &&
|
||||
(permissions === true || permissions?.canUpdate) &&
|
||||
!isPublicRoute && (
|
||||
<div
|
||||
className="tooltip tooltip-bottom"
|
||||
data-tip={t("refresh_preserved_formats")}
|
||||
>
|
||||
<button
|
||||
className="btn btn-xs btn-ghost btn-square text-neutral"
|
||||
onClick={() => onUpdateArchive()}
|
||||
>
|
||||
<i className="bi-arrow-clockwise text-sm" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={`flex flex-col rounded-md p-3 bg-base-200`}>
|
||||
{monolithAvailable(link) ? (
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function LinkActions({ link, btnStyle }: Props) {
|
||||
onClick={() => setLinkModal(true)}
|
||||
>
|
||||
<div className={clsx("btn btn-sm btn-square text-neutral", btnStyle)}>
|
||||
<i title="More" className="bi-three-dots text-xl" />
|
||||
<i title="More" className="bi-info-circle text-xl" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -127,22 +127,6 @@ export default function LinkActions({ link, btnStyle }: Props) {
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{link.type === "url" &&
|
||||
(permissions === true || permissions?.canUpdate) && (
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
updateArchive();
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{t("refresh_preserved_formats")}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{(permissions === true || permissions?.canDelete) && (
|
||||
<li>
|
||||
<div
|
||||
|
||||
@@ -146,6 +146,8 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
||||
editMode &&
|
||||
(permissions === true || permissions?.canCreate || permissions?.canDelete);
|
||||
|
||||
const isPublicRoute = router.pathname.startsWith("/public") ? true : false;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@@ -233,7 +235,7 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
||||
{/* Overlay on hover */}
|
||||
<div className="absolute pointer-events-none top-0 left-0 right-0 bottom-0 bg-base-100 bg-opacity-0 group-hover:bg-opacity-20 group-focus-within:opacity-20 rounded-2xl duration-100"></div>
|
||||
<LinkActions link={link} collection={collection} />
|
||||
<LinkPin link={link} />
|
||||
{!isPublicRoute && <LinkPin link={link} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function LinkCollection({
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
collection: CollectionIncludingMembersAndLinkCount;
|
||||
}) {
|
||||
return (
|
||||
return collection?.name ? (
|
||||
<>
|
||||
<Link
|
||||
href={`/collections/${link.collection.id}`}
|
||||
@@ -40,5 +40,7 @@ export default function LinkCollection({
|
||||
<p className="truncate capitalize">{collection?.name}</p>
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useUser } from "@/hooks/store/user";
|
||||
import { useLinks } from "@/hooks/store/links";
|
||||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
import LinkPin from "./LinkPin";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
@@ -91,6 +92,9 @@ export default function LinkCardCompact({ link, editMode }: Props) {
|
||||
editMode &&
|
||||
(permissions === true || permissions?.canCreate || permissions?.canDelete);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -135,7 +139,7 @@ export default function LinkCardCompact({ link, editMode }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LinkPin link={link} btnStyle="btn-ghost" />
|
||||
{!isPublic && <LinkPin link={link} btnStyle="btn-ghost" />}
|
||||
<LinkActions link={link} collection={collection} btnStyle="btn-ghost" />
|
||||
</div>
|
||||
<div className="last:hidden rounded-none my-0 mx-1 border-t border-base-300 h-[1px]"></div>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useGetLink, useLinks } from "@/hooks/store/links";
|
||||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
import clsx from "clsx";
|
||||
import LinkPin from "./LinkPin";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
@@ -108,6 +109,10 @@ export default function LinkMasonry({ link, editMode, columns }: Props) {
|
||||
const isVisible = useOnScreen(ref);
|
||||
const permissions = usePermissions(collection?.id as number);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
let isPublic = router.pathname.startsWith("/public") ? true : undefined;
|
||||
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout | null = null;
|
||||
|
||||
@@ -241,7 +246,7 @@ export default function LinkMasonry({ link, editMode, columns }: Props) {
|
||||
{/* Overlay on hover */}
|
||||
<div className="absolute pointer-events-none top-0 left-0 right-0 bottom-0 bg-base-100 bg-opacity-0 group-hover:bg-opacity-20 group-focus-within:opacity-20 rounded-2xl duration-100"></div>
|
||||
<LinkActions link={link} collection={collection} />
|
||||
<LinkPin link={link} />
|
||||
{!isPublic && <LinkPin link={link} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function LinkModal({
|
||||
onClick={() => onClose()}
|
||||
></div>
|
||||
|
||||
{(permissions === true || permissions?.canUpdate) && (
|
||||
{(permissions === true || permissions?.canUpdate) && !isPublicRoute && (
|
||||
<div className="flex gap-1 h-8 rounded-full bg-neutral-content bg-opacity-50 text-base-content p-1 text-xs duration-100 select-none z-10">
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -79,87 +79,89 @@ export default function LinkModal({
|
||||
)}
|
||||
|
||||
<div className="flex gap-2">
|
||||
<div className={`dropdown dropdown-end z-20`}>
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onMouseDown={dropdownTriggerer}
|
||||
className="btn btn-sm btn-circle text-base-content opacity-50 hover:opacity-100 z-10"
|
||||
>
|
||||
<i title="More" className="bi-three-dots text-xl" />
|
||||
</div>
|
||||
<ul
|
||||
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box`}
|
||||
>
|
||||
{
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
onPin();
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{link?.pinnedBy && link.pinnedBy[0]
|
||||
? t("unpin")
|
||||
: t("pin_to_dashboard")}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
{link.type === "url" &&
|
||||
(permissions === true || permissions?.canUpdate) && (
|
||||
{!isPublicRoute && (
|
||||
<div className={`dropdown dropdown-end z-20`}>
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onMouseDown={dropdownTriggerer}
|
||||
className="btn btn-sm btn-circle text-base-content opacity-50 hover:opacity-100 z-10"
|
||||
>
|
||||
<i title="More" className="bi-three-dots text-xl" />
|
||||
</div>
|
||||
<ul
|
||||
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box`}
|
||||
>
|
||||
{
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
onUpdateArchive();
|
||||
onPin();
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{t("refresh_preserved_formats")}
|
||||
{link?.pinnedBy && link.pinnedBy[0]
|
||||
? t("unpin")
|
||||
: t("pin_to_dashboard")}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
{link.type === "url" &&
|
||||
(permissions === true || permissions?.canUpdate) && (
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
onUpdateArchive();
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{t("refresh_preserved_formats")}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{(permissions === true || permissions?.canDelete) && (
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={async (e) => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
console.log(e.shiftKey);
|
||||
if (e.shiftKey) {
|
||||
const load = toast.loading(t("deleting"));
|
||||
|
||||
await deleteLink.mutateAsync(link.id as number, {
|
||||
onSettled: (data, error) => {
|
||||
toast.dismiss(load);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
} else {
|
||||
toast.success(t("deleted"));
|
||||
}
|
||||
},
|
||||
});
|
||||
onClose();
|
||||
} else {
|
||||
onDelete();
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{t("delete")}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{(permissions === true || permissions?.canDelete) && (
|
||||
<li>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={async (e) => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
console.log(e.shiftKey);
|
||||
if (e.shiftKey) {
|
||||
const load = toast.loading(t("deleting"));
|
||||
|
||||
await deleteLink.mutateAsync(link.id as number, {
|
||||
onSettled: (data, error) => {
|
||||
toast.dismiss(load);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
} else {
|
||||
toast.success(t("deleted"));
|
||||
}
|
||||
},
|
||||
});
|
||||
onClose();
|
||||
} else {
|
||||
onDelete();
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{t("delete")}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{link.url && (
|
||||
<Link
|
||||
href={link.url}
|
||||
@@ -176,6 +178,7 @@ export default function LinkModal({
|
||||
className="sm:mt-0 -mt-11"
|
||||
mode={mode}
|
||||
setMode={(mode: "view" | "edit") => setMode(mode)}
|
||||
onUpdateArchive={onUpdateArchive}
|
||||
/>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
Reference in New Issue
Block a user