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

feat(download-button): delete button #304

Merged
merged 11 commits into from
Mar 23, 2020
5 changes: 4 additions & 1 deletion src/scripts/components/button/button.styl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
fill: $textWhite

svg
padding-right: emCalc(7px)
fill: $textWhite

svg + .label
padding-left: emCalc(7px)

&:hover, &:focus
color: $main
Expand Down
15 changes: 12 additions & 3 deletions src/scripts/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface Props {
icon?: FunctionComponent;
link?: string;
className?: string;
onClick?: () => void;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
}

const Button: FunctionComponent<Props> = ({
Expand All @@ -23,12 +23,21 @@ const Button: FunctionComponent<Props> = ({

return link ? (
<Link className={classes} to={link}>
{Icon && <Icon />} {label && <FormattedMessage id={label} />}
{Icon && <Icon />}
{label && (
<span className={styles.label}>
<FormattedMessage id={label} />
</span>
)}
</Link>
) : (
<button className={classes} onClick={onClick}>
{Icon && <Icon />}
{label && <FormattedMessage id={label} />}
{label && (
<span className={styles.label}>
<FormattedMessage id={label} />
</span>
)}
</button>
);
};
Expand Down
40 changes: 40 additions & 0 deletions src/scripts/components/download-button/download-button.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@require '../../../variables.styl'

.downloadButton
width: emCalc(20px)
height: emCalc(20px)

button, svg
width: 100%
height: 100%

.complete svg
fill: $main

.notDownloaded svg
fill: $textDefault

.circle
color: red
transform: rotate(-90deg)
fill: none
stroke-width: 4px
stroke: $main
stroke-dasharray: 100

circle
transition: 0.5s all linear

.delete
.trash
display: none

.complete
display: block

&:hover
.trash
display: block

.complete
display: none
72 changes: 61 additions & 11 deletions src/scripts/components/download-button/download-button.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,83 @@
import React, {FunctionComponent} from 'react';
import {useSelector} from 'react-redux';

import {isElectron, downloadUrl, deleteId} from '../../libs/electron/index';
import {downloadedDataSelector} from '../../selectors/offline/downloaded';
import {downloadProgressSelector} from '../../selectors/offline/progress';
import {DownloadIcon} from '../icons/download-icon';
import {DownloadCompleteIcon} from '../icons/download-complete-icon';
import {DeleteIcon} from '../icons/delete-icon';
import Button from '../button/button';

import styles from './download-button.styl';

interface Props {
url: string;
id: string;
url: string;
}

export const DownloadButton: FunctionComponent<Props> = ({url, id}) => {
const downloadedData = useSelector(downloadedDataSelector);
const downloadProgress = useSelector(downloadProgressSelector);
const onDownload = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
event.preventDefault();
isElectron() && downloadUrl(url);
};
const isDownloading = typeof downloadProgress[url] === 'number';
const progress = isDownloading ? Math.ceil(downloadProgress[url] * 100) : 0;
const isDownloaded = [
...downloadedData.stories,
...downloadedData.layers
].includes(id);

if (!isElectron()) {
return null;
}

return (
<div>
<button onClick={onDownload}>Download</button>
<button
onClick={event => {
event.stopPropagation();
event.preventDefault();
deleteId(id);
}}>
Delete
</button>
<div className={styles.downloadButton}>
{!isDownloaded && !isDownloading && (
<Button
icon={DownloadIcon}
onClick={onDownload}
className={styles.notDownloaded}
/>
)}
{isDownloaded && (
<div className={styles.delete}>
<Button className={styles.complete} icon={DownloadCompleteIcon} />
<Button
className={styles.trash}
icon={DeleteIcon}
onClick={event => {
event.stopPropagation();
event.preventDefault();
deleteId(id);
}}
/>
</div>
)}
{isDownloading && (
<svg
className={styles.circle}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 40 40">
<circle
style={{strokeDashoffset: 0}}
stroke="rgba(255, 255, 255, 0.1)"
cx="20"
cy="20"
r="16"
/>
<circle
style={{strokeDashoffset: 100 - progress}}
cx="20"
cy="20"
r="16"
/>
</svg>
)}
</div>
);
};
12 changes: 12 additions & 0 deletions src/scripts/components/icons/delete-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, {FunctionComponent} from 'react';

export const DeleteIcon: FunctionComponent = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z" />
</svg>
);
15 changes: 15 additions & 0 deletions src/scripts/components/icons/download-complete-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, {FunctionComponent} from 'react';

export const DownloadCompleteIcon: FunctionComponent = () => (
<svg
width="24"
height="24"
viewBox="0 0 18 18"
xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.75 8.025L7.2 11.475L14.25 4.5L12.75 3L7.2 8.55L5.25 6.6L3.75 8.025ZM14.25 13.5H3.75V15H14.25V13.5Z"
/>
</svg>
);
15 changes: 15 additions & 0 deletions src/scripts/components/icons/download-icon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, {FunctionComponent} from 'react';

export const DownloadIcon: FunctionComponent = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15 9H19L12 16L5 9H9V3H15V9ZM5 20V18H19V20H5Z"
/>
</svg>
);
33 changes: 2 additions & 31 deletions src/scripts/components/layer-list-item/layer-list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
import React, {FunctionComponent} from 'react';
import {FormattedMessage} from 'react-intl';

