Live video on the AT Protocol

refactor to hook, clean up upload on unmount

+53 -30
+53 -30
js/components/src/streamplace-store/stream.tsx
··· 6 6 import { useUrl } from "./streamplace-store"; 7 7 import { usePDSAgent } from "./xrpc"; 8 8 9 - const uploadThumbnail = async ( 10 - pdsAgent: StreamplaceAgent, 11 - customThumbnail?: Blob, 12 - ) => { 13 - if (customThumbnail) { 14 - let tries = 0; 15 - try { 16 - let thumbnail = await pdsAgent.uploadBlob(customThumbnail); 9 + import { useEffect, useRef } from "react"; 17 10 18 - while ( 19 - thumbnail.data.blob.size === 0 && 20 - customThumbnail.size !== 0 && 21 - tries < 12 22 - ) { 23 - console.warn( 24 - "Reuploading blob as blob sizes don't match! Blob size recieved is", 25 - thumbnail.data.blob.size, 26 - "and sent blob size is", 27 - customThumbnail.size, 28 - ); 29 - thumbnail = await pdsAgent.uploadBlob(customThumbnail); 30 - } 11 + const useUploadThumbnail = () => { 12 + const abortRef = useRef<AbortController | null>(null); 13 + 14 + useEffect(() => { 15 + return () => { 16 + // On unmount, abort any ongoing upload 17 + abortRef.current?.abort(); 18 + }; 19 + }, []); 20 + 21 + const uploadThumbnail = async ( 22 + pdsAgent: StreamplaceAgent, 23 + customThumbnail?: Blob, 24 + ) => { 25 + if (!customThumbnail) return undefined; 31 26 32 - if (tries === 3) { 33 - throw new Error(`Could not successfully upload blob (tried ${tries}x)`); 34 - } 27 + abortRef.current = new AbortController(); 28 + const { signal } = abortRef.current; 29 + 30 + const maxTries = 3; 31 + let lastError: unknown = null; 35 32 36 - if (thumbnail.success) { 37 - console.log("Successfully uploaded thumbnail"); 38 - return thumbnail.data.blob; 33 + for (let tries = 0; tries < maxTries; tries++) { 34 + try { 35 + const thumbnail = await pdsAgent.uploadBlob(customThumbnail, { 36 + signal, 37 + }); 38 + if ( 39 + thumbnail.success && 40 + thumbnail.data.blob.size === customThumbnail.size 41 + ) { 42 + console.log("Successfully uploaded thumbnail"); 43 + return thumbnail.data.blob; 44 + } else { 45 + console.warn( 46 + `Blob size mismatch (attempt ${tries + 1}): received ${thumbnail.data.blob.size}, expected ${customThumbnail.size}`, 47 + ); 48 + } 49 + } catch (e) { 50 + if (signal.aborted) { 51 + console.warn("Upload aborted"); 52 + return undefined; 53 + } 54 + lastError = e; 55 + console.warn(`Error uploading thumbnail (attempt ${tries + 1}): ${e}`); 39 56 } 40 - } catch (e) { 41 - throw new Error("Error uploading thumbnail: " + e); 42 57 } 43 - } 58 + 59 + throw new Error( 60 + `Could not successfully upload blob after ${maxTries} attempts. Last error: ${lastError}`, 61 + ); 62 + }; 63 + 64 + return uploadThumbnail; 44 65 }; 45 66 46 67 async function createNewPost( ··· 99 120 export function useCreateStreamRecord() { 100 121 let agent = usePDSAgent(); 101 122 let url = useUrl(); 123 + const uploadThumbnail = useUploadThumbnail(); 102 124 103 125 return async ( 104 126 title: string, ··· 206 228 export function useUpdateStreamRecord() { 207 229 let agent = usePDSAgent(); 208 230 let url = useUrl(); 231 + const uploadThumbnail = useUploadThumbnail(); 209 232 210 233 return async ( 211 234 title: string,