Skip to content

Commit

Permalink
[PAY-2485][PAY-2483] Adds download Retry behavior for mobile (#7583)
Browse files Browse the repository at this point in the history
Co-authored-by: Raymond Jacobson <ray@audius.co>
  • Loading branch information
schottra and raymondjacobson authored Feb 14, 2024
1 parent 8d509fc commit 199a1a5
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 57 deletions.
5 changes: 5 additions & 0 deletions packages/common/src/store/downloads/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * as downloadsSelectors from './selectors'
export {
default as downloadsReducer,
actions as downloadsActions
} from './slice'
9 changes: 9 additions & 0 deletions packages/common/src/store/downloads/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CommonState } from '../commonStore'

export const getDownloadedPercentage = (state: CommonState) =>
state.downloads.downloadedPercentage
export const getFileName = (state: CommonState) => state.downloads.fileName
export const getFetchCancel = (state: CommonState) =>
state.downloads.fetchCancel
export const getTrackName = (state: CommonState) => state.downloads.trackName
export const getDownloadError = (state: CommonState) => state.downloads.error
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type { Nullable } from '@audius/common/utils'
import type { PayloadAction } from '@reduxjs/toolkit'
import { createSlice } from '@reduxjs/toolkit'

import { Nullable } from '~/utils'

export type DownloadState = typeof initialState

type State = {
downloadedPercentage: number
fetchCancel: Nullable<() => void>
trackName: Nullable<string>
fileName: Nullable<string>
error?: Error
}

