Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3351820
D10854.id36367.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D10854.id36367.diff
View Options
diff --git a/native/media/image-modal.react.js b/native/media/image-modal.react.js
--- a/native/media/image-modal.react.js
+++ b/native/media/image-modal.react.js
@@ -1,7 +1,6 @@
// @flow
import Clipboard from '@react-native-clipboard/clipboard';
-import invariant from 'invariant';
import * as React from 'react';
import type { MediaInfo, Dimensions } from 'lib/types/media-types.js';
@@ -42,15 +41,10 @@
const intentionalSaveMedia = useIntentionalSaveMedia();
const onPressSave = React.useCallback(() => {
- invariant(
- mediaInfo.type === 'photo' || mediaInfo.type === 'video',
- 'saving media of type ' + mediaInfo.type + ' is not supported',
- );
-
- const { id: uploadID, uri } = mediaInfo;
+ const uploadID = mediaInfo.id;
const { id: messageServerID, localID: messageLocalID } = item.messageInfo;
const ids = { uploadID, messageServerID, messageLocalID };
- return intentionalSaveMedia(uri, ids);
+ return intentionalSaveMedia(mediaInfo, ids);
}, [intentionalSaveMedia, item.messageInfo, mediaInfo]);
const onPressCopy = React.useCallback(() => {
diff --git a/native/media/save-media.js b/native/media/save-media.js
--- a/native/media/save-media.js
+++ b/native/media/save-media.js
@@ -13,6 +13,7 @@
MediaMissionStep,
MediaMissionResult,
MediaMissionFailure,
+ MediaInfo,
} from 'lib/types/media-types.js';
import {
reportTypes,
@@ -27,6 +28,7 @@
} from 'lib/utils/report-utils.js';
import { fetchBlob } from './blob-utils.js';
+import { decryptMedia } from './encryption-utils.js';
import {
fetchAssetInfo,
fetchFileInfo,
@@ -43,7 +45,7 @@
import { requestAndroidPermission } from '../utils/android-permissions.js';
export type IntentionalSaveMedia = (
- uri: string,
+ mediaInfo: MediaInfo,
ids: {
uploadID: string,
messageServerID: ?string,
@@ -56,7 +58,7 @@
const mediaReportsEnabled = useIsReportEnabled('mediaReports');
return React.useCallback(
async (
- uri: string,
+ mediaInfo: MediaInfo,
ids: {
uploadID: string,
messageServerID: ?string,
@@ -64,11 +66,19 @@
},
) => {
const start = Date.now();
+ const { uri: mediaURI, blobURI, holder, encryptionKey } = mediaInfo;
+ const uri = mediaURI ?? blobURI ?? holder;
+ invariant(uri, 'mediaInfo should have a uri or a blobURI');
+
const steps: Array<MediaMissionStep> = [
{ step: 'save_media', uri, time: start },
];
- const { resultPromise, reportPromise } = saveMedia(uri, 'request');
+ const { resultPromise, reportPromise } = saveMedia(
+ uri,
+ encryptionKey,
+ 'request',
+ );
const result = await resultPromise;
const userTime = Date.now() - start;
@@ -130,6 +140,7 @@
function saveMedia(
uri: string,
+ encryptionKey?: ?string,
permissions?: Permissions = 'check',
): {
resultPromise: Promise<MediaMissionResult>,
@@ -142,7 +153,12 @@
}
};
- const reportPromise = innerSaveMedia(uri, permissions, sendResult);
+ const reportPromise = innerSaveMedia(
+ uri,
+ encryptionKey,
+ permissions,
+ sendResult,
+ );
const resultPromise = new Promise<MediaMissionResult>(resolve => {
resolveResult = resolve;
});
@@ -152,13 +168,14 @@
async function innerSaveMedia(
uri: string,
+ encryptionKey?: ?string,
permissions: Permissions,
sendResult: (result: MediaMissionResult) => void,
): Promise<$ReadOnlyArray<MediaMissionStep>> {
if (Platform.OS === 'android') {
- return await saveMediaAndroid(uri, permissions, sendResult);
+ return await saveMediaAndroid(uri, encryptionKey, permissions, sendResult);
} else if (Platform.OS === 'ios') {
- return await saveMediaIOS(uri, sendResult);
+ return await saveMediaIOS(uri, encryptionKey, sendResult);
} else {
sendResult({ success: false, reason: 'save_unsupported' });
return [];
@@ -172,6 +189,7 @@
// Pictures directory, and then trigger the media scanner to pick it up
async function saveMediaAndroid(
inputURI: string,
+ encryptionKey?: ?string,
permissions: Permissions,
sendResult: (result: MediaMissionResult) => void,
): Promise<$ReadOnlyArray<MediaMissionStep>> {
@@ -221,7 +239,11 @@
promises.push(
(async () => {
const { result: tempSaveResult, steps: tempSaveSteps } =
- await saveRemoteMediaToDisk(uri, temporaryDirectoryPath);
+ await saveRemoteMediaToDisk(
+ uri,
+ encryptionKey,
+ temporaryDirectoryPath,
+ );
steps.push(...tempSaveSteps);
if (!tempSaveResult.success) {
success = false;
@@ -277,6 +299,7 @@
// On iOS, we save the media to the camera roll
async function saveMediaIOS(
inputURI: string,
+ encryptionKey?: ?string,
sendResult: (result: MediaMissionResult) => void,
): Promise<$ReadOnlyArray<MediaMissionStep>> {
const steps: Array<MediaMissionStep> = [];
@@ -285,7 +308,7 @@
let tempFile;
if (uri.startsWith('http')) {
const { result: tempSaveResult, steps: tempSaveSteps } =
- await saveRemoteMediaToDisk(uri, temporaryDirectoryPath);
+ await saveRemoteMediaToDisk(uri, encryptionKey, temporaryDirectoryPath);
steps.push(...tempSaveSteps);
if (!tempSaveResult.success) {
sendResult(tempSaveResult);
@@ -348,9 +371,54 @@
async function saveRemoteMediaToDisk(
inputURI: string,
+ encryptionKey?: ?string,
directory: string, // should end with a /
): Promise<IntermediateSaveResult> {
const steps: Array<MediaMissionStep> = [];
+ if (encryptionKey) {
+ const { steps: decryptionSteps, result: decryptionResult } =
+ await decryptMedia(inputURI, encryptionKey, {
+ destination: 'file',
+ destinationDirectory: directory,
+ });
+ steps.push(...decryptionSteps);
+ if (!decryptionResult.success) {
+ return { result: decryptionResult, steps };
+ }
+ const { uri } = decryptionResult;
+ const path = pathFromURI(uri);
+ if (!path) {
+ return {
+ result: { success: false, reason: 'resolve_failed', uri },
+ steps,
+ };
+ }
+
+ const { steps: fetchFileInfoSteps, result: fetchFileInfoResult } =
+ await fetchFileInfo(uri, undefined, {
+ mime: true,
+ });
+ steps.push(...fetchFileInfoSteps);
+ if (!fetchFileInfoResult.success) {
+ return { result: fetchFileInfoResult, steps };
+ }
+ const { mime } = fetchFileInfoResult;
+ if (!mime) {
+ return {
+ steps,
+ result: {
+ success: false,
+ reason: 'media_type_fetch_failed',
+ detectedMIME: mime,
+ },
+ };
+ }
+
+ return {
+ result: { success: true, path, mime },
+ steps,
+ };
+ }
const { result: fetchBlobResult, steps: fetchBlobSteps } =
await fetchBlob(inputURI);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 24, 3:43 AM (20 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2573747
Default Alt Text
D10854.id36367.diff (6 KB)
Attached To
Mode
D10854: [native] Add support for saving encrypted media
Attached
Detach File
Event Timeline
Log In to Comment