From 79e45496684594919e61f136cff1d3e8481bffe9 Mon Sep 17 00:00:00 2001 From: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:13:52 +0200 Subject: [PATCH] Merge origin/dev 2021-11-25 (#1780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Release latest to Stage (#988) * Add French translation and set the language selection (#978) * should be almost set * dropdown styling * done with translation * nits here and there * Apply suggestions from code review Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * missing translations * add locale with dayjs * lingui extract Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: GitHub Actions * Fix design nits (#987) * setup warning * colors * icon * buttons * dropdown menu 14 * images * nits * title too big and switch buttons order * Apply suggestions from code review Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * update link closes #849 Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Michael Yankelev Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions * fix link (#1025) * create buckets for new users * remove depraecated files call * [Storage] Copy cid functionality (#1437) * Copy features added, overflow issue still present * Swapped to button * lingui extract * Updated button * lingui extract * Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Fixed size * lingui extract * minmax * lingui extract * revert unrelated changes * lingui extract * Apply suggestions from code review Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan * [STORAGE] Rename file in bucket (#1471) * formik submit not firing * fix mobile * lingui extract * Update packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * make eslint :) Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * 10min timeout for everyone (#1472) * Translated using Weblate (French) (#1478) Currently translated at 100.0% (262 of 262 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * add ui tests for search (#1479) * bump cypress to v8.3.1 (#1480) * Sharing feature explainer (#1477) * refactor modal * steps logic * image centered * lingui extract * lingui extract * typo * refactor with hook, change icon, ad to share menu * nits * fix storage building Co-authored-by: GitHub Actions * Adding multi selects on mobile view with long press (#1470) * using long press * dragg preview problems * typos * events almost ready * resets * long press options * long press updates * formats * dark mode colors * added hover with breakpoints * lingui extract * Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * curly spacing * borders * clicks proper placement * reverted linting * removed preview on mobile Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * bump to 1.18.5 (#1493) * [Files] - New survey - Discord link and tiny fix (#1487) * a nice mix of things * lingui extract Co-authored-by: GitHub Actions * Translations update from Weblate (#1496) * Translated using Weblate (French) Currently translated at 100.0% (266 of 266 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (German) Currently translated at 73.3% (195 of 266 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/de/ Co-authored-by: J. Lavoie * update axios (#1498) * Added docs link (#1497) * Added doc link * lingui extract Co-authored-by: GitHub Actions * upgrade (#1501) * Published (#1503) Co-authored-by: Tanmoy Basak Anjan * Add ui tests for file preview (#1489) * Toasts refactor (#1495) * Toast content ready * toast animations * toasts before relative positioning * component ready, start integration * normal notifications ready * downloads ready * toasts ready * toasts inn storage * Toasts refactor okk * lingui extract * minor changes * Update packages/common-components/src/index.ts Co-authored-by: Ryan Noble * translates * post merge updates * translates update * progress and overrides * updates in lock file * updates ready * lingui extract * removing dark theme from storage and gaming * Update packages/common-components/src/Toasts/types.ts Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * toasts types * missed type updates * update element identifier for toast * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContent.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * download error messages * Update packages/common-components/src/Toasts/types.ts Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * lingui extract * Update packages/files-ui/src/App.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * storybooks update * using usref (#1513) Co-authored-by: GitHub Actions Co-authored-by: Ryan Noble Co-authored-by: Michael Yankelev Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Andrew Snaith Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Progress cancellations (#1500) * Toast content ready * toast animations * toasts before relative positioning * component ready, start integration * normal notifications ready * downloads ready * toasts ready * toasts inn storage * Toasts refactor okk * lingui extract * minor changes * Update packages/common-components/src/index.ts Co-authored-by: Ryan Noble * translates * post merge updates * translates update * progress and overrides * updates in lock file * updates ready * lingui extract * removing dark theme from storage and gaming * Update packages/common-components/src/Toasts/types.ts Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * toasts types * missed type updates * uploads ready * uploads ready * lingui extract * update element identifier for toast * cancel source tokens * toasts added * Update packages/common-components/src/stories/Toasts.stories.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContent.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/common-components/src/Toasts/ToastContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * download error messages * Update packages/common-components/src/Toasts/types.ts Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * lingui extract * resolved errors and lints * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * added closabble states * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Ryan Noble Co-authored-by: Michael Yankelev Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Andrew Snaith Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * survey banner on safari fix (#1512) * banner fix * Update packages/files-ui/src/Components/SurveyBanner.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * fix what I broke (#1518) Co-authored-by: Tanmoy Basak Anjan * Change the survey to a call for a user interview (#1517) * change fore a user interview call * lingui extract * Apply suggestions from code review Co-authored-by: Andrew Snaith Co-authored-by: GitHub Actions Co-authored-by: Andrew Snaith * bump cypress to v8.4 (#1521) * Translations update from Weblate (#1522) * Translated using Weblate (French) Currently translated at 100.0% (271 of 271 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (German) Currently translated at 71.9% (195 of 271 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/de/ Co-authored-by: J. Lavoie * Adjusting login screen sizes. (#1519) * login screen sizes * check for instanceof Error Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Thibaut Sardan * Gaming Dashboard overhaul (#1504) * Updating the dashboard api * Reflected functionality * lingui extract * CS favicon * Update packages/gaming-ui/src/Components/Modules/ApiKeys.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update packages/gaming-ui/src/Components/Pages/LoginPage.tsx Co-authored-by: Tanmoy Basak Anjan Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * bulk recover operation (#1526) * recovers working * removed consoles * Add new ui tests for illogical folder and file move errors (#1520) * Add tests for illogical folder move attempts * Add new check to spec file * Finalize changes to spec file * Removed string checking following PR feedback Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Posthog Analytics Integration (#1514) * added posthog integration * Banner logic working correctly * handle uninitialized posthog * fix lint * extract messages * lingui extract * resolve warning Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Michael Yankelev Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Translated using Weblate (French) (#1528) Currently translated at 100.0% (272 of 272 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * [Files] - Share to home or other shared folder (#1527) * share to home or other shared folder * small changes for a better UX * lingui extract * lingui extract * Update packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx Co-authored-by: Tanmoy Basak Anjan * fix console warnings Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * Folder uploads (#1515) * add basic folder upload * wire up folder uploads for modal * Rename Modal * fix types * remove types * lingui extract * fix lint * add source attribution * lingui extract * incorporate feedback * lingui extract * fix lint * Fix casing * Rename component for consistency * lingui extract * clean up types Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Ryan Noble * Translated using Weblate (French) (#1532) Currently translated at 100.0% (276 of 276 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * Don't track before opt-in (#1531) * dont track users before they opt-in * nits and remove log, calls are still fires * default to not track * lingui extract * with init check * Update packages/files-ui/src/Contexts/PosthogContext.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * [Files] - Show current bucket decryption key (#1534) * show current bucket decryption key * lingui extract Co-authored-by: GitHub Actions * [Files] - Get admin public key from the api (#1485) * bump * use api client * merge dev * lingui extract * support several keys * wih scroll because pub/decryption key are long Co-authored-by: GitHub Actions * Add lint rules for test debug commands (#1535) * prevent double opening (#1538) * Add test coverage for storage summary adjustments (#1536) * Translated using Weblate (French) (#1540) Currently translated at 100.0% (278 of 278 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * Better colors for dark mode select and tagsinput components (#1550) * better colors for dark mode * lingui extract * fix typo Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: GitHub Actions * Filter out current bucket (#1548) * filter out current bucket * Update packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx * fix color (#1555) * Fix user lookup spamming the api, and throwing (#1553) * oh yeaah * lingui extract * lingui extract * Apply suggestions from code review * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan * Upgrade Torus dependencies (#1549) * [wip] update dependencies * Revert "[wip] update dependencies" This reverts commit 04b593bbbe5f8c4c5d6e220c744aae88c2cd95e4. * update torus dependencies * fix storage test Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * reset fields for shared folders (#1577) * reset forms and fix a couple error types (#1578) * Translations update from Weblate (#1557) * Translated using Weblate (French) Currently translated at 100.0% (281 of 281 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (German) Currently translated at 69.7% (196 of 281 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/de/ Co-authored-by: J. Lavoie * hide when ther's no menu item (#1576) * Add "maintenance" label to weblate PRs automatically (#1581) * Upgrade api client and fix issue with shared folder renaming (#1582) * upgrade api client and fix issue * Update packages/files-ui/src/Contexts/FilesContext.tsx * lingui extract Co-authored-by: GitHub Actions * run lint --fix (#1585) * Gaming dashboard - API keys as cards (#1575) * Dashboard cards done * lingui extract * extra padding * Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * lingui extract * Feedback * Removed link * Apply suggestions from code review * Made warning bigger * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * New navigations buttons (#1574) * wip * lingui extract * center and add posthog * Update .eslintrc.json * Add team feature fake door (#1587) * wip * modal and tracking * use data-posthog instead Co-authored-by: GitHub Actions * fix tertiary buttons (#1592) * Posthog identify user on login (#1590) * identify user on login * fix effect deps * show username if known (#1597) * remove fade effect (#1598) * Translations update from Weblate (#1599) * Translated using Weblate (French) Currently translated at 100.0% (285 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (German) Currently translated at 71.2% (203 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/de/ * Update packages/files-ui/src/locales/fr/messages.po Co-authored-by: J. Lavoie Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Mv and rm batch calls (#1596) * wire up all api calls * lingui extract * fix lint * move to helper * fix linting * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan * refresh buckets after deletion (#1604) * Toast messages update (#1602) * messages updated * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Translations update from Weblate (#1600) * Translated using Weblate (French) Currently translated at 100.0% (285 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 41.4% (118 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/nb_NO/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 41.4% (118 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/nb_NO/ * Translated using Weblate (German) Currently translated at 71.2% (203 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/de/ * Translated using Weblate (Spanish) Currently translated at 56.1% (160 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/es/ Co-authored-by: J. Lavoie Co-authored-by: Allan Nordhøy * Translated using Weblate (Norwegian Bokmål) (#1607) Currently translated at 41.4% (118 of 285 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/nb_NO/ * Fix move single file and test (#1608) * fix move single file and test * remove only * lingui extract Co-authored-by: GitHub Actions * bump cypress to v8.5 (#1609) * Migrate to API local store cache (#1591) * Updating for local store manager * lingui extract * Wired up store * lingui extract * Localstore for Files wired up * lingui extract * lingui extract * Removed done * Update packages/files-ui/src/Contexts/UserContext.tsx Co-authored-by: Tanmoy Basak Anjan Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * webkit color added (#1612) * added resolve browser (#1613) Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Detect and dismiss toasts in ui tests (#1610) * bump cypress to v8.5 * define necessary toast identifiers * redefine toasts as separate test objects * update spec file with new toast interaction, detect and dismiss * make toast close button identifiers unique * Add more checks for toasts for greater reliability Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * update tests for unsupported preview * Transfer multiple files and folders (#1606) * initial restructure * folder transfers * sharing working as expected * sharing is ready * toasts and error handling * error messages * transfer progress ready * lingui extract * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * using reduce and handling each file share error * updated terms * lingui extract * added no files check * lingui extract * share messages * lingui extract * lint Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Translations update from Weblate (#1625) * Translated using Weblate (French) Currently translated at 100.0% (282 of 282 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (French) Currently translated at 100.0% (294 of 294 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * bump cypress to v8.6 (#1628) * bump cypress to v8.6 * lingui extract * Update test Co-authored-by: GitHub Actions * Update api error handling (#1626) * implement error handling changes * whoops missed one * lingui extract * lingui extract * resolve linting * fix TS error * lingui extract * lingui extract * fix package version * fix package versions * update create folder modal error * lingui extract * lingui extract * revert change from preview branch * lingui extract * make error handling safer * update comment for consistency * more safety added * fix linting Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Hide network requests from cypress test runner (#1643) * hide requests from the test runner window * add extra steps for test reliability Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * [Files] Fix survey banner and sharing explainer (#1634) * fix survey banner and sharing explainer * fix case where localstore is empty * add init for the 404 * Translated using Weblate (French) (#1645) Currently translated at 100.0% (289 of 289 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * Fix Webpack Build with node 17 (#1651) * Fix build for node 17 * lingui extract Co-authored-by: GitHub Actions * add ui test coverage for the survey banner (#1648) * Add Support for heic images (#1618) * add support for heic * Update packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * fix lint, useMemo * use thenable instead of async/await * fix preview tests * rename variable * remove not null check in test * resolve test issue, add loader * fix lint * update image preview test * use jpg instead of png * lingui extract * update image preview markup * fix lint * lingui extract * fix revocation * fix wrong image * remove unused ref * fix lint * Update packages/files-ui/src/Components/Modules/PreviewRenderers/ImagePreview.tsx Co-authored-by: Tanmoy Basak Anjan * Update packages/files-ui/src/Components/Modules/PreviewRenderers/ImagePreview.tsx * resolve openssl issue * update remaining build commands Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Andrew Snaith Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan * Menu dark mode colors (#1650) * colors changed * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * [EPIC] Link Sharing (#1632) * Create a link sharing from a shared folder manage access (#1619) * wip * add jose and some any on error * with jsrsasign * convert raw to PEM * valid JWT * wrap up link overview * lingui extract * Apply suggestions from code review * generic link * git add fix shared folder creation * Apply suggestions from code review Co-authored-by: Tanmoy Basak Anjan * no more hack :D * Link-sharing landing page (#1620) * link-sharing route and verification * login message * nicer messages * nits * onBrowseBucket * Apply suggestions from code review Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * fix comments * try-catch * cleanup * Update packages/files-ui/src/Components/Modules/LinkSharingModule.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * nit color * Update packages/files-ui/src/Utils/pathUtils.ts Co-authored-by: Ryan Noble Co-authored-by: Michael Yankelev Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Ryan Noble * trans * Merge dev to epic/link sharing (#1633) * update tests for unsupported preview * Transfer multiple files and folders (#1606) * initial restructure * folder transfers * sharing working as expected * sharing is ready * toasts and error handling * error messages * transfer progress ready * lingui extract * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * using reduce and handling each file share error * updated terms * lingui extract * added no files check * lingui extract * share messages * lingui extract * lint Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Translations update from Weblate (#1625) * Translated using Weblate (French) Currently translated at 100.0% (282 of 282 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (French) Currently translated at 100.0% (294 of 294 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * bump cypress to v8.6 (#1628) * bump cypress to v8.6 * lingui extract * Update test Co-authored-by: GitHub Actions * trans Co-authored-by: Michael Yankelev Co-authored-by: Tanmoy Basak Anjan Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Weblate (bot) Co-authored-by: J. Lavoie Co-authored-by: Andrew Snaith * Merge dev to epic/lin-sharing (#1653) * update tests for unsupported preview * Transfer multiple files and folders (#1606) * initial restructure * folder transfers * sharing working as expected * sharing is ready * toasts and error handling * error messages * transfer progress ready * lingui extract * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * using reduce and handling each file share error * updated terms * lingui extract * added no files check * lingui extract * share messages * lingui extract * lint Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Translations update from Weblate (#1625) * Translated using Weblate (French) Currently translated at 100.0% (282 of 282 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (French) Currently translated at 100.0% (294 of 294 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * bump cypress to v8.6 (#1628) * bump cypress to v8.6 * lingui extract * Update test Co-authored-by: GitHub Actions * trans * Update api error handling (#1626) * implement error handling changes * whoops missed one * lingui extract * lingui extract * resolve linting * fix TS error * lingui extract * lingui extract * fix package version * fix package versions * update create folder modal error * lingui extract * lingui extract * revert change from preview branch * lingui extract * make error handling safer * update comment for consistency * more safety added * fix linting Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Hide network requests from cypress test runner (#1643) * hide requests from the test runner window * add extra steps for test reliability Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * [Files] Fix survey banner and sharing explainer (#1634) * fix survey banner and sharing explainer * fix case where localstore is empty * add init for the 404 * Translated using Weblate (French) (#1645) Currently translated at 100.0% (289 of 289 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * Fix Webpack Build with node 17 (#1651) * Fix build for node 17 * lingui extract Co-authored-by: GitHub Actions * add ui test coverage for the survey banner (#1648) * Add Support for heic images (#1618) * add support for heic * Update packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * fix lint, useMemo * use thenable instead of async/await * fix preview tests * rename variable * remove not null check in test * resolve test issue, add loader * fix lint * update image preview test * use jpg instead of png * lingui extract * update image preview markup * fix lint * lingui extract * fix revocation * fix wrong image * remove unused ref * fix lint * Update packages/files-ui/src/Components/Modules/PreviewRenderers/ImagePreview.tsx Co-authored-by: Tanmoy Basak Anjan * Update packages/files-ui/src/Components/Modules/PreviewRenderers/ImagePreview.tsx * resolve openssl issue * update remaining build commands Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Andrew Snaith Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Michael Yankelev Co-authored-by: Tanmoy Basak Anjan Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Weblate (bot) Co-authored-by: J. Lavoie Co-authored-by: Andrew Snaith Co-authored-by: Andrew Snaith * Apply suggestions from code review Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Michael Yankelev Co-authored-by: GitHub Actions Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Ryan Noble Co-authored-by: Weblate (bot) Co-authored-by: J. Lavoie Co-authored-by: Andrew Snaith Co-authored-by: Andrew Snaith * Translated using Weblate (French) (#1661) Currently translated at 100.0% (299 of 299 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * add ui test coverage for the sharing explainer (#1659) * update nav identifiers * add header button identifiers * add identifiers for sharing explainer * fix mistake in identifiers * add spec file for sharing explainer tests * Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Update test spec after pr feedback * lingui extract Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions * Fix toast type when no files successfully shared (#1662) * fix toast type * fix lint * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Andrew Snaith * Update maintenance mode message (#1669) * Files changes * update Files markup * update storage login screen * lingui extract * lingui extract * fix lint * Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * removed unnecessary copy * lingui extract * lingui extract * update examples Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Make intercepts in tests less version specific (#1658) * [Files] Fix home page and nav menu display issues on smaller screens (#1677) * remove absolute positioning * fix button connection * nav bar * Update AppNav.tsx (#1689) * Translations update from Weblate (#1686) * Translated using Weblate (French) Currently translated at 100.0% (300 of 300 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Apply suggestions from code review Co-authored-by: J. Lavoie Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * mobile search (#1676) Co-authored-by: Tanmoy Basak Anjan * [Files] Verfy a sharing link asap and give nice error (#1674) * ugly redirect * better UX * lingui extract * consistancy * hide loader when link is invalid * typo and icon color * add button to go back * lingui extract * nice error with malformed jwt * lingui extract * redirect to / Co-authored-by: GitHub Actions * Add basic file sharing ui tests (#1691) * add new identifiers and test objects for sharing feature * Add sharing spec file * minor comment change * move data to test fixture * add clearShareBucket to cypress commands * add new element identifiers * update sharing spec to use key from fixture * file sharing spec changes (wip) * lingui extract * add a deleteSharedBucket helper and force refresh buckets for each load * finalize changes * Update file-sharing-spec.ts * move username into test fixture Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * Show a loader for shared folder, and an error if the user doesn't have access to it (#1693) * show loading and error when loading a shared folder * lingui extract * Update packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx Co-authored-by: GitHub Actions * Translated using Weblate (French) (#1697) Currently translated at 100.0% (305 of 305 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * Improve reliability of file moving test (#1703) * add checks to dismiss the move success toast * lingui extract * add a function that waits for the bucket refresh to complete Co-authored-by: GitHub Actions * add new workflow for ui tests on demand (#1702) * add new workflow for ui tests on demand * amend slack webhook * Fix Safari Nav Items invisible (#1709) * revert app nav changes * update overflow settings * fix lint * removed minWidth * fix lint * Tweak config of slack notifier (#1710) * Added scroll fi (#1711) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Translated using Weblate (French) (#1714) Currently translated at 100.0% (306 of 306 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * second section and modal scroll * cleanup * Update packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrEditSharedFolderModal.tsx * better mobile folder creation * better mobile view * lingui extract * fix cancel download, content not clearing, search preview content type * fix line lengths * lingui extract * fix indentation * CID cutoff fix (#1713) * width cid cutoff * lingui extract * dynamic ellipsis Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions * dialog * Header buttons and icons alignments (#1712) * word wraps added * lingui extract * shared folder button icon * new folder text * lingui extract Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions * Update packages/files-ui/src/Contexts/FilesContext.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * overflow hidden for any modal * Trim email nonce submition (#1723) * trim email code * gaming and storage as well Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * update package * fix lookup call params * wire up new lookup and toggle flag * Adding dynamic positioning to menu drop down (#1719) * Added dynamic feature * Directional awareness done * Update packages/common-components/src/Scroll/useOnScroll.hook.tsx Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * fix close (#1735) * update styling * add translation * lingui extract * fix lint * Added blockers (#1730) Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Changed mobile click sensitivity (#1705) * Altered click events * lingui extract * Trying drag mutex * Changed logic * Fixed * Rest * getting position * testing new implementation * long press from library * sensitivity readdy * table props removed * removed consoles * threshold const * removed packages Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> * update label * lingui extract * resolve linting errors * Disabled auto focus safari (#1732) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Andrew Snaith * Preview Hotkeys work consistently (#1740) * Add dependencies to hook * fix lint * Fix Preview loading if interrupted (#1738) * ensure no error is returned * ensure download remains active * remove debugger * lint * add template for pull requests (#1751) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Bump ssri from 6.0.1 to 6.0.2 (#1747) Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/npm/ssri/releases) - [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md) - [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2) --- updated-dependencies: - dependency-name: ssri dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Bump merge-deep from 3.0.2 to 3.0.3 (#1744) Bumps [merge-deep](https://github.com/jonschlinkert/merge-deep) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/jonschlinkert/merge-deep/releases) - [Commits](https://github.com/jonschlinkert/merge-deep/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: merge-deep dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Bump dns-packet from 1.3.1 to 1.3.4 (#1745) Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4. - [Release notes](https://github.com/mafintosh/dns-packet/releases) - [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md) - [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4) --- updated-dependencies: - dependency-name: dns-packet dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump tmpl from 1.0.4 to 1.0.5 (#1741) Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/daaku/nodejs-tmpl/releases) - [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5) --- updated-dependencies: - dependency-name: tmpl dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump path-parse from 1.0.6 to 1.0.7 (#1742) Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7. - [Release notes](https://github.com/jbgutierrez/path-parse/releases) - [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7) --- updated-dependencies: - dependency-name: path-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump color-string from 1.5.3 to 1.6.0 (#1743) Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.3 to 1.6.0. - [Release notes](https://github.com/Qix-/color-string/releases) - [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md) - [Commits](https://github.com/Qix-/color-string/commits/1.6.0) --- updated-dependencies: - dependency-name: color-string dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Bump hosted-git-info from 2.8.8 to 2.8.9 (#1746) Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9) --- updated-dependencies: - dependency-name: hosted-git-info dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * bump cypress to v9.0 (#1749) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Bump url-parse from 1.4.7 to 1.5.3 (#1748) Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.3. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.3) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Link sharing redesign (#1722) * redesign * copy buttons * lingui extract * dynamic dropdown * copy working * remove label * styling suggestions * lingui extract Co-authored-by: GitHub Actions Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * Resolved (#1731) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan * Add subdirectory for pr template (#1756) * add test for hotkey navigation in file preview (#1755) * fix video preview overflow (#1752) * Share files from browser support (#1736) * redesign * copy buttons * lingui extract * dynamic dropdown * copy working * remove label * styling suggestions * lingui extract * landing modal alignments * lingui extract * modal config * lingui extract * manage shared folder code refactor * lingui extract * modals progress working * added posthog event * lingui extract * changing widths * added width breaks * removed extra styled * compile translations * added status checks * close instead of cancel Co-authored-by: Thibaut Sardan Co-authored-by: GitHub Actions Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Revert "copy working" (#1759) This reverts commit 424f4278a558c64d6d9317693aa40a4ee477dd03. Co-authored-by: Tanmoy Basak Anjan * Translations update from Hosted Weblate (#1753) * Translated using Weblate (French) Currently translated at 100.0% (304 of 304 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (French) Currently translated at 100.0% (310 of 310 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ * Translated using Weblate (French) Currently translated at 100.0% (317 of 317 strings) Translation: ChainSafe Files/Chainsafe Files user interface Translate-URL: https://hosted.weblate.org/projects/chainsafe-files/chainsafe-files-user-interface/fr/ Co-authored-by: J. Lavoie * [ImgBot] Optimize images (#1767) *Total -- 66.99kb -> 35.67kb (46.75%) /packages/files-ui/cypress/fixtures/uploadedFiles/chainsafe.png -- 38.39kb -> 14.61kb (61.94%) /packages/files-ui/src/Media/sharingExplainer/step2.png -- 9.24kb -> 4.60kb (50.19%) /packages/files-ui/src/Media/sharingExplainer/step1.png -- 5.66kb -> 3.65kb (35.47%) /packages/common-components/src/Icons/svgs/ethereum-logo.svg -- 0.78kb -> 0.62kb (20.88%) /packages/files-ui/src/Media/sharingExplainer/step3.png -- 12.38kb -> 11.66kb (5.87%) /packages/common-components/src/Icons/svgs/document.svg -- 0.54kb -> 0.53kb (0.55%) Signed-off-by: ImgBotApp Co-authored-by: ImgBotApp Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * Report modal styles and cut off fix (#1766) * report modal fix * removed copy Co-authored-by: Ryan Noble * Fix Mobile Nav Drawer blocks breadcrumb (#1768) * secondary colors update (#1769) Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * File info modal CID and Key copy arrangements (#1773) * adding flags * info modal copy buttons ready * lingui extract Co-authored-by: GitHub Actions * fix share transfer modal alignment (#1774) * fix alignment * lint :) Co-authored-by: Ryan Noble * move pr template to .github root dir (#1776) Co-authored-by: Ryan Noble Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * UI fix (#1777) * resolve double tap (#1778) Co-authored-by: Ryan Noble * Mobile sharing folder rename modal (#1779) * Done# * Update packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> * lingui extract Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan Co-authored-by: GitHub Actions * update translations * fix lint * Rename with split extension (#1760) * Filesplitting done * Desktop done * Mobile done * Yolo files * Fixed rename for folder * Removing end of string * Apply suggestions from code review * fix lint Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Co-authored-by: Michael Yankelev Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: GitHub Actions Co-authored-by: Shiva <82167447+RamidiShiva@users.noreply.github.com> Co-authored-by: Cindy Chau <47398578+sweetpea22@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan Co-authored-by: Andrew Snaith Co-authored-by: Thibaut Sardan Co-authored-by: Priom Chowdhury Co-authored-by: Ryan Noble Co-authored-by: Weblate (bot) Co-authored-by: J. Lavoie Co-authored-by: Andrew Snaith Co-authored-by: Weblate (bot) Co-authored-by: Allan Nordhøy Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: imgbot[bot] <31301654+imgbot[bot]@users.noreply.github.com> Co-authored-by: ImgBotApp --- .github/pull_request_template.md | 19 + .github/workflows/test-files-on-demand.yml | 62 +++ .../common-components/src/Button/Button.tsx | 18 +- .../src/CheckboxInput/CheckboxInput.tsx | 1 + .../common-components/src/Dialog/Dialog.tsx | 3 - .../src/Icons/svgs/document.svg | 4 +- .../src/Icons/svgs/ethereum-logo.svg | 14 +- .../src/MenuDropdown/MenuDropdown.tsx | 68 ++- .../common-components/src/Modal/Modal.tsx | 132 ++++-- .../common-components/src/Scroll/index.ts | 1 + .../src/Scroll/useOnScroll.hook.tsx | 35 ++ .../src/SelectInput/SelectInput.tsx | 1 - .../common-components/src/Table/TableCell.tsx | 1 + .../src/stories/Button.stories.tsx | 4 +- .../common-theme/src/Defaults/ThemeConfig.ts | 1 - .../common-theme/src/Hooks/useLongPress.tsx | 71 ++- packages/common-theme/src/Overrides/Button.ts | 12 + .../src/Provider/ThemeSwitcherContext.tsx | 6 + .../cypress/fixtures/filesTestData.ts | 6 + .../fixtures/uploadedFiles/chainsafe.png | Bin 0 -> 14960 bytes packages/files-ui/cypress/support/commands.ts | 13 +- .../cypress/support/page-objects/basePage.ts | 8 +- .../cypress/support/page-objects/homePage.ts | 3 +- .../modals/createSharedFolderModal.ts | 11 + .../modals/deleteSharedFolderModal.ts | 5 + .../support/page-objects/sharedPage.ts | 19 + .../cypress/support/utils/apiTestHelper.ts | 38 +- .../cypress/tests/file-management-spec.ts | 6 +- .../cypress/tests/file-preview-spec.ts | 74 ++++ .../cypress/tests/file-sharing-spec.ts | 63 +++ .../cypress/tests/sharing-explainer-spec.ts | 9 +- packages/files-ui/package.json | 4 +- packages/files-ui/public/index.html | 2 +- .../src/Components/Elements/CustomModal.tsx | 6 +- .../src/Components/Elements/MnemonicForm.tsx | 6 +- .../Elements/NotificationsDropdown.tsx | 1 + .../Elements/ShareTransferRequestModal.tsx | 2 + .../src/Components/Elements/SharedUsers.tsx | 7 +- .../src/Components/Elements/UserBubble.tsx | 8 +- .../src/Components/Layouts/AppNav.tsx | 350 ++++++++------- .../FileBrowsers/CreateFolderModal.tsx | 6 +- .../CreateOrEditSharedFolderModal.tsx | 56 ++- .../CreateOrManageSharedFolder.tsx | 309 +++++++++++++ .../CreateOrManageSharedFolderModal.tsx | 76 ++++ .../Modules/FileBrowsers/FileInfoModal.tsx | 140 +++--- .../FileBrowsers/LinkSharing/LinkList.tsx | 253 ++++++++--- .../FileBrowsers/LinkSharing/SharingLink.tsx | 190 ++++++-- .../Modules/FileBrowsers/MoveFileModal.tsx | 4 +- .../Modules/FileBrowsers/ReportFileModal.tsx | 69 ++- .../FileBrowsers/SearchFileBrowser.tsx | 4 +- .../Modules/FileBrowsers/ShareModal.tsx | 414 ++++++++---------- .../FileBrowsers/SharedFoldersOverview.tsx | 40 +- .../Modules/FileBrowsers/hooks/useGetFile.tsx | 4 +- .../FileBrowsers/hooks/useLookupUser.tsx | 12 +- .../hooks/useSharingExplainerModalFlag.tsx | 1 - .../FileSystemItem/FileSystemGridItem.tsx | 54 ++- .../views/FileSystemItem/FileSystemItem.tsx | 96 +++- .../FileSystemItem/FileSystemTableItem.tsx | 53 ++- .../views/FileSystemItem/SharedFolderRow.tsx | 214 ++++++--- .../Modules/FileBrowsers/views/FilesList.tsx | 20 +- .../Components/Modules/FilePreviewModal.tsx | 12 +- .../Modules/LoginModule/PasswordlessEmail.tsx | 8 +- .../Modules/PreviewRenderers/VideoPreview.tsx | 5 +- .../Modules/Settings/ProfileTab/index.tsx | 177 ++++---- .../src/Components/SharingExplainerModal.tsx | 4 - .../files-ui/src/Contexts/FilesContext.tsx | 26 +- .../files-ui/src/Contexts/PosthogContext.tsx | 13 +- .../files-ui/src/Contexts/UserContext.tsx | 47 +- packages/files-ui/src/locales/de/messages.po | 66 +-- packages/files-ui/src/locales/en/messages.po | 74 ++-- packages/files-ui/src/locales/es/messages.po | 66 +-- packages/files-ui/src/locales/fr/messages.po | 98 +++-- packages/files-ui/src/locales/no/messages.po | 66 +-- packages/gaming-ui/package.json | 4 +- .../gaming-ui/src/Components/GamingRoutes.tsx | 6 - .../Modules/LoginModule/PasswordlessEmail.tsx | 8 +- packages/storage-ui/package.json | 4 +- .../src/Components/Modules/LoginModule.tsx | 2 +- .../Modules/LoginModule/PasswordlessEmail.tsx | 8 +- yarn.lock | 77 ++-- 80 files changed, 2689 insertions(+), 1191 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/test-files-on-demand.yml create mode 100644 packages/common-components/src/Scroll/useOnScroll.hook.tsx create mode 100644 packages/files-ui/cypress/fixtures/uploadedFiles/chainsafe.png create mode 100644 packages/files-ui/cypress/support/page-objects/modals/createSharedFolderModal.ts create mode 100644 packages/files-ui/cypress/support/page-objects/modals/deleteSharedFolderModal.ts create mode 100644 packages/files-ui/cypress/support/page-objects/sharedPage.ts create mode 100644 packages/files-ui/cypress/tests/file-sharing-spec.ts create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolder.tsx create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolderModal.tsx diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..1f16343693 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +closes # + +--- + +Submission checklist: + +> Remove anything below that is not applicable + +- [x] Functionality +- - [x] Feature works as intended after change +- - [x] Applicable dependancies have been deployed + +- [x] Layout +- - [x] Change looks good in the desktop web ui +- - [x] Change looks good in the mobile web ui + +- [x] Theme +- - [x] Components / elements inspected in light mode +- - [x] Components / elements inspected in dark mode \ No newline at end of file diff --git a/.github/workflows/test-files-on-demand.yml b/.github/workflows/test-files-on-demand.yml new file mode 100644 index 0000000000..15b3349453 --- /dev/null +++ b/.github/workflows/test-files-on-demand.yml @@ -0,0 +1,62 @@ +name: Cypress tests - Files (On Demand) +on: [workflow_dispatch] +jobs: + cypress-run: + runs-on: ubuntu-latest + container: cypress/browsers:node14.17.0-chrome91-ff89 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v2 + id: yarn-build-cache + with: + path: | + **/node_modules + ~/.cache/Cypress + **/build + key: ${{ runner.os }}-node_modules-files-build-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node_modules-build- + + # Install NPM dependencies, cache them correctly + # and run all Cypress tests + - name: Cypress run + uses: cypress-io/github-action@v2 + env: + REACT_APP_API_URL: ${{ secrets.GH_REACT_APP_API_URL }} + REACT_APP_BLOCKNATIVE_ID: ${{ secrets.GH_REACT_APP_BLOCKNATIVE_ID }} + REACT_APP_FILES_VERIFIER_NAME: ${{ secrets.GH_REACT_APP_FILES_VERIFIER_NAME }} + REACT_APP_FILES_UUID_VERIFIER_NAME: 'chainsafe-uuid-testnet' + REACT_APP_TEST: 'true' + DEBUG: '@cypress/github-action' + with: + start: yarn start:files-ui + # wait for 10min for the server to be ready + wait-on: 'npx wait-on --timeout 600000 http://localhost:3000' + # custom test command to run + command: yarn test:ci:files-ui + # store the screenshots if the tests fail + - name: Store screenshots + uses: actions/upload-artifact@v1 + if: failure() + with: + name: cypress-screenshots + path: packages/files-ui/cypress/screenshots + # store the videos if the tests fail + # - name: Store videos + # uses: actions/upload-artifact@v1 + # if: failure() + # with: + # name: cypress-videos + # path: packages/files-ui/cypress/videos + + - name: Slack Notification + uses: rtCamp/action-slack-notify@v2.2.0 + env: + SLACK_TITLE: 'Files UI Test Suite On-Demand Result:' + SLACK_MESSAGE: ${{ job.status }} + SLACK_COLOR: ${{ job.status }} + MSG_MINIMAL: actions url + SLACK_WEBHOOK: ${{ secrets.SLACK_UI_WEBHOOK }} + SLACK_FOOTER: 'Test run ${{ github.run_number }} was executed on branch: ${{ github.ref }}' diff --git a/packages/common-components/src/Button/Button.tsx b/packages/common-components/src/Button/Button.tsx index 0900773aaf..05ae395162 100644 --- a/packages/common-components/src/Button/Button.tsx +++ b/packages/common-components/src/Button/Button.tsx @@ -93,7 +93,7 @@ const useStyles = makeStyles( fill: palette.common.white.main }, "&:hover": { - backgroundColor: palette.primary.main, + backgroundColor: palette.primary.hover, color: palette.common.white.main, ...overrides?.Button?.variants?.secondary?.hover }, @@ -109,6 +109,20 @@ const useStyles = makeStyles( }, ...overrides?.Button?.variants?.secondary?.root }, + text: { + backgroundColor: "transparent", + color: palette.additional["gray"][9], + "&:hover": { + ...overrides?.Button?.variants?.text?.hover + }, + "&:focus": { + ...overrides?.Button?.variants?.text?.focus + }, + "&:active": { + ...overrides?.Button?.variants?.text?.active + }, + ...overrides?.Button?.variants?.text?.root + }, tertiary: { backgroundColor: palette.additional["gray"][3], color: palette.common.black.main, @@ -293,7 +307,7 @@ interface IButtonProps extends Omit { className?: string children?: ReactNode | ReactNode[] fullsize?: boolean - variant?: "link" | "primary" | "secondary" |"tertiary" | "outline" | "dashed" | "danger" + variant?: "link" | "primary" | "secondary" |"tertiary" | "outline" | "dashed" | "danger" | "text" iconButton?: boolean size?: "large" | "medium" | "small" type?: "button" | "submit" | "reset" diff --git a/packages/common-components/src/CheckboxInput/CheckboxInput.tsx b/packages/common-components/src/CheckboxInput/CheckboxInput.tsx index 7d508154fa..8a1f4375a3 100644 --- a/packages/common-components/src/CheckboxInput/CheckboxInput.tsx +++ b/packages/common-components/src/CheckboxInput/CheckboxInput.tsx @@ -10,6 +10,7 @@ const useStyles = makeStyles( root: { cursor: "pointer", display: "flex", + alignItems: "center", ...overrides?.CheckboxInput?.root }, checkbox: { diff --git a/packages/common-components/src/Dialog/Dialog.tsx b/packages/common-components/src/Dialog/Dialog.tsx index 039d9690c8..d70fb04f80 100644 --- a/packages/common-components/src/Dialog/Dialog.tsx +++ b/packages/common-components/src/Dialog/Dialog.tsx @@ -7,13 +7,10 @@ import { Button, IButtonProps } from "../Button" const useStyles = makeStyles(({ breakpoints, constants }: ITheme) => createStyles({ - // JSS in CSS goes here root: {}, inner: { padding: constants.generalUnit * 2, borderRadius: 2, - transform: "translate(-50%, 0)", - top: constants.generalUnit * 2, [breakpoints.down("sm")]: { maxWidth: `calc(100% - ${constants.generalUnit * 3}px) !important` } diff --git a/packages/common-components/src/Icons/svgs/document.svg b/packages/common-components/src/Icons/svgs/document.svg index da7d381789..5edd45259b 100644 --- a/packages/common-components/src/Icons/svgs/document.svg +++ b/packages/common-components/src/Icons/svgs/document.svg @@ -1,3 +1 @@ - - - \ No newline at end of file + diff --git a/packages/common-components/src/Icons/svgs/ethereum-logo.svg b/packages/common-components/src/Icons/svgs/ethereum-logo.svg index 15eb15600a..7fe1413ff0 100644 --- a/packages/common-components/src/Icons/svgs/ethereum-logo.svg +++ b/packages/common-components/src/Icons/svgs/ethereum-logo.svg @@ -1,13 +1 @@ - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/packages/common-components/src/MenuDropdown/MenuDropdown.tsx b/packages/common-components/src/MenuDropdown/MenuDropdown.tsx index f4b799141a..b5ac4c6e4f 100644 --- a/packages/common-components/src/MenuDropdown/MenuDropdown.tsx +++ b/packages/common-components/src/MenuDropdown/MenuDropdown.tsx @@ -1,9 +1,10 @@ -import React, { ReactNode, useRef, useState } from "react" +import React, { ReactNode, useCallback, useRef, useState } from "react" import { makeStyles, createStyles, ITheme, useOnClickOutside } from "@chainsafe/common-theme" import { Typography } from "../Typography" import clsx from "clsx" import { DirectionalDownIcon, SvgIcon } from "../Icons" import { Paper } from "../Paper" +import { useOnScroll } from "../Scroll/useOnScroll.hook" const useStyles = makeStyles( ({ constants, animation, typography, palette, overrides }: ITheme) => @@ -83,34 +84,64 @@ const useStyles = makeStyles( zIndex: 1000, padding: 0, "&.top-left": { - top: 0, + "&.up": { + bottom: 0 + }, + "&.down": { + top: 0 + }, left: 0, ...overrides?.MenuDropdown?.options?.position?.topLeft }, "&.top-center": { - top: 0, + "&.up": { + bottom: 0 + }, + "&.down": { + top: 0 + }, left: "50%", transform: "translateX(-50%)", ...overrides?.MenuDropdown?.options?.position?.topCenter }, "&.top-right": { - top: 0, + "&.up": { + bottom: 0 + }, + "&.down": { + top: 0 + }, right: 0, ...overrides?.MenuDropdown?.options?.position?.topRight }, "&.bottom-left": { - top: "100%", + "&.up": { + bottom: "100%" + }, + "&.down": { + top: "100%" + }, left: 0, ...overrides?.MenuDropdown?.options?.position?.bottomLeft }, "&.bottom-center": { - top: "100%", + "&.up": { + bottom: "100%" + }, + "&.down": { + top: "100%" + }, left: "50%", transform: "translateX(-50%)", ...overrides?.MenuDropdown?.options?.position?.bottomCenter }, "&.bottom-right": { - top: "100%", + "&.up": { + bottom: "100%" + }, + "&.down": { + top: "100%" + }, right: 0, ...overrides?.MenuDropdown?.options?.position?.bottomRight }, @@ -168,7 +199,8 @@ interface IMenuDropdownProps { | "bottom-left" | "bottom-center" | "bottom-right" - menuItems?: IMenuItem[] + dynamicAnchor?: boolean + menuItems: IMenuItem[] title?: string classNames?: { icon?: string @@ -208,6 +240,24 @@ const MenuDropdown = ({ } }) + const [dropDirection, setDropDirection] = useState<"up" | "down">("down") + + const managePosition = useCallback(() => { + // TODO: Debounce + const { offsetTop } = ref.current as unknown as HTMLDivElement + + if (offsetTop < window.pageYOffset + (window.innerHeight / 2)) { + setDropDirection("down") + } else { + setDropDirection("up") + } + }, [ref]) + + + useOnScroll({ + onScroll: managePosition + }) + return (
diff --git a/packages/common-components/src/Modal/Modal.tsx b/packages/common-components/src/Modal/Modal.tsx index 9a7dde4899..92d95e9bc4 100644 --- a/packages/common-components/src/Modal/Modal.tsx +++ b/packages/common-components/src/Modal/Modal.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useRef } from "react" +import React, { ReactNode, useEffect, useRef } from "react" import { ITheme, useOnClickOutside, makeStyles, createStyles } from "@chainsafe/common-theme" import clsx from "clsx" import { CloseSvg } from "../Icons/icons/Close.icon" @@ -43,6 +43,7 @@ const useStyles = makeStyles( "&.active": { visibility: "visible", opacity: 1, + zIndex: 2500, "& > *": { opacity: 1, visibility: "visible" @@ -51,17 +52,49 @@ const useStyles = makeStyles( }, ...overrides?.Modal?.root }, - inner: { + modalSection: { ...constants.modal.inner, - flexGrow: 1, - flexDirection: "column", - display: "flex", backgroundColor: palette.common?.white.main, - top: "50%", - left: "50%", + zIndex: 1, + "&.subModal": { + marginTop: "0.5rem" + } + }, + closeIcon: { + ...constants.icon, + width: 15, + height: 15, + display: "block", + top: 0, + cursor: "pointer", position: "absolute", - borderRadius: `${constants.generalUnit / 2}`, - transform: "translate(-50%, -50%)", + zIndex: 2, + "& svg": { + stroke: palette.common?.black.main + }, + "&.right": { + transform: "translate(-50%, 50%)", + right: 0, + ...overrides?.Modal?.closeIcon?.right + }, + "&.left": { + left: 0, + transform: "translate(50%, -50%)", + ...overrides?.Modal?.closeIcon?.left + }, + "&.none": { + display: "none" + }, + ...overrides?.Modal?.closeIcon?.root + }, + wrapper : { + position: "relative", + flexDirection: "column", + display: "flex", + margin: "auto", + maxHeight: "100%", + overflow: "auto", + alignItems: "center", "&.xs": { width: `calc(100% - ${constants.generalUnit * 2}px)`, maxWidth: breakpoints.width("xs"), @@ -88,32 +121,6 @@ const useStyles = makeStyles( ...overrides?.Modal?.inner?.size?.xl }, ...overrides?.Modal?.inner?.root - }, - closeIcon: { - ...constants.icon, - width: 15, - height: 15, - display: "block", - top: 0, - cursor: "pointer", - position: "absolute", - "& svg": { - stroke: palette.common?.black.main - }, - "&.right": { - transform: "translate(-50%, 50%)", - right: 0, - ...overrides?.Modal?.closeIcon?.right - }, - "&.left": { - left: 0, - transform: "translate(50%, -50%)", - ...overrides?.Modal?.closeIcon?.left - }, - "&.none": { - display: "none" - }, - ...overrides?.Modal?.closeIcon?.root } }) ) @@ -121,11 +128,12 @@ const useStyles = makeStyles( interface IModalClasses { inner?: string closeIcon?: string + subModalInner?: string } interface IModalProps { className?: string - active: boolean + active?: boolean injectedClass?: IModalClasses closePosition?: "left" | "right" | "none" children?: ReactNode | ReactNode[] @@ -134,6 +142,27 @@ interface IModalProps { onClickOutside?: (e?: React.MouseEvent) => void testId?: string onClose?: () => void + subModal?: ReactNode | ReactNode[] +} + +interface IModalBaseProps { + children?: ReactNode | ReactNode[] + injectedClassInner?: string +} + +const ModalBase = ({ children, injectedClassInner }: IModalBaseProps) => { + const classes = useStyles() + + return ( +
+ {children} +
+ ) } const Modal = ({ @@ -146,12 +175,22 @@ const Modal = ({ onModalBodyClick, testId, onClose, - onClickOutside + onClickOutside, + subModal }: IModalProps) => { const classes = useStyles() - const ref = useRef(null) + useEffect(() => { + if(!active) return + + document.body.style.overflow = "hidden" + + return () => { + document.body.style.overflow = "auto" + } + }, [active]) + const handleClose = () => { onClose && onClose() } @@ -164,10 +203,10 @@ const Modal = ({
{closePosition !== "none" && (
)} - {children} + + {children} + + {subModal && ( + + {subModal} + + )}
) diff --git a/packages/common-components/src/Scroll/index.ts b/packages/common-components/src/Scroll/index.ts index c130bc4b61..14719ea26d 100644 --- a/packages/common-components/src/Scroll/index.ts +++ b/packages/common-components/src/Scroll/index.ts @@ -1 +1,2 @@ export { useScrollToTop } from "./ScrollToTop.hook" +export { useOnScroll } from "./useOnScroll.hook" \ No newline at end of file diff --git a/packages/common-components/src/Scroll/useOnScroll.hook.tsx b/packages/common-components/src/Scroll/useOnScroll.hook.tsx new file mode 100644 index 0000000000..48619c71d8 --- /dev/null +++ b/packages/common-components/src/Scroll/useOnScroll.hook.tsx @@ -0,0 +1,35 @@ +import { useEffect, useState } from "react" + +interface IUseOnScroll { + onScroll?: (e: Event) => void + onScrollUp?: (e: Event) => void + onScrollDown?: (e: Event) => void +} + +export function useOnScroll({ + onScroll, + onScrollDown, + onScrollUp +}: IUseOnScroll) { + const [scrollTop, setScrollTop] = useState(0) + useEffect(() => { + function onScrollFunc(e: Event) { + const currentPosition = document.documentElement.scrollTop + if (onScroll && scrollTop != currentPosition) { + onScroll(e) + } + if (currentPosition > scrollTop) { + // downscroll code + onScrollDown && onScrollDown(e) + } else { + // upscroll code + onScrollUp && onScrollUp(e) + } + setScrollTop(currentPosition <= 0 ? 0 : currentPosition) + } + + window.addEventListener("scroll", onScrollFunc) + return () => window.removeEventListener("scroll", onScrollFunc) + }, [scrollTop, onScrollDown, onScrollUp, onScroll]) + +} \ No newline at end of file diff --git a/packages/common-components/src/SelectInput/SelectInput.tsx b/packages/common-components/src/SelectInput/SelectInput.tsx index 802587398d..2af294f69b 100644 --- a/packages/common-components/src/SelectInput/SelectInput.tsx +++ b/packages/common-components/src/SelectInput/SelectInput.tsx @@ -8,7 +8,6 @@ const useStyles = makeStyles( ({ animation, constants, palette, overrides }: ITheme) => createStyles({ root: { - margin: 5, display: "block", ...overrides?.SelectInput?.root }, diff --git a/packages/common-components/src/Table/TableCell.tsx b/packages/common-components/src/Table/TableCell.tsx index a0f8a2f570..ce3adcdfe5 100644 --- a/packages/common-components/src/Table/TableCell.tsx +++ b/packages/common-components/src/Table/TableCell.tsx @@ -31,6 +31,7 @@ export interface ITableCellProps { align?: AlignOption onClick?: (e?: React.MouseEvent) => void } + const TableCell = React.forwardRef( ( { diff --git a/packages/common-components/src/stories/Button.stories.tsx b/packages/common-components/src/stories/Button.stories.tsx index cffaf6a3b2..2f26a91e4e 100644 --- a/packages/common-components/src/stories/Button.stories.tsx +++ b/packages/common-components/src/stories/Button.stories.tsx @@ -15,12 +15,14 @@ export const actionsData = { onClick: action("onClickButton") } -type VariantOption = "primary" | "secondary" | "tertiary" | "outline" | "dashed" | "danger" | undefined; +type VariantOption = "primary" | "secondary" | "tertiary" | "link" | "text" | "outline" | "dashed" | "danger" | undefined; const variantOptions: VariantOption[] = [ "primary", "outline", "dashed", "danger", + "link", + "text", undefined ] diff --git a/packages/common-theme/src/Defaults/ThemeConfig.ts b/packages/common-theme/src/Defaults/ThemeConfig.ts index 86acc99a74..ebdaf7211f 100644 --- a/packages/common-theme/src/Defaults/ThemeConfig.ts +++ b/packages/common-theme/src/Defaults/ThemeConfig.ts @@ -34,7 +34,6 @@ const DefaultThemeConfig: IThemeConfig = { modal: { inner: { minWidth: 30, - minHeight: 30, maxWidth: defaultBreakpoints.keys["md"] }, backgroundFade: 0.4 diff --git a/packages/common-theme/src/Hooks/useLongPress.tsx b/packages/common-theme/src/Hooks/useLongPress.tsx index 0833e056ba..a5c305b0e8 100644 --- a/packages/common-theme/src/Hooks/useLongPress.tsx +++ b/packages/common-theme/src/Hooks/useLongPress.tsx @@ -1,4 +1,4 @@ -import { useCallback, useRef, useState } from "react" +import { useCallback, useRef } from "react" export interface LongPressEvents { onMouseDown: (e: React.MouseEvent) => void @@ -6,6 +6,30 @@ export interface LongPressEvents { onMouseUp: (e: React.MouseEvent) => void onMouseLeave: (e: React.MouseEvent) => void onTouchEnd: (e: React.TouchEvent) => void + onTouchMove: (e: React.TouchEvent) => void + onMouseMove: (e: React.MouseEvent) => void +} + +const MOVE_THRESHOLD = 5 + +type Coordinates = { + x: number + y: number +} | null; + +function getCurrentPosition(event: any): Coordinates { + if (isTouchEvent(event)) { + return { + x: event.touches[0].pageX, + y: event.touches[0].pageY + } + } + else { + return { + x: event.pageX, + y: event.pageY + } + } } export const useLongPress = ( @@ -13,9 +37,10 @@ export const useLongPress = ( onClick: ((e?: React.MouseEvent) => void) | null, delay = 300 ): LongPressEvents => { - const [longPressTriggered, setLongPressTriggered] = useState(false) + const shouldAllowClick = useRef(true) const timeout: any = useRef() const target: any = useRef() + const startPosition = useRef(null) const start = useCallback( (event: any) => { @@ -25,9 +50,11 @@ export const useLongPress = ( }) target.current = event.target } + shouldAllowClick.current = true + startPosition.current = getCurrentPosition(event) timeout.current = setTimeout(() => { onLongPress && onLongPress(event) - setLongPressTriggered(true) + shouldAllowClick.current = false }, delay) }, [onLongPress, delay] @@ -36,31 +63,55 @@ export const useLongPress = ( const clear = useCallback( (shouldTriggerClick = true) => { timeout.current && clearTimeout(timeout.current) - shouldTriggerClick && !longPressTriggered && onClick && onClick() - setLongPressTriggered(false) + shouldTriggerClick && shouldAllowClick.current && onClick && onClick() + shouldAllowClick.current = false if (target.current) { target.current.removeEventListener("touchend", preventDefault) } - }, [onClick, longPressTriggered] + }, [onClick] + ) + + const move = useCallback( + (event: any) => { + if (startPosition.current) { + const currentPosition = getCurrentPosition(event) + if (currentPosition) { + const movedDistance = { + x: Math.abs(currentPosition.x - startPosition.current.x), + y: Math.abs(currentPosition.y - startPosition.current.y) + } + + if (movedDistance.x > MOVE_THRESHOLD || movedDistance.y > MOVE_THRESHOLD) { + clear(false) + } + } + } + }, [clear] ) return { + // start onMouseDown: (e: React.MouseEvent) => start(e), onTouchStart: (e: React.TouchEvent) => start(e), - onMouseUp: (e: React.MouseEvent) => clear(e), + // end + onMouseUp: () => clear(true), + onTouchEnd: () => clear(true), + // leave onMouseLeave: () => clear(false), - onTouchEnd: (e: React.TouchEvent) => clear(e) + // move + onTouchMove: (e: React.TouchEvent) => move(e), + onMouseMove: (e: React.MouseEvent) => move(e) } } const isTouchEvent = (event: any) => { - return "touches" in event + return event && "touches" in event } const preventDefault = (event: any) => { if (!isTouchEvent(event)) return - if (event.touches.length < 2 && event.preventDefault) { + if (event.touches.length < 2 && event.preventDefault && event.cancelable) { event.preventDefault() } } diff --git a/packages/common-theme/src/Overrides/Button.ts b/packages/common-theme/src/Overrides/Button.ts index 106c92e11c..6f44e7e8a6 100644 --- a/packages/common-theme/src/Overrides/Button.ts +++ b/packages/common-theme/src/Overrides/Button.ts @@ -42,6 +42,18 @@ export interface IButtonOverride { focus?: Record active?: Record } + link?: { + root?: Record + hover?: Record + focus?: Record + active?: Record + } + text?: { + root?: Record + hover?: Record + focus?: Record + active?: Record + } } state?: { danger?: { diff --git a/packages/common-theme/src/Provider/ThemeSwitcherContext.tsx b/packages/common-theme/src/Provider/ThemeSwitcherContext.tsx index ffe8dc549a..aafb1d886c 100644 --- a/packages/common-theme/src/Provider/ThemeSwitcherContext.tsx +++ b/packages/common-theme/src/Provider/ThemeSwitcherContext.tsx @@ -10,6 +10,8 @@ import { useLocalStorage } from "@chainsafe/browser-storage-hooks" type ThemeSwitcherContext = { desktop: boolean + tablet: boolean + mobile: boolean themeKey: string availableThemes: string[] setTheme(themeName: string): void @@ -26,6 +28,8 @@ type ThemeSwitcherProps = { const ThemeSwitcher = ({ children, themes, storageKey = "cs.themeKey" }: ThemeSwitcherProps) => { const breakpoints = createBreakpoints({}) const desktop = useMediaQuery(breakpoints.up("md")) + const tablet = useMediaQuery(breakpoints.up("sm")) + const mobile = useMediaQuery(breakpoints.up("xs")) const { canUseLocalStorage, localStorageGet, localStorageSet } = useLocalStorage() // TODO: check min 1 theme @@ -60,6 +64,8 @@ const ThemeSwitcher = ({ children, themes, storageKey = "cs.themeKey" }: ThemeSw 01V;j>ZC4(7TmK0L9vX*tieE4Xg(t>2B$k;|% z5<*ImEK}5!J+efyHI~WpoZIL7{o%Quf8e<;F6P|lyqDK|Iq&4^fLSQ7&&Z-jp5k6~bVG2Rdk|dVgM8Id<88d4O2nyQ?LG(BX5`ZE4 zCZ+0=?*Ee?W1@0FzJ6te!Ro5n^z@;d zNjiHpwhs;-2oBiY*kJ5nOE~MUaokMhVaC4LD|^3uCVgl!Asg@9xgBp|uJYr@0b3ij z9XOn$9U(Jg-=1CDy*xE8h3rvR!SC1Ep`p6Hw1jx|Uu_cucewwN~@H%^x zl<~M-YTI*j_8aQ&;PFfo6SPZ;iC!KWDYta=w0GE0R6~PzySb?Q`DmVUQ8&=r5gobb zkSSn_Gt^h+@ksi*J3LQoSe;PSBy0~3+@14ef75$odgNYHBjqCpcOE~gauz0~TU%pj zLs8Sy*-`V-@JjUFhY$7{?pHo?Kn39cN9r*!4Wt=leZmI9kZ{ab^yH}vr~3DBpHd$1 z73U`7+d5u!+uMzP{quXJ{_nyU%_oM(XMZiT-L+Jwior_6>0?I@LJ1-YX5fSP1v7_0 z2$I+U|01BY2b;klD(I-iA=Hq>hK(AMgE#mmAV@Fd=s{EJrOt_r#aJhYfIHq+<}}Ca zd*6AY(iP6qJigQ%9Sg`U)mi`8D*tR{*SdA>m!2R>E_cT6oho|#=~J0|{fF{y`HKU+ z8%$z)2xo`p$0B9s>z(aS1sA@}&OQ`{*03#dtl1l7_t+z9y@u_4t+8H%;<)T8`kB4+ zw1eC#MLmCsWM3c^{I_+%yvJeS?CaC~`glV9=`v3CNBe?wDqhyxrH3}9Z^!Cdx@2GQ zM8K`E9p6Svcx&zbOfGY_(h__6nR|q0D2M+D&?FZ=e?9Ut+X4)7?sQaaOX7Xe`x3W0 zA_EpwQjejHZ@+i1tWaKx?qP!2nNuM_d0TvZ-W z3gv`8SFtRN&M z`16FeE7#h>vBYHRD8l5Z?e&Gx4DaeM7-az2h-i<>sjLv+;O_9&hm^xBB8$Bs98`m>}FM?01RprzXpAQjO%exFcJP|OqtJomB5 z1&_R^Z4XCKv8RXAX`;qIiM+65>8bv-2;X1)A_5w`%QOd+1@Gjx7-`04Ro zC}V*@y{6$=%)ssZz`}eCklXsampdy5y?3^SusJg*!yB+0y8ES~HAxyQ8@N~3pA!#1 zL|dIxk`P~$Y-;>UPk%gMbuK%YqcZolSx?<*yd`4l4d1RHU<&AYVqwp0OCt3fSU^ii z^_SgZ+`C5iiZ>Rk2p{s;gs zejre&cx@iJ;;`&3&zfAa#^#6+6>fGx?ZD)1kCWyjTeiaVFnlO->P@qrL^*xC1c2_* z=N??FBB%B=LiUt9J{$ckyQQf!tP1Nr)qa5S^^>Q^8NZ5Q0DHq-c4b00=CfFMCuct=*@Q-pYTM^=?3*Zrjir%m8ka>Ys->R-e<` z8hivcy>3^dR)!q1dPBD7H?L_9n@;V^Umg%|) zC)beVhO<#7qQJr(tz`*q2vK7@#bryym|RS(R6XiN;I0Qb7pbr!z6QK_=E8$~u6AuE zFr}@01dhlXxZ)MM#=9smGVXd(3HstPcKpn^PH4SV2+8Q%9BUk7O#j}ZQB zJr0!P#-5W2;D?~jLHDMF#`F6jY9y=FR zcgbYV-kl448fxKL^WnEsy!_Gw<3--4Xl3PB4~cm|zpYEJf;iZS6FV(;r)`}%uzK?` zz`@ySL}2@(t}R?g6Opwf{V)uk87v*e-7ZmL@mw8J^AF>|JamINg9l|fRV}*oloek4 zy;Hy)LKkBdueI{OJW%_&j?{EuRR&;am@y6_2j~_!blpc5H^gcKUV33lsJo&jZQ&+l zEpSb8U93*fnsv4r5r|!$3Y?%F7d5*)KTE}PY6tooY6FerzkPwwk3fWP;#C}0Tq+eY zv%xns&*QcGhNv$aW47w}LS~mowSg;rt`NPU;T7Mx1!28S0IVfS1J8R&=|ah$R$(8Q zY1Ks<%g>@r6cB*1Q}o+0yHKISvS}-!!$MhXt^|y{LBPlsMCL>Y@D)f+IuaOB!GLF~ zB=H7b0%-mt9R+N7WO?eGB%n%Ue2B1NP`q_}`*W^mmx`MGr1Tfo4%q2(`&ACR<7G&6 z7?){_EtIb_l_r&F{!MG>TAC_;%}vq#%yWl>Rp3g`?=?Cjebu zSJ>6^{PW2n>qKBzz1pf<&+j~PRMaFF0i@<1z;1D9T!#TZpC)R8$ryiQxUd7M+9*)> ztSJfM3`#~>Ke%#281oSL(DgkFYD&eY5Nl-ZK2~TN6rU2J+y}AS=rv_?+`SIXRE5)TtQd3ox#JdOllt9o5$+0+=bmz0P$ z8W7|Yh`jh$3+|U>nvfWm;qVO8`1)Wy0l$+A9RhGQhjzB|tx|7_NYf#hQbqya*1^3F zh4Wp;YWFch;Luq_nS?;V-`z#JX?FrYTiDVcxq}pWU=12y+FyK1r@S1YYBros$O_@) z-_XE88{J{X3Zf>T56a@8*C$~SKftNO^w3Qa41MYg5V9wHYQt*SL<00Zm#fKh<^OcD zlXZZQW>zc}FBq+V6qhz1mQ99$sr6w0Gc;tdnEqHV6Sy@Nktg z?83Q|{U{_3e0IlQ9O)dtRJ;MkZdjmB7Zle1BkwS!xWt5n?=SYA9scXQJm)IC$>Gw_ zf|5Qm%cBP5?bJyIqYo4ZVhg{>>!RrY8EC!_>;R#}cF$Iy3>*$0{x#aJSP{xeIyHR-GW!r=(B$?ub+T3*+nGi>D4VwJdC_j< zwuettw~1UDUO1V*Z+2IPqS5lRGb3AY#mq0C-JVS1gTKcTjjk;EjJ=PzihA1M|DfjS zctVDEPqUNFKQU;dz~zWBr)Xs%oo&(IPi=K5;iNEff+@Sw}Znwiw4&c}hv=)rWHpUk$ti_Mm9 zs}+7eY}wSsZO%q%+rjg9At7{wg~dNw+x^ZB3;_5 z+nE0{p)G!c%gp+JGrEhaxY3-Gkx_3wYK~~fD-GRFjK1|9p?F~`@S}xejekvybyP2X zslx9s(kTsnJ+A%jfW)u#Xz%ulcNDipt7zTAmqC`+ET3HyXMT)Ws9+rl3OJ7o3ng(6 z(FzXSsZ=>sV)(-BFQm;wJ?;X#FYA_8pBAZU(^8+ZAlO1Eg!A@VcEn9Z zh=+daOxS`%*#bX$aqXI_kQ!dnWcT=u3=uR<$S@B zUru_4d*0?Z+lVSiJr3gV*G9Og@GwGC1a~}>jCUyRN z=ttPNx0t&Zb$2evvcEOBp?2mOjV(eN`}y$asR(ii=kxl>h4D>6V!ai=7wi)X$c2#g zwvRzKwbz!loY{j)vmaT7t7>AFwAmaO2y7UKqPKPv1r7{ysOAwxM;MylIAYpM`ghNZR31Q(?cqeE~_ zYz#2X)Z5usbSZW626V1=sQO%2tM0qs%8LodUst@iRGD+}dhW~>qp_yp`vDs zGu6vUSGGK>5;x#gqJt%fK6kq+e$TzzqA}Ye#FSmL(ag=4f8>g!Oi_zM;Z{1vV{i7S zp!rBx=p0uOulgt3mj3MTDRFiksmw?3+9Z{{uhzt*XM0Re zr%u*DYDq1wQP;ORH0mtKZ46@P1olk0ZB?G_QDhDuGeHJ(0u{Gk=3c1G;Q;XBFJ5b& z$IwfUN)O8nTrY^t9Bwx<=bAc1Ir@!M4!+qRgO)^^8RTtX2l~#ceK3tzs0duXee^n( z#t=nj8GWt0fl)lUE~*lxkSD+Nhr;U07V5YV>DBK{lQTE$vGEwG?0U1=aMoxmE?7(a zQ$mKL6g+q8Zun)}m(8JQdi|A|bJf7Ortt&=o`PcNE7N!#s6{G;Rm7XSYLb`}o@~5_ z;`)}xqC(CSkEk*Zqx0M5!+|>#t^CNT6}$~SMTkpVJR!<;515Yr75zG+RewxtH zn__0%>f#$|zUz}H6Ed?oRF?jQjxu@Z-yWbOl^1B#vs9wEp0J87xQI?&)ZB<$EXXgq zmM9VaipaCenC3?dg+oYAEnz*=H>H)|eRS-kYJ_LTbl0ks=hF+RlSst*mo8no_1fPC zU01f|@^?0LJJVE2!&Up~Oi>eyBj3~XcAN@I>Y<65%sOu&Er!te{?-5fJrs9}(0gn* z9nCsh^8x8)bbKUCT3pe?R6POhR#Zz6XG-zB4MI8dAvdSK`N&+0v%#k@59mXtQc?A- z!Aav=nq9+$*jxglX?6Xi;K3~eUhM8Dol8I8+Tg!7hsr^3<(7O*%X&lR-#IsdvP!#8 z8R`H9K?uj-%frWLH_sXgNbJH?$t9)pBZ5DcyO+ySsH4&QSL#7+^IAw}AeRT#UHLga8mMSsh0IN)D2hkDT?LTSNc zPDh;U$Kf6f!B2xzC8>-TM1D%VgnW@;)T7MT7#Qf`FA26x05ar zgl{k25aZ%;#xg`+q3yR15F!s_Hf*qsM8C;kq_Yfog%Fa;3XbF3eT15#szY)!#>?(X zTnVC2QOz8p%gpGNQc+F@ywE8F9>}TF>OYnIAk}MV8QfxfXG0e)JBTB#6wK)oier;o zQz!RBc&Vr#zSDb&ye2YJAHwG)dk-6+N#+vg;`A2uYpZ0c1A8v~oOPmOXpI+_Y3t}X zlu5w2uZqh^r8+2dew42egh?lO-kUlrqB<{aI4Dnl&E~F0AQhGxGsa`@XN-edc{SJ) z8_*Q-Mkww@e%{m8;NA3;TL0B{D~6L+Uq57eSk`>pXwxp{+|PUn_n~CqE*)KFX!MYu3{sB~<05Tpg<;@=nHT zx=n~-nCNewHC6~mY3+q?28VBD8fT1~0vMCe4a;w2@>Mg&tC2*W5CTcXgLm7Vq^=7K zhGN``yG6>Am&Cml=@6~aXBoSblny|#UuY~W=iGZbC#0Hy_E?a0LzYT2Ea4J-wKW(` zf5fKD%x^wUp95$gK@fSF{1aP^@HS+Fh|3|jFO3)VBTkQ0o*fZ* zSl-!!1og0oYcxZO+gQSXeyJ$2HJA)Io4G{u>5yDUHIc|WiXc;24^sV+9eaq322j@% z_mUC_DU-*w5TC|vl{3bFKx%#Vme{7di#tia#_35{+Q|mIpNLPRb5z#x@ggxwIGuj? z`pyZb5y))uo$)_5EDxkRid!Wuer6@`eraF^3O6MGx5J7N;9et6QK92B^DKKRT15c1 z(ZAW`pW1!GY$ej~BW6Q)SNb!Sn~|P(HSw*;{x0%Gy5;<7E+i^{+H(} z!~6{`IzRY4oT$GC2B)vxNSG~6GA3mCI9v|8HKTYGHeBst8&;QaBL??ZTYQnot3Z6B zj)t3H&F}rEN15*Yfo$^;fd;?!wg~PJqj!HEC9hczai0IJF)6x=n%rFxb@A?sYm+eS z1mIyB-%gl4bXH05_sf&_lf=N0PEnj8)*RI6P}z!4z0^$S;ic9x8Il#1eKPJg6ujV5 zzbUtQni|_$CI=!;$8gqyUKz1(=A_xXmajWAybpj_^70x^hl2n5)R!ka*`;p|=2#$J z+pu~}VOV)>ha)83uMe`<8*}P{HJ=gWR|%`ybR1YZts+hQ8q6t#P1g&XZm{I0rH!(4 z+1J&?3Nc76(9IG%mJal+HkO~(p%JmfeG^XMP?lj_ROG-v6z~d+7+d_7Pe*r5)r5>+IKFM>IdAW&GEKyPk8uj}WD#J}TU*Vr&ylw?F=(Gno#sRR0> z+(QhIb5htX#TmtV6@L)bX_<3M5eNlAO z=?nxS#8<0NSheTcaVetC*&00zk*5m3?<@h^stzSrcyke1fs#TXXI7>F>5f{SYs|?_ zVZPZ1&Z@+KwbO2c(SMWl%UUDU?V64BQ#&LF=oH7#_GobV*Z207g%}_^$sF0W? zqh9UpScu}1+YS0EMn*Qz1LT2O#WTquD+g3D%Hte=n_^vX?y-aYlKn=M>ksg>&fng zP$!>~rQJucX{KQZ>019tdVDn1*XhjbNxAHQ(F7}a))(H*j~|%CFdv+A_Sn1IX{7Rd zq4r4czy*R%*H(dnu&RHz^NB@fFsE&%d#C?Bou?B5=6*8nFrw`sWH;AuJXD zR_Z4y#5*ZRk|6x4;$dq^*%-~s?(%IMNmjkKv|I^D>~_`j$Y(-UZNdgzddB5XLhG^v zt82dGwObXTef3IX#F9ULdY>wTJU-%BKQg%-32bzNfG9?ecK)>bTGy6SBFI;#vSdTd z=*>{r{I=+6gj-{h;lknz$ah+nW+ullS&>7`K%<}D;>B9@hCZ)s_o*<XQ0Vt1q)$z2tCE_@5knWSLbP7p+(=h+YxzNahIo*TudoB zXtluhwiBMU4~5x8`VYHszRSFTYWD2S)dN?B*{A70>MREptP==3qTkybM{SJI+?}XQ z`i#tyj@lw&#i(DkIT62YlKY(Y;GC-^k}5;G4yybZDt_Ae&s&GE2+pdWIOPu1qOhbR z`w0EMZ+_+E;^(@yIEW@q$p`1lcQ|A%<4?!%h&(Cgm)OknG_#j-B>mZA^z%Yq)yw@k zC$Q%qq_1Z-A;$JE{CrI2h)LSwFP=O7<~bTk-9VbDIywj+@D^6B$ZW|X2aY^}B@UB{ z3S)Eqj$^H#FX{Q^<49#t*30y6$E|a9s)SMnONL{Mg*YV}l9XTSV3Gp@J$}Q~gh)+Q z@4fU};%(^YvJAizjWE|<04L=?Y|0AnR%4h02y0Oj>04387pbEkO59=6Ig|;4W+qUL5}Z|gzO;e49q~!jBvjIvV217V z53Tc@-?b{tCehc4ns~62OVw@yD3jm!=U}i~B94k82$HyxI!inuDAEQPc?+(+&eD|7 z@i*)&lDeKW9Ggj|dcESe3bVJ;4Mk0q*<9v$hB!qHikwx?tg{TlhDF@{HGY&nfc(=_ zv@B{eO&zt)9J>4(sFjFL{-^B|l7z_Pz6~Aw30si+=daCVo#h_vc9QVudSibXQ4ErB1~o57LL%MHyd?yX3Sg1#+hhb3S-3%y6ViW#_$IvTy=)E;iHK z-y|R)m2i-L9yQ7`Tu8lx%EKVGlXjs@X0+46uo(AFUE2nk%t-#7__dEO>nz*#uCM(Y zAEqf4B{gisUL)2bzofIO4Y*SENoQRX!NJ_!RSe+zc^&NFCC0v|ClX^R;4z9Mt@v+Y z`D4E*CL8lr5pM%v(}gr-72Q8+xl}H z7&q8tVfGgKLlap8p8SE8_Y{N!F7(`&IsiZE;|{(&}>ncEt*FuimZ73t)3^ z{ITpKxH3K=bc}GMR;YsGXd+AVDhtAzi*SQxKPg^B#v!>iLlz?Px_OlCbk6XQI7XB- z%=^dhOAh%c?si<+PIM&$CUwsb3Xtlwz)4zj!jZkf0-myKMEfsYe8^$G&l$X;c=TpaU^;7npK= zt}ZyzL#WUa%G-h+ljaH>-#Cv7H%{+Pp3wb#C!gXxidr_Tj>B^u@Pq7$r1eLo8jRfi)xLV%L>sf=R~c8j7p&$A)F!;JF#Ru z>Gtg-zeoBjb7!gpsBCqT`S^q&I9j-IJ@fsK?>6PQ1GWCm%hps-2x=LZoHnETfPiSt z+A%L1hxWZ!K+b^9|8m-Zv!n04efAc*&=jP%3PJ}0H+}cGhs0(?G0H1o#^LqJ<81N- zm90w(7;y|w#NbfiWRUm>EosAga>Mf&{Rhu}`C%aP2a;=m4MT+Nt6GQsWRO+}-9nwV zh!+x1-au-iCJ=NejE)J!yxyheCK9J_JK;yuhuZ$=4&_-vw zH)&HCoyd-wKnwaQ$eQ!TJg`KT#ug$4N?%`(J5Eo*etW|?+>%EQjme{IAT+hEAR$EOzvJCLO{y4x0RnA1s68i2upW(JwLgmQg5 z4*|)@H@dJ)vNpv34HiVyfsyUgK%{_NE^yEnXlH~MWh4zk2xZkz zNU_NWXrt>%GZN=&^{kO`qRH#aAEPCytgC|T8umR13t@t~HH*=@w8`4X!eSVl@2uEC zc;K9JkpYf$5fO8v>pdYzF9vWi3y#%XmARP3{OhWA)o6t*^3YiDz4b9K6Rus zfgT&P|6)W)jDN=DcaZK8D^|VATO`cNCA;1H@=PJKt6g}XZ8vZ0xKUMGgg38aW*SaZ-pdc7cf3%bc_pK>&%Zo? z*bzQ5i5w*Ak&fDB+G`y@VP69PXi&C^dlgx#P^LTuAJ%E{T`yPj9sicu<7}D>NCiC zAcV@A<9F5S3RG4%&L8_Ej{`^MD~jL#xU^jpg@OAAZ5n+v4Eg8!*9v2k%fG50%1Prq zW*ZSSwj+Jp%K7domw~E>ivuP4bcC3`pGrP_Y=Pp2)^2N)TOZa-)h1u1Mn??y`)#{P z^ZFMWLYVz@a`n~#;AqE$zl*+gxcf!M!WSdQog_I(url%Dv)3C7#$kHfLJ}kVV|m2U zfx<(`l+Dbdx2OOHyfC{Wu&~zYYHdn^!TW8m!1&#lmxb|ucGuCV zLOQj9g>7=pp|^Sz>Q1bf=xVv`7H|$St|8K%k2#G8iq2-ZmruUZ>|9=$->Tbn+i(mKJ@-rYyH!+(9@yhw0IQFyJo$>?3* z^D#c%oFFWyCev%T=8RWZGdlAyuhTuh5!{8iq02-bl_e#zwLI`fb{qsFFrzy{I?Hd*-7S~MdG=3R&a(uh#IYHwf?ufq=jN zY`EA##vGR>HeeCxn1%Kb8C}g9a|)%gu1#JnMk^q4xpKdTZ%!a-s!AmD5#qGJwH51{ zVrl_7e!non^^Y@nFBclE@^3ioOLJfZ{NC}#4&VL4`5YldoVgWTRsdK5ZGIo>W8_j~ z=_`}-XSyn0ET1Tb2*UE^>gJ4MNawk9?$@HT@4&jNH4@f%b^45=PpNC$wIVVI?$gvp zQC#}$4l2t58&f@6wGmqkHTsB}V%Jl}_W7ygam{tGP14Cfubj$DW|E(7itNgKixx}f zx8Eo1U;`9emo#j3=!WaTy$e9#de^rG>+Ogi9g{3{nJu24$u+*aUIf<=Dx60-i6!qp zv&E6I9vPIFg_zU({3@RtM~IW;j@WOx;8lHloh#!6o7@OxTscQyKA{zXeSN*_5RH|x z>8J65cIa=0`vM^@{A1{Gta;dmvipo6$pBvp!!tAR>BTB=46#56{b=JH>gZ$5@T7{( zJ({EwQa0n>+UVtyDTFxra)}&rF5=TZ_plmw8-`uuWpg>wg4D{zyknE3vt1qr*2bgp>^+F&jp!4I8{ivN697 z^&zOLyGc#Qo{!7N{I9WM?O5HA+U{>0f3dl>X+GUjPK+6$tY&$`KFkv;>p4Lf(tnPY zgTROr%`2Uta}^js%7Jkl`b2HiowdNG<=&&>sq~bMiQ@+AXkO1R42G}??xM-J$oh}a zrn#`26r+0_3q*0si~)hv0qpsF^53U~Qb%c$IXYVlTPaaQmImSbHS;Y}2%N1fSAu&n zFVWv!{`hV?W-25GTAk50k~L_f18atU1-JOR`luxdMX+Zm9>`4=B7v4HuoVLKo_IJ3 zfbMv4-%(i-4=Fe9`s4RdbL0G1wWHmBoK9 zh+>f+Lg(Py5ckn_E`KDMWDaD?seObli?AalbS>s5Ym+(!>XsJS=(0$7}oV(DWlZnDnkSVI>m$?9qLqUF>J!P7c1sgED>*PC&C&I zlZi8Gr$ER0$0GE+HutbC%K{ezupBog#0g&QvimTzzx-n-f}~ z^t^%rF>sm970Xxt_uk_3|5g0C9ZSaX*n0ovJO69#5?hvx(=qFn9^kM~VTbiUgRPYQ zdMiTg-eSmC7%$3T$P+@)hM6Eym*K7`6$c28&n^n%uBqkOevg{amUfjjl+Wgn^O-r? zpf#&TD0M1FM@k7ot!-Ib&pz`jqCafCbQGJIPllpZT28B7EvHE5)y8~?@K0}(+s@{m z;nv>ve3mjKGin+P+IF19oB3jdxUi4q4jLXmrP%22puzeH=A!F&cGEEj5RKX|_;xmb zmF(8Wj$p07xL%G5ug9$@e+`Ywz_gmzUxmpwE ztIuHgUeO?&e8IyPE9cvYqnI_vc!GXU_z@Q4Vi<5ocWr1ego<}yIBEB#52sE-ggOD!W4W5a1`pOrz$pJx$al2l*}4VIpld$ zlPjvJlk#gcZxA-_n5`ybUvC5_zPA?Vmf)EeX^pPnoUTcbUl}8XppGijRPUoN*T^*z zO6|+h)rr~U54yRSNm$B&zCRF+;pdQxA@^hU%=&H%qTJZM9SCx$ufeuK&I8+dgo58M z@l=|q+OX*+9ChVx!Hnx{vREe6D^S-z^gws?DM_al26>c}=J}RRni&5P2)%xR`d{V&2ur@+WyNsEcE(E( zyYrnHl-+_SXoP3YM68q>%`2YpV^thl`y@jhRU1t*!=i63*4R{|jh>*dAfG|uol^=DA2Kw<67b+BfMb%aieyk`pNx%FHU;yzpc7uMIP%3p0 zIF4Uc7?xOfGPIfPzp#k_m}BAp@d9tTZW&9K>2$f#z}9UYdEW5 z%J5tfb6c|4?j<(O6-(xmFM-U)6sknGtcY}y7oNqno_qIRXwBh@rHQ!wolAEk?DlK$o@U0$EE33m!+>7RDC22V3HZqs&Ys8=x{y@F+XE=_!u z%eB)o?#*!-dTh88!slb0K$;navdJ^&Waj5i2^q^mi2OAOKTKSpn$z?71rXy@*N<} zVR&*?c;S|@R-k_J%N4ZdY~=12=yQQKli*fL(o}U23S6>UO}_)~#7v@WStY#t3%^SJ z*g{Yi8}mxJ1W2JU+Z=irYd!p=_vAkwebjm+5**?~A)|0|i;+sDjvjG6)oL*k!a;z8 zf3f~s4j{m#Oyk!PvYIV=6w?My1{^rMS%m3D2!d^zOB>B!)w@AlxFRBE&*%qun#QNB zO-0d9rw6urDT)I_gA{8ZtYDps8S5?`>PN}_XL|!Dzk3H6KkAYUj0bM^$hsQsq0?z% ze#>``aNVAwS*;eKf)L^FFnE_tQKzuPzw6g4E?RZ`77@ZQPZ5GB4qR3(JgrJH3JD1p zkgc@(K3pRFnL3*sXtOBfv-&Pp9P<VlLF9iAKsuUP!Ui>JIL(E(jSVZ5@*P{`6 z&b7{5=sTPkLVKC9FUFR($Tjt&HfH{oHofql<3;*pF5b1vaJ5da4O>TC_-6k36CDLI z|HC(hwE<#8ojlC1?jUvNV?v6+8^GQHQW1qV`b%xJ|HyBr`Qt|qLjcp$?lpE>siR$) zjMq8yRiW!(j5KGfy#Z;O*hRm*V9wvwO^;#yFQod%Gh++LUV-g{)qhUNisIa{WP|NL zFIHqkAT*mYhn}?ZpSn_UbkLdeP4%~8oEm=8o?&=G77eT!w<+jS|6}rR>58n3@kXE* zoM?CC0sGRz#s*&xdX_l54=LLm+MOz&-xC*VUuoCKa{sOEJ;m zAS|H&^5-;*;geCjry9VJiV{(ruWOrg2~m+ia}DAsaC~NfYY!$Z z(A2lR3;CPDRl{s~5(D_J|Jk<|3N(hGRo=`k5)!anR0si~GL z2OtOerzqz`#O@@5!qP@DOaVIMRbj0;Hi#l>^0!y9BH-xJclVmKpcoq7V;S3dA?pm+ zO(xi{^31B;GYmK)>HM?P%nA=K6M!^0)*aNStSoFsV@Za_LE3#Mm$mykyGzsREGyu` zsk#xVHQS>%ij$7&o|X$cDvmfGrr$ShhX?h;GPn@Z-LZV=Tfmf!4x9kXSMp|uLfC=r z!uL`zOg2GyQxNE=KMia*vMcx?^k3xwwiqy>ro4RvGZ{{qew%{m8R@@YoN?X(NNiM^ zjno~*^?d+0`i_PGZs^D4OXEfR(zW}V`bQwF;$UXJJ+KJIiTnLF`bw+C23Kn`9c-g6 z!zU$)uUaj5lqk+E#Ot+U;G+V!u5DYZVYv_N)ta+~s)uk95mB5^;5jV6x#i|xBKzBb z_H58H|17ci?S_tC#&71b6BZ6OQ?0%|GOpkzWh7Jwmn`ygG4I52#-AbKe6sZSFRt%% zK6H;fA%V+!2mmxFyAZwoXN5Loron-~Kh=M=NX>Sn((ymHj}-o+YxM4I&c6QwtQs|= z+tXY3i*1Jnxf5it(78FO9s}M!7`XM+(Ty`nN+^?ELj_x4_xo7t81+k2?dZoYwivKJ zNJ@cI?T)Q^NB}9wTrQTO?Z?c+UOKn_**6A{f!$&2Qt7RdE{QU19y-#t3jMykJ;`lK z#&W1oL$kwnyUIskk8KW z9W6byi%4w<@+0l}i>)^5Uzwx zZpjJXLKPdiwf?ZIJ*YmNtnlC;(E2CD1;T21*5z|YK8DbMwt1Ml=xX}br755|Xkz0= z=^5VOMB3XqXmF0S@SWdTKqAD6Vw5Bb!10L)2CNma!SA30S1hw)$=pJJhw2-V`Gx47 zIxGA}csSUKh24mK4J80Uh1FvJ&Z~OZpuPM)M;PKWK09tEN2i;cwsq2(Y?(6*^U8ehL9r-esT_?ux^bU>}p` z;CTcyMgh3Pl157b=YaL&We$woWMvWnJJAMjKuFU>EEQ`m4wD_Ds^ZrsgD#B_49ImtiWm-< zCpeF?9wz%ak1W1MHjDBUf3iK?nCGP|y zL3aTrZr?)%T?HVky)$Sp9RKA6MriEZ1nH}~c8pSB`M2jElg|Kf3Umnd zt-4orsSMyVS3jR*e68?&CdkK3yFClb#~AQ5Z2hsYezxA&%C~i3IrHujV7RwiD92Wp zl#Ewnfb-`cS?<+0UY&8Bjy?5EkjqspT#+$hoZzn_=$pbn^7YjZgD>SN2OY8xa(4^z zAf5^E0DmB@Jz9Hr?FB#jRINQk9WCPCz3O}R5clk94#fQXe=YF!cR%NO`Tt$8=YJMl zjAQ461#&P48-LHBP`3aN$kg5cv { cy.on("window:before:load", (win) => { @@ -96,11 +98,15 @@ Cypress.Commands.add( apiTestHelper.clearBucket("csf") } + if (deleteShareBucket) { + apiTestHelper.deleteSharedFolders() + } + if (clearTrashBucket) { apiTestHelper.clearBucket("trash") } - if(clearTrashBucket || clearCSFBucket){ + if(clearTrashBucket || clearCSFBucket || deleteShareBucket){ navigationMenu.binNavButton().click() navigationMenu.homeNavButton().click() } @@ -130,6 +136,7 @@ declare global { * @param {Boolean} options.saveBrowser - (default: false) - save the browser to localstorage. * @param {Boolean} options.clearCSFBucket - (default: false) - whether any file in the csf bucket should be deleted. * @param {Boolean} options.clearTrashBucket - (default: false) - whether any file in the trash bucket should be deleted. + * @param {Boolean} options.deleteShareBucket - (default: false) - whether any shared bucket should be deleted. * @example cy.web3Login({saveBrowser: true, url: 'http://localhost:8080'}) */ web3Login: (options?: Web3LoginOptions) => Chainable @@ -146,7 +153,7 @@ declare global { * https://github.com/cypress-io/cypress/issues/7306 * */ - safeClick: () => Chainable + safeClick: () => Chainable } } diff --git a/packages/files-ui/cypress/support/page-objects/basePage.ts b/packages/files-ui/cypress/support/page-objects/basePage.ts index 322ecccf84..8fadff7b66 100644 --- a/packages/files-ui/cypress/support/page-objects/basePage.ts +++ b/packages/files-ui/cypress/support/page-objects/basePage.ts @@ -6,5 +6,11 @@ export const basePage = { signOutDropdown: () => cy.get("[data-testid=menu-title-sign-out]"), signOutMenuOption: () => cy.get("[data-cy=menu-sign-out]"), // Mobile view only element - hamburgerMenuButton: () => cy.get("[data-testId=hamburger-menu]") + hamburgerMenuButton: () => cy.get("[data-testId=hamburger-menu]"), + + // helpers and convenience functions + awaitBucketRefresh() { + cy.intercept("POST", "**/bucket/*/ls").as("refresh") + cy.wait("@refresh") + } } diff --git a/packages/files-ui/cypress/support/page-objects/homePage.ts b/packages/files-ui/cypress/support/page-objects/homePage.ts index 337e56f6a4..23c70df645 100644 --- a/packages/files-ui/cypress/support/page-objects/homePage.ts +++ b/packages/files-ui/cypress/support/page-objects/homePage.ts @@ -15,8 +15,7 @@ export const homePage = { moveSelectedButton: () => cy.get("[data-testId=button-move-selected-file]"), deleteSelectedButton: () => cy.get("[data-testId=button-delete-selected-file]"), selectAllCheckbox: () => cy.get("[data-testId=checkbox-select-all]"), - fileRenameInput: () => cy.get("[data-cy=rename-form] input"), - fileRenameSubmitButton: () => cy.get("[data-cy=rename-submit-button]"), + fileRenameInput: () => cy.get("[data-cy=input-rename-file-or-folder]"), fileRenameErrorLabel: () => cy.get("[data-cy=rename-form] span.minimal.error"), // kebab menu elements diff --git a/packages/files-ui/cypress/support/page-objects/modals/createSharedFolderModal.ts b/packages/files-ui/cypress/support/page-objects/modals/createSharedFolderModal.ts new file mode 100644 index 0000000000..60659ab35d --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/modals/createSharedFolderModal.ts @@ -0,0 +1,11 @@ +export const createEditSharedFolderModal = { + body: () => cy.get("[data-testid=modal-container-create-or-edit-shared-folder]", { timeout: 10000 }), + cancelButton: () => cy.get("[data-cy=button-cancel-create-shared-folder]"), + createButton: () => cy.get("[data-cy=button-create-shared-folder]", { timeout: 10000 }), + editPermissionInput: () => cy.get("[data-cy=input-edit-permission]"), + folderNameInput: () => cy.get("[data-cy=input-shared-folder-name]"), + tagViewPermissionUser: () => cy.get("[data-cy=tag-view-permission-user]"), + tagEditPermissionUser: () => cy.get("[data-cy=tag-edit-permission-user]"), + updateButton: () => cy.get("[data-cy=button-update-shared-folder]", { timeout: 10000 }), + viewOnlyPermissionInput: () => cy.get("[data-cy=input-view-permission") +} \ No newline at end of file diff --git a/packages/files-ui/cypress/support/page-objects/modals/deleteSharedFolderModal.ts b/packages/files-ui/cypress/support/page-objects/modals/deleteSharedFolderModal.ts new file mode 100644 index 0000000000..cd14a6b791 --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/modals/deleteSharedFolderModal.ts @@ -0,0 +1,5 @@ +export const deleteSharedFolderModal = { + body: () => cy.get("[data-testid=modal-container-shared-folder-deletion]"), + cancelButton: () => cy.get("[data-testid=button-cancel-deletion]"), + confirmButton: () => cy.get("[data-testid=button-confirm-deletion]", { timeout: 10000 }) +} \ No newline at end of file diff --git a/packages/files-ui/cypress/support/page-objects/sharedPage.ts b/packages/files-ui/cypress/support/page-objects/sharedPage.ts new file mode 100644 index 0000000000..072482022d --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/sharedPage.ts @@ -0,0 +1,19 @@ +import { basePage } from "./basePage" +import { fileBrowser } from "./fileBrowser" + +export const sharedPage = { + ...basePage, + ...fileBrowser, + + createSharedFolderButton: () => cy.get("[data-cy=button-create-a-shared-folder]"), + sharedFolderItemName: () => cy.get("[data-cy=shared-folder-item-name]"), + sharedFolderItemRow: () => cy.get("[data-cy=shared-folder-item-row]", { timeout: 20000 }), + shareRenameInput: () => cy.get("[data-cy=input-rename-share]"), + + // kebab menu elements + deleteMenuOption: () => cy.get("[data-cy=menu-delete]"), + leaveMenuOption: () => cy.get("[data-cy=menu-leave]"), + manageAccessMenuOption: () => cy.get("[data-cy=menu-manage-access]"), + renameMenuOption: () => cy.get("[data-cy=menu-rename]"), + uploadButton: () => cy.get("[data-cy=button-upload-file]") +} diff --git a/packages/files-ui/cypress/support/utils/apiTestHelper.ts b/packages/files-ui/cypress/support/utils/apiTestHelper.ts index 06286bf47a..50cd2e053a 100644 --- a/packages/files-ui/cypress/support/utils/apiTestHelper.ts +++ b/packages/files-ui/cypress/support/utils/apiTestHelper.ts @@ -7,11 +7,35 @@ import { homePage } from "../page-objects/homePage" const API_BASE_USE = "https://stage.imploy.site/api/v1" const REFRESH_TOKEN_KEY = "csf.refreshToken" +const getApiClient = () => { + // Disable the internal Axios JSON deserialization as this is handled by the client + const axiosInstance = axios.create({ transformResponse: [] }) + const apiClient = new FilesApiClient({}, API_BASE_USE, axiosInstance) + + return apiClient +} export const apiTestHelper = { + deleteSharedFolders() { + const apiClient = getApiClient() + + return new Cypress.Promise(async (resolve) => { + cy.window() + .then(async (win) => { + const tokens = await apiClient.getRefreshToken({ refresh: win.sessionStorage.getItem(REFRESH_TOKEN_KEY) || "" }) + + await apiClient.setToken(tokens.access_token.token) + const buckets = await apiClient.listBuckets(["share"]) + buckets.forEach(async (bucket) => { + cy.log(`Deleting shared bucket: "${bucket.name}""`) + await apiClient.removeBucket(bucket.id) + }) + cy.log("Done deleting shared buckets.") + resolve() + }) + }) + }, clearBucket(bucketType: BucketType) { - // Disable the internal Axios JSON deserialization as this is handled by the client - const axiosInstance = axios.create({ transformResponse: [] }) - const apiClient = new FilesApiClient({}, API_BASE_USE, axiosInstance) + const apiClient = getApiClient() return new Cypress.Promise(async (resolve) => { cy.window() @@ -24,9 +48,9 @@ export const apiTestHelper = { const toDelete = items.map( ({ name }: { name: string }) => `/${name}` ) - cy.log(`Deleting bucket ${bucketType} ${JSON.stringify(toDelete)}`) + cy.log(`Clearing bucket ${bucketType} ${JSON.stringify(toDelete)}`) await apiClient.removeBucketObject(buckets[0].id, { paths: toDelete }) - cy.log("done deleting") + cy.log("Done clearing") resolve() }) }) @@ -34,9 +58,7 @@ export const apiTestHelper = { // create a folder with a full path like "/new folder" // you can create subfolders on the fly too with "/first/sub folder" createFolder(folderPath: string){ - // Disable the internal Axios JSON deserialization as this is handled by the client - const axiosInstance = axios.create({ transformResponse: [] }) - const apiClient = new FilesApiClient({}, API_BASE_USE, axiosInstance) + const apiClient = getApiClient() return new Cypress.Promise((resolve, reject) => { cy.window().then(async (win) => { diff --git a/packages/files-ui/cypress/tests/file-management-spec.ts b/packages/files-ui/cypress/tests/file-management-spec.ts index 24ebd14914..0162687d2a 100644 --- a/packages/files-ui/cypress/tests/file-management-spec.ts +++ b/packages/files-ui/cypress/tests/file-management-spec.ts @@ -37,7 +37,7 @@ describe("File management", () => { it("can add files and cancel modal", () => { cy.web3Login() - // upload a file and see it in the file list + // attach a file and see it in the file list homePage.uploadButton().click() fileUploadModal.body().attachFile("../fixtures/uploadedFiles/text-file.txt") fileUploadModal.fileList().should("have.length", 1) @@ -66,6 +66,9 @@ describe("File management", () => { homePage.moveSelectedButton().click() moveItemModal.folderList().contains(folderName).click() moveItemModal.moveButton().safeClick() + homePage.awaitBucketRefresh() + moveSuccessToast.body().should("be.visible") + moveSuccessToast.closeButton().click() // ensure there is only the folder in the Home directory homePage.fileItemRow().should("have.length", 1) @@ -85,6 +88,7 @@ describe("File management", () => { homePage.moveSelectedButton().click() moveItemModal.folderList().contains("Home").click() moveItemModal.moveButton().safeClick() + homePage.awaitBucketRefresh() moveSuccessToast.body().should("be.visible") moveSuccessToast.closeButton().click() diff --git a/packages/files-ui/cypress/tests/file-preview-spec.ts b/packages/files-ui/cypress/tests/file-preview-spec.ts index 6f2dc17cee..655e1c68e5 100644 --- a/packages/files-ui/cypress/tests/file-preview-spec.ts +++ b/packages/files-ui/cypress/tests/file-preview-spec.ts @@ -51,6 +51,80 @@ describe("File Preview", () => { homePage.appHeaderLabel().should("exist") }) + it("can navigate through preview using keyboard hotkeys", () => { + cy.web3Login({ clearCSFBucket: true }) + + // add files + homePage.uploadFile("../fixtures/uploadedFiles/chainsafe.png") + homePage.uploadFile("../fixtures/uploadedFiles/logo.png") + homePage.uploadFile("../fixtures/uploadedFiles/text-file.txt") + homePage.fileItemRow().should("have.length", 3) + + // store the 3 file names as cypress aliases for later comparison + homePage.fileItemName().eq(0).invoke("text").as("fileNameA") + homePage.fileItemName().eq(1).invoke("text").as("fileNameB") + homePage.fileItemName().eq(2).invoke("text").as("fileNameC") + + // navigate to the preview modal for the first file + homePage.fileItemKebabButton().first().click() + homePage.previewMenuOption().eq(0).click() + previewModal.body().should("exist") + + // ensure the correct file is being previewed + cy.get("@fileNameA").then(($fileNameA) => { + previewModal.fileNameLabel().should("contain.text", $fileNameA) + previewModal.contentContainer() + .should("be.visible") + .should("not.have.text", "Loading preview") + previewModal.unsupportedFileLabel().should("not.exist") + }) + + // browse to the 2nd file via the right arrow key + cy.get("body").type("{rightarrow}") + cy.get("@fileNameB").then(($fileNameB) => { + previewModal.fileNameLabel().should("contain.text", $fileNameB) + previewModal.contentContainer() + .should("be.visible") + .should("not.have.text", "Loading preview") + previewModal.unsupportedFileLabel().should("not.exist") + }) + + // browse to the 3rd file via the right arrow key + cy.get("body").type("{rightarrow}") + cy.get("@fileNameC").then(($fileNameC) => { + previewModal.fileNameLabel().should("contain.text", $fileNameC) + previewModal.contentContainer() + .should("be.visible") + .should("not.have.text", "Loading preview") + previewModal.unsupportedFileLabel().should("not.exist") + }) + + // return to the 2nd file via the left arrow key + cy.get("body").type("{leftarrow}") + cy.get("@fileNameB").then(($fileNameB) => { + previewModal.fileNameLabel().should("contain.text", $fileNameB) + previewModal.contentContainer() + .should("be.visible") + .should("not.have.text", "Loading preview") + previewModal.unsupportedFileLabel().should("not.exist") + }) + + // return to the 1st file via the left arrow key + cy.get("body").type("{leftarrow}") + cy.get("@fileNameA").then(($fileNameA) => { + previewModal.fileNameLabel().should("contain.text", $fileNameA) + previewModal.contentContainer() + .should("be.visible") + .should("not.have.text", "Loading preview") + previewModal.unsupportedFileLabel().should("not.exist") + }) + + // exit preview via the escape key + cy.get("body").type("{esc}") + previewModal.body().should("not.exist") + homePage.appHeaderLabel().should("exist") + }) + it("can see option to download file from the preview screen", () => { cy.web3Login({ clearCSFBucket: true }) homePage.uploadFile("../fixtures/uploadedFiles/logo.png") diff --git a/packages/files-ui/cypress/tests/file-sharing-spec.ts b/packages/files-ui/cypress/tests/file-sharing-spec.ts new file mode 100644 index 0000000000..0e6b04bc9a --- /dev/null +++ b/packages/files-ui/cypress/tests/file-sharing-spec.ts @@ -0,0 +1,63 @@ +import { createEditSharedFolderModal } from "../support/page-objects/modals/createSharedFolderModal" +import { deleteSharedFolderModal } from "../support/page-objects/modals/deleteSharedFolderModal" +import { fileUploadModal } from "../support/page-objects/modals/fileUploadModal" +import { navigationMenu } from "../support/page-objects/navigationMenu" +import { sharingExplainerKey, sharedFolderName, sharedFolderEditedName, validUsername } from "../fixtures/filesTestData" +import { sharedPage } from "../support/page-objects/sharedPage" +import { uploadCompleteToast } from "../support/page-objects/toasts/uploadCompleteToast" + +describe("File Sharing", () => { + + context("desktop", () => { + + it("can create, add, delete a shared folder", () => { + // intercept and stub the response to ensure the explainer is not displayed + cy.intercept("GET", "**/user/store", { + body: { [sharingExplainerKey]: "true" } + }) + + cy.web3Login({ deleteShareBucket: true }) + + // create a shared folder + navigationMenu.sharedNavButton().click() + sharedPage.createSharedFolderButton().click() + createEditSharedFolderModal.body().should("be.visible") + createEditSharedFolderModal.folderNameInput().type(sharedFolderName) + createEditSharedFolderModal.editPermissionInput().type(validUsername).type("{enter}") + createEditSharedFolderModal.createButton().safeClick() + createEditSharedFolderModal.body().should("not.exist") + sharedPage.sharedFolderItemRow().should("have.length", 1) + + // upload to a shared folder + sharedPage.sharedFolderItemName().contains(sharedFolderName) + .should("be.visible") + .dblclick() + sharedPage.uploadButton().click() + fileUploadModal.body().attachFile("../fixtures/uploadedFiles/text-file.txt") + fileUploadModal.fileList().should("have.length", 1) + fileUploadModal.uploadButton().safeClick() + fileUploadModal.body().should("not.exist") + uploadCompleteToast.body().should("be.visible") + uploadCompleteToast.closeButton().click() + sharedPage.fileItemRow().should("have.length", 1) + + // edit name of a shared folder + navigationMenu.sharedNavButton().click() + sharedPage.sharedFolderItemRow().should("have.length", 1) + sharedPage.fileItemKebabButton().click() + sharedPage.renameMenuOption().click() + sharedPage.shareRenameInput() + .type("{selectall}{del}") + .type(`${sharedFolderEditedName}{enter}`) + sharedPage.sharedFolderItemName().contains(sharedFolderEditedName) + + // delete a shared folder + sharedPage.fileItemKebabButton().click() + sharedPage.deleteMenuOption().click() + deleteSharedFolderModal.body().should("be.visible") + deleteSharedFolderModal.confirmButton().safeClick() + deleteSharedFolderModal.body().should("not.exist") + sharedPage.sharedFolderItemRow().should("not.exist") + }) + }) +}) diff --git a/packages/files-ui/cypress/tests/sharing-explainer-spec.ts b/packages/files-ui/cypress/tests/sharing-explainer-spec.ts index 0103251ab8..796ef9c04c 100644 --- a/packages/files-ui/cypress/tests/sharing-explainer-spec.ts +++ b/packages/files-ui/cypress/tests/sharing-explainer-spec.ts @@ -1,16 +1,15 @@ import { navigationMenu } from "../support/page-objects/navigationMenu" import { sharingExplainerModal } from "../support/page-objects/modals/sharingExplainerModal" +import { sharingExplainerKey } from "../fixtures/filesTestData" describe("Sharing Explainer", () => { context("desktop", () => { - const sharingKey = "csf.dismissedSharingExplainer" - it("User can view and dismiss the sharing explainer", () => { // intercept and stub the response to ensure the explainer is displayed cy.intercept("GET", "**/user/store", { - body: { [sharingKey]: "false" } + body: { [sharingExplainerKey]: "false" } }) cy.web3Login() @@ -28,14 +27,14 @@ describe("Sharing Explainer", () => { // intercept POST to ensure the key was updated after the explainer is dismissed cy.wait("@storePost").its("request.body").should("contain", { - [sharingKey]: "true" + [sharingExplainerKey]: "true" }) }) }) it("User should not see sharing explainer if previously dismissed", () => { cy.intercept("GET", "**/user/store", { - body: { [sharingKey]: "true" } + body: { [sharingExplainerKey]: "true" } }) cy.web3Login() diff --git a/packages/files-ui/package.json b/packages/files-ui/package.json index a8e0d81b90..e658ecb5a2 100644 --- a/packages/files-ui/package.json +++ b/packages/files-ui/package.json @@ -6,7 +6,7 @@ "@babel/core": "^7.12.10", "@babel/runtime": "^7.0.0", "@chainsafe/browser-storage-hooks": "^1.0.1", - "@chainsafe/files-api-client": "^1.18.19", + "@chainsafe/files-api-client": "^1.18.20", "@chainsafe/web3-context": "1.1.4", "@emeraldpay/hashicon-react": "^0.5.1", "@lingui/core": "^3.7.2", @@ -77,7 +77,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.6", + "cypress": "^9.0", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/files-ui/public/index.html b/packages/files-ui/public/index.html index 98d6df81a3..badfe0806a 100644 --- a/packages/files-ui/public/index.html +++ b/packages/files-ui/public/index.html @@ -3,7 +3,7 @@ - + diff --git a/packages/files-ui/src/Components/Elements/CustomModal.tsx b/packages/files-ui/src/Components/Elements/CustomModal.tsx index 62d63fa07c..dbe5d76a25 100644 --- a/packages/files-ui/src/Components/Elements/CustomModal.tsx +++ b/packages/files-ui/src/Components/Elements/CustomModal.tsx @@ -13,7 +13,8 @@ const useStyles = makeStyles(({ constants, breakpoints }: CSFTheme) => }, inner: { backgroundColor: constants.modalDefault.backgroundColor, - color: constants.modalDefault.color + color: constants.modalDefault.color, + width: "100%" }, mobileStickyBottom: { [breakpoints.down("md")]: { @@ -51,7 +52,8 @@ const CustomModal = ({ className, children, injectedClass, mobileStickyBottom = className={clsx(classes.root, className)} injectedClass={{ closeIcon: clsx(classes.closeIcon, injectedClass?.closeIcon), - inner: clsx(classes.inner, mobileStickyBottom ? classes.mobileStickyBottom : undefined, injectedClass?.inner) + inner: clsx(classes.inner, mobileStickyBottom ? classes.mobileStickyBottom : undefined, injectedClass?.inner), + subModalInner: injectedClass?.subModalInner }} {...rest} > diff --git a/packages/files-ui/src/Components/Elements/MnemonicForm.tsx b/packages/files-ui/src/Components/Elements/MnemonicForm.tsx index 31e95cf972..067befa0da 100644 --- a/packages/files-ui/src/Components/Elements/MnemonicForm.tsx +++ b/packages/files-ui/src/Components/Elements/MnemonicForm.tsx @@ -136,8 +136,10 @@ const MnemonicForm = ({ buttonLabel, onComplete }: Props) => { component="p" className={classes.loader} > - + Generating… diff --git a/packages/files-ui/src/Components/Elements/NotificationsDropdown.tsx b/packages/files-ui/src/Components/Elements/NotificationsDropdown.tsx index bcbc88baf6..941e778bea 100644 --- a/packages/files-ui/src/Components/Elements/NotificationsDropdown.tsx +++ b/packages/files-ui/src/Components/Elements/NotificationsDropdown.tsx @@ -73,6 +73,7 @@ const NotificationsDropdown: React.FC = ({ notifica return ( modalInner: { padding: constants.generalUnit * 4, textAlign: "center", + display: "flex", + flexDirection: "column", "& img" : { width: "min-content", margin: "auto", diff --git a/packages/files-ui/src/Components/Elements/SharedUsers.tsx b/packages/files-ui/src/Components/Elements/SharedUsers.tsx index 6ea07668bb..c385295223 100644 --- a/packages/files-ui/src/Components/Elements/SharedUsers.tsx +++ b/packages/files-ui/src/Components/Elements/SharedUsers.tsx @@ -3,11 +3,15 @@ import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-th import UserBubble from "./UserBubble" import { BucketKeyPermission, RichUserInfo } from "../../Contexts/FilesContext" import { getUserDisplayName } from "../../Utils/getUserDisplayName" +import { CSFTheme } from "../../Themes/types" -const useStyles = makeStyles(() => { +const useStyles = makeStyles(({ constants }: CSFTheme) => { return createStyles({ root: { display: "flex" + }, + bubble: { + marginRight: constants.generalUnit } }) }) @@ -55,6 +59,7 @@ const SharedUsers = ({ bucket }: Props) => {
1 ? classes.bubble : undefined} /> {userLabels.length > 2 && ( { +const UserBubble = ({ text, tooltip, className }: Props) => { const classes = useStyles() const [showTooltip, setShowTooltip] = useState(false) @@ -80,7 +78,7 @@ const UserBubble = ({ text, tooltip }: Props) => {
setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)} - className={classes.bubble} + className={clsx(classes.bubble, className)} onClick={toggleTooltip} >
diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index f0a43c9b92..71f61bb0c2 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -12,7 +12,8 @@ import { formatBytes, DeleteSvg, UserShareSvg, - MenuDropdown + MenuDropdown, + ScrollbarWrapper } from "@chainsafe/common-components" import { ROUTE_LINKS } from "../FilesRoutes" import { Trans } from "@lingui/macro" @@ -25,17 +26,45 @@ import { Hashicon } from "@emeraldpay/hashicon-react" const useStyles = makeStyles( ({ palette, animation, breakpoints, constants, zIndex }: CSFTheme) => { return createStyles({ + scrollRoot: { + zIndex: zIndex?.layer1 + }, root: { width: 0, - overflow: "auto", + overflowX: "hidden", + overflowY: "auto", transitionDuration: `${animation.translate}ms`, display: "flex", flexDirection: "column", position: "fixed", left: 0, opacity: 0, + "&:before": { + content: "''", + display: "block", + position: "absolute", + top: 0, + left: 0, + height: "100%", + width: "100%", + zIndex: 9999, + opacity: 0, + visibility: "visible", + transitionDuration: `${animation.translate}ms` + }, "&.active": { - opacity: 1 + opacity: 1, + "&:before": { + content: "''", + display: "block", + position: "absolute", + top: 0, + left: 0, + height: "100%", + width: "100%", + zIndex: -1, + visibility: "hidden" + } }, [breakpoints.up("md")]: { padding: `${constants.topPadding}px ${ @@ -53,12 +82,12 @@ const useStyles = makeStyles( top: `${constants.mobileHeaderHeight}px`, backgroundColor: constants.nav.mobileBackgroundColor, zIndex: zIndex?.layer1, - padding: `0 ${constants.generalUnit * 4}px`, maxWidth: "100vw", visibility: "hidden", "&.active": { visibility: "visible", - width: `${constants.mobileNavWidth}px` + width: `${constants.mobileNavWidth}px`, + padding: `0 ${constants.generalUnit * 4}px` } } }, @@ -102,6 +131,7 @@ const useStyles = makeStyles( marginBottom: constants.generalUnit * 2 }, [breakpoints.down("md")]: { + marginTop: constants.generalUnit * 2, transitionDuration: `${animation.translate}ms`, color: palette.additional["gray"][3], "&.active": {} @@ -129,7 +159,7 @@ const useStyles = makeStyles( } }, "& svg": { - "& path" : { + "& path": { fill: constants.nav.headingColor }, transitionDuration: `${animation.transform}ms`, @@ -149,9 +179,6 @@ const useStyles = makeStyles( "& svg": { fill: constants.nav.itemIconColorHover } - }, - [breakpoints.down("md")]: { - minWidth: Number(constants.mobileNavWidth) } }, navItemText: { @@ -234,53 +261,54 @@ const AppNav = ({ navOpen, setNavOpen }: IAppNav) => { const profileTitle = getProfileTitle() return ( -
+
- {isLoggedIn && + : navOpen + })} + > + {isLoggedIn && secured && !!publicKey && !isNewDevice && !shouldInitializeAccount && ( - <> - {desktop && ( -
- signOut(), - contents: ( -
- - - Sign Out - -
- ) - } - ]} - > - {!!profileTitle && + <> + {desktop && ( +
+ signOut(), + contents: ( +
+ + + Sign Out + +
+ ) + } + ]} + > + {!!profileTitle &&
{ {profileTitle}
- } - -
- )} -
- - Folders - -
+ )} +
+ + Folders + + - - {desktop ? Resources : Account} - - + + {desktop ? Resources : Account} + + -
-
- {desktop && ( -
- {storageSummary && ( - <> - {`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( - storageSummary.total_storage, 2 - )} used`} - - - ) - } -
- )} - {!desktop && ( -
{ - handleOnClick() - signOut() - }} - > - - - Sign Out - -
- )} -
- {!desktop && ( -
setNavOpen(false)} - className={clsx(classes.blocker, { - active: navOpen - })} - >
- )} - - )} -
+ + + Settings + + + +
+
+ {desktop && ( +
+ {storageSummary && ( + <> + {`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( + storageSummary.total_storage, 2 + )} used`} + + + ) + } +
+ )} + {!desktop && ( +
setNavOpen(false)} + className={clsx(classes.blocker, { + active: navOpen + })} + >
+ )} +
+ + )} + + ) } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx index 5ab03207c5..cb0841cd68 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx @@ -30,7 +30,9 @@ const useStyles = makeStyles( }, modalRoot: { zIndex: zIndex?.blocker, - [breakpoints.down("md")]: {} + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { backgroundColor: constants.createFolder.backgroundColor, @@ -144,7 +146,7 @@ const CreateFolderModal = ({ modalOpen, close }: ICreateFolderModalProps) => { variant="h5" component="h5" > - Create Folder + New folder )} diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrEditSharedFolderModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrEditSharedFolderModal.tsx index 11f0295159..0424df0615 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrEditSharedFolderModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrEditSharedFolderModal.tsx @@ -13,7 +13,6 @@ import { useLookupSharedFolderUser } from "./hooks/useLookupUser" import { nameValidator } from "../../../Utils/validationSchema" import { getUserDisplayName } from "../../../Utils/getUserDisplayName" import LinkList from "./LinkSharing/LinkList" -import clsx from "clsx" const useStyles = makeStyles( ({ breakpoints, constants, typography, zIndex, palette }: CSFTheme) => { @@ -25,7 +24,10 @@ const useStyles = makeStyles( alignItems: "center" }, modalRoot: { - zIndex: zIndex?.blocker + zIndex: zIndex?.blocker, + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { backgroundColor: constants.modalDefault.backgroundColor, @@ -106,7 +108,8 @@ const useStyles = makeStyles( color: palette.error.main }, sharingLink: { - padding: constants.generalUnit * 1.25 + padding: constants.generalUnit * 2, + margin: 0 } }) } @@ -121,7 +124,7 @@ interface ICreateOrEditSharedFolderModalProps { const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdit }: ICreateOrEditSharedFolderModalProps) => { const classes = useStyles() - const { desktop } = useThemeSwitcher() + const { desktop, tablet, mobile } = useThemeSwitcher() const { handleCreateSharedFolder, handleEditSharedFolder, isEditingSharedFolder, isCreatingSharedFolder } = useCreateOrEditSharedFolder() const [sharedFolderName, setSharedFolderName] = useState("") const { sharedFolderReaders, sharedFolderWriters, onNewUsers, handleLookupUser, usersError, resetUsers } = useLookupSharedFolderUser() @@ -200,6 +203,13 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi active={isModalOpen} closePosition="none" maxWidth="sm" + testId="create-or-edit-shared-folder" + subModal={mode === "edit" && !!bucketToEdit && ( + + )} >
@@ -224,6 +234,7 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi onChange={onNameChange} autoFocus state={nameError ? "error" : "normal"} + data-cy="input-shared-folder-name" /> {nameError && ( } -
+
{ setHasPermissionsChanged(true) @@ -252,13 +266,23 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi ...provided, minHeight: 90, alignContent: "start" + }), + valueContainer: (provided) => mobile && !tablet ? ({ + ...provided, + paddingBottom: 24 + }) : ({ + ...provided }) }} loadingMessage={t`Loading`} noOptionsMessage={t`No user found for this query.`} + data-cy="tag-view-permission-user" />
-
+
{ setHasPermissionsChanged(true) @@ -274,23 +298,19 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi ...provided, minHeight: 90, alignContent: "start" + }), + valueContainer: (provided) => mobile && !tablet ? ({ + ...provided, + paddingBottom: 24 + }) : ({ + ...provided }) }} loadingMessage={t`Loading`} noOptionsMessage={t`No user found for this query.`} + data-cy="tag-edit-permission-user" />
- {mode === "edit" && !!bucketToEdit && ( -
- - Sharing link - - -
- )} Cancel @@ -329,6 +350,7 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi loading={isCreatingSharedFolder || isEditingSharedFolder} onClick={mode === "create" ? onCreateSharedFolder : onEditSharedFolder} disabled={mode === "create" ? (!!usersError || !!nameError) : !hasPermissionsChanged || !!usersError} + data-cy={mode === "create" ? "button-create-shared-folder" : "button-update-shared-folder"} > {mode === "create" ? Create diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolder.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolder.tsx new file mode 100644 index 0000000000..489c4ccebd --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolder.tsx @@ -0,0 +1,309 @@ +import { Button, ShareAltSvg, TagsInput, Typography, Grid, TextInput } from "@chainsafe/common-components" +import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import React, { useState, useCallback } from "react" +import { CSFTheme } from "../../../Themes/types" +import { BucketKeyPermission } from "../../../Contexts/FilesContext" +import CustomButton from "../../Elements/CustomButton" +import { t, Trans } from "@lingui/macro" +import { useEffect } from "react" +import { SharedFolderModalMode } from "./types" +import { useCreateOrEditSharedFolder } from "./hooks/useCreateOrEditSharedFolder" +import { useLookupSharedFolderUser } from "./hooks/useLookupUser" +import { nameValidator } from "../../../Utils/validationSchema" +import { getUserDisplayName } from "../../../Utils/getUserDisplayName" + +const useStyles = makeStyles( + ({ breakpoints, constants, typography, palette }: CSFTheme) => { + return createStyles({ + root: { + padding: constants.generalUnit * 2, + flexDirection: "column", + display: "flex", + alignItems: "center", + [breakpoints.down("sm")]: { + padding: constants.generalUnit + } + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + }, + label: { + fontSize: 14, + lineHeight: "22px" + }, + heading: { + color: constants.modalDefault.color, + fontWeight: typography.fontWeight.semibold, + marginBottom: 10 + }, + iconBacking: { + backgroundColor: constants.modalDefault.iconBackingColor, + width: 48, + height: 48, + borderRadius: 24, + marginBottom: constants.generalUnit * 2, + marginTop: constants.generalUnit, + "& > svg": { + width: 16, + height: 16, + fill: palette.primary.main, + position: "relative", + display: "block", + transform: "translate(-50%, -50%)", + top: "50%", + left: "50%" + } + }, + modalFlexItem: { + width: "100%", + margin: 5 + }, + inputLabel: { + fontSize: 16, + fontWeight: 600 + }, + shareFolderNameInput: { + margin: `0 ${constants.generalUnit * 1.5}px ${constants.generalUnit}px`, + display: "block" + }, + footer: { + width: "100%", + padding: `${constants.generalUnit * 2}px ${constants.generalUnit * 2}px ${constants.generalUnit}px 0` + }, + errorText: { + marginLeft: constants.generalUnit * 1.5, + color: palette.error.main + } + }) + } +) + +interface ICreateOrManageSharedFolderProps { + mode?: SharedFolderModalMode + onClose: () => void + bucketToEdit?: BucketKeyPermission +} + +const CreateOrManageSharedFolder = ({ mode, onClose, bucketToEdit }: ICreateOrManageSharedFolderProps) => { + const classes = useStyles() + const { desktop } = useThemeSwitcher() + const { handleCreateSharedFolder, handleEditSharedFolder, isEditingSharedFolder, isCreatingSharedFolder } = useCreateOrEditSharedFolder() + const [sharedFolderName, setSharedFolderName] = useState("") + const { sharedFolderReaders, sharedFolderWriters, onNewUsers, handleLookupUser, usersError, resetUsers } = useLookupSharedFolderUser() + const [hasPermissionsChanged, setHasPermissionsChanged] = useState(false) + const [nameError, setNameError] = useState("") + + const onReset = useCallback(() => { + setSharedFolderName("") + setHasPermissionsChanged(false) + resetUsers() + }, [resetUsers]) + + useEffect(() => { + onReset() + + if (!bucketToEdit) return + + const newWriters = bucketToEdit.writers.map((writer) => ({ + label: getUserDisplayName(writer), + value: writer.uuid || "", + data: writer + }) + ) || [] + + const newReaders = bucketToEdit.readers.map((reader) => ({ + label: getUserDisplayName(reader), + value: reader.uuid || "", + data: reader + }) + ) || [] + + onNewUsers(newWriters, "write") + onNewUsers(newReaders, "read") + }, [bucketToEdit, onNewUsers, onReset]) + + const onNameChange = useCallback((value?: string | number) => { + if (value === undefined) return + + const name = value.toString() + setSharedFolderName(name) + + nameValidator + .validate({ name }) + .then(() => { + setNameError("") + }) + .catch((e: Error) => { + setNameError(e.message) + }) + }, []) + + const handleClose = useCallback(() => { + onReset() + onClose() + }, [onClose, onReset]) + + const onCreateSharedFolder = useCallback(() => { + handleCreateSharedFolder(sharedFolderName, sharedFolderReaders, sharedFolderWriters) + .catch(console.error) + .finally(handleClose) + }, [handleCreateSharedFolder, sharedFolderName, sharedFolderWriters, sharedFolderReaders, handleClose]) + + const onEditSharedFolder = useCallback(() => { + if (!bucketToEdit) return + handleEditSharedFolder(bucketToEdit, sharedFolderReaders, sharedFolderWriters) + .catch(console.error) + .finally(handleClose) + }, [handleEditSharedFolder, sharedFolderWriters, sharedFolderReaders, handleClose, bucketToEdit]) + + return ( +
+
+ +
+
+ + {mode === "create" + ? Create Shared Folder + : Manage Shared Folder + } + + +
+ {mode === "create" && +
+ + {nameError && ( + + {nameError} + + )} +
+ } +
+ { + setHasPermissionsChanged(true) + onNewUsers(values, "read") + }} + label={t`Give view-only permission to:`} + labelClassName={classes.inputLabel} + value={sharedFolderReaders} + fetchTags={(inputVal) => handleLookupUser(inputVal, "read")} + placeholder={t`Add by sharing address, username or wallet address`} + styles={{ + control: (provided) => ({ + ...provided, + minHeight: 90, + alignContent: "start" + }) + }} + loadingMessage={t`Loading`} + noOptionsMessage={t`No user found for this query.`} + data-cy="tag-view-permission-user" + /> +
+
+ { + setHasPermissionsChanged(true) + onNewUsers(values, "write") + }} + label={t`Give edit permission to:`} + labelClassName={classes.inputLabel} + value={sharedFolderWriters} + fetchTags={(inputVal) => handleLookupUser(inputVal, "write")} + placeholder={t`Add by sharing address, username or wallet address`} + styles={{ + control: (provided) => ({ + ...provided, + minHeight: 90, + alignContent: "start" + }) + }} + loadingMessage={t`Loading`} + noOptionsMessage={t`No user found for this query.`} + data-cy="tag-edit-permission-user" + /> +
+ + {!!usersError && ( + + {usersError} + + )} + + + Cancel + + + + +
+ ) +} + +export default CreateOrManageSharedFolder diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolderModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolderModal.tsx new file mode 100644 index 0000000000..94e3da4c65 --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateOrManageSharedFolderModal.tsx @@ -0,0 +1,76 @@ +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import React from "react" +import CustomModal from "../../Elements/CustomModal" +import { CSFTheme } from "../../../Themes/types" +import { BucketKeyPermission } from "../../../Contexts/FilesContext" +import { SharedFolderModalMode } from "./types" +import LinkList from "./LinkSharing/LinkList" +import CreateOrManageSharedFolder from "./CreateOrManageSharedFolder" + +const useStyles = makeStyles( + ({ breakpoints, constants, zIndex }: CSFTheme) => { + return createStyles({ + modalRoot: { + zIndex: zIndex?.blocker, + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } + }, + modalInner: { + backgroundColor: constants.modalDefault.backgroundColor, + color: constants.modalDefault.color, + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + subModal: { + width: "100%" + } + }) + } +) + +interface ICreateOrManageSharedFolderModalProps { + mode?: SharedFolderModalMode + isModalOpen: boolean + onClose: () => void + bucketToEdit?: BucketKeyPermission +} + +const CreateOrManageSharedFolderModal = ( + { mode, isModalOpen, onClose, bucketToEdit }: ICreateOrManageSharedFolderModalProps +) => { + const classes = useStyles() + + return ( + + )} + > + + + ) +} + +export default CreateOrManageSharedFolderModal diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx index 98e422c918..6bdf62a4f3 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx @@ -9,7 +9,7 @@ import CustomButton from "../../Elements/CustomButton" import { Trans } from "@lingui/macro" import { FileFullInfo } from "../../../Contexts/FilesContext" import { - Button, + CopyIcon, formatBytes, Grid, Loading, @@ -27,7 +27,9 @@ const useStyles = makeStyles( return createStyles({ modalRoot: { zIndex: zIndex?.blocker, - [breakpoints.down("md")]: {} + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { backgroundColor: constants.fileInfoModal.background, @@ -42,7 +44,6 @@ const useStyles = makeStyles( }, closeButton: { flex: 1, - marginLeft: constants.generalUnit * 2, [breakpoints.down("md")]: { position: "fixed", bottom: 0, @@ -59,13 +60,6 @@ const useStyles = makeStyles( textAlign: "center" } }, - heading: { - fontWeight: typography.fontWeight.semibold, - textAlign: "left", - [breakpoints.down("md")]: { - textAlign: "center" - } - }, infoHeading: { fontWeight: typography.fontWeight.semibold, textAlign: "left" @@ -77,13 +71,18 @@ const useStyles = makeStyles( }px` }, infoBox: { - paddingLeft: constants.generalUnit + paddingLeft: constants.generalUnit, + maxWidth: "100%" }, subInfoBox: { padding: `${constants.generalUnit * 1}px 0` }, subSubtitle: { - color: palette.additional["gray"][8] + color: palette.additional["gray"][8], + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + marginRight: constants.generalUnit * 2 }, technicalContainer: { paddingTop: constants.generalUnit, @@ -108,7 +107,7 @@ const useStyles = makeStyles( alignItems: "center", justifyContent: "center", left: "50%", - bottom: "calc(100% + 5px)", + bottom: "calc(100% + 8px)", position: "absolute", transform: "translate(-50%, 0%)", zIndex: zIndex?.layer1, @@ -137,17 +136,20 @@ const useStyles = makeStyles( visibility: "visible" } }, - copyButton: { - width: "100%" - }, copyContainer: { - position: "relative", - flexBasis: "75%", - color: palette.additional["gray"][9], + position: "relative" + }, + copyIcon: { + fontSize: "16px", + fill: palette.additional["gray"][9], [breakpoints.down("md")]: { - flexBasis: "100%", - margin: `${constants.generalUnit * 2}px` + fontSize: "18px", + fill: palette.additional["gray"][9] } + }, + copyRow: { + display: "flex", + cursor: "pointer" } }) } @@ -180,15 +182,27 @@ const FileInfoModal = ({ filePath, close }: IFileInfoModuleProps) => { } , [bucket, filePath, filesApiClient]) - const [copied, setCopied] = useState(false) - const debouncedSwitchCopied = debounce(() => setCopied(false), 3000) + const [copiedCID, setCopiedCID] = useState(false) + const [copiedKey, setCopiedKey] = useState(false) + const debouncedSwitchCopiedCID = debounce(() => setCopiedCID(false), 3000) + const debouncedSwitchCopiedKey = debounce(() => setCopiedKey(false), 3000) const onCopyCID = () => { if (fullFileInfo?.content?.cid) { - navigator.clipboard.writeText(fullFileInfo?.content?.cid) + navigator.clipboard.writeText(fullFileInfo.content.cid) + .then(() => { + setCopiedCID(true) + debouncedSwitchCopiedCID() + }).catch(console.error) + } + } + + const onCopyKey = () => { + if (bucket?.encryptionKey) { + navigator.clipboard.writeText(bucket.encryptionKey) .then(() => { - setCopied(true) - debouncedSwitchCopied() + setCopiedKey(true) + debouncedSwitchCopiedKey() }).catch(console.error) } } @@ -354,13 +368,27 @@ const FileInfoModal = ({ filePath, close }: IFileInfoModuleProps) => { CID (Content Identifier)
- - {fullFileInfo.content?.cid} - + + {fullFileInfo.content?.cid} + +
+ +
+ + + Copied! + + +
+
+
{ Decryption key - - - {bucket?.encryptionKey} - - + + + {bucket?.encryptionKey} + + +
+ +
+ + + Copied! + + +
+
+
@@ -392,24 +434,6 @@ const FileInfoModal = ({ filePath, close }: IFileInfoModuleProps) => { flexDirection="row" className={classes.buttonsContainer} > -
- -
- - - Copied! - - -
-
close()} size="large" diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/LinkList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/LinkList.tsx index f557c760d7..40c13a1822 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/LinkList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/LinkList.tsx @@ -1,8 +1,8 @@ -import { Button, Loading, MenuDropdown } from "@chainsafe/common-components" +import { Button, Loading, MenuDropdown, Typography } from "@chainsafe/common-components" import { createStyles, makeStyles } from "@chainsafe/common-theme" import { NonceResponse, NonceResponsePermission } from "@chainsafe/files-api-client" import { t, Trans } from "@lingui/macro" -import React, { useCallback, useEffect, useState } from "react" +import React, { ReactNode, useCallback, useEffect, useMemo, useState } from "react" import { useFilesApi } from "../../../../Contexts/FilesApiContext" import { CSFTheme } from "../../../../Themes/types" import SharingLink from "./SharingLink" @@ -11,6 +11,7 @@ const useStyles = makeStyles( ({ constants, palette }: CSFTheme) => { return createStyles({ root: { + padding: 2 * constants.generalUnit }, options: { backgroundColor: constants.header.optionsBackground, @@ -47,49 +48,131 @@ const useStyles = makeStyles( }, permissionDropdown: { padding: `0px ${constants.generalUnit}px`, - backgroundColor: palette.additional["gray"][5], - marginLeft: constants.generalUnit + backgroundColor: palette.additional["gray"][1], + marginLeft: constants.generalUnit, + borderColor: palette.additional["gray"][5], + borderWidth: "1px", + borderStyle: "solid", + borderRadius: "4px" }, - createLink: { - display: "flex", - alignItems: "center", - margin: `${constants.generalUnit * 2.5}px 0` + rightsText: { + display: "inline-block" }, createLinkButton: { - marginRight: constants.generalUnit + width: "100%" }, dropdownTitle: { padding: `${constants.generalUnit * 0.75}px ${constants.generalUnit}px` + }, + heading : { + marginBottom: constants.generalUnit + }, + loadingContainer: { + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center" + }, + grayWrapper: { + backgroundColor: palette.additional["gray"][3], + borderRadius: "4px", + display: "flex", + padding: constants.generalUnit * 2, + flexDirection: "column" + }, + creationWrapper: { + display: "flex", + alignItems: "center", + justifyContent: "center", + flexDirection: "column", + margin: "auto" + }, + rightSelection: { + marginBottom: constants.generalUnit + }, + loader: { + textAlign: "center" + }, + activeLinks: { + marginBottom: constants.generalUnit } }) } ) +const MAX_LINKS = 2 + interface Props { bucketId: string bucketEncryptionKey: string } -const readRights = t`read rights` -const editRights = t`edit rights` +interface LinkMenuItems { + id: NonceResponsePermission + onClick: () => void + contents: ReactNode +} + +const readRights = t`view-only` +const editRights = t`can-edit` export const translatedPermission = (permission: NonceResponsePermission) => permission === "read" ? readRights : editRights const LinkList = ({ bucketId, bucketEncryptionKey }: Props) => { const classes = useStyles() const { filesApiClient } = useFilesApi() const [nonces, setNonces] = useState([]) - const [isLoading, setIsLoading] = useState(false) - const [newLinkPermission, setNewLinkPermission] = useState("read") + const [isLoadingNonces, setIsLoadingNonces] = useState(false) + const [isLoadingCreation, setIsLoadingCreation] = useState(false) + const hasAReadNonce = useMemo(() => !!nonces.find(n => n.permission === "read"), [nonces]) + const [newLinkPermission, setNewLinkPermission] = useState(undefined) + const menuItems: LinkMenuItems[] = useMemo(() => [ + { + id: "read", + onClick: () => setNewLinkPermission("read"), + contents: ( +
+ {readRights} +
+ ) + }, + { + id: "write", + onClick: () => setNewLinkPermission("write"), + contents: ( +
+ {editRights} +
+ ) + } + ], [classes.menuItem]) + + const displayedItems = useMemo(() => nonces.length === 0 + ? menuItems + : hasAReadNonce + ? menuItems.filter(i => i.id === "write") + : menuItems.filter(i => i.id === "read") + , [hasAReadNonce, menuItems, nonces.length] + ) + + useEffect(() => { + setNewLinkPermission(displayedItems[0].id) + }, [displayedItems]) const refreshNonces = useCallback(() => { - setIsLoading(true) + setIsLoadingNonces(true) filesApiClient.getAllNonces() .then((res) => { const noncesForCurrentBucket = res.filter(n => n.bucket_id === bucketId) setNonces(noncesForCurrentBucket) }) .catch(console.error) - .finally(() => setIsLoading(false)) + .finally(() => setIsLoadingNonces(false)) }, [bucketId, filesApiClient]) useEffect(() => { @@ -98,77 +181,101 @@ const LinkList = ({ bucketId, bucketEncryptionKey }: Props) => { const onCreateNonce = useCallback(() => { - setIsLoading(true) + if (!newLinkPermission) { + console.error("Permission not set") + return + } + + setIsLoadingCreation(true) return filesApiClient .createNonce({ bucket_id: bucketId, permission: newLinkPermission }) .catch(console.error) .finally(() => { - setIsLoading(false) + setIsLoadingCreation(false) refreshNonces() }) }, [bucketId, filesApiClient, newLinkPermission, refreshNonces]) return (
-
-
+ )} + {isLoadingNonces && ( + - Create new link - - with - setNewLinkPermission("read"), - contents: ( -
- {readRights} -
- ) - }, - { - onClick: () => setNewLinkPermission("write"), - contents: ( -
- {editRights} -
- ) - } - ]} - /> -
- { - isLoading && - } - { - !isLoading && nonces.length > 0 && nonces.map((nonce) => - - ) - } + + )} + {nonces.length < MAX_LINKS && ( + <> + + Create a sharing link + +
+
+
+ + Anyone with the link can: + + +
+ +
+
+ + )}
) } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx index 882f8c64fd..adc8cca9f3 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx @@ -1,5 +1,5 @@ -import { Button, DeleteSvg, Typography } from "@chainsafe/common-components" +import { CopyIcon, DeleteSvg, Loading, MoreIcon, Typography } from "@chainsafe/common-components" import { createStyles, debounce, makeStyles } from "@chainsafe/common-theme" import { NonceResponse } from "@chainsafe/files-api-client" import { Trans } from "@lingui/macro" @@ -7,19 +7,24 @@ import React, { useCallback, useEffect, useState } from "react" import { useFilesApi } from "../../../../Contexts/FilesApiContext" import { useThresholdKey } from "../../../../Contexts/ThresholdKeyContext" import { CSFTheme } from "../../../../Themes/types" +import Menu from "../../../../UI-components/Menu" import { ROUTE_LINKS } from "../../../FilesRoutes" import { translatedPermission } from "./LinkList" const useStyles = makeStyles( - ({ constants }: CSFTheme) => { + ({ constants, palette, zIndex, animation, typography }: CSFTheme) => { return createStyles({ root: { display: "flex", - marginBottom: constants.generalUnit * 0.5 + maxWidth: "100%", + position: "relative", + "&:not(:first-child)": { + marginTop: constants.generalUnit * 2 + } }, linkWrapper: { whiteSpace: "nowrap", - marginRight: constants.generalUnit * 2, + marginRight: constants.generalUnit * 3, display: "flex", alignItems: "center", overflow: "hidden" @@ -27,14 +32,18 @@ const useStyles = makeStyles( permissionWrapper: { display: "flex", alignItems: "center", - marginRight: constants.generalUnit, + marginRight: constants.generalUnit * 3, flex: 1, - whiteSpace: "nowrap" + whiteSpace: "nowrap", + textAlign: "right", + fontWeight: typography.fontWeight.regular }, copyButton: { - flex: 1, - whiteSpace: "nowrap", - marginRight: constants.generalUnit + display: "flex", + justifyContent: "center", + alignItems: "center", + marginRight: constants.generalUnit * 2, + cursor: "pointer" }, link: { textOverflow: "ellipsis", @@ -47,6 +56,69 @@ const useStyles = makeStyles( width: 20, marginRight: constants.generalUnit * 1.5, fill: constants.fileSystemItemRow.menuIcon + }, + copyIcon: { + fontSize: "18px", + fill: palette.additional["gray"][8] + }, + copiedFlag: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + left: "50%", + top: -15, + position: "absolute", + transform: "translate(-50%, -50%)", + zIndex: zIndex?.layer1, + transitionDuration: `${animation.transform}ms`, + backgroundColor: constants.loginModule.flagBg, + color: constants.loginModule.flagText, + padding: `${constants.generalUnit / 2}px ${constants.generalUnit}px`, + borderRadius: 2, + "&:after": { + transitionDuration: `${animation.transform}ms`, + content: "''", + position: "absolute", + top: "100%", + left: "50%", + transform: "translate(-50%,0)", + width: 0, + height: 0, + borderLeft: "5px solid transparent", + borderRight: "5px solid transparent", + borderTop: `5px solid ${constants.loginModule.flagBg}` + } + }, + dropdownIcon: { + width: 14, + height: 14, + padding: 0, + display: "flex", + justifyContent: "center", + alignItems: "center", + fontSize: "unset", + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon, + width: 14, + height: 14 + } + }, + focusVisible: { + backgroundColor: "transparent !important" + }, + menuRoot: { + zIndex: "2500 !important" as any + }, + loader: { + display: "flex", + alignItems: "center", + margin: "auto" + }, + menuWrapper: { + display: "flex", + alignItems: "center", + margin: "auto" } }) } @@ -65,7 +137,7 @@ const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => { const [jwt, setJwt] = useState("") const { createJWT } = useThresholdKey() const [copied, setCopied] = useState(false) - const [isLoading, setIsLoading] = useState(true) + const [isDeleting, setIsDeleting] = useState(false) useEffect(() => { if(!nonce?.bucket_id || !nonce?.id) { @@ -74,7 +146,6 @@ const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => { const newJwt = createJWT(nonce.bucket_id, nonce.id, nonce.permission) newJwt && setJwt(newJwt) - setIsLoading(false) }, [createJWT, nonce]) useEffect(() => { @@ -94,21 +165,74 @@ const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => { debouncedSwitchCopied() }) .catch(console.error) + + // //Create a textbox field where we can insert text to. + // const copyFrom = document.createElement("textarea") + + // //Set the text content to be the text you wished to copy. + // copyFrom.textContent = link + + // //Append the textbox field into the body as a child. + // //"execCommand()" only works when there exists selected text, and the text is inside + // //document.body (meaning the text is part of a valid rendered HTML element). + // document.body.appendChild(copyFrom) + + // //Select all the text! + // copyFrom.select() + + // //Execute command + // document.execCommand("copy") + + // //(Optional) De-select the text using blur(). + // copyFrom.blur() + + // //Remove the textbox field from the document.body, so no other JavaScript nor + // //other elements can get access to this. + // document.body.removeChild(copyFrom) + + setCopied(true) + debouncedSwitchCopied() }, [debouncedSwitchCopied, link]) const onDeleteNonce = useCallback(() => { - setIsLoading(true) + setIsDeleting(true) filesApiClient.revokeNonce(nonce.id) .catch(console.error) .finally(() => { refreshNonces() - setIsLoading(false) + setIsDeleting(false) }) }, [filesApiClient, nonce, refreshNonces]) + if (isDeleting) { + return ( + <> + + + + + ) + } + return (
-
+ {copied && ( +
+ + Copied! + +
+ )} +
{link} @@ -118,26 +242,30 @@ const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => { {translatedPermission(nonce.permission)}
- - + +
+
+ } + options={[{ + contents: ( + <> + + + Delete + + + ), + onClick: onDeleteNonce + }]} + style={{ focusVisible: classes.focusVisible, root: classes.menuRoot }} + /> +
) } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx index bdbf9b88ea..f8a9a6cf1d 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx @@ -18,7 +18,9 @@ const useStyles = makeStyles( return createStyles({ modalRoot: { zIndex: zIndex?.blocker, - [breakpoints.down("md")]: {} + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { backgroundColor: constants.moveFileModal.background, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/ReportFileModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/ReportFileModal.tsx index 8381fd0604..86548de9c0 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/ReportFileModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/ReportFileModal.tsx @@ -26,7 +26,9 @@ const useStyles = makeStyles( return createStyles({ modalRoot: { zIndex: zIndex?.blocker, - [breakpoints.down("md")]: {} + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { backgroundColor: constants.fileInfoModal.background, @@ -65,10 +67,12 @@ const useStyles = makeStyles( }px` }, infoBox: { - paddingLeft: constants.generalUnit + paddingLeft: constants.generalUnit, + maxWidth: "100%" }, subInfoBox: { - padding: `${constants.generalUnit * 1}px 0` + padding: `${constants.generalUnit * 1}px 0`, + maxWidth: "100%" }, subSubtitle: { color: palette.additional["gray"][8] @@ -135,14 +139,37 @@ const useStyles = makeStyles( }, decryptionKey: { width: "100%", - wordBreak: "break-all" + overflow: "hidden", + textOverflow: "ellipsis", + marginRight: constants.generalUnit * 2 }, infoText: { marginBottom: constants.generalUnit * 2 }, keysWrapper: { - maxHeight: constants.generalUnit * 10, - overflow: "scroll" + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + maxWidth: "100%", + display: "flex", + cursor: "pointer" + }, + copyIcon: { + fontSize: "16px", + fill: palette.additional["gray"][9], + marginRight: constants.generalUnit, + [breakpoints.down("md")]: { + fontSize: "18px", + fill: palette.additional["gray"][9] + } + }, + decryptionKeyTitle: { + display: "flex", + justifyContent: "space-between", + marginBottom: constants.generalUnit * 0.5 + }, + titleMargin: { + marginBottom: constants.generalUnit * 0.5 } }) } @@ -162,7 +189,7 @@ const ReportFileModal = ({ filePath, close }: IReportFileModalProps) => { const classes = useStyles() const { bucket } = useFileBrowser() const { encryptionKey, id } = bucket || {} - const [isLoadingAdminKey, setIsloadingAdminKey] = useState(true) + const [isLoadingAdminKey, setIsLoadingAdminKey] = useState(true) const [adminPubKeys, setAdminPubkeys] = useState([]) const [encryptedDecryptionKeyMap, setEncryptedDecryptionKeyMap] = useState([]) const { encryptForPublicKey } = useThresholdKey() @@ -197,7 +224,7 @@ const ReportFileModal = ({ filePath, close }: IReportFileModalProps) => { setEncryptedDecryptionKeyMap(map) }) .catch(console.error) - .finally(() => setIsloadingAdminKey(false)) + .finally(() => setIsLoadingAdminKey(false)) }, [adminPubKeys, encryptForPublicKey, encryptionKey]) @@ -285,12 +312,13 @@ const ReportFileModal = ({ filePath, close }: IReportFileModalProps) => { Bucket id {id} @@ -300,28 +328,33 @@ const ReportFileModal = ({ filePath, close }: IReportFileModalProps) => { File path {filePath}
- + + Decryption key + +
+
- Decryption key - -
{JSON.stringify(encryptedDecryptionKeyMap)} @@ -351,7 +384,7 @@ const ReportFileModal = ({ filePath, close }: IReportFileModalProps) => {
- Copied! + Copied!
diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index 392ecc47e3..055f11bc30 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -10,6 +10,7 @@ import { ROUTE_LINKS } from "../../FilesRoutes" import { t } from "@lingui/macro" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" import { useFilesApi } from "../../../Contexts/FilesApiContext" +import { parseFileContentResponse } from "../../../Utils/Helpers" const SearchFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { const { pathname } = useLocation() @@ -26,7 +27,8 @@ const SearchFileBrowser: React.FC = ({ controls = false try { if (!searchString || !bucket) return [] - const results = await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString }) + const results = (await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString })) + .map(se => ({ ...se, content: parseFileContentResponse(se.content) })) return results } catch (err) { addToast({ diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx index 71424d9a1b..e1faa549b4 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx @@ -2,11 +2,10 @@ import { createStyles, makeStyles } from "@chainsafe/common-theme" import React, { useState } from "react" import CustomModal from "../../Elements/CustomModal" import { t, Trans } from "@lingui/macro" -import { Button, CheckboxInput, SelectInput, ShareAltSvg, TagsInput, TextInput, Typography } from "@chainsafe/common-components" +import { Button, CheckboxInput, SelectInput, ShareAltSvg, TextInput, Typography } from "@chainsafe/common-components" import { CSFTheme } from "../../../Themes/types" import { useCallback } from "react" import { useCreateOrEditSharedFolder } from "./hooks/useCreateOrEditSharedFolder" -import { useLookupSharedFolderUser } from "./hooks/useLookupUser" import { useMemo } from "react" import { BucketKeyPermission, FileSystemItem, useFiles } from "../../../Contexts/FilesContext" import { useUser } from "../../../Contexts/UserContext" @@ -14,66 +13,38 @@ import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import clsx from "clsx" import { useEffect } from "react" import { nameValidator } from "../../../Utils/validationSchema" +import CreateOrManageSharedFolder from "./CreateOrManageSharedFolder" +import LinkList from "./LinkSharing/LinkList" +import { usePosthogContext } from "../../../Contexts/PosthogContext" import { useFilesApi } from "../../../Contexts/FilesApiContext" +interface StyleProps { + width: number +} + const useStyles = makeStyles( ({ breakpoints, constants, palette, typography, zIndex }: CSFTheme) => { return createStyles({ - modalRoot: { - zIndex: zIndex?.blocker, - [breakpoints.down("md")]: {} - }, - modalInner: { - backgroundColor: constants.fileInfoModal.background, - color: constants.fileInfoModal.color - }, root: { - padding: constants.generalUnit * 4, + padding: constants.generalUnit * 3, flexDirection: "column", - display: "flex", - alignItems: "center" + display: "flex" }, - closeButton: { - flex: 1, - marginLeft: constants.generalUnit * 2, - [breakpoints.down("md")]: { - position: "fixed", - bottom: 0, - left: 0, - width: "100%", - height: constants?.mobileButtonHeight, - margin: 0 - } + modalRoot: { + zIndex: zIndex?.blocker }, - title: { - fontWeight: typography.fontWeight.semibold, - textAlign: "left", - [breakpoints.down("md")]: { - textAlign: "center" + modalInner: ({ width }: StyleProps) => ({ + backgroundColor: constants.fileInfoModal.background, + color: constants.fileInfoModal.color, + width, + [breakpoints.down("sm")]: { + width: "100%" } - }, - infoHeading: { - fontWeight: typography.fontWeight.semibold, - textAlign: "left" - }, - infoContainer: { - borderTop: constants.fileInfoModal.infoContainerBorderTop, - padding: `${constants.generalUnit * 2}px ${constants.generalUnit * 3}px` - }, - infoBox: { - paddingLeft: constants.generalUnit - }, - subInfoBox: { - padding: `${constants.generalUnit * 1}px 0` - }, - subSubtitle: { - color: palette.additional["gray"][8] - }, - paddedContainer: { - padding: `${constants.generalUnit * 2}px ${ - constants.generalUnit * 4 - }px`, - borderBottom: `1px solid ${palette.additional["gray"][3]}` + }), + topIconContainer: { + display: "flex", + flexDirection: "column", + alignItems: "center" }, buttonsArea: { display: "flex", @@ -87,29 +58,28 @@ const useStyles = makeStyles( }, buttonsContainer: { display: "flex", - justifyContent: "center", + flexDirection: "column", + alignItems: "center", marginTop: constants.generalUnit * 2 }, mainButton: { - width: "100%" + width: 240, + marginBottom: constants.generalUnit * 0.5 }, - sideBySideButton: { - minWidth: constants.generalUnit * 12, - "&:first-child": { - marginRight: constants.generalUnit * 2 - } + cancelButton: { + maxWidth: 100 }, heading: { color: constants.modalDefault.color, fontWeight: typography.fontWeight.semibold, - marginBottom: 10 + marginBottom: constants.generalUnit * 3 }, iconBacking: { backgroundColor: constants.modalDefault.iconBackingColor, width: 48, height: 48, borderRadius: 24, - marginBottom: 16, + marginBottom: 8, "& > svg": { width: 16, height: 16, @@ -122,83 +92,71 @@ const useStyles = makeStyles( } }, inputLabel: { - fontSize: 16, - fontWeight: 600 + fontSize: 14, + fontWeight: 600, + marginBottom: constants.generalUnit }, modalFlexItem: { width: "100%", - margin: 5, marginBottom: constants.generalUnit * 2 }, - loadingContainer: { - width: "100%", - paddingBottom: constants.generalUnit * 6, - display: "flex", - flexDirection: "column", - alignItems: "center", - "& svg": { - marginBottom: constants.generalUnit * 2 - } - }, - shareFolderNameInput: { - display: "block" + newFolderInput: { + margin: 0, + width: "100%" }, buttonLink: { color: palette.additional["gray"][10], outline: "none", textDecoration: "underline", cursor: "pointer", - textAlign: "center", + textAlign: "left", marginBottom: constants.generalUnit * 2 }, error: { color: palette.error.main, textAlign: "center" }, - checkIcon: { - marginRight: constants.generalUnit * 2 - }, - successBox: { - textAlign: "center", - marginBottom: constants.generalUnit * 4 - }, - successText: { - display: "flex", - marginBottom: constants.generalUnit * 2 - }, inputWrapper: { marginBottom: 0 }, errorText: { - marginLeft: constants.generalUnit * 2, + marginTop: constants.generalUnit * 1, color: palette.error.main }, - titleWrapper: { - padding: "0 5px" - } + subModal: ({ width }: StyleProps) => ({ + width, + [breakpoints.down("sm")]: { + width: "100%" + } + }) }) } ) interface IShareFileProps { fileSystemItems: FileSystemItem[] - close: () => void + onClose: () => void } -const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { - const classes = useStyles() +const ShareModal = ({ onClose, fileSystemItems }: IShareFileProps) => { const { handleCreateSharedFolder } = useCreateOrEditSharedFolder() const { accountRestricted } = useFilesApi() const [sharedFolderName, setSharedFolderName] = useState("") - const { sharedFolderReaders, sharedFolderWriters, handleLookupUser, onNewUsers, usersError, resetUsers } = useLookupSharedFolderUser() - const [isUsingCurrentBucket, setIsUsingCurrentBucket] = useState(true) + const [isUsingExistingBucket, setIsUsingExistingBucket] = useState(true) const [keepOriginalFile, setKeepOriginalFile] = useState(true) + const [bucketToUpload, setBucketToUpload] = useState() const [destinationBucket, setDestinationBucket] = useState() + const [isFolderCreationLoading, setIsFolderCreationLoading] = useState(false) const { buckets, transferFileBetweenBuckets } = useFiles() const { bucket, currentPath } = useFileBrowser() const { profile } = useUser() const [nameError, setNameError] = useState("") const inSharedBucket = useMemo(() => bucket?.type === "share", [bucket]) + + const classes = useStyles({ + width: bucketToUpload ? 600 : 500 + }) + const isReader = useMemo(() => { if (!bucket) return false @@ -212,9 +170,11 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { return buckets .filter(buck => buck.type === "share" || buck.type === "csf") - // filter out the current bucket + // do not show any buckets being deleted + .filter(buck => buck.status !== "deleting") + // Do not show the current bucket .filter(buck => buck.id !== bucket?.id) - // all buckets where the user is reader or writer + // Show only buckets where the user is owner or writer .filter(buck => !!buck.writers.find((w) => w.uuid === profile.userId) || !!buck.owners.find((o) => o.uuid === profile.userId)) // filter out CSF and share buckets where user is an owner if their account is restricted .filter(buck => !(!!accountRestricted && (buck.type === "csf" || !!buck.owners.find(o => o.uuid === profile.userId)))) @@ -227,15 +187,14 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { const hasNoSharedBucket = useMemo(() => bucketsOptions.length === 0, [bucketsOptions.length]) useEffect(() => { - resetUsers() setSharedFolderName("") setNameError("") - }, [resetUsers]) + }, []) // if the user has no shared bucket, we default to new folder creation useEffect(() => { if (hasNoSharedBucket && !accountRestricted) { - setIsUsingCurrentBucket(false) + setIsUsingExistingBucket(false) } }, [hasNoSharedBucket, accountRestricted]) @@ -256,22 +215,23 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { }, []) const handleShare = useCallback(async () => { - if(!bucket) { + if (!bucket) { console.error("Bucket is undefined") return } - if(!destinationBucket && isUsingCurrentBucket){ + if (!destinationBucket && isUsingExistingBucket) { return } let bucketToUpload: BucketKeyPermission | undefined = destinationBucket - if (!isUsingCurrentBucket) { + if (!isUsingExistingBucket) { + setIsFolderCreationLoading(true) try { - const newBucket = await handleCreateSharedFolder(sharedFolderName, sharedFolderReaders, sharedFolderWriters) + const newBucket = await handleCreateSharedFolder(sharedFolderName, [], []) - if(!newBucket){ + if (!newBucket) { return } bucketToUpload = newBucket @@ -279,77 +239,99 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { console.error(e) return } + setIsFolderCreationLoading(false) } - if(!bucketToUpload){ + if (!bucketToUpload) { console.error("Bucket id to upload is undefined") return } transferFileBetweenBuckets(bucket, fileSystemItems, currentPath, bucketToUpload, keepOriginalFile) - close() + setBucketToUpload(bucketToUpload) + if (isUsingExistingBucket) { + onClose() + } }, [ bucket, destinationBucket, handleCreateSharedFolder, - isUsingCurrentBucket, + isUsingExistingBucket, sharedFolderName, - sharedFolderReaders, - sharedFolderWriters, keepOriginalFile, - close, + onClose, transferFileBetweenBuckets, currentPath, fileSystemItems ]) + const { captureEvent } = usePosthogContext() + return ( + )} > -
-
- -
-
- - {inSharedBucket - ? t`Copy file` - : t`Share file` - } - -
-
- {isUsingCurrentBucket - ? ( -
- setDestinationBucket(buckets.find((bu) => bu.id === val))} - /> -
- ) - : ( - <> -
+ {bucketToUpload + ? + :
+
+
+ +
+
+ + {inSharedBucket + ? t`Copy file` + : t`Share file` + } + +
+
+
+ {isUsingExistingBucket + ? ( +
+ setDestinationBucket(buckets.find((bu) => bu.id === val))} + /> +
+ ) + : ( +
- {nameError && ( + {!!nameError && ( { )}
-
- onNewUsers(values, "read")} - label={t`Give view-only permission to:`} - labelClassName={classes.inputLabel} - value={sharedFolderReaders} - fetchTags={(inputVal) => handleLookupUser(inputVal, "read")} - placeholder={t`Add by sharing address, username or wallet address`} - styles={{ - control: (provided) => ({ - ...provided, - minHeight: 90, - alignContent: "start" - }) - }} - loadingMessage={t`Loading`} - noOptionsMessage={t`No user found for this query.`} - /> -
-
- onNewUsers(values, "write")} - label={t`Give edit permission to:`} - labelClassName={classes.inputLabel} - value={sharedFolderWriters} - fetchTags={(inputVal) => handleLookupUser(inputVal, "write")} - placeholder={t`Add by sharing address, username or wallet address`} - styles={{ - control: (provided) => ({ - ...provided, - minHeight: 90, - alignContent: "start" - }) - }} - loadingMessage={t`Loading...`} - noOptionsMessage={t`No user found for this query.`} - /> -
- {!!usersError && ( - - {usersError} - - )} - - )} -
-
+ )} +
{!hasNoSharedBucket && (
setIsUsingCurrentBucket(!isUsingCurrentBucket)} + onClick={() => setIsUsingExistingBucket(!isUsingExistingBucket)} > { - isUsingCurrentBucket - ? Create a new shared folder - : Use an existing shared folder + isUsingExistingBucket + ? Or Create a new shared folder + : Or Use an existing shared folder }
)} - {!isReader && ( -
- setKeepOriginalFile(!keepOriginalFile)} - label={t`Keep original files`} - /> +
+ {!isReader && ( +
+ { + captureEvent("copy or move files on share") + setKeepOriginalFile(!keepOriginalFile) + } + } + label={t`Keep original files`} + /> +
+ )} +
+ +
- )} -
- -
-
+ } + ) } -export default ShareModal +export default ShareModal \ No newline at end of file diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 11043af19a..55662d53fa 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from "react" +import React, { useCallback, useEffect, useMemo, useState } from "react" import { Typography, Table, @@ -17,8 +17,7 @@ import { BucketKeyPermission, useFiles } from "../../../Contexts/FilesContext" import { t, Trans } from "@lingui/macro" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" -import CreateOrEditSharedFolderModal from "./CreateOrEditSharedFolderModal" -import clsx from "clsx" +import CreateOrManageSharedFolderModal from "./CreateOrManageSharedFolderModal" import { useFilesApi } from "../../../Contexts/FilesApiContext" import { ROUTE_LINKS } from "../../FilesRoutes" import SharedFolderRow from "./views/FileSystemItem/SharedFolderRow" @@ -27,13 +26,13 @@ import SharingExplainerModal from "../../SharingExplainerModal" import { useSharingExplainerModalFlag } from "./hooks/useSharingExplainerModalFlag" import { usePageTrack } from "../../../Contexts/PosthogContext" import RestrictedModeBanner from "../../Elements/RestrictedModeBanner" +import clsx from "clsx" -export const desktopSharedGridSettings = "69px 3fr 120px 190px 150px 45px !important" +export const desktopSharedGridSettings = "50px 3fr 90px 140px 140px 45px !important" export const mobileSharedGridSettings = "3fr 80px 45px !important" const useStyles = makeStyles( ({ animation, breakpoints, constants, palette }: CSFTheme) => { - return createStyles({ root: { position: "relative", @@ -107,6 +106,9 @@ const useStyles = makeStyles( }, confirmDeletionDialog: { top: "50%" + }, + buttonWrap: { + whiteSpace: "nowrap" } }) } @@ -132,6 +134,10 @@ const SharedFolderOverview = () => { usePageTrack() + useEffect(() => { + refreshBuckets(true) + }, [refreshBuckets]) + const handleSortToggle = (targetColumn: SortingType) => { if (column !== targetColumn) { setColumn(targetColumn) @@ -190,21 +196,25 @@ const SharedFolderOverview = () => { setBucketToEdit(undefined) setCreateOrEditSharedFolderMode("create") }} - disabled={accountRestricted} + data-cy="button-create-a-shared-folder" > - Create a Shared Folder + + Create a Shared Folder +
{isLoadingBuckets && ( -
- - +
+ + Loading your shared folders…
@@ -281,7 +291,7 @@ const SharedFolderOverview = () => { showModal={!hasSeenSharingExplainerModal} onHide={hideModal} /> - { diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useGetFile.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useGetFile.tsx index 4f3b9363de..240494ab0c 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useGetFile.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useGetFile.tsx @@ -38,7 +38,6 @@ export const useGetFile = () => { const cancelToken = getSource().token setIsDownloading(true) setError("") - try { const content = await getFileContent( id, @@ -60,12 +59,11 @@ export const useGetFile = () => { return content } catch (error) { - setIsDownloading(false) - // If no error is thrown, this was due to a cancellation by the user. if (error) { console.error(error) setError(t`There was an error getting the preview.`) + setIsDownloading(false) } } }, [bucket, getFileContent]) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useLookupUser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useLookupUser.tsx index 918e956183..ab1f55def9 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useLookupUser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useLookupUser.tsx @@ -1,4 +1,3 @@ -import { LookupUserRequest } from "@chainsafe/files-api-client" import { t } from "@lingui/macro" import { useCallback, useState } from "react" import { useFilesApi } from "../../../../Contexts/FilesApiContext" @@ -25,8 +24,7 @@ export const useLookupSharedFolderUser = () => { )) if (foundIntersectingUsers.length) { - setUsersError(t`User ${ - centerEllipsis(foundIntersectingUsers[0].label) + setUsersError(t`User ${centerEllipsis(foundIntersectingUsers[0].label) } is both a reader and writer`) } else { setUsersError("") @@ -36,7 +34,11 @@ export const useLookupSharedFolderUser = () => { const handleLookupUser = useCallback(async (inputVal: string, permission: SharedFolderUserPermission) => { if (inputVal === "") return [] - const lookupBody: LookupUserRequest = {} + const lookupBody = { + public_address: undefined as string | undefined, + identity_public_key: undefined as string | undefined, + username: undefined as string | undefined + } const ethAddressRegex = new RegExp("^0(x|X)[a-fA-F0-9]{40}$") // Eth Address Starting with 0x and 40 HEX chars const pubKeyRegex = new RegExp("^0(x|X)[a-fA-F0-9]{66}$") // Compressed public key, 66 chars long @@ -49,7 +51,7 @@ export const useLookupSharedFolderUser = () => { } try { - const result = await filesApiClient.lookupUser(lookupBody) + const result = await filesApiClient.lookupUser(lookupBody.username, lookupBody.public_address, lookupBody.identity_public_key) if (!result) return [] const usersList = permission === "read" ? sharedFolderReaders : sharedFolderWriters diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useSharingExplainerModalFlag.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useSharingExplainerModalFlag.tsx index b5c02d9318..d706cdc5d4 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useSharingExplainerModalFlag.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/hooks/useSharingExplainerModalFlag.tsx @@ -7,7 +7,6 @@ export const DISMISSED_SHARING_EXPLAINER_KEY = "csf.dismissedSharingExplainer" export const useSharingExplainerModalFlag = () => { const { localStore, setLocalStore } = useUser() const [hasSeenSharingExplainerModal, setHasSeenSharingExplainerModal] = useState(true) - useEffect(() => { if (!localStore) { return diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx index c9d140d722..62b1e5aeb7 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx @@ -1,11 +1,12 @@ -import React, { useCallback, useEffect, useRef } from "react" +import React, { useCallback, useEffect, useMemo, useRef } from "react" import { makeStyles, createStyles, useThemeSwitcher, useOnClickOutside, LongPressEvents } from "@chainsafe/common-theme" import { t } from "@lingui/macro" import clsx from "clsx" import { FormikTextInput, IMenuItem, - MoreIcon + MoreIcon, + Typography } from "@chainsafe/common-components" import { CSFTheme } from "../../../../../Themes/types" import { FileSystemItem } from "../../../../../Contexts/FilesContext" @@ -64,9 +65,15 @@ const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => desktopRename: { display: "flex", flexDirection: "row", + alignItems: "center", "& svg": { width: 20, height: 20 + }, + "& > span": { + fontSize: 16, + lineHeight: "20px", + marginLeft: constants.generalUnit / 2 } }, dropdownIcon: { @@ -152,15 +159,45 @@ const FileSystemGridItem = React.forwardRef( const { desktop } = useThemeSwitcher() const formRef = useRef(null) + const { + fileName, + extension + } = useMemo(() => { + if (isFolder) { + return { + fileName : name, + extension: "" + } + } + const split = name.split(".") + const extension = `.${split[split.length - 1]}` + + if (split.length === 1) { + return { + fileName : name, + extension: "" + } + } + + return { + fileName: name.slice(0, name.length - extension.length), + extension: split[split.length - 1] + } + }, [name, isFolder]) + const formik = useFormik({ initialValues: { - name + name: fileName }, validationSchema: nameValidator, onSubmit: (values: { name: string }) => { - const newName = values.name.trim() + const newName = extension !== "" ? `${values.name.trim()}.${extension}` : values.name.trim() - newName && handleRename && handleRename(file.cid, newName) + if (newName !== name) { + newName && handleRename && handleRename(file.cid, newName) + } else { + stopEditing() + } }, enableReinitialize: true }) @@ -241,6 +278,13 @@ const FileSystemGridItem = React.forwardRef( } autoFocus={editing === cid} /> + { + !isFolder && extension !== "" && ( + + { `.${extension}` } + + ) + } ) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx index 0dabb8a43a..f86bfafdb4 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx @@ -43,13 +43,12 @@ const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { width: "100%", [breakpoints.up("md")]: { margin: 0 - }, - [breakpoints.down("md")]: { - margin: `${constants.generalUnit * 4.2}px 0` } }, modalRoot: { - [breakpoints.down("md")]: {} + [breakpoints.down("md")]: { + paddingBottom: Number(constants?.mobileButtonHeight) + constants.generalUnit + } }, modalInner: { [breakpoints.down("md")]: { @@ -62,18 +61,34 @@ const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { maxWidth: `${breakpoints.width("md")}px !important` } }, + renameModal: { + padding: constants.generalUnit * 4 + }, renameHeader: { textAlign: "center" }, + renameInputWrapper: { + display: "flex", + flexDirection: "row", + alignItems: "flex-end", + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + }, + "& > span": { + display: "block", + fontSize: 16, + lineHeight: "20px", + marginLeft: constants.generalUnit / 2, + marginBottom: (constants.generalUnit * 2.50), + transform: "translateY(50%)" + } + }, renameFooter: { display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "flex-end" }, - renameModal: { - padding: constants.generalUnit * 4 - }, okButton: { marginLeft: constants.generalUnit }, @@ -152,19 +167,55 @@ const FileSystemItem = ({ const { downloadMultipleFiles } = useFiles() const { cid, name, isFolder, content_type } = file const inSharedFolder = useMemo(() => bucket?.type === "share", [bucket]) + + const { + fileName, + extension + } = useMemo(() => { + if (isFolder) { + return { + fileName : name, + extension: "" + } + } + const split = name.split(".") + const extension = `.${split[split.length - 1]}` + + if (split.length === 1) { + return { + fileName : name, + extension: "" + } + } + + return { + fileName: name.slice(0, name.length - extension.length), + extension: split[split.length - 1] + } + }, [name, isFolder]) + const formik = useFormik({ initialValues: { - name + name: fileName }, validationSchema: nameValidator, onSubmit: (values: { name: string }) => { - const newName = values.name.trim() + const newName = extension !== "" ? `${values.name.trim()}.${extension}` : values.name.trim() - newName && handleRename && handleRename(file.cid, newName) + if (newName !== name) { + newName && handleRename && handleRename(file.cid, newName) + } else { + stopEditing() + } }, enableReinitialize: true }) + const stopEditing = useCallback(() => { + setEditing(undefined) + formik.resetForm() + }, [formik, setEditing]) + let Icon if (isFolder) { Icon = FolderFilledSvg @@ -480,7 +531,7 @@ const FileSystemItem = ({ }} closePosition="none" active={editing === cid} - onClose={() => setEditing("")} + onClose={() => stopEditing()} >
@@ -494,13 +545,22 @@ const FileSystemItem = ({ : Rename file } - +
+ + { + !isFolder && extension !== "" && ( + + { `.${extension}` } + + ) + } +
+ +
+ +
+ + {name} + + ) } - - } - options={menuItems} - style={{ focusVisible: classes.focusVisible }} - /> - - + ) } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index 0889c68958..8cdec63e6c 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -53,7 +53,6 @@ import { useFilesApi } from "../../../../Contexts/FilesApiContext" import RestrictedModeBanner from "../../../Elements/RestrictedModeBanner" import clsx from "clsx" import EmptySvg from "../../../../Media/Empty.svg" -import { getPathWithFile } from "../../../../Utils/pathUtils" const baseOperations: FileOperation[] = ["download", "info", "preview", "share"] const readerOperations: FileOperation[] = [...baseOperations, "report"] @@ -302,6 +301,15 @@ const useStyles = makeStyles( width: 20, marginRight: constants.generalUnit * 1.5, fill: constants.previewModal.menuItemIconColor + }, + fileNameHeader: { + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + marginRight: constants.generalUnit * 2 + }, + buttonWrap: { + whiteSpace: "nowrap" } }) } @@ -610,8 +618,8 @@ const FilesList = ({ isShared = false }: Props) => { }, [setIsSurveyBannerVisible]) const handleViewFolder = useCallback((cid: string) => { - viewFolder && viewFolder(cid) - }, [viewFolder]) + !loadingCurrentPath && viewFolder && viewFolder(cid) + }, [viewFolder, loadingCurrentPath]) const handleOpenMoveFileDialog = useCallback((e: React.MouseEvent) => { e.preventDefault() @@ -638,7 +646,7 @@ const FilesList = ({ isShared = false }: Props) => { <> - Create folder + New folder ), @@ -1102,7 +1110,7 @@ const FilesList = ({ isShared = false }: Props) => { closePreview={closePreview} nextFile={fileIndex < files.length - 1 ? setNextPreview : undefined} previousFile={fileIndex > 0 ? setPreviousPreview : undefined} - filePath={isSearch && getPath ? getPath(files[fileIndex].cid) : getPathWithFile(currentPath, files[fileIndex].name)} + filePath={isSearch && getPath ? getPath(files[fileIndex].cid) : currentPath} /> )} { filePath && isReportFileModalOpen && @@ -1125,7 +1133,7 @@ const FilesList = ({ isShared = false }: Props) => { } { !showExplainerBeforeShare && isShareModalOpen && selectedItems.length && { + onClose={() => { setIsShareModalOpen(false) setFilePath(undefined) }} diff --git a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx index 708a6c1493..b20e8a399a 100644 --- a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx +++ b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx @@ -162,7 +162,7 @@ const useStyles = makeStyles( position: "absolute" } }, - focusVisible:{ + focusVisible: { backgroundColor: "transparent !important" }, menuWrapper: { @@ -185,7 +185,7 @@ interface Props { const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, filePath }: Props) => { const classes = useStyles() const { downloadFile } = useFiles() - const [fileContent, setFileContent] = useState() + const [fileContent, setFileContent] = useState() const { bucket } = useFileBrowser() const { buckets } = useFiles() const { desktop } = useThemeSwitcher() @@ -215,8 +215,8 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, filePath bucketId = bucket.id } + setFileContent(undefined) if (previewRendererKey) { - setFileContent(undefined) getFile({ file, filePath: getPathWithFile(filePath, file.name), bucketId }) .then((content) => { setFileContent(content) @@ -241,11 +241,11 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, filePath useHotkeys("Left,ArrowLeft", () => { previousFile && previousFile() - }) + }, [previousFile]) useHotkeys("Right,ArrowRight", () => { nextFile && nextFile() - }) + }, [nextFile]) const handleDownload = useCallback(() => { if (!name || !cid || !bucket) return @@ -296,7 +296,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, filePath } + icon={} options={menuItems} style={{ focusVisible: classes.focusVisible, diff --git a/packages/files-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx b/packages/files-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx index a741b3debb..72f4434b40 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx @@ -95,13 +95,15 @@ const PasswordlessEmail = ({ resetLogin, email }: IPasswordlessEmail) => { const { login } = useThresholdKey() const [error, setError] = useState() - const onSubmitNonce = useCallback((values) => { + const onSubmitNonce = useCallback(({ nonce }: {nonce: string}) => { if (!email) return + setIsSubmitNonceLoading(true) setError(undefined) + filesApiClient.postIdentityEmailToken({ - email: email, - nonce: values.nonce + email, + nonce: nonce.trim() }).then(async (data) => { await login("email", { token: data.token || "", email }) setIsSubmitNonceLoading(false) diff --git a/packages/files-ui/src/Components/Modules/PreviewRenderers/VideoPreview.tsx b/packages/files-ui/src/Components/Modules/PreviewRenderers/VideoPreview.tsx index 8c6b688ea9..8b84d80b7e 100644 --- a/packages/files-ui/src/Components/Modules/PreviewRenderers/VideoPreview.tsx +++ b/packages/files-ui/src/Components/Modules/PreviewRenderers/VideoPreview.tsx @@ -5,7 +5,8 @@ import { makeStyles, createStyles } from "@chainsafe/common-theme" const useStyles = makeStyles(() => createStyles({ root: { - maxWidth: "100vw" + maxWidth: "100vw", + maxHeight: "100vh" } }) ) @@ -28,7 +29,7 @@ const VideoPreview: React.FC = ({ contents }) => { className={classes.root} src={videoUrl} controls - autoPlay/> + autoPlay /> ) } diff --git a/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx b/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx index 13ba3547a5..be30dcfd8a 100644 --- a/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx @@ -9,7 +9,8 @@ import { RadioInput, TextInput, CheckIcon, - Divider + Divider, + CheckboxInput } from "@chainsafe/common-components" import { makeStyles, @@ -89,8 +90,7 @@ const useStyles = makeStyles(({ constants, breakpoints, palette, typography }: C }, button: { width: 200, - margin: `0px ${constants.generalUnit * 0.5}px ${ - constants.generalUnit * 4 + margin: `0px ${constants.generalUnit * 0.5}px ${constants.generalUnit * 4 }px` }, icon: { @@ -161,7 +161,7 @@ const useStyles = makeStyles(({ constants, breakpoints, palette, typography }: C }, usernameForm: { display: "flex", - marginBottom: constants.generalUnit * 4, + marginBottom: constants.generalUnit, "& svg": { fill: palette.success.main } @@ -184,14 +184,14 @@ const profileValidation = yup.object().shape({ const ProfileView = () => { const { themeKey, setTheme } = useThemeSwitcher() const { addToast } = useToasts() - const { profile, updateProfile, addUsername, lookupOnUsername } = useUser() + const { profile, updateProfile, addUsername, lookupOnUsername, toggleLookupConsent } = useUser() const { publicKey } = useThresholdKey() const [updatingProfile, setUpdatingProfile] = useState(false) const [showUsernameForm, setShowUsernameForm] = useState(false) const [username, setUsername] = useState("") const [usernameData, setUsernameData] = useState({ error: "", loading: false }) const formik = useFormik({ - initialValues:{ + initialValues: { firstName: profile?.firstName || "", lastName: profile?.lastName || "" // email: profile?.email || "" @@ -396,90 +396,89 @@ const ProfileView = () => {
} - {profile?.username - ?
- - Username - - - This username is public - -
- -
-
- :
- - Username - - {showUsernameForm - ?
- - Usernames are public and can't be changed after creation. - -
- -
- -
- +
+ {profile?.username + ? <> + + Username + +
+
- :
- - - You haven't set a username yet. - - {" "} - setShowUsernameForm(true)} + + : <> + + Username + + {showUsernameForm + ?
+ - Add a username - - -
- } -
- } + Usernames are public and can't be changed after creation. + +
+ +
+ +
+ +
+ :
+ + + You haven't set a username yet. + + {" "} + setShowUsernameForm(true)} + > + Add a username + + +
+ } + + } + +
@@ -636,7 +635,7 @@ const ProfileView = () => { > Language - +
diff --git a/packages/files-ui/src/Components/SharingExplainerModal.tsx b/packages/files-ui/src/Components/SharingExplainerModal.tsx index 39c97327a7..11ba933f11 100644 --- a/packages/files-ui/src/Components/SharingExplainerModal.tsx +++ b/packages/files-ui/src/Components/SharingExplainerModal.tsx @@ -24,9 +24,6 @@ const useStyles = makeStyles( textAlign: "center", marginBottom: constants.generalUnit * 3 }, - modalInner: { - maxWidth: "400px !important" - }, buttonLink: { outline: "none", textDecoration: "underline", @@ -147,7 +144,6 @@ const SharingExplainerModal = ({ showModal, onHide }: Props) => { return ( { let encryptionKey = "" - switch(bucket.type) { + switch (bucket.type) { case "csf": case "trash": { encryptionKey = personalEncryptionKey @@ -168,7 +168,8 @@ const FilesProvider = ({ children }: FilesContextProps) => { case "share": { encryptionKey = await getKeyForSharedBucket(bucket) break - }} + } + } return encryptionKey }, [getKeyForSharedBucket, personalEncryptionKey, userId]) @@ -493,7 +494,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { itemsToDownload: FileSystemItem[], currentPath: string, bucketId: string - ): Promise => { + ): Promise => { return await itemsToDownload.reduce( async (acc: Promise, item: FileSystemItem): Promise => { if (item.isFolder) { @@ -566,7 +567,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }) - if(file) { + if (file) { const fileArrayBuffer = await file.arrayBuffer() const fullPath = getPathWithFile(item.path, item.name) const relativeFilePath = getRelativePath(fullPath, currentPath) @@ -662,7 +663,9 @@ const FilesProvider = ({ children }: FilesContextProps) => { return Promise.resolve() } catch (error: any) { console.error(error) - let errorMessage = `${t`An error occurred: `} ${typeof(error) === "string" ? error : error.error.message ? error.error.message : ""}` + let errorMessage = `${t`An error occurred: `} ${typeof (error) === "string" + ? error + : error?.error?.message || ""}` if (axios.isCancel(error)) { errorMessage = t`Downloads cancelled` } @@ -678,7 +681,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }, [getFileContent, addToast, updateToast]) - const createSharedFolder = useCallback(async (name: string, writerUsers?: SharedFolderUser[], readerUsers?: SharedFolderUser[]) => { + const createSharedFolder = useCallback(async (name: string, writerUsers?: SharedFolderUser[], readerUsers?: SharedFolderUser[]) => { if (!publicKey) return const bucketEncryptionKey = Buffer.from( @@ -804,7 +807,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }) - if(file) { + if (file) { await encryptAndUploadFiles( destinationBucket, [new File([file], item.name, { type: item.content_type })], @@ -839,7 +842,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { ? t`${successCount} files transferred successfully, ${totalFileNumber - successCount} failed` : t`${inSharedBucket ? "Copying" : "Sharing"} failed`, type: successCount ? "success" : "error", - progress: undefined, + progress: undefined, isClosable: true }, true) setTransfersInProgress(false) @@ -853,15 +856,14 @@ const FilesProvider = ({ children }: FilesContextProps) => { : t`${inSharedBucket ? "Copying" : "Sharing"} failed` if (axios.isCancel(error)) { errorMessage = successCount - ? t`${ - inSharedBucket ? "Copying" : "Sharing" + ? t`${inSharedBucket ? "Copying" : "Sharing" } cancelled - ${successCount} files ${inSharedBucket ? "copied" : "shared"} successfully` : t`${inSharedBucket ? "Copying" : "Sharing"} cancelled` } updateToast(toastId, { title: errorMessage, type: "error", - progress: undefined, + progress: undefined, isClosable: true }, true) } diff --git a/packages/files-ui/src/Contexts/PosthogContext.tsx b/packages/files-ui/src/Contexts/PosthogContext.tsx index 47be991ef7..3021ce91c7 100644 --- a/packages/files-ui/src/Contexts/PosthogContext.tsx +++ b/packages/files-ui/src/Contexts/PosthogContext.tsx @@ -10,6 +10,7 @@ import { useUser } from "./UserContext" export type PosthogContext = { hasOptedIn: boolean posthogInitialized: boolean + captureEvent: (eventName: string, properties?: posthog.Properties) => void } type PosthogProviderProps = posthog.Config & { @@ -18,7 +19,8 @@ type PosthogProviderProps = posthog.Config & { const PosthogContext = React.createContext({ hasOptedIn: false, - posthogInitialized: false + posthogInitialized: false, + captureEvent: () => undefined }) const useStyles = makeStyles( @@ -116,6 +118,12 @@ const PosthogProvider = ({ children }: PosthogProviderProps) => { } }, [posthogInitialized, touchCookieBanner]) + const captureEvent = useCallback((eventName: string, properties?: posthog.Properties) => { + if (posthogInitialized) { + posthog.capture(eventName, properties) + } + }, [posthogInitialized]) + useEffect(() => { if (profile) { posthogInitialized && posthog.identify(profile.userId) @@ -128,7 +136,8 @@ const PosthogProvider = ({ children }: PosthogProviderProps) => { {children} diff --git a/packages/files-ui/src/Contexts/UserContext.tsx b/packages/files-ui/src/Contexts/UserContext.tsx index 54fb81d282..6a886bfc18 100644 --- a/packages/files-ui/src/Contexts/UserContext.tsx +++ b/packages/files-ui/src/Contexts/UserContext.tsx @@ -19,6 +19,7 @@ export type Profile = { email?: string createdAt?: Date username?: string + lookupConsent: boolean } interface ILocalStore { @@ -39,6 +40,7 @@ interface IUserContext { addUsername: (username: string) => Promise removeUser(): void getProfileTitle(): string + toggleLookupConsent(): Promise } const UserContext = React.createContext(undefined) @@ -71,7 +73,8 @@ const UserProvider = ({ children }: UserContextProps) => { email: profileApiData.email, publicAddress: profileApiData.public_address?.toLowerCase(), createdAt: profileApiData.created_at, - username: profileApiData.username + username: profileApiData.username, + lookupConsent: profileApiData.user_lookup_consent || false } setProfile(profileState) return Promise.resolve() @@ -140,10 +143,11 @@ const UserProvider = ({ children }: UserContextProps) => { }) return Promise.resolve() } catch (error: any) { + console.error(error) return Promise.reject( - Array.isArray(error) && error[0] - ? error[0].message - : "There was an error updating profile." + Array.isArray(error.error.details) + ? error.error.details.map((e: Details) => e.message).join(",") + : t`There was an error when setting username.` ) } } @@ -154,7 +158,7 @@ const UserProvider = ({ children }: UserContextProps) => { if (!profile) return Promise.reject("Profile not initialized") try { - await filesApiClient.updateUser({ + const result = await filesApiClient.updateUser({ first_name: profile.firstName || "", last_name: profile.lastName || "", email: profile.email || "", @@ -163,7 +167,33 @@ const UserProvider = ({ children }: UserContextProps) => { setProfile({ ...profile, - username + username: result.username + }) + return Promise.resolve() + } catch (error: any) { + console.error(error) + return Promise.reject( + Array.isArray(error.error.details) + ? error.error.details.map((e: Details) => e.message).join(",") + : t`There was an error when setting username.` + ) + } + } + + const toggleLookupConsent = async () => { + if (!profile) return Promise.reject("Profile not initialized") + try { + const result = await filesApiClient.updateUser({ + first_name: profile.firstName || "", + last_name: profile.lastName || "", + email: profile.email || "", + username: profile.username, + lookup_consent_flag: !profile.lookupConsent + }) + + setProfile({ + ...profile, + lookupConsent: result.user_lookup_consent || false }) return Promise.resolve() } catch (error: any) { @@ -179,7 +209,7 @@ const UserProvider = ({ children }: UserContextProps) => { const lookupOnUsername = async (username: string) => { if (!profile) return false try { - const alreadyExists = await filesApiClient.lookupUser({ username }) + const alreadyExists = await filesApiClient.lookupUser(username) return !!alreadyExists } catch (error) { console.error(error) @@ -215,7 +245,8 @@ const UserProvider = ({ children }: UserContextProps) => { removeUser, addUsername, lookupOnUsername, - getProfileTitle + getProfileTitle, + toggleLookupConsent }} > {children} diff --git a/packages/files-ui/src/locales/de/messages.po b/packages/files-ui/src/locales/de/messages.po index e9aa28ce22..471472f20c 100644 --- a/packages/files-ui/src/locales/de/messages.po +++ b/packages/files-ui/src/locales/de/messages.po @@ -28,6 +28,9 @@ msgstr "Akzeptieren" msgid "Account" msgstr "Konto" +msgid "Active links" +msgstr "" + msgid "Add Card" msgstr "" @@ -55,12 +58,18 @@ msgstr "" msgid "Adding you to the shared folder..." msgstr "" +msgid "Allow lookup by sharing key, wallet address or username" +msgstr "" + msgid "Amount" msgstr "" msgid "An error occurred:" msgstr "Es ist ein Fehler aufgetreten:" +msgid "Anyone with the link can:" +msgstr "" + msgid "Approve" msgstr "Genehmigen" @@ -163,18 +172,12 @@ msgstr "Weiter mit Web3 Wallet" msgid "Copied!" msgstr "Kopiert!" -msgid "Copy CID" -msgstr "IID kopieren" - msgid "Copy file" msgstr "" msgid "Copy info" msgstr "" -msgid "Copy link" -msgstr "" - msgid "Copy over" msgstr "" @@ -187,22 +190,22 @@ msgstr "" msgid "Create" msgstr "Erstellen" -msgid "Create Folder" -msgstr "" - msgid "Create Shared Folder" msgstr "" msgid "Create a Shared Folder" msgstr "" -msgid "Create a new shared folder" +msgid "Create a sharing link" msgstr "" -msgid "Create folder" -msgstr "Ordner erstellen" +msgid "Create folder & Copy over" +msgstr "" + +msgid "Create folder & Move over" +msgstr "" -msgid "Create new link" +msgid "Create link" msgstr "" msgid "Create your public username in <0>Settings!" @@ -442,15 +445,15 @@ msgstr "Vorschau wird geladen" msgid "Loading your shared folders…" msgstr "" -msgid "Loading..." -msgstr "" - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Sieht aus, als würden Sie sich über einen neuen Browser anmelden. Bitte wählen Sie eine der folgenden Möglichkeiten, um fortzufahren:" msgid "Manage Access" msgstr "" +msgid "Manage Shared Folder" +msgstr "" + msgid "Method" msgstr "" @@ -478,6 +481,9 @@ msgstr "Name zu lang" msgid "New folder" msgstr "Neuer Ordner" +msgid "New shared folder name" +msgstr "" + msgid "Next" msgstr "Nächste" @@ -535,6 +541,12 @@ msgstr "" msgid "Operating system:" msgstr "Betriebssystem:" +msgid "Or Create a new shared folder" +msgstr "" + +msgid "Or Use an existing shared folder" +msgstr "" + msgid "Or confirm by signing into your Files on any browser you’ve used before." msgstr "" @@ -634,6 +646,9 @@ msgstr "Datei umbenennen" msgid "Rename folder" msgstr "Ordner umbenennen" +msgid "Rename shared folder" +msgstr "" + msgid "Report" msgstr "Melden" @@ -724,6 +739,9 @@ msgstr "Geteilt" msgid "Shared Folder Name" msgstr "" +msgid "Shared folder name" +msgstr "" + msgid "Shared folders" msgstr "" @@ -733,9 +751,6 @@ msgstr "" msgid "Sharing files" msgstr "" -msgid "Sharing link" -msgstr "" - msgid "Sign Out" msgstr "Abmelden" @@ -856,9 +871,6 @@ msgstr "" msgid "This username is already taken" msgstr "" -msgid "This username is public" -msgstr "Dieser Benutzername ist öffentlich" - msgid "This website uses cookies" msgstr "" @@ -904,9 +916,6 @@ msgstr "Eine andere Anmeldemethode verwenden" msgid "Use a saved browser" msgstr "Einen gespeicherten Browser verwenden" -msgid "Use an existing shared folder" -msgstr "" - msgid "User {0} is both a reader and writer" msgstr "" @@ -997,7 +1006,7 @@ msgstr "" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "Ihr Wiederherstellungsschlüssel kann zur Wiederherstellung Ihres Kontos anstelle Ihres Sicherungsgeheimsatz verwendet werden." -msgid "edit rights" +msgid "can-edit" msgstr "" msgid "me" @@ -1018,13 +1027,10 @@ msgstr "" msgid "per year" msgstr "" -msgid "read rights" -msgstr "" - msgid "unknown" msgstr "unbekannt" -msgid "with" +msgid "view-only" msgstr "" msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 87d0367081..dfeaf3d75e 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -28,6 +28,9 @@ msgstr "Accept" msgid "Account" msgstr "Account" +msgid "Active links" +msgstr "Active links" + msgid "Add Card" msgstr "Add Card" @@ -55,12 +58,18 @@ msgstr "Add viewers and editors by username, sharing id or Ethereum address." msgid "Adding you to the shared folder..." msgstr "Adding you to the shared folder..." +msgid "Allow lookup by sharing key, wallet address or username" +msgstr "Allow lookup by sharing key, wallet address or username" + msgid "Amount" msgstr "Amount" msgid "An error occurred:" msgstr "An error occurred:" +msgid "Anyone with the link can:" +msgstr "Anyone with the link can:" + msgid "Approve" msgstr "Approve" @@ -163,18 +172,12 @@ msgstr "Continue with Web3 Wallet" msgid "Copied!" msgstr "Copied!" -msgid "Copy CID" -msgstr "Copy CID" - msgid "Copy file" msgstr "Copy file" msgid "Copy info" msgstr "Copy info" -msgid "Copy link" -msgstr "Copy link" - msgid "Copy over" msgstr "Copy over" @@ -187,23 +190,23 @@ msgstr "Copying files" msgid "Create" msgstr "Create" -msgid "Create Folder" -msgstr "Create Folder" - msgid "Create Shared Folder" msgstr "Create Shared Folder" msgid "Create a Shared Folder" msgstr "Create a Shared Folder" -msgid "Create a new shared folder" -msgstr "Create a new shared folder" +msgid "Create a sharing link" +msgstr "Create a sharing link" + +msgid "Create folder & Copy over" +msgstr "Create folder & Copy over" -msgid "Create folder" -msgstr "Create folder" +msgid "Create folder & Move over" +msgstr "Create folder & Move over" -msgid "Create new link" -msgstr "Create new link" +msgid "Create link" +msgstr "Create link" msgid "Create your public username in <0>Settings!" msgstr "Create your public username in <0>Settings!" @@ -445,15 +448,15 @@ msgstr "Loading preview" msgid "Loading your shared folders…" msgstr "Loading your shared folders…" -msgid "Loading..." -msgstr "Loading..." - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgid "Manage Access" msgstr "Manage Access" +msgid "Manage Shared Folder" +msgstr "Manage Shared Folder" + msgid "Method" msgstr "Method" @@ -481,6 +484,9 @@ msgstr "Name too long" msgid "New folder" msgstr "New folder" +msgid "New shared folder name" +msgstr "New shared folder name" + msgid "Next" msgstr "Next" @@ -538,6 +544,12 @@ msgstr "Oops! You need to pay for this month to upload more content." msgid "Operating system:" msgstr "Operating system:" +msgid "Or Create a new shared folder" +msgstr "Or Create a new shared folder" + +msgid "Or Use an existing shared folder" +msgstr "Or Use an existing shared folder" + msgid "Or confirm by signing into your Files on any browser you’ve used before." msgstr "Or confirm by signing into your Files on any browser you’ve used before." @@ -637,6 +649,9 @@ msgstr "Rename file" msgid "Rename folder" msgstr "Rename folder" +msgid "Rename shared folder" +msgstr "Rename shared folder" + msgid "Report" msgstr "Report" @@ -727,6 +742,9 @@ msgstr "Shared" msgid "Shared Folder Name" msgstr "Shared Folder Name" +msgid "Shared folder name" +msgstr "Shared folder name" + msgid "Shared folders" msgstr "Shared folders" @@ -736,9 +754,6 @@ msgstr "Shared with" msgid "Sharing files" msgstr "Sharing files" -msgid "Sharing link" -msgstr "Sharing link" - msgid "Sign Out" msgstr "Sign Out" @@ -859,9 +874,6 @@ msgstr "This link is not valid any more." msgid "This username is already taken" msgstr "This username is already taken" -msgid "This username is public" -msgstr "This username is public" - msgid "This website uses cookies" msgstr "This website uses cookies" @@ -907,9 +919,6 @@ msgstr "Use a different login method" msgid "Use a saved browser" msgstr "Use a saved browser" -msgid "Use an existing shared folder" -msgstr "Use an existing shared folder" - msgid "User {0} is both a reader and writer" msgstr "User {0} is both a reader and writer" @@ -1000,8 +1009,8 @@ msgstr "Your plan" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "Your recovery key can be used to restore your account in place of your backup secret phrase." -msgid "edit rights" -msgstr "edit rights" +msgid "can-edit" +msgstr "can-edit" msgid "me" msgstr "me" @@ -1021,14 +1030,11 @@ msgstr "per week" msgid "per year" msgstr "per year" -msgid "read rights" -msgstr "read rights" - msgid "unknown" msgstr "unknown" -msgid "with" -msgstr "with" +msgid "view-only" +msgstr "view-only" msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" msgstr "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" diff --git a/packages/files-ui/src/locales/es/messages.po b/packages/files-ui/src/locales/es/messages.po index ae198a6697..6fe89ddda9 100644 --- a/packages/files-ui/src/locales/es/messages.po +++ b/packages/files-ui/src/locales/es/messages.po @@ -29,6 +29,9 @@ msgstr "" msgid "Account" msgstr "Cuenta" +msgid "Active links" +msgstr "" + msgid "Add Card" msgstr "" @@ -56,12 +59,18 @@ msgstr "" msgid "Adding you to the shared folder..." msgstr "" +msgid "Allow lookup by sharing key, wallet address or username" +msgstr "" + msgid "Amount" msgstr "" msgid "An error occurred:" msgstr "" +msgid "Anyone with the link can:" +msgstr "" + msgid "Approve" msgstr "Aprobar" @@ -164,18 +173,12 @@ msgstr "Continuar con Monedero Web3 " msgid "Copied!" msgstr "Copiado!" -msgid "Copy CID" -msgstr "Copiar CID" - msgid "Copy file" msgstr "" msgid "Copy info" msgstr "" -msgid "Copy link" -msgstr "" - msgid "Copy over" msgstr "" @@ -188,22 +191,22 @@ msgstr "" msgid "Create" msgstr "Crear" -msgid "Create Folder" -msgstr "" - msgid "Create Shared Folder" msgstr "" msgid "Create a Shared Folder" msgstr "" -msgid "Create a new shared folder" +msgid "Create a sharing link" msgstr "" -msgid "Create folder" -msgstr "Crear Carpeta" +msgid "Create folder & Copy over" +msgstr "" + +msgid "Create folder & Move over" +msgstr "" -msgid "Create new link" +msgid "Create link" msgstr "" msgid "Create your public username in <0>Settings!" @@ -446,15 +449,15 @@ msgstr "Cargando vista previa" msgid "Loading your shared folders…" msgstr "" -msgid "Loading..." -msgstr "" - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Parece que está iniciando sesión desde un nuevo navegador. Elija una de las siguientes opciones para continuar:" msgid "Manage Access" msgstr "" +msgid "Manage Shared Folder" +msgstr "" + msgid "Method" msgstr "" @@ -482,6 +485,9 @@ msgstr "" msgid "New folder" msgstr "Nuevo Folder" +msgid "New shared folder name" +msgstr "" + msgid "Next" msgstr "Próximo" @@ -539,6 +545,12 @@ msgstr "" msgid "Operating system:" msgstr "Sistema operativo:" +msgid "Or Create a new shared folder" +msgstr "" + +msgid "Or Use an existing shared folder" +msgstr "" + msgid "Or confirm by signing into your Files on any browser you’ve used before." msgstr "O confirme iniciando sesión en sus Archivos en cualquier navegador que haya usado antes." @@ -638,6 +650,9 @@ msgstr "" msgid "Rename folder" msgstr "" +msgid "Rename shared folder" +msgstr "" + msgid "Report" msgstr "" @@ -728,6 +743,9 @@ msgstr "" msgid "Shared Folder Name" msgstr "" +msgid "Shared folder name" +msgstr "" + msgid "Shared folders" msgstr "" @@ -737,9 +755,6 @@ msgstr "" msgid "Sharing files" msgstr "" -msgid "Sharing link" -msgstr "" - msgid "Sign Out" msgstr "Desconectar" @@ -860,9 +875,6 @@ msgstr "" msgid "This username is already taken" msgstr "" -msgid "This username is public" -msgstr "" - msgid "This website uses cookies" msgstr "" @@ -908,9 +920,6 @@ msgstr "Utilice un método de inicio de sesión diferente" msgid "Use a saved browser" msgstr "Utilice un navegador guardado" -msgid "Use an existing shared folder" -msgstr "" - msgid "User {0} is both a reader and writer" msgstr "" @@ -1001,7 +1010,7 @@ msgstr "" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "" -msgid "edit rights" +msgid "can-edit" msgstr "" msgid "me" @@ -1022,13 +1031,10 @@ msgstr "" msgid "per year" msgstr "" -msgid "read rights" -msgstr "" - msgid "unknown" msgstr "" -msgid "with" +msgid "view-only" msgstr "" msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index 71d2681c90..936d849039 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-04-23 11:05+0200\n" -"PO-Revision-Date: 2021-10-30 12:33+0000\n" +"PO-Revision-Date: 2021-11-21 00:53+0000\n" "Last-Translator: J. Lavoie \n" "Language-Team: French \n" "Language: fr\n" @@ -11,7 +11,7 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.9-dev\n" +"X-Generator: Weblate 4.9.1\n" "Mime-Version: 1.0\n" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" @@ -29,6 +29,9 @@ msgstr "Accepter" msgid "Account" msgstr "Compte" +msgid "Active links" +msgstr "Liens actifs" + msgid "Add Card" msgstr "" @@ -56,12 +59,18 @@ msgstr "Ajoutez des personnes pouvant visualiser ou afficher par nom d'utilisate msgid "Adding you to the shared folder..." msgstr "Je vous ajoute au dossier partagé…" +msgid "Allow lookup by sharing key, wallet address or username" +msgstr "Permettre la recherche par clé de partage, adresse du portefeuille ou nom d'utilisateur" + msgid "Amount" msgstr "" msgid "An error occurred:" msgstr "Une erreur s'est produite :" +msgid "Anyone with the link can:" +msgstr "Toute personne ayant le lien peut le faire :" + msgid "Approve" msgstr "Accepter" @@ -164,18 +173,12 @@ msgstr "Continuer avec un wallet Web3" msgid "Copied!" msgstr "Copié !" -msgid "Copy CID" -msgstr "Copier le CID" - msgid "Copy file" msgstr "Copier le fichier" msgid "Copy info" msgstr "Copier les infos" -msgid "Copy link" -msgstr "Copier le lien" - msgid "Copy over" msgstr "Copier" @@ -188,23 +191,23 @@ msgstr "Copie de fichiers" msgid "Create" msgstr "Créer" -msgid "Create Folder" -msgstr "Créer un dossier" - msgid "Create Shared Folder" msgstr "Créer un dossier partagé" msgid "Create a Shared Folder" msgstr "Créer un dossier partagé" -msgid "Create a new shared folder" -msgstr "Créer un nouveau dossier partagé" +msgid "Create a sharing link" +msgstr "Créer un lien de partage" -msgid "Create folder" -msgstr "Créer un dossier" +msgid "Create folder & Copy over" +msgstr "Créer un dossier et le copier" -msgid "Create new link" -msgstr "Créer un nouveau lien" +msgid "Create folder & Move over" +msgstr "Créer un dossier et le déplacer" + +msgid "Create link" +msgstr "Créer un lien" msgid "Create your public username in <0>Settings!" msgstr "Créez votre nom d'utilisateur public dans <0>Paramètres !" @@ -387,7 +390,7 @@ msgid "Go to Payments" msgstr "" msgid "Go to login" -msgstr "" +msgstr "Aller à la connexion" msgid "Got it" msgstr "Compris" @@ -446,15 +449,15 @@ msgstr "Chargement de l’aperçu" msgid "Loading your shared folders…" msgstr "Chargement de vos dossiers partagés…" -msgid "Loading..." -msgstr "Chargement…" - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Il semble que vous vous connectiez à partir d’un nouveau navigateur. Veuillez choisir une des options suivantes pour continuer :" msgid "Manage Access" msgstr "Gérer l’accès" +msgid "Manage Shared Folder" +msgstr "Gérer le dossier partagé" + msgid "Method" msgstr "" @@ -482,6 +485,9 @@ msgstr "Le nom est trop long" msgid "New folder" msgstr "Nouveau dossier" +msgid "New shared folder name" +msgstr "Nouveau nom de dossier partagé" + msgid "Next" msgstr "Suivant" @@ -495,10 +501,10 @@ msgid "No file to download." msgstr "Aucun fichier à télécharger." msgid "No files to copy" -msgstr "" +msgstr "Aucun fichier à copier" msgid "No files to share" -msgstr "" +msgstr "Aucun fichier à partager" msgid "No files to show" msgstr "" @@ -539,6 +545,12 @@ msgstr "" msgid "Operating system:" msgstr "Système d’exploitation :" +msgid "Or Create a new shared folder" +msgstr "Ou créer un nouveau dossier partagé" + +msgid "Or Use an existing shared folder" +msgstr "Ou utiliser un dossier partagé existant" + msgid "Or confirm by signing into your Files on any browser you’ve used before." msgstr "Ou accepte la requête de connexion depuis n’importe quel appareil ou navigateur utilisé auparavant." @@ -638,6 +650,9 @@ msgstr "Renommer le fichier" msgid "Rename folder" msgstr "Renommer le dossier" +msgid "Rename shared folder" +msgstr "" + msgid "Report" msgstr "Signaler" @@ -728,6 +743,9 @@ msgstr "Partagé" msgid "Shared Folder Name" msgstr "Nom du dossier partagé" +msgid "Shared folder name" +msgstr "Nom du dossier partagé" + msgid "Shared folders" msgstr "Dossiers partagés" @@ -737,9 +755,6 @@ msgstr "Partagé avec" msgid "Sharing files" msgstr "Partage des fichiers" -msgid "Sharing link" -msgstr "Lien de partage" - msgid "Sign Out" msgstr "Se déconnecter" @@ -813,7 +828,7 @@ msgid "The files are already in this folder" msgstr "Les fichiers sont déjà dans ce dossier" msgid "The link you typed in looks malformed. Please verify it." -msgstr "" +msgstr "Le lien que vous avez tapé semble malformé. Veuillez le vérifier." msgid "The username is too long" msgstr "Le nom d'utilisateur est trop long" @@ -840,7 +855,7 @@ msgid "There was an error getting the preview." msgstr "Une erreur s’est produite lors de la génération de l’aperçu." msgid "There was an error moving your data" -msgstr "" +msgstr "Une erreur s'est produite lors du transfert de vos données" msgid "There was an error restoring your data" msgstr "Une erreur s'est produite lors de la restauration de vos données" @@ -849,20 +864,17 @@ msgid "There was an error when setting username." msgstr "Une erreur s'est produite lors de la définition du nom d'utilisateur." msgid "This is the free product." -msgstr "Une erreur s'est produite lors du transfert de vos données" +msgstr "" msgid "This link is marlformed. Please verify that you copy/pasted it correctly." -msgstr "" +msgstr "Ce lien est marlformé. Veuillez vérifier que vous l'avez copié/collé correctement." msgid "This link is not valid any more." -msgstr "" +msgstr "Ce lien n'est plus valide." msgid "This username is already taken" msgstr "Ce nom d’utilisateur est déjà pris" -msgid "This username is public" -msgstr "Ce nom d’utilisateur est public" - msgid "This website uses cookies" msgstr "Ce site web utilise des cookies" @@ -908,9 +920,6 @@ msgstr "Utilisez une méthode de connexion différente" msgid "Use a saved browser" msgstr "Utiliser un navigateur enregistré" -msgid "Use an existing shared folder" -msgstr "Utiliser un dossier partagé existant" - msgid "User {0} is both a reader and writer" msgstr "L'utilisateur {0} est dans les auteurs et lecteurs" @@ -936,7 +945,7 @@ msgid "Verification code sent!" msgstr "Code de vérification envoyé !" msgid "Verifying the link..." -msgstr "" +msgstr "Vérification du lien…" msgid "View folder" msgstr "Voir le dossier" @@ -999,10 +1008,10 @@ msgid "Your plan" msgstr "" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." -msgstr "Votre clé de récupération peut être utilisée pour restaurer votre compte à la place de votre phrase de sauvegarde secrète." +msgstr "" -msgid "edit rights" -msgstr "droits de modification" +msgid "can-edit" +msgstr "peut-modifier" msgid "me" msgstr "moi" @@ -1022,14 +1031,11 @@ msgstr "" msgid "per year" msgstr "" -msgid "read rights" -msgstr "droits de lecture" - msgid "unknown" msgstr "inconnu" -msgid "with" -msgstr "avec" +msgid "view-only" +msgstr "affichage seul" msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" msgstr "{0, plural, one {Téléchargement de {1} fichier} other {Téléchargement de {2} fichiers}}" diff --git a/packages/files-ui/src/locales/no/messages.po b/packages/files-ui/src/locales/no/messages.po index 181aa2fac7..421ffb8d44 100644 --- a/packages/files-ui/src/locales/no/messages.po +++ b/packages/files-ui/src/locales/no/messages.po @@ -28,6 +28,9 @@ msgstr "" msgid "Account" msgstr "Konto" +msgid "Active links" +msgstr "" + msgid "Add Card" msgstr "" @@ -55,12 +58,18 @@ msgstr "" msgid "Adding you to the shared folder..." msgstr "" +msgid "Allow lookup by sharing key, wallet address or username" +msgstr "" + msgid "Amount" msgstr "" msgid "An error occurred:" msgstr "" +msgid "Anyone with the link can:" +msgstr "" + msgid "Approve" msgstr "Godkjenn" @@ -163,18 +172,12 @@ msgstr "Fortsett med Web3-lommebok" msgid "Copied!" msgstr "Kopiert!" -msgid "Copy CID" -msgstr "Kopier CID" - msgid "Copy file" msgstr "" msgid "Copy info" msgstr "" -msgid "Copy link" -msgstr "" - msgid "Copy over" msgstr "" @@ -187,22 +190,22 @@ msgstr "" msgid "Create" msgstr "Opprett" -msgid "Create Folder" -msgstr "" - msgid "Create Shared Folder" msgstr "" msgid "Create a Shared Folder" msgstr "" -msgid "Create a new shared folder" +msgid "Create a sharing link" msgstr "" -msgid "Create folder" -msgstr "Opprett mappe" +msgid "Create folder & Copy over" +msgstr "" + +msgid "Create folder & Move over" +msgstr "" -msgid "Create new link" +msgid "Create link" msgstr "" msgid "Create your public username in <0>Settings!" @@ -442,15 +445,15 @@ msgstr "" msgid "Loading your shared folders…" msgstr "" -msgid "Loading..." -msgstr "" - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "" msgid "Manage Access" msgstr "" +msgid "Manage Shared Folder" +msgstr "" + msgid "Method" msgstr "" @@ -478,6 +481,9 @@ msgstr "Navnet er for langt" msgid "New folder" msgstr "Ny mappe" +msgid "New shared folder name" +msgstr "" + msgid "Next" msgstr "Neste" @@ -535,6 +541,12 @@ msgstr "" msgid "Operating system:" msgstr "Operativsystem:" +msgid "Or Create a new shared folder" +msgstr "" + +msgid "Or Use an existing shared folder" +msgstr "" + msgid "Or confirm by signing into your Files on any browser you’ve used before." msgstr "" @@ -634,6 +646,9 @@ msgstr "" msgid "Rename folder" msgstr "" +msgid "Rename shared folder" +msgstr "" + msgid "Report" msgstr "" @@ -724,6 +739,9 @@ msgstr "Delt" msgid "Shared Folder Name" msgstr "" +msgid "Shared folder name" +msgstr "" + msgid "Shared folders" msgstr "" @@ -733,9 +751,6 @@ msgstr "" msgid "Sharing files" msgstr "" -msgid "Sharing link" -msgstr "" - msgid "Sign Out" msgstr "Logg ut" @@ -856,9 +871,6 @@ msgstr "" msgid "This username is already taken" msgstr "" -msgid "This username is public" -msgstr "Dette brukernavnet er offentlig" - msgid "This website uses cookies" msgstr "" @@ -904,9 +916,6 @@ msgstr "Bruk en annen innloggingsmetode" msgid "Use a saved browser" msgstr "" -msgid "Use an existing shared folder" -msgstr "" - msgid "User {0} is both a reader and writer" msgstr "" @@ -997,7 +1006,7 @@ msgstr "" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "" -msgid "edit rights" +msgid "can-edit" msgstr "" msgid "me" @@ -1018,13 +1027,10 @@ msgstr "" msgid "per year" msgstr "" -msgid "read rights" -msgstr "" - msgid "unknown" msgstr "" -msgid "with" +msgid "view-only" msgstr "" msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" diff --git a/packages/gaming-ui/package.json b/packages/gaming-ui/package.json index bc30f7d52c..22f8021c47 100644 --- a/packages/gaming-ui/package.json +++ b/packages/gaming-ui/package.json @@ -6,7 +6,7 @@ "@babel/core": "^7.12.10", "@babel/runtime": "^7.0.0", "@chainsafe/browser-storage-hooks": "^1.0.1", - "@chainsafe/files-api-client": "^1.18.19", + "@chainsafe/files-api-client": "^1.18.20", "@chainsafe/web3-context": "1.1.4", "@lingui/core": "^3.7.2", "@lingui/react": "^3.7.2", @@ -56,7 +56,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.6", + "cypress": "^9.0", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/gaming-ui/src/Components/GamingRoutes.tsx b/packages/gaming-ui/src/Components/GamingRoutes.tsx index 22d434014a..c8fb48f479 100644 --- a/packages/gaming-ui/src/Components/GamingRoutes.tsx +++ b/packages/gaming-ui/src/Components/GamingRoutes.tsx @@ -31,12 +31,6 @@ const GamingRoutes = () => { component={DashboardPage} redirectPath={ROUTE_LINKS.Landing} /> - { }).finally (() => setIsSubmitEmailLoading(false)) }, [gamingApiClient]) - const onSubmitNonce = useCallback((values: {nonce: string}) => { + const onSubmitNonce = useCallback(({ nonce }: {nonce: string}) => { if (!email) return + setIsSubmitNonceLoading(true) setError(undefined) + gamingApiClient.postIdentityEmailToken({ - email: email, - nonce: values.nonce + email, + nonce: nonce.trim() }).then(async (data) => { await login("email", { token: data || "", email }) }).catch ((e) => { diff --git a/packages/storage-ui/package.json b/packages/storage-ui/package.json index a912064d6b..e7ae0c3fba 100644 --- a/packages/storage-ui/package.json +++ b/packages/storage-ui/package.json @@ -6,7 +6,7 @@ "@babel/core": "^7.12.10", "@babel/runtime": "^7.0.0", "@chainsafe/browser-storage-hooks": "^1.0.1", - "@chainsafe/files-api-client": "^1.18.19", + "@chainsafe/files-api-client": "^1.18.20", "@chainsafe/web3-context": "1.1.4", "@lingui/core": "^3.7.2", "@lingui/react": "^3.7.2", @@ -64,7 +64,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.6", + "cypress": "^9.0", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/storage-ui/src/Components/Modules/LoginModule.tsx b/packages/storage-ui/src/Components/Modules/LoginModule.tsx index f05306d874..f6cbe3da5d 100644 --- a/packages/storage-ui/src/Components/Modules/LoginModule.tsx +++ b/packages/storage-ui/src/Components/Modules/LoginModule.tsx @@ -32,7 +32,7 @@ const useStyles = makeStyles( alignItems: "center", borderRadius: 6, [breakpoints.up("md")]:{ - minHeight: "64vh", + minHeight: "72vh", justifyContent: "space-between", width: 440 }, diff --git a/packages/storage-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx b/packages/storage-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx index 77c3d0cb51..44da20a907 100644 --- a/packages/storage-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx +++ b/packages/storage-ui/src/Components/Modules/LoginModule/PasswordlessEmail.tsx @@ -93,13 +93,15 @@ const PasswordlessEmail = ({ resetLogin, email }: IPasswordlessEmail) => { const [hasEmailResent, setHasEmailResent] = useState(false) const [error, setError] = useState() - const onSubmitNonce = useCallback((values) => { + const onSubmitNonce = useCallback(({ nonce }: {nonce: string}) => { if (!email) return + setIsSubmitNonceLoading(true) setError(undefined) + storageApiClient.postIdentityEmailToken({ - email: email, - nonce: values.nonce + email, + nonce: nonce.trim() }).then(async (data) => { await login("email", { token: data || "", email }) }).catch ((e) => { diff --git a/yarn.lock b/yarn.lock index 41eb650088..ce8b61ac3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1925,10 +1925,10 @@ resolved "https://registry.yarnpkg.com/@chainsafe/browser-storage-hooks/-/browser-storage-hooks-1.0.1.tgz#26d32cde1999914db755a631e2643823c54959f7" integrity sha512-Q4b5gQAZnsRXKeADspd5isqfwwhhXjDk70y++YadufA6EZ3tf340oW0OVszp74KaGEw+CAYFGQR4X7bzpZ3x9Q== -"@chainsafe/files-api-client@^1.18.19": - version "1.18.19" - resolved "https://registry.yarnpkg.com/@chainsafe/files-api-client/-/files-api-client-1.18.19.tgz#2093d508d55b71abb3ec9cfb9c219fa62a4494c4" - integrity sha512-fFIGJg3XcS3hrNq4+UxjOEYYYiOtrYLTKxn0c/jXrlDFxA+Zjsn6G6d5O+KHJVfqoinRIyrRrMPXBHpBlFHpZg== +"@chainsafe/files-api-client@^1.18.20": + version "1.18.20" + resolved "https://registry.yarnpkg.com/@chainsafe/files-api-client/-/files-api-client-1.18.20.tgz#5b184946acfa4026b21c95faa2f0c1c9d9999481" + integrity sha512-OQN4V2bUe0981RsBirjNA2YrkaMzAV/7oWSzBwN27ejhAbM6fRbxDEYTZfLRVuick91/JfM2/TbKc3vHYQRDdg== dependencies: "@redocly/openapi-cli" "^1.0.0-beta.58" "@redocly/openapi-core" "^1.0.0-beta.58" @@ -2017,10 +2017,10 @@ react-qr-reader "^2.2.1" rxjs "^6.6.3" -"@cypress/request@^2.88.6": - version "2.88.6" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9" - integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ== +"@cypress/request@^2.88.7": + version "2.88.7" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.7.tgz#386d960ab845a96953723348088525d5a75aaac4" + integrity sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -9991,9 +9991,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + version "1.6.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" + integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -10787,12 +10787,12 @@ cypress-pipe@^2.0.0: resolved "https://registry.yarnpkg.com/cypress-pipe/-/cypress-pipe-2.0.0.tgz#577df7a70a8603d89a96dfe4092a605962181af8" integrity sha512-KW9s+bz4tFLucH3rBGfjW+Q12n7S4QpUSSyxiGrgPOfoHlbYWzAGB3H26MO0VTojqf9NVvfd5Kt0MH5XMgbfyg== -cypress@^8.6: - version "8.6.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.6.0.tgz#8d02fa58878b37cfc45bbfce393aa974fa8a8e22" - integrity sha512-F7qEK/6Go5FsqTueR+0wEw2vOVKNgk5847Mys8vsWkzPoEKdxs+7N9Y1dit+zhaZCLtMPyrMwjfA53ZFy+lSww== +cypress@^9.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.0.0.tgz#8c496f7f350e611604cc2f77b663fb81d0c235d2" + integrity sha512-/93SWBZTw7BjFZ+I9S8SqkFYZx7VhedDjTtRBmXO0VzTeDbmxgK/snMJm/VFjrqk/caWbI+XY4Qr80myDMQvYg== dependencies: - "@cypress/request" "^2.88.6" + "@cypress/request" "^2.88.7" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" "@types/sinonjs__fake-timers" "^6.0.2" @@ -10827,7 +10827,6 @@ cypress@^8.6: ospath "^1.2.2" pretty-bytes "^5.6.0" proxy-from-env "1.0.0" - ramda "~0.27.1" request-progress "^3.0.0" supports-color "^8.1.1" tmp "~0.2.1" @@ -11213,9 +11212,9 @@ dns-over-http-resolver@^1.0.0: receptacle "^1.3.2" dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -14404,9 +14403,9 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hotkeys-js@3.8.1: version "3.8.1" @@ -17410,9 +17409,9 @@ merge-class-names@^1.1.1: integrity sha512-k0Qaj36VBpKgdc8c188LEZvo6v/zzry/FUufwopWbMSp6/knfVFU/KIB55/hJjeIpg18IH2WskXJCRnM/1BrdQ== merge-deep@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" - integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== + version "3.0.3" + resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.3.tgz#1a2b2ae926da8b2ae93a0ac15d90cd1922766003" + integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA== dependencies: arr-union "^3.1.0" clone-deep "^0.2.4" @@ -18961,9 +18960,9 @@ path-key@^3.0.0, path-key@^3.1.0: integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" @@ -20411,7 +20410,7 @@ ramda@^0.21.0: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= -ramda@^0.27.1, ramda@~0.27.1: +ramda@^0.27.1: version "0.27.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== @@ -22800,9 +22799,9 @@ sshpk@^1.7.0: tweetnacl "~0.14.0" ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== dependencies: figgy-pudding "^3.5.1" @@ -23559,9 +23558,9 @@ tmp@~0.2.1: rimraf "^3.0.0" tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-arraybuffer@^1.0.0: version "1.0.1" @@ -24107,9 +24106,9 @@ url-loader@2.3.0, url-loader@^2.0.1: schema-utils "^2.5.0" url-parse@^1.4.3: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + version "1.5.3" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" + integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0"