@@ -651,7 +624,7 @@ const PhotoFrame = ({
return sum;
})();
files.length < 30 &&
- !searchMode &&
+ !isInSearchMode &&
timeStampList.push({
itemType: ITEM_TYPE.BANNER,
banner: (
diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx
index cffd5b679..7bcb3ad55 100644
--- a/src/components/PhotoSwipe/PhotoSwipe.tsx
+++ b/src/components/PhotoSwipe/PhotoSwipe.tsx
@@ -7,16 +7,15 @@ import {
addToFavorites,
removeFromFavorites,
} from 'services/collectionService';
-import { File, FILE_TYPE } from 'services/fileService';
+import { File } from 'services/fileService';
import constants from 'utils/strings/constants';
-import DownloadManger from 'services/downloadManager';
import exifr from 'exifr';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import styled from 'styled-components';
import events from './events';
-import { fileNameWithoutExtension, formatDateTime } from 'utils/file';
+import { downloadFile, formatDateTime } from 'utils/file';
import { FormCheck } from 'react-bootstrap';
import { prettyPrintExif } from 'utils/exif';
@@ -297,21 +296,11 @@ function PhotoSwipe(props: Iprops) {
setShowInfo(true);
};
- const downloadFile = async (file) => {
+ const downloadFileHelper = async (file) => {
const { loadingBar } = props;
- const a = document.createElement('a');
- a.style.display = 'none';
loadingBar.current.continuousStart();
- a.href = await DownloadManger.getFile(file);
+ await downloadFile(file);
loadingBar.current.complete();
- if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
- a.download = fileNameWithoutExtension(file.metadata.title) + '.zip';
- } else {
- a.download = file.metadata.title;
- }
- document.body.appendChild(a);
- a.click();
- a.remove();
};
const { id } = props;
let { className } = props;
@@ -345,7 +334,7 @@ function PhotoSwipe(props: Iprops) {
className="pswp-custom download-btn"
title={constants.DOWNLOAD}
onClick={() =>
- downloadFile(photoSwipe.currItem)
+ downloadFileHelper(photoSwipe.currItem)
}
/>
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index e854e2639..2463ffa75 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -10,6 +10,7 @@ import {
getYearSuggestion,
parseHumanDate,
searchCollection,
+ searchFiles,
searchLocation,
} from 'services/searchService';
import { getFormattedDate } from 'utils/search';
@@ -20,6 +21,9 @@ import SearchIcon from './icons/SearchIcon';
import CrossIcon from './icons/CrossIcon';
import { Collection } from 'services/collectionService';
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 }>`
position: fixed;
@@ -74,6 +78,8 @@ export enum SuggestionType {
DATE,
LOCATION,
COLLECTION,
+ IMAGE,
+ VIDEO,
}
export interface DateValue {
date?: number;
@@ -94,6 +100,7 @@ interface Props {
searchStats: SearchStats;
collections: Collection[];
setActiveCollection: (id: number) => void;
+ files: File[];
}
export default function SearchBar(props: Props) {
const [value, setValue] = useState
(null);
@@ -112,14 +119,14 @@ export default function SearchBar(props: Props) {
if (!searchPhrase?.length) {
return [];
}
- const option = [
+ const options = [
...getHolidaySuggestion(searchPhrase),
...getYearSuggestion(searchPhrase),
];
const searchedDates = parseHumanDate(searchPhrase);
- option.push(
+ options.push(
...searchedDates.map((searchedDate) => ({
type: SuggestionType.DATE,
value: searchedDate,
@@ -131,7 +138,7 @@ export default function SearchBar(props: Props) {
searchPhrase,
props.collections
);
- option.push(
+ options.push(
...collectionResults.map(
(searchResult) =>
({
@@ -141,8 +148,20 @@ export default function SearchBar(props: Props) {
} 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);
- option.push(
+ options.push(
...locationResults.map(
(searchResult) =>
({
@@ -152,7 +171,7 @@ export default function SearchBar(props: Props) {
} as Suggestion)
)
);
- return option;
+ return options;
};
const getOptions = debounce(getAutoCompleteSuggestions, 250);
@@ -161,7 +180,6 @@ export default function SearchBar(props: Props) {
if (!selectedOption) {
return;
}
-
switch (selectedOption.type) {
case SuggestionType.DATE:
props.setSearch({
@@ -177,12 +195,17 @@ export default function SearchBar(props: Props) {
break;
case SuggestionType.COLLECTION:
props.setActiveCollection(selectedOption.value as number);
- resetSearch(true);
+ setValue(null);
+ break;
+ case SuggestionType.IMAGE:
+ case SuggestionType.VIDEO:
+ props.setSearch({ fileIndex: selectedOption.value as number });
+ setValue(null);
break;
}
};
- const resetSearch = async (force?: boolean) => {
- if (props.isOpen || force) {
+ const resetSearch = () => {
+ if (props.isOpen) {
props.loadingBar.current?.continuousStart();
props.setSearch({});
setTimeout(() => {
@@ -205,6 +228,10 @@ export default function SearchBar(props: Props) {
return ;
case SuggestionType.COLLECTION:
return ;
+ case SuggestionType.IMAGE:
+ return ;
+ case SuggestionType.VIDEO:
+ return ;
default:
return ;
}
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index fb38ba5e0..8d7358048 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -36,6 +36,7 @@ import {
ARCHIVE_SECTION,
TRASH_SECTION,
} from 'components/pages/gallery/Collections';
+import FixLargeThumbnails from './FixLargeThumbnail';
interface Props {
collections: Collection[];
setDialogMessage: SetDialogMessage;
@@ -53,6 +54,7 @@ export default function Sidebar(props: Props) {
const [recoverModalView, setRecoveryModalView] = useState(false);
const [twoFactorModalView, setTwoFactorModalView] = useState(false);
const [exportModalView, setExportModalView] = useState(false);
+ const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
const galleryContext = useContext(GalleryContext);
useEffect(() => {
const main = async () => {
@@ -278,6 +280,18 @@ export default function Sidebar(props: Props) {
{constants.UPDATE_EMAIL}
+ <>
+ setFixLargeThumbsView(false)}
+ show={() => setFixLargeThumbsView(true)}
+ />
+ setFixLargeThumbsView(true)}>
+ {constants.FIX_LARGE_THUMBNAILS}
+
+ >
diff --git a/src/components/icons/ImageIcon.tsx b/src/components/icons/ImageIcon.tsx
new file mode 100644
index 000000000..6e2d6f8de
--- /dev/null
+++ b/src/components/icons/ImageIcon.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+
+export default function ImageIcon(props) {
+ return (
+
+ );
+}
+
+ImageIcon.defaultProps = {
+ height: 24,
+ width: 24,
+ viewBox: '0 0 24 24',
+};
diff --git a/src/components/icons/VideoIcon.tsx b/src/components/icons/VideoIcon.tsx
new file mode 100644
index 000000000..510367881
--- /dev/null
+++ b/src/components/icons/VideoIcon.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+
+export default function VideoIcon(props) {
+ return (
+
+ );
+}
+
+VideoIcon.defaultProps = {
+ height: 24,
+ width: 24,
+ viewBox: '0 0 24 24',
+};
diff --git a/src/components/pages/gallery/Collections.tsx b/src/components/pages/gallery/Collections.tsx
index e0b2bb5a3..391eddd77 100644
--- a/src/components/pages/gallery/Collections.tsx
+++ b/src/components/pages/gallery/Collections.tsx
@@ -36,7 +36,7 @@ interface CollectionProps {
syncWithRemote: () => Promise;
setCollectionNamerAttributes: SetCollectionNamerAttributes;
startLoadingBar: () => void;
- searchMode: boolean;
+ isInSearchMode: boolean;
collectionFilesCount: Map;
}
@@ -104,6 +104,11 @@ const SectionChipCreater =
/>
);
+const Hider = styled.div<{ hide: boolean }>`
+ opacity: ${(props) => (props.hide ? '0' : '100')};
+ height: ${(props) => (props.hide ? '0' : 'auto')};
+`;
+
export default function Collections(props: CollectionProps) {
const { activeCollection, collections, setActiveCollection } = props;
const [selectedCollectionID, setSelectedCollectionID] =
@@ -136,7 +141,7 @@ export default function Collections(props: CollectionProps) {
useEffect(() => {
updateScrollObj();
- }, [collectionWrapperRef.current]);
+ }, [collectionWrapperRef.current, props.isInSearchMode, collections]);
useEffect(() => {
if (!collectionWrapperRef?.current) {
@@ -199,101 +204,94 @@ export default function Collections(props: CollectionProps) {
const SectionChip = SectionChipCreater({ activeCollection, clickHandler });
return (
- !props.searchMode && (
- <>
- setCollectionShareModalView(false)}
- collection={getSelectedCollection(
- selectedCollectionID,
- props.collections
+
+ setCollectionShareModalView(false)}
+ collection={getSelectedCollection(
+ selectedCollectionID,
+ props.collections
+ )}
+ syncWithRemote={props.syncWithRemote}
+ />
+
+
+ {scrollObj.scrollLeft > 0 && (
+
)}
- syncWithRemote={props.syncWithRemote}
- />
-
-
- {scrollObj.scrollLeft > 0 && (
-
- )}
-
-
- {sortCollections(
- collections,
- props.collectionAndTheirLatestFile,
- collectionSortBy
- ).map((item) => (
-
-
- {item.name}
- {item.type !==
- CollectionType.favorites &&
- item.owner.id === user?.id ? (
-
-
- setSelectedCollectionID(
- item.id
- )
- }
- />
-
- ) : (
-
+
+ {sortCollections(
+ collections,
+ props.collectionAndTheirLatestFile,
+ collectionSortBy
+ ).map((item) => (
+
+
+ {item.name}
+ {item.type !== CollectionType.favorites &&
+ item.owner.id === user?.id ? (
+
+
+ setSelectedCollectionID(
+ item.id
+ )
+ }
/>
- )}
-
-
- ))}
-
-
-
- {scrollObj.scrollLeft <
- scrollObj.scrollWidth - scrollObj.clientWidth && (
-
- )}
-
-
-
- >
- )
+
+ ) : (
+
+ )}
+
+
+ ))}
+
+
+
+ {scrollObj.scrollLeft <
+ scrollObj.scrollWidth - scrollObj.clientWidth && (
+
+ )}
+
+
+
+
);
}
diff --git a/src/components/pages/gallery/PreviewCard.tsx b/src/components/pages/gallery/PreviewCard.tsx
index 5c698ffb0..bacbff199 100644
--- a/src/components/pages/gallery/PreviewCard.tsx
+++ b/src/components/pages/gallery/PreviewCard.tsx
@@ -128,15 +128,19 @@ export default function PreviewCard(props: IProps) {
useLayoutEffect(() => {
if (file && !file.msrc) {
const main = async () => {
- const url = await DownloadManager.getPreview(file);
- if (isMounted.current) {
- setImgSrc(url);
- thumbs.set(file.id, url);
- file.msrc = url;
- if (!file.src) {
- file.src = url;
+ try {
+ const url = await DownloadManager.getPreview(file);
+ if (isMounted.current) {
+ setImgSrc(url);
+ thumbs.set(file.id, url);
+ file.msrc = url;
+ if (!file.src) {
+ file.src = url;
+ }
+ updateUrl(url);
}
- updateUrl(url);
+ } catch (e) {
+ // no-op
}
};
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index ac2a2d16b..0d5a52017 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -79,12 +79,33 @@ const GlobalStyles = createGlobalStyle`
height: 100%;
}
- .video-loading > div {
+ .video-loading > div.spinner-border {
position: relative;
top: -50vh;
left: 50vw;
}
+
+
+ .video-loading > div.download-message {
+ position: relative;
+ top: -60vh;
+ left: 0;
+ height: 16vh;
+ padding:2vh 0;
+ background-color: #151414;
+ color:#ddd;
+ display: flex;
+ flex-direction:column;
+ align-items: center;
+ justify-content: space-around;
+ opacity: 0.8;
+ font-size:20px;
+ }
+ .download-message > a{
+ width: 130px;
+ }
+
:root {
--primary: #e26f99,
};
diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx
index 2d0ddac7c..a28f361cb 100644
--- a/src/pages/gallery/index.tsx
+++ b/src/pages/gallery/index.tsx
@@ -120,6 +120,7 @@ export type setSearchStats = React.Dispatch
>;
export type Search = {
date?: DateValue;
location?: Bbox;
+ fileIndex?: number;
};
export interface SearchStats {
resultCount: number;
@@ -173,6 +174,7 @@ export default function Gallery() {
const [search, setSearch] = useState({
date: null,
location: null,
+ fileIndex: null,
});
const [uploadInProgress, setUploadInProgress] = useState(false);
const {
@@ -188,7 +190,7 @@ export default function Gallery() {
});
const loadingBar = useRef(null);
- const [searchMode, setSearchMode] = useState(false);
+ const [isInSearchMode, setIsInSearchMode] = useState(false);
const [searchStats, setSearchStats] = useState(null);
const syncInProgress = useRef(true);
const resync = useRef(false);
@@ -197,11 +199,6 @@ export default function Gallery() {
const [collectionFilesCount, setCollectionFilesCount] =
useState