Page MenuHomePhabricator

D7279.id24557.diff
No OneTemporary

D7279.id24557.diff

diff --git a/lib/types/media-types.js b/lib/types/media-types.js
--- a/lib/types/media-types.js
+++ b/lib/types/media-types.js
@@ -10,6 +10,8 @@
export type MediaType = 'photo' | 'video';
+export type EncryptedMediaType = 'encrypted_photo' | 'encrypted_video';
+
export type Image = {
+id: string,
+uri: string,
diff --git a/web/media/encrypted-multimedia.react.js b/web/media/encrypted-multimedia.react.js
new file mode 100644
--- /dev/null
+++ b/web/media/encrypted-multimedia.react.js
@@ -0,0 +1,97 @@
+// @flow
+
+import * as React from 'react';
+import 'react-circular-progressbar/dist/styles.css';
+import { AlertCircle as AlertCircleIcon } from 'react-feather';
+
+import type { EncryptedMediaType } from 'lib/types/media-types.js';
+
+import { decryptMedia } from './encryption-utils.js';
+import css from './media.css';
+import LoadingIndicator from '../loading-indicator.react.js';
+
+type Props = {
+ +holder: string,
+ +encryptionKey: string,
+ +type: EncryptedMediaType,
+};
+
+function EncryptedMultimedia(props: Props): React.Node {
+ const { holder, encryptionKey } = props;
+
+ const [source, setSource] = React.useState(null);
+ const videoRef = React.useRef(null);
+
+ React.useEffect(() => {
+ let isMounted = true,
+ uriToDispose;
+ setSource(null);
+
+ const loadDecrypted = async () => {
+ const { result } = await decryptMedia(holder, encryptionKey);
+ if (!isMounted) {
+ return;
+ }
+
+ if (result.success) {
+ const { uri } = result;
+ setSource({ uri });
+ uriToDispose = uri;
+ } else {
+ setSource({ error: result.reason });
+ }
+ };
+
+ loadDecrypted();
+
+ return () => {
+ isMounted = false;
+ if (uriToDispose) {
+ URL.revokeObjectURL(uriToDispose);
+ }
+ };
+ }, [holder, encryptionKey]);
+
+ // we need to update the video source when the source changes
+ // because re-rendering the <source> element wouldn't reload parent <video>
+ React.useEffect(() => {
+ if (videoRef.current && source?.uri) {
+ videoRef.current.src = source.uri;
+ videoRef.current.load();
+ }
+ }, [source]);
+
+ let loadingIndicator, errorIndicator;
+
+ if (!source) {
+ loadingIndicator = (
+ <LoadingIndicator
+ status="loading"
+ size="large"
+ color="white"
+ loadingClassName={css.loadingIndicator}
+ />
+ );
+ }
+
+ if (source?.error) {
+ errorIndicator = <AlertCircleIcon className={css.uploadError} size={36} />;
+ }
+
+ let mediaNode;
+ if (props.type === 'encrypted_photo') {
+ mediaNode = <img src={source?.uri} key={holder} />;
+ } else {
+ mediaNode = <video controls ref={videoRef} key={holder} />;
+ }
+
+ return (
+ <>
+ {mediaNode}
+ {loadingIndicator}
+ {errorIndicator}
+ </>
+ );
+}
+
+export default EncryptedMultimedia;
diff --git a/web/media/media.css b/web/media/media.css
--- a/web/media/media.css
+++ b/web/media/media.css
@@ -32,7 +32,7 @@
span.multimedia:hover > .multimediaImage svg.removeUpload {
display: inherit;
}
-span.multimedia > svg.uploadError {
+span.multimedia svg.uploadError {
position: absolute;
top: 0;
bottom: 0;
@@ -55,6 +55,17 @@
height: 50px;
}
+span.multimedia .loadingIndicator {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: auto auto;
+ width: 25px;
+ height: 25px;
+}
+
:global(.CircularProgressbar-background) {
fill: #666 !important;
}

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 23, 9:26 AM (18 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2694347
Default Alt Text
D7279.id24557.diff (3 KB)

Event Timeline