add pin to hover view + add number of pins to dashboard + bug fixes
This commit is contained in:
parent
fb1869ca7a
commit
9b1506a64e
|
@ -14,12 +14,7 @@ export default function dashboardItem({
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-4 flex flex-col justify-center">
|
<div className="ml-4 flex flex-col justify-center">
|
||||||
<p className="text-neutral text-xs tracking-wider">{name}</p>
|
<p className="text-neutral text-xs tracking-wider">{name}</p>
|
||||||
<p className="font-thin text-5xl text-primary mt-0.5 hidden sm:block md:hidden">
|
<p className="font-thin text-5xl text-primary mt-0.5">{value || 0}</p>
|
||||||
{value < 1000 ? value : (value / 1000).toFixed(1) + "k"}
|
|
||||||
</p>
|
|
||||||
<p className="font-thin text-5xl text-primary mt-0.5 sm:hidden md:block">
|
|
||||||
{value}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,12 +7,12 @@ import usePermissions from "@/hooks/usePermissions";
|
||||||
import DeleteLinkModal from "@/components/ModalContent/DeleteLinkModal";
|
import DeleteLinkModal from "@/components/ModalContent/DeleteLinkModal";
|
||||||
import { dropdownTriggerer } from "@/lib/client/utils";
|
import { dropdownTriggerer } from "@/lib/client/utils";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { useUser } from "@/hooks/store/user";
|
import { useDeleteLink, useGetLink } from "@/hooks/store/links";
|
||||||
import { useDeleteLink, useGetLink, useUpdateLink } from "@/hooks/store/links";
|
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import LinkModal from "@/components/ModalContent/LinkModal";
|
import LinkModal from "@/components/ModalContent/LinkModal";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import usePinLink from "@/lib/client/pinLink";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: LinkIncludingShortenedCollectionAndTags;
|
link: LinkIncludingShortenedCollectionAndTags;
|
||||||
|
@ -27,41 +27,14 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
const permissions = usePermissions(link.collection.id as number);
|
const permissions = usePermissions(link.collection.id as number);
|
||||||
const getLink = useGetLink();
|
const getLink = useGetLink();
|
||||||
|
|
||||||
|
const pinLink = usePinLink();
|
||||||
|
|
||||||
const [editLinkModal, setEditLinkModal] = useState(false);
|
const [editLinkModal, setEditLinkModal] = useState(false);
|
||||||
const [linkModal, setLinkModal] = useState(false);
|
const [linkModal, setLinkModal] = useState(false);
|
||||||
const [deleteLinkModal, setDeleteLinkModal] = useState(false);
|
const [deleteLinkModal, setDeleteLinkModal] = useState(false);
|
||||||
|
|
||||||
const { data: user = {} } = useUser();
|
|
||||||
|
|
||||||
const updateLink = useUpdateLink();
|
|
||||||
const deleteLink = useDeleteLink();
|
const deleteLink = useDeleteLink();
|
||||||
|
|
||||||
const pinLink = async () => {
|
|
||||||
const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0] ? true : false;
|
|
||||||
|
|
||||||
const load = toast.loading(t("updating"));
|
|
||||||
|
|
||||||
await updateLink.mutateAsync(
|
|
||||||
{
|
|
||||||
...link,
|
|
||||||
pinnedBy: isAlreadyPinned ? [{ id: undefined }] : [{ id: user.id }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSettled: (data, error) => {
|
|
||||||
toast.dismiss(load);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
toast.error(error.message);
|
|
||||||
} else {
|
|
||||||
toast.success(
|
|
||||||
isAlreadyPinned ? t("link_unpinned") : t("link_pinned")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateArchive = async () => {
|
const updateArchive = async () => {
|
||||||
const load = toast.loading(t("sending_request"));
|
const load = toast.loading(t("sending_request"));
|
||||||
|
|
||||||
|
@ -103,7 +76,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={`dropdown dropdown-left absolute ${
|
className={`dropdown dropdown-end absolute ${
|
||||||
className || "top-3 right-3"
|
className || "top-3 right-3"
|
||||||
} z-20`}
|
} z-20`}
|
||||||
>
|
>
|
||||||
|
@ -117,7 +90,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
className={
|
className={
|
||||||
"dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box mr-1"
|
"dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
|
@ -126,7 +99,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
pinLink();
|
pinLink(link);
|
||||||
}}
|
}}
|
||||||
className="whitespace-nowrap"
|
className="whitespace-nowrap"
|
||||||
>
|
>
|
||||||
|
@ -216,7 +189,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
{editLinkModal && (
|
{editLinkModal && (
|
||||||
<LinkModal
|
<LinkModal
|
||||||
onClose={() => setEditLinkModal(false)}
|
onClose={() => setEditLinkModal(false)}
|
||||||
onPin={pinLink}
|
onPin={() => pinLink(link)}
|
||||||
onUpdateArchive={updateArchive}
|
onUpdateArchive={updateArchive}
|
||||||
onDelete={() => setDeleteLinkModal(true)}
|
onDelete={() => setDeleteLinkModal(true)}
|
||||||
link={link}
|
link={link}
|
||||||
|
@ -232,7 +205,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
||||||
{linkModal && (
|
{linkModal && (
|
||||||
<LinkModal
|
<LinkModal
|
||||||
onClose={() => setLinkModal(false)}
|
onClose={() => setLinkModal(false)}
|
||||||
onPin={pinLink}
|
onPin={() => pinLink(link)}
|
||||||
onUpdateArchive={updateArchive}
|
onUpdateArchive={updateArchive}
|
||||||
onDelete={() => setDeleteLinkModal(true)}
|
onDelete={() => setDeleteLinkModal(true)}
|
||||||
link={link}
|
link={link}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { useUser } from "@/hooks/store/user";
|
||||||
import { useGetLink, useLinks } from "@/hooks/store/links";
|
import { useGetLink, useLinks } from "@/hooks/store/links";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useLocalSettingsStore from "@/store/localSettings";
|
import useLocalSettingsStore from "@/store/localSettings";
|
||||||
|
import LinkPin from "./LinkPin";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: LinkIncludingShortenedCollectionAndTags;
|
link: LinkIncludingShortenedCollectionAndTags;
|
||||||
|
@ -36,13 +37,13 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const heightMap = {
|
const heightMap = {
|
||||||
1: "h-48",
|
1: "h-44",
|
||||||
2: "h-44",
|
2: "h-40",
|
||||||
3: "h-40",
|
3: "h-36",
|
||||||
4: "h-36",
|
4: "h-32",
|
||||||
5: "h-32",
|
5: "h-28",
|
||||||
6: "h-28",
|
6: "h-24",
|
||||||
7: "h-24",
|
7: "h-20",
|
||||||
8: "h-20",
|
8: "h-20",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,7 +217,7 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
||||||
<div>
|
<div>
|
||||||
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
|
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
|
||||||
<div className="flex justify-between text-xs text-neutral px-3 pb-1 gap-2">
|
<div className="flex justify-between items-center text-xs text-neutral px-3 pb-1 gap-2">
|
||||||
{show.collection && (
|
{show.collection && (
|
||||||
<div className="cursor-pointer truncate">
|
<div className="cursor-pointer truncate">
|
||||||
<LinkCollection link={link} collection={collection} />
|
<LinkCollection link={link} collection={collection} />
|
||||||
|
@ -238,6 +239,10 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
||||||
"top-3 right-3 group-hover:opacity-100 group-focus-within:opacity-100 opacity-0 duration-100"
|
"top-3 right-3 group-hover:opacity-100 group-focus-within:opacity-100 opacity-0 duration-100"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<LinkPin
|
||||||
|
link={link}
|
||||||
|
className="absolute top-3 right-[3.25rem] group-hover:opacity-100 group-focus-within:opacity-100 opacity-0 duration-100"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,13 +35,13 @@ export default function LinkMasonry({ link, editMode, columns }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const heightMap = {
|
const heightMap = {
|
||||||
1: "h-48",
|
1: "h-44",
|
||||||
2: "h-44",
|
2: "h-40",
|
||||||
3: "h-40",
|
3: "h-36",
|
||||||
4: "h-36",
|
4: "h-32",
|
||||||
5: "h-32",
|
5: "h-28",
|
||||||
6: "h-28",
|
6: "h-24",
|
||||||
7: "h-24",
|
7: "h-20",
|
||||||
8: "h-20",
|
8: "h-20",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ export default function LinkMasonry({ link, editMode, columns }: Props) {
|
||||||
<div>
|
<div>
|
||||||
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
|
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-between text-xs text-neutral px-3 pb-1 w-full gap-x-2">
|
<div className="flex flex-wrap justify-between items-center text-xs text-neutral px-3 pb-1 w-full gap-x-2">
|
||||||
{show.collection && (
|
{show.collection && (
|
||||||
<div className="cursor-pointer truncate">
|
<div className="cursor-pointer truncate">
|
||||||
<LinkCollection link={link} collection={collection} />
|
<LinkCollection link={link} collection={collection} />
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import usePinLink from "@/lib/client/pinLink";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
link: LinkIncludingShortenedCollectionAndTags;
|
||||||
|
className?: string;
|
||||||
|
btnStyle?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LinkPin({ link, className, btnStyle }: Props) {
|
||||||
|
const pinLink = usePinLink();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isPublicRoute = router.pathname.startsWith("/public") ? true : false;
|
||||||
|
const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0] ? true : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(className || "top-3 right-3 absolute", btnStyle)}
|
||||||
|
onClick={() => pinLink(link)}
|
||||||
|
>
|
||||||
|
<div className="btn btn-sm btn-square text-neutral">
|
||||||
|
<i
|
||||||
|
title="Pin"
|
||||||
|
className={clsx(
|
||||||
|
"text-xl",
|
||||||
|
isAlreadyPinned ? "bi-pin-fill" : "bi-pin"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -130,7 +130,7 @@ export default function ViewDropdown({ viewMode, setViewMode }: Props) {
|
||||||
className="range range-xs range-primary"
|
className="range range-xs range-primary"
|
||||||
step="1"
|
step="1"
|
||||||
/>
|
/>
|
||||||
<div className="flex w-full justify-between px-2 text-xs text-neutral">
|
<div className="flex w-full justify-between px-2 text-xs text-neutral select-none">
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
|
|
|
@ -121,8 +121,11 @@ const useAddLink = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
if (!oldData) return undefined;
|
if (!oldData?.links) return undefined;
|
||||||
return [data, ...oldData];
|
return {
|
||||||
|
...oldData,
|
||||||
|
links: [data, ...oldData.links],
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -161,8 +164,8 @@ const useUpdateLink = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
// if (!oldData) return undefined;
|
// if (!oldData?.links) return undefined;
|
||||||
// return oldData.map((e: any) => (e.id === data.id ? data : e));
|
// return oldData.links.map((e: any) => (e.id === data.id ? data : e));
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
// queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -202,8 +205,11 @@ const useDeleteLink = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
if (!oldData) return undefined;
|
if (!oldData?.links) return undefined;
|
||||||
return oldData.filter((e: any) => e.id !== data.id);
|
return {
|
||||||
|
...oldData,
|
||||||
|
links: oldData.links.filter((e: any) => e.id !== data.id),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -249,8 +255,11 @@ const useGetLink = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
if (!oldData) return undefined;
|
if (!oldData?.links) return undefined;
|
||||||
return oldData.map((e: any) => (e.id === data.id ? data : e));
|
return {
|
||||||
|
...oldData,
|
||||||
|
links: oldData.links.map((e: any) => (e.id === data.id ? data : e)),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -302,8 +311,8 @@ const useBulkDeleteLinks = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
if (!oldData) return undefined;
|
if (!oldData.links) return undefined;
|
||||||
return oldData.filter((e: any) => !data.includes(e.id));
|
return oldData.links.filter((e: any) => !data.includes(e.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -377,8 +386,11 @@ const useUploadFile = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
if (!oldData) return undefined;
|
if (!oldData?.links) return undefined;
|
||||||
return [data, ...oldData];
|
return {
|
||||||
|
...oldData,
|
||||||
|
links: [data, ...oldData.links],
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
||||||
|
@ -464,8 +476,8 @@ const useBulkEditLinks = () => {
|
||||||
onSuccess: (data, { links, newData, removePreviousTags }) => {
|
onSuccess: (data, { links, newData, removePreviousTags }) => {
|
||||||
// TODO: Fix these
|
// TODO: Fix these
|
||||||
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
||||||
// if (!oldData) return undefined;
|
// if (!oldData?.links) return undefined;
|
||||||
// return oldData.map((e: any) =>
|
// return oldData.links.map((e: any) =>
|
||||||
// data.find((d: any) => d.id === e.id) ? data : e
|
// data.find((d: any) => d.id === e.id) ? data : e
|
||||||
// );
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
|
@ -107,9 +107,15 @@ export default async function getDashboardData(
|
||||||
const links = [...recentlyAddedLinks, ...pinnedLinks].sort(
|
const links = [...recentlyAddedLinks, ...pinnedLinks].sort(
|
||||||
(a, b) => new Date(b.id).getTime() - new Date(a.id).getTime()
|
(a, b) => new Date(b.id).getTime() - new Date(a.id).getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Make sure links are unique
|
||||||
|
const uniqueLinks = links.filter(
|
||||||
|
(link, index, self) => index === self.findIndex((t) => t.id === link.id)
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
links,
|
links: uniqueLinks,
|
||||||
numberOfPinnedLinks,
|
numberOfPinnedLinks,
|
||||||
},
|
},
|
||||||
message: "Dashboard data fetched successfully.",
|
message: "Dashboard data fetched successfully.",
|
||||||
|
|
|
@ -124,7 +124,7 @@ export default async function updateLinkById(
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
pinnedBy:
|
pinnedBy:
|
||||||
data?.pinnedBy && data.pinnedBy[0]
|
data?.pinnedBy && data.pinnedBy[0].id === userId
|
||||||
? { connect: { id: userId } }
|
? { connect: { id: userId } }
|
||||||
: { disconnect: { id: userId } },
|
: { disconnect: { id: userId } },
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { useUpdateLink } from "@/hooks/store/links";
|
||||||
|
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
import { useUser } from "@/hooks/store/user";
|
||||||
|
|
||||||
|
const usePinLink = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const updateLink = useUpdateLink();
|
||||||
|
const { data: user = {} } = useUser();
|
||||||
|
|
||||||
|
// Return a function that can be used to pin/unpin the link
|
||||||
|
const pinLink = async (link: LinkIncludingShortenedCollectionAndTags) => {
|
||||||
|
const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0] ? true : false;
|
||||||
|
|
||||||
|
const load = toast.loading(t("updating"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateLink.mutateAsync(
|
||||||
|
{
|
||||||
|
...link,
|
||||||
|
pinnedBy: isAlreadyPinned ? [{ id: undefined }] : [{ id: user.id }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSettled: (data, error) => {
|
||||||
|
toast.dismiss(load);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
toast.error(error.message);
|
||||||
|
} else {
|
||||||
|
toast.success(
|
||||||
|
isAlreadyPinned ? t("link_unpinned") : t("link_pinned")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
toast.dismiss(load);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return pinLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default usePinLink;
|
|
@ -1,7 +1,6 @@
|
||||||
import MainLayout from "@/layouts/MainLayout";
|
import MainLayout from "@/layouts/MainLayout";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import useWindowDimensions from "@/hooks/useWindowDimensions";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { MigrationFormat, MigrationRequest, ViewMode } from "@/types/global";
|
import { MigrationFormat, MigrationRequest, ViewMode } from "@/types/global";
|
||||||
|
@ -21,14 +20,14 @@ import useLocalSettingsStore from "@/store/localSettings";
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { data: collections = [] } = useCollections();
|
const { data: collections = [] } = useCollections();
|
||||||
const { data: { links = [] } = { links: [] }, ...dashboardData } =
|
const {
|
||||||
useDashboardData();
|
data: { links = [], numberOfPinnedLinks } = { links: [] },
|
||||||
|
...dashboardData
|
||||||
|
} = useDashboardData();
|
||||||
const { data: tags = [] } = useTags();
|
const { data: tags = [] } = useTags();
|
||||||
|
|
||||||
const [numberOfLinks, setNumberOfLinks] = useState(0);
|
const [numberOfLinks, setNumberOfLinks] = useState(0);
|
||||||
|
|
||||||
const [showLinks, setShowLinks] = useState(3);
|
|
||||||
|
|
||||||
const { settings } = useLocalSettingsStore();
|
const { settings } = useLocalSettingsStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -41,25 +40,19 @@ export default function Dashboard() {
|
||||||
);
|
);
|
||||||
}, [collections]);
|
}, [collections]);
|
||||||
|
|
||||||
const handleNumberOfLinksToShow = () => {
|
const numberOfLinksToShow = useMemo(() => {
|
||||||
if (window.innerWidth > 1900) {
|
if (window.innerWidth > 1900) {
|
||||||
setShowLinks(10);
|
return 10;
|
||||||
} else if (window.innerWidth > 1500) {
|
} else if (window.innerWidth > 1500) {
|
||||||
setShowLinks(8);
|
return 8;
|
||||||
} else if (window.innerWidth > 880) {
|
} else if (window.innerWidth > 880) {
|
||||||
setShowLinks(6);
|
return 6;
|
||||||
} else if (window.innerWidth > 550) {
|
} else if (window.innerWidth > 550) {
|
||||||
setShowLinks(4);
|
return 4;
|
||||||
} else setShowLinks(2);
|
} else {
|
||||||
};
|
return 2;
|
||||||
|
}
|
||||||
const { width } = useWindowDimensions();
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
settings.columns === 0
|
|
||||||
? handleNumberOfLinksToShow()
|
|
||||||
: setShowLinks(settings.columns);
|
|
||||||
}, [width, settings.columns]);
|
|
||||||
|
|
||||||
const importBookmarks = async (
|
const importBookmarks = async (
|
||||||
e: React.ChangeEvent<HTMLInputElement>,
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
@ -119,32 +112,30 @@ export default function Dashboard() {
|
||||||
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="xl:flex flex flex-col sm:grid grid-cols-2 gap-5 xl:flex-row xl:justify-evenly xl:w-full h-full rounded-2xl p-5 bg-base-200 border border-neutral-content">
|
||||||
<div className="flex justify-evenly flex-col items-start sm:flex-row sm:items-center gap-5 xl:w-full h-full rounded-2xl p-5 bg-base-200">
|
<DashboardItem
|
||||||
<DashboardItem
|
name={numberOfLinks === 1 ? t("link") : t("links")}
|
||||||
name={numberOfLinks === 1 ? t("link") : t("links")}
|
value={numberOfLinks}
|
||||||
value={numberOfLinks}
|
icon={"bi-link-45deg"}
|
||||||
icon={"bi-link-45deg"}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="divider m-0"></div>
|
<DashboardItem
|
||||||
|
name={collections.length === 1 ? t("collection") : t("collections")}
|
||||||
|
value={collections.length}
|
||||||
|
icon={"bi-folder"}
|
||||||
|
/>
|
||||||
|
|
||||||
<DashboardItem
|
<DashboardItem
|
||||||
name={
|
name={tags.length === 1 ? t("tag") : t("tags")}
|
||||||
collections.length === 1 ? t("collection") : t("collections")
|
value={tags.length}
|
||||||
}
|
icon={"bi-hash"}
|
||||||
value={collections.length}
|
/>
|
||||||
icon={"bi-folder"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="divider m-0"></div>
|
<DashboardItem
|
||||||
|
name={t("pinned")}
|
||||||
<DashboardItem
|
value={numberOfPinnedLinks}
|
||||||
name={tags.length === 1 ? t("tag") : t("tags")}
|
icon={"bi-pin-angle"}
|
||||||
value={tags.length}
|
/>
|
||||||
icon={"bi-hash"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
|
@ -174,13 +165,19 @@ export default function Dashboard() {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Links
|
<Links
|
||||||
layout={viewMode}
|
layout={viewMode}
|
||||||
placeholderCount={showLinks / 2}
|
placeholderCount={settings.columns || 1}
|
||||||
useData={dashboardData}
|
useData={dashboardData}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : links && links[0] && !dashboardData.isLoading ? (
|
) : links && links[0] && !dashboardData.isLoading ? (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Links links={links.slice(0, showLinks)} layout={viewMode} />
|
<Links
|
||||||
|
links={links.slice(
|
||||||
|
0,
|
||||||
|
settings.columns ? settings.columns * 2 : numberOfLinksToShow
|
||||||
|
)}
|
||||||
|
layout={viewMode}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200">
|
<div className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200">
|
||||||
|
@ -311,7 +308,7 @@ export default function Dashboard() {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Links
|
<Links
|
||||||
layout={viewMode}
|
layout={viewMode}
|
||||||
placeholderCount={showLinks / 2}
|
placeholderCount={settings.columns || 1}
|
||||||
useData={dashboardData}
|
useData={dashboardData}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -320,7 +317,12 @@ export default function Dashboard() {
|
||||||
<Links
|
<Links
|
||||||
links={links
|
links={links
|
||||||
.filter((e: any) => e.pinnedBy && e.pinnedBy[0])
|
.filter((e: any) => e.pinnedBy && e.pinnedBy[0])
|
||||||
.slice(0, showLinks)}
|
.slice(
|
||||||
|
0,
|
||||||
|
settings.columns
|
||||||
|
? settings.columns * 2
|
||||||
|
: numberOfLinksToShow
|
||||||
|
)}
|
||||||
layout={viewMode}
|
layout={viewMode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
Ŝarĝante…
Reference in New Issue