File tree Expand file tree Collapse file tree 2 files changed +30
-9
lines changed
Expand file tree Collapse file tree 2 files changed +30
-9
lines changed Original file line number Diff line number Diff line change @@ -27,6 +27,19 @@ const SystemClock: AttachmentServiceClock = {
2727
2828const INIT_UPLOAD_TTL_SEC = 900 ;
2929
30+ const MIME_BY_EXT : Record < string , string > = {
31+ png : 'image/png' ,
32+ jpg : 'image/jpeg' ,
33+ jpeg : 'image/jpeg' ,
34+ gif : 'image/gif' ,
35+ webp : 'image/webp' ,
36+ svg : 'image/svg+xml' ,
37+ avif : 'image/avif' ,
38+ pdf : 'application/pdf' ,
39+ mp4 : 'video/mp4' ,
40+ webm : 'video/webm' ,
41+ } ;
42+
3043export class AttachmentService {
3144 private readonly uploads = new Map < string , PendingUpload > ( ) ;
3245 private readonly clock : AttachmentServiceClock ;
@@ -60,9 +73,17 @@ export class AttachmentService {
6073 }
6174 }
6275
63- /** Get the MIME content-type for a stored upload. */
76+ /** Get the MIME content-type for a stored upload.
77+ * Falls back to deriving the type from the file extension so the correct
78+ * Content-Type is returned even after a server restart (when the in-memory
79+ * uploads map has been cleared).
80+ */
6481 getContentType ( objectKey : string ) : string {
65- return this . uploads . get ( objectKey ) ?. contentType ?? 'application/octet-stream' ;
82+ const stored = this . uploads . get ( objectKey ) ?. contentType ;
83+ if ( stored && stored !== 'application/octet-stream' ) return stored ;
84+ // Derive from extension — objectKey preserves the original filename suffix.
85+ const ext = objectKey . split ( '.' ) . pop ( ) ?. toLowerCase ( ) ?? '' ;
86+ return MIME_BY_EXT [ ext ] ?? stored ?? 'application/octet-stream' ;
6687 }
6788
6889 initUpload ( input : {
Original file line number Diff line number Diff line change @@ -257,14 +257,14 @@ export function useMessageSender() {
257257 fileContentType : input . file . type || "application/octet-stream" ,
258258 } )
259259
260- // downloadUrl is the node-served URL; send it as the payload so the
261- // receiver can display the image directly from the sender's node.
260+ // downloadUrl is the node-served URL. Use it as displayText so the URL
261+ // is persistent after page reload (blob: URLs die on navigation). By this
262+ // point the file is already saved on the server (saved inline in
263+ // completeAttachmentUpload above), so the sender can fetch it immediately.
262264 const downloadUrl = ( completed as { downloadUrl ?: string } ) . downloadUrl
263- ?? `${ initialized . uploadUrl . replace ( / \/ [ ^ / ] + $ / , '' ) } ` // fallback: strip objectKey from uploadUrl
264- const displayText = contentType === "image"
265- ? URL . createObjectURL ( input . file ) // local blob for sender's own preview
266- : input . file . name
267- const rawPayloadText = downloadUrl // what the receiver actually uses
265+ ?? `${ initialized . uploadUrl . replace ( / \/ [ ^ / ] + $ / , '' ) } ` // fallback
266+ const displayText = contentType === "image" ? downloadUrl : input . file . name
267+ const rawPayloadText = downloadUrl
268268
269269 return sendEnvelope ( {
270270 conversationId : conversation . conversationId ,
You can’t perform that action at this time.
0 commit comments