diff --git a/web/packages/media/collection.ts b/web/packages/media/collection.ts index f325eb6434..554f2256cf 100644 --- a/web/packages/media/collection.ts +++ b/web/packages/media/collection.ts @@ -481,9 +481,9 @@ export const decryptRemoteCollection = async ( } = collection; // We've already used them to derive the `collectionKey`. - drop([encryptedKey, keyDecryptionNonce]); + ignore([encryptedKey, keyDecryptionNonce]); // Mobile specific attribute not currently used by us. - drop(attributes); + ignore(attributes); const name = // `||` is used because remote sets name to blank to indicate absence. @@ -568,10 +568,12 @@ export const decryptRemoteCollection = async ( }; }; -// A no-op function to pretend that we're using some values. This is handy when -// we want to destructure some fields so that they don't get forwarded, but -// otherwise don't need to use them. -const drop = (xs: unknown) => typeof xs; +/** + * A no-op function to pretend that we're using some values. This is handy when + * we want to destructure some fields so that they don't get forwarded, but + * otherwise don't need to use them. + */ +export const ignore = (xs: unknown) => typeof xs; /** * A convenience function to discard the unused name field from the collection @@ -581,7 +583,7 @@ const parseRemoteCollectionUser = ({ name, ...rest }: RemoteCollectionUser): CollectionUser => { - drop(name); + ignore(name); return rest; }; @@ -673,7 +675,7 @@ export interface CollectionPrivateMagicMetadataData { * * See: [Note: Use looseObject for metadata Zod schemas] */ -const CollectionPrivateMagicMetadataData = z.looseObject({ +export const CollectionPrivateMagicMetadataData = z.looseObject({ subType: z.number().nullish().transform(nullToUndefined), visibility: z.number().nullish().transform(nullToUndefined), order: z.number().nullish().transform(nullToUndefined), @@ -710,7 +712,7 @@ export interface CollectionPublicMagicMetadataData { /** * Zod schema for {@link CollectionPublicMagicMetadataData}. */ -const CollectionPublicMagicMetadataData = z.looseObject({ +export const CollectionPublicMagicMetadataData = z.looseObject({ asc: z.boolean().nullish().transform(nullToUndefined), coverID: z.number().nullish().transform(nullToUndefined), }); @@ -743,6 +745,6 @@ export interface CollectionShareeMagicMetadataData { /** * Zod schema for {@link CollectionShareeMagicMetadataData}. */ -const CollectionShareeMagicMetadataData = z.looseObject({ +export const CollectionShareeMagicMetadataData = z.looseObject({ visibility: z.number().nullish().transform(nullToUndefined), }); diff --git a/web/packages/new/photos/services/files-db.ts b/web/packages/new/photos/services/files-db.ts index d20f4f9981..0910c5abcd 100644 --- a/web/packages/new/photos/services/files-db.ts +++ b/web/packages/new/photos/services/files-db.ts @@ -19,3 +19,57 @@ * reason to migrate this data to another IndexedDB table (it works fine as it * is, really). However we do want to avoid adding more items here. */ + +import { + CollectionPrivateMagicMetadataData, + CollectionPublicMagicMetadataData, + CollectionShareeMagicMetadataData, + ignore, + RemoteCollectionUser, + RemotePublicURL, +} from "ente-media/collection"; +import { RemoteMagicMetadata } from "ente-media/magic-metadata"; +import { nullishToEmpty } from "ente-utils/transform"; +import { z } from "zod/v4"; + +/** + * Zod schema for a {@link Collection} saved in our local persistence. + * + * This is similar to {@link RemoteCollection}, but has significant differences + * too in that it contains the decrypted fields, and some minor refinements. + */ +// TODO(C2): Use me +export const LocalCollection = z.looseObject({ + id: z.number(), + owner: RemoteCollectionUser, + key: z.string(), + name: z.string(), + type: z.string(), + sharees: z.array(RemoteCollectionUser).nullish().transform(nullishToEmpty), + publicURLs: z.array(RemotePublicURL).nullish().transform(nullishToEmpty), + updationTime: z.number(), + magicMetadata: RemoteMagicMetadata.nullish().transform((mm) => { + if (!mm) return undefined; + // Old code used to save the header, however it's unnecessary so we drop + // it on the next read. New code will not save it, so eventually this + // special case can be removed. Note added Jun 2025 (tag: Migration). + const { header, ...rest } = mm; + ignore(header); + const data = CollectionPrivateMagicMetadataData.parse(rest.data); + return { ...rest, data }; + }), + pubMagicMetadata: RemoteMagicMetadata.nullish().transform((mm) => { + if (!mm) return undefined; + const { header, ...rest } = mm; + ignore(header); + const data = CollectionPublicMagicMetadataData.parse(rest.data); + return { ...rest, data }; + }), + sharedMagicMetadata: RemoteMagicMetadata.nullish().transform((mm) => { + if (!mm) return undefined; + const { header, ...rest } = mm; + ignore(header); + const data = CollectionShareeMagicMetadataData.parse(rest.data); + return { ...rest, data }; + }), +});