Convert images, audio, and voice messages over to the new helper
							parent
							
								
									040802e29f
								
							
						
					
					
						commit
						5fce0ccd9d
					
				|  | @ -18,22 +18,22 @@ import React from "react"; | |||
| import { MatrixEvent } from "matrix-js-sdk/src/models/event"; | ||||
| import { replaceableComponent } from "../../../utils/replaceableComponent"; | ||||
| import { Playback } from "../../../voice/Playback"; | ||||
| import MFileBody from "./MFileBody"; | ||||
| import InlineSpinner from '../elements/InlineSpinner'; | ||||
| import { _t } from "../../../languageHandler"; | ||||
| import { mediaFromContent } from "../../../customisations/Media"; | ||||
| import { decryptFile } from "../../../utils/DecryptFile"; | ||||
| import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent"; | ||||
| import AudioPlayer from "../audio_messages/AudioPlayer"; | ||||
| import { MediaEventHelper } from "../../../utils/MediaEventHelper"; | ||||
| import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent"; | ||||
| import { TileShape } from "../rooms/EventTile"; | ||||
| 
 | ||||
| interface IProps { | ||||
|     mxEvent: MatrixEvent; | ||||
|     tileShape?: TileShape; | ||||
|     mediaEventHelper: MediaEventHelper; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     error?: Error; | ||||
|     playback?: Playback; | ||||
|     decryptedBlob?: Blob; | ||||
| } | ||||
| 
 | ||||
| @replaceableComponent("views.messages.MAudioBody") | ||||
|  | @ -46,33 +46,34 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> { | |||
| 
 | ||||
|     public async componentDidMount() { | ||||
|         let buffer: ArrayBuffer; | ||||
|         const content: IMediaEventContent = this.props.mxEvent.getContent(); | ||||
|         const media = mediaFromContent(content); | ||||
|         if (media.isEncrypted) { | ||||
| 
 | ||||
|         try { | ||||
|             try { | ||||
|                 const blob = await decryptFile(content.file); | ||||
|                 const blob = await this.props.mediaEventHelper.sourceBlob.value; | ||||
|                 buffer = await blob.arrayBuffer(); | ||||
|                 this.setState({ decryptedBlob: blob }); | ||||
|             } catch (e) { | ||||
|                 this.setState({ error: e }); | ||||
|                 console.warn("Unable to decrypt audio message", e); | ||||
|                 return; // stop processing the audio file
 | ||||
|             } | ||||
|         } else { | ||||
|             try { | ||||
|                 buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer()); | ||||
|             } catch (e) { | ||||
|                 this.setState({ error: e }); | ||||
|                 console.warn("Unable to download audio message", e); | ||||
|                 return; // stop processing the audio file
 | ||||
|             } | ||||
|         } catch (e) { | ||||
|             this.setState({ error: e }); | ||||
|             console.warn("Unable to decrypt/download audio message", e); | ||||
|             return; // stop processing the audio file
 | ||||
|         } | ||||
| 
 | ||||
|         // We should have a buffer to work with now: let's set it up
 | ||||
|         const playback = new Playback(buffer); | ||||
| 
 | ||||
|         // Note: we don't actually need a waveform to render an audio event, but voice messages do.
 | ||||
|         const content = this.props.mxEvent.getContent<IMediaEventContent>(); | ||||
|         const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024); | ||||
| 
 | ||||
|         // We should have a buffer to work with now: let's set it up
 | ||||
|         const playback = new Playback(buffer, waveform); | ||||
|         playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent); | ||||
|         this.setState({ playback }); | ||||
|         // Note: the RecordingPlayback component will handle preparing the Playback class for us.
 | ||||
| 
 | ||||
|         // Note: the components later on will handle preparing the Playback class for us.
 | ||||
|     } | ||||
| 
 | ||||
