diff --git a/src/app/(gistLayout)/mygist/[gistId]/page.tsx b/src/app/(gistLayout)/mygist/[gistId]/page.tsx index e165ed0..b8b2b54 100644 --- a/src/app/(gistLayout)/mygist/[gistId]/page.tsx +++ b/src/app/(gistLayout)/mygist/[gistId]/page.tsx @@ -1,9 +1,10 @@ "use client" import React from "react" import MyGistIdPage from "./page-ui" -import { useGist, usePatchGistContent, usePatchGistName } from "@/lib/queries/gists.queries" +import { useEditGist, useGist, usePatchGistContent, usePatchGistName } from "@/lib/queries/gists.queries" import { useToast } from "@/components/shadcn/use-toast" import { getRawGistURL } from "@/lib/utils" +import { useMe } from "@/lib/queries/user.queries" interface MyGistIdFeaturePageProps { params: { @@ -15,6 +16,7 @@ export default function MyGistIdFeaturePage({ params }: MyGistIdFeaturePageProps const { gistId } = params const { data } = useGist(gistId) const { toast } = useToast() + const { data: me } = useMe() const { mutate: updateName } = usePatchGistName({ onSuccess: () => { @@ -29,6 +31,14 @@ export default function MyGistIdFeaturePage({ params }: MyGistIdFeaturePageProps onSuccess: () => {}, }) + const { mutate: edit } = useEditGist({ + onSuccess: () => { + toast({ + title: "Gist Saved", + description: "Your gist has been saved successfully", + }) + }, + }) const onDownload = () => { toast({ title: "Gist Downloaded", @@ -36,11 +46,16 @@ export default function MyGistIdFeaturePage({ params }: MyGistIdFeaturePageProps }) } const onSave = (name: string, code: string) => { - updateContent({ id: gistId, content: code }) - updateName({ id: gistId, name }) - toast({ - title: "Gist Saved", - description: "Your gist has been saved successfully", + console.log("save") + edit({ + id: gistId, + name, + content: code, + description: "", + visibility: "public", + org_id: null, + language: "plaintext", + owner_id: me?.id || "", }) } diff --git a/src/app/(gistLayout)/org/[orgId]/gist/[gistId]/page.tsx b/src/app/(gistLayout)/org/[orgId]/gist/[gistId]/page.tsx index 4ac7d00..61be503 100644 --- a/src/app/(gistLayout)/org/[orgId]/gist/[gistId]/page.tsx +++ b/src/app/(gistLayout)/org/[orgId]/gist/[gistId]/page.tsx @@ -1,7 +1,7 @@ "use client" import { useToast } from "@/components/shadcn/use-toast" import GistDetails from "@/components/ui/gist-details" -import { useGist, usePatchGistContent, usePatchGistName } from "@/lib/queries/gists.queries" +import { useEditGist, useGist, usePatchGistContent, usePatchGistName } from "@/lib/queries/gists.queries" import { useOrg } from "@/lib/queries/orgs.queries" import { getRawGistURL } from "@/lib/utils" import React from "react" @@ -31,6 +31,15 @@ export default function MyOrgGistIdFeaturePage({ params }: MyOrgGistIdFeaturePag onSuccess: () => {}, }) + const { mutate: edit } = useEditGist({ + onSuccess: () => { + toast({ + title: "Gist Saved", + description: "Your gist has been saved successfully", + }) + }, + }) + const onDownload = () => { toast({ title: "Gist Downloaded", @@ -56,7 +65,6 @@ export default function MyOrgGistIdFeaturePage({ params }: MyOrgGistIdFeaturePag } const onDelete = (id: string) => { - console.log(`Deleting gist with ID: ${id}`) toast({ title: "Gist Deleted", description: "Your gist has been deleted successfully", diff --git a/src/components/contexts/pagination.tsx b/src/components/contexts/pagination.tsx index e468ec7..be9fa0b 100644 --- a/src/components/contexts/pagination.tsx +++ b/src/components/contexts/pagination.tsx @@ -58,7 +58,6 @@ export function PaginationProvider({ children, fromUrl }: { children: ReactNode; if (searchParams.has("page")) { const page = parseInt(searchParams.get("page") as string) const offset = (page - 1) * limit - console.log("offset", offset) if (!checkOffset(offset)) return setOffset(offset) } diff --git a/src/components/ui/gist-details.tsx b/src/components/ui/gist-details.tsx index f4d873b..bf35d26 100644 --- a/src/components/ui/gist-details.tsx +++ b/src/components/ui/gist-details.tsx @@ -12,11 +12,11 @@ import { Trash2Icon, } from "lucide-react" import Link from "next/link" -import { useState } from "react" +import { useEffect, useState } from "react" import { Codearea } from "../shadcn/codearea" import { getLanguage } from "@/lib/language" import TooltipShortcut, { TooltipShortcutTrigger } from "./tooltip-shortcut" -import { getBackendURL, getRawGistURL } from "@/lib/utils" +import { getRawGistURL } from "@/lib/utils" import { SidebarTrigger } from "../shadcn/sidebar" import { Button } from "../shadcn/button" @@ -46,6 +46,15 @@ export default function GistDetails({ const [gistId] = useState(gist.id) const [gistName, setGistName] = useState(gist.name) const [gistCode, setGistCode] = useState(gist.code) + const [needSync, setNeedSync] = useState(false) + + useEffect(() => { + if (gist.name !== gistName || gist.code !== gistCode) { + setNeedSync(true) + } else { + setNeedSync(false) + } + }, [gistName, gistCode]) const gistState: Gist = { id: gistId, @@ -54,7 +63,6 @@ export default function GistDetails({ } const onOpenPlainText = (gistID: string) => { - // if production go to https://raw.gists.app/{gistID} console.log(process.env.NODE_ENV) window.open(getRawGistURL(gistID), "_blank") } @@ -117,7 +125,7 @@ export default function GistDetails({ - onSave(gistName, gistCode)} variant={"menu"}> + onSave(gistName, gistCode)} variant={"menu"} disabled={!needSync}> Save diff --git a/src/lib/queries/gists.queries.tsx b/src/lib/queries/gists.queries.tsx index 9350069..ea28ddf 100644 --- a/src/lib/queries/gists.queries.tsx +++ b/src/lib/queries/gists.queries.tsx @@ -2,7 +2,9 @@ import ky from "ky" import { getBackendURL } from "../utils" import { Gist } from "@/types" -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" +import { keepPreviousData, useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query" +import { useEffect } from "react" +import { useGistStorage } from "../storage/gists" //types @@ -11,6 +13,9 @@ export interface ApiGist { name: string content: string owner_id: string + language: string + description: string + visibility: string org_id: string | null } @@ -131,17 +136,35 @@ const fetchDeleteGist = async (id: string) => { //hooks export const useGists = ({ offset, limit }: { offset?: number; limit?: number }) => { - const { data, error, isPending } = useQuery({ - queryKey: ["gists", offset?.toString(), limit?.toString()], - queryFn: () => - fetchGists({ - offset: offset || 0, + const { data, error, isFetching, fetchNextPage, fetchPreviousPage } = useInfiniteQuery({ + queryKey: ["gists"], + queryFn: ({ pageParam }) => fetchGists(pageParam), + initialPageParam: { + offset: offset || 0, + limit: limit || 50, + }, + getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => { + return { + offset: offset || 0 + (limit || 50), limit: limit || 50, - }), - staleTime: 5000, + } + }, }) - return { data: data?.gists, nb_pages: data?.nb_pages, error, isPending } + const currentPage = Math.trunc((offset || 0) / (limit || 50)) + + useEffect(() => { + fetchNextPage() //prefetch + }, [offset, limit]) + + return { + data: data?.pages[currentPage]?.gists, + nb_pages: data?.pages[0].nb_pages, + error, + isPending: isFetching, + fetchNextPage, + fetchPreviousPage, + } } export const useGist = (gistId: string) => { @@ -160,10 +183,14 @@ export const useCreateGist = ({ onSuccess }: { onSuccess: () => void }) => { return fetchCreateGist(gist) }, onSuccess: (newGist) => { - queryClient.setQueryData(["gists"], (oldData: any) => { - // Assuming oldData is an array, you might need to adjust this based on your actual data structure - return [...(oldData || []), newGist] - }) + //invalidate + queryClient.invalidateQueries({ queryKey: ["gists"] }) + // queryClient.setQueryData(["gists"], (oldData: any) => { + // // Assuming oldData is an array, you might need to adjust this based on your actual data structure + // + // + // return [...(oldData || []), newGist] + // }) // Call the onSuccess callback if provided if (onSuccess) { @@ -228,12 +255,55 @@ export const useDeleteGist = ({ onSuccess }: { onSuccess: (id: string) => void } return fetchDeleteGist(id) }, onSuccess: (gistID) => { - queryClient.setQueryData(["gists"], (oldData: any) => { - return oldData.filter((gist: Gist) => gist.id !== gistID.toString()) - }) + // queryClient.setQueryData(["gists"], (oldData: any) => { + // return oldData.filter((gist: Gist) => gist.id !== gistID.toString()) + // }) + queryClient.invalidateQueries({ queryKey: ["gists"] }) onSuccess(gistID.toString()) }, }) return { mutate, error, data, isPending } } + +const fetchUpdateGist = async (payload: ApiGist): Promise => { + const json = await ky + .put(`${getBackendURL()}/gists/${payload.id}`, { + credentials: "include", + json: { + name: payload.name, + content: payload.content, + description: payload.description, + visibility: payload.visibility, + org_id: payload.org_id, + }, + }) + .json() + console.log(json) + return { + id: json.id, + name: json.name, + code: json.content, + } +} + +export const useEditGist = ({ onSuccess }: { onSuccess: () => void }) => { + const queryClient = useQueryClient() // Access the Query Client + + const { mutate, error, data, isPending } = useMutation({ + mutationFn: (payload: ApiGist) => { + return fetchUpdateGist(payload) + }, + onError(error, variables, context) { + console.log(error) + }, + onSuccess: (newGist) => { + queryClient.invalidateQueries({ queryKey: ["gists"] }) + queryClient.invalidateQueries({ queryKey: ["gists", newGist.id] }) + if (onSuccess) { + onSuccess() + } + }, + }) + return { mutate, error, data, isPending } +} diff --git a/src/lib/storage/gists.ts b/src/lib/storage/gists.ts new file mode 100644 index 0000000..8219dfa --- /dev/null +++ b/src/lib/storage/gists.ts @@ -0,0 +1,12 @@ +import { Gist } from "@/types" +import DataStorage from "./storage" +import { set } from "react-hook-form" + +const globalGistStorage = new DataStorage() + +export const useGistStorage = () => { + return { + set: globalGistStorage.set.bind(globalGistStorage), + getItems: globalGistStorage.getItems.bind(globalGistStorage), + } +} diff --git a/src/lib/storage/storage.ts b/src/lib/storage/storage.ts new file mode 100644 index 0000000..572d34d --- /dev/null +++ b/src/lib/storage/storage.ts @@ -0,0 +1,20 @@ +"use client" +interface ItemWithID { + id: string +} + +class DataStorage { + private data: Map = new Map() + set(data: T) { + this.data.set(data.id, data) + } + getItems(offset?: number, limit?: number): T[] { + if (offset !== undefined && limit !== undefined) { + return Array.from(this.data.values()).slice(offset, offset + limit) + } + + return Array.from(this.data.values()) + } +} + +export default DataStorage