+
{previewAvailable(link) ? (
{
@@ -167,9 +183,13 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
}}
/>
) : link.preview === "unavailable" ? (
-
+
) : (
-
+
)}
{show.icon && (
@@ -184,7 +204,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
{show.name && (
-
+
{unescapeString(link.name)}
)}
@@ -209,11 +229,14 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
+ {/* Overlay on hover */}
+
);
diff --git a/components/LinkViews/LinkComponents/LinkList.tsx b/components/LinkViews/LinkComponents/LinkList.tsx
index 90baa8bc..4433fdc6 100644
--- a/components/LinkViews/LinkComponents/LinkList.tsx
+++ b/components/LinkViews/LinkComponents/LinkList.tsx
@@ -24,15 +24,10 @@ type Props = {
link: LinkIncludingShortenedCollectionAndTags;
count: number;
className?: string;
- flipDropdown?: boolean;
editMode?: boolean;
};
-export default function LinkCardCompact({
- link,
- flipDropdown,
- editMode,
-}: Props) {
+export default function LinkCardCompact({ link, editMode }: Props) {
const { t } = useTranslation();
const { data: collections = [] } = useCollections();
@@ -142,8 +137,7 @@ export default function LinkCardCompact({
diff --git a/components/LinkViews/LinkComponents/LinkMasonry.tsx b/components/LinkViews/LinkComponents/LinkMasonry.tsx
index 8da4f255..6e51b641 100644
--- a/components/LinkViews/LinkComponents/LinkMasonry.tsx
+++ b/components/LinkViews/LinkComponents/LinkMasonry.tsx
@@ -3,7 +3,7 @@ import {
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
import useLinkStore from "@/store/links";
import unescapeString from "@/lib/client/unescapeString";
import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions";
@@ -27,15 +27,29 @@ import clsx from "clsx";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
- count: number;
- className?: string;
- flipDropdown?: boolean;
+ columns: number;
editMode?: boolean;
};
-export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
+export default function LinkMasonry({ link, editMode, columns }: Props) {
const { t } = useTranslation();
+ const heightMap = {
+ 1: "h-48",
+ 2: "h-44",
+ 3: "h-40",
+ 4: "h-36",
+ 5: "h-32",
+ 6: "h-28",
+ 7: "h-24",
+ 8: "h-20",
+ };
+
+ const imageHeightClass = useMemo(
+ () => (columns ? heightMap[columns as keyof typeof heightMap] : "h-40"),
+ [columns]
+ );
+
const { data: collections = [] } = useCollections();
const { data: user = {} } = useUser();
@@ -126,7 +140,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
return (
selectable
? handleCheckboxClick(link)
@@ -150,7 +164,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
width={1280}
height={720}
alt=""
- className="rounded-t-2xl select-none object-cover z-10 h-40 w-full shadow opacity-80 scale-105"
+ className={`rounded-t-2xl select-none object-cover z-10 ${imageHeightClass} w-full shadow opacity-80 scale-105`}
style={show.icon ? { filter: "blur(1px)" } : undefined}
draggable="false"
onError={(e) => {
@@ -159,7 +173,9 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
}}
/>
) : link.preview === "unavailable" ? null : (
-
+
)}
{show.icon && (
@@ -174,7 +190,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
{show.name && (
-
+
{unescapeString(link.name)}
)}
@@ -182,12 +198,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
{show.link &&
}
{show.description && link.description && (
-
+
{unescapeString(link.description)}
)}
@@ -226,15 +237,14 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
)}
+ {/* Overlay on hover */}
+
);
diff --git a/components/LinkViews/Links.tsx b/components/LinkViews/Links.tsx
index c8de1e5d..c0253ed3 100644
--- a/components/LinkViews/Links.tsx
+++ b/components/LinkViews/Links.tsx
@@ -3,7 +3,7 @@ import {
LinkIncludingShortenedCollectionAndTags,
ViewMode,
} from "@/types/global";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
import { useInView } from "react-intersection-observer";
import LinkMasonry from "@/components/LinkViews/LinkComponents/LinkMasonry";
import Masonry from "react-masonry-css";
@@ -11,6 +11,7 @@ import resolveConfig from "tailwindcss/resolveConfig";
import tailwindConfig from "../../tailwind.config.js";
import { useMemo } from "react";
import LinkList from "@/components/LinkViews/LinkComponents/LinkList";
+import useLocalSettingsStore from "@/store/localSettings";
export function CardView({
links,
@@ -27,16 +28,68 @@ export function CardView({
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]
+ );
+
+ 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 (
-
+
{links?.map((e, i) => {
return (
);
})}
@@ -76,6 +129,58 @@ export function MasonryView({
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]
+ );
+
+ 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 fullConfig = resolveConfig(tailwindConfig as any);
const breakpointColumnsObj = useMemo(() => {
@@ -90,18 +195,19 @@ export function MasonryView({
return (
{links?.map((e, i) => {
return (
);
})}
@@ -144,15 +250,7 @@ export function ListView({
return (
{links?.map((e, i) => {
- return (
-
- );
+ return ;
})}
{(hasNextPage || isLoading) &&
diff --git a/components/ReadableView.tsx b/components/ReadableView.tsx
index 942b66c4..6a4e3429 100644
--- a/components/ReadableView.tsx
+++ b/components/ReadableView.tsx
@@ -3,7 +3,6 @@ import { readabilityAvailable } from "@/lib/shared/getArchiveValidity";
import isValidUrl from "@/lib/shared/isValidUrl";
import {
ArchivedFormat,
- CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import ColorThief, { RGBColor } from "colorthief";
@@ -11,10 +10,8 @@ import DOMPurify from "dompurify";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
-import React, { useEffect, useMemo, useState } from "react";
-import LinkActions from "./LinkViews/LinkComponents/LinkActions";
+import React, { useEffect, useState } from "react";
import { useTranslation } from "next-i18next";
-import { useCollections } from "@/hooks/store/collections";
import { useGetLink } from "@/hooks/store/links";
import { IconWeight } from "@phosphor-icons/react";
import Icon from "./Icon";