Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link-sharing landing page #1620

Merged
merged 12 commits into from
Oct 13, 2021
Merged
5 changes: 3 additions & 2 deletions packages/common-components/src/Router/ConditionalRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ConditionalRoute: React.FC<IConditionalRouteProps> = ({
exact,
...rest
}) => {
const { state, pathname } = useLocation<{from?: string} | undefined>()
const { state, pathname, hash } = useLocation<{from?: string} | undefined>()
const from = (state as any)?.from

return <Route
Expand All @@ -34,7 +34,8 @@ const ConditionalRoute: React.FC<IConditionalRouteProps> = ({
? <Redirect
to={{
pathname: redirectToSource && from ? from : redirectPath,
state: { from: pathname }
state: { from: pathname },
hash
}}
/>
// this may be converted into loading
Expand Down
2 changes: 1 addition & 1 deletion packages/files-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.12",
"@chainsafe/files-api-client": "^1.18.14",
"@chainsafe/web3-context": "1.1.4",
"@lingui/core": "^3.7.2",
"@lingui/react": "^3.7.2",
Expand Down
14 changes: 11 additions & 3 deletions packages/files-ui/src/Components/FilesRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ 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",
Expand All @@ -29,8 +32,7 @@ export const ROUTE_LINKS = {
SharedFolders: "/shared-overview",
SharedFolderBrowserRoot: "/shared",
SharingLink: (permission: NonceResponsePermission, jwt: string, bucketEncryptionKey: string) =>
// eslint-disable-next-line max-len
`${window.location.origin}/sharing-link/${permissionPath(permission)}/${encodeURIComponent(jwt)}#${encodeURIComponent(bucketEncryptionKey)}`,
`${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 /
Expand All @@ -53,6 +55,12 @@ const FilesRoutes = () => {
[isLoggedIn, isNewDevice, publicKey, secured, shouldInitializeAccount])
return (
<Switch>
<ConditionalRoute
path={LINK_SHARING_BASE}
isAuthorized={isAuthorized}
component={LinkSharingLanding}
redirectPath={ROUTE_LINKS.Landing}
/>
<ConditionalRoute
exact
path={ROUTE_LINKS.SharedFolders}
Expand Down Expand Up @@ -106,7 +114,7 @@ const FilesRoutes = () => {
redirectPath={ROUTE_LINKS.Landing}
/>
<ConditionalRoute
path='/'
path={ROUTE_LINKS.Landing}
isAuthorized={!isAuthorized}
component={LoginPage}
redirectPath={ROUTE_LINKS.Drive("/")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const SharingLink = ({ nonce, bucketEncryptionKey, refreshNonces }: Props) => {
return
}

setLink(ROUTE_LINKS.SharingLink(nonce.permission, jwt, bucketEncryptionKey))
setLink(`${window.location.origin}${ROUTE_LINKS.SharingLink(nonce.permission, jwt, bucketEncryptionKey)}`)
}, [jwt, bucketEncryptionKey, nonce])

const debouncedSwitchCopied = debounce(() => setCopied(false), 3000)
Expand Down
150 changes: 150 additions & 0 deletions packages/files-ui/src/Components/Modules/LinkSharingModule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"
import { Button, CheckCircleIcon, Loading, Typography, useHistory, useLocation } from "@chainsafe/common-components"
import { getBucketDecryptionFromHash, getJWT } from "../../Utils/pathUtils"
import { useFilesApi } from "../../Contexts/FilesApiContext"
import { useThresholdKey } from "../../Contexts/ThresholdKeyContext"
import { Trans } from "@lingui/macro"
import { useFiles } from "../../Contexts/FilesContext"
import jwtDecode from "jwt-decode"
import { createStyles, makeStyles } from "@chainsafe/common-theme"
import { CSFTheme } from "../../Themes/types"
import { ROUTE_LINKS } from "../FilesRoutes"

const useStyles = makeStyles(
({ constants, palette, breakpoints }: CSFTheme) =>
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
}
},
error: {
color: palette.error.main
},
messageWrapper: {
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
},
browseButton : {
marginTop: constants.generalUnit * 2
}
})
)

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 [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState("")
const bucketId = useMemo(() => jwt && jwtDecode<{bucket_id?: string}>(jwt)?.bucket_id, [jwt])
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
const classes = useStyles()
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(() => {
!!newBucket && setIsLoading(false)
}, [newBucket])
Tbaut marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if(!jwt || !encryptedEncryptionKey) return

filesApiClient.verifyNonce({ jwt, encryption_key: encryptedEncryptionKey })
.catch((e:any) => {
console.error(error)
setError(e.message)
tanmoyAtb marked this conversation as resolved.
Show resolved Hide resolved
})
.finally(() => {
refreshBuckets()
})
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
}, [encryptedEncryptionKey, error, filesApiClient, jwt, refreshBuckets])

const onBrowserBucker = useCallback(() => {
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
newBucket && redirect(ROUTE_LINKS.SharedFolderExplorer(newBucket.id, "/"))
}, [newBucket, redirect])

return (
<div className={classes.root}>
<div className={classes.box}>
<div className={classes.messageWrapper}>
{isLoading && (
<>
<Loading
type="inherit"
size={48}
className={classes.icon}
/>
<Typography variant={"h4"} >
<Trans>Adding you to the shared folder...</Trans>
</Typography>
</>
)}
{!isLoading && !error && newBucket && (
<>
<CheckCircleIcon
size={48}
className={classes.icon}
/>
<Typography variant={"h4"} >
<Trans>
You were added to the shared folder: {newBucket.name}
</Trans>
</Typography>
<Button
className={classes.browseButton}
onClick={onBrowserBucker}
>
<Trans>Browse {newBucket.name}</Trans>
</Button>
</>
)}
</div>
{!!error && (
<Typography
variant="body2"
className={classes.error}
>
{error}
</Typography>
)}
</div>
</div>
)
}

export default LinkSharingModule
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react"
import React, { useCallback, useMemo, useState } from "react"
import {
Button,
GithubLogoIcon,
Expand All @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -379,7 +382,10 @@ const InitialScreen = ({ className }: IInitialScreen) => {
component="h1"
className={classes.headerText}
>
<Trans>Get Started</Trans>
{isSharing
? <Trans>Sign in/up to access the shared folder</Trans>
: <Trans>Get Started</Trans>
}
</Typography>
)}
{!error && (
Expand Down
11 changes: 11 additions & 0 deletions packages/files-ui/src/Components/Pages/LinkSharingLanding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react"
import { usePageTrack } from "../../Contexts/PosthogContext"
import LinkSharingModule from "../Modules/LinkSharingModule"

const LinkSharingLanding = () => {
usePageTrack()

return <LinkSharingModule/>
}

export default LinkSharingLanding
3 changes: 0 additions & 3 deletions packages/files-ui/src/Components/Pages/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -167,8 +166,6 @@ const LoginPage = () => {
<ChainsafeFilesLogo className={classes.filesLogo} />
ChainSafe Files
</Typography>
<>
</>
{
themeKey === "dark"
? <>
Expand Down
23 changes: 23 additions & 0 deletions packages/files-ui/src/Utils/pathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 extractione 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 extractione error, unexpected hash", hash)
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
return
}

return decodeURIComponent(hash.substr(1))
}

export const getUrlSafePathWithFile = (path: string, fileName: string) => {
let urlSafePath = getURISafePathFromArray(getArrayOfPaths(path))
if (urlSafePath === "/") {
Expand Down
12 changes: 12 additions & 0 deletions packages/files-ui/src/locales/de/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -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 while downloading the file"
msgstr "Beim Herunterladen der Datei ist ein Fehler aufgetreten"

Expand All @@ -61,6 +64,9 @@ msgstr "Der Sicherungsgeheimsatz stimmt nicht mit dem Benutzerkonto überein, bi
msgid "Bin"
msgstr "Papierkorb"

msgid "Browse {0}"
msgstr ""

msgid "Browser:"
msgstr "Browser:"

Expand Down Expand Up @@ -655,6 +661,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 ""

Expand Down Expand Up @@ -847,6 +856,9 @@ 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}"
msgstr ""

msgid "You will need to sign a message in your wallet to complete sign in."
msgstr ""

Expand Down
Loading