You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
2.6 KiB
84 lines
2.6 KiB
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<TMediaUploadServiceContext | undefined>(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<string, string | undefined>() |
|
|
|
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 ( |
|
<MediaUploadServiceContext.Provider value={{ service, updateService, upload }}> |
|
{children} |
|
</MediaUploadServiceContext.Provider> |
|
) |
|
}
|
|
|