update global (texte + logo)

This commit is contained in:
2024-04-18 17:19:24 +02:00
parent f9d05a2fd3
commit 1c73080fe3
307 changed files with 28214 additions and 105 deletions

View File

@ -0,0 +1,177 @@
import { useState } from "react";
import {
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import usePermissions from "@/hooks/usePermissions";
import EditLinkModal from "@/components/ModalContent/EditLinkModal";
import DeleteLinkModal from "@/components/ModalContent/DeleteLinkModal";
import PreservedFormatsModal from "@/components/ModalContent/PreservedFormatsModal";
import useLinkStore from "@/store/links";
import { toast } from "react-hot-toast";
import useAccountStore from "@/store/account";
import { dropdownTriggerer } from "@/lib/client/utils";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
collection: CollectionIncludingMembersAndLinkCount;
position?: string;
toggleShowInfo?: () => void;
linkInfo?: boolean;
flipDropdown?: boolean;
};
export default function LinkActions({
link,
toggleShowInfo,
position,
linkInfo,
flipDropdown,
}: Props) {
const permissions = usePermissions(link.collection.id as number);
const [editLinkModal, setEditLinkModal] = useState(false);
const [deleteLinkModal, setDeleteLinkModal] = useState(false);
const [preservedFormatsModal, setPreservedFormatsModal] = useState(false);
const { account } = useAccountStore();
const { removeLink, updateLink } = useLinkStore();
const pinLink = async () => {
const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0];
const load = toast.loading("Applying...");
const response = await updateLink({
...link,
pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }],
});
toast.dismiss(load);
response.ok &&
toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`);
};
const deleteLink = async () => {
const load = toast.loading("Deleting...");
const response = await removeLink(link.id as number);
toast.dismiss(load);
response.ok && toast.success(`Link Deleted.`);
};
return (
<>
<div
className={`dropdown dropdown-left dropdown-end absolute ${
position || "top-3 right-3"
} z-20`}
>
<div
tabIndex={0}
role="button"
onMouseDown={dropdownTriggerer}
className="btn btn-ghost btn-sm btn-square text-neutral"
>
<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 w-44 mr-1 translate-y-10">
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
pinLink();
}}
>
{link?.pinnedBy && link.pinnedBy[0]
? "Unpin"
: "Pin to Dashboard"}
</div>
</li>
{linkInfo !== undefined && toggleShowInfo ? (
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
toggleShowInfo();
}}
>
{!linkInfo ? "Show" : "Hide"} Link Details
</div>
</li>
) : undefined}
{permissions === true || permissions?.canUpdate ? (
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setEditLinkModal(true);
}}
>
Edit Link
</div>
</li>
) : undefined}
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setPreservedFormatsModal(true);
}}
>
Preserved Formats
</div>
</li>
{permissions === true || permissions?.canDelete ? (
<li>
<div
role="button"
tabIndex={0}
onClick={(e) => {
(document?.activeElement as HTMLElement)?.blur();
e.shiftKey ? deleteLink() : setDeleteLinkModal(true);
}}
>
Delete
</div>
</li>
) : undefined}
</ul>
</div>
{editLinkModal ? (
<EditLinkModal
onClose={() => setEditLinkModal(false)}
activeLink={link}
/>
) : undefined}
{deleteLinkModal ? (
<DeleteLinkModal
onClose={() => setDeleteLinkModal(false)}
activeLink={link}
/>
) : undefined}
{preservedFormatsModal ? (
<PreservedFormatsModal
onClose={() => setPreservedFormatsModal(false)}
activeLink={link}
/>
) : undefined}
{/* {expandedLink ? (
<ExpandedLink onClose={() => setExpandedLink(false)} link={link} />
) : undefined} */}
</>
);
}

View File

@ -0,0 +1,34 @@
import {
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
export default function LinkCollection({
link,
collection,
}: {
link: LinkIncludingShortenedCollectionAndTags;
collection: CollectionIncludingMembersAndLinkCount;
}) {
const router = useRouter();
return (
<Link
href={`/collections/${link.collection.id}`}
onClick={(e) => {
e.stopPropagation();
}}
className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100 select-none"
title={collection?.name}
>
<i
className="bi-folder-fill text-lg drop-shadow"
style={{ color: collection?.color }}
></i>
<p className="truncate capitalize">{collection?.name}</p>
</Link>
);
}

View File

@ -0,0 +1,23 @@
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import React from "react";
export default function LinkDate({
link,
}: {
link: LinkIncludingShortenedCollectionAndTags;
}) {
const formattedDate = new Date(
(link.importDate || link.createdAt) as string
).toLocaleString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
return (
<div className="flex items-center gap-1 text-neutral">
<i className="bi-calendar3 text-lg"></i>
<p>{formattedDate}</p>
</div>
);
}

View File

@ -0,0 +1,53 @@
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import Image from "next/image";
import isValidUrl from "@/lib/shared/isValidUrl";
import React from "react";
import Link from "next/link";
export default function LinkGroupedIconURL({
link,
}: {
link: LinkIncludingShortenedCollectionAndTags;
}) {
const url =
isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined;
const [showFavicon, setShowFavicon] = React.useState<boolean>(true);
let shortendURL;
try {
shortendURL = new URL(link.url || "").host.toLowerCase();
} catch (error) {
console.log(error);
}
return (
<Link href={link.url || ""} target="_blank">
<div className="bg-white shadow-md rounded-md border-[2px] flex gap-1 item-center justify-center border-white select-none z-10 max-w-full">
{link.url && url && showFavicon ? (
<Image
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${link.url}&size=32`}
width={64}
height={64}
alt=""
className="w-5 h-5 rounded"
draggable="false"
onError={() => {
setShowFavicon(false);
}}
/>
) : showFavicon === false ? (
<i className="bi-link-45deg text-xl leading-none text-black"></i>
) : link.type === "pdf" ? (
<i className={`bi-file-earmark-pdf`}></i>
) : link.type === "image" ? (
<i className={`bi-file-earmark-image`}></i>
) : undefined}
<p className="truncate bg-white text-black mr-1">
<p className="text-sm">{shortendURL}</p>
</p>
</div>
</Link>
);
}

View File

@ -0,0 +1,48 @@
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import Image from "next/image";
import isValidUrl from "@/lib/shared/isValidUrl";
import React from "react";
export default function LinkIcon({
link,
width,
}: {
link: LinkIncludingShortenedCollectionAndTags;
width?: string;
}) {
const url =
isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined;
const iconClasses: string =
"bg-white shadow rounded-md border-[2px] flex item-center justify-center border-white select-none z-10" +
" " +
(width || "w-12");
const [showFavicon, setShowFavicon] = React.useState<boolean>(true);
return (
<>
{link.url && url && showFavicon ? (
<Image
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${link.url}&size=32`}
width={64}
height={64}
alt=""
className={iconClasses}
draggable="false"
onError={() => {
setShowFavicon(false);
}}
/>
) : showFavicon === false ? (
<div className={iconClasses}>
<i className="bi-link-45deg text-4xl text-black"></i>
</div>
) : link.type === "pdf" ? (
<i className={`bi-file-earmark-pdf ${iconClasses}`}></i>
) : link.type === "image" ? (
<i className={`bi-file-earmark-image ${iconClasses}`}></i>
) : undefined}
</>
);
}