Skip to content

Commit d30c719

Browse files
committed
fix: differentiates between in thread and in channel audio player with the same asset url
1 parent bc598a2 commit d30c719

File tree

6 files changed

+179
-16
lines changed

6 files changed

+179
-16
lines changed

src/components/Attachment/Audio.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ const UnMemoizedAudio = (props: AudioProps) => {
3131
* with the default SDK components, but can be done with custom API calls.In this case all the Audio
3232
* widgets will share the state.
3333
*/
34-
const { message } = useMessageContext() ?? {};
34+
const { message, threadList } = useMessageContext() ?? {};
3535

3636
const audioPlayer = useAudioPlayer({
3737
mimeType: mime_type,
38-
requester: message?.id && `${message.parent_id}${message.id}`,
38+
requester:
39+
message?.id &&
40+
`${threadList ? (message.parent_id ?? message.id) : ''}${message.id}`,
3941
src: asset_url,
4042
});
4143

src/components/Attachment/Card.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,13 @@ const AudioWidget = ({ mimeType, src }: { src: string; mimeType?: string }) => {
144144
* with the default SDK components, but can be done with custom API calls.In this case all the Audio
145145
* widgets will share the state.
146146
*/
147-
const { message } = useMessageContext() ?? {};
147+
const { message, threadList } = useMessageContext() ?? {};
148148

149149
const audioPlayer = useAudioPlayer({
150150
mimeType,
151-
requester: message?.id && `${message.parent_id}${message.id}`,
151+
requester:
152+
message?.id &&
153+
`${threadList ? (message.parent_id ?? message.id) : ''}${message.id}`,
152154
src,
153155
});
154156

src/components/Attachment/VoiceRecording.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ export const VoiceRecordingPlayer = ({
5151
* with the default SDK components, but can be done with custom API calls.In this case all the Audio
5252
* widgets will share the state.
5353
*/
54-
const { message } = useMessageContext() ?? {};
54+
const { message, threadList } = useMessageContext() ?? {};
5555

5656
const audioPlayer = useAudioPlayer({
5757
durationSeconds: duration ?? 0,
5858
mimeType: mime_type,
5959
playbackRates,
60-
requester: message?.id && `${message.parent_id}${message.id}`,
60+
requester:
61+
message?.id &&
62+
`${threadList ? (message.parent_id ?? message.id) : ''}${message.id}`,
6163
src: asset_url,
6264
});
6365

src/components/Attachment/__tests__/Audio.test.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-libra
33
import '@testing-library/jest-dom';
44

55
import { Audio } from '../Audio';
6-
import { generateAudioAttachment } from '../../../mock-builders';
6+
import { generateAudioAttachment, generateMessage } from '../../../mock-builders';
77
import { prettifyFileSize } from '../../MessageInput/hooks/utils';
88
import { WithAudioPlayback } from '../../AudioPlayer/WithAudioPlayback';
9+
import { MessageProvider } from '../../../context';
910

1011
jest.mock('../../../context/ChatContext', () => ({
1112
useChatContext: () => ({ client: mockClient }),
@@ -243,4 +244,34 @@ describe('Audio', () => {
243244
expect(screen.getByTestId('audio-progress')).toHaveAttribute('data-progress', '50');
244245
});
245246
});
247+
248+
it('differentiates between in thread and in channel audio player', () => {
249+
const message = generateMessage();
250+
render(
251+
<WithAudioPlayback>
252+
<MessageProvider value={{ message }}>
253+
<Audio og={audioAttachment} />
254+
</MessageProvider>
255+
<MessageProvider value={{ message, threadList: true }}>
256+
<Audio og={audioAttachment} />
257+
</MessageProvider>
258+
</WithAudioPlayback>,
259+
);
260+
expect(createdAudios).toHaveLength(2);
261+
});
262+
263+
it('keeps a single copy of audio player for the same requester', () => {
264+
const message = generateMessage();
265+
render(
266+
<WithAudioPlayback>
267+
<MessageProvider value={{ message }}>
268+
<Audio og={audioAttachment} />
269+
</MessageProvider>
270+
<MessageProvider value={{ message }}>
271+
<Audio og={audioAttachment} />
272+
</MessageProvider>
273+
</WithAudioPlayback>,
274+
);
275+
expect(createdAudios).toHaveLength(1);
276+
});
246277
});

src/components/Attachment/__tests__/Card.test.js

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import '@testing-library/jest-dom';
44

55
import { Card } from '../Card';
66

7-
import { ChannelActionProvider, TranslationContext } from '../../../context';
7+
import {
8+
ChannelActionProvider,
9+
MessageProvider,
10+
TranslationContext,
11+
} from '../../../context';
812
import { ChannelStateProvider } from '../../../context/ChannelStateContext';
913
import { ChatProvider } from '../../../context/ChatContext';
1014
import { ComponentProvider } from '../../../context/ComponentContext';
@@ -13,6 +17,7 @@ import {
1317
generateChannel,
1418
generateGiphyAttachment,
1519
generateMember,
20+
generateMessage,
1621
generateUser,
1722
getOrCreateChannelApi,
1823
getTestClientWithUser,
@@ -291,4 +296,73 @@ describe('Card', () => {
291296
});
292297
expect(getByText('theverge.com')).toBeInTheDocument();
293298
});
299+
300+
it('differentiates between in thread and in channel audio player', () => {
301+
const createdAudios = []; //HTMLAudioElement[]
302+
const RealAudio = window.Audio;
303+
const spy = jest.spyOn(window, 'Audio').mockImplementation(function AudioMock(
304+
...args
305+
) {
306+
const el = new RealAudio(...args);
307+
createdAudios.push(el);
308+
return el;
309+
});
310+
311+
const audioAttachment = {
312+
...dummyAttachment,
313+
image_url: undefined,
314+
thumb_url: undefined,
315+
title: 'test',
316+
type: 'audio',
317+
};
318+
319+
const message = generateMessage();
320+
321+
render(
322+
<WithAudioPlayback>
323+
<MessageProvider value={{ message }}>
324+
<Card {...audioAttachment} />
325+
</MessageProvider>
326+
<MessageProvider value={{ message, threadList: true }}>
327+
<Card {...audioAttachment} />
328+
</MessageProvider>
329+
</WithAudioPlayback>,
330+
);
331+
expect(createdAudios).toHaveLength(2);
332+
spy.mockRestore();
333+
});
334+
335+
it('keeps a single copy of audio player for the same requester', () => {
336+
const createdAudios = []; //HTMLAudioElement[]
337+
const RealAudio = window.Audio;
338+
const spy = jest.spyOn(window, 'Audio').mockImplementation(function AudioMock(
339+
...args
340+
) {
341+
const el = new RealAudio(...args);
342+
createdAudios.push(el);
343+
return el;
344+
});
345+
346+
const audioAttachment = {
347+
...dummyAttachment,
348+
image_url: undefined,
349+
thumb_url: undefined,
350+
title: 'test',
351+
type: 'audio',
352+
};
353+
354+
const message = generateMessage();
355+
render(
356+
<WithAudioPlayback>
357+
<MessageProvider value={{ message }}>
358+
<Card {...audioAttachment} />
359+
</MessageProvider>
360+
<MessageProvider value={{ message }}>
361+
<Card {...audioAttachment} />
362+
</MessageProvider>
363+
</WithAudioPlayback>,
364+
);
365+
expect(createdAudios).toHaveLength(1);
366+
spy.mockRestore();
367+
});
294368
});

