diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml new file mode 100644 index 000000000..25340ec9f --- /dev/null +++ b/.github/workflows/cla.yaml @@ -0,0 +1,38 @@ +name: "CLA Assistant" +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +jobs: + CLAAssistant: + # This job only runs for pull request comments + if: ${{ github.event.issue.pull_request }} + runs-on: ubuntu-latest + steps: + - name: "CLA Assistant" + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' + # Beta Release + uses: contributor-assistant/github-action@v2.2.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # the below token should have repo scope and must be manually added by you in the repository's secret + PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + with: + path-to-signatures: "signatures/version1/cla.json" + path-to-document: "https://github.com/ente-io/cla/blob/main/CLA.md" # e.g. a CLA or a DCO document + # branch should not be protected + branch: "main" + allowlist: enteio + + # the followings are the optional inputs - If the optional inputs are not given, then default values will be taken + #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) + remote-repository-name: cla + #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' + #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' + #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' + #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' + #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' + #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) + #use-dco-flag: true - If you are using DCO instead of CLA diff --git a/CLA.md b/CLA.md new file mode 100644 index 000000000..71c216537 --- /dev/null +++ b/CLA.md @@ -0,0 +1,98 @@ +## Contributor License Agreement + +Thank you for your contribution to ente projects. + +This contributor license agreement documents the rights granted by contributors +to Ente Technologies, Inc ("ente"). This license is for your protection as a +Contributor as well as the protection of ente, its users, and its licensees; you +may still license your own Contributions under other terms. + +In exchange for the ability to participate in the ente community and for other +good consideration, the receipt of which is hereby acknowledged, you accept and +agree to the following terms and conditions for Your present and future +Contributions submitted to ente. Except for the license granted herein to ente +and recipients of software distributed by ente, You reserve all right, title, +and interest in and to Your Contributions. + +1. Definitions. + + "You" (or "Your") shall mean the copyright owner or legal entity authorized + by the copyright owner that is making this Agreement with ente. For legal + entities, the entity making a Contribution and all other entities that + control, are controlled by, or are under common control with that entity are + considered to be a single Contributor. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the direction or + management of such entity, whether by contract or otherwise, or (ii) + ownership of fifty percent (50%) or more of the outstanding shares, or (iii) + beneficial ownership of such entity. + + "Contribution" shall mean any original work of authorship or invention, + including any modifications or additions to an existing work, that is + intentionally submitted by You to ente for inclusion in, or documentation of, + any of the products owned or managed by ente (the "Work"). For the purposes + of this definition, "submitted" means any form of electronic, verbal, or + written communication sent to ente or its representatives, including but not + limited to communication on electronic mailing lists, source code control + systems, and issue tracking systems that are managed by, or on behalf of, + ente for the purpose of discussing and improving the Work, but excluding + communication that is conspicuously marked or otherwise designated in writing + by You as "Not a Contribution." + +2. Grant of Copyright License. Subject to the terms and conditions of this + Agreement, You hereby grant to ente and to recipients of software distributed + by ente a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable copyright license to reproduce, prepare derivative works of, + publicly display, publicly perform, and distribute Your Contributions and + such derivative works. + +3. Grant of Patent License. Subject to the terms and conditions of this + Agreement, You hereby grant to ente and to recipients of software distributed + by ente a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable (except as stated in this section) patent license to make, have + made, use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable by You that + are necessarily infringed by Your Contribution(s) alone or by combination of + Your Contribution(s) with the Work to which such Contribution(s) was + submitted. If any entity institutes patent litigation against You or any + other entity (including a cross-claim or counterclaim in a lawsuit) alleging + that your Contribution, or the Work to which you have contributed, + constitutes direct or contributory patent infringement, then any patent + licenses granted to that entity under this Agreement for that Contribution or + Work shall terminate as of the date such litigation is filed. + +4. You represent that you are legally entitled to grant the above license. If + your employer(s) has rights to intellectual property that you create that + includes your Contributions, you represent that you have received permission + to make Contributions on behalf of that employer, that your employer has + waived such rights for your contributions to ente, or that your employer has + executed with ente a separate contributor license agreement substantially + similar to this Agreement. If You are a current employee or contractor of + ente, then the terms of your existing Employment Agreement or Consulting + Services Agreement shall supersede this CLA, and remain in full effect. + +5. You represent that each of Your Contributions is Your original creation (see + section 7 for submissions on behalf of others). You represent that Your + Contribution submissions include complete details of any third-party license + or other restriction (including, but not limited to, related patents and + trademarks) of which you are personally aware and which are associated with + any part of Your Contributions. + +6. You are not expected to provide support for Your Contributions, except to the + extent You desire to provide support. You may provide support for free, for + a fee, or not at all. Unless required by applicable law or agreed to in + writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, + without limitation, any warranties or conditions of title, non-infringement, + merchantability, or fitness for a particular purpose. + +7. Should You wish to submit work that is not Your original creation, You may + submit it to ente separately from any Contribution, identifying the complete + details of its source and of any license or other restriction (including, but + not limited to, related patents, trademarks, and license agreements) of which + you are personally aware, and conspicuously marking the work as "Not a + Contribution". Third-party materials licensed pursuant to: [license name(s) + here]" (substituting the bracketed text with the appropriate license + name(s)). + +8. You agree to notify ente of any facts or circumstances of which you become + aware that would make these representations inaccurate in any respect. diff --git a/README.md b/README.md index a13292474..b76c41c55 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,15 @@ We maintain a public roadmap, that's driven by our community @ [roadmap.ente.io] If you like this project, please consider upgrading to a paid subscription. -If you would like to motivate us to keep building, you can do so by -[starring](https://github.com/ente-io/photos-web/stargazers) this project. +And [star this repo](https://github.com/ente-io/photos-web/stargazers)! + +
+ +## 🌍 Translate + +Create a copy of +[src/utils/strings/englishConstants.tsx](src/utils/strings/englishConstants.tsx) +and open a PR.
diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 28584b9a8..34cd52f62 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -203,17 +203,21 @@ const PhotoFrame = ({ h: window.innerHeight, title: item.pubMagicMetadata?.data.caption, }; - if (galleryContext.thumbs.has(item.id)) { - updateFileMsrcProps( - filteredItem, - galleryContext.thumbs.get(item.id) - ); - } - if (galleryContext.files.has(item.id)) { - updateFileSrcProps( - filteredItem, - galleryContext.files.get(item.id) - ); + try { + if (galleryContext.thumbs.has(item.id)) { + updateFileMsrcProps( + filteredItem, + galleryContext.thumbs.get(item.id) + ); + } + if (galleryContext.files.has(item.id)) { + updateFileSrcProps( + filteredItem, + galleryContext.files.get(item.id) + ); + } + } catch (e) { + logError(e, 'PhotoFrame url prefill failed'); } return filteredItem; }); @@ -459,12 +463,12 @@ const PhotoFrame = ({ ) { handleSelect( filteredData[i].id, - filteredData[i].ownerID === user.id + filteredData[i].ownerID === user?.id )(!checked); } handleSelect( filteredData[index].id, - filteredData[index].ownerID === user.id, + filteredData[index].ownerID === user?.id, index )(!checked); } @@ -479,7 +483,10 @@ const PhotoFrame = ({ file={item} updateURL={updateURL(index)} onClick={onThumbnailClick(index)} - onSelect={handleSelect(item.id, item.ownerID === user.id, index)} + selectable={ + !publicCollectionGalleryContext?.accessedThroughSharedURL + } + onSelect={handleSelect(item.id, item.ownerID === user?.id, index)} selected={ selected.collectionID === activeCollection && selected[item.id] } diff --git a/src/components/PhotoList.tsx b/src/components/PhotoList.tsx index 527abe7ee..4c042369c 100644 --- a/src/components/PhotoList.tsx +++ b/src/components/PhotoList.tsx @@ -663,7 +663,11 @@ export function PhotoList({ let sum = 0; for (let i = 0; i < listItem.groups.length - 1; i++) { sum = sum + listItem.groups[i]; - ret.splice(sum, 0,
); + ret.splice( + sum, + 0, +
+ ); sum += 1; } } @@ -687,7 +691,7 @@ export function PhotoList({ width={width} itemCount={timeStampList.length} itemKey={generateKey} - overscanCount={0} + overscanCount={3} useIsScrolling> {({ index, style, isScrolling }) => ( diff --git a/src/components/pages/gallery/PreviewCard.tsx b/src/components/pages/gallery/PreviewCard.tsx index 18b72e8eb..667af3d35 100644 --- a/src/components/pages/gallery/PreviewCard.tsx +++ b/src/components/pages/gallery/PreviewCard.tsx @@ -20,6 +20,7 @@ interface IProps { file: EnteFile; updateURL: (id: number, url: string) => void; onClick: () => void; + selectable: boolean; selected: boolean; onSelect: (checked: boolean) => void; onHover: () => void; @@ -202,6 +203,7 @@ export default function PreviewCard(props: IProps) { file, onClick, updateURL, + selectable, selected, onSelect, selectOnClick, @@ -220,6 +222,12 @@ export default function PreviewCard(props: IProps) { const isMounted = useRef(true); + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + useEffect(() => { if (!file.msrc && !props.showPlaceholder) { const main = async () => { @@ -253,11 +261,8 @@ export default function PreviewCard(props: IProps) { } }; main(); - return () => { - isMounted.current = false; - }; } - }, []); + }, [props.showPlaceholder]); const handleClick = () => { if (selectOnClick) { @@ -290,19 +295,20 @@ export default function PreviewCard(props: IProps) { return ( - e.stopPropagation()} - /> - + {...(selectable ? useLongPress(longPressCallback, 500) : {})}> + {selectable && ( + e.stopPropagation()} + /> + )} {(file?.msrc || imgSrc) && } {file?.metadata.fileType === 1 && } diff --git a/src/services/fileService.ts b/src/services/fileService.ts index f9be6b1b0..eba0cec1f 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -59,7 +59,7 @@ export const syncFiles = async ( let files = await removeDeletedCollectionFiles(collections, localFiles); if (files.length !== localFiles.length) { await setLocalFiles(files); - setFiles(files); + setFiles(sortFiles(mergeMetadata(files))); } for (const collection of collections) { if (!getToken()) { diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 145dcf727..3ffddf36d 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -166,19 +166,14 @@ export function getSelectedFiles( } export function sortFiles(files: EnteFile[]) { - // sort according to modification time first - // then sort according to creation time, maintaining ordering according to modification time for files with creation time - return files - .sort( - (a, b) => b.metadata.modificationTime - a.metadata.modificationTime - ) - .sort((a, b) => { - if (a.metadata.modificationTime !== b.metadata.modificationTime) { - return 0; - } else { - return b.metadata.creationTime - a.metadata.creationTime; - } - }); + // sort based on the time of creation time of the file, + // for files with same creation time, sort based on the time of last modification + return files.sort((a, b) => { + if (a.metadata.creationTime === b.metadata.creationTime) { + return b.metadata.modificationTime - a.metadata.modificationTime; + } + return b.metadata.creationTime - a.metadata.creationTime; + }); } export async function decryptFile( diff --git a/src/utils/photoFrame/index.ts b/src/utils/photoFrame/index.ts index 01ac352ca..b54db60b3 100644 --- a/src/utils/photoFrame/index.ts +++ b/src/utils/photoFrame/index.ts @@ -1,6 +1,7 @@ import { FILE_TYPE } from 'constants/file'; import { EnteFile } from 'types/file'; import { MergedSourceURL } from 'types/gallery'; +import { logError } from 'utils/sentry'; import constants from 'utils/strings/constants'; const WAIT_FOR_VIDEO_PLAYBACK = 1 * 1000; @@ -61,7 +62,11 @@ export function updateFileMsrcProps(file: EnteFile, url: string) { } else if (file.metadata.fileType === FILE_TYPE.IMAGE) { file.src = url; } else { - throw Error('Invalid file type'); + logError( + Error(`unknown file type - ${file.metadata.fileType}`), + 'Unknown file type' + ); + file.src = url; } } @@ -77,15 +82,18 @@ export async function updateFileSrcProps( let originalVideoURL; let convertedImageURL; let convertedVideoURL; + let originalURL; if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) { [originalImageURL, originalVideoURL] = urls.original; [convertedImageURL, convertedVideoURL] = urls.converted; } else if (file.metadata.fileType === FILE_TYPE.VIDEO) { [originalVideoURL] = urls.original; [convertedVideoURL] = urls.converted; - } else { + } else if (file.metadata.fileType === FILE_TYPE.IMAGE) { [originalImageURL] = urls.original; [convertedImageURL] = urls.converted; + } else { + [originalURL] = urls.original; } const isPlayable = @@ -141,6 +149,10 @@ export async function updateFileSrcProps( } else if (file.metadata.fileType === FILE_TYPE.IMAGE) { file.src = convertedImageURL; } else { - throw Error('Invalid file type'); + logError( + Error(`unknown file type - ${file.metadata.fileType}`), + 'Unknown file type' + ); + file.src = originalURL; } }