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

View file

@ -86,6 +86,7 @@ const getTemplateColumns = (columns: number, groups?: number[]): string => {
};
const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
user-select: none;
display: grid;
grid-template-columns: ${({ columns, groups }) =>
getTemplateColumns(columns, groups)};
@ -100,6 +101,7 @@ const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
`;
const DateContainer = styled.div<{ span: number }>`
user-select: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -181,6 +183,28 @@ const PhotoFrame = ({
const startTime = Date.now();
const galleryContext = useContext(GalleryContext);
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(() => {
if (isInSearchMode) {
@ -204,6 +228,12 @@ const PhotoFrame = ({
setFetching({});
}, [files, search, deleted]);
useEffect(() => {
if (selected.count === 0) {
setRangeStart(null);
}
}, [selected]);
const updateUrl = (index: number) => (url: string) => {
files[index] = {
...files[index],
@ -276,10 +306,16 @@ const PhotoFrame = ({
setOpen(true);
};
const handleSelect = (id: number) => (checked: boolean) => {
const handleSelect = (id: number, index?: number) => (checked: boolean) => {
if (selected.collectionID !== activeCollection) {
setSelected({ count: 0, collectionID: 0 });
}
if (rangeStart || rangeStart === 0) {
setRangeStart(null);
} else if (checked) {
setRangeStart(index);
}
setSelected((selected) => ({
...selected,
[id]: checked,
@ -287,19 +323,50 @@ const PhotoFrame = ({
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) => (
<PreviewCard
key={`tile-${file[index].id}`}
key={`tile-${file[index].id}-selected-${
selected[file[index].id] ?? false
}`}
file={file[index]}
updateUrl={updateUrl(file[index].dataIndex)}
onClick={onThumbnailClick(index)}
selectable={!isSharedCollection}
onSelect={handleSelect(file[index].id)}
onSelect={handleSelect(file[index].id, index)}
selected={
selected.collectionID === activeCollection &&
selected[file[index].id]
}
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;
selected?: boolean;
onSelect?: (checked: boolean) => void;
onHover?: () => void;
onRangeSelect?: () => void;
isRangeSelectActive?: boolean;
selectOnClick?: boolean;
isInsSelectRange?: boolean;
}
const Check = styled.input`
const Check = styled.input<{ active: boolean }>`
appearance: none;
position: absolute;
right: 0;
z-index: 10;
left: 0;
opacity: 0;
outline: none;
cursor: pointer;
@ -34,7 +39,7 @@ const Check = styled.input`
width: 16px;
height: 16px;
border: 2px solid #fff;
background-color: rgba(0, 0, 0, 0.5);
background-color: #ddd;
display: inline-block;
border-radius: 50%;
vertical-align: bottom;
@ -43,18 +48,19 @@ const Check = styled.input`
line-height: 16px;
transition: background-color 0.3s ease;
pointer-events: inherit;
color: #aaa;
}
&::after {
content: '';
width: 5px;
height: 10px;
border-right: 2px solid #fff;
border-bottom: 2px solid #fff;
border-right: 2px solid #333;
border-bottom: 2px solid #333;
transform: translate(-18px, 8px);
opacity: 0;
transition: transform 0.3s ease;
position: absolute;
pointer-events: inherit;
transform: translate(-18px, 10px) rotate(45deg);
}
/** checked */
@ -65,15 +71,50 @@ const Check = styled.input`
color: #fff;
}
&:checked::after {
opacity: 1;
transform: translate(-18px, 10px) rotate(45deg);
content: '';
border-right: 2px solid #ddd;
border-bottom: 2px solid #ddd;
}
${(props) => props.active && 'opacity: 0.5 '};
&: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 }>`
background: #222;
display: flex;
@ -107,6 +148,9 @@ const Cont = styled.div<{ disabled: boolean; selected: boolean }>`
}
&:hover ${Check} {
opacity: 0.5;
}
&:hover ${HoverOverlay} {
opacity: 1;
}
`;
@ -123,6 +167,10 @@ export default function PreviewCard(props: IProps) {
selected,
onSelect,
selectOnClick,
onHover,
onRangeSelect,
isRangeSelectActive,
isInsSelectRange,
} = props;
const isMounted = useRef(true);
useLayoutEffect(() => {
@ -167,24 +215,36 @@ export default function PreviewCard(props: IProps) {
const handleClick = () => {
if (selectOnClick) {
onSelect?.(!selected);
if (isRangeSelectActive) {
onRangeSelect();
} else {
onSelect?.(!selected);
}
} else if (file?.msrc || imgSrc) {
onClick?.();
}
};
const handleSelect: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (isRangeSelectActive) {
onRangeSelect?.();
}
onSelect?.(e.target.checked);
};
const longPressCallback = () => {
onSelect(!selected);
};
const handleHover = () => {
if (selectOnClick) {
onHover();
}
};
return (
<Cont
id={`thumb-${file?.id}`}
onClick={handleClick}
onMouseEnter={handleHover}
disabled={!forcedEnable && !file?.msrc && !imgSrc}
selected={selected}
{...(selectable ? useLongPress(longPressCallback, 500) : {})}>
@ -193,11 +253,16 @@ export default function PreviewCard(props: IProps) {
type="checkbox"
checked={selected}
onChange={handleSelect}
active={isRangeSelectActive && isInsSelectRange}
onClick={(e) => e.stopPropagation()}
/>
)}
{(file?.msrc || imgSrc) && <img src={file?.msrc || imgSrc} />}
{file?.metadata.fileType === 1 && <PlayCircleOutline />}
<HoverOverlay checked={selected} />
<InSelectRangeOverLay
active={isRangeSelectActive && isInsSelectRange}
/>
</Cont>
);
}

View file

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

View file

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