Add buttons for deleting downloaded tracks from playlist or album

This commit is contained in:
Lei Nelissen
2022-01-02 17:46:50 +01:00
parent db1c9d8af9
commit 95a008d8af
7 changed files with 52 additions and 8 deletions

View File

@@ -20,6 +20,10 @@ const BaseButton = styled.Pressable`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-grow: 1; flex-grow: 1;
${(props) => props.disabled && css`
opacity: 0.25;
`}
`; `;
const ButtonText = styled.Text<{ active?: boolean }>` const ButtonText = styled.Text<{ active?: boolean }>`
@@ -32,7 +36,7 @@ const ButtonText = styled.Text<{ active?: boolean }>`
`; `;
const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) { const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) {
const { icon: Icon, title, ...rest } = props; const { icon: Icon, title, disabled, ...rest } = props;
const defaultStyles = useDefaultStyles(); const defaultStyles = useDefaultStyles();
const [isPressed, setPressed] = useState(false); const [isPressed, setPressed] = useState(false);
const handlePressIn = useCallback(() => setPressed(true), []); const handlePressIn = useCallback(() => setPressed(true), []);
@@ -41,6 +45,7 @@ const Button = React.forwardRef<View, ButtonProps>(function Button(props, ref) {
return ( return (
<BaseButton <BaseButton
{...rest} {...rest}
disabled={disabled}
ref={ref} ref={ref}
onPressIn={handlePressIn} onPressIn={handlePressIn}
onPressOut={handlePressOut} onPressOut={handlePressOut}

View File

@@ -51,5 +51,7 @@
"download-playlist": "Download Playlist", "download-playlist": "Download Playlist",
"no-downloads": "You have not yet downloaded any tracks", "no-downloads": "You have not yet downloaded any tracks",
"delete-all-tracks": "Delete All Tracks", "delete-all-tracks": "Delete All Tracks",
"delete-album": "Delete Album",
"delete-playlist": "Delete Playlist",
"total-download-size": "Total Download Size" "total-download-size": "Total Download Size"
} }

View File

@@ -48,6 +48,7 @@ export type LocaleKeys = 'play-next'
| 'download-track' | 'download-track'
| 'download-album' | 'download-album'
| 'download-playlist' | 'download-playlist'
| 'delete-all-tracks' | 'delete-album'
| 'delete-playlist'
| 'total-download-size' | 'total-download-size'
| 'no-downloads' | 'no-downloads'

View File

@@ -37,7 +37,8 @@ const Album: React.FC = () => {
refresh={refresh} refresh={refresh}
playButtonText={t('play-album')} playButtonText={t('play-album')}
shuffleButtonText={t('shuffle-album')} shuffleButtonText={t('shuffle-album')}
downloadText={t('download-album')} downloadButtonText={t('download-album')}
deleteButtonText={t('delete-album')}
/> />
); );
}; };

View File

@@ -37,7 +37,8 @@ const Playlist: React.FC = () => {
listNumberingStyle='index' listNumberingStyle='index'
playButtonText={t('play-playlist')} playButtonText={t('play-playlist')}
shuffleButtonText={t('shuffle-playlist')} shuffleButtonText={t('shuffle-playlist')}
downloadText={t('download-playlist')} downloadButtonText={t('download-playlist')}
deleteButtonText={t('delete-playlist')}
/> />
); );
}; };

View File

@@ -19,8 +19,10 @@ import { MusicNavigationProp } from 'screens/Music/types';
import DownloadIcon from 'components/DownloadIcon'; import DownloadIcon from 'components/DownloadIcon';
import Button from 'components/Button'; import Button from 'components/Button';
import CloudDownArrow from 'assets/cloud-down-arrow.svg'; import CloudDownArrow from 'assets/cloud-down-arrow.svg';
import Trash from 'assets/trash.svg';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { downloadTrack } from 'store/downloads/actions'; import { downloadTrack, removeDownloadedTrack } from 'store/downloads/actions';
import { selectDownloadedTracks } from 'store/downloads/selectors';
const Screen = Dimensions.get('screen'); const Screen = Dimensions.get('screen');
@@ -68,7 +70,8 @@ interface TrackListViewProps {
refresh: () => void; refresh: () => void;
playButtonText: string; playButtonText: string;
shuffleButtonText: string; shuffleButtonText: string;
downloadText: string; downloadButtonText: string;
deleteButtonText: string;
listNumberingStyle?: 'album' | 'index'; listNumberingStyle?: 'album' | 'index';
} }
@@ -80,7 +83,8 @@ const TrackListView: React.FC<TrackListViewProps> = ({
refresh, refresh,
playButtonText, playButtonText,
shuffleButtonText, shuffleButtonText,
downloadText, downloadButtonText,
deleteButtonText,
listNumberingStyle = 'album', listNumberingStyle = 'album',
}) => { }) => {
const defaultStyles = useDefaultStyles(); const defaultStyles = useDefaultStyles();
@@ -88,6 +92,7 @@ const TrackListView: React.FC<TrackListViewProps> = ({
// Retrieve state // Retrieve state
const tracks = useTypedSelector((state) => state.music.tracks.entities); const tracks = useTypedSelector((state) => state.music.tracks.entities);
const isLoading = useTypedSelector((state) => state.music.tracks.isLoading); const isLoading = useTypedSelector((state) => state.music.tracks.isLoading);
const downloadedTracks = useTypedSelector(selectDownloadedTracks(trackIds));
// Retrieve helpers // Retrieve helpers
const getImage = useGetImage(); const getImage = useGetImage();
@@ -110,6 +115,9 @@ const TrackListView: React.FC<TrackListViewProps> = ({
const downloadAllTracks = useCallback(() => { const downloadAllTracks = useCallback(() => {
trackIds.forEach((trackId) => dispatch(downloadTrack(trackId))); trackIds.forEach((trackId) => dispatch(downloadTrack(trackId)));
}, [dispatch, trackIds]); }, [dispatch, trackIds]);
const deleteAllTracks = useCallback(() => {
downloadedTracks.forEach((trackId) => dispatch(removeDownloadedTrack(trackId)));
}, [dispatch, downloadedTracks]);
return ( return (
<ScrollView <ScrollView
@@ -162,7 +170,20 @@ const TrackListView: React.FC<TrackListViewProps> = ({
</TrackContainer> </TrackContainer>
</TouchableHandler> </TouchableHandler>
)} )}
<Button icon={CloudDownArrow} title={downloadText} style={{ marginTop: 12 }} onPress={downloadAllTracks} /> <WrappableButtonRow style={{ marginTop: 24 }}>
<WrappableButton
icon={CloudDownArrow}
title={downloadButtonText}
onPress={downloadAllTracks}
disabled={downloadedTracks.length === trackIds.length}
/>
<WrappableButton
icon={Trash}
title={deleteButtonText}
onPress={deleteAllTracks}
disabled={downloadedTracks.length === 0}
/>
</WrappableButtonRow>
</View> </View>
</ScrollView> </ScrollView>
); );

View File

@@ -0,0 +1,13 @@
import { createSelector, EntityId } from '@reduxjs/toolkit';
import { intersection } from 'lodash';
import { AppState } from 'store';
export const selectDownloadedTracks = (trackIds: EntityId[]) => (
createSelector(
(state: AppState) => state.downloads,
({ entities, ids }) => {
return intersection(trackIds, ids)
.filter((id) => entities[id]?.isComplete);
}
)
);