const initialState: State = {
Expand All @@ -22,9 +24,17 @@ const slice = createSlice({
name: 'downloadTrack',
initialState,
reducers: {
beginDownload: (state) => {
state.error = undefined
},
setDownloadError: (state, action: PayloadAction<Error>) => {
state.error = action.payload
},
// Mobile only
setDownloadedPercentage: (state, action: PayloadAction<number>) => {
state.downloadedPercentage = action.payload
},
// Mobile only
setFileInfo: (
state,
action: PayloadAction<{
Expand All @@ -35,13 +45,20 @@ const slice = createSlice({
state.trackName = action.payload.trackName
state.fileName = action.payload.fileName
},
// Mobile only
setFetchCancel: (state, action: PayloadAction<() => void>) => {
state.fetchCancel = action.payload
}
}
})

export const { setDownloadedPercentage, setFileInfo, setFetchCancel } =
slice.actions
export const {
beginDownload,
setDownloadedPercentage,
setFileInfo,
setFetchCancel,
setDownloadError
} = slice.actions
export const actions = slice.actions

export default slice.reducer
1 change: 1 addition & 0 deletions packages/common/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export * from './solana'
export * from './playlist-updates'
export * from './saved-collections'
export * from './confirmer'
export * from './downloads'
5 changes: 4 additions & 1 deletion packages/common/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ChangePasswordState } from './change-password/types'
import collectibles from './collectibles/slice'
import confirmer from './confirmer/reducer'
import { ConfirmerState } from './confirmer/types'
import downloads, { DownloadState } from './downloads/slice'
import gatedContent from './gated-content/slice'
import musicConfettiReducer, {
MusicConfettiState
Expand Down Expand Up @@ -282,7 +283,8 @@ export const reducers = (
collectibles,

upload,
confirmer
confirmer,
downloads
})

export type CommonState = {
Expand Down Expand Up @@ -410,4 +412,5 @@ export type CommonState = {

upload: UploadState
confirmer: ConfirmerState
downloads: DownloadState
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { useCallback } from 'react'

import { downloadsSelectors } from '@audius/common/store'
import { View } from 'react-native'
import { useSelector } from 'react-redux'

import { NativeDrawer } from 'app/components/drawer'
import LoadingSpinner from 'app/components/loading-spinner'
import Text from 'app/components/text'
import { getFileName, getFetchCancel } from 'app/store/download/selectors'
import { makeStyles } from 'app/styles'
import { useColor } from 'app/utils/theme'

const { getFileName, getFetchCancel } = downloadsSelectors

const useStyles = makeStyles(({ palette }) => ({
view: {
display: 'flex',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import { useCallback } from 'react'
import { useCallback, useEffect } from 'react'

import { DownloadQuality } from '@audius/common/models'
import type { CommonState } from '@audius/common/store'
import {
cacheTracksSelectors,
tracksSocialActions,
useWaitForDownloadModal
useWaitForDownloadModal,
downloadsSelectors
} from '@audius/common/store'
import { getDownloadFilename } from '@audius/common/utils'
import { css } from '@emotion/native'
import { useDispatch, useSelector } from 'react-redux'

import {
Divider,
Flex,
Hint,
IconError,
IconReceive,
Text,
Flex,
TextLink,
useTheme
} from '@audius/harmony-native'
import Drawer from 'app/components/drawer'

import LoadingSpinner from '../loading-spinner'
const { getTrack } = cacheTracksSelectors
const { getDownloadError } = downloadsSelectors

const messages = {
title: 'Downloading...'
title: 'Downloading...',
somethingWrong:
'Something went wrong. Please check your connection and storage and try again.',
tryAgain: 'Try again.'
}

export const WaitForDownloadDrawer = () => {
Expand All @@ -36,6 +44,8 @@ export const WaitForDownloadDrawer = () => {
onClosed
} = useWaitForDownloadModal()

const downloadError = useSelector(getDownloadError)

const { spacing } = useTheme()
const track = useSelector((state: CommonState) =>
getTrack(state, { id: parentTrackId ?? trackIds[0] })
Expand All @@ -53,6 +63,20 @@ export const WaitForDownloadDrawer = () => {
onClosed()
}, [onClosed, dispatch])

const performDownload = useCallback(() => {
dispatch(
tracksSocialActions.downloadTrack({
trackIds,
parentTrackId,
original: quality === DownloadQuality.ORIGINAL
})
)
}, [parentTrackId, trackIds, quality, dispatch])

useEffect(() => {
performDownload()
}, [performDownload])

return (
<Drawer isOpen={isOpen} onClose={onClose} onClosed={handleClosed}>
<Flex p='xl' gap='xl' alignItems='center'>
Expand All @@ -76,10 +100,27 @@ export const WaitForDownloadDrawer = () => {
{trackName}
</Text>
</Flex>
<Flex>
<LoadingSpinner
style={{ width: spacing.unit7, height: spacing.unit7 }}
/>
<Flex ph='l'>
{downloadError ? (
<Hint icon={IconError}>
<Flex direction='column' gap='m'>
<Text variant='body' color='default'>
{messages.somethingWrong}
</Text>
<TextLink
variant='visible'
textVariant='body'
onPress={performDownload}
>
{messages.tryAgain}
</TextLink>
</Flex>
</Hint>
) : (
<LoadingSpinner
style={{ width: spacing.unit7, height: spacing.unit7 }}
/>
)}
</Flex>
</Flex>
</Drawer>
Expand Down
8 changes: 4 additions & 4 deletions packages/mobile/src/harmony-native/components/Hint/Hint.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { IconComponent } from 'app/harmony-native/icons'

import { Text } from '../Text/Text'
import type { PaperProps } from '../layout'
import { Paper } from '../layout'

Expand All @@ -21,12 +20,13 @@ export const Hint = (props: HintProps) => {
backgroundColor='surface2'
shadow='flat'
border='strong'
// Width 100% is necessary to allow for text wrapping inside
// the flex container that wraps children
style={{ width: '100%' }}
{...other}
>
<Icon size='l' color='default' />
<Text variant='body' color='default'>
{children}
</Text>
{children}
</Paper>
)
}
22 changes: 6 additions & 16 deletions packages/mobile/src/screens/track-screen/DownloadSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ import {
useDownloadableContentAccess,
useGatedContentAccess
} from '@audius/common/hooks'
import { ModalSource, DownloadQuality } from '@audius/common/models'
import type { ID } from '@audius/common/models'
import { DownloadQuality, ModalSource } from '@audius/common/models'
import type { CommonState } from '@audius/common/store'
import {
cacheTracksSelectors,
usePremiumContentPurchaseModal,
useWaitForDownloadModal,
tracksSocialActions as socialTracksActions
useWaitForDownloadModal
} from '@audius/common/store'
import type { CommonState } from '@audius/common/store'
import { USDC } from '@audius/fixed-decimal'
import { css } from '@emotion/native'
import { LayoutAnimation } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import { useSelector } from 'react-redux'

import {
Button,
Flex,
IconLockUnlocked,
IconReceive,
Text,
Button,
IconLockUnlocked,
useTheme
} from '@audius/harmony-native'
import { SegmentedControl } from 'app/components/core'
Expand Down Expand Up @@ -53,7 +52,6 @@ const messages = {
}

export const DownloadSection = ({ trackId }: { trackId: ID }) => {
const dispatch = useDispatch()
const { color } = useTheme()
const { toast } = useToast()
const { onOpen: openPremiumContentPurchaseModal } =
Expand Down Expand Up @@ -111,17 +109,9 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => {
trackIds,
quality
})
dispatch(
socialTracksActions.downloadTrack({
trackIds,
parentTrackId,
original: quality === DownloadQuality.ORIGINAL
})
)
}
},
[
dispatch,
openWaitForDownloadModal,
quality,
shouldDisplayDownloadFollowGated,
Expand Down
22 changes: 17 additions & 5 deletions packages/mobile/src/services/track-download.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DownloadTrackArgs } from '@audius/common/services'
import { TrackDownload as TrackDownloadBase } from '@audius/common/services'
import { tracksSocialActions } from '@audius/common/store'
import { tracksSocialActions, downloadsActions } from '@audius/common/store'
import { Platform, Share } from 'react-native'
import { zip } from 'react-native-zip-archive'
import type {
Expand All @@ -9,15 +9,16 @@ import type {
StatefulPromise
} from 'rn-fetch-blob'
import RNFetchBlob from 'rn-fetch-blob'
import { dedupFilenames } from '~/utils'

import { dispatch } from 'app/store'
import { setFetchCancel, setFileInfo } from 'app/store/download/slice'
import { setVisibility } from 'app/store/drawers/slice'

import { audiusBackendInstance } from './audius-backend-instance'
import { dedupFilenames } from '~/utils'

const { downloadFinished } = tracksSocialActions
const { beginDownload, setDownloadError, setFetchCancel, setFileInfo } =
downloadsActions

let fetchTasks: StatefulPromise<FetchBlobResponse>[] = []

Expand Down Expand Up @@ -75,6 +76,11 @@ const downloadOne = async ({
await onFetchComplete?.(fetchRes.path())
} catch (err) {
console.error(err)
dispatch(
setDownloadError(
err instanceof Error ? err : new Error(`Download failed: ${err}`)
)
)
// On failure attempt to delete the file
removePathIfExists(filePath)
}
Expand Down Expand Up @@ -113,6 +119,11 @@ const downloadMany = async ({
responses.forEach((response) => response.flush())
} catch (err) {
console.error(err)
dispatch(
setDownloadError(
err instanceof Error ? err : new Error(`Download failed: ${err}`)
)
)
} finally {
// Remove source directory at the end of the process regardless of what happens
removePathIfExists(directory)
Expand All @@ -126,6 +137,8 @@ const download = async ({
}: DownloadTrackArgs) => {
if (files.length === 0) return

dispatch(beginDownload())

dispatch(
setFileInfo({
trackName: rootDirectoryName ?? '',
Expand All @@ -139,8 +152,7 @@ const download = async ({
}
// TODO: Remove this method of canceling after the lossless
// feature set launches. The abort signal should be the way to do
// this task cancellation going forward. The corresponding slice
// may also be deleted.
// this task cancellation going forward.
dispatch(setFetchCancel(cancelDownloadTask))

const audiusDirectory =
Expand Down
7 changes: 0 additions & 7 deletions packages/mobile/src/store/download/selectors.ts

This file was deleted.

Loading

0 comments on commit 199a1a5

Please sign in to comment.