import {isElectron, downloadUrl, deleteId} from '../../libs/electron/index';
import {replaceUrlPlaceholders} from '../../libs/replace-url-placeholders';
import config from '../../config/main';
import {DownloadButton} from '../download-button/download-button';

import {LayerListItem as LayerListItemType} from '../../types/layer-list';
import {DownloadProgress} from '../../types/download-progress';

import styles from './layer-list-item.styl';

interface Props {
layer: LayerListItemType;
isMainSelected: boolean;
isDownloaded: boolean;
downloadProgress: DownloadProgress;
onSelect: (id: string, isMain: boolean) => void;
}

const LayerListItem: FunctionComponent<Props> = ({
layer,
isMainSelected,
isDownloaded,
downloadProgress,
onSelect
}) => {
const packageUrl = config.api.layerOfflinePackage;
const offlineUrl = replaceUrlPlaceholders(packageUrl, {id: layer.id});
const onDownload = () => isElectron() && downloadUrl(offlineUrl);
const progress = downloadProgress[offlineUrl];

return (
<div className={styles.layerItem} onClick={() => onSelect(layer.id, true)}>
Expand All @@ -44,29 +37,7 @@ const LayerListItem: FunctionComponent<Props> = ({
</button>
)}

{isElectron() && typeof progress === 'number' && (
<span>{Math.ceil(progress * 100)}</span>
)}

{isElectron() && !isDownloaded && (
<button
onClick={event => {
event.stopPropagation();
onDownload();
}}>
Download
</button>
)}

{isElectron() && isDownloaded && (
<button
onClick={event => {
event.stopPropagation();
deleteId(layer.id);
}}>
Delete
</button>
)}
<DownloadButton url={offlineUrl} id={layer.id} />
</div>
);
};
Expand Down
7 changes: 0 additions & 7 deletions src/scripts/components/layer-list/layer-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,19 @@ import LayerListItem from '../layer-list-item/layer-list-item';

import {SelectedLayerIdsState} from '../../reducers/layers/selected-ids';

import {DownloadProgress} from '../../types/download-progress';
import {LayerListItem as LayerListItemType} from '../../types/layer-list';

import styles from './layer-list.styl';

interface Props {
selectedLayerIds: SelectedLayerIdsState;
layers: LayerListItemType[];
downloadedLayerIds: string[];
downloadProgress: DownloadProgress;
onSelect: (id: string, isMain: boolean) => void;
}

const LayerList: FunctionComponent<Props> = ({
selectedLayerIds,
layers,
downloadedLayerIds,
downloadProgress,
onSelect
}) => {
const {mainId} = selectedLayerIds;
Expand All @@ -36,8 +31,6 @@ const LayerList: FunctionComponent<Props> = ({
<LayerListItem
onSelect={(id, isMain) => onSelect(id, isMain)}
isMainSelected={isMainSelected}
isDownloaded={downloadedLayerIds.includes(layer.id)}
downloadProgress={downloadProgress}
layer={layer}
/>
</li>
Expand Down
6 changes: 0 additions & 6 deletions src/scripts/components/layer-selector/layer-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ import {layersSelector} from '../../selectors/layers/list';
import styles from './layer-selector.styl';
import setSelectedLayerIdsAction from '../../actions/set-selected-layer-id';
import {selectedLayerIdsSelector} from '../../selectors/layers/selected-ids';
import {downloadedDataSelector} from '../../selectors/offline/downloaded';
import {downloadProgressSelector} from '../../selectors/offline/progress';

const LayerSelector: FunctionComponent = () => {
const dispatch = useDispatch();
const layers = useSelector(layersSelector);
const selectedLayerIds = useSelector(selectedLayerIdsSelector);
const showLayerSelector = useSelector(showLayerSelectorSelector);
const downloadedData = useSelector(downloadedDataSelector);
const downloadProgress = useSelector(downloadProgressSelector);
const selectedMainLayer = layers.find(
layer => layer.id === selectedLayerIds.mainId
);
Expand Down Expand Up @@ -67,8 +63,6 @@ const LayerSelector: FunctionComponent = () => {
<LayerList
layers={layers}
selectedLayerIds={selectedLayerIds}
downloadedLayerIds={downloadedData.layers}
downloadProgress={downloadProgress}
onSelect={(layerId, isMain) =>
dispatch(setSelectedLayerIdsAction(layerId, isMain))
}
Expand Down
9 changes: 9 additions & 0 deletions src/scripts/components/story-list-item/story-list-item.styl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
line-height: 30px

.imageInfo
display: flex
flex-direction: column
padding: 16px
height: 40%
background-color: $darkGrey1
Expand Down Expand Up @@ -56,3 +58,10 @@ a

.description
font-size: 1.5em

.downloadButton
display: flex
flex-grow: 1
flex-direction: column
justify-content: flex-end
align-self: flex-end
4 changes: 3 additions & 1 deletion src/scripts/components/story-list-item/story-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ const StoryListItemContent: FunctionComponent<Props> = ({
<div className={styles.imageInfo}>
<p className={styles.title}>{story.title}</p>
<p className={styles.description}>{story.description}</p>
<DownloadButton url={downloadUrl} id={downloadId} />
<div className={styles.downloadButton}>
<DownloadButton url={downloadUrl} id={downloadId} />
</div>
</div>
</div>
);
Expand Down
Loading