This commit is contained in:
vishnukvmd 2024-01-23 16:22:03 +05:30
parent 49ec70f5cf
commit 94d6d34625
7 changed files with 113 additions and 29 deletions

View file

@ -30,6 +30,7 @@ import { LocationTagData } from 'types/entity';
import { FILE_TYPE } from 'constants/file';
import { InputActionMeta } from 'react-select/src/types';
import { components } from 'react-select';
import { City } from 'services/locationSearchService';
interface Iprops {
isOpen: boolean;
@ -122,6 +123,12 @@ export default function SearchInput(props: Iprops) {
};
props.setIsOpen(true);
break;
case SuggestionType.CITY:
search = {
city: selectedOption.value as City,
};
props.setIsOpen(true);
break;
case SuggestionType.COLLECTION:
search = { collection: selectedOption.value as number };
setValue(null);

View file

@ -17,6 +17,7 @@ const getIconByType = (type: SuggestionType) => {
case SuggestionType.DATE:
return <CalendarIcon />;
case SuggestionType.LOCATION:
case SuggestionType.CITY:
return <LocationIcon />;
case SuggestionType.COLLECTION:
return <FolderIcon />;

View file

@ -120,7 +120,7 @@ import GalleryEmptyState from 'components/GalleryEmptyState';
import AuthenticateUserModal from 'components/AuthenticateUserModal';
import useMemoSingleThreaded from '@ente/shared/hooks/useMemoSingleThreaded';
import { isArchivedFile } from 'utils/magicMetadata';
import { isSameDayAnyYear, isInsideLocationTag } from 'utils/search';
import { isSameDayAnyYear } from 'utils/search';
import { getSessionExpiredMessage } from 'utils/ui';
import { syncEntities } from 'services/entityService';
import { constructUserIDToEmailMap } from 'services/collectionService';
@ -131,7 +131,10 @@ import { ClipService } from 'services/clipService';
import isElectron from 'is-electron';
import downloadManager from 'services/download';
import { APPS } from '@ente/shared/apps/constants';
import locationSearchService from 'services/locationSearchService';
import locationSearchService, {
isInsideCity,
isInsideLocationTag,
} from 'services/locationSearchService';
export const DeadCenter = styled('div')`
flex: 1;
@ -518,6 +521,18 @@ export default function Gallery() {
) {
return false;
}
if (
search?.city &&
!isInsideCity(
{
latitude: item.metadata.latitude,
longitude: item.metadata.longitude,
},
search.city
)
) {
return false;
}
if (
search?.person &&
search.person.files.indexOf(item.id) === -1

View file

@ -1,23 +1,68 @@
import { CITIES_URL } from '@ente/shared/constants/urls';
import { LocationTagData } from 'types/entity';
import { Location } from 'types/upload';
interface City {
export interface City {
city: string;
country: string;
lat: number;
lng: number;
}
const DEFAULT_CITY_RADIUS = 10;
class LocationSearchService {
private cities: Array<City> = [];
private citiesPromise: Promise<void>;
loadCities() {
fetch(CITIES_URL).then((response) => {
response.json().then((data) => {
this.cities = data;
console.log(this.cities);
if (this.citiesPromise) {
return;
}
this.citiesPromise = fetch(CITIES_URL).then((response) => {
return response.json().then((data) => {
this.cities = data['data'];
});
});
}
async searchCities(searchTerm: string) {
if (!this.citiesPromise) {
this.loadCities();
}
await this.citiesPromise;
return this.cities.filter((city) => {
return city.city.toLowerCase().includes(searchTerm.toLowerCase());
});
}
}
export default new LocationSearchService();
export function isInsideLocationTag(
location: Location,
locationTag: LocationTagData
) {
const { centerPoint, aSquare, bSquare } = locationTag;
const { latitude, longitude } = location;
const x = Math.abs(centerPoint.latitude - latitude);
const y = Math.abs(centerPoint.longitude - longitude);
if ((x * x) / aSquare + (y * y) / bSquare <= 1) {
return true;
} else {
return false;
}
}
// TODO: Verify correctness
export function isInsideCity(location: Location, city: City) {
const { lat, lng } = city;
const { latitude, longitude } = location;
const x = Math.abs(lat - latitude);
const y = Math.abs(lng - longitude);
if (x * x + y * y <= DEFAULT_CITY_RADIUS * DEFAULT_CITY_RADIUS) {
return true;
} else {
return false;
}
}

View file

@ -16,11 +16,7 @@ import {
ClipSearchScores,
} from 'types/search';
import ObjectService from './machineLearning/objectService';
import {
getFormattedDate,
isInsideLocationTag,
isSameDayAnyYear,
} from 'utils/search';
import { getFormattedDate, isSameDayAnyYear } from 'utils/search';
import { Person, Thing } from 'types/machineLearning';
import { getUniqueFiles } from 'utils/file';
import { getLatestEntities } from './entityService';
@ -31,6 +27,11 @@ import { ClipService, computeClipMatchScore } from './clipService';
import { CustomError } from '@ente/shared/error';
import { Model } from 'types/embedding';
import { getLocalEmbeddings } from './embeddingService';
import locationSearchService, {
City,
isInsideCity,
isInsideLocationTag,
} from './locationSearchService';
const DIGITS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
@ -61,6 +62,7 @@ export const getAutoCompleteSuggestions =
getFileNameSuggestion(searchPhrase, files),
getFileCaptionSuggestion(searchPhrase, files),
...(await getLocationTagSuggestions(searchPhrase)),
...(await getCitySuggestions(searchPhrase)),
...(await getThingSuggestion(searchPhrase)),
].filter((suggestion) => !!suggestion);
@ -279,6 +281,21 @@ async function getLocationTagSuggestions(searchPhrase: string) {
);
}
async function getCitySuggestions(searchPhrase: string) {
const searchResults = await locationSearchService.searchCities(
searchPhrase
);
return searchResults.map(
(city) =>
({
type: SuggestionType.CITY,
value: city,
label: city.city,
} as Suggestion)
);
}
async function getThingSuggestion(searchPhrase: string): Promise<Suggestion[]> {
const thingResults = await searchThing(searchPhrase);
@ -425,6 +442,15 @@ function isSearchedFile(file: EnteFile, search: Search) {
search.location
);
}
if (search?.city) {
return isInsideCity(
{
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
},
search.city
);
}
if (search?.files) {
return search.files.indexOf(file.id) !== -1;
}
@ -460,6 +486,9 @@ function convertSuggestionToSearchQuery(option: Suggestion): Search {
location: option.value as LocationTagData,
};
case SuggestionType.CITY:
return { city: option.value as City };
case SuggestionType.COLLECTION:
return { collection: option.value as number };

View file

@ -3,6 +3,7 @@ import { IndexStatus } from 'types/machineLearning/ui';
import { EnteFile } from 'types/file';
import { LocationTagData } from 'types/entity';
import { FILE_TYPE } from 'constants/file';
import { City } from 'services/locationSearchService';
export enum SuggestionType {
DATE = 'DATE',
@ -16,6 +17,7 @@ export enum SuggestionType {
FILE_CAPTION = 'FILE_CAPTION',
FILE_TYPE = 'FILE_TYPE',
CLIP = 'CLIP',
CITY = 'CITY',
}
export interface DateValue {
@ -35,6 +37,7 @@ export interface Suggestion {
| Thing
| WordGroup
| LocationTagData
| City
| FILE_TYPE
| ClipSearchScores;
hide?: boolean;
@ -43,6 +46,7 @@ export interface Suggestion {
export type Search = {
date?: DateValue;
location?: LocationTagData;
city?: City;
collection?: number;
files?: number[];
person?: Person;

View file

@ -1,6 +1,4 @@
import { LocationTagData } from 'types/entity';
import { DateValue } from 'types/search';
import { Location } from 'types/upload';
export const isSameDayAnyYear =
(baseDate: DateValue) => (compareDate: Date) => {
@ -28,18 +26,3 @@ export function getFormattedDate(date: DateValue) {
new Date(date.year ?? 1, date.month ?? 1, date.date ?? 1)
);
}
export function isInsideLocationTag(
location: Location,
locationTag: LocationTagData
) {
const { centerPoint, aSquare, bSquare } = locationTag;
const { latitude, longitude } = location;
const x = Math.abs(centerPoint.latitude - latitude);
const y = Math.abs(centerPoint.longitude - longitude);
if ((x * x) / aSquare + (y * y) / bSquare <= 1) {
return true;
} else {
return false;
}
}