From 812d24bafaa02c77225ba6a5f66e9ffcd9241fbc Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:53:36 -0400 Subject: [PATCH 1/6] Plumbing to allow previewable tracks in mobile player --- packages/common/src/store/queue/types.ts | 1 + packages/common/src/utils/streaming.ts | 7 +++ .../mobile/src/components/audio/Audio.tsx | 50 ++++++++++--------- .../now-playing-drawer/NowPlayingDrawer.tsx | 3 +- .../components/now-playing-drawer/PlayBar.tsx | 4 +- .../useTrackPlayerDuration.ts | 17 +++++++ packages/web/src/common/store/lineup/sagas.js | 10 +++- packages/web/src/common/store/player/sagas.ts | 10 ++-- 8 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts diff --git a/packages/common/src/store/queue/types.ts b/packages/common/src/store/queue/types.ts index 0d2bc11adf..4e3b935e4c 100644 --- a/packages/common/src/store/queue/types.ts +++ b/packages/common/src/store/queue/types.ts @@ -27,6 +27,7 @@ export type Queueable = { uid: UID artistId?: ID collectible?: Collectible + isPreview?: boolean source: QueueSource } diff --git a/packages/common/src/utils/streaming.ts b/packages/common/src/utils/streaming.ts index 8072550e9f..e6c9a83545 100644 --- a/packages/common/src/utils/streaming.ts +++ b/packages/common/src/utils/streaming.ts @@ -8,6 +8,8 @@ import { Nullable } from './typeUtils' const { getPremiumTrackSignatureMap } = premiumContentSelectors +const PREVIEW_LENGTH_SECONDS = 30 + export async function generateUserSignature( audiusBackendInstance: AudiusBackend ) { @@ -50,3 +52,8 @@ export function* doesUserHaveTrackAccess(track: Nullable) { return !isPremium || hasPremiumContentSignature } + +export function getTrackPreviewDuration(track: Track) { + const previewStartSeconds = track.preview_start_seconds || 0 + return Math.min(track.duration - previewStartSeconds, PREVIEW_LENGTH_SECONDS) +} diff --git a/packages/mobile/src/components/audio/Audio.tsx b/packages/mobile/src/components/audio/Audio.tsx index dc8c5e42ec..0db04fe947 100644 --- a/packages/mobile/src/components/audio/Audio.tsx +++ b/packages/mobile/src/components/audio/Audio.tsx @@ -5,6 +5,7 @@ import type { ID, Nullable, QueryParams, + Queueable, Track } from '@audius/common' import { @@ -30,7 +31,8 @@ import { SquareSizes, shallowCompare, savedPageTracksLineupActions, - useAppContext + useAppContext, + getTrackPreviewDuration } from '@audius/common' import { isEqual } from 'lodash' import TrackPlayer, { @@ -149,6 +151,10 @@ const unlistedTrackFallbackTrackData = { duration: 0 } +type QueueableTrack = { + track: Nullable +} & Pick + export const Audio = () => { const { isEnabled: isNewPodcastControlsEnabled } = useFeatureFlag( FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED, @@ -184,11 +190,12 @@ export const Audio = () => { (state) => getTracks(state, { uids: queueTrackUids }), shallowCompare ) - const queueTracks = queueOrder.map( - (trackData) => queueTrackMap[trackData.id] as Nullable - ) + const queueTracks: QueueableTrack[] = queueOrder.map(({ id, isPreview }) => ({ + track: queueTrackMap[id] as Nullable, + isPreview + })) const queueTrackOwnerIds = queueTracks - .map((track) => track?.owner_id) + .map(({ track }) => track?.owner_id) .filter(removeNullable) const queueTrackOwnersMap = useSelector( @@ -278,10 +285,10 @@ export const Audio = () => { }>({}) const handleGatedQueryParams = useCallback( - async (tracks: Nullable[]) => { + async (tracks: QueueableTrack[]) => { const queryParamsMap: { [trackId: ID]: QueryParams } = {} - for (const track of tracks) { + for (const { track } of tracks) { if (!track) { continue } @@ -355,7 +362,7 @@ export const Audio = () => { // Figure out how to call next earlier next() } else { - const track = queueTracks[playerIndex] + const { track } = queueTracks[playerIndex] // Skip track if user does not have access i.e. for an unlocked premium track const doesUserHaveAccess = (() => { @@ -410,8 +417,8 @@ export const Audio = () => { } const isLongFormContent = - queueTracks[playerIndex]?.genre === Genre.PODCASTS || - queueTracks[playerIndex]?.genre === Genre.AUDIOBOOKS + queueTracks[playerIndex].track?.genre === Genre.PODCASTS || + queueTracks[playerIndex].track?.genre === Genre.AUDIOBOOKS if (isLongFormContent !== isLongFormContentRef.current) { isLongFormContentRef.current = isLongFormContent // Update playback rate based on if the track is a podcast or not @@ -429,7 +436,7 @@ export const Audio = () => { event?.position !== null && event?.track !== null ) { - const track = queueTracks[event.track] + const { track } = queueTracks[event.track] const isLongFormContent = track?.genre === Genre.PODCASTS || track?.genre === Genre.AUDIOBOOKS const isAtEndOfTrack = @@ -574,7 +581,7 @@ export const Audio = () => { ? await handleGatedQueryParams(newQueueTracks) : null - const newTrackData = newQueueTracks.map((track) => { + const newTrackData = newQueueTracks.map(({ track, isPreview }) => { if (!track) { return unlistedTrackFallbackTrackData } @@ -589,17 +596,14 @@ export const Audio = () => { const audioFilePath = getLocalAudioPath(trackId) url = `file://${audioFilePath}` } else { - const queryParams = queryParamsMap?.[track.track_id] - if (queryParams) { - url = apiClient.makeUrl( - `/tracks/${encodeHashId(track.track_id)}/stream`, - queryParams - ) - } else { - url = apiClient.makeUrl( - `/tracks/${encodeHashId(track.track_id)}/stream` - ) + const queryParams = { + ...queryParamsMap?.[track.track_id], + preview: isPreview } + url = apiClient.makeUrl( + `/tracks/${encodeHashId(track.track_id)}/stream`, + queryParams + ) } const localTrackImageSource = @@ -627,7 +631,7 @@ export const Audio = () => { genre: track.genre, date: track.created_at, artwork: imageUrl, - duration: track?.duration + duration: isPreview ? getTrackPreviewDuration(track) : track.duration } }) diff --git a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx index b4cf21fad3..4169e5cbbf 100644 --- a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx +++ b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx @@ -45,6 +45,7 @@ import { PlayBar } from './PlayBar' import { TitleBar } from './TitleBar' import { TrackInfo } from './TrackInfo' import { PLAY_BAR_HEIGHT } from './constants' +import { useTrackPlayerDuration } from './useTrackPlayerDuration' const { seek, reset } = playerActions const { getPlaying, getCurrentTrack, getCounter, getUid } = playerSelectors @@ -208,8 +209,8 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( const [isGestureEnabled, setIsGestureEnabled] = useState(true) const track = useSelector(getCurrentTrack) + const trackDuration = useTrackPlayerDuration() const trackId = track?.track_id - const trackDuration = track?.duration ?? 0 const user = useSelector((state) => getUser(state, track ? { id: track.owner_id } : {}) diff --git a/packages/mobile/src/components/now-playing-drawer/PlayBar.tsx b/packages/mobile/src/components/now-playing-drawer/PlayBar.tsx index 67ca3ba877..1035a492ee 100644 --- a/packages/mobile/src/components/now-playing-drawer/PlayBar.tsx +++ b/packages/mobile/src/components/now-playing-drawer/PlayBar.tsx @@ -19,6 +19,7 @@ import { zIndex } from 'app/utils/zIndex' import { PlayButton } from './PlayButton' import { TrackingBar } from './TrackingBar' import { NOW_PLAYING_HEIGHT, PLAY_BAR_HEIGHT } from './constants' +import { useTrackPlayerDuration } from './useTrackPlayerDuration' const { getAccountUser } = accountSelectors const { saveTrack, unsaveTrack } = tracksSocialActions @@ -97,6 +98,7 @@ export const PlayBar = (props: PlayBarProps) => { const styles = useStyles() const dispatch = useDispatch() const currentUser = useSelector(getAccountUser) + const duration = useTrackPlayerDuration() const onPressFavoriteButton = useCallback(() => { if (track) { @@ -136,7 +138,7 @@ export const PlayBar = (props: PlayBarProps) => { return ( diff --git a/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts b/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts new file mode 100644 index 0000000000..646b670d72 --- /dev/null +++ b/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts @@ -0,0 +1,17 @@ +import { useState } from 'react' + +import TrackPlayer, { + useTrackPlayerEvents, + Event +} from 'react-native-track-player' + +export const useTrackPlayerDuration = () => { + const [duration, setDuration] = useState(0) + useTrackPlayerEvents([Event.PlaybackTrackChanged], async (event) => { + if (event.type === Event.PlaybackTrackChanged) { + const newDuration = await TrackPlayer.getDuration() + setDuration(newDuration) + } + }) + return duration +} diff --git a/packages/web/src/common/store/lineup/sagas.js b/packages/web/src/common/store/lineup/sagas.js index 32ea8e2a60..9f55ee1020 100644 --- a/packages/web/src/common/store/lineup/sagas.js +++ b/packages/web/src/common/store/lineup/sagas.js @@ -369,7 +369,15 @@ function* play(lineupActions, lineupSelector, prefix, action) { source !== lineup.prefix ) { const toQueue = yield all( - lineup.entries.map((e) => call(getToQueue, lineup.prefix, e)) + lineup.entries.map((e) => { + const queueable = call(getToQueue, lineup.prefix, e) + // If the entry is the one we're playing, set isPreview to incoming + // value + if (queueable.uid === action.uid) { + queueable.isPreview = isPreview + } + return queueable + }) ) const flattenedQueue = flatten(toQueue).filter((e) => Boolean(e)) yield put(queueActions.clear({})) diff --git a/packages/web/src/common/store/player/sagas.ts b/packages/web/src/common/store/player/sagas.ts index 7aa0489528..b3dc50f405 100644 --- a/packages/web/src/common/store/player/sagas.ts +++ b/packages/web/src/common/store/player/sagas.ts @@ -19,7 +19,8 @@ import { QueryParams, Genre, doesUserHaveTrackAccess, - getQueryParams + getQueryParams, + getTrackPreviewDuration } from '@audius/common' import { eventChannel } from 'redux-saga' import { @@ -66,7 +67,6 @@ const { getIsReachable } = reachabilitySelectors const PLAYER_SUBSCRIBER_NAME = 'PLAYER' const RECORD_LISTEN_SECONDS = 1 const RECORD_LISTEN_INTERVAL = 1000 -const PREVIEW_LENGTH_SECONDS = 30 export function* watchPlay() { const getFeatureEnabled = yield* getContext('getFeatureEnabled') @@ -119,12 +119,8 @@ export function* watchPlay() { if (isPreview) { // Add preview query string and calculate preview duration for use later - const previewStartSeconds = track.preview_start_seconds || 0 queryParams.preview = true - trackDuration = Math.min( - track.duration - previewStartSeconds, - PREVIEW_LENGTH_SECONDS - ) + trackDuration = getTrackPreviewDuration(track) } const mp3Url = apiClient.makeUrl( From 49bad21c9bf0da513e9c994ed5b7ff54eb93abe3 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:55:40 -0400 Subject: [PATCH 2/6] Hook up preview button on mobile --- .../components/details-tile/DetailsTile.tsx | 22 ++++++--- .../src/components/details-tile/types.ts | 7 +++ .../track-screen/TrackScreenDetailsTile.tsx | 49 +++++++++++++------ 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/packages/mobile/src/components/details-tile/DetailsTile.tsx b/packages/mobile/src/components/details-tile/DetailsTile.tsx index 31ba526535..86c2641489 100644 --- a/packages/mobile/src/components/details-tile/DetailsTile.tsx +++ b/packages/mobile/src/components/details-tile/DetailsTile.tsx @@ -161,6 +161,7 @@ export const DetailsTile = ({ hideRepostCount, hideShare, isPlaying, + isPreviewing, isPlayable = true, isPlaylist = false, isPublished = true, @@ -169,6 +170,7 @@ export const DetailsTile = ({ onPressFavorites, onPressOverflow, onPressPlay, + onPressPreview, onPressPublish, onPressRepost, onPressReposts, @@ -213,6 +215,9 @@ export const DetailsTile = ({ const isUSDCPurchaseGated = isPremiumContentUSDCPurchaseGated(premiumConditions) + const isPlayingPreview = isPreviewing && isPlaying + const isPlayingFullAccess = isPlaying && !isPreviewing + const handlePressArtistName = useCallback(() => { if (!user) { return @@ -229,6 +234,11 @@ export const DetailsTile = ({ onPressPlay() }, [onPressPlay]) + const handlePressPreview = useCallback(() => { + light() + onPressPreview() + }, [onPressPreview]) + const renderDogEar = () => { const dogEarType = getDogEarType({ isOwner, @@ -298,13 +308,11 @@ export const DetailsTile = ({ text: styles.playButtonText, icon: styles.playButtonIcon }} - title={isPlaying ? messages.pause : messages.preview} + title={isPlayingPreview ? messages.pause : messages.preview} size='large' iconPosition='left' - icon={isPlaying ? IconPause : PlayIcon} - onPress={() => { - console.info('Preview button pressed') - }} + icon={isPlayingPreview ? IconPause : PlayIcon} + onPress={handlePressPreview} disabled={!isPlayable} fullWidth /> @@ -365,10 +373,10 @@ export const DetailsTile = ({ text: styles.playButtonText, icon: styles.playButtonIcon }} - title={isPlaying ? messages.pause : playText} + title={isPlayingFullAccess ? messages.pause : playText} size='large' iconPosition='left' - icon={isPlaying ? IconPause : PlayIcon} + icon={isPlayingFullAccess ? IconPause : PlayIcon} onPress={handlePressPlay} disabled={!isPlayable} fullWidth diff --git a/packages/mobile/src/components/details-tile/types.ts b/packages/mobile/src/components/details-tile/types.ts index 942acbd767..f707ef461c 100644 --- a/packages/mobile/src/components/details-tile/types.ts +++ b/packages/mobile/src/components/details-tile/types.ts @@ -12,6 +12,7 @@ export type DetailsTileDetail = { isHidden?: boolean label: string value: ReactNode + valueStyle?: TextStyle } @@ -64,6 +65,9 @@ export type DetailsTileProps = { /** Is the item playing */ isPlaying?: boolean + /** Is the currently playing item a preview */ + isPreviewing?: boolean + /** Is the item loaded and in a playable state */ isPlayable?: boolean @@ -88,6 +92,9 @@ export type DetailsTileProps = { /** Function to call when play button is pressed */ onPressPlay: GestureResponderHandler + /** Function to call when play button is pressed */ + onPressPreview: GestureResponderHandler + /** Function to call when publish button is pressed */ onPressPublish?: GestureResponderHandler diff --git a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx index 32c2ceed66..eb3443e507 100644 --- a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx +++ b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx @@ -59,7 +59,7 @@ import { moodMap } from 'app/utils/moods' import { useThemeColors } from 'app/utils/theme' import { TrackScreenDownloadButtons } from './TrackScreenDownloadButtons' -const { getPlaying, getTrackId } = playerSelectors +const { getPlaying, getTrackId, getPreviewing } = playerSelectors const { setFavorite } = favoritesUserListActions const { setRepost } = repostsUserListActions const { requestOpen: requestOpenShareModal } = shareModalUIActions @@ -220,6 +220,7 @@ export const TrackScreenDetailsTile = ({ const dispatch = useDispatch() const playingId = useSelector(getTrackId) const isPlaying = useSelector(getPlaying) + const isPreviewing = useSelector(getPreviewing) const isPlayingId = playingId === track.track_id const { @@ -289,20 +290,37 @@ export const TrackScreenDetailsTile = ({ [track] ) - const handlePressPlay = useCallback(() => { - if (isLineupLoading) return - - if (isPlaying && isPlayingId) { - dispatch(tracksActions.pause()) - recordPlay(track_id, false) - } else if (!isPlayingId) { - dispatch(tracksActions.play(uid)) - recordPlay(track_id) - } else { - dispatch(tracksActions.play()) - recordPlay(track_id) - } - }, [track_id, uid, isPlayingId, dispatch, isPlaying, isLineupLoading]) + const play = useCallback( + ({ isPreview = false } = {}) => { + if (isLineupLoading) return + + if (isPlaying && isPlayingId && isPreviewing === isPreview) { + dispatch(tracksActions.pause()) + recordPlay(track_id, false) + } else if (!isPlayingId) { + dispatch(tracksActions.play(uid, { isPreview })) + recordPlay(track_id) + } else { + dispatch(tracksActions.play()) + recordPlay(track_id) + } + }, + [ + track_id, + uid, + isPlayingId, + dispatch, + isPlaying, + isPreviewing, + isLineupLoading + ] + ) + + const handlePressPlay = useCallback(() => play({ isPreview: false }), [play]) + const handlePressPreview = useCallback( + () => play({ isPreview: true }), + [play] + ) const handlePressFavorites = useCallback(() => { dispatch(setFavorite(track_id, FavoriteType.TRACK)) @@ -542,6 +560,7 @@ export const TrackScreenDetailsTile = ({ onPressFavorites={handlePressFavorites} onPressOverflow={handlePressOverflow} onPressPlay={handlePressPlay} + onPressPreview={handlePressPreview} onPressRepost={handlePressRepost} onPressReposts={handlePressReposts} onPressSave={handlePressSave} From b18400620cc44e1be0cbec3185fb51d643e34b6e Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:18:19 -0400 Subject: [PATCH 3/6] fixes for previewing --- packages/common/src/store/player/slice.ts | 6 +++++- packages/mobile/src/components/audio/Audio.tsx | 15 ++++++++++++--- .../now-playing-drawer/NowPlayingDrawer.tsx | 5 +++-- .../components/now-playing-drawer/PlayBar.tsx | 5 ++--- .../useCurrentTrackDuration.ts | 15 +++++++++++++++ .../useTrackPlayerDuration.ts | 17 ----------------- .../track-screen/TrackScreenDetailsTile.tsx | 1 + packages/web/src/common/store/lineup/sagas.js | 4 ++-- 8 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 packages/mobile/src/components/now-playing-drawer/useCurrentTrackDuration.ts delete mode 100644 packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts diff --git a/packages/common/src/store/player/slice.ts b/packages/common/src/store/player/slice.ts index 1e191f868a..d39e48b238 100644 --- a/packages/common/src/store/player/slice.ts +++ b/packages/common/src/store/player/slice.ts @@ -91,6 +91,7 @@ type SetBufferingPayload = { type SetPayload = { uid: UID trackId: ID + previewing?: boolean } type SeekPayload = { @@ -139,6 +140,7 @@ const slice = createSlice({ action: PayloadAction ) => { const { collectible } = action.payload + state.previewing = false state.playing = true state.uid = null state.trackId = null @@ -158,15 +160,17 @@ const slice = createSlice({ state.counter = state.counter + 1 }, set: (state, action: PayloadAction) => { - const { uid, trackId } = action.payload + const { previewing, uid, trackId } = action.payload state.uid = uid state.trackId = trackId + state.previewing = !!previewing }, reset: (_state, _action: PayloadAction) => {}, resetSucceeded: (state, action: PayloadAction) => { const { shouldAutoplay } = action.payload state.playing = shouldAutoplay state.counter = state.counter + 1 + state.previewing = false }, seek: (state, action: PayloadAction) => { const { seconds } = action.payload diff --git a/packages/mobile/src/components/audio/Audio.tsx b/packages/mobile/src/components/audio/Audio.tsx index 0db04fe947..c5cd3e0db4 100644 --- a/packages/mobile/src/components/audio/Audio.tsx +++ b/packages/mobile/src/components/audio/Audio.tsx @@ -251,8 +251,16 @@ export const Audio = () => { [dispatch] ) const updatePlayerInfo = useCallback( - ({ trackId, uid }: { trackId: number; uid: string }) => { - dispatch(playerActions.set({ trackId, uid })) + ({ + previewing, + trackId, + uid + }: { + previewing: boolean + trackId: number + uid: string + }) => { + dispatch(playerActions.set({ previewing, trackId, uid })) }, [dispatch] ) @@ -362,7 +370,7 @@ export const Audio = () => { // Figure out how to call next earlier next() } else { - const { track } = queueTracks[playerIndex] + const { track, isPreview } = queueTracks[playerIndex] // Skip track if user does not have access i.e. for an unlocked premium track const doesUserHaveAccess = (() => { @@ -388,6 +396,7 @@ export const Audio = () => { // Update queue info and handle playback position updates updateQueueIndex(playerIndex) updatePlayerInfo({ + previewing: !!isPreview, trackId: track.track_id, uid: queueTrackUids[playerIndex] }) diff --git a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx index 4169e5cbbf..61087ae354 100644 --- a/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx +++ b/packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx @@ -45,7 +45,7 @@ import { PlayBar } from './PlayBar' import { TitleBar } from './TitleBar' import { TrackInfo } from './TrackInfo' import { PLAY_BAR_HEIGHT } from './constants' -import { useTrackPlayerDuration } from './useTrackPlayerDuration' +import { useCurrentTrackDuration } from './useCurrentTrackDuration' const { seek, reset } = playerActions const { getPlaying, getCurrentTrack, getCounter, getUid } = playerSelectors @@ -209,7 +209,7 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( const [isGestureEnabled, setIsGestureEnabled] = useState(true) const track = useSelector(getCurrentTrack) - const trackDuration = useTrackPlayerDuration() + const trackDuration = useCurrentTrackDuration() const trackId = track?.track_id const user = useSelector((state) => @@ -307,6 +307,7 @@ export const NowPlayingDrawer = memo(function NowPlayingDrawer( ({ type PlayBarProps = { track: Nullable + duration: number user: Nullable onPress: () => void translationAnim: Animated.Value @@ -94,11 +94,10 @@ type PlayBarProps = { } export const PlayBar = (props: PlayBarProps) => { - const { track, user, onPress, translationAnim, mediaKey } = props + const { duration, track, user, onPress, translationAnim, mediaKey } = props const styles = useStyles() const dispatch = useDispatch() const currentUser = useSelector(getAccountUser) - const duration = useTrackPlayerDuration() const onPressFavoriteButton = useCallback(() => { if (track) { diff --git a/packages/mobile/src/components/now-playing-drawer/useCurrentTrackDuration.ts b/packages/mobile/src/components/now-playing-drawer/useCurrentTrackDuration.ts new file mode 100644 index 0000000000..30fcf0b218 --- /dev/null +++ b/packages/mobile/src/components/now-playing-drawer/useCurrentTrackDuration.ts @@ -0,0 +1,15 @@ +import { getTrackPreviewDuration, playerSelectors } from '@audius/common' +import { useSelector } from 'react-redux' + +const { getCurrentTrack, getPreviewing } = playerSelectors + +export const useCurrentTrackDuration = () => { + const track = useSelector(getCurrentTrack) + const isPreviewing = useSelector(getPreviewing) + + return !track + ? 0 + : isPreviewing + ? getTrackPreviewDuration(track) + : track.duration +} diff --git a/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts b/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts deleted file mode 100644 index 646b670d72..0000000000 --- a/packages/mobile/src/components/now-playing-drawer/useTrackPlayerDuration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useState } from 'react' - -import TrackPlayer, { - useTrackPlayerEvents, - Event -} from 'react-native-track-player' - -export const useTrackPlayerDuration = () => { - const [duration, setDuration] = useState(0) - useTrackPlayerEvents([Event.PlaybackTrackChanged], async (event) => { - if (event.type === Event.PlaybackTrackChanged) { - const newDuration = await TrackPlayer.getDuration() - setDuration(newDuration) - } - }) - return duration -} diff --git a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx index eb3443e507..fbd0c99646 100644 --- a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx +++ b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx @@ -556,6 +556,7 @@ export const TrackScreenDetailsTile = ({ hideListenCount={is_unlisted && !field_visibility?.play_count} hideRepostCount={is_unlisted} isPlaying={isPlaying && isPlayingId} + isPreviewing={isPreviewing} isUnlisted={is_unlisted} onPressFavorites={handlePressFavorites} onPressOverflow={handlePressOverflow} diff --git a/packages/web/src/common/store/lineup/sagas.js b/packages/web/src/common/store/lineup/sagas.js index 9f55ee1020..6925f2ea46 100644 --- a/packages/web/src/common/store/lineup/sagas.js +++ b/packages/web/src/common/store/lineup/sagas.js @@ -369,8 +369,8 @@ function* play(lineupActions, lineupSelector, prefix, action) { source !== lineup.prefix ) { const toQueue = yield all( - lineup.entries.map((e) => { - const queueable = call(getToQueue, lineup.prefix, e) + lineup.entries.map(function* (e) { + const queueable = yield call(getToQueue, lineup.prefix, e) // If the entry is the one we're playing, set isPreview to incoming // value if (queueable.uid === action.uid) { From 5bdfb779156dd2d2609e11f0496c680b686701af Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:33:15 -0400 Subject: [PATCH 4/6] preview button support should be optional --- .../mobile/src/components/details-tile/DetailsTile.tsx | 9 +++++---- packages/mobile/src/components/details-tile/types.ts | 4 ++-- .../collection-screen/CollectionScreenDetailsTile.tsx | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/mobile/src/components/details-tile/DetailsTile.tsx b/packages/mobile/src/components/details-tile/DetailsTile.tsx index 86c2641489..08345978ff 100644 --- a/packages/mobile/src/components/details-tile/DetailsTile.tsx +++ b/packages/mobile/src/components/details-tile/DetailsTile.tsx @@ -218,6 +218,9 @@ export const DetailsTile = ({ const isPlayingPreview = isPreviewing && isPlaying const isPlayingFullAccess = isPlaying && !isPreviewing + const showPreviewButton = + isUSDCPurchaseGated && (isOwner || !doesUserHaveAccess) && onPressPreview + const handlePressArtistName = useCallback(() => { if (!user) { return @@ -236,7 +239,7 @@ export const DetailsTile = ({ const handlePressPreview = useCallback(() => { light() - onPressPreview() + onPressPreview?.() }, [onPressPreview]) const renderDogEar = () => { @@ -389,9 +392,7 @@ export const DetailsTile = ({ trackArtist={user} /> ) : null} - {isUSDCPurchaseGated && (isOwner || !doesUserHaveAccess) ? ( - - ) : null} + {showPreviewButton ? : null} From 5a91c0b337f4560b371dfb357c966882ec9196af Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:58:02 -0400 Subject: [PATCH 5/6] fix some unsafe access of values --- packages/mobile/src/components/audio/Audio.tsx | 12 ++++++------ packages/web/src/common/store/lineup/sagas.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/mobile/src/components/audio/Audio.tsx b/packages/mobile/src/components/audio/Audio.tsx index c5cd3e0db4..967195165b 100644 --- a/packages/mobile/src/components/audio/Audio.tsx +++ b/packages/mobile/src/components/audio/Audio.tsx @@ -370,7 +370,7 @@ export const Audio = () => { // Figure out how to call next earlier next() } else { - const { track, isPreview } = queueTracks[playerIndex] + const { track, isPreview } = queueTracks[playerIndex] ?? {} // Skip track if user does not have access i.e. for an unlocked premium track const doesUserHaveAccess = (() => { @@ -426,8 +426,8 @@ export const Audio = () => { } const isLongFormContent = - queueTracks[playerIndex].track?.genre === Genre.PODCASTS || - queueTracks[playerIndex].track?.genre === Genre.AUDIOBOOKS + queueTracks[playerIndex]?.track?.genre === Genre.PODCASTS || + queueTracks[playerIndex]?.track?.genre === Genre.AUDIOBOOKS if (isLongFormContent !== isLongFormContentRef.current) { isLongFormContentRef.current = isLongFormContent // Update playback rate based on if the track is a podcast or not @@ -442,10 +442,10 @@ export const Audio = () => { // Handle track end event if ( isNewPodcastControlsEnabled && - event?.position !== null && - event?.track !== null + event?.position != null && + event?.track != null ) { - const { track } = queueTracks[event.track] + const { track } = queueTracks[event.track] ?? {} const isLongFormContent = track?.genre === Genre.PODCASTS || track?.genre === Genre.AUDIOBOOKS const isAtEndOfTrack = diff --git a/packages/web/src/common/store/lineup/sagas.js b/packages/web/src/common/store/lineup/sagas.js index 6925f2ea46..538174f725 100644 --- a/packages/web/src/common/store/lineup/sagas.js +++ b/packages/web/src/common/store/lineup/sagas.js @@ -355,7 +355,7 @@ function* play(lineupActions, lineupSelector, prefix, action) { // If preview isn't forced, check for track acccess and switch to preview // if the user doesn't have access but the track is previewable - if (!isPreview && requestedPlayTrack.is_premium) { + if (!isPreview && requestedPlayTrack?.is_premium) { const hasAccess = yield call(doesUserHaveTrackAccess, requestedPlayTrack) isPreview = !hasAccess && !!requestedPlayTrack.preview_cid } From c20d75bd26c9b27cc16ed47e8415974d4104b929 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:01:18 -0400 Subject: [PATCH 6/6] remove unnecessary args --- .../mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx index fbd0c99646..3ea8da055b 100644 --- a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx +++ b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx @@ -316,7 +316,7 @@ export const TrackScreenDetailsTile = ({ ] ) - const handlePressPlay = useCallback(() => play({ isPreview: false }), [play]) + const handlePressPlay = useCallback(() => play(), [play]) const handlePressPreview = useCallback( () => play({ isPreview: true }), [play]