Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 'use client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { api } from '@/lib/queryClient' import { songShareKeys } from '@/lib/queryKeys' import type { SongShareVisibility } from '@/db/schema/song-shares' export interface SongShareInfo { id: string url: string visibility: SongShareVisibility views: number createdAt: number lastViewedAt: number | null } interface ListResponse { shares: SongShareInfo[] } /** * Owner-side management of permanent, revocable public links to a song. * * Lists active shares and exposes create / update-visibility / revoke * mutations, each invalidating the per-song share list. */ export function useSongShares(songId: string | null, enabled = true) { const queryClient = useQueryClient() const invalidate = () => queryClient.invalidateQueries({ queryKey: songShareKeys.forSong(songId ?? '') }) const sharesQuery = useQuery({ queryKey: songShareKeys.forSong(songId ?? ''), queryFn: async (): Promise<SongShareInfo[]> => { const res = await api(`songs/${songId}/share`) if (!res.ok) throw new Error('Failed to load shares') const data = (await res.json()) as ListResponse return data.shares }, enabled: enabled && !!songId, }) const createShare = useMutation({ mutationFn: async (visibility: SongShareVisibility): Promise<SongShareInfo> => { const res = await api(`songs/${songId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ visibility }), }) if (!res.ok) { const err = await res.json().catch(() => ({})) throw new Error(err.error || 'Failed to create share') } return res.json() }, onSuccess: invalidate, }) const updateVisibility = useMutation({ mutationFn: async ({ token, visibility, }: { token: string visibility: SongShareVisibility }) => { const res = await api(`songs/${songId}/share?token=${encodeURIComponent(token)}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ visibility }), }) if (!res.ok) throw new Error('Failed to update share') return res.json() }, onSuccess: invalidate, }) const revokeShare = useMutation({ mutationFn: async (token: string) => { const res = await api(`songs/${songId}/share?token=${encodeURIComponent(token)}`, { method: 'DELETE', }) if (!res.ok) throw new Error('Failed to revoke share') return res.json() }, onSuccess: invalidate, }) return { shares: sharesQuery.data ?? [], isLoading: sharesQuery.isLoading, createShare, updateVisibility, revokeShare, } } |