feat: added duration to media attachments

added showLightboxFromAttachmentProps for future use
pull/3017/head
William Grant 2 years ago
parent c0cfe153c6
commit 537897dedb

@ -38,6 +38,8 @@ import { MIME } from '../../types';
import { AttachmentTypeWithPath } from '../../types/Attachment';
import {
THUMBNAIL_CONTENT_TYPE,
getAudioDuration,
getVideoDuration,
makeImageThumbnailBuffer,
makeVideoScreenshot,
} from '../../types/attachments/VisualAttachment';
@ -52,6 +54,7 @@ import { NoMessageInConversation } from './SubtleNotification';
import { ConversationHeaderWithDetails } from './header/ConversationHeader';
import { MessageDetail } from './message/message-item/MessageDetail';
import { isAudio } from '../../types/MIME';
import { HTMLDirection } from '../../util/i18n';
import { NoticeBanner } from '../NoticeBanner';
import { SessionSpinner } from '../basic/SessionSpinner';
@ -452,19 +455,25 @@ export class SessionConversation extends React.Component<Props, State> {
const attachmentWithVideoPreview = await renderVideoPreview(contentType, file, fileName);
this.addAttachments([attachmentWithVideoPreview]);
} else {
this.addAttachments([
{
file,
size: file.size,
contentType,
fileName,
url: '',
isVoiceMessage: false,
fileSize: null,
screenshot: null,
thumbnail: null,
},
]);
const attachment: StagedAttachmentType = {
file,
size: file.size,
contentType,
fileName,
url: '',
isVoiceMessage: false,
fileSize: null,
screenshot: null,
thumbnail: null,
};
if (isAudio(contentType)) {
const objectUrl = URL.createObjectURL(file);
const duration = await getAudioDuration({ objectUrl, contentType });
attachment.duration = duration;
}
this.addAttachments([attachment]);
}
} catch (e) {
window?.log?.error(
@ -567,6 +576,10 @@ const renderVideoPreview = async (contentType: string, file: File, fileName: str
objectUrl,
contentType: type,
});
const duration = await getVideoDuration({
objectUrl,
contentType: type,
});
const data = await blobToArrayBuffer(thumbnail);
const url = arrayBufferToObjectURL({
data,
@ -577,6 +590,7 @@ const renderVideoPreview = async (contentType: string, file: File, fileName: str
size: file.size,
fileName,
contentType,
duration,
videoUrl: objectUrl,
url,
isVoiceMessage: false,

@ -218,6 +218,47 @@ function attachmentIsAttachmentTypeWithPath(attac: any): attac is AttachmentType
return attac.path !== undefined;
}
export async function showLightboxFromAttachmentProps(
messageId: string,
selected: AttachmentTypeWithPath | AttachmentType | PropsForAttachment
) {
const found = await Data.getMessageById(messageId);
if (!found) {
window.log.warn(`showLightboxFromAttachmentProps Message not found ${messageId}}`);
return;
}
const msgAttachments = found.getPropsForMessage().attachments;
let index = -1;
const media = (msgAttachments || []).map(attachmentForMedia => {
index++;
const messageTimestamp =
found.get('timestamp') || found.get('serverTimestamp') || found.get('received_at');
return {
index: clone(index),
objectURL: attachmentForMedia.url || undefined,
contentType: attachmentForMedia.contentType,
attachment: attachmentForMedia,
messageSender: found.getSource(),
messageTimestamp,
messageId,
};
});
if (attachmentIsAttachmentTypeWithPath(selected)) {
const lightBoxOptions: LightBoxOptions = {
media: media as any,
attachment: selected,
};
window.inboxStore?.dispatch(showLightBox(lightBoxOptions));
} else {
window.log.warn('Attachment is not of the right type');
}
}
const onClickAttachment = async (onClickProps: {
attachment: AttachmentTypeWithPath | AttachmentType;
messageId: string;

@ -1,11 +1,11 @@
import moment from 'moment';
import { isUndefined, padStart } from 'lodash';
import moment from 'moment';
import * as MIME from './MIME';
import { saveURLAsFile } from '../util/saveURLAsFile';
import { SignalService } from '../protobuf';
import { isImageTypeSupported, isVideoTypeSupported } from '../util/GoogleChrome';
import { ATTACHMENT_DEFAULT_MAX_SIDE } from '../util/attachmentsUtil';
import { saveURLAsFile } from '../util/saveURLAsFile';
import * as MIME from './MIME';
import { THUMBNAIL_SIDE } from './attachments/VisualAttachment';
const MAX_WIDTH = THUMBNAIL_SIDE;
@ -29,6 +29,7 @@ export interface AttachmentType {
pending?: boolean;
width?: number;
height?: number;
duration?: string;
screenshot: {
height: number;
width: number;

@ -2,6 +2,7 @@
/* global document, URL, Blob */
import { blobToArrayBuffer, dataURLToBlob } from 'blob-util';
import moment from 'moment';
import { toLogFormat } from './Errors';
import {
@ -11,6 +12,7 @@ import {
import { ToastUtils } from '../../session/utils';
import { GoogleChrome } from '../../util';
import { autoScaleForAvatar, autoScaleForThumbnail } from '../../util/attachmentsUtil';
import { isAudio } from '../MIME';
export const THUMBNAIL_SIDE = 200;
export const THUMBNAIL_CONTENT_TYPE = 'image/png';
@ -106,6 +108,72 @@ export const makeVideoScreenshot = async ({
});
});
// TODO need to confirm this works
export async function getVideoDuration({
objectUrl,
contentType,
}: {
objectUrl: string;
contentType: string;
}): Promise<string> {
return new Promise((resolve, reject) => {
const video = document.createElement('video');
video.addEventListener('loadedmetadata', () => {
const duration = moment.duration(video.duration, 'seconds');
const durationString = moment.utc(duration.asMilliseconds()).format('m:ss');
resolve(durationString);
});
video.addEventListener('error', error => {
reject(error);
});
void getDecryptedMediaUrl(objectUrl, contentType, false)
.then(decryptedUrl => {
video.src = decryptedUrl;
})
.catch(err => {
reject(err);
});
});
}
// TODO need to confirm this works
export async function getAudioDuration({
objectUrl,
contentType,
}: {
objectUrl: string;
contentType: string;
}): Promise<string> {
if (!isAudio(contentType)) {
throw new Error('getAudioDuration can only be called with audio content type');
}
return new Promise((resolve, reject) => {
const audio = document.createElement('audio');
audio.addEventListener('loadedmetadata', () => {
const duration = moment.duration(audio.duration, 'seconds');
const durationString = moment.utc(duration.asMilliseconds()).format('m:ss');
resolve(durationString);
});
audio.addEventListener('error', error => {
reject(error);
});
void getDecryptedMediaUrl(objectUrl, contentType, false)
.then(decryptedUrl => {
audio.src = decryptedUrl;
})
.catch(err => {
reject(err);
});
});
}
export const makeObjectUrl = (data: ArrayBufferLike, contentType: string) => {
const blob = new Blob([data], {
type: contentType,

Loading…
Cancel
Save