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 className="ml-4 flex flex-col justify-center">
|
||||
<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">
|
||||
{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>
|
||||
<p className="font-thin text-5xl text-primary mt-0.5">{value || 0}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,12 +7,12 @@ import usePermissions from "@/hooks/usePermissions";
|
|||
import DeleteLinkModal from "@/components/ModalContent/DeleteLinkModal";
|
||||
import { dropdownTriggerer } from "@/lib/client/utils";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
import { useDeleteLink, useGetLink, useUpdateLink } from "@/hooks/store/links";
|
||||
import { useDeleteLink, useGetLink } from "@/hooks/store/links";
|
||||
import toast from "react-hot-toast";
|
||||
import LinkModal from "@/components/ModalContent/LinkModal";
|
||||
import { useRouter } from "next/router";
|
||||
import clsx from "clsx";
|
||||
import usePinLink from "@/lib/client/pinLink";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -27,41 +27,14 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
const permissions = usePermissions(link.collection.id as number);
|
||||
const getLink = useGetLink();
|
||||
|
||||
const pinLink = usePinLink();
|
||||
|
||||
const [editLinkModal, setEditLinkModal] = useState(false);
|
||||
const [linkModal, setLinkModal] = useState(false);
|
||||
const [deleteLinkModal, setDeleteLinkModal] = useState(false);
|
||||
|
||||
const { data: user = {} } = useUser();
|
||||
|
||||
const updateLink = useUpdateLink();
|
||||
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 load = toast.loading(t("sending_request"));
|
||||
|
||||
|
@ -103,7 +76,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={`dropdown dropdown-left absolute ${
|
||||
className={`dropdown dropdown-end absolute ${
|
||||
className || "top-3 right-3"
|
||||
} z-20`}
|
||||
>
|
||||
|
@ -117,7 +90,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
</div>
|
||||
<ul
|
||||
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>
|
||||
|
@ -126,7 +99,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
pinLink();
|
||||
pinLink(link);
|
||||
}}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
|
@ -216,7 +189,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
{editLinkModal && (
|
||||
<LinkModal
|
||||
onClose={() => setEditLinkModal(false)}
|
||||
onPin={pinLink}
|
||||
onPin={() => pinLink(link)}
|
||||
onUpdateArchive={updateArchive}
|
||||
onDelete={() => setDeleteLinkModal(true)}
|
||||
link={link}
|
||||
|
@ -232,7 +205,7 @@ export default function LinkActions({ link, className, btnStyle }: Props) {
|
|||
{linkModal && (
|
||||
<LinkModal
|
||||
onClose={() => setLinkModal(false)}
|
||||
onPin={pinLink}
|
||||
onPin={() => pinLink(link)}
|
||||
onUpdateArchive={updateArchive}
|
||||
onDelete={() => setDeleteLinkModal(true)}
|
||||
link={link}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { useUser } from "@/hooks/store/user";
|
|||
import { useGetLink, useLinks } from "@/hooks/store/links";
|
||||
import { useRouter } from "next/router";
|
||||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
import LinkPin from "./LinkPin";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -36,13 +37,13 @@ export default function LinkCard({ link, columns, editMode }: 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",
|
||||
1: "h-44",
|
||||
2: "h-40",
|
||||
3: "h-36",
|
||||
4: "h-32",
|
||||
5: "h-28",
|
||||
6: "h-24",
|
||||
7: "h-20",
|
||||
8: "h-20",
|
||||
};
|
||||
|
||||
|
@ -216,7 +217,7 @@ export default function LinkCard({ link, columns, editMode }: Props) {
|
|||
<div>
|
||||
<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 && (
|
||||
<div className="cursor-pointer truncate">
|
||||
<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"
|
||||
}
|
||||
/>
|
||||
<LinkPin
|
||||
link={link}
|
||||
className="absolute top-3 right-[3.25rem] group-hover:opacity-100 group-focus-within:opacity-100 opacity-0 duration-100"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,13 +35,13 @@ 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",
|
||||
1: "h-44",
|
||||
2: "h-40",
|
||||
3: "h-36",
|
||||
4: "h-32",
|
||||
5: "h-28",
|
||||
6: "h-24",
|
||||
7: "h-20",
|
||||
8: "h-20",
|
||||
};
|
||||
|
||||
|
@ -225,7 +225,7 @@ export default function LinkMasonry({ link, editMode, columns }: Props) {
|
|||
<div>
|
||||
<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 && (
|
||||
<div className="cursor-pointer truncate">
|
||||
<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"
|
||||
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>
|
||||
|
|
|
@ -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
|
||||
// );
|
||||
// });
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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 } },
|
||||
},
|
||||
|
|
|
@ -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 { 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<HTMLInputElement>,
|
||||
|
@ -119,32 +112,30 @@ export default function Dashboard() {
|
|||
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<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
|
||||
name={numberOfLinks === 1 ? t("link") : t("links")}
|
||||
value={numberOfLinks}
|
||||
icon={"bi-link-45deg"}
|
||||
/>
|
||||
<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">
|
||||
<DashboardItem
|
||||
name={numberOfLinks === 1 ? t("link") : t("links")}
|
||||
value={numberOfLinks}
|
||||
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
|
||||
name={
|
||||
collections.length === 1 ? t("collection") : t("collections")
|
||||
}
|
||||
value={collections.length}
|
||||
icon={"bi-folder"}
|
||||
/>
|
||||
<DashboardItem
|
||||
name={tags.length === 1 ? t("tag") : t("tags")}
|
||||
value={tags.length}
|
||||
icon={"bi-hash"}
|
||||
/>
|
||||
|
||||
<div className="divider m-0"></div>
|
||||
|
||||
<DashboardItem
|
||||
name={tags.length === 1 ? t("tag") : t("tags")}
|
||||
value={tags.length}
|
||||
icon={"bi-hash"}
|
||||
/>
|
||||
</div>
|
||||
<DashboardItem
|
||||
name={t("pinned")}
|
||||
value={numberOfPinnedLinks}
|
||||
icon={"bi-pin-angle"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
|
@ -174,13 +165,19 @@ export default function Dashboard() {
|
|||
<div className="w-full">
|
||||
<Links
|
||||
layout={viewMode}
|
||||
placeholderCount={showLinks / 2}
|
||||
placeholderCount={settings.columns || 1}
|
||||
useData={dashboardData}
|
||||
/>
|
||||
</div>
|
||||
) : links && links[0] && !dashboardData.isLoading ? (
|
||||
<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 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">
|
||||
<Links
|
||||
layout={viewMode}
|
||||
placeholderCount={showLinks / 2}
|
||||
placeholderCount={settings.columns || 1}
|
||||
useData={dashboardData}
|
||||
/>
|
||||
</div>
|
||||
|
@ -320,7 +317,12 @@ export default function Dashboard() {
|
|||
<Links
|
||||
links={links
|
||||
.filter((e: any) => e.pinnedBy && e.pinnedBy[0])
|
||||
.slice(0, showLinks)}
|
||||
.slice(
|
||||
0,
|
||||
settings.columns
|
||||
? settings.columns * 2
|
||||
: numberOfLinksToShow
|
||||
)}
|
||||
layout={viewMode}
|
||||
/>
|
||||
</div>
|
||||
|
|
Ŝarĝante…
Reference in New Issue