make tags visible on public collections
This commit is contained in:
parent
d86bbcd940
commit
2e1e94112f
|
@ -130,31 +130,6 @@ export default function EditCollectionSharingModal({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{permissions === true && collection.isPublic && (
|
|
||||||
<div>
|
|
||||||
<p>Show tags in public collection</p>
|
|
||||||
|
|
||||||
<label className="label cursor-pointer justify-start gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={collection.tagsArePublic}
|
|
||||||
onChange={() =>
|
|
||||||
setCollection({
|
|
||||||
...collection,
|
|
||||||
tagsArePublic: !collection.tagsArePublic,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
className="checkbox checkbox-primary"
|
|
||||||
/>
|
|
||||||
<span className="label-text">Show tags in public collection</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<p className="text-neutral text-sm">
|
|
||||||
This will let <b>Anyone</b> view this collections tags and search
|
|
||||||
this collections links by them.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{collection.isPublic && (
|
{collection.isPublic && (
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-2">{t("sharable_link")}</p>
|
<p className="mb-2">{t("sharable_link")}</p>
|
||||||
|
|
|
@ -84,7 +84,6 @@ export default async function updateCollection(
|
||||||
icon: data.icon,
|
icon: data.icon,
|
||||||
iconWeight: data.iconWeight,
|
iconWeight: data.iconWeight,
|
||||||
isPublic: data.isPublic,
|
isPublic: data.isPublic,
|
||||||
tagsArePublic: data.tagsArePublic,
|
|
||||||
parent:
|
parent:
|
||||||
data.parentId && data.parentId !== "root"
|
data.parentId && data.parentId !== "root"
|
||||||
? {
|
? {
|
||||||
|
|
|
@ -190,7 +190,6 @@ export const UpdateCollectionSchema = z.object({
|
||||||
isPublic: z.boolean().optional(),
|
isPublic: z.boolean().optional(),
|
||||||
icon: z.string().trim().max(50).nullish(),
|
icon: z.string().trim().max(50).nullish(),
|
||||||
iconWeight: z.string().trim().max(50).nullish(),
|
iconWeight: z.string().trim().max(50).nullish(),
|
||||||
tagsArePublic: z.boolean().optional(),
|
|
||||||
parentId: z.union([z.number(), z.literal("root")]).nullish(),
|
parentId: z.union([z.number(), z.literal("root")]).nullish(),
|
||||||
members: z.array(
|
members: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
|
|
|
@ -25,11 +25,10 @@ import { usePublicLinks } from "@/hooks/store/publicLinks";
|
||||||
import Links from "@/components/LinkViews/Links";
|
import Links from "@/components/LinkViews/Links";
|
||||||
import { Disclosure, Transition } from "@headlessui/react";
|
import { Disclosure, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
|
|
||||||
export default function PublicCollections() {
|
export default function PublicCollections() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { settings } = useLocalSettingsStore();
|
const { settings } = useLocalSettingsStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -37,22 +36,31 @@ const { settings } = useLocalSettingsStore();
|
||||||
Partial<AccountSettings>
|
Partial<AccountSettings>
|
||||||
>({});
|
>({});
|
||||||
|
|
||||||
const handleTagSelection = (tag: string | undefined) => {
|
const handleTagSelection = (tag: string | undefined) => {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
Object.keys(searchFilter).forEach((v) => searchFilter[(v as keyof {name: boolean, url: boolean, description: boolean, tags: boolean, textContent: boolean})] = false)
|
Object.keys(searchFilter).forEach(
|
||||||
|
(v) =>
|
||||||
|
(searchFilter[
|
||||||
|
v as keyof {
|
||||||
|
name: boolean;
|
||||||
|
url: boolean;
|
||||||
|
description: boolean;
|
||||||
|
tags: boolean;
|
||||||
|
textContent: boolean;
|
||||||
|
}
|
||||||
|
] = false)
|
||||||
|
);
|
||||||
searchFilter.tags = true;
|
searchFilter.tags = true;
|
||||||
return router.push(
|
return router.push(
|
||||||
"/public/collections/" +
|
"/public/collections/" +
|
||||||
router.query.id +
|
router.query.id +
|
||||||
"?q=" +
|
"?q=" +
|
||||||
encodeURIComponent(tag || "")
|
encodeURIComponent(tag || "")
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return router.push(
|
return router.push("/public/collections/" + router.query.id);
|
||||||
"/public/collections/" +
|
|
||||||
router.query.id)
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const [searchFilter, setSearchFilter] = useState({
|
const [searchFilter, setSearchFilter] = useState({
|
||||||
name: true,
|
name: true,
|
||||||
|
@ -106,18 +114,18 @@ const { settings } = useLocalSettingsStore();
|
||||||
(localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card
|
(localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card
|
||||||
);
|
);
|
||||||
|
|
||||||
const [tagDisclosure, setTagDisclosure] = useState<boolean>(() => {
|
const [tagDisclosure, setTagDisclosure] = useState<boolean>(() => {
|
||||||
const storedValue = localStorage.getItem(
|
const storedValue = localStorage.getItem(
|
||||||
"tagDisclosureForPublicCollection" + collection?.id
|
"tagDisclosureForPublicCollection" + collection?.id
|
||||||
);
|
);
|
||||||
return storedValue ? storedValue === "true" : true;
|
return storedValue ? storedValue === "true" : true;
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"tagDisclosureForPublicCollection" + collection?.id,
|
"tagDisclosureForPublicCollection" + collection?.id,
|
||||||
tagDisclosure ? "true" : "false"
|
tagDisclosure ? "true" : "false"
|
||||||
);
|
);
|
||||||
}, [tagDisclosure]);
|
}, [tagDisclosure]);
|
||||||
|
|
||||||
if (!collection) return <></>;
|
if (!collection) return <></>;
|
||||||
else
|
else
|
||||||
|
@ -243,88 +251,85 @@ useEffect(() => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</LinkListOptions>
|
</LinkListOptions>
|
||||||
{collection.tagsArePublic &&
|
{linksForWholeCollection?.flatMap((l) => l.tags)[0] && (
|
||||||
linksForWholeCollection?.flatMap((l) => l.tags)[0] && (
|
<Disclosure defaultOpen={tagDisclosure}>
|
||||||
<Disclosure defaultOpen={tagDisclosure}>
|
<Disclosure.Button
|
||||||
<Disclosure.Button
|
onClick={() => {
|
||||||
onClick={() => {
|
setTagDisclosure(!tagDisclosure);
|
||||||
setTagDisclosure(!tagDisclosure);
|
}}
|
||||||
}}
|
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
|
||||||
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
|
>
|
||||||
>
|
<p className="text-sm">{t("browse_by_topic")}</p>
|
||||||
<p className="text-sm">{t("browse_by_topic")}</p>
|
<i
|
||||||
<i
|
className={`bi-chevron-down ${
|
||||||
className={`bi-chevron-down ${
|
tagDisclosure ? "rotate-reverse" : "rotate"
|
||||||
tagDisclosure ? "rotate-reverse" : "rotate"
|
}`}
|
||||||
}`}
|
></i>
|
||||||
></i>
|
</Disclosure.Button>
|
||||||
</Disclosure.Button>
|
<Transition
|
||||||
<Transition
|
enter="transition duration-100 ease-out"
|
||||||
enter="transition duration-100 ease-out"
|
enterFrom="transform opacity-0 -translate-y-3"
|
||||||
enterFrom="transform opacity-0 -translate-y-3"
|
enterTo="transform opacity-100 translate-y-0"
|
||||||
enterTo="transform opacity-100 translate-y-0"
|
leave="transition duration-100 ease-out"
|
||||||
leave="transition duration-100 ease-out"
|
leaveFrom="transform opacity-100 translate-y-0"
|
||||||
leaveFrom="transform opacity-100 translate-y-0"
|
leaveTo="transform opacity-0 -translate-y-3"
|
||||||
leaveTo="transform opacity-0 -translate-y-3"
|
>
|
||||||
>
|
<Disclosure.Panel>
|
||||||
<Disclosure.Panel>
|
<div className="flex gap-2 mt-2 mb-6 flex-wrap">
|
||||||
<div className="flex gap-2 mt-2 mb-6 flex-wrap">
|
<button
|
||||||
<button
|
className="max-w-full"
|
||||||
className="max-w-full"
|
onClick={() => handleTagSelection(undefined)}
|
||||||
onClick={() => handleTagSelection(undefined)}
|
>
|
||||||
>
|
<div
|
||||||
<div
|
className="
|
||||||
className="
|
|
||||||
bg-neutral-content/20 hover:bg-neutral/20 duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8"
|
bg-neutral-content/20 hover:bg-neutral/20 duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8"
|
||||||
>
|
>
|
||||||
<i className="text-primary bi-hash text-2xl text-primary drop-shadow"></i>
|
<i className="text-primary bi-hash text-2xl text-primary drop-shadow"></i>
|
||||||
<p className="truncate pr-7">{t("all_links")}</p>
|
<p className="truncate pr-7">{t("all_links")}</p>
|
||||||
<div className="text-neutral drop-shadow text-neutral text-xs">
|
<div className="text-neutral drop-shadow text-neutral text-xs">
|
||||||
{collection._count?.links}
|
{collection._count?.links}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
{linksForWholeCollection
|
</button>
|
||||||
.flatMap((l) => l.tags)
|
{linksForWholeCollection
|
||||||
.map((t) => t.name)
|
.flatMap((l) => l.tags)
|
||||||
.filter(
|
.map((t) => t.name)
|
||||||
(item, pos, self) => self.indexOf(item) === pos
|
.filter((item, pos, self) => self.indexOf(item) === pos)
|
||||||
)
|
.sort((a, b) => a.localeCompare(b))
|
||||||
.sort((a, b) => a.localeCompare(b))
|
.map((e, i) => {
|
||||||
.map((e, i) => {
|
const active = router.query.q === e;
|
||||||
const active = router.query.q === e;
|
return (
|
||||||
return (
|
<button
|
||||||
<button
|
className="max-w-full"
|
||||||
className="max-w-full"
|
key={i}
|
||||||
key={i}
|
onClick={() => handleTagSelection(e)}
|
||||||
onClick={() => handleTagSelection(e)}
|
>
|
||||||
>
|
<div
|
||||||
<div
|
className={`
|
||||||
className={`
|
|
||||||
${
|
${
|
||||||
active
|
active
|
||||||
? "bg-primary/20"
|
? "bg-primary/20"
|
||||||
: "bg-neutral-content/20 hover:bg-neutral/20"
|
: "bg-neutral-content/20 hover:bg-neutral/20"
|
||||||
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8`}
|
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8`}
|
||||||
>
|
>
|
||||||
<i className="bi-hash text-2xl text-primary drop-shadow"></i>
|
<i className="bi-hash text-2xl text-primary drop-shadow"></i>
|
||||||
<p className="truncate pr-7">{e}</p>
|
<p className="truncate pr-7">{e}</p>
|
||||||
<div className="drop-shadow text-neutral text-xs">
|
<div className="drop-shadow text-neutral text-xs">
|
||||||
{
|
{
|
||||||
linksForWholeCollection.filter((l) =>
|
linksForWholeCollection.filter((l) =>
|
||||||
l.tags.map((t) => t.name).includes(e)
|
l.tags.map((t) => t.name).includes(e)
|
||||||
).length
|
).length
|
||||||
}
|
}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
);
|
</button>
|
||||||
})}
|
);
|
||||||
</div>
|
})}
|
||||||
</Disclosure.Panel>
|
</div>
|
||||||
</Transition>
|
</Disclosure.Panel>
|
||||||
</Disclosure>
|
</Transition>
|
||||||
)}
|
</Disclosure>
|
||||||
|
)}
|
||||||
<Links
|
<Links
|
||||||
links={
|
links={
|
||||||
links?.map((e, i) => {
|
links?.map((e, i) => {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Collection" ADD COLUMN "tagsArePublic" BOOLEAN NOT NULL DEFAULT false;
|
|
|
@ -105,7 +105,6 @@ model Collection {
|
||||||
parent Collection? @relation("SubCollections", fields: [parentId], references: [id])
|
parent Collection? @relation("SubCollections", fields: [parentId], references: [id])
|
||||||
subCollections Collection[] @relation("SubCollections")
|
subCollections Collection[] @relation("SubCollections")
|
||||||
isPublic Boolean @default(false)
|
isPublic Boolean @default(false)
|
||||||
tagsArePublic Boolean @default(false)
|
|
||||||
owner User @relation(fields: [ownerId], references: [id])
|
owner User @relation(fields: [ownerId], references: [id])
|
||||||
ownerId Int
|
ownerId Int
|
||||||
members UsersAndCollections[]
|
members UsersAndCollections[]
|
||||||
|
|
Ŝarĝante…
Reference in New Issue