Skip to content

Commit

Permalink
Impl [Download] Revamp Download by Figma
Browse files Browse the repository at this point in the history
  • Loading branch information
mariana-furyk committed Oct 24, 2023
1 parent b524f76 commit 79c0d5f
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 302 deletions.
223 changes: 47 additions & 176 deletions src/common/Download/Download.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,202 +17,73 @@ illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import React, { useState, useEffect, useCallback, useRef } from 'react'
import React, { useRef } from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
import classnames from 'classnames'
import { useDispatch } from 'react-redux'
import { useParams } from 'react-router-dom'
import classnames from 'classnames'

import ProgressRing from '../ProgressRing/ProgressRing'
import { RoundedIcon, Tooltip, TextTooltipTemplate } from 'igz-controls/components'

import { mainHttpClient } from '../../httpClient'
import { setNotification } from '../../reducers/notificationReducer'
import downloadFile from '../../utils/downloadFile'
import { setDownloadItem } from '../../reducers/downloadReducer'

import './download.scss'
import colors from 'igz-controls/scss/colors.scss'
import { ReactComponent as DownloadIcon } from '../../../../dashboard-react-controls/src/lib/images/download.svg'

const DEFAULT_FILE_NAME = 'mlrun-file'

const Download = ({ disabled, fileName, path, user }) => {
const [progress, setProgress] = useState(0)
const [isDownload, setDownload] = useState(false)
const params = useParams()
const downloadContainerClassnames = classnames(
'download-container',
disabled && 'download-container_disabled'
)
import './download.scss'

const Download = ({ className, disabled, fileName, onlyIcon, path, user, withoutIcon }) => {
const downloadRef = useRef(null)
const dispatch = useDispatch()

const progressRingRadius = '20'
const progressRingStroke = '3'

let file = fileName ?? path.match(/\/(?<filename>[^/]+)$/)?.groups?.filename ?? DEFAULT_FILE_NAME

const retryDownload = useCallback(
item => {
mainHttpClient
.get(item.url)
.then(response => {
downloadFile(item.file, response)
dispatch(
setNotification({
status: response.status,
url: response.config.url,
id: Math.random(),
message: 'Downloaded successfully'
})
)
})
.catch(() => {
dispatch(
setNotification({
status: 400,
url: item.url,
file: item.file,
id: Math.random(),
message: 'Failed to download'
})
)
})
},
[dispatch]
)

const downloadCallback = useCallback(() => {
if (isDownload) {
const config = {
onDownloadProgress: progressEvent => {
const percentCompleted = (progressEvent.loaded * 100) / progressEvent.total
setProgress(percentCompleted)
},
cancelToken: new axios.CancelToken(cancel => {
downloadRef.current.cancel = cancel
}),
params: { path },
responseType: 'arraybuffer'
}

if (path.startsWith('/User')) {
config.params.user = user
}

mainHttpClient
.get(`projects/${params.projectName}/files`, config)
.then(response => {
downloadFile(file, response)
dispatch(
setNotification({
status: response.status,
url: response.config.url,
id: Math.random(),
message: 'Downloaded successfully'
})
)
if (downloadRef.current) {
setDownload(false)
setProgress(0)
}
})
.catch(error => {
if (axios.isCancel(error)) {
setDownload(false)
return setProgress(0)
}
dispatch(
setNotification({
status: 400,
url: `/files?path=${path}&user=${user}`,
file,
id: Math.random(),
retry: item => retryDownload(item),
message: 'Failed to download'
})
)
if (downloadRef.current) {
setDownload(false)
setProgress(0)
}
})
.finally(() => {
if (downloadRef.current) downloadRef.current.cancel = null
})
}
}, [isDownload, path, params.projectName, user, file, dispatch, retryDownload])

useEffect(() => {
let cancelFetch = downloadRef.current

downloadCallback()

return () => {
cancelFetch.cancel && cancelFetch.cancel()
}
}, [downloadCallback, downloadRef])
const downloadClassNames = classnames('download', className, disabled && 'download_disabled')

const handleClick = () => {
if (!disabled) {
if (downloadRef.current?.cancel) {
return downloadRef.current.cancel('cancel')
}

setDownload(!isDownload)
}
dispatch(
setDownloadItem({
filename: fileName,
id: path + Date.now(),
path,
user: user
})
)
}

return (
<div className={downloadContainerClassnames} ref={downloadRef} onClick={handleClick}>
<ProgressRing
radius={progressRingRadius}
stroke={progressRingStroke}
progress={progress}
color={progress !== 0 ? colors.mulledWine : 'transparent'}
>
<g className={!isDownload ? 'download' : 'downloading'}>
<circle r="12" cx="20px" cy="20px" />
{!isDownload ? (
<g className="download-container__icon">
<rect
width="9.05318"
height="1.50886"
transform="matrix(-0.711236 -0.702953 0.711236 -0.702953 12.4389 19.0002)"
/>
<rect
width="9.05318"
height="1.50886"
transform="matrix(0.711236 -0.702953 0.711236 0.702953 11.4879 18.0004)"
/>
<rect
x="11.7744"
y="17.5"
width="13"
height="1.5"
transform="rotate(-90 11.7744 17.5)"
/>
<rect x="5" y="19" width="15" height="1.5" />
</g>
) : (
<g className="cancel_container">
<rect x="2.19238" y="1" width="13" height="1.5" transform="rotate(45 2.19238 1)" />
<rect
width="13"
height="1.5"
transform="matrix(-0.707107 0.707107 0.707107 0.707107 10.1924 1)"
/>
</g>
)}
</g>
</ProgressRing>
<div
className={downloadClassNames}
data-testid="download-btn"
ref={downloadRef}
onClick={handleClick}
>
{onlyIcon ? (
<Tooltip template={<TextTooltipTemplate text="Download" />}>
<RoundedIcon>
<DownloadIcon />
</RoundedIcon>
</Tooltip>
) : (
<>
<div className="download__label">Download</div>
{!withoutIcon && <DownloadIcon />}
</>
)}
</div>
)
}

Download.defaultProps = {
className: '',
disabled: false,
onlyIcon: false,
withoutIcon: false
}

Download.propTypes = {
className: PropTypes.string,
disabled: PropTypes.bool,
fileName: PropTypes.string,
path: PropTypes.string.isRequired
onlyIcon: PropTypes.bool,
path: PropTypes.string.isRequired,
user: PropTypes.string,
withoutIcon: PropTypes.bool
}

export default React.memo(Download)
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import { Transition, TransitionGroup } from 'react-transition-group'

import DownloadItem from './DownloadItem'

import './donwloadWrapper.scss'
import './downloadContainer.scss'

const DownloadWrapper = () => {
const DownloadContainer = () => {
const downloadStore = useSelector(store => store.downloadStore)
const duration = 500

Expand All @@ -49,7 +49,11 @@ const DownloadWrapper = () => {
<Transition timeout={duration}>
{state =>
createPortal(
<div className="download-container" style={{ ...transitionStyles[state] }}>
<div
className="download-container"
data-testid="download-container"
style={{ ...transitionStyles[state] }}
>
<div className="download-container__header">Downloads</div>
<div className="download-container__body">
{downloadStore.downloadList.map((downloadItem, index) => {
Expand All @@ -66,4 +70,4 @@ const DownloadWrapper = () => {
)
}

export default DownloadWrapper
export default DownloadContainer
25 changes: 16 additions & 9 deletions src/common/Download/DownloadItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const DownloadItem = ({ downloadItem }) => {
const params = useParams()

const downloadRef = useRef(null)
const timeoutRef = useRef(null)
const dispatch = useDispatch()

let file = useMemo(
Expand Down Expand Up @@ -85,6 +86,7 @@ const DownloadItem = ({ downloadItem }) => {
setDownload(false)
return setProgress(0)
}

if (downloadRef.current) {
setDownload(false)
setProgress(0)
Expand All @@ -94,7 +96,8 @@ const DownloadItem = ({ downloadItem }) => {
if (downloadRef.current) {
downloadRef.current.cancel = null
}
setTimeout(() => {

timeoutRef.current = setTimeout(() => {
dispatch(removeDownloadItem(downloadItem.id))
}, 1000)
})
Expand All @@ -121,16 +124,24 @@ const DownloadItem = ({ downloadItem }) => {
}
}, [downloadCallback, downloadRef])

const handleClick = () => {
const handleCancel = () => {
if (downloadRef.current?.cancel) {
return downloadRef.current.cancel('cancel')
}

setDownload(!isDownload)
}

const handleRetry = () => {
setDownload(true)

if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
}

return (
<div className="download-item" ref={downloadRef}>
<div className="download-item" data-testid="download-item" ref={downloadRef}>
<Tooltip className="download-item__filename" template={<TextTooltipTemplate text={file} />}>
{file}
</Tooltip>
Expand All @@ -145,15 +156,11 @@ const DownloadItem = ({ downloadItem }) => {
</div>
<div className="download-item__buttons">
{isDownload ? (
<RoundedIcon onClick={handleClick}>
<RoundedIcon onClick={handleCancel}>
<Close />
</RoundedIcon>
) : !isSuccessResponse ? (
<RoundedIcon
onClick={() => {
setDownload(true)
}}
>
<RoundedIcon onClick={handleRetry}>
<RefreshIcon />
</RoundedIcon>
) : null}
Expand Down
Loading

0 comments on commit 79c0d5f

Please sign in to comment.