[web] Fix search when an album only has symlinks

The uniquification would prevent albums that only contains photos that are
already present in another album from appearing in search results.
This commit is contained in:
Manav Rathi
2024-11-13 15:18:35 +05:30
parent c2380de406
commit 0c820a6ec4
6 changed files with 58 additions and 40 deletions

View File

@@ -24,7 +24,6 @@ import {
SearchResultsHeader,
} from "@/new/photos/components/gallery";
import {
uniqueFilesByID,
useGalleryReducer,
type GalleryBarMode,
} from "@/new/photos/components/gallery/reducer";
@@ -401,11 +400,7 @@ export default function Gallery() {
}, []);
useEffect(
() =>
setSearchCollectionsAndFiles({
collections: collections ?? [],
files: uniqueFilesByID(files ?? []),
}),
() => setSearchCollectionsAndFiles({ collections, files }),
[collections, files],
);

View File

@@ -33,7 +33,7 @@ import {
getLatestVersionFiles,
groupFilesByCollectionID,
} from "../../services/file";
import { sortFiles } from "../../services/files";
import { sortFiles, uniqueFilesByID } from "../../services/files";
import {
isArchivedCollection,
isArchivedFile,
@@ -763,29 +763,6 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
export const useGalleryReducer = () =>
useReducer(galleryReducer, initialGalleryState);
/**
* File IDs themselves are unique across all the files for the user (in fact,
* they're unique across all the files in an Ente instance). However, we still
* can have multiple entries for the same file ID in our local database because
* the unit of account is not actually a file, but a "Collection File": a
* collection and file pair.
*
* For example, if the same file is symlinked into two collections, then we will
* have two "Collection File" entries for it, both with the same file ID, but
* with different collection IDs.
*
* This function returns files such that only one of these entries (the newer
* one in case of dupes) is returned.
*/
export const uniqueFilesByID = (files: EnteFile[]) => {
const seen = new Set<number>();
return files.filter(({ id }) => {
if (seen.has(id)) return false;
seen.add(id);
return true;
});
};
/**
* Compute archived collection IDs from their dependencies.
*/

View File

@@ -61,6 +61,29 @@ export const sortFiles = (files: EnteFile[], sortAsc = false) => {
});
};
/**
* File IDs themselves are unique across all the files for the user (in fact,
* they're unique across all the files in an Ente instance). However, we still
* can have multiple entries for the same file ID in our local database because
* the unit of account is not actually a file, but a "Collection File": a
* collection and file pair.
*
* For example, if the same file is symlinked into two collections, then we will
* have two "Collection File" entries for it, both with the same file ID, but
* with different collection IDs.
*
* This function returns files such that only one of these entries (the newer
* one in case of dupes) is returned.
*/
export const uniqueFilesByID = (files: EnteFile[]) => {
const seen = new Set<number>();
return files.filter(({ id }) => {
if (seen.has(id)) return false;
seen.add(id);
return true;
});
};
export const TRASH = "file-trash";
export async function getLocalTrash() {

View File

@@ -3,6 +3,7 @@ import { masterKeyFromSession } from "@/base/session-store";
import { ComlinkWorker } from "@/base/worker/comlink-worker";
import { FileType } from "@/media/file-type";
import i18n, { t } from "i18next";
import { uniqueFilesByID } from "../files";
import { clipMatches, isMLEnabled, isMLSupported } from "../ml";
import type { NamedPerson } from "../ml/people";
import type {
@@ -54,8 +55,17 @@ export const searchDataSync = () =>
/**
* Set the collections and files over which we should search.
*/
export const setSearchCollectionsAndFiles = (cf: SearchCollectionsAndFiles) =>
void worker().then((w) => w.setCollectionsAndFiles(cf));
export const setSearchCollectionsAndFiles = ({
collections,
files,
}: Omit<SearchCollectionsAndFiles, "collectionFiles">) =>
void worker().then((w) =>
w.setCollectionsAndFiles({
collections: collections,
files: uniqueFilesByID(files),
collectionFiles: files,
}),
);
/**
* Set the (named) people that we should search across.

View File

@@ -49,7 +49,20 @@ export interface SearchOption {
*/
export interface SearchCollectionsAndFiles {
collections: Collection[];
/**
* Unique files (by ID).
*
* @see {@link uniqueFilesByID}.
*/
files: EnteFile[];
/**
* One entry per collection/file pair.
*
* Whenever the same file (ID) is in multiple collections, the
* {@link collectionFiles} will have multiple entries with the same file ID,
* one per collection in which that file (ID) occurs.
*/
collectionFiles: EnteFile[];
}
export interface LabelledSearchDateComponents {

View File

@@ -36,6 +36,7 @@ export class SearchWorker {
private collectionsAndFiles: SearchCollectionsAndFiles = {
collections: [],
files: [],
collectionFiles: [],
};
private people: NamedPerson[] = [];
@@ -94,19 +95,16 @@ export class SearchWorker {
* Return {@link EnteFile}s that satisfy the given {@link suggestion}.
*/
filterSearchableFiles(suggestion: SearchSuggestion) {
return filterSearchableFiles(
this.collectionsAndFiles.files,
suggestion,
);
return filterSearchableFiles(this.collectionsAndFiles, suggestion);
}
/**
* Batched variant of {@link filterSearchableFiles}.
*/
filterSearchableFilesMulti(suggestions: SearchSuggestion[]) {
const files = this.collectionsAndFiles.files;
const cf = this.collectionsAndFiles;
return suggestions
.map((sg) => [filterSearchableFiles(files, sg), sg] as const)
.map((sg) => [filterSearchableFiles(cf, sg), sg] as const)
.filter(([files]) => files.length);
}
}
@@ -350,11 +348,13 @@ const locationSuggestions = (
};
const filterSearchableFiles = (
files: EnteFile[],
{ files, collectionFiles }: SearchCollectionsAndFiles,
suggestion: SearchSuggestion,
) =>
sortMatchesIfNeeded(
files.filter((f) => isMatchingFile(f, suggestion)),
(suggestion.type == "collection" ? collectionFiles : files).filter(
(f) => isMatchingFile(f, suggestion),
),
suggestion,
);