diff --git a/package.json b/package.json index 5f5256267..903786fe6 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,13 @@ "prepare": "husky install" }, "dependencies": { + "@date-io/date-fns": "^2.14.0", "@ente-io/next-with-workbox": "^1.0.3", "@mui/icons-material": "^5.6.2", "@mui/material": "^5.6.2", "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest", "@mui/styled-engine-sc": "^5.6.1", + "@mui/x-date-pickers": "^5.0.0-alpha.6", "@sentry/nextjs": "^6.7.1", "@stripe/stripe-js": "^1.13.2", "@typescript-eslint/eslint-plugin": "^4.25.0", @@ -49,7 +51,6 @@ "piexifjs": "^1.0.6", "react": "^17.0.2", "react-bootstrap": "^1.3.0", - "react-datepicker": "^4.3.0", "react-dom": "^17.0.2", "react-dropzone": "^11.2.4", "react-otp-input": "^2.3.1", diff --git a/src/components/Collections/index.tsx b/src/components/Collections/index.tsx index eef851ea7..89bd81ffc 100644 --- a/src/components/Collections/index.tsx +++ b/src/components/Collections/index.tsx @@ -7,6 +7,7 @@ import { ALL_SECTION } from 'constants/collection'; import CollectionShare from 'components/Collections/CollectionShare'; import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer'; import { ITEM_TYPE, TimeStampListItem } from 'components/PhotoList'; +import { hasNonEmptyCollections } from 'utils/collection'; interface Iprops { collections: Collection[]; @@ -35,7 +36,8 @@ export default function Collections(props: Iprops) { const collectionsMap = useRef>(new Map()); const activeCollection = useRef(null); - const shouldBeHidden = isInSearchMode || collectionSummaries?.size <= 3; + const shouldBeHidden = + isInSearchMode || hasNonEmptyCollections(collectionSummaries); useEffect(() => { collectionsMap.current = new Map( diff --git a/src/components/Collections/styledComponents.ts b/src/components/Collections/styledComponents.ts index 82d34105a..87b987614 100644 --- a/src/components/Collections/styledComponents.ts +++ b/src/components/Collections/styledComponents.ts @@ -15,6 +15,7 @@ export const CollectionListBarWrapper = styled(PaddedContainer)` `; export const CollectionInfoBarWrapper = styled(Box)` + width: 100%; margin-bottom: 24px; `; diff --git a/src/components/Container.ts b/src/components/Container.ts index 3af2b1b27..448d51a27 100644 --- a/src/components/Container.ts +++ b/src/components/Container.ts @@ -39,6 +39,7 @@ export const IconButton = styled('button')` `; export const Row = styled('div')` + min-height: 32px; display: flex; align-items: center; margin-bottom: ${({ theme }) => theme.spacing(2)}; diff --git a/src/components/EnteDateTimePicker.tsx b/src/components/EnteDateTimePicker.tsx index 164417fd9..532549732 100644 --- a/src/components/EnteDateTimePicker.tsx +++ b/src/components/EnteDateTimePicker.tsx @@ -1,50 +1,68 @@ -import React from 'react'; +import React, { useState } from 'react'; -import DatePicker from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; import { MIN_EDITED_CREATION_TIME, MAX_EDITED_CREATION_TIME, - ALL_TIME, } from 'constants/file'; - -const isSameDay = (first, second) => - first.getFullYear() === second.getFullYear() && - first.getMonth() === second.getMonth() && - first.getDate() === second.getDate(); +import { TextField } from '@mui/material'; +import { + LocalizationProvider, + MobileDateTimePicker, +} from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; interface Props { - loading?: boolean; - isInEditMode: boolean; - pickedTime: Date; - handleChange: (date: Date) => void; + initialValue?: Date; + disabled?: boolean; + label?: string; + onSubmit: (date: Date) => void; + onClose?: () => void; } const EnteDateTimePicker = ({ - loading, - isInEditMode, - pickedTime, - handleChange, -}: Props) => ( - -); + initialValue, + disabled, + onSubmit, + onClose, +}: Props) => { + const [open, setOpen] = useState(true); + const [value, setValue] = useState(initialValue ?? new Date()); + + const handleClose = () => { + setOpen(false); + onClose?.(); + }; + return ( + + setOpen(true)} + maxDateTime={MAX_EDITED_CREATION_TIME} + minDateTime={MIN_EDITED_CREATION_TIME} + disabled={disabled} + onAccept={onSubmit} + DialogProps={{ + sx: { + zIndex: '1502', + '.MuiPickersToolbar-penIconButton': { + display: 'none', + }, + }, + }} + renderInput={(params) => ( + + )} + /> + + ); +}; export default EnteDateTimePicker; diff --git a/src/components/FixCreationTime/options.tsx b/src/components/FixCreationTime/options.tsx index 673260aef..3415c3887 100644 --- a/src/components/FixCreationTime/options.tsx +++ b/src/components/FixCreationTime/options.tsx @@ -69,9 +69,7 @@ export default function FixCreationTimeOptions({ handleChange, values }) { {Number(values.option) === FIX_OPTIONS.CUSTOM_TIME && ( + onSubmit={(x: Date) => handleChange('customTime')(x.toUTCString()) } /> diff --git a/src/components/PhotoList.tsx b/src/components/PhotoList.tsx index 99f534deb..41130987a 100644 --- a/src/components/PhotoList.tsx +++ b/src/components/PhotoList.tsx @@ -107,6 +107,7 @@ const FooterContainer = styled(ListItemContainer)` } color: #979797; text-align: center; + justify-content: center; align-items: flex-end; margin-top: calc(2rem + 20px); `; @@ -163,7 +164,11 @@ export function PhotoList({ }; useEffect(() => { - let timeStampList: TimeStampListItem[] = [getPhotoListHeader()]; + let timeStampList: TimeStampListItem[] = []; + + if (galleryContext.photoListHeader) { + timeStampList.push(getPhotoListHeader()); + } if (deduplicateContext.isOnDeduplicatePage) { skipMerge = true; groupByFileSize(timeStampList); diff --git a/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx b/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx index b27152d04..aaf2e34f4 100644 --- a/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx +++ b/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx @@ -10,10 +10,8 @@ import { import EditIcon from 'components/icons/EditIcon'; import { IconButton, Label, Row, Value } from 'components/Container'; import { logError } from 'utils/sentry'; -import CloseIcon from '@mui/icons-material/Close'; -import TickIcon from '@mui/icons-material/Done'; -import EnteDateTimePicker from 'components/EnteDateTimePicker'; import { SmallLoadingSpinner } from '../styledComponents/SmallLoadingSpinner'; +import EnteDateTimePicker from 'components/EnteDateTimePicker'; export function RenderCreationTime({ shouldDisableEdits, @@ -28,12 +26,10 @@ export function RenderCreationTime({ const originalCreationTime = new Date(file?.metadata.creationTime / 1000); const [isInEditMode, setIsInEditMode] = useState(false); - const [pickedTime, setPickedTime] = useState(originalCreationTime); - const openEditMode = () => setIsInEditMode(true); const closeEditMode = () => setIsInEditMode(false); - const saveEdits = async () => { + const saveEdits = async (pickedTime: Date) => { try { setLoading(true); if (isInEditMode && file) { @@ -59,59 +55,38 @@ export function RenderCreationTime({ setLoading(false); } }; - const discardEdits = () => { - setPickedTime(originalCreationTime); - closeEditMode(); - }; - const handleChange = (newDate: Date) => { - if (newDate instanceof Date) { - setPickedTime(newDate); - } - }; + return ( <> {isInEditMode ? ( ) : ( - formatDateTime(pickedTime) + formatDateTime(originalCreationTime) )} - {!shouldDisableEdits && ( + {!shouldDisableEdits && !isInEditMode && ( - {!isInEditMode ? ( + {loading ? ( + + + + ) : ( - ) : ( - <> - - {loading ? ( - - ) : ( - - )} - - - - - )} )} diff --git a/src/constants/file/index.ts b/src/constants/file/index.ts index a7aa4121a..93f8eb81b 100644 --- a/src/constants/file/index.ts +++ b/src/constants/file/index.ts @@ -1,6 +1,5 @@ export const MIN_EDITED_CREATION_TIME = new Date(1800, 0, 1); export const MAX_EDITED_CREATION_TIME = new Date(); -export const ALL_TIME = new Date(1800, 0, 1, 23, 59, 59); export const MAX_EDITED_FILE_NAME_LENGTH = 100; export const MAX_TRASH_BATCH_SIZE = 1000; diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index a60102e5e..8e80da06c 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -75,6 +75,7 @@ import { getSelectedCollection, isFavoriteCollection, getArchivedCollections, + hasNonEmptyCollections, } from 'utils/collection'; import { logError } from 'utils/sentry'; import { @@ -687,7 +688,7 @@ export default function Gallery() { setUploadInProgress={setUploadInProgress} fileRejections={fileRejections} setFiles={setFiles} - isFirstUpload={collectionSummaries?.size === 0} + isFirstUpload={hasNonEmptyCollections(collectionSummaries)} electronFiles={electronFiles} setElectronFiles={setElectronFiles} uploadTypeSelectorView={uploadTypeSelectorView} diff --git a/src/utils/collection/index.ts b/src/utils/collection/index.ts index 89c89105f..efe3a6935 100644 --- a/src/utils/collection/index.ts +++ b/src/utils/collection/index.ts @@ -14,7 +14,11 @@ import { User } from 'types/user'; import { getData, LS_KEYS } from 'utils/storage/localStorage'; import { logError } from 'utils/sentry'; import constants from 'utils/strings/constants'; -import { Collection, CollectionMagicMetadataProps } from 'types/collection'; +import { + Collection, + CollectionMagicMetadataProps, + CollectionSummaries, +} from 'types/collection'; import { CollectionType } from 'constants/collection'; import { getAlbumSiteHost } from 'constants/pages'; import { getUnixTimeInMicroSecondsWithDelta } from 'utils/time'; @@ -189,3 +193,9 @@ export const changeCollectionVisibility = async ( export const getArchivedCollections = (collections: Collection[]) => { return collections.filter(IsArchived).map((collection) => collection.id); }; + +export const hasNonEmptyCollections = ( + collectionSummaries: CollectionSummaries +) => { + return collectionSummaries?.size <= 3; +}; diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index bdadaa8b3..3bb4bccfa 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -202,7 +202,7 @@ export function formatDateTime(date: number | Date) { day: 'numeric', }); const timeFormat = new Intl.DateTimeFormat('en-IN', { - timeStyle: 'medium', + timeStyle: 'short', }); return `${dateTimeFormat.format(date)} ${timeFormat.format(date)}`; } diff --git a/yarn.lock b/yarn.lock index ce35a0fbc..a7ae4e097 100644 --- a/yarn.lock +++ b/yarn.lock @@ -904,6 +904,39 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" +"@date-io/core@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@date-io/core/-/core-2.14.0.tgz#03e9b9b9fc8e4d561c32dd324df0f3ccd967ef14" + integrity sha512-qFN64hiFjmlDHJhu+9xMkdfDG2jLsggNxKXglnekUpXSq8faiqZgtHm2lsHCUuaPDTV6wuXHcCl8J1GQ5wLmPw== + +"@date-io/date-fns@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-2.14.0.tgz#92ab150f488f294c135c873350d154803cebdbea" + integrity sha512-4fJctdVyOd5cKIKGaWUM+s3MUXMuzkZaHuTY15PH70kU1YTMrCoauA7hgQVx9qj0ZEbGrH9VSPYJYnYro7nKiA== + dependencies: + "@date-io/core" "^2.14.0" + +"@date-io/dayjs@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@date-io/dayjs/-/dayjs-2.14.0.tgz#8d4e93e1d473bb5f25210866204dc33384ca4c20" + integrity sha512-4fRvNWaOh7AjvOyJ4h6FYMS7VHLQnIEeAV5ahv6sKYWx+1g1UwYup8h7+gPuoF+sW2hTScxi7PVaba2Jk/U8Og== + dependencies: + "@date-io/core" "^2.14.0" + +"@date-io/luxon@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@date-io/luxon/-/luxon-2.14.0.tgz#cd1641229e00a899625895de3a31e3aaaf66629f" + integrity sha512-KmpBKkQFJ/YwZgVd0T3h+br/O0uL9ZdE7mn903VPAG2ZZncEmaUfUdYKFT7v7GyIKJ4KzCp379CRthEbxevEVg== + dependencies: + "@date-io/core" "^2.14.0" + +"@date-io/moment@^2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-2.14.0.tgz#8300abd6ae8c55d8edee90d118db3cef0b1d4f58" + integrity sha512-VsoLXs94GsZ49ecWuvFbsa081zEv2xxG7d+izJsqGa2L8RPZLlwk27ANh87+SNnOUpp+qy2AoCAf0mx4XXhioA== + dependencies: + "@date-io/core" "^2.14.0" + "@emotion/cache@^11.4.0": version "11.4.0" resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz" @@ -1126,6 +1159,17 @@ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.1.3.tgz#d7636f3046110bcccc63e6acfd100e2ad9ca712a" integrity sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA== +"@mui/utils@^5.4.1": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.8.0.tgz#4b1d19cbcf70773283375e763b7b3552b84cb58f" + integrity sha512-7LgUtCvz78676iC0wpTH7HizMdCrTphhBmRWimIMFrp5Ph6JbDFVuKS1CwYnWWxRyYKL0QzXrDL0lptAU90EXg== + dependencies: + "@babel/runtime" "^7.17.2" + "@types/prop-types" "^15.7.5" + "@types/react-is" "^16.7.1 || ^17.0.0" + prop-types "^15.8.1" + react-is "^17.0.2" + "@mui/utils@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.6.1.tgz#4ab79a21bd481555d9a588f4b18061b3c28ea5db" @@ -1137,6 +1181,22 @@ prop-types "^15.7.2" react-is "^17.0.2" +"@mui/x-date-pickers@^5.0.0-alpha.6": + version "5.0.0-alpha.6" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-5.0.0-alpha.6.tgz#90fdb5730e0411862d7a7edd18af44f03ab8d889" + integrity sha512-2JeagDwwa/V2XPj243cZg5ReZ2553OzukUAfbdxXwj9gGGLeXjBa95NP4kPOBOze4tJq1y/4aYt/aK50aZWElQ== + dependencies: + "@babel/runtime" "^7.17.2" + "@date-io/date-fns" "^2.14.0" + "@date-io/dayjs" "^2.14.0" + "@date-io/luxon" "^2.14.0" + "@date-io/moment" "^2.14.0" + "@mui/utils" "^5.4.1" + clsx "^1.1.1" + prop-types "^15.7.2" + react-transition-group "^4.4.2" + rifm "^0.12.1" + "@next/bundle-analyzer@^9.5.3": version "9.5.5" resolved "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-9.5.5.tgz" @@ -1524,7 +1584,7 @@ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== -"@types/prop-types@^15.7.4": +"@types/prop-types@^15.7.4", "@types/prop-types@^15.7.5": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== @@ -2234,7 +2294,7 @@ cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -classnames@^2.2.6, classnames@^2.3.1: +classnames@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== @@ -2475,7 +2535,7 @@ damerau-levenshtein@^1.0.6: resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz" integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== -date-fns@^2.0.1, date-fns@^2.24.0: +date-fns@^2.0.1: version "2.25.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.25.0.tgz" integrity sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w== @@ -4571,6 +4631,15 @@ prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + property-expr@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz" @@ -4649,18 +4718,6 @@ react-bootstrap@^1.3.0: uncontrollable "^7.2.1" warning "^4.0.3" -react-datepicker@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.3.0.tgz" - integrity sha512-cg+gp4YnPcZc6iZ0+v7VuRgcFG22KL7izHYrCxXeSLOn5VGkyxTfcu5qA/cGIsngurCt/u0NtgVTlHB1Fwap1g== - dependencies: - "@popperjs/core" "^2.9.2" - classnames "^2.2.6" - date-fns "^2.24.0" - prop-types "^15.7.2" - react-onclickoutside "^6.12.0" - react-popper "^2.2.5" - react-dom@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" @@ -4701,7 +4758,7 @@ react-input-autosize@^3.0.0: dependencies: prop-types "^15.5.8" -react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -4716,11 +4773,6 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-onclickoutside@^6.12.0: - version "6.12.0" - resolved "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.0.tgz" - integrity sha512-oPlOTYcISLHfpMog2lUZMFSbqOs4LFcA4+vo7fpfevB5v9Z0D5VBDBkfeO5lv+hpEcGoaGk67braLT+QT+eICA== - react-otp-input@^2.3.1: version "2.4.0" resolved "https://registry.npmjs.org/react-otp-input/-/react-otp-input-2.4.0.tgz" @@ -4958,6 +5010,11 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rifm@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.1.tgz#8fa77f45b7f1cda2a0068787ac821f0593967ac4" + integrity sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg== + rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz"