setup data pipeline

This commit is contained in:
Abhinav 2022-06-01 15:00:21 +05:30
parent 85c61e84c7
commit 4e0e1e4e19
11 changed files with 177 additions and 83 deletions

View file

@ -6,7 +6,7 @@ import { EnteFile } from 'types/file';
export default function CollectionCard(props: {
children?: any;
latestFile?: EnteFile;
latestFile: EnteFile;
onClick: () => void;
collectionTile: any;
}) {

View file

@ -86,3 +86,9 @@ export const CollectionSelectorTile = styled(AllCollectionTile)`
width: 192px;
margin: 10px;
`;
export const ResultPreviewTile = styled(AllCollectionTile)`
width: 48px;
height: 48px;
border-radius: 4px;
`;

View file

@ -7,7 +7,6 @@ import DownloadManager from 'services/downloadManager';
import constants from 'utils/strings/constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import PhotoSwipe from 'components/PhotoSwipe/PhotoSwipe';
import { isInsideBox, isSameDay as isSameDayAnyYear } from 'utils/search';
import { formatDateRelative } from 'utils/file';
import {
ALL_SECTION,
@ -17,7 +16,7 @@ import {
import { isSharedFile } from 'utils/file';
import { isPlaybackPossible } from 'utils/photoFrame';
import { PhotoList } from './PhotoList';
import { SetFiles, SelectedState, Search, SetSearchStats } from 'types/gallery';
import { SetFiles, SelectedState, SetSearchStats } from 'types/gallery';
import { FILE_TYPE } from 'constants/file';
import PublicCollectionDownloadManager from 'services/publicCollectionDownloadManager';
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
@ -26,6 +25,8 @@ import EmptyScreen from './EmptyScreen';
import { AppContext } from 'pages/_app';
import { DeduplicateContext } from 'pages/deduplicate';
import { IsArchived } from 'utils/magicMetadata';
import { isSameDayAnyYear, isInsideBox } from 'utils/search';
import { Search } from 'types/search';
const Container = styled.div`
display: block;
@ -99,7 +100,7 @@ const PhotoFrame = ({
const [rangeStart, setRangeStart] = useState(null);
const [currentHover, setCurrentHover] = useState(null);
const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false);
const filteredDataRef = useRef([]);
const filteredDataRef = useRef<EnteFile[]>([]);
const filteredData = filteredDataRef?.current ?? [];
const router = useRouter();
const [isSourceLoaded, setIsSourceLoaded] = useState(false);
@ -141,9 +142,9 @@ const PhotoFrame = ({
timeTaken: (Date.now() - startTime) / 1000,
});
}
if (search?.fileIndex || search?.fileIndex === 0) {
if (search?.file || search?.file === 0) {
const filteredDataIdx = filteredData.findIndex(
(data) => data.dataIndex === search.fileIndex
(data) => data.id === search.file
);
if (filteredDataIdx || filteredDataIdx === 0) {
onThumbnailClick(filteredDataIdx)();

View file

@ -5,11 +5,12 @@ import ImageIcon from 'components/icons/ImageIcon';
import LocationIcon from 'components/icons/LocationIcon';
import VideoIcon from 'components/icons/VideoIcon';
import { components } from 'react-select';
import { SuggestionType } from 'types/search';
import { SearchOption, SuggestionType } from 'types/search';
import SearchIcon from '@mui/icons-material/Search';
import { Box, Divider, Stack, Typography } from '@mui/material';
import { FreeFlowText, SpaceBetweenFlex } from 'components/Container';
import { PreviewResultImages } from './styledComponents';
import CollectionCard from 'components/Collections/CollectionCard';
import { ResultPreviewTile } from 'components/Collections/styledComponents';
const { Option, Control } = components;
@ -32,7 +33,7 @@ const getIconByType = (type: SuggestionType) => {
export const OptionWithIcon = (props) => (
<Option {...props}>
<LabelWithIcon type={props.data.type} label={props.data.label} />
<LabelWithIcon data={props.data} />
</Option>
);
export const ControlWithIcon = (props) => (
@ -49,7 +50,7 @@ export const ControlWithIcon = (props) => (
</Control>
);
const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
const LabelWithIcon = ({ data }: { data: SearchOption }) => (
<>
<Box className="main" px={2} py={1}>
<Typography
@ -63,15 +64,22 @@ const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
<SpaceBetweenFlex>
<Box mr={1}>
<FreeFlowText>
<Typography>{props.label}</Typography>
<Typography>{data.label}</Typography>
</FreeFlowText>
<Typography color="text.secondary"> 22 Photos</Typography>
<Typography color="text.secondary">
{data.fileCount} Photos
</Typography>
</Box>
<Stack direction={'row'} spacing={1}>
<PreviewResultImages src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" />
<PreviewResultImages src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" />
<PreviewResultImages src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" />
{data.previewFiles.map((file) => (
<CollectionCard
key={file.id}
latestFile={file}
onClick={() => null}
collectionTile={ResultPreviewTile}
/>
))}
</Stack>
</SpaceBetweenFlex>
</Box>

View file

@ -30,6 +30,16 @@ export default function SearchInput(props: Iprops) {
setValue(value);
};
const [defaultOption, setDefaultOption] = useState<Suggestion[]>(null);
useEffect(() => {
const main = async () => {
const d = await getOptions('photo');
setDefaultOption(d);
};
main();
}, []);
useEffect(() => search(value), [value]);
const resetSearch = () => {
@ -72,7 +82,7 @@ export default function SearchInput(props: Iprops) {
break;
case SuggestionType.IMAGE:
case SuggestionType.VIDEO:
props.setSearch({ fileIndex: selectedOption.value as number });
props.setSearch({ file: selectedOption.value as number });
setValue(null);
break;
}
@ -92,7 +102,9 @@ export default function SearchInput(props: Iprops) {
isClearable
escapeClearsValue
styles={SelectStyles}
noOptionsMessage={() => null}
// noOptionsMessage={() => null}
defaultOptions={defaultOption}
menuIsOpen
/>
{props.isOpen && (

View file

@ -27,8 +27,3 @@ export const SearchInputWrapper = styled(FlexWrapper)`
max-width: 484px;
margin: auto;
`;
export const PreviewResultImages = styled.img`
width: 48px;
height: 48px;
border-radius: 4px;
`;

View file

@ -34,6 +34,9 @@ export const SelectStyles = {
'& .main': {
backgroundColor: isFocused && '#343434',
},
'&:last-child .MuiDivider-root': {
display: 'none',
},
}),
dropdownIndicator: (style) => ({
...style,

View file

@ -7,13 +7,16 @@ import { EnteFile } from 'types/file';
import { logError } from 'utils/sentry';
import {
Bbox,
DateValue,
LocationSearchResponse,
Search,
SearchOption,
Suggestion,
SuggestionType,
} from 'types/search';
import { FILE_TYPE } from 'constants/file';
import { getFormattedDate, isInsideBox } from 'utils/search';
import { getFormattedDate, isInsideBox, isSameDayAnyYear } from 'utils/search';
const ENDPOINT = getEndpoint();
@ -26,16 +29,35 @@ export const getAutoCompleteSuggestions =
if (!searchPhrase?.length) {
return [];
}
const options = [
const suggestions = [
...getHolidaySuggestion(searchPhrase),
...getYearSuggestion(searchPhrase),
...getDateSuggestion(searchPhrase),
...getCollectionSuggestion(searchPhrase, collections),
...getFileSuggestion(searchPhrase, files),
...(await getLocationSuggestions(searchPhrase, files)),
...(await getLocationSuggestions(searchPhrase /* files*/)),
];
return options;
console.log(suggestions);
const previewImageAppendedOptions: SearchOption[] = suggestions
.map((suggestion) => ({
suggestion,
searchQuery: convertSuggestionToSearchQuery(suggestion),
}))
.map(({ suggestion, searchQuery }) => {
const resultFiles = files.filter((file) =>
isSearchedFiles(file, searchQuery)
);
return {
...suggestion,
fileCount: resultFiles.length,
previewFiles: resultFiles.slice(0, 3),
};
})
.filter((option) => option.fileCount);
return previewImageAppendedOptions;
};
function getHolidaySuggestion(searchPhrase: string): Suggestion[] {
@ -135,6 +157,7 @@ function getCollectionSuggestion(
function getFileSuggestion(searchPhrase: string, files: EnteFile[]) {
const fileResults = searchFiles(searchPhrase, files);
console.log(fileResults);
return fileResults.map((file) => ({
type:
file.type === FILE_TYPE.IMAGE
@ -145,31 +168,33 @@ function getFileSuggestion(searchPhrase: string, files: EnteFile[]) {
}));
}
async function getLocationSuggestions(searchPhrase: string, files: EnteFile[]) {
async function getLocationSuggestions(
searchPhrase: string /* files: EnteFile[]*/
) {
const locationResults = await searchLocation(searchPhrase);
const locationResultsHasFiles: boolean[] = new Array(
locationResults.length
).fill(false);
files.map((file) => {
for (const [index, location] of locationResults.entries()) {
if (
isInsideBox(
{
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
},
location.bbox
)
) {
locationResultsHasFiles[index] = true;
}
}
});
const filteredLocationWithFiles = locationResults.filter(
(_, index) => locationResultsHasFiles[index]
);
return filteredLocationWithFiles.map(
// const locationResultsHasFiles: boolean[] = new Array(
// locationResults.length
// ).fill(false);
// files.map((file) => {
// for (const [index, location] of locationResults.entries()) {
// if (
// isInsideBox(
// {
// latitude: file.metadata.latitude,
// longitude: file.metadata.longitude,
// },
// location.bbox
// )
// ) {
// locationResultsHasFiles[index] = true;
// }
// }
// });
// const filteredLocationWithFiles = locationResults.filter(
// (_, index) => locationResultsHasFiles[index]
// );
return locationResults.map(
(searchResult) =>
({
type: SuggestionType.LOCATION,
@ -223,3 +248,48 @@ async function searchLocation(
}
return [];
}
export function isSearchedFiles(file: EnteFile, search: Search) {
if (search?.date) {
return isSameDayAnyYear(search.date)(
new Date(file.metadata.creationTime / 1000)
);
}
if (search?.location) {
return isInsideBox(
{
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
},
search.location
);
}
if (search?.file) {
return file.id === search.file;
}
if (search?.collection) {
return search.collection === file.collectionID;
}
return false;
}
function convertSuggestionToSearchQuery(option: Suggestion): Search {
switch (option.type) {
case SuggestionType.DATE:
return {
date: option.value as DateValue,
};
case SuggestionType.LOCATION:
return {
location: option.value as Bbox,
};
case SuggestionType.COLLECTION:
return { collection: option.value as number };
case SuggestionType.IMAGE:
case SuggestionType.VIDEO:
return { file: option.value as number };
}
}

View file

@ -1,6 +1,6 @@
import { Collection } from 'types/collection';
import { EnteFile } from 'types/file';
import { DateValue, Bbox } from 'types/search';
import { Search, SearchStats } from 'types/search';
export type SelectedState = {
[k: number]: boolean;
@ -13,16 +13,6 @@ export type SetLoading = React.Dispatch<React.SetStateAction<Boolean>>;
export type SetSearchStats = React.Dispatch<React.SetStateAction<SearchStats>>;
export type SetSearch = React.Dispatch<React.SetStateAction<Search>>;
export type Search = {
date?: DateValue;
location?: Bbox;
fileIndex?: number;
};
export interface SearchStats {
resultCount: number;
timeTaken: number;
}
export type GalleryContextType = {
thumbs: Map<number, string>;
files: Map<number, string>;

View file

@ -1,3 +1,5 @@
import { EnteFile } from 'types/file';
export type Bbox = [number, number, number, number];
export interface LocationSearchResponse {
@ -24,3 +26,19 @@ export interface Suggestion {
label: string;
value: Bbox | DateValue | number;
}
export type Search = {
date?: DateValue;
location?: Bbox;
collection?: number;
file?: number;
};
export interface SearchStats {
resultCount: number;
timeTaken: number;
}
export interface SearchOption extends Suggestion {
fileCount: number;
previewFiles: EnteFile[];
}

View file

@ -1,4 +1,3 @@
import { EnteFile } from 'types/file';
import { Bbox, DateValue } from 'types/search';
import { Location } from 'types/upload';
@ -16,31 +15,23 @@ export function isInsideBox({ latitude, longitude }: Location, bbox: Bbox) {
}
}
export const isSameDay = (baseDate: DateValue) => (compareDate: Date) => {
let same = true;
export const isSameDayAnyYear =
(baseDate: DateValue) => (compareDate: Date) => {
let same = true;
if (baseDate.month || baseDate.month === 0) {
same = baseDate.month === compareDate.getMonth();
}
if (same && baseDate.date) {
same = baseDate.date === compareDate.getDate();
}
if (same && baseDate.year) {
same = baseDate.year === compareDate.getFullYear();
}
if (baseDate.month || baseDate.month === 0) {
same = baseDate.month === compareDate.getMonth();
}
if (same && baseDate.date) {
same = baseDate.date === compareDate.getDate();
}
if (same && baseDate.year) {
same = baseDate.year === compareDate.getFullYear();
}
return same;
};
return same;
};
export function getFilesWithCreationDay(
files: EnteFile[],
searchedDate: DateValue
) {
const isSearchedDate = isSameDay(searchedDate);
return files.filter((file) =>
isSearchedDate(new Date(file.metadata.creationTime / 1000))
);
}
export function getFormattedDate(date: DateValue) {
const options = {};
date.date && (options['day'] = 'numeric');