import { simplifyUrl } from '@/lib/url' import storage from '@/services/local-storage.service' import { createContext, useContext, useState } from 'react' import { z } from 'zod' import { useNostr } from './NostrProvider' type TMediaUploadServiceContext = { service: string updateService: (service: string) => void upload: (file: File) => Promise<{ url: string; tags: string[][] }> } const MediaUploadServiceContext = createContext(undefined) export const useMediaUploadService = () => { const context = useContext(MediaUploadServiceContext) if (!context) { throw new Error('useMediaUploadService must be used within MediaUploadServiceProvider') } return context } const ServiceUploadUrlMap = new Map() export function MediaUploadServiceProvider({ children }: { children: React.ReactNode }) { const { signHttpAuth } = useNostr() const [service, setService] = useState(storage.getMediaUploadService()) const updateService = (newService: string) => { setService(newService) storage.setMediaUploadService(newService) } const upload = async (file: File) => { let uploadUrl = ServiceUploadUrlMap.get(service) if (!uploadUrl) { const response = await fetch(`${service}/.well-known/nostr/nip96.json`) if (!response.ok) { throw new Error( `${simplifyUrl(service)} does not work, please try another service in your settings` ) } const data = await response.json() uploadUrl = data?.api_url if (!uploadUrl) { throw new Error( `${simplifyUrl(service)} does not work, please try another service in your settings` ) } ServiceUploadUrlMap.set(service, uploadUrl) } const formData = new FormData() formData.append('file', file) const auth = await signHttpAuth(uploadUrl, 'POST') const response = await fetch(uploadUrl, { method: 'POST', body: formData, headers: { Authorization: auth } }) if (!response.ok) { throw new Error(response.status.toString() + ' ' + response.statusText) } const data = await response.json() const tags = z.array(z.array(z.string())).parse(data.nip94_event?.tags ?? []) const imageUrl = tags.find(([tagName]) => tagName === 'url')?.[1] if (imageUrl) { return { url: imageUrl, tags } } else { throw new Error('No image url found') } } return ( {children} ) }