Skip to content

Commit 2f86376

Browse files
authored
feat: add download attachment message action (#3126)
1 parent 3702caa commit 2f86376

38 files changed

Lines changed: 1079 additions & 368 deletions

src/components/Attachment/Audio.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import React from 'react';
22
import type { Attachment } from 'stream-chat';
33

4-
import { FileSizeIndicator as DefaultFileSizeIndicator } from './components';
4+
import {
5+
FileSizeIndicator as DefaultFileSizeIndicator,
6+
DownloadButton,
7+
} from './components';
58
import type { AudioPlayerState } from '../AudioPlayback/AudioPlayer';
69
import { useAudioPlayer } from '../AudioPlayback/WithAudioPlayback';
710
import { useStateStore } from '../../store';
@@ -34,12 +37,6 @@ const AudioAttachmentUI = ({ audioPlayer }: AudioAttachmentUIProps) => {
3437
<div className='str-chat__message-attachment-audio-widget--title'>
3538
{audioPlayer.title}
3639
</div>
37-
<FileIcon
38-
className='str-chat__file-icon'
39-
mimeType={audioPlayer.mimeType}
40-
size='sm'
41-
/>
42-
{/*<DownloadButton assetUrl={audioPlayer.src} />*/}
4340
</div>
4441
<div className='str-chat__message-attachment-audio-widget--text-second-row'>
4542
{durationSeconds ? (
@@ -59,6 +56,8 @@ const AudioAttachmentUI = ({ audioPlayer }: AudioAttachmentUIProps) => {
5956
)}
6057
</div>
6158
</div>
59+
<FileIcon className='str-chat__file-icon' mimeType={audioPlayer.mimeType} />
60+
<DownloadButton assetUrl={audioPlayer.src} suggestedFileName={audioPlayer.title} />
6261
</div>
6362
);
6463
};

src/components/Attachment/FileAttachment.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { useComponentContext } from '../../context/ComponentContext';
33
import { FileIcon } from '../FileIcon';
44
import type { Attachment } from 'stream-chat';
55

6-
import { FileSizeIndicator as DefaultFileSizeIndicator } from './components';
6+
import {
7+
FileSizeIndicator as DefaultFileSizeIndicator,
8+
DownloadButton,
9+
} from './components';
710

811
export type FileAttachmentProps = {
912
attachment: Attachment;
@@ -31,12 +34,15 @@ export const FileAttachment = ({ attachment }: FileAttachmentProps) => {
3134
>
3235
{attachment.title}
3336
</div>
34-
{/*<DownloadButton assetUrl={attachment.asset_url} />*/}
3537
</div>
3638
<div className='str-chat__message-attachment-file--item__data'>
3739
<FileSizeIndicator fileSize={attachment.file_size} />
3840
</div>
3941
</div>
42+
<DownloadButton
43+
assetUrl={attachment.asset_url}
44+
suggestedFileName={attachment.title}
45+
/>
4046
</div>
4147
);
4248
};

src/components/Attachment/__tests__/__snapshots__/File.test.tsx.snap

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ exports[`File > should render File component 1`] = `
88
>
99
<svg
1010
class="str-chat__file-icon str-chat__file-icon--size-md str-chat__file-icon--pdf str-chat__file-icon--no-label str-chat__file-icon"
11-
height="40"
12-
label="pdf"
11+
height="32"
12+
style="flex-shrink: 0; height: 32px; width: 26px;"
1313
viewBox="0 0 32 40"
14-
width="32"
14+
width="26"
1515
xmlns="http://www.w3.org/2000/svg"
1616
>
1717
<g
@@ -22,34 +22,18 @@ exports[`File > should render File component 1`] = `
2222
fill="#E71A01"
2323
/>
2424
<path
25-
d="M32 10H25.4C23.7431 10 22.4 8.65685 22.4 7V0L32 10Z"
25+
d="M23.7533 19.2C23.28 18.7033 22.3093 18.42 20.9373 18.42C20.204 18.42 19.3526 18.4906 18.43 18.656C17.8647 18.1124 17.3499 17.5186 16.892 16.882C16.5373 16.4086 16.2293 15.8886 15.9453 15.3913C16.49 13.7353 16.75 12.3866 16.75 11.4166C16.75 10.3286 16.348 9.19263 15.1886 9.19263C14.834 9.19263 14.4786 9.40596 14.2893 9.71329C13.7693 10.636 14.006 12.6466 14.9046 14.634C14.5664 15.6502 14.1877 16.6526 13.7693 17.6386C13.3906 18.538 12.9646 19.4606 12.4913 20.312C9.88931 21.3533 8.20931 22.56 8.01998 23.5053C7.94931 23.8606 8.06731 24.192 8.32798 24.452C8.42265 24.5233 8.75398 24.8073 9.32131 24.8073C11.048 24.8073 12.87 22.0153 13.7926 20.336C14.5026 20.0993 15.2126 19.8626 15.922 19.6733C16.6704 19.4701 17.4284 19.3043 18.1933 19.1766C20.0146 20.8086 21.6233 21.0693 22.428 21.0693C23.4213 21.0693 23.7766 20.6666 23.8946 20.336C24.108 19.9093 23.966 19.4366 23.7533 19.2ZM22.8066 19.8866C22.7353 20.2413 22.38 20.478 21.884 20.478C21.742 20.478 21.624 20.454 21.4813 20.4306C20.5826 20.2173 19.7306 19.768 18.8793 19.058C19.5357 18.947 20.2003 18.8917 20.866 18.8926C21.3633 18.8926 21.7893 18.916 22.0726 18.9873C22.404 19.058 22.9246 19.2713 22.806 19.886L22.8066 19.8866ZM17.7906 18.7746C17.1305 18.8983 16.4755 19.0482 15.8273 19.224C15.262 19.3709 14.7017 19.5366 14.1473 19.7206C14.4355 19.1625 14.7039 18.5944 14.952 18.0173C15.236 17.3546 15.472 16.6686 15.7093 16.03C15.9453 16.432 16.206 16.8346 16.466 17.1893C16.8911 17.7313 17.3329 18.26 17.7906 18.7746ZM14.692 9.97329C14.7338 9.88949 14.7978 9.81875 14.877 9.7688C14.9563 9.71884 15.0477 9.69157 15.1413 9.68996C15.638 9.68996 15.7326 10.2573 15.7326 10.7073C15.7326 11.464 15.496 12.6233 15.094 13.948C14.4073 12.1026 14.3606 10.5653 14.692 9.97329ZM12.0893 20.998C10.8826 22.9853 9.72331 24.2153 9.01398 24.2153C8.88599 24.2161 8.76132 24.1746 8.65931 24.0973C8.51731 23.9553 8.44598 23.79 8.49331 23.6006C8.63531 22.8906 10.0073 21.8973 12.0893 20.998Z"
2626
fill="white"
27-
opacity="0.5"
2827
/>
29-
<g
30-
clip-path="url(#clip0_1008_7871)"
31-
>
32-
<path
33-
d="M23.7533 19.2C23.28 18.7033 22.3093 18.42 20.9373 18.42C20.204 18.42 19.3526 18.4906 18.43 18.656C17.8647 18.1124 17.3499 17.5186 16.892 16.882C16.5373 16.4086 16.2293 15.8886 15.9453 15.3913C16.49 13.7353 16.75 12.3866 16.75 11.4166C16.75 10.3286 16.348 9.19263 15.1886 9.19263C14.834 9.19263 14.4786 9.40596 14.2893 9.71329C13.7693 10.636 14.006 12.6466 14.9046 14.634C14.5664 15.6502 14.1877 16.6526 13.7693 17.6386C13.3906 18.538 12.9646 19.4606 12.4913 20.312C9.88931 21.3533 8.20931 22.56 8.01998 23.5053C7.94931 23.8606 8.06731 24.192 8.32798 24.452C8.42265 24.5233 8.75398 24.8073 9.32131 24.8073C11.048 24.8073 12.87 22.0153 13.7926 20.336C14.5026 20.0993 15.2126 19.8626 15.922 19.6733C16.6704 19.4701 17.4284 19.3043 18.1933 19.1766C20.0146 20.8086 21.6233 21.0693 22.428 21.0693C23.4213 21.0693 23.7766 20.6666 23.8946 20.336C24.108 19.9093 23.966 19.4366 23.7533 19.2ZM22.8066 19.8866C22.7353 20.2413 22.38 20.478 21.884 20.478C21.742 20.478 21.624 20.454 21.4813 20.4306C20.5826 20.2173 19.7306 19.768 18.8793 19.058C19.5357 18.947 20.2003 18.8917 20.866 18.8926C21.3633 18.8926 21.7893 18.916 22.0726 18.9873C22.404 19.058 22.9246 19.2713 22.806 19.886L22.8066 19.8866ZM17.7906 18.7746C17.1305 18.8983 16.4755 19.0482 15.8273 19.224C15.262 19.3709 14.7017 19.5366 14.1473 19.7206C14.4355 19.1625 14.7039 18.5944 14.952 18.0173C15.236 17.3546 15.472 16.6686 15.7093 16.03C15.9453 16.432 16.206 16.8346 16.466 17.1893C16.8911 17.7313 17.3329 18.26 17.7906 18.7746ZM14.692 9.97329C14.7338 9.88949 14.7978 9.81875 14.877 9.7688C14.9563 9.71884 15.0477 9.69157 15.1413 9.68996C15.638 9.68996 15.7326 10.2573 15.7326 10.7073C15.7326 11.464 15.496 12.6233 15.094 13.948C14.4073 12.1026 14.3606 10.5653 14.692 9.97329ZM12.0893 20.998C10.8826 22.9853 9.72331 24.2153 9.01398 24.2153C8.88599 24.2161 8.76132 24.1746 8.65931 24.0973C8.51731 23.9553 8.44598 23.79 8.49331 23.6006C8.63531 22.8906 10.0073 21.8973 12.0893 20.998Z"
34-
fill="white"
35-
/>
36-
</g>
3728
<path
3829
d="M9.74219 34.4258V28.6992H10.8828V29.3633H10.9531C11.0286 29.2096 11.1276 29.0781 11.25 28.9688C11.375 28.8594 11.5208 28.776 11.6875 28.7188C11.8542 28.6589 12.0391 28.6289 12.2422 28.6289C12.6016 28.6289 12.9115 28.7188 13.1719 28.8984C13.4323 29.0781 13.6328 29.3333 13.7734 29.6641C13.9167 29.9922 13.9883 30.3854 13.9883 30.8438V30.8516C13.9883 31.3125 13.918 31.7083 13.7773 32.0391C13.6367 32.3698 13.4362 32.6237 13.1758 32.8008C12.9154 32.9779 12.6042 33.0664 12.2422 33.0664C12.0443 33.0664 11.8607 33.0365 11.6914 32.9766C11.5247 32.9141 11.3776 32.8268 11.25 32.7148C11.125 32.6029 11.026 32.4688 10.9531 32.3125H10.8828V34.4258H9.74219ZM11.8516 32.1211C12.0547 32.1211 12.2279 32.0703 12.3711 31.9688C12.5169 31.8672 12.6289 31.7214 12.707 31.5312C12.7878 31.3411 12.8281 31.1146 12.8281 30.8516V30.8438C12.8281 30.5807 12.7878 30.3542 12.707 30.1641C12.6289 29.974 12.5169 29.8281 12.3711 29.7266C12.2279 29.625 12.0547 29.5742 11.8516 29.5742C11.651 29.5742 11.4766 29.625 11.3281 29.7266C11.1823 29.8281 11.069 29.974 10.9883 30.1641C10.9102 30.3516 10.8711 30.5781 10.8711 30.8438V30.8516C10.8711 31.112 10.9115 31.3385 10.9922 31.5312C11.0729 31.7214 11.1862 31.8672 11.332 31.9688C11.4805 32.0703 11.6536 32.1211 11.8516 32.1211ZM16.457 33.0664C16.1003 33.0664 15.7904 32.9779 15.5273 32.8008C15.2669 32.6211 15.0651 32.3659 14.9219 32.0352C14.7812 31.7044 14.7109 31.3099 14.7109 30.8516V30.8438C14.7109 30.3828 14.7799 29.987 14.918 29.6562C15.0586 29.3255 15.2591 29.0716 15.5195 28.8945C15.7799 28.7174 16.0924 28.6289 16.457 28.6289C16.6523 28.6289 16.8333 28.6602 17 28.7227C17.1693 28.7826 17.3177 28.8685 17.4453 28.9805C17.5729 29.0924 17.6719 29.2279 17.7422 29.3867H17.8125V27.0547H18.9531V33H17.8125V32.332H17.7422C17.6693 32.4857 17.5703 32.6172 17.4453 32.7266C17.3229 32.8359 17.1784 32.9206 17.0117 32.9805C16.8451 33.0378 16.6602 33.0664 16.457 33.0664ZM16.8438 32.1211C17.0469 32.1211 17.2214 32.0703 17.3672 31.9688C17.513 31.8672 17.625 31.7214 17.7031 31.5312C17.7839 31.3411 17.8242 31.1159 17.8242 30.8555V30.8477C17.8242 30.582 17.7839 30.3555 17.7031 30.168C17.625 29.9779 17.5117 29.832 17.3633 29.7305C17.2174 29.6263 17.0443 29.5742 16.8438 29.5742C16.6458 29.5742 16.4727 29.6263 16.3242 29.7305C16.1784 29.832 16.0664 29.9779 15.9883 30.168C15.9102 30.3555 15.8711 30.5807 15.8711 30.8438V30.8516C15.8711 31.1146 15.9102 31.3411 15.9883 31.5312C16.0664 31.7214 16.1784 31.8672 16.3242 31.9688C16.4701 32.0703 16.6432 32.1211 16.8438 32.1211ZM20.4648 33V29.5586H19.7695V28.6992H20.4648V28.2969C20.4648 28.0104 20.5156 27.7721 20.6172 27.582C20.7188 27.3919 20.8776 27.25 21.0938 27.1562C21.3125 27.0599 21.5951 27.0117 21.9414 27.0117C22.0586 27.0117 22.1641 27.0156 22.2578 27.0234C22.3542 27.0312 22.444 27.0417 22.5273 27.0547V27.8164C22.4909 27.8086 22.4427 27.8034 22.3828 27.8008C22.3255 27.7956 22.2604 27.793 22.1875 27.793C21.9661 27.793 21.8099 27.8438 21.7188 27.9453C21.6276 28.0443 21.582 28.1875 21.582 28.375V28.6992H22.4922V29.5586H21.6055V33H20.4648Z"
3930
fill="white"
4031
/>
41-
<defs>
42-
<clippath
43-
id="clip0_1008_7871"
44-
>
45-
<rect
46-
fill="white"
47-
height="16"
48-
transform="translate(8 9)"
49-
width="16"
50-
/>
51-
</clippath>
52-
</defs>
32+
<path
33+
d="M32 10H25.4C23.7431 10 22.4 8.65685 22.4 7V0L32 10Z"
34+
fill="white"
35+
opacity="0.5"
36+
/>
5337
</g>
5438
</svg>
5539
<div
@@ -76,6 +60,30 @@ exports[`File > should render File component 1`] = `
7660
</span>
7761
</div>
7862
</div>
63+
<a
64+
aria-label="Download attachment"
65+
class="str-chat__button str-chat__button--secondary str-chat__button--outline str-chat__button--circular str-chat__button--size-sm str-chat__audio-attachment-download-button"
66+
download="Nice file"
67+
href="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
68+
rel="noopener noreferrer"
69+
target="_blank"
70+
title="Download Attachment"
71+
>
72+
<div
73+
class="str-chat__button__content"
74+
>
75+
<svg
76+
class="str-chat__icon str-chat__icon--download str-chat__icon str-chat__audio-attachment-download-button__icon"
77+
viewBox="0 0 20 20"
78+
xmlns="http://www.w3.org/2000/svg"
79+
>
80+
<path
81+
d="M2.375 11.25C2.375 10.8358 2.71079 10.5 3.125 10.5C3.53921 10.5 3.875 10.8358 3.875 11.25V15.5H16.125V11.25C16.125 10.8358 16.4608 10.5 16.875 10.5C17.2892 10.5 17.625 10.8358 17.625 11.25V16.25C17.625 16.6642 17.2892 17 16.875 17H3.125C2.71079 17 2.375 16.6642 2.375 16.25V11.25ZM9.25 2.5C9.25 2.08579 9.58579 1.75 10 1.75C10.4142 1.75 10.75 2.08579 10.75 2.5V9.43945L12.5947 7.59473C12.8876 7.30183 13.3624 7.30183 13.6553 7.59473C13.9482 7.88762 13.9482 8.36238 13.6553 8.65527L10.5303 11.7803C10.2374 12.0732 9.76262 12.0732 9.46973 11.7803L6.34473 8.65527C6.05183 8.36238 6.05183 7.88762 6.34473 7.59473C6.63762 7.30183 7.11238 7.30183 7.40527 7.59473L9.25 9.43945V2.5Z"
82+
fill="currentColor"
83+
/>
84+
</svg>
85+
</div>
86+
</a>
7987
</div>
8088
</div>
8189
`;
Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
11
import React from 'react';
2+
import clsx from 'clsx';
3+
import { sanitizeUrl } from '@braintree/sanitize-url';
24

3-
import { DownloadIcon } from '../icons';
4-
import { SafeAnchor } from '../../SafeAnchor';
5+
import { useTranslationContext } from '../../../context';
6+
import { IconDownload } from '../../Icons';
57

6-
type DownloadButtonProps = {
8+
export type DownloadButtonProps = {
9+
/** Attachment asset URL (e.g. `asset_url`). */
710
assetUrl?: string;
11+
className?: string;
12+
/** Suggested filename for the `download` attribute (not the HTML `title` tooltip). */
13+
suggestedFileName?: string;
14+
/** Native browser tooltip; defaults to translated “Download Attachment”. */
15+
tooltipTitle?: string;
816
};
917

10-
export const DownloadButton = ({ assetUrl }: DownloadButtonProps) => (
11-
<SafeAnchor
12-
className='str-chat__message-attachment-file--item-download'
13-
download
14-
href={assetUrl}
15-
target='_blank'
16-
>
17-
<DownloadIcon className='str-chat__message-attachment-download-icon' />
18-
</SafeAnchor>
19-
);
18+
/**
19+
* Icon download control for {@link Audio} and {@link FileAttachment} rows.
20+
* (BaseImage defines its own small download link when `showDownloadButtonOnError` is used.)
21+
*/
22+
export const DownloadButton = ({
23+
assetUrl,
24+
className,
25+
suggestedFileName,
26+
tooltipTitle,
27+
}: DownloadButtonProps) => {
28+
const { t } = useTranslationContext();
29+
if (!assetUrl) return null;
30+
const href = sanitizeUrl(assetUrl);
31+
if (!href) return null;
32+
33+
return (
34+
<a
35+
aria-label={t('aria/Download attachment')}
36+
className={clsx(
37+
'str-chat__button',
38+
'str-chat__button--secondary',
39+
'str-chat__button--outline',
40+
'str-chat__button--circular',
41+
'str-chat__button--size-sm',
42+
'str-chat__audio-attachment-download-button',
43+
className,
44+
)}
45+
download={suggestedFileName ?? ''}
46+
href={href}
47+
rel='noopener noreferrer'
48+
target='_blank'
49+
title={tooltipTitle ?? t('Download Attachment')}
50+
>
51+
<div className='str-chat__button__content'>
52+
<IconDownload className='str-chat__icon str-chat__audio-attachment-download-button__icon' />
53+
</div>
54+
</a>
55+
);
56+
};

src/components/Attachment/styling/Audio.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@
4646
gap: var(--spacing-xxs);
4747
}
4848

49-
.str-chat__message-attachment-audio-widget--progress-track {
50-
//max-width: 120px
51-
}
52-
5349
.str-chat__message-attachment-audio-widget--text-first-row {
5450
display: flex;
5551
justify-content: space-between;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.str-chat__button.str-chat__button--secondary.str-chat__button--outline.str-chat__audio-attachment-download-button {
2+
border-color: var(--chat-border-on-chat-incoming);
3+
flex-shrink: 0;
4+
5+
.str-chat__attachment-download-button__icon {
6+
height: 20px;
7+
width: 20px;
8+
}
9+
}
10+
11+
.str-chat__message--me
12+
.str-chat__button.str-chat__button--outline.str-chat__audio-attachment-download-button {
13+
border-color: var(--chat-border-on-chat-outgoing);
14+
}

src/components/Attachment/styling/ModalGallery.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@
112112

113113
&.str-chat__modal-gallery__image--load-failed {
114114
cursor: pointer;
115+
min-height: 200px;
116+
117+
.str-chat__image-placeholder.str-chat__base-image--load-failed {
118+
width: 100%;
119+
min-height: 200px;
120+
align-self: stretch;
121+
}
115122

116123
img {
117124
opacity: 0;

src/components/Attachment/styling/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
@use 'AttachmentActions';
33
@use 'Audio';
44
@use 'CardAudio';
5+
@use 'DownloadButton';
56
@use 'Geolocation';
67
@use 'Giphy';
78
@use 'LinkPreview';

src/components/AudioPlayback/plugins/AudioPlayerNotificationsPlugin.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { TFunction } from 'i18next';
44
import type { AddNotification } from '../../Notifications/hooks/useNotificationApi';
55
import type { NotificationTargetPanel } from '../../Notifications/notificationTarget';
66

7+
const SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS = 1000;
8+
79
export const audioPlayerNotificationsPluginFactory = ({
810
addNotification,
911
panel = 'channel',
@@ -20,10 +22,25 @@ export const audioPlayerNotificationsPluginFactory = ({
2022
),
2123
'seek-not-supported': new Error(t('Cannot seek in the recording')),
2224
};
25+
let lastSeekNotSupportedNotificationAt: number | undefined;
2326

2427
return {
2528
id: 'AudioPlayerNotificationsPlugin',
2629
onError: ({ errCode, error: e }) => {
30+
if (errCode === 'seek-not-supported') {
31+
const now = Date.now();
32+
33+
if (
34+
typeof lastSeekNotSupportedNotificationAt === 'number' &&
35+
now - lastSeekNotSupportedNotificationAt <
36+
SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS
37+
) {
38+
return;
39+
}
40+
41+
lastSeekNotSupportedNotificationAt = now;
42+
}
43+
2744
const error =
2845
(errCode && errors[errCode]) ??
2946
e ??

src/components/AudioPlayback/plugins/__tests__/AudioPlayerNotificationsPlugin.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,30 @@ describe('audioPlayerNotificationsPluginFactory', () => {
6767
const call = addNotification.mock.calls[0][0];
6868
expect(call.message).toBe('Error reproducing the recording');
6969
});
70+
71+
it('debounces seek-not-supported notifications', () => {
72+
const { addNotification } = makeNotifier();
73+
const plugin = audioPlayerNotificationsPluginFactory({
74+
addNotification,
75+
t,
76+
});
77+
const dateNowSpy = vi.spyOn(Date, 'now');
78+
79+
dateNowSpy
80+
.mockReturnValueOnce(1000)
81+
.mockReturnValueOnce(1200)
82+
.mockReturnValueOnce(1800)
83+
.mockReturnValueOnce(2201);
84+
85+
plugin.onError?.({ errCode: 'seek-not-supported', player: fromPartial({}) });
86+
plugin.onError?.({ errCode: 'seek-not-supported', player: fromPartial({}) });
87+
plugin.onError?.({ errCode: 'seek-not-supported', player: fromPartial({}) });
88+
plugin.onError?.({ errCode: 'seek-not-supported', player: fromPartial({}) });
89+
90+
expect(addNotification).toHaveBeenCalledTimes(2);
91+
expect(addNotification.mock.calls[0][0].message).toBe('Cannot seek in the recording');
92+
expect(addNotification.mock.calls[1][0].message).toBe('Cannot seek in the recording');
93+
94+
dateNowSpy.mockRestore();
95+
});
7096
});

0 commit comments

Comments
 (0)