Skip to content

Commit 66d8848

Browse files
Feat: adds skip buttons to both the archive view and homescreen (#146)
1 parent 2f77a84 commit 66d8848

File tree

5 files changed

+163
-11
lines changed

5 files changed

+163
-11
lines changed

src/app/Home/PlayButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export default function PlayButton({
113113
style={[styles.playButton, isPlaying && styles.playButtonActive]}
114114
onPress={onPress}
115115
activeOpacity={0.8}
116-
aria-label={playbackButtonLabel}
116+
accessibilityLabel={playbackButtonLabel}
117117
>
118118
<View style={styles.buttonContent}>
119119
<View style={styles.iconContainer}>{playbackIcon}</View>

src/app/Home/index.tsx

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import {
1414
import TrackPlayer, {
1515
State,
1616
usePlaybackState,
17+
useProgress,
1718
} from 'react-native-track-player';
19+
import Icon from 'react-native-vector-icons/Ionicons';
20+
import { liveCapabilities, SKIP_INTERVAL } from '@utils/TrackPlayerUtils';
1821
import LinearGradient from 'react-native-linear-gradient';
1922
import { GestureHandlerRootView } from 'react-native-gesture-handler';
2023
import { SvgXml } from 'react-native-svg';
@@ -30,14 +33,14 @@ import { WmbrRouteName } from '@customTypes/Navigation';
3033
import { DEFAULT_NAME } from '@customTypes/Playlist';
3134
import { COLORS, CORE_COLORS } from '@utils/Colors';
3235
import { formatArchiveDate } from '@utils/DateTime';
33-
import { liveCapabilities } from '@utils/TrackPlayerUtils';
3436

3537
import HomeNowPlaying from './HomeNowPlaying';
3638

3739
const streamUrl = 'https://wmbr.org:8002/hi';
3840

3941
export default function HomeScreen() {
4042
const playbackState = usePlaybackState();
43+
const progress = useProgress();
4144
const insets = useSafeAreaInsets();
4245
const [currentShow, setCurrentShow] = useState(DEFAULT_NAME);
4346
const [, setSongHistory] = useState<Song[]>([]);
@@ -215,6 +218,19 @@ export default function HomeScreen() {
215218
}
216219
}, [currentShow]);
217220

221+
const handleSkipBackward = useCallback(async () => {
222+
const newPosition = Math.max(progress.position - SKIP_INTERVAL, 0);
223+
await TrackPlayer.seekTo(newPosition);
224+
}, [progress.position]);
225+
226+
const handleSkipForward = useCallback(async () => {
227+
const newPosition = Math.min(
228+
progress.position + SKIP_INTERVAL,
229+
progress.duration,
230+
);
231+
await TrackPlayer.seekTo(newPosition);
232+
}, [progress.position, progress.duration]);
233+
218234
const handleOpenShowDetails = useCallback(() => {
219235
const show = archiveState.currentShow;
220236
if (!show) return;
@@ -299,10 +315,46 @@ export default function HomeScreen() {
299315
</>
300316
)}
301317
</View>
302-
<PlayButton
303-
onPress={togglePlayback}
304-
isPlayingArchive={archiveState.isPlayingArchive}
305-
/>
318+
<View style={styles.playbackControls}>
319+
{archiveState.isPlayingArchive && (
320+
<TouchableOpacity
321+
style={styles.skipButton}
322+
onPress={handleSkipBackward}
323+
activeOpacity={0.7}
324+
accessibilityLabel={`Skip backward ${SKIP_INTERVAL} seconds`}
325+
>
326+
<Icon
327+
name="refresh-outline"
328+
size={28}
329+
color={COLORS.TEXT.PRIMARY}
330+
style={styles.skipBackIcon}
331+
/>
332+
<Text style={styles.skipText}>{SKIP_INTERVAL}</Text>
333+
</TouchableOpacity>
334+
)}
335+
<PlayButton
336+
onPress={togglePlayback}
337+
isPlayingArchive={archiveState.isPlayingArchive}
338+
/>
339+
{archiveState.isPlayingArchive && (
340+
<TouchableOpacity
341+
style={styles.skipButton}
342+
onPress={handleSkipForward}
343+
activeOpacity={0.7}
344+
accessibilityLabel={`Skip forward ${SKIP_INTERVAL} seconds`}
345+
>
346+
<Icon
347+
name="refresh-outline"
348+
size={28}
349+
color={COLORS.TEXT.PRIMARY}
350+
style={styles.skipForwardIcon}
351+
/>
352+
<Text style={styles.skipText} aria-hidden={true}>
353+
{SKIP_INTERVAL}
354+
</Text>
355+
</TouchableOpacity>
356+
)}
357+
</View>
306358
<View style={styles.bottomInfo}>
307359
{!archiveState.isPlayingArchive && showDescription && (
308360
<Text
@@ -408,4 +460,27 @@ const styles = StyleSheet.create({
408460
textAlign: 'center',
409461
},
410462
liveButtonTextActive: { color: '#FFFFFF' },
463+
playbackControls: {
464+
flexDirection: 'row',
465+
alignItems: 'center',
466+
justifyContent: 'center',
467+
gap: 32,
468+
},
469+
skipButton: {
470+
alignItems: 'center',
471+
justifyContent: 'center',
472+
width: 48,
473+
height: 48,
474+
},
475+
skipBackIcon: {
476+
transform: [{ scaleX: -1 }],
477+
},
478+
skipForwardIcon: {
479+
transform: [{ scaleX: 1 }],
480+
},
481+
skipText: {
482+
color: COLORS.TEXT.PRIMARY,
483+
fontSize: 10,
484+
fontWeight: '600',
485+
},
411486
});

src/app/Schedule/ArchivedShowView.tsx

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { PlaylistService } from '@services/PlaylistService';
2828
import { ArchiveService } from '@services/ArchiveService';
2929
import { secondsToTime, formatTime } from '@utils/DateTime';
3030
import { COLORS } from '@utils/Colors';
31+
import { SKIP_INTERVAL } from '@utils/TrackPlayerUtils';
3132
import {
3233
generateDarkGradientColors,
3334
generateGradientColors,
@@ -205,6 +206,19 @@ export default function ArchivedShowView() {
205206
}
206207
};
207208

209+
const handleSkipBackward = async () => {
210+
const newPosition = Math.max(progress.position - SKIP_INTERVAL, 0);
211+
await TrackPlayer.seekTo(newPosition);
212+
};
213+
214+
const handleSkipForward = async () => {
215+
const newPosition = Math.min(
216+
progress.position + SKIP_INTERVAL,
217+
progress.duration,
218+
);
219+
await TrackPlayer.seekTo(newPosition);
220+
};
221+
208222
return (
209223
<>
210224
<StatusBar barStyle="light-content" backgroundColor={gradientStart} />
@@ -221,8 +235,26 @@ export default function ArchivedShowView() {
221235
>
222236
<ShowImage showName={show.name} archiveDate={archive.date} />
223237

224-
{/* Play/Pause Button */}
238+
{/* Playback Controls */}
225239
<View style={styles.playSection}>
240+
{isArchivePlaying && (
241+
<TouchableOpacity
242+
style={styles.skipButton}
243+
onPress={handleSkipBackward}
244+
activeOpacity={0.7}
245+
accessibilityLabel={`Skip backward ${SKIP_INTERVAL} seconds`}
246+
>
247+
<Icon
248+
name="refresh-outline"
249+
size={28}
250+
color={COLORS.TEXT.PRIMARY}
251+
style={styles.skipBackIcon}
252+
/>
253+
<Text style={styles.skipText} aria-hidden={true}>
254+
{SKIP_INTERVAL}
255+
</Text>
256+
</TouchableOpacity>
257+
)}
226258
<TouchableOpacity
227259
style={styles.playButton}
228260
onPress={handlePlayPause}
@@ -234,6 +266,24 @@ export default function ArchivedShowView() {
234266
<Icon name="play-circle" size={64} color="#FFFFFF" />
235267
)}
236268
</TouchableOpacity>
269+
{isArchivePlaying && (
270+
<TouchableOpacity
271+
style={styles.skipButton}
272+
onPress={handleSkipForward}
273+
activeOpacity={0.7}
274+
accessibilityLabel={`Skip forward ${SKIP_INTERVAL} seconds`}
275+
>
276+
<Icon
277+
name="refresh-outline"
278+
size={28}
279+
color={COLORS.TEXT.PRIMARY}
280+
style={styles.skipForwardIcon}
281+
/>
282+
<Text style={styles.skipText} aria-hidden={true}>
283+
{SKIP_INTERVAL}
284+
</Text>
285+
</TouchableOpacity>
286+
)}
237287
</View>
238288

239289
{/* Progress Bar - Only show when this archive is playing */}
@@ -342,11 +392,32 @@ const styles = StyleSheet.create({
342392
flex: 1,
343393
},
344394
playSection: {
395+
flexDirection: 'row',
345396
alignItems: 'center',
397+
justifyContent: 'center',
346398
paddingVertical: 20,
399+
gap: 24,
347400
},
348401
playButton: {
349-
padding: 16,
402+
padding: 8,
403+
},
404+
skipButton: {
405+
alignItems: 'center',
406+
justifyContent: 'center',
407+
width: 48,
408+
height: 48,
409+
},
410+
skipBackIcon: {
411+
transform: [{ scaleX: -1 }],
412+
},
413+
skipForwardIcon: {
414+
transform: [{ scaleX: 1 }],
415+
},
416+
skipText: {
417+
color: COLORS.TEXT.PRIMARY,
418+
fontSize: 10,
419+
fontWeight: '600',
420+
marginTop: 0,
350421
},
351422
playlistSection: {
352423
paddingHorizontal: 20,

src/services/ArchiveService.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import TrackPlayer, { Track } from 'react-native-track-player';
22
import { Show, Archive } from '@customTypes/RecentlyPlayed';
33
import { debugLog, debugError } from '@utils/Debug';
44
import { DEFAULT_NAME } from '@customTypes/Playlist';
5-
import { archiveCapabilities, liveCapabilities } from '@utils/TrackPlayerUtils';
5+
import {
6+
archiveCapabilities,
7+
liveCapabilities,
8+
SKIP_INTERVAL,
9+
} from '@utils/TrackPlayerUtils';
610

711
export interface ArchivePlaybackState {
812
isPlayingArchive: boolean;
@@ -65,8 +69,8 @@ export class ArchiveService {
6569
await TrackPlayer.updateOptions({
6670
capabilities: archiveCapabilities,
6771
compactCapabilities: archiveCapabilities,
68-
forwardJumpInterval: 30,
69-
backwardJumpInterval: 30,
72+
forwardJumpInterval: SKIP_INTERVAL,
73+
backwardJumpInterval: SKIP_INTERVAL,
7074
});
7175

7276
// Add and play archive

src/utils/TrackPlayerUtils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Capability } from 'react-native-track-player';
22

3+
export const SKIP_INTERVAL = 30;
4+
35
export const archiveCapabilities = [
46
Capability.Play,
57
Capability.Pause,

0 commit comments

Comments
 (0)