add getPodcasts query atom

This commit is contained in:
thierryverse 2025-11-19 08:29:33 +02:00
parent eca4580f76
commit d2fddd5ea5
14 changed files with 77 additions and 14 deletions

View file

@ -15,7 +15,7 @@ import { AnimatePresence, motion, type Variants } from "motion/react";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
import { deleteChatMutationAtom } from "@/atoms/chats/chat-mutation.atoms";
import { chatsAtom } from "@/atoms/chats/chat-querie.atoms";
import { chatsAtom } from "@/atoms/chats/chat-query.atoms";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";

View file

@ -4,7 +4,7 @@ import { type CreateMessage, type Message, useChat } from "@ai-sdk/react";
import { useAtomValue } from "jotai";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useMemo, useRef } from "react";
import { activeChatAtom } from "@/atoms/chats/chat-querie.atoms";
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
import { activeChatIdAtom } from "@/atoms/chats/ui.atoms";
import ChatInterface from "@/components/chat/ChatInterface";
import { useChatAPI, useChatState } from "@/hooks/use-chat";

View file

@ -5,13 +5,15 @@ import type { DeletePodcastRequest, Podcast } from "@/contracts/types/podcast.ty
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { queryClient } from "@/lib/query-client/client";
import { globalPodcastsQueryParamsAtom } from "./ui.atoms";
export const deletePodcastMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const authToken = localStorage.getItem("surfsense_bearer_token");
const podcastsQueryParams = get(globalPodcastsQueryParamsAtom);
return {
mutationKey: cacheKeys.podcasts(),
mutationKey: cacheKeys.podcasts.globalQueryParams(podcastsQueryParams),
enabled: !!searchSpaceId && !!authToken,
mutationFn: async (request: DeletePodcastRequest) => {
return podcastsApiService.deletePodcast(request);
@ -19,9 +21,12 @@ export const deletePodcastMutationAtom = atomWithMutation((get) => {
onSuccess: (_, request: DeletePodcastRequest) => {
toast.success("Podcast deleted successfully");
queryClient.setQueryData(cacheKeys.podcasts(), (oldData: Podcast[]) => {
return oldData.filter((podcast) => podcast.id !== request.id);
});
queryClient.setQueryData(
cacheKeys.podcasts.globalQueryParams(podcastsQueryParams),
(oldData: Podcast[]) => {
return oldData.filter((podcast) => podcast.id !== request.id);
}
);
},
};
});

View file

@ -0,0 +1,17 @@
import { atomWithQuery } from "jotai-tanstack-query";
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { globalPodcastsQueryParamsAtom } from "./ui.atoms";
export const podcastsAtom = atomWithQuery((get) => {
const queryParams = get(globalPodcastsQueryParamsAtom);
return {
queryKey: cacheKeys.podcasts.globalQueryParams(queryParams),
queryFn: async () => {
return podcastsApiService.getPodcasts({
queryParams: queryParams,
});
},
};
});

View file

@ -0,0 +1,7 @@
import { atom } from "jotai";
import type { GetPodcastsRequest } from "@/contracts/types/podcast.types";
export const globalPodcastsQueryParamsAtom = atom<GetPodcastsRequest["queryParams"]>({
limit: 5,
skip: 0,
});

View file

@ -2,7 +2,7 @@
import { useAtom, useAtomValue } from "jotai";
import { LoaderIcon, PanelRight, TriangleAlert } from "lucide-react";
import { toast } from "sonner";
import { activeChatAtom } from "@/atoms/chats/chat-querie.atoms";
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms";
import type { GeneratePodcastRequest } from "@/contracts/types/podcast.types";
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";

View file

@ -4,7 +4,7 @@ import { useAtom, useAtomValue } from "jotai";
import { AlertCircle, Play, RefreshCw, Sparkles } from "lucide-react";
import { motion } from "motion/react";
import { useCallback } from "react";
import { activeChatAtom } from "@/atoms/chats/chat-querie.atoms";
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
import { activeChathatUIAtom } from "@/atoms/chats/ui.atoms";
import { cn } from "@/lib/utils";
import { getPodcastStalenessMessage, isPodcastStale } from "../PodcastUtils";

View file

@ -3,7 +3,7 @@
import { useAtomValue } from "jotai";
import { Pencil } from "lucide-react";
import { useCallback, useContext, useState } from "react";
import { activeChatAtom } from "@/atoms/chats/chat-querie.atoms";
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import type { GeneratePodcastRequest } from "./ChatPanelContainer";

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import React, { useEffect } from "react";
import { activeChatAtom } from "@/atoms/chats/chat-querie.atoms";
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
import {
Breadcrumb,
BreadcrumbItem,

View file

@ -5,7 +5,7 @@ import { Trash2 } from "lucide-react";
import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useState } from "react";
import { deleteChatMutationAtom } from "@/atoms/chats/chat-mutation.atoms";
import { chatsAtom } from "@/atoms/chats/chat-querie.atoms";
import { chatsAtom } from "@/atoms/chats/chat-query.atoms";
import { globalChatsQueryParamsAtom } from "@/atoms/chats/ui.atoms";
import { AppSidebar } from "@/components/sidebar/app-sidebar";
import { Button } from "@/components/ui/button";

View file

@ -1,4 +1,5 @@
import { z } from "zod";
import { paginationQueryParams } from ".";
export const podcast = z.object({
id: z.number(),
@ -36,6 +37,10 @@ export const loadPodcastRequest = z.object({
id: z.number(),
});
export const getPodcastsRequest = z.object({
queryParams: paginationQueryParams.nullish(),
});
export type GeneratePodcastRequest = z.infer<typeof generatePodcastRequest>;
export type GetPodcastByChatIdRequest = z.infer<typeof getPodcastByChatIdRequest>;
export type GetPodcastByChatIdResponse = z.infer<typeof getPodcastByChaIdResponse>;
@ -43,3 +48,4 @@ export type DeletePodcastRequest = z.infer<typeof deletePodcastRequest>;
export type DeletePodcastResponse = z.infer<typeof deletePodcastResponse>;
export type LoadPodcastRequest = z.infer<typeof loadPodcastRequest>;
export type Podcast = z.infer<typeof podcast>;
export type GetPodcastsRequest = z.infer<typeof getPodcastsRequest>;

View file

@ -5,9 +5,11 @@ import {
deletePodcastResponse,
type GeneratePodcastRequest,
type GetPodcastByChatIdRequest,
type GetPodcastsRequest,
generatePodcastRequest,
getPodcastByChaIdResponse,
getPodcastByChatIdRequest,
getPodcastsRequest,
type LoadPodcastRequest,
loadPodcastRequest,
podcast,
@ -16,8 +18,30 @@ import { ValidationError } from "../error";
import { baseApiService } from "./base-api.service";
class PodcastsApiService {
getPodcasts = async () => {
return baseApiService.get("/api/v1/podcasts", z.array(podcast));
getPodcasts = async (request: GetPodcastsRequest) => {
// Validate the request
const parsedRequest = getPodcastsRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
// Format a user frendly error message
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
// Transform queries params to be string values
const transformedQueryParams = parsedRequest.data.queryParams
? Object.fromEntries(
Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)])
)
: undefined;
const queryParams = transformedQueryParams
? new URLSearchParams(transformedQueryParams).toString()
: undefined;
return baseApiService.get(`/api/v1/podcasts?${queryParams}`, z.array(podcast));
};
getPodcastByChatId = async (request: GetPodcastByChatIdRequest) => {

View file

@ -1,4 +1,5 @@
import type { GetChatsRequest } from "@/contracts/types/chat.types";
import type { GetPodcastsRequest } from "@/contracts/types/podcast.types";
export const cacheKeys = {
chats: {
@ -6,7 +7,10 @@ export const cacheKeys = {
globalQueryParams: (queries: GetChatsRequest["queryParams"]) =>
["chats", ...(queries ? Object.values(queries) : [])] as const,
},
podcasts: () => ["podcasts"] as const,
podcasts: {
globalQueryParams: (queries: GetPodcastsRequest["queryParams"]) =>
["podcasts", ...(queries ? Object.values(queries) : [])] as const,
},
auth: {
user: ["auth", "user"] as const,
},