31 |
32 | {queueItems.map(({ track, isPlaying }, index) => (
33 |
34 |
42 | track && handlePlayQueueTrack(track?.id, index)
43 | }
44 | primaryText={track?.name ?? 'Unknown Track'}
45 | rightNode={
46 | track?.link && (
47 | {
54 | handleVisitTrackOnMusicService(track?.link);
55 | }
56 | }
57 | ]}
58 | />
59 | )
60 | }
61 | secondaryText={`${track?.artistName}${
62 | track?.albumName && ` • ${track?.albumName}`
63 | }`}
64 | />
65 |
66 | ))}
67 |
68 |
69 | );
70 | };
71 |
72 | const QueueList = styled(List)`
73 | background: ${token('colors.surface')};
74 | `;
75 |
--------------------------------------------------------------------------------
/src/ui/shared/components/Queue/useQueue.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 |
3 | import type { MusicService } from '~/types';
4 | import { getLink } from '~core/links';
5 | import { sendToContent } from '~core/messaging/sendToContent';
6 | import { MusicControllerMessage } from '~types';
7 | import { useMusicServiceTab } from '~ui/shared/contexts/MusicServiceTab';
8 | import { sendAnalytic } from '~util/analytics';
9 | import { findIndexes } from '~util/findIndexes';
10 | import { getMusicServiceName } from '~util/musicService';
11 |
12 | export const useQueue = (startAt: 'top' | 'next', count?: number) => {
13 | const { musicServiceTab } = useMusicServiceTab();
14 | const playerState = musicServiceTab?.playbackState;
15 | const currentTrack = musicServiceTab?.currentTrack;
16 |
17 | const queue = useMemo(() => {
18 | let currentQueue = playerState?.queue;
19 |
20 | if (!currentQueue) {
21 | return [];
22 | }
23 |
24 | if (startAt === 'top') {
25 | currentQueue = currentQueue.slice(0);
26 | } else {
27 | const currentTrackIndex = currentQueue.findIndex(
28 | (item) => item.track?.id === currentTrack?.id
29 | );
30 |
31 | currentQueue = currentQueue.slice(currentTrackIndex);
32 | }
33 |
34 | if (count) {
35 | currentQueue = currentQueue.slice(0, count);
36 | }
37 |
38 | return currentQueue;
39 | }, [playerState, count, currentTrack?.id, startAt]);
40 |
41 | const musicServiceName = useMemo(
42 | () =>
43 | musicServiceTab ? getMusicServiceName(musicServiceTab.musicService) : '',
44 | [musicServiceTab]
45 | );
46 |
47 | const handlePlayQueueTrack = (trackId: string, trackIndex: number) => {
48 | const trackIndexes = findIndexes(
49 | queue,
50 | (item) => item.track?.id === trackId
51 | );
52 | const duplicateIndex = trackIndexes.indexOf(trackIndex);
53 |
54 | sendToContent(
55 | {
56 | name: MusicControllerMessage.PLAY_QUEUE_TRACK,
57 | body: {
58 | trackId,
59 | duplicateIndex
60 | }
61 | },
62 | musicServiceTab?.tabId
63 | );
64 |
65 | sendAnalytic({
66 | name: 'play_queue_track'
67 | });
68 | };
69 |
70 | const handleVisitTrackOnMusicService = (link?: string) => {
71 | if (!link) {
72 | return;
73 | }
74 |
75 | window.open(link, '_blank');
76 |
77 | sendAnalytic({
78 | name: 'visit_music_service_link'
79 | });
80 | };
81 |
82 | return {
83 | handlePlayQueueTrack,
84 | handleVisitTrackOnMusicService,
85 | musicService: musicServiceTab?.musicService,
86 | musicServiceName,
87 | queueItems: queue
88 | };
89 | };
90 |
--------------------------------------------------------------------------------
/src/ui/shared/components/RepeatButton/index.tsx:
--------------------------------------------------------------------------------
1 | import { Icon } from '@synqapp/ui';
2 | import { css, styled, useTheme } from 'styled-components';
3 |
4 | import { RepeatMode } from '~types';
5 |
6 | import { useRepeatButton } from './useRepeatButton';
7 |
8 | interface RepeatButtonProps {
9 | size?: number;
10 | }
11 |
12 | export const RepeatButton = ({ size }: RepeatButtonProps) => {
13 | const { repeatMode, handleClick } = useRepeatButton();
14 | const theme = useTheme();
15 |
16 | if (!size) {
17 | size = 32;
18 | }
19 |
20 | return (
21 |