Towards new layering

This commit is contained in:
Manav Rathi
2025-03-13 09:27:03 +05:30
parent 400157a46d
commit 1f8fa69f8b
5 changed files with 104 additions and 74 deletions

View File

@@ -4,10 +4,7 @@ import { type Electron } from "@/base/types/ipc";
import { downloadAndRevokeObjectURL } from "@/base/utils/web";
import { downloadManager } from "@/gallery/services/download";
import { updateFileMagicMetadata } from "@/gallery/services/file";
import {
isArchivedFile,
updateMagicMetadata,
} from "@/gallery/services/magic-metadata";
import { updateMagicMetadata } from "@/gallery/services/magic-metadata";
import { detectFileTypeInfo } from "@/gallery/utils/detect-type";
import { writeStream } from "@/gallery/utils/native-stream";
import {
@@ -15,7 +12,7 @@ import {
FileMagicMetadataProps,
FileWithUpdatedMagicMetadata,
} from "@/media/file";
import { ItemVisibility } from "@/media/file-metadata";
import { ItemVisibility, isArchivedFile } from "@/media/file-metadata";
import { FileType } from "@/media/file-type";
import { decodeLivePhoto } from "@/media/live-photo";
import { deleteFromTrash, moveToTrash } from "@/new/photos/services/collection";

View File

@@ -18,7 +18,7 @@ import {
type FileInfoProps,
} from "@/gallery/components/FileInfo";
import type { Collection } from "@/media/collection";
import { ItemVisibility } from "@/media/file-metadata";
import { fileVisibility, ItemVisibility } from "@/media/file-metadata";
import { FileType } from "@/media/file-type";
import type { EnteFile } from "@/media/file.js";
import { isHEICExtension, needsJPEGConversion } from "@/media/formats";
@@ -53,7 +53,6 @@ import React, {
useRef,
useState,
} from "react";
import { fileVisibility } from "../../services/magic-metadata";
import {
fileInfoExifForFile,
updateItemDataAlt,

View File

@@ -3,41 +3,8 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import { sharedCryptoWorker } from "@/base/crypto";
import type { Collection } from "@/media/collection";
import { fileLogID, type EnteFile, type MagicMetadataCore } from "@/media/file";
import {
ItemVisibility,
type PrivateMagicMetadata,
} from "@/media/file-metadata";
/**
* Return the magic metadata for an {@link EnteFile}.
*
* We are not expected to be in a scenario where the file gets to the UI without
* having its magic metadata decrypted, so this function is a sanity
* check and should be a no-op in usually. It'll throw if it finds its
* assumptions broken. Once the types have been refactored this entire
* check/cast shouldn't be needed, and this should become a trivial accessor.
*/
export const fileMagicMetadata = (file: EnteFile) => {
if (!file.magicMetadata) return undefined;
if (typeof file.magicMetadata.data == "string") {
throw new Error(
`Magic metadata for ${fileLogID(file)} had not been decrypted even when the file reached the UI layer`,
);
}
// This cast is unavoidable in the current setup. We need to refactor the
// types so that this cast in not needed.
return file.magicMetadata.data as PrivateMagicMetadata;
};
/**
* Return the {@link ItemVisibility} for the given {@link file}.
*/
export const fileVisibility = (file: EnteFile) =>
fileMagicMetadata(file)?.visibility;
export const isArchivedFile = (item: EnteFile) =>
fileVisibility(item) === ItemVisibility.archived;
import { type MagicMetadataCore } from "@/media/file";
import { ItemVisibility } from "@/media/file-metadata";
export const isArchivedCollection = (item: Collection) => {
if (!item) {

View File

@@ -152,6 +152,8 @@ export interface Metadata {
* - Unlike {@link PublicMagicMetadata}, this is only available to the owner of
* the file.
*
* [Note: Private magic metadata is called magic metadata on remote]
*
* For historical reasons, the unqualified phrase "magic metadata" in various
* APIs refers to the (this) private metadata, even though the mutable public
* metadata is the much more frequently used of the two. See: [Note: Metadatum].
@@ -294,6 +296,29 @@ const PublicMagicMetadata = z
})
.passthrough();
/**
* Return the private magic metadata for an {@link EnteFile}.
*
* We are not expected to be in a scenario where the file gets to the UI without
* having its private magic metadata decrypted, so this function is a sanity
* check and should be a no-op in usually. It'll throw if it finds its
* assumptions broken. Once the types have been refactored this entire
* check/cast shouldn't be needed, and this should become a trivial accessor.
*/
export const filePrivateMagicMetadata = (file: EnteFile) => {
// TODO: Audit the types.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!file.magicMetadata) return undefined;
if (typeof file.magicMetadata.data == "string") {
throw new Error(
`Private magic metadata for ${fileLogID(file)} had not been decrypted even when the file reached the UI layer`,
);
}
// This cast is unavoidable in the current setup. We need to refactor the
// types so that this cast in not needed.
return file.magicMetadata.data as PrivateMagicMetadata;
};
/**
* Return the public magic metadata for an {@link EnteFile}.
*
@@ -410,6 +435,35 @@ export const fileCreationPhotoDate = (
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 PrivateMagicMetadata} containing the
* fields that we want to add or update.
*/
export const updateRemotePrivateMagicMetadata = async (
file: EnteFile,
metadataUpdates: Partial<PrivateMagicMetadata>,
) => {
const existingMetadata = filePrivateMagicMetadata(file);
const updatedMetadata = { ...(existingMetadata ?? {}), ...metadataUpdates };
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const metadataVersion = file.magicMetadata?.version ?? 1;
const updateRequest = await updateMagicMetadataRequest(
file,
updatedMetadata,
metadataVersion,
);
return putFilesPrivateMagicMetadata(updateRequest);
};
/**
* Update the public magic metadata associated with a file on remote.
*
@@ -553,13 +607,14 @@ const updateMagicMetadataRequest = async (
};
/**
* Update the magic metadata for a list of files.
* 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.
*/
// TODO: Remove export once this is used.
export const putFilesMagicMetadata = async (
const putFilesPrivateMagicMetadata = async (
request: UpdateMagicMetadataRequest,
) =>
ensureOk(
@@ -587,6 +642,46 @@ const putFilesPublicMagicMetadata = async (
}),
);
/**
* Return the {@link ItemVisibility} for the given {@link file}.
*/
export const fileVisibility = (file: EnteFile) =>
filePrivateMagicMetadata(file)?.visibility;
/**
* Return `true` if the {@link ItemVisibility} of the given {@link file} is
* archived.
*/
export const isArchivedFile = (item: EnteFile) =>
fileVisibility(item) === ItemVisibility.archived;
/**
* Return the GPS coordinates (if any) present in the given {@link EnteFile}.
*/
export const fileLocation = (file: EnteFile): Location | undefined => {
// TODO: EnteFile types. Need to verify that metadata itself, and
// metadata.lat/lng can not be null (I think they likely can, if so need to
// update the types). Need to supress the linter meanwhile.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!file.metadata) return undefined;
const latitude = nullToUndefined(file.metadata.latitude);
const longitude = nullToUndefined(file.metadata.longitude);
if (latitude === undefined || longitude === undefined) return undefined;
if (Number.isNaN(latitude) || Number.isNaN(longitude)) return undefined;
return { latitude, longitude };
};
/**
* Return the caption, aka "description", (if any) attached to the given
* {@link EnteFile}.
*/
export const fileCaption = (file: EnteFile): string | undefined =>
filePublicMagicMetadata(file)?.caption;
/**
* Metadata about a file extracted from various sources (like Exif) when
* uploading it into Ente.
@@ -835,30 +930,3 @@ export const createPhotoDate = (
return new Date(dateLike / 1000);
}
};
/**
* Return the GPS coordinates (if any) present in the given {@link EnteFile}.
*/
export const fileLocation = (file: EnteFile): Location | undefined => {
// TODO: EnteFile types. Need to verify that metadata itself, and
// metadata.lat/lng can not be null (I think they likely can, if so need to
// update the types). Need to supress the linter meanwhile.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!file.metadata) return undefined;
const latitude = nullToUndefined(file.metadata.latitude);
const longitude = nullToUndefined(file.metadata.longitude);
if (latitude === undefined || longitude === undefined) return undefined;
if (Number.isNaN(latitude) || Number.isNaN(longitude)) return undefined;
return { latitude, longitude };
};
/**
* Return the caption, aka "description", (if any) attached to the given
* {@link EnteFile}.
*/
export const fileCaption = (file: EnteFile): string | undefined =>
filePublicMagicMetadata(file)?.caption;

View File

@@ -1,6 +1,5 @@
import {
isArchivedCollection,
isArchivedFile,
isPinnedCollection,
} from "@/gallery/services/magic-metadata";
import {
@@ -10,7 +9,7 @@ import {
} from "@/media/collection";
import type { EnteFile } from "@/media/file";
import { mergeMetadata } from "@/media/file";
import { ItemVisibility } from "@/media/file-metadata";
import { isArchivedFile, ItemVisibility } from "@/media/file-metadata";
import {
createCollectionNameByID,
isHiddenCollection,