Unused
This commit is contained in:
@@ -846,7 +846,17 @@ const Page: React.FC = () => {
|
||||
dispatch({ type: "addPendingVisibilityUpdate", fileID });
|
||||
try {
|
||||
await updateFilesVisibility([file], visibility);
|
||||
// TODO: Replace with file fetch?
|
||||
// [Note: Interactive updates to file metadata]
|
||||
//
|
||||
// 1. Update the remote metadata.
|
||||
//
|
||||
// 2. Construct a fake a metadata object with the updates
|
||||
// reflected in it.
|
||||
//
|
||||
// 3. The caller (eventually) triggers a remote sync in the
|
||||
// background, but meanwhile uses this updated metadata.
|
||||
//
|
||||
// TODO(RE): Replace with file fetch?
|
||||
dispatch({
|
||||
type: "unsyncedPrivateMagicMetadataUpdate",
|
||||
fileID,
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import { encryptMetadataJSON } from "ente-base/crypto";
|
||||
import { authenticatedRequestHeaders, ensureOk } from "ente-base/http";
|
||||
import { apiURL } from "ente-base/origins";
|
||||
import { type Location } from "ente-base/types";
|
||||
import {
|
||||
type EnteFile,
|
||||
type EnteFile2,
|
||||
type FileMagicMetadata,
|
||||
type FilePrivateMagicMetadata,
|
||||
} from "ente-media/file";
|
||||
import { type EnteFile, type EnteFile2 } from "ente-media/file";
|
||||
import { nullToUndefined } from "ente-utils/transform";
|
||||
import { z } from "zod/v4";
|
||||
import { FileType } from "./file-type";
|
||||
import type { RemoteMagicMetadata } from "./magic-metadata";
|
||||
|
||||
/**
|
||||
* Information about the file that never changes post upload.
|
||||
@@ -489,155 +480,6 @@ export const fileCreationPhotoDate = (file: EnteFile) =>
|
||||
file.metadata.creationTime,
|
||||
);
|
||||
|
||||
/**
|
||||
* Update the private magic metadata associated with a file on remote.
|
||||
*
|
||||
* @param file The {@link EnteFile} whose public magic metadata we want to
|
||||
* update.
|
||||
*
|
||||
* @param metadataUpdates A subset of {@link FilePrivateMagicMetadataData} containing
|
||||
* the fields that we want to add or update.
|
||||
*
|
||||
* @returns An updated {@link FilePrivateMagicMetadataData} object containing the
|
||||
* (decrypted) metadata updates we just made. This is effectively what we would
|
||||
* get if we to ask the remote for the latest file for this ID, except we don't
|
||||
* do an actual sync and instead reconstruct it piecemeal.
|
||||
*
|
||||
* [Note: Interactive updates to file metadata]
|
||||
*
|
||||
* This function updates the magic metadata on remote, and returns a magic
|
||||
* metadata object with the updated (and decrypted) values, but it does not
|
||||
* update the state of the file objects in our databases.
|
||||
*
|
||||
* The caller needs to ensure that we subsequently sync with remote to fetch the
|
||||
* updates as part of the diff and update the {@link EnteFile} that is persisted
|
||||
* in our local db.
|
||||
*
|
||||
* This partial update approach is used because a full sync requires multiple
|
||||
* API calls, which can cause a slow experience for interactive operations (e.g.
|
||||
* archiving a file). So this function does not immediately perform the sync,
|
||||
* but instead expects the caller to arrange for an eventual delayed sync in the
|
||||
* background without waiting for it to complete.
|
||||
*
|
||||
* Returning a modified in-memory object is essential because in addition to the
|
||||
* updated metadata itself, the metadatum (See: [Note: Metadatum]) contain a
|
||||
* version field that is incremented for each change. So if we were not to
|
||||
* update the version, and if the user were to perform another operation on that
|
||||
* file before the asynchronous remote sync completes, the client will send a
|
||||
* stale version of the metadata, and remote will reject the update.
|
||||
*
|
||||
* The overall sequence is thus:
|
||||
*
|
||||
* 1. This function modifies the remote metadata.
|
||||
*
|
||||
* 2. It returns a metadata object with the updates reflected in it.
|
||||
*
|
||||
* 3. The caller (eventually) triggers a remote sync in the background, but
|
||||
* meanwhile uses this updated metadata.
|
||||
*/
|
||||
export const updateRemotePrivateMagicMetadata = async (
|
||||
file: EnteFile,
|
||||
metadataUpdates: Partial<FilePrivateMagicMetadataData>,
|
||||
): Promise<FilePrivateMagicMetadata> => {
|
||||
const existingMetadata = file.magicMetadata?.data;
|
||||
|
||||
const updatedMetadata = { ...(existingMetadata ?? {}), ...metadataUpdates };
|
||||
|
||||
const metadataVersion = file.magicMetadata?.version ?? 1;
|
||||
|
||||
const updateRequest = await updateMagicMetadataRequest(
|
||||
file,
|
||||
updatedMetadata,
|
||||
metadataVersion,
|
||||
);
|
||||
|
||||
const updatedEnvelope = updateRequest.metadataList[0]!.magicMetadata;
|
||||
|
||||
await putFilesPrivateMagicMetadata(updateRequest);
|
||||
|
||||
// See: [Note: Interactive updates to file metadata]
|
||||
|
||||
// Use the updated envelope we sent as a starting point for the metadata we
|
||||
// will use for the updated file.
|
||||
const updatedMagicMetadata = updatedEnvelope as FileMagicMetadata;
|
||||
// The correct version will come in the updated EnteFile we get in the
|
||||
// response of the /diff. Temporarily bump it to reflect our latest edit.
|
||||
updatedMagicMetadata.version = metadataVersion + 1;
|
||||
// Set the contents (data) to the updated metadata contents we just PUT.
|
||||
updatedMagicMetadata.data = updatedMetadata;
|
||||
|
||||
return updatedMagicMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* The shape of the JSON body payload expected by the APIs that update the
|
||||
* public and private magic metadata fields associated with a file.
|
||||
*/
|
||||
interface UpdateMagicMetadataRequest {
|
||||
/** The list of (file id, new magic metadata) pairs to update */
|
||||
metadataList: {
|
||||
/** File ID */
|
||||
id: number;
|
||||
/** The new metadata to use */
|
||||
magicMetadata: RemoteMagicMetadata;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an remote update request payload from the public or private magic
|
||||
* metadata JSON object for a {@link file}, using the provided
|
||||
* {@link encryptMetadataF} function to encrypt the JSON.
|
||||
*/
|
||||
const updateMagicMetadataRequest = async (
|
||||
file: EnteFile,
|
||||
metadata: FilePrivateMagicMetadataData | FilePublicMagicMetadataData,
|
||||
metadataVersion: number,
|
||||
): Promise<UpdateMagicMetadataRequest> => {
|
||||
// Drop all null or undefined values to obtain the syncable entries.
|
||||
// See: [Note: Optional magic metadata keys].
|
||||
const validEntries = Object.entries(metadata).filter(
|
||||
([, v]) => v !== null && v !== undefined,
|
||||
);
|
||||
|
||||
const { encryptedData, decryptionHeader } = await encryptMetadataJSON(
|
||||
Object.fromEntries(validEntries),
|
||||
file.key,
|
||||
);
|
||||
|
||||
return {
|
||||
metadataList: [
|
||||
{
|
||||
id: file.id,
|
||||
magicMetadata: {
|
||||
version: metadataVersion,
|
||||
count: validEntries.length,
|
||||
data: encryptedData,
|
||||
header: decryptionHeader,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the (private) magic metadata for a list of files.
|
||||
*
|
||||
* See: [Note: Private magic metadata is called magic metadata on remote]
|
||||
*
|
||||
* @param request The list of file ids and the updated encrypted magic metadata
|
||||
* associated with each of them.
|
||||
*/
|
||||
const putFilesPrivateMagicMetadata = async (
|
||||
request: UpdateMagicMetadataRequest,
|
||||
) =>
|
||||
ensureOk(
|
||||
await fetch(await apiURL("/files/magic-metadata"), {
|
||||
method: "PUT",
|
||||
headers: await authenticatedRequestHeaders(),
|
||||
body: JSON.stringify(request),
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the GPS coordinates (if any) present in the given {@link EnteFile}.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user