Skip to content

Commit

Permalink
[EPIC] Link Sharing (#1632)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>

* fix comments

* try-catch

* cleanup

* Update packages/files-ui/src/Components/Modules/LinkSharingModule.tsx

Co-authored-by: Michael Yankelev <[email protected]>

* nit color

* Update packages/files-ui/src/Utils/pathUtils.ts

Co-authored-by: Ryan Noble <[email protected]>

Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: GitHub Actions <[email protected]>
Co-authored-by: Tanmoy Basak Anjan <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Ryan Noble <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Thibaut Sardan <[email protected]>

* 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 <[email protected]>

* bump cypress to v8.6 (#1628)

* bump cypress to v8.6

* lingui extract

* Update test

Co-authored-by: GitHub Actions <[email protected]>

* trans

Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Tanmoy Basak Anjan <[email protected]>
Co-authored-by: GitHub Actions <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Weblate (bot) <[email protected]>
Co-authored-by: J. Lavoie <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Thibaut Sardan <[email protected]>

* 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 <[email protected]>

* bump cypress to v8.6 (#1628)

* bump cypress to v8.6

* lingui extract

* Update test

Co-authored-by: GitHub Actions <[email protected]>

* 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 <[email protected]>
Co-authored-by: Thibaut Sardan <[email protected]>

* 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 <[email protected]>

* [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 <[email protected]>

* Fix Webpack Build with node 17 (#1651)

* Fix build for node 17

* lingui extract

Co-authored-by: GitHub Actions <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* Update packages/files-ui/src/Components/Modules/PreviewRenderers/ImagePreview.tsx

* resolve openssl issue

* update remaining build commands

Co-authored-by: Thibaut Sardan <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>
Co-authored-by: GitHub Actions <[email protected]>
Co-authored-by: Tanmoy Basak Anjan <[email protected]>

Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Tanmoy Basak Anjan <[email protected]>
Co-authored-by: GitHub Actions <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Weblate (bot) <[email protected]>
Co-authored-by: J. Lavoie <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>

* Apply suggestions from code review

Co-authored-by: Michael Yankelev <[email protected]>

Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: GitHub Actions <[email protected]>
Co-authored-by: Tanmoy Basak Anjan <[email protected]>
Co-authored-by: Michael Yankelev <[email protected]>
Co-authored-by: Ryan Noble <[email protected]>
Co-authored-by: Weblate (bot) <[email protected]>
Co-authored-by: J. Lavoie <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>
Co-authored-by: Andrew Snaith <[email protected]>
  • Loading branch information
10 people authored Oct 25, 2021
1 parent 90ed9dd commit c5dd1c2
Show file tree
Hide file tree
Showing 23 changed files with 784 additions and 27 deletions.
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
3 changes: 3 additions & 0 deletions packages/files-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
15 changes: 14 additions & 1 deletion packages/files-ui/src/Components/FilesRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 /
Expand All @@ -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]

Expand All @@ -48,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 @@ -101,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 @@ -12,6 +12,8 @@ import { useCreateOrEditSharedFolder } from "./hooks/useCreateOrEditSharedFolder
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) => {
Expand Down Expand Up @@ -102,6 +104,9 @@ const useStyles = makeStyles(
errorText: {
marginLeft: constants.generalUnit * 1.5,
color: palette.error.main
},
sharingLink: {
padding: constants.generalUnit * 1.25
}
})
}
Expand Down Expand Up @@ -275,6 +280,17 @@ const CreateOrEditSharedFolderModal = ({ mode, isModalOpen, onClose, bucketToEdi
noOptionsMessage={t`No user found for this query.`}
/>
</div>
{mode === "edit" && !!bucketToEdit && (
<div className={clsx(classes.modalFlexItem, classes.sharingLink)}>
<Typography className={classes.inputLabel}>
<Trans>Sharing link</Trans>
</Typography>
<LinkList
bucketEncryptionKey={bucketToEdit.encryptionKey}
bucketId={bucketToEdit.id}
/>
</div>
)}
<Grid
item
flexDirection="row"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { Button, Loading, MenuDropdown } 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 { useFilesApi } from "../../../../Contexts/FilesApiContext"
import { CSFTheme } from "../../../../Themes/types"
import SharingLink from "./SharingLink"

const useStyles = makeStyles(
({ constants, palette }: CSFTheme) => {
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<NonceResponse[]>([])
const [isLoading, setIsLoading] = useState(false)
const [newLinkPermission, setNewLinkPermission] = useState<NonceResponsePermission>("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 (
<div className={classes.root}>
<div className={classes.createLink}>
<Button
className={classes.createLinkButton}
onClick={onCreateNonce}
disabled={isLoading}
>
<Trans>Create new link</Trans>
</Button>
<Trans>with</Trans>
<MenuDropdown
title={translatedPermission(newLinkPermission)}
anchor="bottom-right"
className={classes.permissionDropdown}
classNames={{
icon: classes.icon,
options: classes.options,
title: classes.dropdownTitle
}}
testId="permission"
menuItems={[
{
onClick: () => setNewLinkPermission("read"),
contents: (
<div
data-cy="menu-read"
className={classes.menuItem}
>
{readRights}
</div>
)
},
{
onClick: () => setNewLinkPermission("write"),
contents: (
<div
data-cy="menu-write"
className={classes.menuItem}
>
{editRights}
</div>
)
}
]}
/>
</div>
{
isLoading && <Loading size={16} />
}
{
!isLoading && nonces.length > 0 && nonces.map((nonce) =>
<SharingLink
key={nonce.id}
refreshNonces={refreshNonces}
bucketEncryptionKey={bucketEncryptionKey}
nonce={nonce}
/>
)
}
</div>
)
}

export default LinkList
Loading

0 comments on commit c5dd1c2

Please sign in to comment.