refactor collections store
This commit is contained in:
parent
cd82083e09
commit
05c5bdf63c
|
@ -9,7 +9,6 @@ import Tree, {
|
|||
TreeSourcePosition,
|
||||
TreeDestinationPosition,
|
||||
} from "@atlaskit/tree";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import { Collection } from "@prisma/client";
|
||||
import Link from "next/link";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
|
@ -17,6 +16,7 @@ import { useRouter } from "next/router";
|
|||
import useAccountStore from "@/store/account";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections, useUpdateCollection } from "@/hooks/store/collections";
|
||||
|
||||
interface ExtendedTreeItem extends TreeItem {
|
||||
data: Collection;
|
||||
|
@ -24,7 +24,9 @@ interface ExtendedTreeItem extends TreeItem {
|
|||
|
||||
const CollectionListing = () => {
|
||||
const { t } = useTranslation();
|
||||
const { collections, updateCollection } = useCollectionStore();
|
||||
const updateCollection = useUpdateCollection();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
const { account, updateAccount } = useAccountStore();
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -151,7 +153,7 @@ const CollectionListing = () => {
|
|||
const updatedCollectionOrder = [...account.collectionOrder];
|
||||
|
||||
if (source.parentId !== destination.parentId) {
|
||||
await updateCollection({
|
||||
await updateCollection.mutateAsync({
|
||||
...movedCollection,
|
||||
parentId:
|
||||
destination.parentId && destination.parentId !== "root"
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { styles } from "./styles";
|
||||
import { Options } from "./types";
|
||||
import CreatableSelect from "react-select/creatable";
|
||||
import Select from "react-select";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onChange: any;
|
||||
|
@ -24,7 +24,9 @@ export default function CollectionSelection({
|
|||
showDefaultValue = true,
|
||||
creatable = true,
|
||||
}: Props) {
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [options, setOptions] = useState<Options[]>([]);
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
} from "@/types/global";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useLinkStore from "@/store/links";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions";
|
||||
import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate";
|
||||
|
@ -21,6 +20,7 @@ import usePermissions from "@/hooks/usePermissions";
|
|||
import toast from "react-hot-toast";
|
||||
import LinkTypeBadge from "./LinkComponents/LinkTypeBadge";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -34,7 +34,9 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
|
|||
const { t } = useTranslation();
|
||||
|
||||
const viewMode = localStorage.getItem("viewMode") || "card";
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const { account } = useAccountStore();
|
||||
|
||||
const { links, getLink, setSelectedLinks, selectedLinks } = useLinkStore();
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
} from "@/types/global";
|
||||
import { useEffect, useState } from "react";
|
||||
import useLinkStore from "@/store/links";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions";
|
||||
import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate";
|
||||
|
@ -17,6 +16,7 @@ import usePermissions from "@/hooks/usePermissions";
|
|||
import toast from "react-hot-toast";
|
||||
import LinkTypeBadge from "./LinkComponents/LinkTypeBadge";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -33,7 +33,9 @@ export default function LinkCardCompact({
|
|||
}: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const { account } = useAccountStore();
|
||||
const { links, setSelectedLinks, selectedLinks } = useLinkStore();
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
} from "@/types/global";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useLinkStore from "@/store/links";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions";
|
||||
import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate";
|
||||
|
@ -21,6 +20,7 @@ import usePermissions from "@/hooks/usePermissions";
|
|||
import toast from "react-hot-toast";
|
||||
import LinkTypeBadge from "./LinkComponents/LinkTypeBadge";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
link: LinkIncludingShortenedCollectionAndTags;
|
||||
|
@ -33,7 +33,8 @@ type Props = {
|
|||
export default function LinkMasonry({ link, flipDropdown, editMode }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
const { account } = useAccountStore();
|
||||
|
||||
const { links, getLink, setSelectedLinks, selectedLinks } = useLinkStore();
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import toast from "react-hot-toast";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
import { useRouter } from "next/router";
|
||||
import usePermissions from "@/hooks/usePermissions";
|
||||
import Modal from "../Modal";
|
||||
import Button from "../ui/Button";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useDeleteCollection } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -22,7 +21,6 @@ export default function DeleteCollectionModal({
|
|||
const [collection, setCollection] =
|
||||
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const { removeCollection } = useCollectionStore();
|
||||
const router = useRouter();
|
||||
const [inputField, setInputField] = useState("");
|
||||
const permissions = usePermissions(collection.id as number);
|
||||
|
@ -31,6 +29,8 @@ export default function DeleteCollectionModal({
|
|||
setCollection(activeCollection);
|
||||
}, []);
|
||||
|
||||
const deleteCollection = useDeleteCollection();
|
||||
|
||||
const submit = async () => {
|
||||
if (permissions === true && collection.name !== inputField) return;
|
||||
if (!submitLoader) {
|
||||
|
@ -39,19 +39,12 @@ export default function DeleteCollectionModal({
|
|||
|
||||
setSubmitLoader(true);
|
||||
|
||||
const load = toast.loading(t("deleting_collection"));
|
||||
|
||||
let response = await removeCollection(collection.id as number);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(t("deleted"));
|
||||
deleteCollection.mutateAsync(collection.id as number, {
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
router.push("/collections");
|
||||
} else {
|
||||
toast.error(response.data as string);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
setSubmitLoader(false);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import React, { useState } from "react";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import toast from "react-hot-toast";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
import Modal from "../Modal";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useUpdateCollection } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -21,7 +20,7 @@ export default function EditCollectionModal({
|
|||
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
|
||||
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const { updateCollection } = useCollectionStore();
|
||||
const updateCollection = useUpdateCollection();
|
||||
|
||||
const submit = async () => {
|
||||
if (!submitLoader) {
|
||||
|
@ -30,16 +29,11 @@ export default function EditCollectionModal({
|
|||
|
||||
setSubmitLoader(true);
|
||||
|
||||
const load = toast.loading(t("updating_collection"));
|
||||
|
||||
let response = await updateCollection(collection as any);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(t("updated"));
|
||||
await updateCollection.mutateAsync(collection, {
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
} else toast.error(response.data as string);
|
||||
},
|
||||
});
|
||||
|
||||
setSubmitLoader(false);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import toast from "react-hot-toast";
|
||||
import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global";
|
||||
import getPublicUserData from "@/lib/client/getPublicUserData";
|
||||
|
@ -11,6 +10,7 @@ import addMemberToCollection from "@/lib/client/addMemberToCollection";
|
|||
import Modal from "../Modal";
|
||||
import { dropdownTriggerer } from "@/lib/client/utils";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useUpdateCollection } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -27,7 +27,7 @@ export default function EditCollectionSharingModal({
|
|||
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
|
||||
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const { updateCollection } = useCollectionStore();
|
||||
const updateCollection = useUpdateCollection();
|
||||
|
||||
const submit = async () => {
|
||||
if (!submitLoader) {
|
||||
|
@ -36,18 +36,11 @@ export default function EditCollectionSharingModal({
|
|||
|
||||
setSubmitLoader(true);
|
||||
|
||||
const load = toast.loading(t("updating"));
|
||||
|
||||
let response;
|
||||
|
||||
response = await updateCollection(collection as any);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(t("updated"));
|
||||
await updateCollection.mutateAsync(collection, {
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
} else toast.error(response.data as string);
|
||||
},
|
||||
});
|
||||
|
||||
setSubmitLoader(false);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import toast from "react-hot-toast";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { Collection } from "@prisma/client";
|
||||
import Modal from "../Modal";
|
||||
|
@ -9,6 +7,7 @@ import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
|||
import useAccountStore from "@/store/account";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCreateCollection } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -33,7 +32,8 @@ export default function NewCollectionModal({ onClose, parent }: Props) {
|
|||
}, []);
|
||||
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const { addCollection } = useCollectionStore();
|
||||
|
||||
const createCollection = useCreateCollection();
|
||||
|
||||
const submit = async () => {
|
||||
if (submitLoader) return;
|
||||
|
@ -41,18 +41,11 @@ export default function NewCollectionModal({ onClose, parent }: Props) {
|
|||
|
||||
setSubmitLoader(true);
|
||||
|
||||
const load = toast.loading(t("creating"));
|
||||
|
||||
let response = await addCollection(collection as any);
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(t("created_success"));
|
||||
if (response.data) {
|
||||
setAccount(data?.user.id as number);
|
||||
await createCollection.mutateAsync(collection, {
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
}
|
||||
} else toast.error(response.data as string);
|
||||
},
|
||||
});
|
||||
|
||||
setSubmitLoader(false);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import CollectionSelection from "@/components/InputSelect/CollectionSelection";
|
||||
import TagSelection from "@/components/InputSelect/TagSelection";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import useLinkStore from "@/store/links";
|
||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
@ -12,6 +10,7 @@ import { useRouter } from "next/router";
|
|||
import toast from "react-hot-toast";
|
||||
import Modal from "../Modal";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -44,7 +43,8 @@ export default function NewLinkModal({ onClose }: Props) {
|
|||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const [optionsExpanded, setOptionsExpanded] = useState(false);
|
||||
const router = useRouter();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const setCollection = (e: any) => {
|
||||
if (e?.__isNew__) e.value = null;
|
||||
|
|
|
@ -3,7 +3,6 @@ import CollectionSelection from "@/components/InputSelect/CollectionSelection";
|
|||
import TagSelection from "@/components/InputSelect/TagSelection";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import useLinkStore from "@/store/links";
|
||||
import {
|
||||
LinkIncludingShortenedCollectionAndTags,
|
||||
|
@ -14,6 +13,7 @@ import { useRouter } from "next/router";
|
|||
import toast from "react-hot-toast";
|
||||
import Modal from "../Modal";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
|
@ -49,7 +49,8 @@ export default function UploadFileModal({ onClose }: Props) {
|
|||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const [optionsExpanded, setOptionsExpanded] = useState(false);
|
||||
const router = useRouter();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const setCollection = (e: any) => {
|
||||
if (e?.__isNew__) e.value = null;
|
||||
|
|
|
@ -14,8 +14,8 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import LinkActions from "./LinkViews/LinkComponents/LinkActions";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
type LinkContent = {
|
||||
title: string;
|
||||
|
@ -46,7 +46,8 @@ export default function ReadableView({ link }: Props) {
|
|||
const router = useRouter();
|
||||
|
||||
const { getLink } = useLinkStore();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const collection = useMemo(() => {
|
||||
return collections.find(
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import useTagStore from "@/store/tags";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
@ -7,6 +6,7 @@ import { Disclosure, Transition } from "@headlessui/react";
|
|||
import SidebarHighlightLink from "@/components/SidebarHighlightLink";
|
||||
import CollectionListing from "@/components/CollectionListing";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
export default function Sidebar({ className }: { className?: string }) {
|
||||
const { t } = useTranslation();
|
||||
|
@ -22,7 +22,9 @@ export default function Sidebar({ className }: { className?: string }) {
|
|||
}
|
||||
);
|
||||
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const { tags } = useTagStore();
|
||||
const [active, setActive] = useState("");
|
||||
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
color: string;
|
||||
size: string;
|
||||
};
|
||||
|
||||
const Loader = (props: Props) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
width={props.size}
|
||||
height={props.size}
|
||||
className={props.className}
|
||||
style={{
|
||||
shapeRendering: "auto",
|
||||
display: "block",
|
||||
background: "rgba(255, 255, 255, 0)",
|
||||
}}
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<g>
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.9166666666666666s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(30 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.8333333333333334s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(60 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.75s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(90 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.6666666666666666s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(120 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.5833333333333334s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(150 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.5s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(180 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.4166666666666667s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(210 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.3333333333333333s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(240 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.25s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(270 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.16666666666666666s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(300 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="-0.08333333333333333s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(330 50 50)">
|
||||
<rect
|
||||
fill={props.color}
|
||||
height="12"
|
||||
width="6"
|
||||
ry="1.8"
|
||||
rx="1.8"
|
||||
y="24"
|
||||
x="47"
|
||||
>
|
||||
<animate
|
||||
repeatCount="indefinite"
|
||||
begin="0s"
|
||||
dur="1s"
|
||||
keyTimes="0;1"
|
||||
values="1;0"
|
||||
attributeName="opacity"
|
||||
></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g></g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loader;
|
|
@ -0,0 +1,131 @@
|
|||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const useCollections = () => {
|
||||
return useQuery({
|
||||
queryKey: ["collections"],
|
||||
queryFn: async (): Promise<{
|
||||
response: CollectionIncludingMembersAndLinkCount[];
|
||||
}> => {
|
||||
const response = await fetch("/api/v1/collections");
|
||||
const data = await response.json();
|
||||
return data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useCreateCollection = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (data: any) => {
|
||||
const load = toast.loading(t("creating"));
|
||||
|
||||
const response = await fetch("/api/v1/collections", {
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
toast.success(t("created"));
|
||||
return queryClient.setQueryData(["collections"], (oldData: any) => {
|
||||
return {
|
||||
response: [...oldData.response, data.response],
|
||||
};
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useUpdateCollection = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (data: any) => {
|
||||
const load = toast.loading(t("updating_collection"));
|
||||
|
||||
const response = await fetch(`/api/v1/collections/${data.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
{
|
||||
toast.success(t("updated"));
|
||||
return queryClient.setQueryData(["collections"], (oldData: any) => {
|
||||
return {
|
||||
response: oldData.response.map((collection: any) =>
|
||||
collection.id === data.response.id ? data.response : collection
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useDeleteCollection = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id: number) => {
|
||||
const load = toast.loading(t("deleting_collection"));
|
||||
|
||||
const response = await fetch(`/api/v1/collections/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
toast.success(t("deleted"));
|
||||
return queryClient.setQueryData(["collections"], (oldData: any) => {
|
||||
return {
|
||||
response: oldData.response.filter(
|
||||
(collection: any) => collection.id !== data.response.id
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
useCollections,
|
||||
useCreateCollection,
|
||||
useUpdateCollection,
|
||||
useDeleteCollection,
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
import useAccountStore from "@/store/account";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import { Member } from "@/types/global";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCollections } from "./store/collections";
|
||||
|
||||
export default function useCollectivePermissions(collectionIds: number[]) {
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const { account } = useAccountStore();
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import { useEffect } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import useTagStore from "@/store/tags";
|
||||
|
@ -7,7 +6,7 @@ import useLocalSettingsStore from "@/store/localSettings";
|
|||
|
||||
export default function useInitialData() {
|
||||
const { status, data } = useSession();
|
||||
const { setCollections } = useCollectionStore();
|
||||
// const { setCollections } = useCollectionStore();
|
||||
const { setTags } = useTagStore();
|
||||
// const { setLinks } = useLinkStore();
|
||||
const { account, setAccount } = useAccountStore();
|
||||
|
@ -24,7 +23,7 @@ export default function useInitialData() {
|
|||
// Get the rest of the data
|
||||
useEffect(() => {
|
||||
if (account.id && (!process.env.NEXT_PUBLIC_STRIPE || account.username)) {
|
||||
setCollections();
|
||||
// setCollections();
|
||||
setTags();
|
||||
// setLinks();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import useAccountStore from "@/store/account";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import { Member } from "@/types/global";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCollections } from "./store/collections";
|
||||
|
||||
export default function usePermissions(collectionId: number) {
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const { account } = useAccountStore();
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
"@mozilla/readability": "^0.4.4",
|
||||
"@prisma/client": "^4.16.2",
|
||||
"@stripe/stripe-js": "^1.54.1",
|
||||
"@tanstack/react-query": "^5.51.15",
|
||||
"@tanstack/react-query-devtools": "^5.51.15",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/node": "^20.10.4",
|
||||
|
|
|
@ -11,7 +11,10 @@ import { Session } from "next-auth";
|
|||
import { isPWA } from "@/lib/client/utils";
|
||||
// import useInitialData from "@/hooks/useInitialData";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import nextI18nextConfig from "../next-i18next.config";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
function App({
|
||||
Component,
|
||||
|
@ -29,6 +32,7 @@ function App({
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<SessionProvider
|
||||
session={pageProps.session}
|
||||
refetchOnWindowFocus={false}
|
||||
|
@ -95,6 +99,8 @@ function App({
|
|||
{/* </GetData> */}
|
||||
</AuthRedirect>
|
||||
</SessionProvider>
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import useLinkStore from "@/store/links";
|
||||
import {
|
||||
CollectionIncludingMembersAndLinkCount,
|
||||
|
@ -26,6 +25,7 @@ import MasonryView from "@/components/LinkViews/Layouts/MasonryView";
|
|||
import getServerSideProps from "@/lib/client/getServerSideProps";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import LinkListOptions from "@/components/LinkListOptions";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
export default function Index() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -34,7 +34,8 @@ export default function Index() {
|
|||
const router = useRouter();
|
||||
|
||||
const { links } = useLinkStore();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import CollectionCard from "@/components/CollectionCard";
|
||||
import { useState } from "react";
|
||||
import MainLayout from "@/layouts/MainLayout";
|
||||
|
@ -10,10 +9,12 @@ import NewCollectionModal from "@/components/ModalContent/NewCollectionModal";
|
|||
import PageHeader from "@/components/PageHeader";
|
||||
import getServerSideProps from "@/lib/client/getServerSideProps";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
export default function Collections() {
|
||||
const { t } = useTranslation();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
|
||||
const [sortedCollections, setSortedCollections] = useState(collections);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import useLinkStore from "@/store/links";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import useTagStore from "@/store/tags";
|
||||
import MainLayout from "@/layouts/MainLayout";
|
||||
import { useEffect, useState } from "react";
|
||||
|
@ -19,10 +18,12 @@ import { dropdownTriggerer } from "@/lib/client/utils";
|
|||
import MasonryView from "@/components/LinkViews/Layouts/MasonryView";
|
||||
import getServerSideProps from "@/lib/client/getServerSideProps";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
export default function Dashboard() {
|
||||
const { t } = useTranslation();
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
const { links } = useLinkStore();
|
||||
const { tags } = useTagStore();
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ import ListView from "@/components/LinkViews/Layouts/ListView";
|
|||
import MasonryView from "@/components/LinkViews/Layouts/MasonryView";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import getServerSideProps from "@/lib/client/getServerSideProps";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import LinkListOptions from "@/components/LinkListOptions";
|
||||
import { useCollections } from "@/hooks/store/collections";
|
||||
|
||||
export default function PublicCollections() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -32,7 +32,8 @@ export default function PublicCollections() {
|
|||
|
||||
const { settings } = useLocalSettingsStore();
|
||||
|
||||
const { collections } = useCollectionStore();
|
||||
const { data: { response: collections } = { response: [] } } =
|
||||
useCollections();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import { create } from "zustand";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
import useTagStore from "./tags";
|
||||
|
||||
type ResponseObject = {
|
||||
ok: boolean;
|
||||
data: object | string;
|
||||
};
|
||||
|
||||
type CollectionStore = {
|
||||
collections: CollectionIncludingMembersAndLinkCount[];
|
||||
setCollections: () => void;
|
||||
addCollection: (
|
||||
body: CollectionIncludingMembersAndLinkCount
|
||||
) => Promise<ResponseObject>;
|
||||
updateCollection: (
|
||||
collection: CollectionIncludingMembersAndLinkCount
|
||||
) => Promise<ResponseObject>;
|
||||
removeCollection: (collectionId: number) => Promise<ResponseObject>;
|
||||
};
|
||||
|
||||
const useCollectionStore = create<CollectionStore>()((set) => ({
|
||||
collections: [],
|
||||
setCollections: async () => {
|
||||
const response = await fetch("/api/v1/collections");
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) set({ collections: data.response });
|
||||
},
|
||||
addCollection: async (body) => {
|
||||
const response = await fetch("/api/v1/collections", {
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok)
|
||||
set((state) => ({
|
||||
collections: [...state.collections, data.response],
|
||||
}));
|
||||
|
||||
return { ok: response.ok, data: data.response };
|
||||
},
|
||||
updateCollection: async (collection) => {
|
||||
const response = await fetch(`/api/v1/collections/${collection.id}`, {
|
||||
body: JSON.stringify(collection),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "PUT",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok)
|
||||
set((state) => ({
|
||||
collections: state.collections.map((e) =>
|
||||
e.id === data.response.id ? data.response : e
|
||||
),
|
||||
}));
|
||||
|
||||
return { ok: response.ok, data: data.response };
|
||||
},
|
||||
removeCollection: async (collectionId) => {
|
||||
const response = await fetch(`/api/v1/collections/${collectionId}`, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
set((state) => ({
|
||||
collections: state.collections.filter(
|
||||
(collection) =>
|
||||
collection.id !== collectionId &&
|
||||
collection.parentId !== collectionId
|
||||
),
|
||||
}));
|
||||
useTagStore.getState().setTags();
|
||||
}
|
||||
|
||||
return { ok: response.ok, data: data.response };
|
||||
},
|
||||
}));
|
||||
|
||||
export default useCollectionStore;
|
24
yarn.lock
24
yarn.lock
|
@ -1903,6 +1903,30 @@
|
|||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@tanstack/query-core@5.51.15":
|
||||
version "5.51.15"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.51.15.tgz#7aee6a2d5d3f64de3e54096607233b1132dc6afd"
|
||||
integrity sha512-xyobHDJ0yhPE3+UkSQ2/4X1fLSg7ICJI5J1JyU9yf7F3deQfEwSImCDrB1WSRrauJkMtXW7YIEcC0oA6ZZWt5A==
|
||||
|
||||
"@tanstack/query-devtools@5.51.15":
|
||||
version "5.51.15"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.51.15.tgz#81c5c28231adc4b95fe4a5e1004020fdca5ea447"
|
||||
integrity sha512-1oSCl+PsCa/aBCGVM2ZdcQLuQ0QYmKXJJB264twEMVM1M0n5CI40trtywORPF+wLGuZNIZzkKL7j/98mOLAIag==
|
||||
|
||||
"@tanstack/react-query-devtools@^5.51.15":
|
||||
version "5.51.15"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.51.15.tgz#5c4d21305fd25c35dc88bd280304f77a45554fc2"
|
||||
integrity sha512-bvGvJoncjZ3irEofoFevptj5BPkDpQrp2+dZhtFqPUZXRT6MAKPmOqtSmZPfacLR5jQLpqw/7d3Zxr173z7WDA==
|
||||
dependencies:
|
||||
"@tanstack/query-devtools" "5.51.15"
|
||||
|
||||
"@tanstack/react-query@^5.51.15":
|
||||
version "5.51.15"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.15.tgz#059bb2966f828263adb355de81410d107e22b5bc"
|
||||
integrity sha512-UgFg23SrdIYrmfTSxAUn9g+J64VQy11pb9/EefoY/u2+zWuNMeqEOnvpJhf52XQy0yztQoyM9p6x8PFyTNaxXg==
|
||||
dependencies:
|
||||
"@tanstack/query-core" "5.51.15"
|
||||
|
||||
"@tokenizer/token@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
|
||||
|
|
Ŝarĝante…
Reference in New Issue