stream-chat-react 12.0.0-rc.1 → 12.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/components/Attachment/components/WaveProgressBar.d.ts +3 -1
- package/dist/components/Attachment/components/WaveProgressBar.js +44 -9
- package/dist/components/Channel/Channel.js +19 -32
- package/dist/components/Channel/channelState.js +1 -0
- package/dist/components/Channel/hooks/useCreateChannelStateContext.js +1 -1
- package/dist/components/ChannelList/ChannelList.js +1 -1
- package/dist/components/Chat/Chat.d.ts +1 -1
- package/dist/components/Chat/hooks/useChat.d.ts +2 -2
- package/dist/components/Chat/hooks/useChat.js +1 -2
- package/dist/components/Chat/hooks/useCreateChatClient.d.ts +4 -2
- package/dist/components/Chat/hooks/useCreateChatClient.js +5 -4
- package/dist/components/DateSeparator/DateSeparator.d.ts +1 -1
- package/dist/components/DateSeparator/DateSeparator.js +1 -1
- package/dist/components/EventComponent/EventComponent.d.ts +1 -1
- package/dist/components/EventComponent/EventComponent.js +1 -1
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +9 -3
- package/dist/components/MediaRecorder/AudioRecorder/AudioRecordingInProgress.js +3 -0
- package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -7
- package/dist/components/MediaRecorder/classes/MediaRecorderController.js +0 -5
- package/dist/components/MediaRecorder/hooks/index.d.ts +1 -1
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.d.ts +1 -2
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.js +1 -1
- package/dist/components/MediaRecorder/index.d.ts +1 -0
- package/dist/components/MediaRecorder/transcode/index.d.ts +6 -5
- package/dist/components/MediaRecorder/transcode/index.js +5 -15
- package/dist/components/Message/MessageSimple.js +1 -1
- package/dist/components/Message/MessageStatus.js +3 -2
- package/dist/components/Message/MessageTimestamp.d.ts +1 -2
- package/dist/components/Message/MessageTimestamp.js +0 -1
- package/dist/components/Message/Timestamp.d.ts +1 -2
- package/dist/components/Message/Timestamp.js +4 -5
- package/dist/components/Message/renderText/rehypePlugins/mentionsMarkdownPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/htmlToTextPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.js +2 -6
- package/dist/components/Message/renderText/renderText.d.ts +2 -2
- package/dist/components/Message/renderText/renderText.js +8 -6
- package/dist/components/Message/utils.js +2 -0
- package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +23 -27
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +1 -0
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +1 -1
- package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +2 -1
- package/dist/components/MessageInput/MessageInput.d.ts +4 -6
- package/dist/components/MessageInput/MessageInputFlat.js +4 -7
- package/dist/components/MessageInput/hooks/useAttachments.d.ts +1 -5
- package/dist/components/MessageInput/hooks/useAttachments.js +65 -52
- package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +2 -19
- package/dist/components/MessageInput/hooks/useMessageInputState.d.ts +2 -35
- package/dist/components/MessageInput/hooks/useMessageInputState.js +2 -107
- package/dist/components/MessageInput/hooks/usePasteHandler.js +1 -3
- package/dist/components/MessageInput/hooks/useSubmitHandler.js +19 -71
- package/dist/components/MessageInput/hooks/useTimeElapsed.js +5 -4
- package/dist/components/MessageInput/hooks/utils.d.ts +1 -2
- package/dist/components/MessageInput/icons.d.ts +0 -1
- package/dist/components/MessageInput/icons.js +0 -3
- package/dist/components/MessageInput/types.d.ts +3 -30
- package/dist/components/MessageList/MessageList.d.ts +3 -1
- package/dist/components/MessageList/MessageList.js +2 -1
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +3 -1
- package/dist/components/MessageList/VirtualizedMessageList.js +3 -3
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +3 -2
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.d.ts +2 -1
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.js +3 -3
- package/dist/components/MessageList/utils.d.ts +1 -1
- package/dist/components/MessageList/utils.js +17 -7
- package/dist/components/ReactFileUtilities/types.d.ts +0 -29
- package/dist/components/ReactFileUtilities/utils.d.ts +2 -0
- package/dist/components/ReactFileUtilities/utils.js +2 -0
- package/dist/components/Thread/Thread.d.ts +0 -2
- package/dist/components/Thread/Thread.js +1 -2
- package/dist/components/UtilityComponents/ErrorBoundary.d.ts +16 -0
- package/dist/components/UtilityComponents/ErrorBoundary.js +19 -0
- package/dist/components/UtilityComponents/index.d.ts +1 -0
- package/dist/components/UtilityComponents/index.js +1 -0
- package/dist/components/Window/Window.d.ts +1 -3
- package/dist/components/Window/Window.js +2 -2
- package/dist/context/ChannelActionContext.d.ts +2 -2
- package/dist/context/MessageInputContext.d.ts +1 -5
- package/dist/context/TranslationContext.d.ts +1 -11
- package/dist/context/TranslationContext.js +1 -9
- package/dist/css/v2/emoji-replacement.css +1 -1
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/i18n/Streami18n.d.ts +3 -3
- package/dist/i18n/Streami18n.js +1 -2
- package/dist/i18n/de.json +3 -1
- package/dist/i18n/en.json +3 -1
- package/dist/i18n/es.json +3 -1
- package/dist/i18n/fr.json +3 -1
- package/dist/i18n/hi.json +3 -1
- package/dist/i18n/index.d.ts +2 -1
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/it.json +3 -1
- package/dist/i18n/ja.json +3 -1
- package/dist/i18n/ko.json +3 -1
- package/dist/i18n/nl.json +3 -1
- package/dist/i18n/pt.json +3 -1
- package/dist/i18n/ru.json +3 -1
- package/dist/i18n/tr.json +3 -1
- package/dist/i18n/types.d.ts +26 -0
- package/dist/i18n/types.js +1 -0
- package/dist/i18n/utils.d.ts +9 -20
- package/dist/i18n/utils.js +10 -1
- package/dist/index.browser.cjs +47221 -0
- package/dist/index.browser.cjs.map +7 -0
- package/dist/{index.cjs.js → index.node.cjs} +17604 -29018
- package/dist/index.node.cjs.map +7 -0
- package/dist/{components → plugins}/Emojis/EmojiPicker.js +1 -1
- package/dist/plugins/Emojis/icons.d.ts +2 -0
- package/dist/plugins/Emojis/icons.js +4 -0
- package/dist/plugins/Emojis/index.browser.cjs +167 -0
- package/dist/plugins/Emojis/index.browser.cjs.map +7 -0
- package/dist/plugins/Emojis/index.d.ts +2 -0
- package/dist/plugins/Emojis/index.js +2 -0
- package/dist/{components/Emojis/index.cjs.js → plugins/Emojis/index.node.cjs} +31 -192
- package/dist/plugins/Emojis/index.node.cjs.map +7 -0
- package/dist/plugins/encoders/mp3.browser.cjs +105 -0
- package/dist/plugins/encoders/mp3.browser.cjs.map +7 -0
- package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.js +3 -3
- package/dist/plugins/encoders/mp3.node.cjs +109 -0
- package/dist/plugins/encoders/mp3.node.cjs.map +7 -0
- package/dist/scss/v2/Autocomplete/Autocomplete-layout.scss +1 -1
- package/dist/scss/v2/Autocomplete/Autocomplete-theme.scss +4 -2
- package/dist/scss/v2/Avatar/Avatar-layout.scss +31 -23
- package/dist/scss/v2/Channel/Channel-layout.scss +0 -4
- package/dist/scss/v2/ChannelList/ChannelList-layout.scss +0 -5
- package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
- package/dist/scss/v2/EditMessageForm/EditMessageForm-theme.scss +9 -9
- package/dist/scss/v2/Message/Message-layout.scss +39 -6
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +18 -0
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +5 -0
- package/dist/scss/v2/Thread/Thread-layout.scss +0 -5
- package/dist/scss/v2/_base.scss +1 -0
- package/dist/scss/v2/_emoji-replacement.scss +4 -2
- package/package.json +50 -18
- package/dist/components/Emojis/index.cjs.js.map +0 -7
- package/dist/components/Emojis/index.d.ts +0 -1
- package/dist/components/Emojis/index.js +0 -1
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.d.ts +0 -11
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.js +0 -51
- package/dist/components/MessageInput/hooks/useFileUploads.d.ts +0 -7
- package/dist/components/MessageInput/hooks/useFileUploads.js +0 -85
- package/dist/components/MessageInput/hooks/useImageUploads.d.ts +0 -8
- package/dist/components/MessageInput/hooks/useImageUploads.js +0 -94
- package/dist/index.cjs.js.map +0 -7
- /package/dist/{components → plugins}/Emojis/EmojiPicker.d.ts +0 -0
- /package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.d.ts +0 -0
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ For components that implement significant logic, it's helpful to split the compo
|
|
|
90
90
|
|
|
91
91
|
### Customizing Styles
|
|
92
92
|
|
|
93
|
-
The preferred method for overriding the pre-defined styles in the library is to two
|
|
93
|
+
The preferred method for overriding the pre-defined styles in the library is to two-step process. First, import our bundled CSS into the file where you instantiate your chat application. Second, locate any Stream styles you want to override using either the browser inspector or by viewing the library code. You can then add selectors to your local CSS file to override our defaults. For example:
|
|
94
94
|
|
|
95
95
|
```js
|
|
96
96
|
import 'stream-chat-react/dist/css/v2/index.css';
|
|
@@ -110,3 +110,13 @@ We recently closed a [$38 million Series B funding round](https://techcrunch.com
|
|
|
110
110
|
Our APIs are used by more than a billion end-users, and by working at Stream, you have the chance to make a huge impact on a team of very strong engineers.
|
|
111
111
|
|
|
112
112
|
Check out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs).
|
|
113
|
+
|
|
114
|
+
## Acknowledgements
|
|
115
|
+
|
|
116
|
+
### Lamejs
|
|
117
|
+
|
|
118
|
+
This project uses `lamejs` library under the LGPL license to convert the recorded audio to mp3 format.
|
|
119
|
+
The library source code is dynamically imported and used only if audio recording is enabled.
|
|
120
|
+
|
|
121
|
+
You can obtain the source code for `lamejs` from the [lamejs repository](https://github.com/gideonstele/lamejs) that is a fork of [the original JS library](https://github.com/zhuker/lamejs).
|
|
122
|
+
You can find the source code for LAME at https://lame.sourceforge.net and its license at: https://lame.sourceforge.net/license.txt
|
|
@@ -9,6 +9,8 @@ type WaveProgressBarProps = {
|
|
|
9
9
|
amplitudesCount?: number;
|
|
10
10
|
/** Progress expressed in fractional number value btw 0 and 100. */
|
|
11
11
|
progress?: number;
|
|
12
|
+
relativeAmplitudeBarWidth?: number;
|
|
13
|
+
relativeAmplitudeGap?: number;
|
|
12
14
|
};
|
|
13
|
-
export declare const WaveProgressBar: ({ amplitudesCount, progress, seek, waveformData, }: WaveProgressBarProps) => React.JSX.Element | null;
|
|
15
|
+
export declare const WaveProgressBar: ({ amplitudesCount, progress, relativeAmplitudeBarWidth, relativeAmplitudeGap, seek, waveformData, }: WaveProgressBarProps) => React.JSX.Element | null;
|
|
14
16
|
export {};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import throttle from 'lodash.throttle';
|
|
2
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
2
3
|
import clsx from 'clsx';
|
|
3
4
|
import { resampleWaveformData } from '../audioSampling';
|
|
4
|
-
export const WaveProgressBar = ({ amplitudesCount = 40, progress = 0, seek, waveformData, }) => {
|
|
5
|
+
export const WaveProgressBar = ({ amplitudesCount = 40, progress = 0, relativeAmplitudeBarWidth = 2, relativeAmplitudeGap = 1, seek, waveformData, }) => {
|
|
5
6
|
const [progressIndicator, setProgressIndicator] = useState(null);
|
|
6
7
|
const isDragging = useRef(false);
|
|
7
|
-
const
|
|
8
|
+
const [root, setRoot] = useState(null);
|
|
9
|
+
const [trackAxisX, setTrackAxisX] = useState();
|
|
10
|
+
const lastRootWidth = useRef();
|
|
8
11
|
const handleDragStart = (e) => {
|
|
9
12
|
e.preventDefault();
|
|
10
13
|
if (!progressIndicator)
|
|
@@ -25,22 +28,54 @@ export const WaveProgressBar = ({ amplitudesCount = 40, progress = 0, seek, wave
|
|
|
25
28
|
isDragging.current = false;
|
|
26
29
|
progressIndicator.style.removeProperty('cursor');
|
|
27
30
|
}, [progressIndicator]);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const getTrackAxisX = useMemo(() => throttle((rootWidth) => {
|
|
32
|
+
if (rootWidth === lastRootWidth.current)
|
|
33
|
+
return;
|
|
34
|
+
lastRootWidth.current = rootWidth;
|
|
35
|
+
const possibleAmpCount = Math.floor(rootWidth / (relativeAmplitudeGap + relativeAmplitudeBarWidth));
|
|
36
|
+
const tooManyAmplitudesToRender = possibleAmpCount < amplitudesCount;
|
|
37
|
+
const barCount = tooManyAmplitudesToRender ? possibleAmpCount : amplitudesCount;
|
|
38
|
+
const amplitudeBarWidthToGapRatio = relativeAmplitudeBarWidth / (relativeAmplitudeBarWidth + relativeAmplitudeGap);
|
|
39
|
+
const barWidth = barCount && (rootWidth / barCount) * amplitudeBarWidthToGapRatio;
|
|
40
|
+
setTrackAxisX({
|
|
41
|
+
barCount,
|
|
42
|
+
barWidth,
|
|
43
|
+
gap: barWidth * (relativeAmplitudeGap / relativeAmplitudeBarWidth),
|
|
44
|
+
});
|
|
45
|
+
}, 1), [relativeAmplitudeBarWidth, relativeAmplitudeGap, amplitudesCount]);
|
|
46
|
+
const resampledWaveformData = useMemo(() => (trackAxisX ? resampleWaveformData(waveformData, trackAxisX.barCount) : []), [trackAxisX, waveformData]);
|
|
32
47
|
useEffect(() => {
|
|
33
48
|
document.addEventListener('pointerup', handleDragStop);
|
|
34
49
|
return () => {
|
|
35
50
|
document.removeEventListener('pointerup', handleDragStop);
|
|
36
51
|
};
|
|
37
52
|
}, [handleDragStop]);
|
|
38
|
-
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!root || typeof ResizeObserver === 'undefined')
|
|
55
|
+
return;
|
|
56
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
57
|
+
getTrackAxisX(entry.contentRect.width);
|
|
58
|
+
});
|
|
59
|
+
observer.observe(root);
|
|
60
|
+
return () => {
|
|
61
|
+
observer.disconnect();
|
|
62
|
+
};
|
|
63
|
+
}, [getTrackAxisX, root]);
|
|
64
|
+
useLayoutEffect(() => {
|
|
65
|
+
if (!root)
|
|
66
|
+
return;
|
|
67
|
+
const { width: rootWidth } = root.getBoundingClientRect();
|
|
68
|
+
getTrackAxisX(rootWidth);
|
|
69
|
+
}, [getTrackAxisX, root]);
|
|
70
|
+
if (!waveformData.length || trackAxisX?.barCount === 0)
|
|
39
71
|
return null;
|
|
40
|
-
return (React.createElement("div", { className: 'str-chat__wave-progress-bar__track', "data-testid": 'wave-progress-bar-track', onClick: seek, onPointerDown: handleDragStart, onPointerMove: handleDrag, onPointerUp: handleDragStop, ref:
|
|
72
|
+
return (React.createElement("div", { className: 'str-chat__wave-progress-bar__track', "data-testid": 'wave-progress-bar-track', onClick: seek, onPointerDown: handleDragStart, onPointerMove: handleDrag, onPointerUp: handleDragStop, ref: setRoot, role: 'progressbar', style: {
|
|
73
|
+
'--str-chat__voice-recording-amplitude-bar-gap-width': trackAxisX?.gap + 'px',
|
|
74
|
+
} },
|
|
41
75
|
resampledWaveformData.map((amplitude, i) => (React.createElement("div", { className: clsx('str-chat__wave-progress-bar__amplitude-bar', {
|
|
42
76
|
['str-chat__wave-progress-bar__amplitude-bar--active']: progress > (i / resampledWaveformData.length) * 100,
|
|
43
77
|
}), "data-testid": 'amplitude-bar', key: `amplitude-${i}`, style: {
|
|
78
|
+
'--str-chat__voice-recording-amplitude-bar-width': trackAxisX?.barWidth + 'px',
|
|
44
79
|
'--str-chat__wave-progress-bar__amplitude-bar-height': amplitude
|
|
45
80
|
? amplitude * 100 + '%'
|
|
46
81
|
: '0%',
|
|
@@ -68,7 +68,11 @@ const ChannelInner = (props) => {
|
|
|
68
68
|
const [state, dispatch] = useReducer(channelReducer,
|
|
69
69
|
// channel.initialized === false if client.channel().query() was not called, e.g. ChannelList is not used
|
|
70
70
|
// => Channel will call channel.watch() in useLayoutEffect => state.loading is used to signal the watch() call state
|
|
71
|
-
{
|
|
71
|
+
{
|
|
72
|
+
...initialState,
|
|
73
|
+
hasMore: channel.state.messagePagination.hasPrev,
|
|
74
|
+
loading: !channel.initialized,
|
|
75
|
+
});
|
|
72
76
|
const isMounted = useIsMounted();
|
|
73
77
|
const originalTitle = useRef('');
|
|
74
78
|
const lastRead = useRef();
|
|
@@ -184,7 +188,6 @@ const ChannelInner = (props) => {
|
|
|
184
188
|
useLayoutEffect(() => {
|
|
185
189
|
let errored = false;
|
|
186
190
|
let done = false;
|
|
187
|
-
let channelInitializedExternally = true;
|
|
188
191
|
(async () => {
|
|
189
192
|
if (!channel.initialized && initializeOnMount) {
|
|
190
193
|
try {
|
|
@@ -210,7 +213,6 @@ const ChannelInner = (props) => {
|
|
|
210
213
|
await getChannel({ channel, client, members, options: channelQueryOptions });
|
|
211
214
|
const config = channel.getConfig();
|
|
212
215
|
setChannelConfig(config);
|
|
213
|
-
channelInitializedExternally = false;
|
|
214
216
|
}
|
|
215
217
|
catch (e) {
|
|
216
218
|
dispatch({ error: e, type: 'setError' });
|
|
@@ -222,8 +224,7 @@ const ChannelInner = (props) => {
|
|
|
222
224
|
if (!errored) {
|
|
223
225
|
dispatch({
|
|
224
226
|
channel,
|
|
225
|
-
hasMore:
|
|
226
|
-
hasMoreMessagesProbably(channel.state.messages.length, channelQueryOptions.messages.limit),
|
|
227
|
+
hasMore: channel.state.messagePagination.hasPrev,
|
|
227
228
|
type: 'initStateFromChannel',
|
|
228
229
|
});
|
|
229
230
|
if (client.user?.id && channel.state.read[client.user.id]) {
|
|
@@ -283,7 +284,7 @@ const ChannelInner = (props) => {
|
|
|
283
284
|
dispatch({ hasMore, messages, type: 'loadMoreFinished' });
|
|
284
285
|
}, 2000, { leading: true, trailing: true }), []);
|
|
285
286
|
const loadMore = async (limit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE) => {
|
|
286
|
-
if (!online.current || !window.navigator.onLine || !state.
|
|
287
|
+
if (!online.current || !window.navigator.onLine || !channel.state.messagePagination.hasPrev)
|
|
287
288
|
return 0;
|
|
288
289
|
// prevent duplicate loading events...
|
|
289
290
|
const oldestMessage = state?.messages?.[0];
|
|
@@ -305,12 +306,11 @@ const ChannelInner = (props) => {
|
|
|
305
306
|
dispatch({ loadingMore: false, type: 'setLoadingMore' });
|
|
306
307
|
return 0;
|
|
307
308
|
}
|
|
308
|
-
|
|
309
|
-
loadMoreFinished(hasMoreMessages, channel.state.messages);
|
|
309
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
310
310
|
return queryResponse.messages.length;
|
|
311
311
|
};
|
|
312
312
|
const loadMoreNewer = async (limit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE) => {
|
|
313
|
-
if (!online.current || !window.navigator.onLine || !state.
|
|
313
|
+
if (!online.current || !window.navigator.onLine || !channel.state.messagePagination.hasNext)
|
|
314
314
|
return 0;
|
|
315
315
|
const newestMessage = state?.messages?.[state?.messages?.length - 1];
|
|
316
316
|
if (state.loadingMore || state.loadingMoreNewer)
|
|
@@ -330,9 +330,8 @@ const ChannelInner = (props) => {
|
|
|
330
330
|
dispatch({ loadingMoreNewer: false, type: 'setLoadingMoreNewer' });
|
|
331
331
|
return 0;
|
|
332
332
|
}
|
|
333
|
-
const hasMoreNewerMessages = channel.state.messages !== channel.state.latestMessages;
|
|
334
333
|
dispatch({
|
|
335
|
-
hasMoreNewer:
|
|
334
|
+
hasMoreNewer: channel.state.messagePagination.hasNext,
|
|
336
335
|
messages: channel.state.messages,
|
|
337
336
|
type: 'loadMoreNewerFinished',
|
|
338
337
|
});
|
|
@@ -342,15 +341,9 @@ const ChannelInner = (props) => {
|
|
|
342
341
|
const jumpToMessage = useCallback(async (messageId, messageLimit = DEFAULT_JUMP_TO_PAGE_SIZE, highlightDuration = DEFAULT_HIGHLIGHT_DURATION) => {
|
|
343
342
|
dispatch({ loadingMore: true, type: 'setLoadingMore' });
|
|
344
343
|
await channel.state.loadMessageIntoState(messageId, undefined, messageLimit);
|
|
345
|
-
|
|
346
|
-
* if the message we are jumping to has less than half of the page size older messages,
|
|
347
|
-
* we have jumped to the beginning of the channel.
|
|
348
|
-
*/
|
|
349
|
-
const indexOfMessage = channel.state.messages.findIndex((message) => message.id === messageId);
|
|
350
|
-
const hasMoreMessages = indexOfMessage >= Math.floor(messageLimit / 2);
|
|
351
|
-
loadMoreFinished(hasMoreMessages, channel.state.messages);
|
|
344
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
352
345
|
dispatch({
|
|
353
|
-
hasMoreNewer: channel.state.
|
|
346
|
+
hasMoreNewer: channel.state.messagePagination.hasNext,
|
|
354
347
|
highlightedMessageId: messageId,
|
|
355
348
|
type: 'jumpToMessageFinished',
|
|
356
349
|
});
|
|
@@ -364,9 +357,7 @@ const ChannelInner = (props) => {
|
|
|
364
357
|
}, [channel, loadMoreFinished]);
|
|
365
358
|
const jumpToLatestMessage = useCallback(async () => {
|
|
366
359
|
await channel.state.loadMessageIntoState('latest');
|
|
367
|
-
|
|
368
|
-
const hasMoreOlder = channel.state.messages.length >= 25;
|
|
369
|
-
loadMoreFinished(hasMoreOlder, channel.state.messages);
|
|
360
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
370
361
|
dispatch({
|
|
371
362
|
type: 'jumpToLatestMessage',
|
|
372
363
|
});
|
|
@@ -377,7 +368,6 @@ const ChannelInner = (props) => {
|
|
|
377
368
|
let lastReadMessageId = channelUnreadUiState?.last_read_message_id;
|
|
378
369
|
let firstUnreadMessageId = channelUnreadUiState?.first_unread_message_id;
|
|
379
370
|
let isInCurrentMessageSet = false;
|
|
380
|
-
let hasMoreMessages = true;
|
|
381
371
|
if (firstUnreadMessageId) {
|
|
382
372
|
const result = findInMsgSetById(firstUnreadMessageId, channel.state.messages);
|
|
383
373
|
isInCurrentMessageSet = result.index !== -1;
|
|
@@ -409,27 +399,25 @@ const ChannelInner = (props) => {
|
|
|
409
399
|
}
|
|
410
400
|
catch (e) {
|
|
411
401
|
addNotification(t('Failed to jump to the first unread message'), 'error');
|
|
412
|
-
loadMoreFinished(
|
|
402
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
413
403
|
return;
|
|
414
404
|
}
|
|
415
405
|
const firstMessageWithCreationDate = messages.find((msg) => msg.created_at);
|
|
416
406
|
if (!firstMessageWithCreationDate) {
|
|
417
407
|
addNotification(t('Failed to jump to the first unread message'), 'error');
|
|
418
|
-
loadMoreFinished(
|
|
408
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
419
409
|
return;
|
|
420
410
|
}
|
|
421
411
|
const firstMessageTimestamp = new Date(firstMessageWithCreationDate.created_at).getTime();
|
|
422
412
|
if (lastReadTimestamp < firstMessageTimestamp) {
|
|
423
413
|
// whole channel is unread
|
|
424
414
|
firstUnreadMessageId = firstMessageWithCreationDate.id;
|
|
425
|
-
hasMoreMessages = false;
|
|
426
415
|
}
|
|
427
416
|
else {
|
|
428
417
|
const result = findInMsgSetByDate(channelUnreadUiState.last_read, messages);
|
|
429
418
|
lastReadMessageId = result.target?.id;
|
|
430
|
-
hasMoreMessages = result.index >= Math.floor(queryMessageLimit / 2);
|
|
431
419
|
}
|
|
432
|
-
loadMoreFinished(
|
|
420
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
433
421
|
}
|
|
434
422
|
}
|
|
435
423
|
if (!firstUnreadMessageId && !lastReadMessageId) {
|
|
@@ -446,14 +434,13 @@ const ChannelInner = (props) => {
|
|
|
446
434
|
* we have arrived to the oldest page of the channel
|
|
447
435
|
*/
|
|
448
436
|
const indexOfTarget = channel.state.messages.findIndex((message) => message.id === targetId);
|
|
449
|
-
|
|
450
|
-
loadMoreFinished(hasMoreMessages, channel.state.messages);
|
|
437
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
451
438
|
firstUnreadMessageId =
|
|
452
439
|
firstUnreadMessageId ?? channel.state.messages[indexOfTarget + 1]?.id;
|
|
453
440
|
}
|
|
454
441
|
catch (e) {
|
|
455
442
|
addNotification(t('Failed to jump to the first unread message'), 'error');
|
|
456
|
-
loadMoreFinished(
|
|
443
|
+
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
|
|
457
444
|
return;
|
|
458
445
|
}
|
|
459
446
|
}
|
|
@@ -468,7 +455,7 @@ const ChannelInner = (props) => {
|
|
|
468
455
|
last_read_message_id: lastReadMessageId,
|
|
469
456
|
});
|
|
470
457
|
dispatch({
|
|
471
|
-
hasMoreNewer: channel.state.
|
|
458
|
+
hasMoreNewer: channel.state.messagePagination.hasNext,
|
|
472
459
|
highlightedMessageId: firstUnreadMessageId,
|
|
473
460
|
type: 'jumpToMessageFinished',
|
|
474
461
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import { isDate, isDayOrMoment } from '../../../
|
|
2
|
+
import { isDate, isDayOrMoment } from '../../../i18n';
|
|
3
3
|
export const useCreateChannelStateContext = (value) => {
|
|
4
4
|
const { acceptedFiles, channel, channelCapabilitiesArray = [], channelConfig, debounceURLEnrichmentMs, dragAndDropWindow, enrichURLForPreview, giphyVersion, error, findURLFn, hasMore, hasMoreNewer, imageAttachmentSizeHandler, suppressAutoscroll, highlightedMessageId, loading, loadingMore, maxNumberOfFiles, members, messages = [], multipleUploads, mutes, notifications, onLinkPreviewDismissed, pinnedMessages, quotedMessage, read = {}, shouldGenerateVideoThumbnail, skipMessageDataMemoization, thread, threadHasMore, threadLoadingMore, threadMessages = [], channelUnreadUiState, videoAttachmentSizeHandler, watcherCount, watcher_count, watchers, } = value;
|
|
5
5
|
const channelId = channel.cid;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from 'react';
|
|
2
2
|
import { CustomClasses } from '../../context/ChatContext';
|
|
3
|
-
import { SupportedTranslations } from '../../context/TranslationContext';
|
|
4
3
|
import type { StreamChat } from 'stream-chat';
|
|
4
|
+
import type { SupportedTranslations } from '../../i18n/types';
|
|
5
5
|
import type { Streami18n } from '../../i18n/Streami18n';
|
|
6
6
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
7
7
|
export type ChatProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Streami18n } from '../../../i18n';
|
|
1
|
+
import { TranslationContextValue } from '../../../context/TranslationContext';
|
|
2
|
+
import { Streami18n, SupportedTranslations } from '../../../i18n';
|
|
3
3
|
import type { AppSettingsAPIResponse, Channel, Mute, StreamChat } from 'stream-chat';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
5
5
|
export type UseChatParams<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { defaultDateTimeParser, isLanguageSupported, } from '../../../
|
|
3
|
-
import { Streami18n } from '../../../i18n';
|
|
2
|
+
import { defaultDateTimeParser, isLanguageSupported, Streami18n, } from '../../../i18n';
|
|
4
3
|
import { version } from '../../../version';
|
|
5
4
|
export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialNavOpen, }) => {
|
|
6
5
|
const [translators, setTranslators] = useState({
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StreamChat } from 'stream-chat';
|
|
2
|
+
import type { DefaultGenerics, ExtendableGenerics, OwnUserResponse, StreamChatOptions, TokenOrProvider, UserResponse } from 'stream-chat';
|
|
2
3
|
/**
|
|
3
4
|
* React hook to create, connect and return `StreamChat` client.
|
|
4
5
|
*/
|
|
5
|
-
export declare const useCreateChatClient: <SCG extends ExtendableGenerics = DefaultGenerics>({ apiKey, tokenOrProvider, userData, }: {
|
|
6
|
+
export declare const useCreateChatClient: <SCG extends ExtendableGenerics = DefaultGenerics>({ apiKey, options, tokenOrProvider, userData, }: {
|
|
6
7
|
apiKey: string;
|
|
7
8
|
tokenOrProvider: TokenOrProvider;
|
|
8
9
|
userData: OwnUserResponse<SCG> | UserResponse<SCG>;
|
|
10
|
+
options?: StreamChatOptions;
|
|
9
11
|
}) => StreamChat<SCG> | null;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { StreamChat
|
|
2
|
+
import { StreamChat } from 'stream-chat';
|
|
3
3
|
/**
|
|
4
4
|
* React hook to create, connect and return `StreamChat` client.
|
|
5
5
|
*/
|
|
6
|
-
export const useCreateChatClient = ({ apiKey, tokenOrProvider, userData, }) => {
|
|
6
|
+
export const useCreateChatClient = ({ apiKey, options, tokenOrProvider, userData, }) => {
|
|
7
7
|
const [chatClient, setChatClient] = useState(null);
|
|
8
8
|
const [cachedUserData, setCachedUserData] = useState(userData);
|
|
9
9
|
if (userData.id !== cachedUserData.id) {
|
|
10
10
|
setCachedUserData(userData);
|
|
11
11
|
}
|
|
12
|
+
const [cachedOptions] = useState(options);
|
|
12
13
|
useEffect(() => {
|
|
13
|
-
const client = new StreamChat(apiKey);
|
|
14
|
+
const client = new StreamChat(apiKey, undefined, cachedOptions);
|
|
14
15
|
let didUserConnectInterrupt = false;
|
|
15
16
|
const connectionPromise = client.connectUser(cachedUserData, tokenOrProvider).then(() => {
|
|
16
17
|
if (!didUserConnectInterrupt)
|
|
@@ -25,6 +26,6 @@ export const useCreateChatClient = ({ apiKey, tokenOrProvider, userData, }) => {
|
|
|
25
26
|
console.log(`Connection for user "${cachedUserData.id}" has been closed`);
|
|
26
27
|
});
|
|
27
28
|
};
|
|
28
|
-
}, [apiKey, cachedUserData, tokenOrProvider]);
|
|
29
|
+
}, [apiKey, cachedUserData, cachedOptions, tokenOrProvider]);
|
|
29
30
|
return chatClient;
|
|
30
31
|
};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { useTranslationContext } from '../../context/TranslationContext';
|
|
3
3
|
import { getDateString } from '../../i18n/utils';
|
|
4
4
|
const UnMemoizedDateSeparator = (props) => {
|
|
5
|
-
const { calendar
|
|
5
|
+
const { calendar, date: messageCreatedAt, formatDate, position = 'right', unread, ...restTimestampFormatterOptions } = props;
|
|
6
6
|
const { t, tDateTimeParser } = useTranslationContext('DateSeparator');
|
|
7
7
|
const formattedDate = getDateString({
|
|
8
8
|
calendar,
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { AvatarProps } from '../Avatar';
|
|
3
3
|
import type { StreamMessage } from '../../context/ChannelStateContext';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
5
|
-
import { TimestampFormatterOptions } from '../../i18n/
|
|
5
|
+
import type { TimestampFormatterOptions } from '../../i18n/types';
|
|
6
6
|
export type EventComponentProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = TimestampFormatterOptions & {
|
|
7
7
|
/** Message object */
|
|
8
8
|
message: StreamMessage<StreamChatGenerics>;
|
|
@@ -6,7 +6,7 @@ import { getDateString } from '../../i18n/utils';
|
|
|
6
6
|
* Component to display system and channel event messages
|
|
7
7
|
*/
|
|
8
8
|
const UnMemoizedEventComponent = (props) => {
|
|
9
|
-
const { calendar, calendarFormats, format
|
|
9
|
+
const { calendar, calendarFormats, format, Avatar = DefaultAvatar, message } = props;
|
|
10
10
|
const { t, tDateTimeParser } = useTranslationContext('EventComponent');
|
|
11
11
|
const { created_at = '', event, text, type } = message;
|
|
12
12
|
const getDateOptions = { messageCreatedAt: created_at.toString(), tDateTimeParser };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { deprecationAndReplacementWarning } from '../../utils/deprecationWarning';
|
|
3
3
|
import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD } from '../../constants/limits';
|
|
4
4
|
/**
|
|
@@ -17,6 +17,8 @@ export const InfiniteScroll = (props) => {
|
|
|
17
17
|
const hasNextPageFlag = hasNextPage || hasMoreNewer;
|
|
18
18
|
const hasPreviousPageFlag = hasPreviousPage || hasMore;
|
|
19
19
|
const scrollComponent = useRef();
|
|
20
|
+
const previousOffset = useRef();
|
|
21
|
+
const previousReverseOffset = useRef();
|
|
20
22
|
const scrollListenerRef = useRef();
|
|
21
23
|
scrollListenerRef.current = () => {
|
|
22
24
|
const element = scrollComponent.current;
|
|
@@ -32,7 +34,11 @@ export const InfiniteScroll = (props) => {
|
|
|
32
34
|
}
|
|
33
35
|
if (isLoading)
|
|
34
36
|
return;
|
|
35
|
-
|
|
37
|
+
if (previousOffset.current === offset && previousReverseOffset.current === reverseOffset)
|
|
38
|
+
return;
|
|
39
|
+
previousOffset.current = offset;
|
|
40
|
+
previousReverseOffset.current = reverseOffset;
|
|
41
|
+
// FIXME: this triggers loadMore call when a user types messages in thread and the scroll container expands
|
|
36
42
|
if (reverseOffset < Number(threshold) &&
|
|
37
43
|
typeof loadPreviousPageFn === 'function' &&
|
|
38
44
|
hasPreviousPageFlag) {
|
|
@@ -51,7 +57,7 @@ export const InfiniteScroll = (props) => {
|
|
|
51
57
|
], 'InfiniteScroll');
|
|
52
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
59
|
}, []);
|
|
54
|
-
|
|
60
|
+
useEffect(() => {
|
|
55
61
|
const scrollElement = scrollComponent.current?.parentNode;
|
|
56
62
|
if (!scrollElement)
|
|
57
63
|
return;
|
|
@@ -29,6 +29,9 @@ export const AudioRecordingInProgress = () => {
|
|
|
29
29
|
if (!recorder?.mediaRecorder)
|
|
30
30
|
return;
|
|
31
31
|
const { mediaRecorder } = recorder;
|
|
32
|
+
if (mediaRecorder.state === 'recording') {
|
|
33
|
+
startCounter();
|
|
34
|
+
}
|
|
32
35
|
mediaRecorder.addEventListener('start', startCounter);
|
|
33
36
|
mediaRecorder.addEventListener('resume', startCounter);
|
|
34
37
|
mediaRecorder.addEventListener('stop', stopCounter);
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import { AmplitudeRecorder, AmplitudeRecorderConfig } from './AmplitudeRecorder';
|
|
2
2
|
import { BrowserPermission } from './BrowserPermission';
|
|
3
3
|
import { BehaviorSubject, Subject } from '../observable';
|
|
4
|
+
import { TranscoderConfig } from '../transcode';
|
|
4
5
|
import { RecordedMediaType } from '../../ReactFileUtilities';
|
|
5
6
|
import { TranslationContextValue } from '../../../context';
|
|
6
7
|
import type { LocalVoiceRecordingAttachment } from '../../MessageInput';
|
|
7
8
|
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
8
|
-
export declare const POSSIBLE_TRANSCODING_MIME_TYPES: readonly ["audio/wav", "audio/mp3"];
|
|
9
9
|
export declare const DEFAULT_MEDIA_RECORDER_CONFIG: MediaRecorderConfig;
|
|
10
10
|
export declare const DEFAULT_AUDIO_TRANSCODER_CONFIG: TranscoderConfig;
|
|
11
|
-
type SupportedTranscodeMimeTypes = typeof POSSIBLE_TRANSCODING_MIME_TYPES[number];
|
|
12
|
-
export type TranscoderConfig = {
|
|
13
|
-
sampleRate: number;
|
|
14
|
-
targetMimeType: SupportedTranscodeMimeTypes;
|
|
15
|
-
};
|
|
16
11
|
type MediaRecorderConfig = Omit<MediaRecorderOptions, 'mimeType'> & Required<Pick<MediaRecorderOptions, 'mimeType'>>;
|
|
17
12
|
export type AudioRecorderConfig = {
|
|
18
13
|
amplitudeRecorderConfig: AmplitudeRecorderConfig;
|
|
19
14
|
mediaRecorderConfig: MediaRecorderOptions;
|
|
20
15
|
transcoderConfig: TranscoderConfig;
|
|
21
16
|
};
|
|
17
|
+
type PartialValues<T> = {
|
|
18
|
+
[P in keyof T]?: Partial<T[P]>;
|
|
19
|
+
};
|
|
20
|
+
export type CustomAudioRecordingConfig = PartialValues<AudioRecorderConfig>;
|
|
22
21
|
export type AudioRecorderOptions = {
|
|
23
|
-
config?:
|
|
22
|
+
config?: CustomAudioRecordingConfig;
|
|
24
23
|
generateRecordingTitle?: (mimeType: string) => string;
|
|
25
24
|
t?: TranslationContextValue['t'];
|
|
26
25
|
};
|
|
@@ -15,7 +15,6 @@ const RECORDED_MIME_TYPE_BY_BROWSER = {
|
|
|
15
15
|
safari: 'audio/mp4;codecs=mp4a.40.2',
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
|
-
export const POSSIBLE_TRANSCODING_MIME_TYPES = ['audio/wav', 'audio/mp3'];
|
|
19
18
|
export const DEFAULT_MEDIA_RECORDER_CONFIG = {
|
|
20
19
|
mimeType: isSafari()
|
|
21
20
|
? RECORDED_MIME_TYPE_BY_BROWSER.audio.safari
|
|
@@ -23,7 +22,6 @@ export const DEFAULT_MEDIA_RECORDER_CONFIG = {
|
|
|
23
22
|
};
|
|
24
23
|
export const DEFAULT_AUDIO_TRANSCODER_CONFIG = {
|
|
25
24
|
sampleRate: 16000,
|
|
26
|
-
targetMimeType: 'audio/mp3',
|
|
27
25
|
};
|
|
28
26
|
const disposeOfMediaStream = (stream) => {
|
|
29
27
|
if (!stream?.active)
|
|
@@ -245,9 +243,6 @@ export class MediaRecorderController {
|
|
|
245
243
|
this.amplitudeRecorderConfig = mergeDeepUndefined({ ...config?.amplitudeRecorderConfig }, DEFAULT_AMPLITUDE_RECORDER_CONFIG);
|
|
246
244
|
this.mediaRecorderConfig = mergeDeepUndefined({ ...config?.mediaRecorderConfig }, DEFAULT_MEDIA_RECORDER_CONFIG);
|
|
247
245
|
this.transcoderConfig = mergeDeepUndefined({ ...config?.transcoderConfig }, DEFAULT_AUDIO_TRANSCODER_CONFIG);
|
|
248
|
-
if (!POSSIBLE_TRANSCODING_MIME_TYPES.includes(this.transcoderConfig.targetMimeType)) {
|
|
249
|
-
this.transcoderConfig.targetMimeType = DEFAULT_AUDIO_TRANSCODER_CONFIG.targetMimeType;
|
|
250
|
-
}
|
|
251
246
|
const mediaType = getRecordedMediaTypeFromMimeType(this.mediaRecorderConfig.mimeType);
|
|
252
247
|
if (!mediaType) {
|
|
253
248
|
throw new Error(`Unsupported media type (supported audio or video only). Provided mimeType: ${this.mediaRecorderConfig.mimeType}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { RecordingController } from './useMediaRecorder';
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { MessageInputContextValue } from '../../../context';
|
|
2
|
-
import {
|
|
2
|
+
import { CustomAudioRecordingConfig, MediaRecorderController, MediaRecordingState } from '../classes';
|
|
3
3
|
import type { LocalVoiceRecordingAttachment } from '../../MessageInput';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
5
|
-
export type CustomAudioRecordingConfig = Partial<AudioRecorderConfig>;
|
|
6
5
|
export type RecordingController<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
7
6
|
completeRecording: () => void;
|
|
8
7
|
permissionState?: PermissionState;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useTranslationContext } from '../../../context';
|
|
3
|
-
import { MediaRecorderController } from '../classes';
|
|
3
|
+
import { MediaRecorderController, } from '../classes';
|
|
4
4
|
export const useMediaRecorder = ({ asyncMessagesMultiSendEnabled, enabled, generateRecordingTitle, handleSubmit, recordingConfig, uploadAttachment, }) => {
|
|
5
5
|
const { t } = useTranslationContext('useMediaRecorder');
|
|
6
6
|
const [recording, setRecording] = useState();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
type
|
|
2
|
-
blob: Blob;
|
|
1
|
+
export type TranscoderConfig = {
|
|
3
2
|
sampleRate: number;
|
|
4
|
-
|
|
3
|
+
encoder?: (file: File, sampleRate: number) => Promise<Blob>;
|
|
4
|
+
};
|
|
5
|
+
export type TranscodeParams = TranscoderConfig & {
|
|
6
|
+
blob: Blob;
|
|
5
7
|
};
|
|
6
|
-
export declare const transcode: ({ blob, sampleRate,
|
|
7
|
-
export {};
|
|
8
|
+
export declare const transcode: ({ blob, encoder, sampleRate, }: TranscodeParams) => Promise<Blob>;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { encodeToWaw } from './wav';
|
|
2
|
-
import { encodeToMp3 } from './mp3';
|
|
3
2
|
import { createFileFromBlobs, getExtensionFromMimeType } from '../../ReactFileUtilities';
|
|
4
|
-
export const transcode = ({ blob, sampleRate,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
if (targetMimeType.match('audio/wav')) {
|
|
11
|
-
return encodeToWaw(file, sampleRate);
|
|
12
|
-
}
|
|
13
|
-
if (targetMimeType.match('audio/mp3')) {
|
|
14
|
-
return encodeToMp3(file, sampleRate);
|
|
15
|
-
}
|
|
16
|
-
return Promise.resolve(blob);
|
|
17
|
-
};
|
|
3
|
+
export const transcode = ({ blob, encoder = encodeToWaw, sampleRate, }) => encoder(createFileFromBlobs({
|
|
4
|
+
blobsArray: [blob],
|
|
5
|
+
fileName: `audio_recording_${new Date().toISOString()}.${getExtensionFromMimeType(blob.type)}`,
|
|
6
|
+
mimeType: blob.type,
|
|
7
|
+
}), sampleRate);
|
|
@@ -92,7 +92,7 @@ const MessageSimpleWithContext = (props) => {
|
|
|
92
92
|
showMetadata && (React.createElement("div", { className: 'str-chat__message-metadata' },
|
|
93
93
|
React.createElement(MessageStatus, null),
|
|
94
94
|
!isMyMessage() && !!message.user && (React.createElement("span", { className: 'str-chat__message-simple-name' }, message.user.name || message.user.id)),
|
|
95
|
-
React.createElement(MessageTimestamp, {
|
|
95
|
+
React.createElement(MessageTimestamp, { customClass: 'str-chat__message-simple-timestamp' }),
|
|
96
96
|
isEdited && (React.createElement("span", { className: 'str-chat__mesage-simple-edited' }, t('Edited'))),
|
|
97
97
|
isEdited && React.createElement(MessageEditedTimestamp, { calendar: true, open: isEditedTimestampOpen }))))));
|
|
98
98
|
};
|
|
@@ -26,9 +26,10 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
26
26
|
const sending = message.status === 'sending';
|
|
27
27
|
const delivered = message.status === 'received' && message.id === lastReceivedId && !threadList;
|
|
28
28
|
const deliveredAndRead = !!(readBy?.length && !threadList && !justReadByMe);
|
|
29
|
-
const
|
|
29
|
+
const readersWithoutOwnUser = deliveredAndRead
|
|
30
30
|
? readBy.filter((item) => item.id !== client.user?.id)
|
|
31
31
|
: [];
|
|
32
|
+
const [lastReadUser] = readersWithoutOwnUser;
|
|
32
33
|
return (React.createElement("span", { className: rootClassName, "data-testid": clsx({
|
|
33
34
|
'message-status-read-by': deliveredAndRead,
|
|
34
35
|
'message-status-received': delivered && !deliveredAndRead,
|
|
@@ -47,6 +48,6 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
47
48
|
(MessageReadStatus ? (React.createElement(MessageReadStatus, null)) : (React.createElement(React.Fragment, null,
|
|
48
49
|
React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, getReadByTooltipText(readBy, t, client, tooltipUserNameMapper)),
|
|
49
50
|
React.createElement(Avatar, { className: 'str-chat__avatar--message-status', image: lastReadUser.image, name: lastReadUser.name || lastReadUser.id, user: lastReadUser }),
|
|
50
|
-
|
|
51
|
+
readersWithoutOwnUser.length > 1 && (React.createElement("span", { className: `str-chat__message-${messageType}-status-number`, "data-testid": 'message-status-read-by-many' }, readersWithoutOwnUser.length)))))));
|
|
51
52
|
};
|
|
52
53
|
export const MessageStatus = React.memo(UnMemoizedMessageStatus);
|