mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-25 19:15:18 +02:00
Add delete podcast api service
This commit is contained in:
parent
bf94903459
commit
1a954bc184
7 changed files with 72 additions and 50 deletions
|
|
@ -161,7 +161,7 @@ export default function ChatsPageClient({ searchSpaceId }: ChatsPageClientProps)
|
|||
const handleDeleteChat = async () => {
|
||||
if (!chatToDelete) return;
|
||||
|
||||
await deleteChat(chatToDelete.id);
|
||||
await deleteChat({ id: chatToDelete.id });
|
||||
|
||||
setDeleteDialogOpen(false);
|
||||
setChatToDelete(null);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { format } from "date-fns";
|
||||
import { useAtom } from "jotai";
|
||||
import {
|
||||
Calendar,
|
||||
MoreHorizontal,
|
||||
|
|
@ -19,6 +20,7 @@ import { AnimatePresence, motion, type Variants } from "motion/react";
|
|||
import Image from "next/image";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { deletePodcastMutationAtom } from "@/atoms/podcasts/podcast-mutation.atoms";
|
||||
// UI Components
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
|
@ -88,7 +90,6 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
id: number;
|
||||
title: string;
|
||||
} | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
// Audio player state
|
||||
const [currentPodcast, setCurrentPodcast] = useState<Podcast | null>(null);
|
||||
|
|
@ -101,6 +102,8 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
const [isMuted, setIsMuted] = useState(false);
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||
const currentObjectUrlRef = useRef<string | null>(null);
|
||||
const [{ isPending: isDeletingPodcast, mutateAsync: deletePodcast, error: deleteError }] =
|
||||
useAtom(deletePodcastMutationAtom);
|
||||
|
||||
// Add podcast image URL constant
|
||||
const PODCAST_IMAGE_URL =
|
||||
|
|
@ -330,7 +333,7 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
|
||||
try {
|
||||
const response = await podcastsApiService.loadPodcast({
|
||||
podcast,
|
||||
request: { id: podcast.id },
|
||||
controller,
|
||||
});
|
||||
const objectUrl = URL.createObjectURL(response);
|
||||
|
|
@ -364,38 +367,13 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
const handleDeletePodcast = async () => {
|
||||
if (!podcastToDelete) return;
|
||||
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
const token = localStorage.getItem("surfsense_bearer_token");
|
||||
if (!token) {
|
||||
setIsDeleting(false);
|
||||
return;
|
||||
}
|
||||
await deletePodcast({ id: podcastToDelete.id });
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/${podcastToDelete.id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to delete podcast: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Close dialog and refresh podcasts
|
||||
// Close dialog
|
||||
setDeleteDialogOpen(false);
|
||||
setPodcastToDelete(null);
|
||||
|
||||
// Update local state by removing the deleted podcast
|
||||
setPodcasts((prevPodcasts) =>
|
||||
prevPodcasts.filter((podcast) => podcast.id !== podcastToDelete.id)
|
||||
);
|
||||
|
||||
// If the current playing podcast is deleted, stop playback
|
||||
if (currentPodcast && currentPodcast.id === podcastToDelete.id) {
|
||||
if (audioRef.current) {
|
||||
|
|
@ -404,13 +382,9 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
setCurrentPodcast(null);
|
||||
setIsPlaying(false);
|
||||
}
|
||||
|
||||
toast.success("Podcast deleted successfully");
|
||||
} catch (error) {
|
||||
console.error("Error deleting podcast:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Failed to delete podcast");
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -933,17 +907,17 @@ export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClient
|
|||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setDeleteDialogOpen(false)}
|
||||
disabled={isDeleting}
|
||||
disabled={isDeletingPodcast}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDeletePodcast}
|
||||
disabled={isDeleting}
|
||||
disabled={isDeletingPodcast}
|
||||
className="gap-2"
|
||||
>
|
||||
{isDeleting ? (
|
||||
{isDeletingPodcast ? (
|
||||
<>
|
||||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
||||
Deleting...
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { atomWithMutation } from "jotai-tanstack-query";
|
||||
import { toast } from "sonner";
|
||||
import type { Chat } from "@/app/dashboard/[search_space_id]/chats/chats-client";
|
||||
import type { DeleteChatRequest } from "@/contracts/types/chat.types";
|
||||
import { chatsApiService } from "@/lib/apis/chats-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { queryClient } from "@/lib/query-client/client";
|
||||
|
|
@ -13,16 +14,16 @@ export const deleteChatMutationAtom = atomWithMutation((get) => {
|
|||
return {
|
||||
mutationKey: cacheKeys.activeSearchSpace.chats(searchSpaceId ?? ""),
|
||||
enabled: !!searchSpaceId && !!authToken,
|
||||
mutationFn: async (chatId: number) => {
|
||||
return chatsApiService.deleteChat({ id: chatId });
|
||||
mutationFn: async (request: DeleteChatRequest) => {
|
||||
return chatsApiService.deleteChat(request);
|
||||
},
|
||||
|
||||
onSuccess: (_, chatId) => {
|
||||
onSuccess: (_, request: DeleteChatRequest) => {
|
||||
toast.success("Chat deleted successfully");
|
||||
queryClient.setQueryData(
|
||||
cacheKeys.activeSearchSpace.chats(searchSpaceId!),
|
||||
(oldData: Chat[]) => {
|
||||
return oldData.filter((chat) => chat.id !== chatId);
|
||||
return oldData.filter((chat) => chat.id !== request.id);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export function PodcastPlayer({
|
|||
|
||||
try {
|
||||
const response = await podcastsApiService.loadPodcast({
|
||||
podcast,
|
||||
request: { id: podcast.id },
|
||||
controller,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,24 @@ export const getPodcastByChatIdRequest = z.object({
|
|||
chat_id: z.number(),
|
||||
});
|
||||
|
||||
export const getPodcastByChatResponse = podcast.nullish();
|
||||
export const getPodcastByChaIdResponse = podcast.nullish();
|
||||
|
||||
export const deletePodcastRequest = z.object({
|
||||
id: z.number(),
|
||||
});
|
||||
|
||||
export const deletePodcastResponse = z.object({
|
||||
message: z.literal("Podcast deleted successfully"),
|
||||
});
|
||||
|
||||
export const loadPodcastRequest = z.object({
|
||||
id: z.number(),
|
||||
});
|
||||
|
||||
export type GeneratePodcastRequest = z.infer<typeof generatePodcastRequest>;
|
||||
export type GetPodcastByChatIdRequest = z.infer<typeof getPodcastByChatIdRequest>;
|
||||
export type GetPodcastByChatResponse = z.infer<typeof getPodcastByChatResponse>;
|
||||
export type GetPodcastByChatIdResponse = z.infer<typeof getPodcastByChaIdResponse>;
|
||||
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>;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import {
|
||||
type DeletePodcastRequest,
|
||||
deletePodcastRequest,
|
||||
deletePodcastResponse,
|
||||
type GeneratePodcastRequest,
|
||||
type GetPodcastByChatIdRequest,
|
||||
generatePodcastRequest,
|
||||
getPodcastByChaIdResponse,
|
||||
getPodcastByChatIdRequest,
|
||||
getPodcastByChatResponse,
|
||||
type Podcast,
|
||||
type LoadPodcastRequest,
|
||||
loadPodcastRequest,
|
||||
} from "@/contracts/types/podcast.types";
|
||||
import { ValidationError } from "../error";
|
||||
import { baseApiService } from "./base-api.service";
|
||||
|
|
@ -24,7 +28,7 @@ class PodcastsApiService {
|
|||
|
||||
return baseApiService.get(
|
||||
`/api/v1/podcasts/by-chat/${request.chat_id}`,
|
||||
getPodcastByChatResponse
|
||||
getPodcastByChaIdResponse
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -46,16 +50,42 @@ class PodcastsApiService {
|
|||
};
|
||||
|
||||
loadPodcast = async ({
|
||||
podcast,
|
||||
request,
|
||||
controller,
|
||||
}: {
|
||||
podcast: Podcast;
|
||||
request: LoadPodcastRequest;
|
||||
controller?: AbortController;
|
||||
}) => {
|
||||
return await baseApiService.getBlob(`/api/v1/podcasts/${podcast.id}/stream`, {
|
||||
// Validate the request
|
||||
const parsedRequest = loadPodcastRequest.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}`);
|
||||
}
|
||||
|
||||
return await baseApiService.getBlob(`/api/v1/podcasts/${request.id}/stream`, {
|
||||
signal: controller?.signal,
|
||||
});
|
||||
};
|
||||
|
||||
deletePodcast = async (request: DeletePodcastRequest) => {
|
||||
// Validate the request
|
||||
const parsedRequest = deletePodcastRequest.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}`);
|
||||
}
|
||||
|
||||
return baseApiService.delete(`/api/v1/podcasts/${request.id}`, deletePodcastResponse);
|
||||
};
|
||||
}
|
||||
|
||||
export const podcastsApiService = new PodcastsApiService();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ export const cacheKeys = {
|
|||
activeSearchSpace: {
|
||||
chats: (searchSpaceId: string) => ["active-search-space", "chats", searchSpaceId] as const,
|
||||
activeChat: (chatId: string) => ["active-search-space", "active-chat", chatId] as const,
|
||||
podcasts: (searchSpaceId: string) =>
|
||||
["active-search-space", "podcasts", searchSpaceId] as const,
|
||||
},
|
||||
auth: {
|
||||
user: ["auth", "user"] as const,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue