commit
44a37ad387
|
@ -185,6 +185,14 @@ const PhotoFrame = ({
|
||||||
timeTaken: (Date.now() - startTime) / 1000,
|
timeTaken: (Date.now() - startTime) / 1000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (search.fileIndex || search.fileIndex === 0) {
|
||||||
|
const filteredDataIdx = filteredData.findIndex(
|
||||||
|
(data) => data.dataIndex === search.fileIndex
|
||||||
|
);
|
||||||
|
if (filteredDataIdx || filteredDataIdx === 0) {
|
||||||
|
onThumbnailClick(filteredDataIdx)();
|
||||||
|
}
|
||||||
|
}
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
getYearSuggestion,
|
getYearSuggestion,
|
||||||
parseHumanDate,
|
parseHumanDate,
|
||||||
searchCollection,
|
searchCollection,
|
||||||
|
searchFiles,
|
||||||
searchLocation,
|
searchLocation,
|
||||||
} from 'services/searchService';
|
} from 'services/searchService';
|
||||||
import { getFormattedDate } from 'utils/search';
|
import { getFormattedDate } from 'utils/search';
|
||||||
|
@ -20,6 +21,9 @@ import SearchIcon from './icons/SearchIcon';
|
||||||
import CrossIcon from './icons/CrossIcon';
|
import CrossIcon from './icons/CrossIcon';
|
||||||
import { Collection } from 'services/collectionService';
|
import { Collection } from 'services/collectionService';
|
||||||
import CollectionIcon from './icons/CollectionIcon';
|
import CollectionIcon from './icons/CollectionIcon';
|
||||||
|
import { File, FILE_TYPE } from 'services/fileService';
|
||||||
|
import ImageIcon from './icons/ImageIcon';
|
||||||
|
import VideoIcon from './icons/VideoIcon';
|
||||||
|
|
||||||
const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>`
|
const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -74,6 +78,8 @@ export enum SuggestionType {
|
||||||
DATE,
|
DATE,
|
||||||
LOCATION,
|
LOCATION,
|
||||||
COLLECTION,
|
COLLECTION,
|
||||||
|
IMAGE,
|
||||||
|
VIDEO,
|
||||||
}
|
}
|
||||||
export interface DateValue {
|
export interface DateValue {
|
||||||
date?: number;
|
date?: number;
|
||||||
|
@ -94,6 +100,7 @@ interface Props {
|
||||||
searchStats: SearchStats;
|
searchStats: SearchStats;
|
||||||
collections: Collection[];
|
collections: Collection[];
|
||||||
setActiveCollection: (id: number) => void;
|
setActiveCollection: (id: number) => void;
|
||||||
|
files: File[];
|
||||||
}
|
}
|
||||||
export default function SearchBar(props: Props) {
|
export default function SearchBar(props: Props) {
|
||||||
const [value, setValue] = useState<Suggestion>(null);
|
const [value, setValue] = useState<Suggestion>(null);
|
||||||
|
@ -112,14 +119,14 @@ export default function SearchBar(props: Props) {
|
||||||
if (!searchPhrase?.length) {
|
if (!searchPhrase?.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const option = [
|
const options = [
|
||||||
...getHolidaySuggestion(searchPhrase),
|
...getHolidaySuggestion(searchPhrase),
|
||||||
...getYearSuggestion(searchPhrase),
|
...getYearSuggestion(searchPhrase),
|
||||||
];
|
];
|
||||||
|
|
||||||
const searchedDates = parseHumanDate(searchPhrase);
|
const searchedDates = parseHumanDate(searchPhrase);
|
||||||
|
|
||||||
option.push(
|
options.push(
|
||||||
...searchedDates.map((searchedDate) => ({
|
...searchedDates.map((searchedDate) => ({
|
||||||
type: SuggestionType.DATE,
|
type: SuggestionType.DATE,
|
||||||
value: searchedDate,
|
value: searchedDate,
|
||||||
|
@ -131,7 +138,7 @@ export default function SearchBar(props: Props) {
|
||||||
searchPhrase,
|
searchPhrase,
|
||||||
props.collections
|
props.collections
|
||||||
);
|
);
|
||||||
option.push(
|
options.push(
|
||||||
...collectionResults.map(
|
...collectionResults.map(
|
||||||
(searchResult) =>
|
(searchResult) =>
|
||||||
({
|
({
|
||||||
|
@ -141,8 +148,20 @@ export default function SearchBar(props: Props) {
|
||||||
} as Suggestion)
|
} as Suggestion)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const fileResults = searchFiles(searchPhrase, props.files);
|
||||||
|
options.push(
|
||||||
|
...fileResults.map((file) => ({
|
||||||
|
type:
|
||||||
|
file.type === FILE_TYPE.IMAGE
|
||||||
|
? SuggestionType.IMAGE
|
||||||
|
: SuggestionType.VIDEO,
|
||||||
|
value: file.index,
|
||||||
|
label: file.title,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
const locationResults = await searchLocation(searchPhrase);
|
const locationResults = await searchLocation(searchPhrase);
|
||||||
option.push(
|
options.push(
|
||||||
...locationResults.map(
|
...locationResults.map(
|
||||||
(searchResult) =>
|
(searchResult) =>
|
||||||
({
|
({
|
||||||
|
@ -152,7 +171,7 @@ export default function SearchBar(props: Props) {
|
||||||
} as Suggestion)
|
} as Suggestion)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return option;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOptions = debounce(getAutoCompleteSuggestions, 250);
|
const getOptions = debounce(getAutoCompleteSuggestions, 250);
|
||||||
|
@ -161,7 +180,6 @@ export default function SearchBar(props: Props) {
|
||||||
if (!selectedOption) {
|
if (!selectedOption) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (selectedOption.type) {
|
switch (selectedOption.type) {
|
||||||
case SuggestionType.DATE:
|
case SuggestionType.DATE:
|
||||||
props.setSearch({
|
props.setSearch({
|
||||||
|
@ -179,6 +197,11 @@ export default function SearchBar(props: Props) {
|
||||||
props.setActiveCollection(selectedOption.value as number);
|
props.setActiveCollection(selectedOption.value as number);
|
||||||
setValue(null);
|
setValue(null);
|
||||||
break;
|
break;
|
||||||
|
case SuggestionType.IMAGE:
|
||||||
|
case SuggestionType.VIDEO:
|
||||||
|
props.setSearch({ fileIndex: selectedOption.value as number });
|
||||||
|
setValue(null);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
|
@ -205,6 +228,10 @@ export default function SearchBar(props: Props) {
|
||||||
return <LocationIcon />;
|
return <LocationIcon />;
|
||||||
case SuggestionType.COLLECTION:
|
case SuggestionType.COLLECTION:
|
||||||
return <CollectionIcon />;
|
return <CollectionIcon />;
|
||||||
|
case SuggestionType.IMAGE:
|
||||||
|
return <ImageIcon />;
|
||||||
|
case SuggestionType.VIDEO:
|
||||||
|
return <VideoIcon />;
|
||||||
default:
|
default:
|
||||||
return <SearchIcon />;
|
return <SearchIcon />;
|
||||||
}
|
}
|
||||||
|
|
21
src/components/icons/ImageIcon.tsx
Normal file
21
src/components/icons/ImageIcon.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function ImageIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={props.height}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
width={props.width}
|
||||||
|
fill="currentColor">
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
|
<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14 6 17h12l-3.86-5.14z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIcon.defaultProps = {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
};
|
20
src/components/icons/VideoIcon.tsx
Normal file
20
src/components/icons/VideoIcon.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function VideoIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={props.height}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
width={props.width}
|
||||||
|
fill="currentColor">
|
||||||
|
<path d="M4 6.47L5.76 10H20v8H4V6.47M22 4h-4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoIcon.defaultProps = {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
};
|
|
@ -106,6 +106,7 @@ export type setSearchStats = React.Dispatch<React.SetStateAction<SearchStats>>;
|
||||||
export type Search = {
|
export type Search = {
|
||||||
date?: DateValue;
|
date?: DateValue;
|
||||||
location?: Bbox;
|
location?: Bbox;
|
||||||
|
fileIndex?: number;
|
||||||
};
|
};
|
||||||
export interface SearchStats {
|
export interface SearchStats {
|
||||||
resultCount: number;
|
resultCount: number;
|
||||||
|
@ -159,6 +160,7 @@ export default function Gallery() {
|
||||||
const [search, setSearch] = useState<Search>({
|
const [search, setSearch] = useState<Search>({
|
||||||
date: null,
|
date: null,
|
||||||
location: null,
|
location: null,
|
||||||
|
fileIndex: null,
|
||||||
});
|
});
|
||||||
const [uploadInProgress, setUploadInProgress] = useState(false);
|
const [uploadInProgress, setUploadInProgress] = useState(false);
|
||||||
const {
|
const {
|
||||||
|
@ -184,11 +186,6 @@ export default function Gallery() {
|
||||||
useState<Map<number, number>>();
|
useState<Map<number, number>>();
|
||||||
const [activeCollection, setActiveCollection] = useState<number>(undefined);
|
const [activeCollection, setActiveCollection] = useState<number>(undefined);
|
||||||
|
|
||||||
const [isSharedCollectionActive, setIsSharedCollectionActive] =
|
|
||||||
useState(false);
|
|
||||||
|
|
||||||
const [isFavCollectionActive, setIsFavCollectionActive] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
||||||
if (!key) {
|
if (!key) {
|
||||||
|
@ -246,14 +243,6 @@ export default function Gallery() {
|
||||||
}
|
}
|
||||||
const href = `/gallery${collectionURL}`;
|
const href = `/gallery${collectionURL}`;
|
||||||
router.push(href, undefined, { shallow: true });
|
router.push(href, undefined, { shallow: true });
|
||||||
|
|
||||||
setIsSharedCollectionActive(
|
|
||||||
isSharedCollection(activeCollection, collections)
|
|
||||||
);
|
|
||||||
|
|
||||||
setIsFavCollectionActive(
|
|
||||||
isFavoriteCollection(activeCollection, collections)
|
|
||||||
);
|
|
||||||
}, [activeCollection]);
|
}, [activeCollection]);
|
||||||
|
|
||||||
const syncWithRemote = async (force = false, silent = false) => {
|
const syncWithRemote = async (force = false, silent = false) => {
|
||||||
|
@ -544,6 +533,7 @@ export default function Gallery() {
|
||||||
loadingBar={loadingBar}
|
loadingBar={loadingBar}
|
||||||
isFirstFetch={isFirstFetch}
|
isFirstFetch={isFirstFetch}
|
||||||
collections={collections}
|
collections={collections}
|
||||||
|
files={files}
|
||||||
setActiveCollection={setActiveCollection}
|
setActiveCollection={setActiveCollection}
|
||||||
setSearch={updateSearch}
|
setSearch={updateSearch}
|
||||||
searchStats={searchStats}
|
searchStats={searchStats}
|
||||||
|
@ -624,7 +614,10 @@ export default function Gallery() {
|
||||||
deleted={deleted}
|
deleted={deleted}
|
||||||
setDialogMessage={setDialogMessage}
|
setDialogMessage={setDialogMessage}
|
||||||
activeCollection={activeCollection}
|
activeCollection={activeCollection}
|
||||||
isSharedCollection={isSharedCollectionActive}
|
isSharedCollection={isSharedCollection(
|
||||||
|
activeCollection,
|
||||||
|
collections
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{selected.count > 0 &&
|
{selected.count > 0 &&
|
||||||
selected.collectionID === activeCollection && (
|
selected.collectionID === activeCollection && (
|
||||||
|
@ -652,7 +645,10 @@ export default function Gallery() {
|
||||||
count={selected.count}
|
count={selected.count}
|
||||||
clearSelection={clearSelection}
|
clearSelection={clearSelection}
|
||||||
activeCollection={activeCollection}
|
activeCollection={activeCollection}
|
||||||
isFavoriteCollection={isFavCollectionActive}
|
isFavoriteCollection={isFavoriteCollection(
|
||||||
|
activeCollection,
|
||||||
|
collections
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FullScreenDropZone>
|
</FullScreenDropZone>
|
||||||
|
|
|
@ -23,7 +23,6 @@ export enum CollectionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
|
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
|
||||||
const FAV_COLLECTION = 'fav-collection';
|
|
||||||
const COLLECTIONS = 'collections';
|
const COLLECTIONS = 'collections';
|
||||||
|
|
||||||
export interface Collection {
|
export interface Collection {
|
||||||
|
@ -340,7 +339,11 @@ export const addToFavorites = async (file: File) => {
|
||||||
'Favorites',
|
'Favorites',
|
||||||
CollectionType.favorites
|
CollectionType.favorites
|
||||||
);
|
);
|
||||||
await localForage.setItem(FAV_COLLECTION, favCollection);
|
const localCollections = await getLocalCollections();
|
||||||
|
await localForage.setItem(COLLECTIONS, [
|
||||||
|
...localCollections,
|
||||||
|
favCollection,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
await addToCollection(favCollection, [file]);
|
await addToCollection(favCollection, [file]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { getToken } from 'utils/common/key';
|
||||||
import { DateValue, Suggestion, SuggestionType } from 'components/SearchBar';
|
import { DateValue, Suggestion, SuggestionType } from 'components/SearchBar';
|
||||||
import HTTPService from './HTTPService';
|
import HTTPService from './HTTPService';
|
||||||
import { Collection } from './collectionService';
|
import { Collection } from './collectionService';
|
||||||
|
import { File } from './fileService';
|
||||||
|
import { User } from './userService';
|
||||||
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
|
|
||||||
const ENDPOINT = getEndpoint();
|
const ENDPOINT = getEndpoint();
|
||||||
const DIGITS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
|
const DIGITS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
|
||||||
|
@ -110,3 +113,25 @@ export function searchCollection(
|
||||||
collection.name.toLowerCase().includes(searchPhrase)
|
collection.name.toLowerCase().includes(searchPhrase)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function searchFiles(searchPhrase: string, files: File[]) {
|
||||||
|
const user: User = getData(LS_KEYS.USER) ?? {};
|
||||||
|
const idSet = new Set();
|
||||||
|
return files
|
||||||
|
.map((file, idx) => ({
|
||||||
|
title: file.metadata.title,
|
||||||
|
index: idx,
|
||||||
|
type: file.metadata.fileType,
|
||||||
|
ownerID: file.ownerID,
|
||||||
|
id: file.id,
|
||||||
|
}))
|
||||||
|
.filter((file) => {
|
||||||
|
if (file.ownerID === user.id && !idSet.has(file.id)) {
|
||||||
|
idSet.add(file.id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.filter(({ title }) => title.toLowerCase().includes(searchPhrase))
|
||||||
|
.slice(0, 4);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue