-
Notifications
You must be signed in to change notification settings - Fork 301
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
feat(storage-browser): add custom actions #6210
Changes from 8 commits
3e07873
d7d0186
acb38e1
e210bb5
f163cb3
0c38743
3999f5a
4eb4862
1437455
a6b07bb
105c332
cb47f4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import React from 'react'; | ||
|
||
import { createStorageBrowser } from '@aws-amplify/ui-react-storage/browser'; | ||
|
||
import { Flex } from '@aws-amplify/ui-react'; | ||
|
||
import '@aws-amplify/ui-react-storage/styles.css'; | ||
|
||
const { StorageBrowser } = createStorageBrowser({ | ||
actions: { | ||
default: { | ||
copy: { | ||
actionListItem: { | ||
icon: 'copy-file', | ||
label: 'Override Copy', | ||
}, | ||
handler: ({ data }) => { | ||
const { key } = data; | ||
return { | ||
result: Promise.resolve({ status: 'COMPLETE', value: { key } }), | ||
}; | ||
}, | ||
viewName: 'CopyView', | ||
}, | ||
createFolder: { | ||
actionListItem: { | ||
icon: 'create-folder', | ||
label: 'Override Create Folder', | ||
}, | ||
handler: ({ data }) => { | ||
const { key } = data; | ||
return { | ||
result: Promise.resolve({ status: 'COMPLETE', value: { key } }), | ||
}; | ||
}, | ||
viewName: 'CreateFolderView', | ||
}, | ||
delete: { | ||
actionListItem: { | ||
icon: 'delete-file', | ||
label: 'Override Delete', | ||
}, | ||
handler: ({ data }) => { | ||
const { key } = data; | ||
return { | ||
result: Promise.resolve({ status: 'COMPLETE', value: { key } }), | ||
}; | ||
}, | ||
viewName: 'DeleteView', | ||
}, | ||
download: () => { | ||
return { | ||
result: Promise.resolve({ | ||
status: 'COMPLETE', | ||
value: { url: new URL('') }, | ||
}), | ||
}; | ||
}, | ||
upload: { | ||
actionListItem: { | ||
icon: 'upload-file', | ||
label: 'Override Upload', | ||
}, | ||
handler: ({ data }) => { | ||
const { key } = data; | ||
return { | ||
result: Promise.resolve({ status: 'COMPLETE', value: { key } }), | ||
}; | ||
}, | ||
viewName: 'UploadView', | ||
}, | ||
listLocationItems: () => | ||
Promise.resolve({ | ||
items: [ | ||
{ | ||
id: 'jaskjkaska', | ||
key: 'item-key', | ||
lastModified: new Date(), | ||
size: 1008, | ||
type: 'FILE' as const, | ||
}, | ||
], | ||
nextToken: undefined, | ||
}), | ||
}, | ||
}, | ||
config: { | ||
getLocationCredentials: () => | ||
Promise.resolve({ | ||
credentials: { | ||
accessKeyId: '', | ||
expiration: new Date(), | ||
secretAccessKey: '', | ||
sessionToken: '', | ||
}, | ||
}), | ||
region: '', | ||
registerAuthListener: () => null, | ||
listLocations: () => | ||
Promise.resolve({ | ||
items: [ | ||
{ | ||
bucket: 'my-bucket', | ||
id: crypto.randomUUID(), | ||
permissions: ['delete', 'get', 'list', 'write'], | ||
prefix: 'my-prefix', | ||
type: 'PREFIX', | ||
}, | ||
], | ||
nextToken: undefined, | ||
}), | ||
}, | ||
}); | ||
|
||
function Example() { | ||
return ( | ||
<Flex | ||
direction="column" | ||
width="100vw" | ||
height="100vh" | ||
overflow="hidden" | ||
padding="xl" | ||
> | ||
<StorageBrowser /> | ||
</Flex> | ||
); | ||
} | ||
|
||
export default Example; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,104 @@ | ||
import React from 'react'; | ||
import { getUrl } from '@aws-amplify/storage/internals'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not specific to this PR but we need to make sure we don't recommend this in our public doc |
||
|
||
import { createStorageBrowser } from '@aws-amplify/ui-react-storage/browser'; | ||
import { | ||
ActionViewConfig, | ||
ActionHandler, | ||
createStorageBrowser, | ||
} from '@aws-amplify/ui-react-storage/browser'; | ||
|
||
import { managedAuthAdapter } from '../managedAuthAdapter'; | ||
import { SignIn, SignOutButton } from './routed/components'; | ||
|
||
import { Flex, View } from '@aws-amplify/ui-react'; | ||
import { | ||
Button, | ||
Flex, | ||
Link, | ||
StepperField, | ||
Text, | ||
View, | ||
} from '@aws-amplify/ui-react'; | ||
|
||
import '@aws-amplify/ui-react-storage/styles.css'; | ||
import '@aws-amplify/ui-react-storage/storage-browser-styles.css'; | ||
|
||
const { StorageBrowser } = createStorageBrowser({ | ||
type GetLink = ActionHandler<{ duration: number; fileKey: string }, string>; | ||
|
||
const getLink: GetLink = ({ data, config }) => { | ||
const result = getUrl({ | ||
path: data.key, | ||
options: { | ||
bucket: { bucketName: config.bucket, region: config.region }, | ||
locationCredentialsProvider: config.credentials, | ||
expiresIn: data.duration * 60, | ||
validateObjectExistence: true, | ||
}, | ||
}).then((res) => ({ | ||
status: 'COMPLETE' as const, | ||
value: res.url.toString(), | ||
})); | ||
|
||
return { result }; | ||
}; | ||
|
||
const generateLink: ActionViewConfig<GetLink, 'LinkActionView'> = { | ||
handler: getLink, | ||
viewName: 'LinkActionView', | ||
actionListItem: { | ||
icon: 'download', | ||
label: 'Generate Download Links', | ||
disable: (selected) => !selected?.length, | ||
}, | ||
}; | ||
|
||
const { StorageBrowser, useAction, useView } = createStorageBrowser({ | ||
actions: { custom: { generateLink } }, | ||
config: managedAuthAdapter, | ||
}); | ||
|
||
const LinkActionView = () => { | ||
const [duration, setDuration] = React.useState(60); | ||
|
||
const locationDetailState = useView('LocationDetail'); | ||
const { onActionExit, fileDataItems } = locationDetailState; | ||
|
||
const items = React.useMemo( | ||
() => | ||
!fileDataItems | ||
? [] | ||
: fileDataItems.map((item) => ({ ...item, duration })), | ||
[fileDataItems, duration] | ||
); | ||
|
||
const [{ tasks }, handleCreate] = useAction('generateLink', { items }); | ||
|
||
return ( | ||
<Flex direction="column"> | ||
<Button onClick={onActionExit}>Exit</Button> | ||
<StepperField | ||
label="Duration" | ||
step={15} | ||
value={duration} | ||
min={15} | ||
max={300} | ||
onStepChange={(value) => { | ||
setDuration(value); | ||
}} | ||
/> | ||
<Button onClick={() => handleCreate()}>Start</Button> | ||
{!tasks | ||
? null | ||
: tasks.map(({ data, status, value }) => { | ||
return ( | ||
<Flex direction="row" key={data.fileKey}> | ||
<Text>{data.fileKey}</Text> | ||
{value ? <Link href={value}>link</Link> : null} | ||
<Text>{status}</Text> | ||
</Flex> | ||
); | ||
})} | ||
</Flex> | ||
); | ||
}; | ||
|
||
function Example() { | ||
const [showSignIn, setShowSignIn] = React.useState(false); | ||
|
||
|
@@ -29,9 +114,8 @@ function Example() { | |
> | ||
<SignOutButton onSignOut={() => setShowSignIn(false)} /> | ||
<View flex="1" overflow="hidden"> | ||
<StorageBrowser | ||
displayText={{ LocationsView: { title: 'Home - Managed Auth' } }} | ||
/> | ||
<StorageBrowser views={{ LinkActionView }} /> | ||
<StorageBrowser.LocationActionView /> | ||
</View> | ||
</Flex> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,6 @@ import { SignOutButton } from '../../components'; | |
import { StorageBrowser } from '../../StorageBrowser'; | ||
|
||
import '@aws-amplify/ui-react-storage/styles.css'; | ||
import '@aws-amplify/ui-react-storage/storage-browser-styles.css'; | ||
|
||
export default function Page() { | ||
const { back, query, pathname, replace } = useRouter(); | ||
|
@@ -50,7 +49,10 @@ export default function Page() { | |
}} | ||
/> | ||
{typeof query.actionType === 'string' ? ( | ||
<dialog open={!!query.actionType}> | ||
<dialog | ||
open={!!query.actionType} | ||
style={{ width: '50vw', position: 'absolute', zIndex: 1000 }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sanity check: intended inline css? |
||
> | ||
<StorageBrowser.LocationActionView | ||
onExit={() => { | ||
replace({ query: { ...query, actionType: undefined } }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking but I think this file is not used anywhere and can be cleared. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you're probably right. We can do that separately for sure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be kept?