Merge branch 'master' into update-creation-time

This commit is contained in:
Abhinav 2021-11-01 22:37:55 +05:30
commit 4b3e9f9e08
5 changed files with 164 additions and 30 deletions

View file

@ -24,6 +24,7 @@ interface Props {
export enum FIX_STATE { export enum FIX_STATE {
NOT_STARTED, NOT_STARTED,
FIX_LATER, FIX_LATER,
NOOP,
RUNNING, RUNNING,
COMPLETED, COMPLETED,
COMPLETED_WITH_ERRORS, COMPLETED_WITH_ERRORS,
@ -38,6 +39,9 @@ function Message(props: { fixState: FIX_STATE }) {
case FIX_STATE.COMPLETED: case FIX_STATE.COMPLETED:
message = constants.REPLACE_THUMBNAIL_COMPLETED(); message = constants.REPLACE_THUMBNAIL_COMPLETED();
break; break;
case FIX_STATE.NOOP:
message = constants.REPLACE_THUMBNAIL_NOOP();
break;
case FIX_STATE.COMPLETED_WITH_ERRORS: case FIX_STATE.COMPLETED_WITH_ERRORS:
message = constants.REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR(); message = constants.REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR();
break; break;
@ -64,6 +68,10 @@ export default function FixLargeThumbnails(props: Props) {
fixState = FIX_STATE.NOT_STARTED; fixState = FIX_STATE.NOT_STARTED;
updateFixState(fixState); updateFixState(fixState);
} }
if (fixState === FIX_STATE.COMPLETED) {
fixState = FIX_STATE.NOOP;
updateFixState(fixState);
}
setFixState(fixState); setFixState(fixState);
return fixState; return fixState;
}; };
@ -83,14 +91,14 @@ export default function FixLargeThumbnails(props: Props) {
props.show(); props.show();
} }
if ( if (
fixState === FIX_STATE.COMPLETED && (fixState === FIX_STATE.COMPLETED || fixState === FIX_STATE.NOOP) &&
largeThumbnailFiles.length > 0 largeThumbnailFiles.length > 0
) { ) {
updateFixState(FIX_STATE.NOT_STARTED); updateFixState(FIX_STATE.NOT_STARTED);
logError(Error(), 'large thumbnail files left after migration'); logError(Error(), 'large thumbnail files left after migration');
} }
if (largeThumbnailFiles.length === 0) { if (largeThumbnailFiles.length === 0 && fixState !== FIX_STATE.NOOP) {
updateFixState(FIX_STATE.COMPLETED); updateFixState(FIX_STATE.NOOP);
} }
}; };
useEffect(() => { useEffect(() => {

View file

@ -86,6 +86,7 @@ const getTemplateColumns = (columns: number, groups?: number[]): string => {
}; };
const ListContainer = styled.div<{ columns: number; groups?: number[] }>` const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
user-select: none;
display: grid; display: grid;
grid-template-columns: ${({ columns, groups }) => grid-template-columns: ${({ columns, groups }) =>
getTemplateColumns(columns, groups)}; getTemplateColumns(columns, groups)};
@ -100,6 +101,7 @@ const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
`; `;
const DateContainer = styled.div<{ span: number }>` const DateContainer = styled.div<{ span: number }>`
user-select: none;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -181,6 +183,28 @@ const PhotoFrame = ({
const startTime = Date.now(); const startTime = Date.now();
const galleryContext = useContext(GalleryContext); const galleryContext = useContext(GalleryContext);
const listRef = useRef(null); const listRef = useRef(null);
const [rangeStart, setRangeStart] = useState(null);
const [currentHover, setCurrentHover] = useState(null);
const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Shift') {
setIsShiftKeyPressed(true);
}
};
const handleKeyUp = (e: KeyboardEvent) => {
if (e.key === 'Shift') {
setIsShiftKeyPressed(false);
}
};
document.addEventListener('keydown', handleKeyDown, false);
document.addEventListener('keyup', handleKeyUp, false);
return () => {
document.addEventListener('keydown', handleKeyDown, false);
document.addEventListener('keyup', handleKeyUp, false);
};
}, []);
useEffect(() => { useEffect(() => {
if (isInSearchMode) { if (isInSearchMode) {
@ -204,6 +228,12 @@ const PhotoFrame = ({
setFetching({}); setFetching({});
}, [files, search, deleted]); }, [files, search, deleted]);
useEffect(() => {
if (selected.count === 0) {
setRangeStart(null);
}
}, [selected]);
const updateUrl = (index: number) => (url: string) => { const updateUrl = (index: number) => (url: string) => {
files[index] = { files[index] = {
...files[index], ...files[index],
@ -276,10 +306,16 @@ const PhotoFrame = ({
setOpen(true); setOpen(true);
}; };
const handleSelect = (id: number) => (checked: boolean) => { const handleSelect = (id: number, index?: number) => (checked: boolean) => {
if (selected.collectionID !== activeCollection) { if (selected.collectionID !== activeCollection) {
setSelected({ count: 0, collectionID: 0 }); setSelected({ count: 0, collectionID: 0 });
} }
if (rangeStart || rangeStart === 0) {
setRangeStart(null);
} else if (checked) {
setRangeStart(index);
}
setSelected((selected) => ({ setSelected((selected) => ({
...selected, ...selected,
[id]: checked, [id]: checked,
@ -287,19 +323,50 @@ const PhotoFrame = ({
collectionID: activeCollection, collectionID: activeCollection,
})); }));
}; };
const onHoverOver = (index: number) => () => {
setCurrentHover(index);
};
const handleRangeSelect = (index: number) => () => {
if (rangeStart !== index) {
let leftEnd = -1;
let rightEnd = -1;
if (index < rangeStart) {
leftEnd = index;
rightEnd = rangeStart;
} else {
leftEnd = rangeStart;
rightEnd = index;
}
for (let i = leftEnd; i <= rightEnd; i++) {
handleSelect(filteredData[i].id)(true);
}
}
};
const getThumbnail = (file: File[], index: number) => ( const getThumbnail = (file: File[], index: number) => (
<PreviewCard <PreviewCard
key={`tile-${file[index].id}`} key={`tile-${file[index].id}-selected-${
selected[file[index].id] ?? false
}`}
file={file[index]} file={file[index]}
updateUrl={updateUrl(file[index].dataIndex)} updateUrl={updateUrl(file[index].dataIndex)}
onClick={onThumbnailClick(index)} onClick={onThumbnailClick(index)}
selectable={!isSharedCollection} selectable={!isSharedCollection}
onSelect={handleSelect(file[index].id)} onSelect={handleSelect(file[index].id, index)}
selected={ selected={
selected.collectionID === activeCollection && selected.collectionID === activeCollection &&
selected[file[index].id] selected[file[index].id]
} }
selectOnClick={selected.count > 0} selectOnClick={selected.count > 0}
onHover={onHoverOver(index)}
onRangeSelect={handleRangeSelect(index)}
isRangeSelectActive={
isShiftKeyPressed && (rangeStart || rangeStart === 0)
}
isInsSelectRange={
(index >= rangeStart + 1 && index <= currentHover) ||
(index >= currentHover && index <= rangeStart - 1)
}
/> />
); );

View file

@ -15,13 +15,18 @@ interface IProps {
selectable?: boolean; selectable?: boolean;
selected?: boolean; selected?: boolean;
onSelect?: (checked: boolean) => void; onSelect?: (checked: boolean) => void;
onHover?: () => void;
onRangeSelect?: () => void;
isRangeSelectActive?: boolean;
selectOnClick?: boolean; selectOnClick?: boolean;
isInsSelectRange?: boolean;
} }
const Check = styled.input` const Check = styled.input<{ active: boolean }>`
appearance: none; appearance: none;
position: absolute; position: absolute;
right: 0; z-index: 10;
left: 0;
opacity: 0; opacity: 0;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
@ -34,7 +39,7 @@ const Check = styled.input`
width: 16px; width: 16px;
height: 16px; height: 16px;
border: 2px solid #fff; border: 2px solid #fff;
background-color: rgba(0, 0, 0, 0.5); background-color: #ddd;
display: inline-block; display: inline-block;
border-radius: 50%; border-radius: 50%;
vertical-align: bottom; vertical-align: bottom;
@ -43,18 +48,19 @@ const Check = styled.input`
line-height: 16px; line-height: 16px;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
pointer-events: inherit; pointer-events: inherit;
color: #aaa;
} }
&::after { &::after {
content: ''; content: '';
width: 5px; width: 5px;
height: 10px; height: 10px;
border-right: 2px solid #fff; border-right: 2px solid #333;
border-bottom: 2px solid #fff; border-bottom: 2px solid #333;
transform: translate(-18px, 8px); transform: translate(-18px, 8px);
opacity: 0;
transition: transform 0.3s ease; transition: transform 0.3s ease;
position: absolute; position: absolute;
pointer-events: inherit; pointer-events: inherit;
transform: translate(-18px, 10px) rotate(45deg);
} }
/** checked */ /** checked */
@ -65,15 +71,50 @@ const Check = styled.input`
color: #fff; color: #fff;
} }
&:checked::after { &:checked::after {
opacity: 1; content: '';
transform: translate(-18px, 10px) rotate(45deg); border-right: 2px solid #ddd;
border-bottom: 2px solid #ddd;
} }
${(props) => props.active && 'opacity: 0.5 '};
&:checked { &:checked {
opacity: 1; opacity: 1 !important;
} }
`; `;
export const HoverOverlay = styled.div<{ checked: boolean }>`
opacity: 0;
left: 0;
top: 0;
outline: none;
height: 40%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 900;
position: absolute;
${(props) =>
!props.checked &&
'background:linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0))'};
`;
export const InSelectRangeOverLay = styled.div<{ active: boolean }>`
opacity: ${(props) => (!props.active ? 0 : 1)});
left: 0;
top: 0;
outline: none;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 900;
position: absolute;
${(props) => props.active && 'background:rgba(81, 205, 124, 0.25)'};
`;
const Cont = styled.div<{ disabled: boolean; selected: boolean }>` const Cont = styled.div<{ disabled: boolean; selected: boolean }>`
background: #222; background: #222;
display: flex; display: flex;
@ -107,6 +148,9 @@ const Cont = styled.div<{ disabled: boolean; selected: boolean }>`
} }
&:hover ${Check} { &:hover ${Check} {
opacity: 0.5;
}
&:hover ${HoverOverlay} {
opacity: 1; opacity: 1;
} }
`; `;
@ -123,6 +167,10 @@ export default function PreviewCard(props: IProps) {
selected, selected,
onSelect, onSelect,
selectOnClick, selectOnClick,
onHover,
onRangeSelect,
isRangeSelectActive,
isInsSelectRange,
} = props; } = props;
const isMounted = useRef(true); const isMounted = useRef(true);
useLayoutEffect(() => { useLayoutEffect(() => {
@ -167,24 +215,36 @@ export default function PreviewCard(props: IProps) {
const handleClick = () => { const handleClick = () => {
if (selectOnClick) { if (selectOnClick) {
onSelect?.(!selected); if (isRangeSelectActive) {
onRangeSelect();
} else {
onSelect?.(!selected);
}
} else if (file?.msrc || imgSrc) { } else if (file?.msrc || imgSrc) {
onClick?.(); onClick?.();
} }
}; };
const handleSelect: React.ChangeEventHandler<HTMLInputElement> = (e) => { const handleSelect: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (isRangeSelectActive) {
onRangeSelect?.();
}
onSelect?.(e.target.checked); onSelect?.(e.target.checked);
}; };
const longPressCallback = () => { const longPressCallback = () => {
onSelect(!selected); onSelect(!selected);
}; };
const handleHover = () => {
if (selectOnClick) {
onHover();
}
};
return ( return (
<Cont <Cont
id={`thumb-${file?.id}`} id={`thumb-${file?.id}`}
onClick={handleClick} onClick={handleClick}
onMouseEnter={handleHover}
disabled={!forcedEnable && !file?.msrc && !imgSrc} disabled={!forcedEnable && !file?.msrc && !imgSrc}
selected={selected} selected={selected}
{...(selectable ? useLongPress(longPressCallback, 500) : {})}> {...(selectable ? useLongPress(longPressCallback, 500) : {})}>
@ -193,11 +253,16 @@ export default function PreviewCard(props: IProps) {
type="checkbox" type="checkbox"
checked={selected} checked={selected}
onChange={handleSelect} onChange={handleSelect}
active={isRangeSelectActive && isInsSelectRange}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
/> />
)} )}
{(file?.msrc || imgSrc) && <img src={file?.msrc || imgSrc} />} {(file?.msrc || imgSrc) && <img src={file?.msrc || imgSrc} />}
{file?.metadata.fileType === 1 && <PlayCircleOutline />} {file?.metadata.fileType === 1 && <PlayCircleOutline />}
<HoverOverlay checked={selected} />
<InSelectRangeOverLay
active={isRangeSelectActive && isInsSelectRange}
/>
</Cont> </Cont>
); );
} }

View file

@ -14,7 +14,6 @@ import { decryptFile, mergeMetadata, sortFiles } from 'utils/file';
import CryptoWorker from 'utils/crypto'; import CryptoWorker from 'utils/crypto';
const ENDPOINT = getEndpoint(); const ENDPOINT = getEndpoint();
const DIFF_LIMIT: number = 1000;
const FILES = 'files'; const FILES = 'files';
@ -148,13 +147,7 @@ export const syncFiles = async (
continue; continue;
} }
const fetchedFiles = const fetchedFiles =
(await getFiles( (await getFiles(collection, lastSyncTime, files, setFiles)) ?? [];
collection,
lastSyncTime,
DIFF_LIMIT,
files,
setFiles
)) ?? [];
files.push(...fetchedFiles); files.push(...fetchedFiles);
const latestVersionFiles = new Map<string, File>(); const latestVersionFiles = new Map<string, File>();
files.forEach((file) => { files.forEach((file) => {
@ -198,7 +191,6 @@ export const syncFiles = async (
export const getFiles = async ( export const getFiles = async (
collection: Collection, collection: Collection,
sinceTime: number, sinceTime: number,
limit: number,
files: File[], files: File[],
setFiles: (files: File[]) => void setFiles: (files: File[]) => void
): Promise<File[]> => { ): Promise<File[]> => {
@ -215,11 +207,10 @@ export const getFiles = async (
break; break;
} }
resp = await HTTPService.get( resp = await HTTPService.get(
`${ENDPOINT}/collections/diff`, `${ENDPOINT}/collections/v2/diff`,
{ {
collectionID: collection.id, collectionID: collection.id,
sinceTime: time, sinceTime: time,
limit,
}, },
{ {
'X-Auth-Token': token, 'X-Auth-Token': token,
@ -249,7 +240,7 @@ export const getFiles = async (
) )
) )
); );
} while (resp.data.diff.length === limit); } while (resp.data.hasMore);
return decryptedFiles; return decryptedFiles;
} catch (e) { } catch (e) {
logError(e, 'Get files failed'); logError(e, 'Get files failed');

View file

@ -590,6 +590,9 @@ const englishConstants = {
REPLACE_THUMBNAIL_COMPLETED: () => ( REPLACE_THUMBNAIL_COMPLETED: () => (
<>successfully compressed all thumbnails</> <>successfully compressed all thumbnails</>
), ),
REPLACE_THUMBNAIL_NOOP: () => (
<>you have no thumbnails that can be compressed further</>
),
REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => (
<>could not compress some of your thumbnails, please retry</> <>could not compress some of your thumbnails, please retry</>
), ),