diff --git a/components/LinkViews/LinkComponents/LinkPin.tsx b/components/LinkViews/LinkComponents/LinkPin.tsx
new file mode 100644
index 0000000..cf38d21
--- /dev/null
+++ b/components/LinkViews/LinkComponents/LinkPin.tsx
@@ -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 (
+
+ );
+}
diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx
index ce2a368..fdcd2af 100644
--- a/components/ViewDropdown.tsx
+++ b/components/ViewDropdown.tsx
@@ -130,7 +130,7 @@ export default function ViewDropdown({ viewMode, setViewMode }: Props) {
className="range range-xs range-primary"
step="1"
/>
-
+
|
|
|
diff --git a/hooks/store/links.tsx b/hooks/store/links.tsx
index 037f974..ffbb24b 100644
--- a/hooks/store/links.tsx
+++ b/hooks/store/links.tsx
@@ -121,8 +121,11 @@ const useAddLink = () => {
},
onSuccess: (data) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- if (!oldData) return undefined;
- return [data, ...oldData];
+ if (!oldData?.links) return undefined;
+ return {
+ ...oldData,
+ links: [data, ...oldData.links],
+ };
});
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -161,8 +164,8 @@ const useUpdateLink = () => {
},
onSuccess: (data) => {
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- // if (!oldData) return undefined;
- // return oldData.map((e: any) => (e.id === data.id ? data : e));
+ // if (!oldData?.links) return undefined;
+ // return oldData.links.map((e: any) => (e.id === data.id ? data : e));
// });
// queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -202,8 +205,11 @@ const useDeleteLink = () => {
},
onSuccess: (data) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- if (!oldData) return undefined;
- return oldData.filter((e: any) => e.id !== data.id);
+ if (!oldData?.links) return undefined;
+ return {
+ ...oldData,
+ links: oldData.links.filter((e: any) => e.id !== data.id),
+ };
});
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -249,8 +255,11 @@ const useGetLink = () => {
},
onSuccess: (data) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- if (!oldData) return undefined;
- return oldData.map((e: any) => (e.id === data.id ? data : e));
+ if (!oldData?.links) return undefined;
+ return {
+ ...oldData,
+ links: oldData.links.map((e: any) => (e.id === data.id ? data : e)),
+ };
});
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -302,8 +311,8 @@ const useBulkDeleteLinks = () => {
},
onSuccess: (data) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- if (!oldData) return undefined;
- return oldData.filter((e: any) => !data.includes(e.id));
+ if (!oldData.links) return undefined;
+ return oldData.links.filter((e: any) => !data.includes(e.id));
});
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -377,8 +386,11 @@ const useUploadFile = () => {
},
onSuccess: (data) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- if (!oldData) return undefined;
- return [data, ...oldData];
+ if (!oldData?.links) return undefined;
+ return {
+ ...oldData,
+ links: [data, ...oldData.links],
+ };
});
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
@@ -464,8 +476,8 @@ const useBulkEditLinks = () => {
onSuccess: (data, { links, newData, removePreviousTags }) => {
// TODO: Fix these
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
- // if (!oldData) return undefined;
- // return oldData.map((e: any) =>
+ // if (!oldData?.links) return undefined;
+ // return oldData.links.map((e: any) =>
// data.find((d: any) => d.id === e.id) ? data : e
// );
// });
diff --git a/lib/api/controllers/dashboard/getDashboardDataV2.ts b/lib/api/controllers/dashboard/getDashboardDataV2.ts
index 0f93ec6..b764737 100644
--- a/lib/api/controllers/dashboard/getDashboardDataV2.ts
+++ b/lib/api/controllers/dashboard/getDashboardDataV2.ts
@@ -107,9 +107,15 @@ export default async function getDashboardData(
const links = [...recentlyAddedLinks, ...pinnedLinks].sort(
(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 {
data: {
- links,
+ links: uniqueLinks,
numberOfPinnedLinks,
},
message: "Dashboard data fetched successfully.",
diff --git a/lib/api/controllers/links/linkId/updateLinkById.ts b/lib/api/controllers/links/linkId/updateLinkById.ts
index 8115f04..7ba5953 100644
--- a/lib/api/controllers/links/linkId/updateLinkById.ts
+++ b/lib/api/controllers/links/linkId/updateLinkById.ts
@@ -124,7 +124,7 @@ export default async function updateLinkById(
})),
},
pinnedBy:
- data?.pinnedBy && data.pinnedBy[0]
+ data?.pinnedBy && data.pinnedBy[0].id === userId
? { connect: { id: userId } }
: { disconnect: { id: userId } },
},
diff --git a/lib/client/pinLink.ts b/lib/client/pinLink.ts
new file mode 100644
index 0000000..5b92741
--- /dev/null
+++ b/lib/client/pinLink.ts
@@ -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;
diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx
index eaebd34..cbec492 100644
--- a/pages/dashboard.tsx
+++ b/pages/dashboard.tsx
@@ -1,7 +1,6 @@
import MainLayout from "@/layouts/MainLayout";
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
-import useWindowDimensions from "@/hooks/useWindowDimensions";
import React from "react";
import { toast } from "react-hot-toast";
import { MigrationFormat, MigrationRequest, ViewMode } from "@/types/global";
@@ -21,14 +20,14 @@ import useLocalSettingsStore from "@/store/localSettings";
export default function Dashboard() {
const { t } = useTranslation();
const { data: collections = [] } = useCollections();
- const { data: { links = [] } = { links: [] }, ...dashboardData } =
- useDashboardData();
+ const {
+ data: { links = [], numberOfPinnedLinks } = { links: [] },
+ ...dashboardData
+ } = useDashboardData();
const { data: tags = [] } = useTags();
const [numberOfLinks, setNumberOfLinks] = useState(0);
- const [showLinks, setShowLinks] = useState(3);
-
const { settings } = useLocalSettingsStore();
useEffect(() => {
@@ -41,25 +40,19 @@ export default function Dashboard() {
);
}, [collections]);
- const handleNumberOfLinksToShow = () => {
+ const numberOfLinksToShow = useMemo(() => {
if (window.innerWidth > 1900) {
- setShowLinks(10);
+ return 10;
} else if (window.innerWidth > 1500) {
- setShowLinks(8);
+ return 8;
} else if (window.innerWidth > 880) {
- setShowLinks(6);
+ return 6;
} else if (window.innerWidth > 550) {
- setShowLinks(4);
- } else setShowLinks(2);
- };
-
- const { width } = useWindowDimensions();
-
- useEffect(() => {
- settings.columns === 0
- ? handleNumberOfLinksToShow()
- : setShowLinks(settings.columns);
- }, [width, settings.columns]);
+ return 4;
+ } else {
+ return 2;
+ }
+ }, []);
const importBookmarks = async (
e: React.ChangeEvent,
@@ -119,32 +112,30 @@ export default function Dashboard() {
-
-
@@ -174,13 +165,19 @@ export default function Dashboard() {
) : links && links[0] && !dashboardData.isLoading ? (
-
+
) : (
@@ -311,7 +308,7 @@ export default function Dashboard() {
@@ -320,7 +317,12 @@ export default function Dashboard() {
e.pinnedBy && e.pinnedBy[0])
- .slice(0, showLinks)}
+ .slice(
+ 0,
+ settings.columns
+ ? settings.columns * 2
+ : numberOfLinksToShow
+ )}
layout={viewMode}
/>