src/components/Attachment/__tests__/VoiceRecording.test.js

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import React from 'react';
22
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
33
import '@testing-library/jest-dom';
44

5-
import { generateVoiceRecordingAttachment } from '../../../mock-builders';
5+
import {
6+
generateMessage,
7+
generateVoiceRecordingAttachment,
8+
} from '../../../mock-builders';
69
import { VoiceRecording, VoiceRecordingPlayer } from '../VoiceRecording';
7-
import { ChannelActionProvider } from '../../../context';
10+
import { MessageProvider } from '../../../context';
811
import { ResizeObserverMock } from '../../../mock-builders/browser';
912
import { WithAudioPlayback } from '../../AudioPlayer';
1013

@@ -30,14 +33,11 @@ const clickPlay = async () => {
3033
jest.spyOn(window.HTMLMediaElement.prototype, 'play').mockImplementation(() => {});
3134
jest.spyOn(window.HTMLMediaElement.prototype, 'pause').mockImplementation(() => {});
3235

33-
const addNotificationSpy = jest.fn();
3436
const renderComponent = (props, VoiceRecordingComponent = VoiceRecording) =>
3537
render(
36-
<ChannelActionProvider value={{ addNotification: addNotificationSpy }}>
37-
<WithAudioPlayback>
38-
<VoiceRecordingComponent {...props} />
39-
</WithAudioPlayback>
40-
</ChannelActionProvider>,
38+
<WithAudioPlayback>
39+
<VoiceRecordingComponent {...props} />
40+
</WithAudioPlayback>,
4141
);
4242

4343
describe('VoiceRecording', () => {
@@ -51,6 +51,58 @@ describe('VoiceRecording', () => {
5151
expect(queryByTestId(QUOTED_AUDIO_RECORDING_TEST_ID)).toBeInTheDocument();
5252
expect(queryByTestId(AUDIO_RECORDING_PLAYER_TEST_ID)).not.toBeInTheDocument();
5353
});
54+
it('differentiates between in thread and in channel audio player', () => {
55+
const createdAudios = []; //HTMLAudioElement[]
56+
const RealAudio = window.Audio;
57+
const spy = jest.spyOn(window, 'Audio').mockImplementation(function AudioMock(
58+
...args
59+
) {
60+
const el = new RealAudio(...args);
61+
createdAudios.push(el);
62+
return el;
63+
});
64+
const message = generateMessage();
65+
render(
66+
<WithAudioPlayback>
67+
<MessageProvider value={{ message }}>
68+
<VoiceRecording attachment={attachment} />
69+
</MessageProvider>
70+
<MessageProvider value={{ message, threadList: true }}>
71+
<VoiceRecording attachment={attachment} />
72+
</MessageProvider>
73+
</WithAudioPlayback>,
74+
);
75+
expect(createdAudios).toHaveLength(2);
76+
spy.mockRestore();
77+
});
78+
79+
it('keeps a single copy of audio player for the same requester', () => {
80+
const createdAudios = []; //HTMLAudioElement[]
81+
const RealAudio = window.Audio;
82+
const spy = jest.spyOn(window, 'Audio').mockImplementation(function AudioMock(
83+
...args
84+
) {
85+
const el = new RealAudio(...args);
86+
createdAudios.push(el);
87+
return el;
88+
});
89+
const message = generateMessage();
90+
render(
91+
<WithAudioPlayback>
92+
<MessageProvider value={{ message }}>
93+
<VoiceRecording attachment={attachment} />
94+
</MessageProvider>
95+
<MessageProvider value={{ message }}>
96+
<VoiceRecording attachment={attachment} />
97+
</MessageProvider>
98+
<MessageProvider value={{ message }}>
99+
<VoiceRecording attachment={attachment} isQuoted={true} />
100+
</MessageProvider>
101+
</WithAudioPlayback>,
102+
);
103+
expect(createdAudios).toHaveLength(1);
104+
spy.mockRestore();
105+
});
54106
});
55107

56108
describe('VoiceRecordingPlayer', () => {

0 commit comments

Comments
 (0)