diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js
index 960afe54f..040a0d437 100644
--- a/js/views/conversation_view.js
+++ b/js/views/conversation_view.js
@@ -583,27 +583,14 @@
       // - [ ] Fetch file attachments
       // - [ ] Add mechanism to fetch more data
 
-      const mediaWithoutAttachmentData =
-        await Signal.Backbone.Conversation.fetchVisualMediaAttachments({
-            conversationId: this.model.get('id'),
-            WhisperMessageCollection: Whisper.MessageCollection,
-        });
-
-      const mediaWithAttachmentData =
-        await Promise.all(mediaWithoutAttachmentData.map(Signal.Migrations.loadMessage));
-
-      const withObjectURL = message => {
-        if (!message.attachments || message.attachments.length === 0) {
-            throw new TypeError('`message.attachments` cannot be empty');
-        }
-        const attachment = message.attachments[0];
-        const objectURL = Signal.Util.arrayBufferToObjectURL({
-            data: attachment.data,
-            type: attachment.contentType,
-        });
-        return Object.assign({}, message, {objectURL});
-      }
-      const mediaWithObjectURLs = mediaWithAttachmentData.map(withObjectURL);
+      const media = await Signal.Backbone.Conversation.fetchVisualMediaAttachments({
+        conversationId: this.model.get('id'),
+        WhisperMessageCollection: Whisper.MessageCollection,
+      });
+      const loadMessages = Signal.Components.PropTypes.Message.loadWithObjectURL(
+        Signal.Migrations.loadMessage
+      );
+      const mediaWithObjectURLs = await loadMessages(media);
 
       const props = {
         media: mediaWithObjectURLs,
diff --git a/preload.js b/preload.js
index e47f0dc39..8b6eaa2a3 100644
--- a/preload.js
+++ b/preload.js
@@ -166,8 +166,14 @@ const { MediaGallery } =
   require('./ts/components/conversation/media-gallery/MediaGallery');
 const { Quote } = require('./ts/components/conversation/Quote');
 
+const PropTypesMessage =
+  require('./ts/components/conversation/media-gallery/propTypes/Message');
+
 window.Signal.Components = {
   MediaGallery,
+  PropTypes: {
+    Message: PropTypesMessage,
+  },
   Quote,
 };
 
diff --git a/ts/backbone/Conversation.ts b/ts/backbone/Conversation.ts
index 2d652029a..f687d72fd 100644
--- a/ts/backbone/Conversation.ts
+++ b/ts/backbone/Conversation.ts
@@ -3,8 +3,8 @@
  */
 import is from '@sindresorhus/is';
 
-import { deferredToPromise } from '../../js/modules/deferred_to_promise';
 import { Collection as BackboneCollection } from '../types/backbone/Collection';
+import { deferredToPromise } from '../../js/modules/deferred_to_promise';
 import { Message } from '../types/Message';
 
 export const fetchVisualMediaAttachments = async ({
diff --git a/ts/components/conversation/media-gallery/propTypes/Message.tsx b/ts/components/conversation/media-gallery/propTypes/Message.tsx
index c86a7e29f..b37b1cba3 100644
--- a/ts/components/conversation/media-gallery/propTypes/Message.tsx
+++ b/ts/components/conversation/media-gallery/propTypes/Message.tsx
@@ -1,15 +1,45 @@
 /**
  * @prettier
  */
-export interface Message {
-  body?: string;
+import is from '@sindresorhus/is';
+
+import { arrayBufferToObjectURL } from '../../../../util/arrayBufferToObjectURL';
+import { Attachment } from '../../../../types/Attachment';
+import { MapAsync } from '../../../../types/MapAsync';
+import { MIMEType } from '../../../../types/MIME';
+
+export type Message = {
+  attachments: Array<Attachment>;
   received_at: number;
-  attachments: Array<{
-    data?: ArrayBuffer;
-    fileName?: string;
-    size?: number;
-  }>;
-
-  // TODO: Revisit
-  objectURL?: string;
-}
+} & { objectURL?: string };
+
+const DEFAULT_CONTENT_TYPE: MIMEType = 'application/octet-stream' as MIMEType;
+
+export const loadWithObjectURL = (loadMessage: MapAsync<Message>) => async (
+  media: Array<Message>
+): Promise<Array<Message>> => {
+  if (!is.function_(loadMessage)) {
+    throw new TypeError("'loadMessage' must be a function");
+  }
+  if (!is.array(media)) {
+    throw new TypeError("'media' must be a function");
+  }
+
+  const mediaWithAttachmentData = await Promise.all(media.map(loadMessage));
+  return mediaWithAttachmentData.map(withObjectURL);
+};
+
+const withObjectURL = (message: Message): Message => {
+  if (message.attachments.length === 0) {
+    throw new TypeError('`message.attachments` cannot be empty');
+  }
+  const attachment = message.attachments[0];
+  const objectURL = arrayBufferToObjectURL({
+    data: attachment.data,
+    type: attachment.contentType || DEFAULT_CONTENT_TYPE,
+  });
+  return {
+    ...message,
+    objectURL,
+  };
+};