setup data pipeline
This commit is contained in:
parent
85c61e84c7
commit
4e0e1e4e19
|
@ -6,7 +6,7 @@ import { EnteFile } from 'types/file';
|
|||
|
||||
export default function CollectionCard(props: {
|
||||
children?: any;
|
||||
latestFile?: EnteFile;
|
||||
latestFile: EnteFile;
|
||||
onClick: () => void;
|
||||
collectionTile: any;
|
||||
}) {
|
||||
|
|
|
@ -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;
|
||||
`;
|
||||
|
|
|
@ -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)();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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;
|
||||
`;
|
||||
|
|
|
@ -34,6 +34,9 @@ export const SelectStyles = {
|
|||
'& .main': {
|
||||
backgroundColor: isFocused && '#343434',
|
||||
},
|
||||
'&:last-child .MuiDivider-root': {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
dropdownIndicator: (style) => ({
|
||||
...style,
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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[];
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue