diff --git a/packages/common-components/src/Router/ConditionalRoute.tsx b/packages/common-components/src/Router/ConditionalRoute.tsx index 004c2131a4..5237a38d92 100644 --- a/packages/common-components/src/Router/ConditionalRoute.tsx +++ b/packages/common-components/src/Router/ConditionalRoute.tsx @@ -21,7 +21,7 @@ const ConditionalRoute: React.FC = ({ exact, ...rest }) => { - const { state, pathname } = useLocation<{from?: string} | undefined>() + const { state, pathname, hash } = useLocation<{from?: string} | undefined>() const from = (state as any)?.from return = ({ ? // this may be converted into loading diff --git a/packages/files-ui/package.json b/packages/files-ui/package.json index bcaa1044e7..47de55c414 100644 --- a/packages/files-ui/package.json +++ b/packages/files-ui/package.json @@ -31,6 +31,8 @@ "ethers": "^5.4.3", "fflate": "^0.7.1", "formik": "^2.2.5", + "jsrsasign": "^10.4.1", + "key-encoder": "^2.0.3", "heic-convert": "^1.2.4", "mime-matcher": "^1.0.5", "posthog-js": "^1.13.10", @@ -62,6 +64,7 @@ "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.5.0", "@types/jest": "^26.0.16", + "@types/jsrsasign": "^8.0.13", "@types/node": "^14.14.10", "@types/react": "^17.0.0", "@types/react-beforeunload": "^2.1.0", diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index 74b404c6fa..70ae37185a 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -10,8 +10,12 @@ import PurchasePlanPage from "./Pages/PurchasePlanPage" import { useThresholdKey } from "../Contexts/ThresholdKeyContext" import ShareFilesPage from "./Pages/SharedFilesPage" import SharedFoldersOverview from "./Modules/FileBrowsers/SharedFoldersOverview" +import { NonceResponsePermission } from "@chainsafe/files-api-client" +import LinkSharingLanding from "./Pages/LinkSharingLanding" export const SETTINGS_BASE = "/settings" +export const LINK_SHARING_BASE = "/link-sharing" + export const ROUTE_LINKS = { Landing: "/", PrivacyPolicy: "https://files.chainsafe.io/privacy-policy", @@ -27,6 +31,8 @@ export const ROUTE_LINKS = { UserSurvey: "https://calendly.com/colinschwarz/chainsafe-files-chat", SharedFolders: "/shared-overview", SharedFolderBrowserRoot: "/shared", + SharingLink: (permission: NonceResponsePermission, jwt: string, bucketEncryptionKey: string) => + `${LINK_SHARING_BASE}/${permissionPath(permission)}/${encodeURIComponent(jwt)}#${encodeURIComponent(bucketEncryptionKey)}`, SharedFolderExplorer: (bucketId: string, rawCurrentPath: string) => { // bucketId should not have a / at the end // rawCurrentPath can be empty, or / @@ -37,6 +43,7 @@ export const ROUTE_LINKS = { TeamSignup: "https://shrl.ink/cgQy" } +export const permissionPath = (permission: NonceResponsePermission) => permission === "read" ? "read" : "edit" export const SETTINGS_PATHS = ["profile", "plan", "security"] as const export type SettingsPath = typeof SETTINGS_PATHS[number] @@ -48,6 +55,12 @@ const FilesRoutes = () => { [isLoggedIn, isNewDevice, publicKey, secured, shouldInitializeAccount]) return ( + { redirectPath={ROUTE_LINKS.Landing} /> { @@ -102,6 +104,9 @@ const useStyles = makeStyles( errorText: { marginLeft: constants.generalUnit * 1.5, color: palette.error.main + }, + sharingLink: { + padding: constants.generalUnit * 1.25 } }) } @@ -275,6 +280,17 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi noOptionsMessage={t`No user found for this query.`} /> + {mode === "edit" && !!bucketToEdit && ( +
+ + Sharing link + + +
+ )} { + return createStyles({ + root: { + }, + options: { + backgroundColor: constants.header.optionsBackground, + color: constants.header.optionsTextColor, + border: `1px solid ${constants.header.optionsBorder}`, + minWidth: 145 + }, + menuItem: { + width: "100%", + display: "flex", + flexDirection: "row", + alignItems: "center", + color: constants.header.menuItemTextColor, + "& svg": { + width: constants.generalUnit * 2, + height: constants.generalUnit * 2, + marginRight: constants.generalUnit, + fill: palette.additional["gray"][7], + stroke: palette.additional["gray"][7] + } + }, + icon: { + "& svg": { + fill: constants.header.iconColor + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + fill: constants.fileSystemItemRow.menuIcon + }, + permissionDropdown: { + padding: `0px ${constants.generalUnit}px`, + backgroundColor: palette.additional["gray"][5], + marginLeft: constants.generalUnit + }, + createLink: { + display: "flex", + alignItems: "center", + margin: `${constants.generalUnit * 2.5}px 0` + }, + createLinkButton: { + marginRight: constants.generalUnit + }, + dropdownTitle: { + padding: `${constants.generalUnit * 0.75}px ${constants.generalUnit}px` + } + }) + } +) + +interface Props { + bucketId: string + bucketEncryptionKey: string +} + +const readRights = t`read rights` +const editRights = t`edit rights` +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 refreshNonces = useCallback(() => { + setIsLoading(true) + filesApiClient.getAllNonces() + .then((res) => { + const noncesForCurrentBucket = res.filter(n => n.bucket_id === bucketId) + setNonces(noncesForCurrentBucket) + }) + .catch(console.error) + .finally(() => setIsLoading(false)) + }, [bucketId, filesApiClient]) + + useEffect(() => { + refreshNonces() + }, [filesApiClient, refreshNonces]) + + const onCreateNonce = useCallback(() => { + + setIsLoading(true) + + return filesApiClient + .createNonce({ bucket_id: bucketId, permission: newLinkPermission }) + .catch(console.error) + .finally(() => { + setIsLoading(false) + refreshNonces() + }) + }, [bucketId, filesApiClient, newLinkPermission, refreshNonces]) + + return ( +
+
+ + with + setNewLinkPermission("read"), + contents: ( +
+ {readRights} +
+ ) + }, + { + onClick: () => setNewLinkPermission("write"), + contents: ( +
+ {editRights} +
+ ) + } + ]} + /> +
+ { + isLoading && + } + { + !isLoading && nonces.length > 0 && nonces.map((nonce) => + + ) + } +
+ ) +} + +export default LinkList diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx new file mode 100644 index 0000000000..882f8c64fd --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/LinkSharing/SharingLink.tsx @@ -0,0 +1,145 @@ + +import { Button, DeleteSvg, 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" +import React, { useCallback, useEffect, useState } from "react" +import { useFilesApi } from "../../../../Contexts/FilesApiContext" +import { useThresholdKey } from "../../../../Contexts/ThresholdKeyContext" +import { CSFTheme } from "../../../../Themes/types" +import { ROUTE_LINKS } from "../../../FilesRoutes" +import { translatedPermission } from "./LinkList" + +const useStyles = makeStyles( + ({ constants }: CSFTheme) => { + return createStyles({ + root: { + display: "flex", + marginBottom: constants.generalUnit * 0.5 + }, + linkWrapper: { + whiteSpace: "nowrap", + marginRight: constants.generalUnit * 2, + display: "flex", + alignItems: "center", + overflow: "hidden" + }, + permissionWrapper: { + display: "flex", + alignItems: "center", + marginRight: constants.generalUnit, + flex: 1, + whiteSpace: "nowrap" + }, + copyButton: { + flex: 1, + whiteSpace: "nowrap", + marginRight: constants.generalUnit + }, + link: { + textOverflow: "ellipsis", + overflow: "hidden" + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + fill: constants.fileSystemItemRow.menuIcon + } + }) + } +) + +interface Props { + nonce: NonceResponse + bucketEncryptionKey: string + refreshNonces: () => void +} + +const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => { + const classes = useStyles() + const { filesApiClient } = useFilesApi() + const [link, setLink] = useState("") + const [jwt, setJwt] = useState("") + const { createJWT } = useThresholdKey() + const [copied, setCopied] = useState(false) + const [isLoading, setIsLoading] = useState(true) + + useEffect(() => { + if(!nonce?.bucket_id || !nonce?.id) { + return + } + + const newJwt = createJWT(nonce.bucket_id, nonce.id, nonce.permission) + newJwt && setJwt(newJwt) + setIsLoading(false) + }, [createJWT, nonce]) + + useEffect(() => { + if(!jwt) { + return + } + + setLink(`${window.location.origin}${ROUTE_LINKS.SharingLink(nonce.permission, jwt, bucketEncryptionKey)}`) + }, [jwt, bucketEncryptionKey, nonce]) + + const debouncedSwitchCopied = debounce(() => setCopied(false), 3000) + + const onCopyInfo = useCallback(() => { + navigator.clipboard.writeText(link) + .then(() => { + setCopied(true) + debouncedSwitchCopied() + }) + .catch(console.error) + }, [debouncedSwitchCopied, link]) + + const onDeleteNonce = useCallback(() => { + setIsLoading(true) + filesApiClient.revokeNonce(nonce.id) + .catch(console.error) + .finally(() => { + refreshNonces() + setIsLoading(false) + }) + }, [filesApiClient, nonce, refreshNonces]) + + return ( +
+
+ + {link} + +
+
+ + {translatedPermission(nonce.permission)} + +
+ + +
+ ) +} + +export default SharingLink diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx index 3c615971bb..d88b27c568 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx @@ -321,7 +321,6 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { } -
{isUsingCurrentBucket ? ( @@ -464,3 +463,4 @@ const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { } export default ShareModal + diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index f3d3df1afd..2bc948f9dd 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -160,6 +160,7 @@ const SharedFolderOverview = () => { const openSharedFolder = useCallback((bucketId: string) => { redirect(ROUTE_LINKS.SharedFolderExplorer(bucketId, "/")) }, [redirect]) + return ( <>
+ createStyles({ + root:{ + display: "flex", + flexDirection: "column", + alignItems: "center" + }, + box: { + backgroundColor: constants.loginModule.background, + border: `1px solid ${constants.landing.border}`, + boxShadow: constants.landing.boxShadow, + borderRadius: 6, + maxWidth: constants.generalUnit * 70, + padding: constants.generalUnit * 5, + [breakpoints.down("md")]: { + justifyContent: "center", + width: "100%" + } + }, + icon : { + display: "flex", + alignItems: "center", + fontSize: constants.generalUnit * 6, + "& svg": { + marginRight: constants.generalUnit, + fill: palette.additional["gray"][7] + } + }, + error: { + color: palette.error.main + }, + messageWrapper: { + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center" + }, + browseButton : { + marginTop: constants.generalUnit * 2 + } + }) +) + +interface DecodedJwt { + bucket_id?: string + permission?: NonceResponsePermission +} +const LinkSharingModule = () => { + const { pathname, hash } = useLocation() + const { redirect } = useHistory() + const jwt = useMemo(() => getJWT(pathname), [pathname]) + const bucketDecryptionKey = useMemo(() => getBucketDecryptionFromHash(hash), [hash]) + const { filesApiClient } = useFilesApi() + const { refreshBuckets, buckets } = useFiles() + const { publicKey, encryptForPublicKey } = useThresholdKey() + const [encryptedEncryptionKey, setEncryptedEncryptionKey] = useState("") + const [error, setError] = useState("") + const classes = useStyles() + const { bucket_id: bucketId, permission } = useMemo(() => { + try { + return (jwt && jwtDecode(jwt)) || {} + }catch (e) { + console.error(e) + return {} + } + }, [jwt]) + const newBucket = useMemo(() => buckets.find((b) => b.id === bucketId), [bucketId, buckets]) + + useEffect(() => { + if(!publicKey || !bucketDecryptionKey) return + + encryptForPublicKey(publicKey, bucketDecryptionKey) + .then(setEncryptedEncryptionKey) + .catch(console.error) + + }, [bucketDecryptionKey, encryptForPublicKey, publicKey]) + + useEffect(() => { + if(!jwt || !encryptedEncryptionKey || !!newBucket) return + + filesApiClient.verifyNonce({ jwt, encryption_key: encryptedEncryptionKey }) + .catch((e:any) => { + console.error(e) + setError(e.error.message) + }) + .finally(() => { + refreshBuckets() + }) + }, [encryptedEncryptionKey, error, filesApiClient, jwt, newBucket, refreshBuckets]) + + const onBrowseBucket = useCallback(() => { + newBucket && redirect(ROUTE_LINKS.SharedFolderExplorer(newBucket.id, "/")) + }, [newBucket, redirect]) + + return ( +
+
+
+ {!error && !newBucket && ( + <> + + + Adding you to the shared folder... + + + )} + {!error && newBucket && permission && ( + <> + + + + You were added to the shared folder ({translatedPermission(permission)}): {newBucket.name} + + + + + )} +
+ {!!error && ( + + {error} + + )} +
+
+ ) +} + +export default LinkSharingModule diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index f693f6a190..3dc697007d 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react" +import React, { useCallback, useMemo, useState } from "react" import { Button, GithubLogoIcon, @@ -7,7 +7,8 @@ import { Loading, Typography, FormikTextInput, - EthereumLogoIcon + EthereumLogoIcon, + useLocation } from "@chainsafe/common-components" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" @@ -16,7 +17,7 @@ import { useFilesApi } from "../../../Contexts/FilesApiContext" import { useWeb3 } from "@chainsafe/web3-context" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" import { LOGIN_TYPE } from "@toruslabs/torus-direct-web-sdk" -import { ROUTE_LINKS } from "../../FilesRoutes" +import { LINK_SHARING_BASE, ROUTE_LINKS } from "../../FilesRoutes" import clsx from "clsx" import { IdentityProvider } from "@chainsafe/files-api-client" import PasswordlessEmail from "./PasswordlessEmail" @@ -91,14 +92,14 @@ const useStyles = makeStyles( maxWidth: 240 }, headerText: { + textAlign: "center", [breakpoints.up("md")]: { paddingTop: constants.generalUnit * 4, paddingBottom: constants.generalUnit * 8 }, [breakpoints.down("md")]: { paddingTop: constants.generalUnit * 3, - paddingBottom: constants.generalUnit * 3, - textAlign: "center" + paddingBottom: constants.generalUnit * 3 } }, footer: { @@ -173,6 +174,8 @@ const InitialScreen = ({ className }: IInitialScreen) => { const [isConnecting, setIsConnecting] = useState(false) const { filesApiClient } = useFilesApi() const [email, setEmail] = useState("") + const { state } = useLocation<{from?: string}>() + const isSharing = useMemo(() => state?.from?.includes(LINK_SHARING_BASE), [state]) const handleSelectWalletAndConnect = async () => { setError(undefined) @@ -375,7 +378,10 @@ const InitialScreen = ({ className }: IInitialScreen) => { component="h1" className={classes.headerText} > - Get Started + {isSharing + ? Sign in/up to access the shared folder + : Get Started + } )} {!error && ( diff --git a/packages/files-ui/src/Components/Pages/LinkSharingLanding.tsx b/packages/files-ui/src/Components/Pages/LinkSharingLanding.tsx new file mode 100644 index 0000000000..17a5f585b0 --- /dev/null +++ b/packages/files-ui/src/Components/Pages/LinkSharingLanding.tsx @@ -0,0 +1,11 @@ +import React from "react" +import { usePageTrack } from "../../Contexts/PosthogContext" +import LinkSharingModule from "../Modules/LinkSharingModule" + +const LinkSharingLanding = () => { + usePageTrack() + + return +} + +export default LinkSharingLanding diff --git a/packages/files-ui/src/Components/Pages/LoginPage.tsx b/packages/files-ui/src/Components/Pages/LoginPage.tsx index 8b32ef849e..0a5f26bb13 100644 --- a/packages/files-ui/src/Components/Pages/LoginPage.tsx +++ b/packages/files-ui/src/Components/Pages/LoginPage.tsx @@ -12,7 +12,6 @@ import BottomDarkSVG from "../../Media/landing/layers/dark/Bottom.dark.svg" import TopDarkSVG from "../../Media/landing/layers/dark/Top.dark.svg" import BottomLightSVG from "../../Media/landing/layers/light/Bottom.light.svg" import TopLightSVG from "../../Media/landing/layers/light/Top.light.svg" -// import { ForegroundSVG } from "../../Media/landing/layers/ForegroundSVG" import MigrateAccount from "../Modules/LoginModule/MigrateAccount" import InitializeAccount from "../Modules/LoginModule/InitializeAccount" import { useFilesApi } from "../../Contexts/FilesApiContext" @@ -167,8 +166,6 @@ const LoginPage = () => { ChainSafe Files - <> - { themeKey === "dark" ? <> diff --git a/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx b/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx index 9259fe6413..b9788d080c 100644 --- a/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx +++ b/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx @@ -15,12 +15,14 @@ import EthCrypto from "eth-crypto" import { useWeb3 } from "@chainsafe/web3-context" import ShareTransferRequestModal from "../Components/Elements/ShareTransferRequestModal" import BN from "bn.js" -import { IdentityProvider } from "@chainsafe/files-api-client" +import { IdentityProvider, NonceResponsePermission } from "@chainsafe/files-api-client" import { capitalize, centerEllipsis } from "../Utils/Helpers" import { t } from "@lingui/macro" import jwtDecode from "jwt-decode" import { IdentityToken } from "@chainsafe/files-api-client" import dayjs from "dayjs" +import keyEncoder from "key-encoder" +import { KJUR } from "jsrsasign" const TORUS_POSTBOX_KEY = "csf.postboxKey" const TKEY_STORE_KEY = "csf.tkeyStore" @@ -68,6 +70,7 @@ export type TThresholdKeyContext = { getAvailableShareIndices(): string[] | undefined refreshTKeyMeta(): Promise loggedinAs: string + createJWT: (bucketId: string, nonceId: string, nonce: NonceResponsePermission) => string | undefined } type ThresholdKeyProviderProps = { @@ -396,6 +399,31 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f } }, [userInfo, address]) + const createJWT = useCallback((bucketId: string, nonceId: string, permission: NonceResponsePermission) => { + + if(!privateKey) { + console.error("no private key found") + return + } + + const ke = new keyEncoder("secp256k1") + const pem = ke.encodePrivate(privateKey, "raw", "pem") + const header = { alg: "ES256", typ: "JWT" } + const payload = { + type: "link_sharing", + permission, + iat: KJUR.jws.IntDate.get("now"), + bucket_id: bucketId, + nonce_id: nonceId + } + + const sHeader = JSON.stringify(header) + const sPayload = JSON.stringify(payload) + + const sJWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, pem) + return sJWT + }, [privateKey]) + const login = async (loginType: IdentityProvider, tokenInfo?: {token: string; email: string}) => { if (!TKeySdk || maintenanceMode) return try { @@ -828,7 +856,8 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f resetStatus: () => setStatus("initialized"), getAvailableShareIndices, refreshTKeyMeta, - loggedinAs + loggedinAs, + createJWT }} > {!isNewDevice && pendingShareTransferRequests.length > 0 && process.env.REACT_APP_TEST !== "true" && ( diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index ee3cf3a5f6..5f96cf6bae 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -103,6 +103,29 @@ export const isSubFolder = (fold1: string, fold2: string) => { return result } +// get the jwt from /link-sharing/permision/jwt +export const getJWT = (pathname: string) => { + const arrayOfPaths = getArrayOfPaths(pathname) + + if(arrayOfPaths.length !== 3){ + console.error("JWT extraction error, unexpected path", pathname) + return + } + + return decodeURIComponent(arrayOfPaths[2]) +} + +// return the hash from #hash +export const getBucketDecryptionFromHash = (hash: string) => { + + if(!hash.startsWith("#")){ + console.error("Bucket encryption key extraction error, unexpected hash", hash) + return + } + + return decodeURIComponent(hash.substr(1)) +} + export const getUrlSafePathWithFile = (path: string, fileName: string) => { let urlSafePath = getURISafePathFromArray(getArrayOfPaths(path)) if (urlSafePath === "/") { diff --git a/packages/files-ui/src/locales/de/messages.po b/packages/files-ui/src/locales/de/messages.po index 4d3c4c3bf4..e05e0f362d 100644 --- a/packages/files-ui/src/locales/de/messages.po +++ b/packages/files-ui/src/locales/de/messages.po @@ -43,6 +43,9 @@ msgstr "Weitere Dateien hinzufügen" msgid "Add viewers and editors by username, sharing id or Ethereum address." msgstr "" +msgid "Adding you to the shared folder..." +msgstr "" + msgid "An error occurred:" msgstr "Es ist ein Fehler aufgetreten:" @@ -58,6 +61,9 @@ msgstr "Der Sicherungsgeheimsatz stimmt nicht mit dem Benutzerkonto überein, bi msgid "Bin" msgstr "Papierkorb" +msgid "Browse {0}" +msgstr "" + msgid "Browser:" msgstr "Browser:" @@ -133,6 +139,9 @@ msgstr "" msgid "Copy info" msgstr "" +msgid "Copy link" +msgstr "" + msgid "Copy over" msgstr "" @@ -160,6 +169,9 @@ msgstr "" msgid "Create folder" msgstr "Ordner erstellen" +msgid "Create new link" +msgstr "" + msgid "Create your public username in <0>Settings!" msgstr "" @@ -640,6 +652,9 @@ msgstr "" msgid "Sharing files" msgstr "" +msgid "Sharing link" +msgstr "" + msgid "Sign Out" msgstr "Abmelden" @@ -649,6 +664,9 @@ msgstr "Anmelden" msgid "Sign in with a different account" msgstr "Mit einem anderen Konto anmelden" +msgid "Sign in/up to access the shared folder" +msgstr "" + msgid "Sign me up!" msgstr "" @@ -841,21 +859,33 @@ msgstr "Sie können keine Ordner in diesen Pfad verschieben" msgid "You haven't set a username yet." msgstr "Sie haben noch keinen Benutzernamen festgelegt." +msgid "You were added to the shared folder ({0}): {1}" +msgstr "" + msgid "You will need to sign a message in your wallet to complete sign in." 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" +msgstr "" + msgid "me" msgstr "ich" msgid "on" msgstr "am" +msgid "read rights" +msgstr "" + msgid "unknown" msgstr "unbekannt" +msgid "with" +msgstr "" + msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" msgstr "{0, plural, one {{1} Datei wird heruntergeladen} other {{2} Dateien werden heruntergeladen}}" diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index cbd244e49d..04a0eccd69 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -43,6 +43,9 @@ msgstr "Add more files" msgid "Add viewers and editors by username, sharing id or Ethereum address." 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 "An error occurred:" msgstr "An error occurred:" @@ -58,6 +61,9 @@ msgstr "Backup secret phrase does not match user account, please double-check an msgid "Bin" msgstr "Bin" +msgid "Browse {0}" +msgstr "Browse {0}" + msgid "Browser:" msgstr "Browser:" @@ -133,6 +139,9 @@ msgstr "Copy file" msgid "Copy info" msgstr "Copy info" +msgid "Copy link" +msgstr "Copy link" + msgid "Copy over" msgstr "Copy over" @@ -160,6 +169,9 @@ msgstr "Create a new shared folder" msgid "Create folder" msgstr "Create folder" +msgid "Create new link" +msgstr "Create new link" + msgid "Create your public username in <0>Settings!" msgstr "Create your public username in <0>Settings!" @@ -643,6 +655,9 @@ msgstr "Shared with" msgid "Sharing files" msgstr "Sharing files" +msgid "Sharing link" +msgstr "Sharing link" + msgid "Sign Out" msgstr "Sign Out" @@ -652,6 +667,9 @@ msgstr "Sign in" msgid "Sign in with a different account" msgstr "Sign in with a different account" +msgid "Sign in/up to access the shared folder" +msgstr "Sign in/up to access the shared folder" + msgid "Sign me up!" msgstr "Sign me up!" @@ -844,21 +862,33 @@ msgstr "You can't move folders to this path" msgid "You haven't set a username yet." msgstr "You haven't set a username yet." +msgid "You were added to the shared folder ({0}): {1}" +msgstr "You were added to the shared folder ({0}): {1}" + msgid "You will need to sign a message in your wallet to complete sign in." msgstr "You will need to sign a message in your wallet to complete sign in." 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 "me" msgstr "me" msgid "on" msgstr "on" +msgid "read rights" +msgstr "read rights" + msgid "unknown" msgstr "unknown" +msgid "with" +msgstr "with" + 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 a7aaf63fd4..98cde9765d 100644 --- a/packages/files-ui/src/locales/es/messages.po +++ b/packages/files-ui/src/locales/es/messages.po @@ -44,6 +44,9 @@ msgstr "Agrega mas archivos" msgid "Add viewers and editors by username, sharing id or Ethereum address." msgstr "" +msgid "Adding you to the shared folder..." +msgstr "" + msgid "An error occurred:" msgstr "" @@ -59,6 +62,9 @@ msgstr "" msgid "Bin" msgstr "Papelera" +msgid "Browse {0}" +msgstr "" + msgid "Browser:" msgstr "Navegador:" @@ -134,6 +140,9 @@ msgstr "" msgid "Copy info" msgstr "" +msgid "Copy link" +msgstr "" + msgid "Copy over" msgstr "" @@ -161,6 +170,9 @@ msgstr "" msgid "Create folder" msgstr "Crear Carpeta" +msgid "Create new link" +msgstr "" + msgid "Create your public username in <0>Settings!" msgstr "" @@ -644,6 +656,9 @@ msgstr "" msgid "Sharing files" msgstr "" +msgid "Sharing link" +msgstr "" + msgid "Sign Out" msgstr "Desconectar" @@ -653,6 +668,9 @@ msgstr "Registrarse" msgid "Sign in with a different account" msgstr "Inicie sesión con una cuenta diferente" +msgid "Sign in/up to access the shared folder" +msgstr "" + msgid "Sign me up!" msgstr "" @@ -845,21 +863,33 @@ msgstr "" msgid "You haven't set a username yet." msgstr "" +msgid "You were added to the shared folder ({0}): {1}" +msgstr "" + msgid "You will need to sign a message in your wallet to complete sign in." msgstr "Deberá firmar un mensaje en su billetera para completar el inicio de sesión." msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "" +msgid "edit rights" +msgstr "" + msgid "me" msgstr "" msgid "on" msgstr "en" +msgid "read rights" +msgstr "" + msgid "unknown" msgstr "" +msgid "with" +msgstr "" + msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" msgstr "" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index 461b228c09..c271d9b08a 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -44,6 +44,9 @@ msgstr "Ajouter d’autres fichiers" msgid "Add viewers and editors by username, sharing id or Ethereum address." msgstr "Ajoutez des personnes pouvant visualiser ou afficher par nom d'utilisateur, identifiant de partage ou adresse Ethereum." +msgid "Adding you to the shared folder..." +msgstr "" + msgid "An error occurred:" msgstr "Une erreur s'est produite :" @@ -59,6 +62,9 @@ msgstr "La phrase secrète de sauvegarde est incorrecte, merci de vérifier et r msgid "Bin" msgstr "Corbeille" +msgid "Browse {0}" +msgstr "" + msgid "Browser:" msgstr "Explorateur :" @@ -134,6 +140,9 @@ msgstr "Copier le fichier" msgid "Copy info" msgstr "Copier les infos" +msgid "Copy link" +msgstr "" + msgid "Copy over" msgstr "Copier" @@ -161,6 +170,9 @@ msgstr "Créer un nouveau dossier partagé" msgid "Create folder" msgstr "Créer un dossier" +msgid "Create new link" +msgstr "" + msgid "Create your public username in <0>Settings!" msgstr "Créez votre nom d'utilisateur public dans <0>Paramètres !" @@ -642,7 +654,10 @@ msgid "Shared with" msgstr "Partagé avec" msgid "Sharing files" -msgstr "Partage des fichiers" +msgstr "" + +msgid "Sharing link" +msgstr "" msgid "Sign Out" msgstr "Se déconnecter" @@ -653,6 +668,9 @@ msgstr "Se connecter" msgid "Sign in with a different account" msgstr "Se connecter avec un autre compte" +msgid "Sign in/up to access the shared folder" +msgstr "" + msgid "Sign me up!" msgstr "S'inscrire !" @@ -845,21 +863,33 @@ msgstr "Vous ne pouvez pas déplacer les dossiers vers ce chemin" msgid "You haven't set a username yet." msgstr "Vous n’avez pas encore défini de nom d’utilisateur." +msgid "You were added to the shared folder ({0}): {1}" +msgstr "" + msgid "You will need to sign a message in your wallet to complete sign in." msgstr "Vous devrez signer un message avec votre wallet pour terminer la procédure connexion." 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." +msgid "edit rights" +msgstr "" + msgid "me" msgstr "moi" msgid "on" msgstr "le" +msgid "read rights" +msgstr "" + msgid "unknown" msgstr "inconnu" +msgid "with" +msgstr "" + 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 348339e91e..d2f340a862 100644 --- a/packages/files-ui/src/locales/no/messages.po +++ b/packages/files-ui/src/locales/no/messages.po @@ -43,6 +43,9 @@ msgstr "Legg til flere filer" msgid "Add viewers and editors by username, sharing id or Ethereum address." msgstr "" +msgid "Adding you to the shared folder..." +msgstr "" + msgid "An error occurred:" msgstr "" @@ -58,6 +61,9 @@ msgstr "" msgid "Bin" msgstr "" +msgid "Browse {0}" +msgstr "" + msgid "Browser:" msgstr "" @@ -133,6 +139,9 @@ msgstr "" msgid "Copy info" msgstr "" +msgid "Copy link" +msgstr "" + msgid "Copy over" msgstr "" @@ -160,6 +169,9 @@ msgstr "" msgid "Create folder" msgstr "Opprett mappe" +msgid "Create new link" +msgstr "" + msgid "Create your public username in <0>Settings!" msgstr "" @@ -640,6 +652,9 @@ msgstr "" msgid "Sharing files" msgstr "" +msgid "Sharing link" +msgstr "" + msgid "Sign Out" msgstr "Logg ut" @@ -649,6 +664,9 @@ msgstr "Logg inn" msgid "Sign in with a different account" msgstr "Logg inn med en annen konto" +msgid "Sign in/up to access the shared folder" +msgstr "" + msgid "Sign me up!" msgstr "" @@ -841,21 +859,33 @@ msgstr "" msgid "You haven't set a username yet." msgstr "Du har ikke satt noe brukernavn enda." +msgid "You were added to the shared folder ({0}): {1}" +msgstr "" + msgid "You will need to sign a message in your wallet to complete sign in." msgstr "" msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "" +msgid "edit rights" +msgstr "" + msgid "me" msgstr "" msgid "on" msgstr "" +msgid "read rights" +msgstr "" + msgid "unknown" msgstr "" +msgid "with" +msgstr "" + msgid "{0, plural, one {Downloading {1} file} other {Downloading {2} files}}" msgstr "" diff --git a/packages/files-ui/src/serviceWorker.ts b/packages/files-ui/src/serviceWorker.ts index 8e951fbfee..8e879f512e 100644 --- a/packages/files-ui/src/serviceWorker.ts +++ b/packages/files-ui/src/serviceWorker.ts @@ -139,7 +139,7 @@ export function unregister() { .then(registration => { registration.unregister() }) - .catch(error => { + .catch((error: any) => { console.error(error.message) }) } diff --git a/packages/gaming-ui/src/serviceWorker.ts b/packages/gaming-ui/src/serviceWorker.ts index 8e951fbfee..8e879f512e 100644 --- a/packages/gaming-ui/src/serviceWorker.ts +++ b/packages/gaming-ui/src/serviceWorker.ts @@ -139,7 +139,7 @@ export function unregister() { .then(registration => { registration.unregister() }) - .catch(error => { + .catch((error: any) => { console.error(error.message) }) } diff --git a/packages/storage-ui/src/serviceWorker.ts b/packages/storage-ui/src/serviceWorker.ts index 8e951fbfee..8e879f512e 100644 --- a/packages/storage-ui/src/serviceWorker.ts +++ b/packages/storage-ui/src/serviceWorker.ts @@ -139,7 +139,7 @@ export function unregister() { .then(registration => { registration.unregister() }) - .catch(error => { + .catch((error: any) => { console.error(error.message) }) } diff --git a/yarn.lock b/yarn.lock index f7f01d0c90..ab35e87884 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5824,6 +5824,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/bn.js@*", "@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + "@types/bn.js@4.11.6", "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -5831,13 +5838,6 @@ dependencies: "@types/node" "*" -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== - dependencies: - "@types/node" "*" - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -5850,6 +5850,13 @@ dependencies: "@types/node" "*" +"@types/elliptic@^6.4.9": + version "6.4.13" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.13.tgz#7e8ac814f748deb01a712e5147b128caf9dffa2d" + integrity sha512-e8iyLJ8vMLpWxXpVWrIt0ujqsfHWgVe5XAz9IMhBYoDirK6th7J+mHjzp797OLc62ZX419nrlwwzsNAA0a0mKg== + dependencies: + "@types/bn.js" "*" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -5967,6 +5974,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/jsrsasign@^8.0.13": + version "8.0.13" + resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-8.0.13.tgz#770c1e429107dfb0cc4f5b78b472584511a55a28" + integrity sha512-+0Ij59D6NXP48KkeLhPXeQKOyLjvA9CD7zacc0Svy2IWHdl62BmDeTvGSIwKaGZSoamLJOo+on1AG/wPRLsd7A== + "@types/lodash@^4.14.165": version "4.14.165" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" @@ -7420,7 +7432,7 @@ asap@~2.0.6: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= -asn1.js@^5.2.0: +asn1.js@^5.0.1, asn1.js@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== @@ -16290,6 +16302,11 @@ jsqr@^1.2.0: resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.4.0.tgz#8efb8d0a7cc6863cb6d95116b9069123ce9eb2d1" integrity sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A== +jsrsasign@^10.4.1: + version "10.4.1" + resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.4.1.tgz#3aec10b6201e3c321d4ee2b1e010bc2f808ed4d0" + integrity sha512-g2CP2nb8xKdmfZhuHaJEz1zVYTsZc+lUjLFvgbMX35/cUALK0G15sQfCbCpDg/UivkjCNlq0lV6FxCfPhv0shw== + jss-plugin-camel-case@^10.0.3: version "10.4.0" resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz#46c75ff7fd61c304984c21af5817823f0f501ceb" @@ -16477,6 +16494,16 @@ keccak@^3.0.0: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +key-encoder@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/key-encoder/-/key-encoder-2.0.3.tgz#77073bb48ff1fe2173bb2088b83b91152c8fa4ba" + integrity sha512-fgBtpAGIr/Fy5/+ZLQZIPPhsZEcbSlYu/Wu96tNDFNSjSACw5lEIOFeaVdQ/iwrb8oxjlWi6wmWdH76hV6GZjg== + dependencies: + "@types/elliptic" "^6.4.9" + asn1.js "^5.0.1" + bn.js "^4.11.8" + elliptic "^6.4.1" + keyvaluestorage-interface@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz#13ebdf71f5284ad54be94bd1ad9ed79adad515ff"