|     public componentWillUnmount() { | ||||
|  | @ -103,7 +104,7 @@ export default class MAudioBody extends React.PureComponent<IProps, IState> { | |||
|         return ( | ||||
|             <span className="mx_MAudioBody"> | ||||
|                 <AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} /> | ||||
|                 <MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} /> | ||||
|                 {/*<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />*/} | ||||
|             </span> | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| /* | ||||
| Copyright 2015, 2016 OpenMarket Ltd | ||||
| Copyright 2018 New Vector Ltd | ||||
| Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. | ||||
| Copyright 2018, 2019 Michael Telatynski <7t3chguy@gmail.com> | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  | @ -21,7 +20,6 @@ import { Blurhash } from "react-blurhash"; | |||
| 
 | ||||
| import MFileBody from './MFileBody'; | ||||
| import Modal from '../../../Modal'; | ||||
| import { decryptFile } from '../../../utils/DecryptFile'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import SettingsStore from "../../../settings/SettingsStore"; | ||||
| import MatrixClientContext from "../../../contexts/MatrixClientContext"; | ||||
|  | @ -34,6 +32,7 @@ import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; | |||
| import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent'; | ||||
| import ImageView from '../elements/ImageView'; | ||||
| import { SyncState } from 'matrix-js-sdk/src/sync.api'; | ||||
| import { MediaEventHelper } from "../../../utils/MediaEventHelper"; | ||||
| 
 | ||||
| export interface IProps { | ||||
|     /* the MatrixEvent to show */ | ||||
|  | @ -46,6 +45,7 @@ export interface IProps { | |||
| 
 | ||||
|     /* the permalinkCreator */ | ||||
|     permalinkCreator?: RoomPermalinkCreator; | ||||
|     mediaEventHelper: MediaEventHelper; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|  | @ -257,38 +257,24 @@ export default class MImageBody extends React.Component<IProps, IState> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private downloadImage(): void { | ||||
|     private async downloadImage() { | ||||
|         const content = this.props.mxEvent.getContent(); | ||||
|         if (content.file !== undefined && this.state.decryptedUrl === null) { | ||||
|             let thumbnailPromise = Promise.resolve(null); | ||||
|             if (content.info && content.info.thumbnail_file) { | ||||
|                 thumbnailPromise = decryptFile( | ||||
|                     content.info.thumbnail_file, | ||||
|                 ).then(function(blob) { | ||||
|                     return URL.createObjectURL(blob); | ||||
|         if (this.props.mediaEventHelper.media.isEncrypted && this.state.decryptedUrl === null) { | ||||
|             try { | ||||
|                 const thumbnailUrl = await this.props.mediaEventHelper.thumbnailUrl.value; | ||||
|                 this.setState({ | ||||
|                     decryptedUrl: await this.props.mediaEventHelper.sourceUrl.value, | ||||
|                     decryptedThumbnailUrl: thumbnailUrl, | ||||
|                     decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value, | ||||
|                 }); | ||||
|             } | ||||
|             let decryptedBlob; | ||||
|             thumbnailPromise.then((thumbnailUrl) => { | ||||
|                 return decryptFile(content.file).then(function(blob) { | ||||
|                     decryptedBlob = blob; | ||||
|                     return URL.createObjectURL(blob); | ||||
|                 }).then((contentUrl) => { | ||||
|                     if (this.unmounted) return; | ||||
|                     this.setState({ | ||||
|                         decryptedUrl: contentUrl, | ||||
|                         decryptedThumbnailUrl: thumbnailUrl, | ||||
|                         decryptedBlob: decryptedBlob, | ||||
|                     }); | ||||
|                 }); | ||||
|             }).catch((err) => { | ||||
|             } catch (err) { | ||||
|                 if (this.unmounted) return; | ||||
|                 console.warn("Unable to decrypt attachment: ", err); | ||||
|                 // Set a placeholder image when we can't decrypt the image.
 | ||||
|                 this.setState({ | ||||
|                     error: err, | ||||
|                 }); | ||||
|             }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -300,10 +286,10 @@ export default class MImageBody extends React.Component<IProps, IState> { | |||
|             localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true"; | ||||
| 
 | ||||
|         if (showImage) { | ||||
|             // Don't download anything becaue we don't want to display anything.
 | ||||
|             // noinspection JSIgnoredPromiseFromCall
 | ||||
|             this.downloadImage(); | ||||
|             this.setState({ showImage: true }); | ||||
|         } | ||||
|         } // else don't download anything because we don't want to display anything.
 | ||||
| 
 | ||||
|         this._afterComponentDidMount(); | ||||
|     } | ||||
|  | @ -316,13 +302,6 @@ export default class MImageBody extends React.Component<IProps, IState> { | |||
|     componentWillUnmount() { | ||||
|         this.unmounted = true; | ||||
|         this.context.removeListener('sync', this.onClientSync); | ||||
| 
 | ||||
|         if (this.state.decryptedUrl) { | ||||
|             URL.revokeObjectURL(this.state.decryptedUrl); | ||||
|         } | ||||
|         if (this.state.decryptedThumbnailUrl) { | ||||
|             URL.revokeObjectURL(this.state.decryptedThumbnailUrl); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected messageContent( | ||||
|  |  | |||
|  | @ -15,72 +15,16 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import React from "react"; | ||||
| import { MatrixEvent } from "matrix-js-sdk/src/models/event"; | ||||
| import { replaceableComponent } from "../../../utils/replaceableComponent"; | ||||
| import { Playback } from "../../../voice/Playback"; | ||||
| import MFileBody from "./MFileBody"; | ||||
| import InlineSpinner from '../elements/InlineSpinner'; | ||||
| import { _t } from "../../../languageHandler"; | ||||
| import { mediaFromContent } from "../../../customisations/Media"; | ||||
| import { decryptFile } from "../../../utils/DecryptFile"; | ||||
| import RecordingPlayback from "../audio_messages/RecordingPlayback"; | ||||
| import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent"; | ||||
| import { TileShape } from "../rooms/EventTile"; | ||||
| 
 | ||||
| interface IProps { | ||||
|     mxEvent: MatrixEvent; | ||||
|     tileShape?: TileShape; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     error?: Error; | ||||
|     playback?: Playback; | ||||
|     decryptedBlob?: Blob; | ||||
| } | ||||
| import MAudioBody from "./MAudioBody"; | ||||
| 
 | ||||
| @replaceableComponent("views.messages.MVoiceMessageBody") | ||||
| export default class MVoiceMessageBody extends React.PureComponent<IProps, IState> { | ||||
|     constructor(props: IProps) { | ||||
|         super(props); | ||||
| export default class MVoiceMessageBody extends MAudioBody { | ||||
| 
 | ||||
|         this.state = {}; | ||||
|     } | ||||
| 
 | ||||
|     public async componentDidMount() { | ||||
|         let buffer: ArrayBuffer; | ||||
|         const content: IMediaEventContent = this.props.mxEvent.getContent(); | ||||
|         const media = mediaFromContent(content); | ||||
|         if (media.isEncrypted) { | ||||
|             try { | ||||
|                 const blob = await decryptFile(content.file); | ||||
|                 buffer = await blob.arrayBuffer(); | ||||
|                 this.setState({ decryptedBlob: blob }); | ||||
|             } catch (e) { | ||||
|                 this.setState({ error: e }); | ||||
|                 console.warn("Unable to decrypt voice message", e); | ||||
|                 return; // stop processing the audio file
 | ||||
|             } | ||||
|         } else { | ||||
|             try { | ||||
|                 buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer()); | ||||
|             } catch (e) { | ||||
|                 this.setState({ error: e }); | ||||
|                 console.warn("Unable to download voice message", e); | ||||
|                 return; // stop processing the audio file
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024); | ||||
| 
 | ||||
|         // We should have a buffer to work with now: let's set it up
 | ||||
|         const playback = new Playback(buffer, waveform); | ||||
|         this.setState({ playback }); | ||||
|         // Note: the RecordingPlayback component will handle preparing the Playback class for us.
 | ||||
|     } | ||||
| 
 | ||||
|     public componentWillUnmount() { | ||||
|         this.state.playback?.destroy(); | ||||
|     } | ||||
|     // A voice message is an audio file but rendered in a special way.
 | ||||
| 
 | ||||
|     public render() { | ||||
|         if (this.state.error) { | ||||
|  | @ -106,7 +50,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat | |||
|         return ( | ||||
|             <span className="mx_MVoiceMessageBody"> | ||||
|                 <RecordingPlayback playback={this.state.playback} tileShape={this.props.tileShape} /> | ||||
|                 <MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} /> | ||||
|                 {/*<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />*/} | ||||
|             </span> | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston