mirror of
https://github.com/linkwarden/linkwarden.git
synced 2026-03-03 02:47:02 +00:00
* build(deps): bump the npm_and_yarn group across 5 directories with 22 updates Bumps the npm_and_yarn group with 18 updates in the / directory: | Package | From | To | | --- | --- | --- | | [axios](https://github.com/axios/axios) | `1.5.1` | `1.12.0` | | [dompurify](https://github.com/cure53/DOMPurify) | `3.0.6` | `3.2.4` | | [formidable](https://github.com/node-formidable/formidable) | `3.5.1` | `3.5.4` | | [next](https://github.com/vercel/next.js) | `13.4.12` | `14.2.35` | | [next-auth](https://github.com/nextauthjs/next-auth) | `4.22.1` | `4.24.12` | | [playwright](https://github.com/microsoft/playwright) | `1.55.0` | `1.55.1` | | [@mozilla/readability](https://github.com/mozilla/readability) | `0.4.4` | `0.6.0` | | [ai](https://github.com/vercel/ai) | `4.3.9` | `5.0.52` | | [nodemailer](https://github.com/nodemailer/nodemailer) | `6.9.3` | `7.0.11` | | [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.11` | `1.1.12` | | [braces](https://github.com/micromatch/braces) | `3.0.2` | `3.0.3` | | [form-data](https://github.com/form-data/form-data) | `3.0.3` | `3.0.4` | | [js-yaml](https://github.com/nodeca/js-yaml) | `3.14.1` | `3.14.2` | | [micromatch](https://github.com/micromatch/micromatch) | `4.0.5` | `4.0.8` | | [min-document](https://github.com/Raynos/min-document) | `2.19.0` | `2.19.2` | | [nanoid](https://github.com/ai/nanoid) | `3.3.6` | `3.3.8` | | [node-forge](https://github.com/digitalbazaar/forge) | `1.3.1` | `1.3.3` | | [tar](https://github.com/isaacs/node-tar) | `6.1.13` | `6.2.1` | Bumps the npm_and_yarn group with 1 update in the /apps/web directory: [next](https://github.com/vercel/next.js). Bumps the npm_and_yarn group with 2 updates in the /apps/worker directory: [@mozilla/readability](https://github.com/mozilla/readability) and [ai](https://github.com/vercel/ai). Bumps the npm_and_yarn group with 1 update in the /packages/lib directory: [nodemailer](https://github.com/nodemailer/nodemailer). Bumps the npm_and_yarn group with 1 update in the /packages/router directory: [next](https://github.com/vercel/next.js). Updates `axios` from 1.5.1 to 1.12.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.5.1...v1.12.0) Updates `dompurify` from 3.0.6 to 3.2.4 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.0.6...3.2.4) Updates `formidable` from 3.5.1 to 3.5.4 - [Release notes](https://github.com/node-formidable/formidable/releases) - [Changelog](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md) - [Commits](https://github.com/node-formidable/formidable/commits) Updates `next` from 13.4.12 to 14.2.35 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35) Updates `next-auth` from 4.22.1 to 4.24.12 - [Release notes](https://github.com/nextauthjs/next-auth/releases) - [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@4.22.1...next-auth@4.24.12) Updates `playwright` from 1.55.0 to 1.55.1 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.55.0...v1.55.1) Updates `postcss` from 8.4.26 to 8.5.3 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.26...8.5.3) Updates `@mozilla/readability` from 0.4.4 to 0.6.0 - [Changelog](https://github.com/mozilla/readability/blob/main/CHANGELOG.md) - [Commits](https://github.com/mozilla/readability/compare/0.4.4...0.6.0) Updates `ai` from 4.3.9 to 5.0.52 - [Release notes](https://github.com/vercel/ai/releases) - [Changelog](https://github.com/vercel/ai/blob/main/CHANGELOG.md) - [Commits](https://github.com/vercel/ai/compare/ai@4.3.9...ai@5.0.52) Updates `nodemailer` from 6.9.3 to 7.0.11 - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.3...v7.0.11) Updates `@babel/runtime` from 7.21.5 to 7.27.0 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime) Updates `brace-expansion` from 1.1.11 to 1.1.12 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12) Updates `braces` from 3.0.2 to 3.0.3 - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) Updates `follow-redirects` from 1.15.3 to 1.15.11 - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.11) Updates `form-data` from 3.0.3 to 3.0.4 - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v3.0.3...v3.0.4) Updates `jose` from 4.14.4 to 4.15.9 - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/v4.15.9/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v4.14.4...v4.15.9) Updates `js-yaml` from 3.14.1 to 3.14.2 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2) Updates `micromatch` from 4.0.5 to 4.0.8 - [Release notes](https://github.com/micromatch/micromatch/releases) - [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8) Updates `min-document` from 2.19.0 to 2.19.2 - [Commits](https://github.com/Raynos/min-document/compare/v2.19.0...v2.19.2) Updates `nanoid` from 3.3.6 to 3.3.8 - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.3.6...3.3.8) Updates `node-forge` from 1.3.1 to 1.3.3 - [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md) - [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.3) Updates `tar` from 6.1.13 to 6.2.1 - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.13...v6.2.1) Updates `next` from 13.4.12 to 14.2.35 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35) Updates `@mozilla/readability` from 0.4.4 to 0.6.0 - [Changelog](https://github.com/mozilla/readability/blob/main/CHANGELOG.md) - [Commits](https://github.com/mozilla/readability/compare/0.4.4...0.6.0) Updates `ai` from 4.3.19 to 5.0.113 - [Release notes](https://github.com/vercel/ai/releases) - [Changelog](https://github.com/vercel/ai/blob/main/CHANGELOG.md) - [Commits](https://github.com/vercel/ai/compare/ai@4.3.9...ai@5.0.52) Updates `nodemailer` from 6.10.1 to 7.0.11 - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.3...v7.0.11) Updates `next` from 13.4.12 to 14.2.35 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35) --- updated-dependencies: - dependency-name: axios dependency-version: 1.12.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: dompurify dependency-version: 3.2.4 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: formidable dependency-version: 3.5.4 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: next dependency-version: 14.2.35 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: next-auth dependency-version: 4.24.12 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: playwright dependency-version: 1.55.1 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: postcss dependency-version: 8.5.3 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: "@mozilla/readability" dependency-version: 0.6.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: ai dependency-version: 5.0.52 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: nodemailer dependency-version: 7.0.11 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: "@babel/runtime" dependency-version: 7.27.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: brace-expansion dependency-version: 1.1.12 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: braces dependency-version: 3.0.3 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: follow-redirects dependency-version: 1.15.11 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: form-data dependency-version: 3.0.4 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: jose dependency-version: 4.15.9 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: js-yaml dependency-version: 3.14.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: micromatch dependency-version: 4.0.8 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: min-document dependency-version: 2.19.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: nanoid dependency-version: 3.3.8 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: node-forge dependency-version: 1.3.3 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: tar dependency-version: 6.2.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: next dependency-version: 14.2.35 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: "@mozilla/readability" dependency-version: 0.6.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: ai dependency-version: 5.0.113 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: nodemailer dependency-version: 7.0.11 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: next dependency-version: 14.2.35 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * bug fixes and improvements * always show navbar in reader view * bug fix and small performance improvement * minor fix * Refactor link selection management and bulk actions - Replaced the use of selectedLinks with selectedIds in the link store for better performance and clarity. - Updated LinkListOptions, BulkDeleteLinksModal, and BulkEditLinksModal components to utilize the new selection management. - Modified LinkCard, LinkMasonry, and LinkList components to handle selection state through props. - Enhanced updateLinks API to support bulk updates with improved tag management. - Cleaned up unused imports and code related to previous selection methods. * move refetching logic to Links component * move disableDraggable and user hook out of each card to improve efficiency * cleaner code * memoize components and increase performance * fix: update announcement links to use the correct domain * feat: add favicon field to Link model + update packages + bug fix * feat: implement favicon fetching API and update Link model for favicon support * feat: add priority attribute to Image components in Sidebar * Refactor pages to use consistent layout handling (yes, I forgot to do that until now :P) * bump version * Refactor setting pages to use consistent layout handling * upgrade yarn to 4.12.0 * fix DnD bug * Enhance announcement handling by adding support for announcement messages * slimmed down the docker image size * update Node and yarn versions in playwright tests workflow * small fix * fix attempt 2 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
591 lines
16 KiB
TypeScript
591 lines
16 KiB
TypeScript
import {
|
|
useInfiniteQuery,
|
|
useQueryClient,
|
|
useMutation,
|
|
useQuery,
|
|
} from "@tanstack/react-query";
|
|
import { useMemo } from "react";
|
|
import {
|
|
ArchivedFormat,
|
|
LinkIncludingShortenedCollectionAndTags,
|
|
LinkRequestQuery,
|
|
MobileAuth,
|
|
} from "@linkwarden/types";
|
|
import { useSession } from "next-auth/react";
|
|
import {
|
|
LinkArchiveActionSchemaType,
|
|
PostLinkSchemaType,
|
|
} from "@linkwarden/lib/schemaValidation";
|
|
import getFormatFromContentType from "@linkwarden/lib/getFormatFromContentType";
|
|
import getLinkTypeFromFormat from "@linkwarden/lib/getLinkTypeFromFormat";
|
|
|
|
const useLinks = (params: LinkRequestQuery = {}, auth?: MobileAuth) => {
|
|
const sort =
|
|
params.sort ??
|
|
(typeof window !== "undefined"
|
|
? Number(window.localStorage.getItem("sortBy"))
|
|
: 0) ??
|
|
0;
|
|
|
|
const queryString = useMemo(() => {
|
|
return buildQueryString({
|
|
sort,
|
|
collectionId: params.collectionId,
|
|
tagId: params.tagId,
|
|
pinnedOnly: params.pinnedOnly ?? undefined,
|
|
searchQueryString: params.searchQueryString,
|
|
});
|
|
}, [
|
|
sort,
|
|
params.collectionId,
|
|
params.tagId,
|
|
params.pinnedOnly,
|
|
params.searchQueryString,
|
|
]);
|
|
|
|
const query = useFetchLinks(queryString, auth);
|
|
|
|
const links = useMemo(() => {
|
|
return query.data?.pages?.flatMap((p) => p.links ?? []) ?? [];
|
|
}, [query.dataUpdatedAt]);
|
|
|
|
return {
|
|
links,
|
|
data: query,
|
|
};
|
|
};
|
|
|
|
const useFetchLinks = (params: string, auth?: MobileAuth) => {
|
|
let status: "loading" | "authenticated" | "unauthenticated";
|
|
|
|
if (!auth) {
|
|
const session = useSession();
|
|
status = session.status;
|
|
} else {
|
|
status = auth?.status;
|
|
}
|
|
|
|
return useInfiniteQuery({
|
|
queryKey: ["links", { params }],
|
|
queryFn: async (params) => {
|
|
const url =
|
|
(auth?.instance ? auth?.instance : "") +
|
|
"/api/v1/search?cursor=" +
|
|
params.pageParam +
|
|
((params.queryKey[1] as any).params
|
|
? "&" + (params.queryKey[1] as any).params
|
|
: "");
|
|
const response = await fetch(
|
|
url,
|
|
auth?.session
|
|
? {
|
|
headers: {
|
|
Authorization: `Bearer ${auth.session}`,
|
|
},
|
|
}
|
|
: undefined
|
|
);
|
|
const data = await response.json();
|
|
|
|
return {
|
|
links: data.data.links as LinkIncludingShortenedCollectionAndTags[],
|
|
nextCursor: data.data.nextCursor as number | null,
|
|
};
|
|
},
|
|
initialPageParam: 0,
|
|
refetchOnWindowFocus: false,
|
|
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
|
|
enabled: status === "authenticated",
|
|
});
|
|
};
|
|
|
|
const buildQueryString = (params: LinkRequestQuery) => {
|
|
return Object.keys(params)
|
|
.filter((key) => params[key as keyof LinkRequestQuery] !== undefined)
|
|
.map(
|
|
(key) =>
|
|
`${encodeURIComponent(key)}=${encodeURIComponent(
|
|
params[key as keyof LinkRequestQuery] as string
|
|
)}`
|
|
)
|
|
.join("&");
|
|
};
|
|
|
|
const useAddLink = (auth?: MobileAuth) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (link: PostLinkSchemaType) => {
|
|
if (link.url || link.type === "url") {
|
|
try {
|
|
new URL(link.url || "");
|
|
} catch (error) {
|
|
throw new Error("invalid_url_guide");
|
|
}
|
|
}
|
|
|
|
const response = await fetch(
|
|
(auth?.instance ? auth?.instance : "") + "/api/v1/links",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(auth?.session
|
|
? { Authorization: `Bearer ${auth.session}` }
|
|
: {}),
|
|
},
|
|
body: JSON.stringify(link),
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return data.response;
|
|
},
|
|
onSuccess: (data: LinkIncludingShortenedCollectionAndTags[]) => {
|
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
|
if (!oldData) return undefined;
|
|
return {
|
|
pages: [
|
|
{
|
|
links: [data, ...oldData?.pages?.[0]?.links],
|
|
nextCursor: oldData?.pages?.[0]?.nextCursor,
|
|
},
|
|
...oldData?.pages?.slice(1),
|
|
],
|
|
pageParams: oldData?.pageParams,
|
|
};
|
|
});
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useUpdateLink = (auth?: MobileAuth) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (link: LinkIncludingShortenedCollectionAndTags) => {
|
|
const response = await fetch(
|
|
(auth?.instance ? auth?.instance : "") + `/api/v1/links/${link.id}`,
|
|
{
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(auth?.session
|
|
? { Authorization: `Bearer ${auth.session}` }
|
|
: {}),
|
|
},
|
|
body: JSON.stringify(link),
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return data.response;
|
|
},
|
|
onSuccess: (data) => {
|
|
queryClient.invalidateQueries({ queryKey: ["links"] });
|
|
queryClient.invalidateQueries({ queryKey: ["link", data.id] });
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useDeleteLink = (auth?: MobileAuth) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: number) => {
|
|
const response = await fetch(
|
|
(auth?.instance ? auth?.instance : "") + `/api/v1/links/${id}`,
|
|
{
|
|
method: "DELETE",
|
|
headers: {
|
|
...(auth?.session
|
|
? { Authorization: `Bearer ${auth.session}` }
|
|
: {}),
|
|
},
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return data.response;
|
|
},
|
|
onSuccess: (data) => {
|
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
|
if (!oldData?.pages?.[0]) return undefined;
|
|
|
|
return {
|
|
pages: oldData.pages.map((page: any) => ({
|
|
links: page.links.filter((item: any) => item.id !== data.id),
|
|
nextCursor: page.nextCursor,
|
|
})),
|
|
pageParams: oldData.pageParams,
|
|
};
|
|
});
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
type UseGetLinkParams = {
|
|
id: number;
|
|
isPublicRoute?: boolean;
|
|
auth?: MobileAuth;
|
|
enabled?: boolean;
|
|
};
|
|
|
|
const useGetLink = ({
|
|
id,
|
|
isPublicRoute = false,
|
|
auth,
|
|
enabled = false,
|
|
}: UseGetLinkParams) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useQuery(
|
|
// unique key for this link
|
|
{
|
|
queryKey: ["link", id, isPublicRoute],
|
|
placeholderData: {} as LinkIncludingShortenedCollectionAndTags,
|
|
enabled: id != null && enabled,
|
|
queryFn: async () => {
|
|
const base = auth?.instance ?? "";
|
|
const route = isPublicRoute
|
|
? `/api/v1/public/links/${id}`
|
|
: `/api/v1/links/${id}`;
|
|
const url = `${base}${route}`;
|
|
|
|
const res = await fetch(
|
|
url,
|
|
auth?.session
|
|
? {
|
|
headers: { Authorization: `Bearer ${auth.session}` },
|
|
}
|
|
: undefined
|
|
);
|
|
const payload = await res.json();
|
|
if (!res.ok) {
|
|
throw new Error(payload.response ?? "Failed to fetch link");
|
|
}
|
|
|
|
const data =
|
|
payload.response as LinkIncludingShortenedCollectionAndTags;
|
|
|
|
// update dashboardData.links
|
|
queryClient.setQueryData(["dashboardData"], (old: any) => {
|
|
if (!old?.links) return old;
|
|
return {
|
|
...old,
|
|
links: old.links.map((l: any) => (l.id === data.id ? data : l)),
|
|
};
|
|
});
|
|
|
|
// update paginated "links"
|
|
queryClient.setQueriesData({ queryKey: ["links"] }, (old: any) => {
|
|
if (!old?.pages) return old;
|
|
return {
|
|
...old,
|
|
pages: old.pages.map((page: any) => ({
|
|
...page,
|
|
links: page.links.map((l: any) => (l.id === data.id ? data : l)),
|
|
})),
|
|
};
|
|
});
|
|
|
|
// update paginated "publicLinks"
|
|
queryClient.setQueriesData(
|
|
{ queryKey: ["publicLinks"] },
|
|
(old: any) => {
|
|
if (!old?.pages) return old;
|
|
return {
|
|
...old,
|
|
pages: old.pages.map((page: any) => ({
|
|
...page,
|
|
links: page.links.map((l: any) =>
|
|
l.id === data.id ? data : l
|
|
),
|
|
})),
|
|
};
|
|
}
|
|
);
|
|
|
|
return data;
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
const useBulkDeleteLinks = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (linkIds: number[]) => {
|
|
const response = await fetch("/api/v1/links", {
|
|
method: "DELETE",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ linkIds }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return linkIds;
|
|
},
|
|
onSuccess: (data) => {
|
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
|
if (!oldData) return undefined;
|
|
return {
|
|
pages: oldData.pages.map((page: any) => ({
|
|
links: page.links.filter((item: any) => !data.includes(item.id)),
|
|
nextCursor: page.nextCursor,
|
|
})),
|
|
pageParams: oldData.pageParams,
|
|
};
|
|
});
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useUploadFile = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ link, file }: any) => {
|
|
let format = getFormatFromContentType(file?.type);
|
|
let linkType = getLinkTypeFromFormat(format);
|
|
|
|
const response = await fetch("/api/v1/links", {
|
|
body: JSON.stringify({
|
|
...link,
|
|
type: linkType,
|
|
name: link.name ? link.name : file.name,
|
|
}),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
method: "POST",
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
if (response.ok) {
|
|
const formBody = new FormData();
|
|
file && formBody.append("file", file);
|
|
|
|
await fetch(
|
|
`/api/v1/archives/${(data as any).response.id}?format=${format}`,
|
|
{
|
|
body: formBody,
|
|
method: "POST",
|
|
}
|
|
);
|
|
}
|
|
|
|
return data.response;
|
|
},
|
|
onSuccess: (data) => {
|
|
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
|
|
if (!oldData?.links) return undefined;
|
|
return {
|
|
...oldData,
|
|
links: [data, ...oldData?.links],
|
|
};
|
|
});
|
|
|
|
queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
|
|
if (!oldData) return undefined;
|
|
return {
|
|
pages: [
|
|
{
|
|
links: [data, ...oldData?.pages?.[0]?.links],
|
|
nextCursor: oldData?.pages?.[0].nextCursor,
|
|
},
|
|
...oldData?.pages?.slice(1),
|
|
],
|
|
pageParams: oldData?.pageParams,
|
|
};
|
|
});
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useUpdateFile = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
linkId,
|
|
file,
|
|
isPreview,
|
|
}: {
|
|
linkId: number;
|
|
file: File;
|
|
isPreview?: boolean;
|
|
}) => {
|
|
const formBody = new FormData();
|
|
|
|
let format = getFormatFromContentType(file?.type);
|
|
|
|
if (isPreview) format = ArchivedFormat.jpeg;
|
|
|
|
if (!linkId || !file)
|
|
throw new Error("Error generating preview: Invalid parameters");
|
|
|
|
formBody.append("file", file);
|
|
|
|
const res = await fetch(
|
|
`/api/v1/archives/${linkId}?format=` +
|
|
format +
|
|
(isPreview ? "&preview=true" : ""),
|
|
{
|
|
body: formBody,
|
|
method: "POST",
|
|
}
|
|
);
|
|
|
|
const data = res.json();
|
|
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["links"] });
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useBulkEditLinks = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
links,
|
|
newData,
|
|
removePreviousTags,
|
|
}: {
|
|
links: Pick<LinkIncludingShortenedCollectionAndTags, "id">[];
|
|
newData: Pick<
|
|
LinkIncludingShortenedCollectionAndTags,
|
|
"tags" | "collectionId"
|
|
>;
|
|
removePreviousTags: boolean;
|
|
}) => {
|
|
const response = await fetch("/api/v1/links", {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ links, newData, removePreviousTags }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return data.response;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["links"] });
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
queryClient.invalidateQueries({ queryKey: ["collections"] });
|
|
queryClient.invalidateQueries({ queryKey: ["tags"] });
|
|
queryClient.invalidateQueries({ queryKey: ["publicLinks"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const useArchiveAction = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (payload: LinkArchiveActionSchemaType) => {
|
|
const response = await fetch("/api/v1/links/archive", {
|
|
body: JSON.stringify({
|
|
action: payload.action,
|
|
linkIds: payload.linkIds,
|
|
}),
|
|
method: "DELETE",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!response.ok) throw new Error(data.response);
|
|
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["links"] });
|
|
queryClient.invalidateQueries({ queryKey: ["dashboardData"] });
|
|
},
|
|
});
|
|
};
|
|
|
|
const resetInfiniteQueryPagination = async (
|
|
queryClient: any,
|
|
queryKey: any
|
|
) => {
|
|
queryClient.setQueriesData({ queryKey }, (oldData: any) => {
|
|
if (!oldData) return undefined;
|
|
|
|
return {
|
|
pages: oldData.pages.slice(0, 1),
|
|
pageParams: oldData.pageParams.slice(0, 1),
|
|
};
|
|
});
|
|
|
|
await queryClient.invalidateQueries(queryKey);
|
|
};
|
|
|
|
export {
|
|
useLinks,
|
|
useAddLink,
|
|
useUpdateLink,
|
|
useDeleteLink,
|
|
useBulkDeleteLinks,
|
|
useUploadFile,
|
|
useGetLink,
|
|
useBulkEditLinks,
|
|
useArchiveAction,
|
|
resetInfiniteQueryPagination,
|
|
useUpdateFile,
|
|
};
|