Files
linkwarden/apps/web/components/LinkViews/Links.tsx
Daniel 389e5df117 Chore/tech debts (#1536)
* 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>
2025-12-21 18:09:05 -05:00

485 lines
12 KiB
TypeScript

import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard";
import {
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
ViewMode,
} from "@linkwarden/types";
import { useEffect, useState } from "react";
import { useInView } from "react-intersection-observer";
import LinkMasonry from "@/components/LinkViews/LinkComponents/LinkMasonry";
import Masonry from "react-masonry-css";
import { useMemo } from "react";
import LinkList from "@/components/LinkViews/LinkComponents/LinkList";
import useLocalSettingsStore from "@/store/localSettings";
import { useCollections } from "@linkwarden/router/collections";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
import { TFunction } from "i18next";
import useLinkStore from "@/store/links";
import useMediaQuery from "@/hooks/useMediaQuery";
import { useUser } from "@linkwarden/router/user";
function CardView({
links,
collectionsById,
isPublicRoute,
t,
user,
disableDraggable,
isSelected,
toggleSelected,
editMode,
isLoading,
hasNextPage,
placeHolderRef,
}: {
links: LinkIncludingShortenedCollectionAndTags[];
collectionsById: Map<number, CollectionIncludingMembersAndLinkCount>;
isPublicRoute: boolean;
t: TFunction<"translation", undefined>;
user: any;
disableDraggable: boolean;
isSelected: (id: number) => boolean;
toggleSelected: (id: number) => void;
editMode: boolean;
isLoading: boolean;
hasNextPage: boolean;
placeHolderRef: any;
}) {
const settings = useLocalSettingsStore((state) => state.settings);
const gridMap = {
1: "grid-cols-1",
2: "grid-cols-2",
3: "grid-cols-3",
4: "grid-cols-4",
5: "grid-cols-5",
6: "grid-cols-6",
7: "grid-cols-7",
8: "grid-cols-8",
};
const getColumnCount = () => {
const width = window.innerWidth;
if (width >= 1901) return 5;
if (width >= 1501) return 4;
if (width >= 881) return 3;
if (width >= 551) return 2;
return 1;
};
const [columnCount, setColumnCount] = useState(
settings.columns || getColumnCount()
);
const gridColClass = useMemo(
() => gridMap[columnCount as keyof typeof gridMap],
[columnCount]
);
const heightMap = {
1: "h-44",
2: "h-40",
3: "h-36",
4: "h-32",
5: "h-28",
6: "h-24",
7: "h-20",
8: "h-20",
};
const imageHeightClass = useMemo(
() =>
columnCount ? heightMap[columnCount as keyof typeof heightMap] : "h-40",
[columnCount]
);
useEffect(() => {
const handleResize = () => {
if (settings.columns === 0) {
// Only recalculate if zustandColumns is zero
setColumnCount(getColumnCount());
}
};
if (settings.columns === 0) {
window.addEventListener("resize", handleResize);
}
setColumnCount(settings.columns || getColumnCount());
return () => {
if (settings.columns === 0) {
window.removeEventListener("resize", handleResize);
}
};
}, [settings.columns]);
return (
<div className={`${gridColClass} grid gap-5 pb-5`}>
{links?.map((e) => {
const collection = collectionsById.get(e.collection.id as number);
const selected = isSelected(e.id as number);
return (
<LinkCard
key={e.id}
link={e}
collection={collection as CollectionIncludingMembersAndLinkCount}
isPublicRoute={isPublicRoute}
t={t}
user={user}
disableDraggable={disableDraggable}
isSelected={selected}
toggleSelected={toggleSelected}
editMode={editMode}
imageHeightClass={imageHeightClass}
/>
);
})}
{(hasNextPage || isLoading) && (
<div className="flex flex-col gap-4" ref={placeHolderRef}>
<div className="skeleton h-40 w-full"></div>
<div className="skeleton h-3 w-2/3"></div>
<div className="skeleton h-3 w-full"></div>
<div className="skeleton h-3 w-full"></div>
<div className="skeleton h-3 w-1/3"></div>
</div>
)}
</div>
);
}
function MasonryView({
links,
collectionsById,
isPublicRoute,
t,
disableDraggable,
user,
isSelected,
toggleSelected,
editMode,
isLoading,
hasNextPage,
placeHolderRef,
}: {
links: LinkIncludingShortenedCollectionAndTags[];
collectionsById: Map<number, CollectionIncludingMembersAndLinkCount>;
isPublicRoute: boolean;
t: TFunction<"translation", undefined>;
disableDraggable: boolean;
user: any;
isSelected: (id: number) => boolean;
toggleSelected: (id: number) => void;
editMode: boolean;
isLoading: boolean;
hasNextPage: boolean;
placeHolderRef: any;
}) {
const settings = useLocalSettingsStore((state) => state.settings);
const gridMap = {
1: "grid-cols-1",
2: "grid-cols-2",
3: "grid-cols-3",
4: "grid-cols-4",
5: "grid-cols-5",
6: "grid-cols-6",
7: "grid-cols-7",
8: "grid-cols-8",
};
const getColumnCount = () => {
const width = window.innerWidth;
if (width >= 1901) return 5;
if (width >= 1501) return 4;
if (width >= 881) return 3;
if (width >= 551) return 2;
return 1;
};
const [columnCount, setColumnCount] = useState(
settings.columns || getColumnCount()
);
const gridColClass = useMemo(
() => gridMap[columnCount as keyof typeof gridMap],
[columnCount]
);
const heightMap = {
1: "h-44",
2: "h-40",
3: "h-36",
4: "h-32",
5: "h-28",
6: "h-24",
7: "h-20",
8: "h-20",
};
const imageHeightClass = useMemo(
() =>
columnCount ? heightMap[columnCount as keyof typeof heightMap] : "h-40",
[columnCount]
);
useEffect(() => {
const handleResize = () => {
if (settings.columns === 0) {
// Only recalculate if zustandColumns is zero
setColumnCount(getColumnCount());
}
};
if (settings.columns === 0) {
window.addEventListener("resize", handleResize);
}
setColumnCount(settings.columns || getColumnCount());
return () => {
if (settings.columns === 0) {
window.removeEventListener("resize", handleResize);
}
};
}, [settings.columns]);
const breakpointColumnsObj = { default: 5, 1900: 4, 1500: 3, 880: 2, 550: 1 };
return (
<Masonry
breakpointCols={
settings.columns === 0 ? breakpointColumnsObj : columnCount
}
columnClassName="flex flex-col gap-5 !w-full"
className={`${gridColClass} grid gap-5 pb-5`}
>
{links?.map((e) => {
const collection = collectionsById.get(e.collection.id as number);
const selected = isSelected(e.id as number);
return (
<LinkMasonry
key={e.id}
link={e}
collection={collection as CollectionIncludingMembersAndLinkCount}
isPublicRoute={isPublicRoute}
t={t}
disableDraggable={disableDraggable}
user={user}
isSelected={selected}
toggleSelected={toggleSelected}
imageHeightClass={imageHeightClass}
editMode={editMode}
/>
);
})}
{(hasNextPage || isLoading) && (
<div className="flex flex-col gap-4" ref={placeHolderRef}>
<div className="skeleton h-40 w-full"></div>
<div className="skeleton h-3 w-2/3"></div>
<div className="skeleton h-3 w-full"></div>
<div className="skeleton h-3 w-full"></div>
<div className="skeleton h-3 w-1/3"></div>
</div>
)}
</Masonry>
);
}
function ListView({
links,
collectionsById,
isPublicRoute,
t,
disableDraggable,
user,
isSelected,
toggleSelected,
editMode,
isLoading,
hasNextPage,
placeHolderRef,
}: {
links: LinkIncludingShortenedCollectionAndTags[];
collectionsById: Map<number, CollectionIncludingMembersAndLinkCount>;
isPublicRoute: boolean;
t: TFunction<"translation", undefined>;
disableDraggable: boolean;
user: any;
isSelected: (id: number) => boolean;
toggleSelected: (id: number) => void;
editMode: boolean;
isLoading: boolean;
hasNextPage: boolean;
placeHolderRef: any;
}) {
return (
<div className="flex flex-col">
{links?.map((e, i) => {
const collection = collectionsById.get(e.collection.id as number);
const selected = isSelected(e.id as number);
return (
<LinkList
key={e.id}
link={e}
collection={collection as CollectionIncludingMembersAndLinkCount}
isPublicRoute={isPublicRoute}
t={t}
disableDraggable={disableDraggable}
user={user}
isSelected={selected}
toggleSelected={toggleSelected}
count={i}
editMode={editMode}
/>
);
})}
{(hasNextPage || isLoading) && (
<div ref={placeHolderRef} className="flex gap-2 py-2 px-1">
<div className="skeleton h-12 w-12"></div>
<div className="flex flex-col gap-3 w-full">
<div className="skeleton h-2 w-2/3"></div>
<div className="skeleton h-2 w-full"></div>
<div className="skeleton h-2 w-1/3"></div>
</div>
</div>
)}
</div>
);
}
export default function Links({
layout,
links,
editMode,
useData,
}: {
layout: ViewMode;
links?: LinkIncludingShortenedCollectionAndTags[];
editMode?: boolean;
useData?: any;
}) {
const { ref, inView } = useInView();
const { t } = useTranslation();
const router = useRouter();
const isPublicRoute = router.pathname.startsWith("/public") ? true : false;
useEffect(() => {
if (!inView) return;
if (!useData.hasNextPage) return;
if (useData.isFetchingNextPage) return;
useData.fetchNextPage();
}, [
inView,
useData.hasNextPage,
useData.isFetchingNextPage,
useData.fetchNextPage,
]);
const { data: collections = [] } = useCollections();
const collectionsById = useMemo(() => {
const m = new Map<number, (typeof collections)[number]>();
for (const c of collections) m.set(c.id as any, c);
return m;
}, [collections]);
const { clearSelected, isSelected, toggleSelected } = useLinkStore();
useEffect(() => {
if (!editMode) {
clearSelected();
}
}, [editMode]);
useEffect(() => {
let interval: NodeJS.Timeout | null = null;
if (
links?.some(
(e) => !e.preview?.startsWith("archives") && e.preview !== "unavailable"
)
) {
interval = setInterval(async () => {
useData.refetch().catch((error: any) => {
console.error("Error refetching link:", error);
});
}, 5000);
}
return () => {
if (interval) {
clearInterval(interval);
}
};
}, [links]);
const disableDraggable = useMediaQuery("(max-width: 1023px)");
const { data: user } = useUser();
if (layout === ViewMode.List) {
return (
<ListView
links={links || []}
collectionsById={collectionsById}
isPublicRoute={isPublicRoute}
t={t}
disableDraggable={disableDraggable}
user={user}
toggleSelected={toggleSelected}
isSelected={isSelected}
editMode={editMode || false}
isLoading={useData?.isLoading}
hasNextPage={useData?.hasNextPage}
placeHolderRef={ref}
/>
);
} else if (layout === ViewMode.Masonry) {
return (
<MasonryView
links={links || []}
collectionsById={collectionsById}
isPublicRoute={isPublicRoute}
t={t}
disableDraggable={disableDraggable}
user={user}
toggleSelected={toggleSelected}
isSelected={isSelected}
editMode={editMode || false}
isLoading={useData?.isLoading}
hasNextPage={useData?.hasNextPage}
placeHolderRef={ref}
/>
);
} else {
// Default to card view
return (
<CardView
links={links || []}
collectionsById={collectionsById}
isPublicRoute={isPublicRoute}
t={t}
user={user}
disableDraggable={disableDraggable}
toggleSelected={toggleSelected}
isSelected={isSelected}
editMode={editMode || false}
isLoading={useData?.isLoading}
hasNextPage={useData?.hasNextPage}
placeHolderRef={ref}
/>
);
}
}