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 Jul 21, 2023
1 parent 038331c commit 8b9190c
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 1 deletion.
174 changes: 174 additions & 0 deletions src/common/Download/DownloadItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
Copyright 2019 Iguazio Systems Ltd.
Licensed under the Apache License, Version 2.0 (the "License") with
an addition restriction as set forth herein. You may not use this
file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
In addition, you may not use the software for any purposes that are
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, useMemo } from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
import { useDispatch } from 'react-redux'
import { useParams } from 'react-router-dom'

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

import { mainHttpClient } from '../../httpClient'
import downloadFile from '../../utils/downloadFile'
import { removeDownloadItem } from '../../reducers/downloadReducer'

import { ReactComponent as Close } from 'igz-controls/images/close.svg'
import { ReactComponent as RefreshIcon } from 'igz-controls/images/refresh.svg'

const DEFAULT_FILE_NAME = 'mlrun-file'

const DownloadItem = ({ downloadItem }) => {
const [progress, setProgress] = useState(0)
const [isDownload, setDownload] = useState(true)
const [isSuccessResponse, setIsSuccessResponse] = useState(null)
const params = useParams()

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

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

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

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

mainHttpClient
.get(`projects/${params.projectName}/files`, config)
.then(response => {
downloadFile(file, response)
if (downloadRef.current) {
setDownload(false)
setIsSuccessResponse(true)
}
})
.catch(error => {
if (axios.isCancel(error)) {
setDownload(false)
return setProgress(0)
}
if (downloadRef.current) {
setDownload(false)
setProgress(0)
}
})
.finally(() => {
if (downloadRef.current) {
downloadRef.current.cancel = null
}
setTimeout(() => {
dispatch(removeDownloadItem(downloadItem.id))
}, 1000)
})
}
}, [
isDownload,
downloadItem.path,
params.projectName,
downloadItem.user,
file,
dispatch,
downloadItem.id
])

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

setTimeout(() => {
downloadCallback()
}, 1000)

return () => {
cancelFetch.cancel && cancelFetch.cancel()
}
}, [downloadCallback, downloadRef])

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

setDownload(!isDownload)
}

return (
<div className="download-item" ref={downloadRef}>
<Tooltip className="download-item__filename" template={<TextTooltipTemplate text={file} />}>
{file}
</Tooltip>
<div className="download-item__status">
{isDownload ? (
`${Math.floor(progress)}%`
) : isSuccessResponse ? (
<div className="download-item__message_succeed">Done</div>
) : (
<div className="download-item__message_failed">Failed</div>
)}
</div>
<div className="download-item__buttons">
{isDownload ? (
<RoundedIcon onClick={handleClick}>
<Close />
</RoundedIcon>
) : !isSuccessResponse ? (
<RoundedIcon
onClick={() => {
setDownload(true)
}}
>
<RefreshIcon />
</RoundedIcon>
) : null}
</div>
</div>
)
}

DownloadItem.propTypes = {
downloadItem: PropTypes.shape({
filename: PropTypes.string,
id: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
user: PropTypes.string
}).isRequired
}

export default React.memo(DownloadItem)
69 changes: 69 additions & 0 deletions src/common/Download/DownloadWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright 2019 Iguazio Systems Ltd.
Licensed under the Apache License, Version 2.0 (the "License") with
an addition restriction as set forth herein. You may not use this
file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
In addition, you may not use the software for any purposes that are
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 from 'react'
import { createPortal } from 'react-dom'
import { useSelector } from 'react-redux'
import { Transition, TransitionGroup } from 'react-transition-group'

import DownloadItem from './DownloadItem'

import './donwloadWrapper.scss'

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

const transitionStyles = {
entered: {
transform: 'translateY(-130px)',
opacity: 1,
transition: `transform ${duration}ms ease-in-out, opacity ${duration}ms ease-in-out`
},
exiting: {
transform: 'translateY(0px)',
opacity: 0,
transition: `transform ${duration}ms ease-in-out, opacity ${duration}ms ease-in-out`
}
}

return (
<TransitionGroup>
{downloadStore.downloadList.length > 0 && (
<Transition timeout={duration}>
{state =>
createPortal(
<div className="download-container" style={{ ...transitionStyles[state] }}>
<div className="download-container__header">Downloads</div>
<div className="download-container__body">
{downloadStore.downloadList.map((downloadItem, index) => {
return <DownloadItem downloadItem={downloadItem} key={index} />
})}
</div>
</div>,
document.getElementById('overlay_container')
)
}
</Transition>
)}
</TransitionGroup>
)
}

export default DownloadWrapper
62 changes: 62 additions & 0 deletions src/common/Download/downloadWrapper.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@import '~igz-controls/scss/colors';
@import '~igz-controls/scss/shadows';

.download-container {
position: fixed;
right: 24px;
bottom: -100px;
opacity: 0;
z-index: 1000;
width: 220px;
min-height: 90px;
max-height: 200px;
padding: 15px;
color: $white;
background-color: $darkPurple;
border-radius: 2px;
box-shadow: $tooltipShadow;

&__header {
margin-bottom: 15px;
}

&__body {
display: flex;
flex-direction: column;

.download-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
font-size: 12px;

&__message {
&_succeed {
color: $brightTurquoise;
}

&_failed {
color: $burntSienna;
}
}

&__buttons {
min-width: 20px;

.round-icon-cp__circle {
width: 20px;
height: 20px;
}
}
}
}

.downloading {
cursor: pointer;
}

.cancel_container {
transform: translate(14px, 14px);
}
}
2 changes: 1 addition & 1 deletion src/components/Datasets/datasets.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const generatePageData = (selectedItem, viewMode) => ({
additionalInfo: {
header: 'Producer',
body: generateProducerDetailsInfo(selectedItem),
hidden: !selectedItem.item?.producer
hidden: !selectedItem.producer
},
hideBackBtn: viewMode === FULL_VIEW_MODE,
withToggleViewBtn: true
Expand Down
46 changes: 46 additions & 0 deletions src/reducers/downloadReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright 2019 Iguazio Systems Ltd.
Licensed under the Apache License, Version 2.0 (the "License") with
an addition restriction as set forth herein. You may not use this
file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
In addition, you may not use the software for any purposes that are
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 { createSlice } from '@reduxjs/toolkit'

const initialState = {
downloadList: []
}

const downloadSlice = createSlice({
name: 'download',
initialState,
reducers: {
setDownloadItem(state, { payload }) {
if (payload.error) {
/* eslint-disable-next-line no-console */
console.error(payload.error)
}

state.downloadList.push(payload)
},
removeDownloadItem(state, { payload }) {
state.downloadList = state.downloadList.filter(item => item.id !== payload)
}
}
})

export const { setDownloadItem, removeDownloadItem } = downloadSlice.actions

export default downloadSlice.reducer
2 changes: 2 additions & 0 deletions src/store/toolkitStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { configureStore } from '@reduxjs/toolkit'
import appStore from '../reducers/appReducer'
import artifactsStore from '../reducers/artifactsReducer'
import detailsStore from '../reducers/detailsReducer'
import downloadStore from '../reducers/downloadReducer'
import featureStore from '../reducers/featureStoreReducer'
import filtersStore from '../reducers/filtersReducer'
import functionsStore from '../reducers/functionReducer'
Expand All @@ -37,6 +38,7 @@ const toolkitStore = configureStore({
appStore,
artifactsStore,
detailsStore,
downloadStore,
featureStore,
filtersStore,
functionsStore,
Expand Down

0 comments on commit 8b9190c

Please sign in to comment.