Created
September 15, 2023 19:08
-
-
Save akodkod/0d950824e23442510a9eb9340a6453be to your computer and use it in GitHub Desktop.
Manage likes using Zustand
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { captureException } from "@sentry/nextjs" | |
import toast from "react-hot-toast" | |
import { create } from "zustand" | |
import likePhoto from "@/actions/likePhoto" | |
import { ServerAction } from "@/actions/withServerAction" | |
import SignInModal from "@/components/auth/SignInModal" | |
import { getBaseErrorMessage } from "@/lib/helpers/error" | |
import { currentUser } from "@/stores/currentUser" | |
import { openModal } from "@/stores/modal" | |
type Action = "add" | "remove" | "toggle" | |
const OppositeAction: Record<Action, Action> = { | |
add: "remove", | |
remove: "add", | |
toggle: "toggle", | |
} | |
type LikesStore = { | |
likes: Set<string> | |
loaded: boolean | |
fetch: () => void | |
add: (photoId: string) => void | |
remove: (photoId: string) => void | |
toggle: (photoId: string) => void | |
isLiked: (photoId: string) => boolean | |
perform: (action: Action, photoId: string, albumId?: string) => void | |
} | |
const store = create<LikesStore>((set, get) => ({ | |
likes: new Set(), | |
loaded: false, | |
fetch: async () => { | |
try { | |
const response = await fetch("/api/likes/all") | |
const data = await response.json() | |
set({ likes: data, loaded: true }) | |
} catch (error) { | |
captureException(error) | |
} | |
}, | |
add: (photoId) => { | |
const likes = new Set(get().likes) | |
likes.add(photoId) | |
set({ likes }) | |
}, | |
remove: (photoId) => { | |
const likes = new Set(get().likes) | |
likes.delete(photoId) | |
set({ likes }) | |
}, | |
toggle: (photoId) => { | |
const likes = new Set(get().likes) | |
if (likes.has(photoId)) { | |
likes.delete(photoId) | |
} else { | |
likes.add(photoId) | |
} | |
set({ likes }) | |
}, | |
isLiked: (photoId) => new Set(get().likes).has(photoId), | |
perform: async (action, photoId, albumId) => { | |
if (!currentUser()) { | |
openModal(<SignInModal text="You need to sign in to like photos" />) | |
return | |
} | |
get()[action](photoId) | |
const rollback = () => get()[OppositeAction[action]](photoId) | |
try { | |
const [status, _, errors] = await likePhoto(photoId, albumId, action) | |
if (status === ServerAction.Failure) { | |
rollback() | |
toast.error(getBaseErrorMessage(errors) || "Something went wrong") | |
} | |
} catch (error) { | |
rollback() | |
captureException(error) | |
toast.error("Something went wrong") | |
} | |
}, | |
})) | |
export const useIsLikesLoaded = () => store((state) => state.loaded) | |
export const useIsLiked = () => store((state) => state.isLiked) | |
export const fetchLikes = async () => store.getState().fetch() | |
export const addLike = (photoId: string, albumId?: string) => store.getState().perform("add", photoId, albumId) | |
export const removeLike = (photoId: string, albumId?: string) => store.getState().perform("remove", photoId, albumId) | |
export const toggleLike = (photoId: string, albumId?: string) => store.getState().perform("toggle", photoId, albumId) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment