[web] Fix search when an album only has symlinks (#4025)
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:
@@ -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],
|
||||
);
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user