stream-chat-react-native-core 9.0.0-beta.6 → 9.0.0-beta.7
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/lib/commonjs/components/Attachment/Audio/AudioAttachment.js +0 -1
- package/lib/commonjs/components/Attachment/Audio/AudioAttachment.js.map +1 -1
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +16 -9
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js +6 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +7 -29
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +0 -1
- package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js +3 -14
- package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/MessageUserReactions.js +1 -0
- package/lib/commonjs/components/MessageMenu/MessageUserReactions.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js +52 -45
- package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollOption.js +24 -14
- package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js +4 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js +46 -19
- package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
- package/lib/commonjs/components/Poll/hooks/usePollState.js +6 -3
- package/lib/commonjs/components/Poll/hooks/usePollState.js.map +1 -1
- package/lib/commonjs/components/ThreadList/ThreadListItem.js +46 -15
- package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
- package/lib/commonjs/components/ThreadList/ThreadListItemMessagePreview.js +23 -5
- package/lib/commonjs/components/ThreadList/ThreadListItemMessagePreview.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +4 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/Audio/AudioAttachment.js +0 -1
- package/lib/module/components/Attachment/Audio/AudioAttachment.js.map +1 -1
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +16 -9
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageFooter.js +6 -1
- package/lib/module/components/Message/MessageItemView/MessageFooter.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageStatus.js +7 -29
- package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +0 -1
- package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
- package/lib/module/components/MessageInput/hooks/useAudioRecorder.js +3 -14
- package/lib/module/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
- package/lib/module/components/MessageMenu/MessageUserReactions.js +1 -0
- package/lib/module/components/MessageMenu/MessageUserReactions.js.map +1 -1
- package/lib/module/components/Poll/components/PollAnswersList.js +52 -45
- package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/module/components/Poll/components/PollOption.js +24 -14
- package/lib/module/components/Poll/components/PollOption.js.map +1 -1
- package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js +4 -1
- package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
- package/lib/module/components/Poll/components/PollResults/PollResultItem.js +46 -19
- package/lib/module/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
- package/lib/module/components/Poll/hooks/usePollState.js +6 -3
- package/lib/module/components/Poll/hooks/usePollState.js.map +1 -1
- package/lib/module/components/ThreadList/ThreadListItem.js +46 -15
- package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
- package/lib/module/components/ThreadList/ThreadListItemMessagePreview.js +23 -5
- package/lib/module/components/ThreadList/ThreadListItemMessagePreview.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +4 -1
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Attachment/Audio/AudioAttachment.d.ts.map +1 -1
- package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts +1 -1
- package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageFooter.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts +1 -5
- package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/hooks/useAudioRecorder.d.ts +0 -2
- package/lib/typescript/components/MessageInput/hooks/useAudioRecorder.d.ts.map +1 -1
- package/lib/typescript/components/MessageMenu/MessageUserReactions.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollAnswersList.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollOption.d.ts +2 -1
- package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollResults/PollResultItem.d.ts.map +1 -1
- package/lib/typescript/components/Poll/hooks/usePollState.d.ts +1 -0
- package/lib/typescript/components/Poll/hooks/usePollState.d.ts.map +1 -1
- package/lib/typescript/components/ThreadList/ThreadListItem.d.ts.map +1 -1
- package/lib/typescript/components/ThreadList/ThreadListItemMessagePreview.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/ThemeContext.d.ts +3 -0
- package/lib/typescript/contexts/themeContext/ThemeContext.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +3 -0
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
- package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts +2 -1
- package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Attachment/Audio/AudioAttachment.tsx +0 -1
- package/src/components/ChannelList/__tests__/ChannelList.test.js +43 -0
- package/src/components/ChannelList/hooks/usePaginatedChannels.ts +8 -5
- package/src/components/Message/MessageItemView/MessageFooter.tsx +9 -3
- package/src/components/Message/MessageItemView/MessageStatus.tsx +4 -37
- package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx +0 -1
- package/src/components/MessageInput/hooks/useAudioRecorder.tsx +4 -14
- package/src/components/MessageMenu/MessageUserReactions.tsx +1 -0
- package/src/components/Poll/components/PollAnswersList.tsx +45 -32
- package/src/components/Poll/components/PollOption.tsx +24 -15
- package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx +4 -1
- package/src/components/Poll/components/PollResults/PollResultItem.tsx +36 -12
- package/src/components/Poll/hooks/usePollState.ts +4 -0
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +52 -0
- package/src/components/ThreadList/ThreadListItem.tsx +56 -10
- package/src/components/ThreadList/ThreadListItemMessagePreview.tsx +18 -3
- package/src/contexts/themeContext/utils/theme.ts +6 -0
- package/src/contexts/threadsContext/ThreadListItemContext.tsx +2 -1
- package/src/version.json +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-react-native-core",
|
|
3
3
|
"description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "9.0.0-beta.
|
|
4
|
+
"version": "9.0.0-beta.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -168,7 +168,6 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
|
|
|
168
168
|
const dragEnd = async (currentProgress: number) => {
|
|
169
169
|
const positionInSeconds = (currentProgress * duration) / ONE_SECOND_IN_MILLISECONDS;
|
|
170
170
|
await audioPlayer.seek(positionInSeconds);
|
|
171
|
-
audioPlayer.play();
|
|
172
171
|
};
|
|
173
172
|
|
|
174
173
|
const onSpeedChangeHandler = async () => {
|
|
@@ -20,6 +20,7 @@ import dispatchChannelDeletedEvent from '../../../mock-builders/event/channelDel
|
|
|
20
20
|
import dispatchChannelHiddenEvent from '../../../mock-builders/event/channelHidden';
|
|
21
21
|
import dispatchChannelTruncatedEvent from '../../../mock-builders/event/channelTruncated';
|
|
22
22
|
import dispatchChannelUpdatedEvent from '../../../mock-builders/event/channelUpdated';
|
|
23
|
+
import dispatchConnectionChangedEvent from '../../../mock-builders/event/connectionChanged';
|
|
23
24
|
import dispatchConnectionRecoveredEvent from '../../../mock-builders/event/connectionRecovered';
|
|
24
25
|
import dispatchMessageNewEvent from '../../../mock-builders/event/messageNew';
|
|
25
26
|
import dispatchNotificationAddedToChannelEvent from '../../../mock-builders/event/notificationAddedToChannel';
|
|
@@ -75,6 +76,11 @@ const ChannelListSwipeActionsProbe = () => {
|
|
|
75
76
|
return <Text testID='swipe-actions-enabled'>{`${swipeActionsEnabled}`}</Text>;
|
|
76
77
|
};
|
|
77
78
|
|
|
79
|
+
const ChannelListRefreshingProbe = () => {
|
|
80
|
+
const { refreshing } = useChannelsContext();
|
|
81
|
+
return <Text testID='refreshing'>{`${refreshing}`}</Text>;
|
|
82
|
+
};
|
|
83
|
+
|
|
78
84
|
const ChannelPreviewContent = ({ unread }) => <Text testID='preview-unread'>{`${unread}`}</Text>;
|
|
79
85
|
|
|
80
86
|
const ChannelListWithChannelPreview = () => {
|
|
@@ -805,6 +811,43 @@ describe('ChannelList', () => {
|
|
|
805
811
|
});
|
|
806
812
|
});
|
|
807
813
|
|
|
814
|
+
describe('connection.changed', () => {
|
|
815
|
+
it('should keep background reconnection refreshes debounced and out of pull-to-refresh UI', async () => {
|
|
816
|
+
useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
|
|
817
|
+
const deferredPromise = new DeferredPromise();
|
|
818
|
+
const dateNowSpy = jest.spyOn(Date, 'now');
|
|
819
|
+
dateNowSpy.mockReturnValueOnce(0);
|
|
820
|
+
dateNowSpy.mockReturnValue(6000);
|
|
821
|
+
|
|
822
|
+
render(
|
|
823
|
+
<Chat client={chatClient}>
|
|
824
|
+
<ChannelList {...props} List={ChannelListRefreshingProbe} />
|
|
825
|
+
</Chat>,
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
await waitFor(() => {
|
|
829
|
+
expect(screen.getByTestId('refreshing').children[0]).toBe('false');
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
chatClient.queryChannels = jest.fn(() => deferredPromise.promise);
|
|
833
|
+
|
|
834
|
+
act(() => dispatchConnectionChangedEvent(chatClient, false));
|
|
835
|
+
act(() => dispatchConnectionChangedEvent(chatClient, true));
|
|
836
|
+
|
|
837
|
+
await waitFor(() => {
|
|
838
|
+
expect(chatClient.queryChannels).toHaveBeenCalled();
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
act(() => dispatchConnectionChangedEvent(chatClient, true));
|
|
842
|
+
|
|
843
|
+
expect(chatClient.queryChannels).toHaveBeenCalledTimes(1);
|
|
844
|
+
expect(screen.getByTestId('refreshing').children[0]).toBe('false');
|
|
845
|
+
|
|
846
|
+
deferredPromise.resolve([testChannel1]);
|
|
847
|
+
dateNowSpy.mockRestore();
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
|
|
808
851
|
describe('channel.truncated', () => {
|
|
809
852
|
it('should call the `onChannelTruncated` function prop, if provided', async () => {
|
|
810
853
|
useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
|
|
@@ -24,7 +24,7 @@ type Parameters = {
|
|
|
24
24
|
|
|
25
25
|
const RETRY_INTERVAL_IN_MS = 5000;
|
|
26
26
|
|
|
27
|
-
type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels';
|
|
27
|
+
type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels' | 'backgroundRefresh';
|
|
28
28
|
|
|
29
29
|
export type QueryChannels = (queryType?: QueryType, retryCount?: number) => Promise<void>;
|
|
30
30
|
|
|
@@ -68,6 +68,7 @@ export const usePaginatedChannels = ({
|
|
|
68
68
|
const hasUpdatedData =
|
|
69
69
|
queryType === 'loadChannels' ||
|
|
70
70
|
queryType === 'refresh' ||
|
|
71
|
+
queryType === 'backgroundRefresh' ||
|
|
71
72
|
[
|
|
72
73
|
JSON.stringify(filtersRef.current) !== JSON.stringify(filters),
|
|
73
74
|
JSON.stringify(sortRef.current) !== JSON.stringify(sort),
|
|
@@ -129,7 +130,7 @@ export const usePaginatedChannels = ({
|
|
|
129
130
|
setActiveQueryType(null);
|
|
130
131
|
};
|
|
131
132
|
|
|
132
|
-
const refreshList = async () => {
|
|
133
|
+
const refreshList = async ({ isBackground = false }: { isBackground?: boolean } = {}) => {
|
|
133
134
|
const now = Date.now();
|
|
134
135
|
// Only allow pull-to-refresh 5 seconds after last successful refresh.
|
|
135
136
|
if (now - lastRefresh.current < RETRY_INTERVAL_IN_MS && error === undefined) {
|
|
@@ -137,7 +138,7 @@ export const usePaginatedChannels = ({
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
lastRefresh.current = Date.now();
|
|
140
|
-
await queryChannels('refresh');
|
|
141
|
+
await queryChannels(isBackground ? 'backgroundRefresh' : 'refresh');
|
|
141
142
|
};
|
|
142
143
|
|
|
143
144
|
const reloadList = async () => {
|
|
@@ -167,7 +168,9 @@ export const usePaginatedChannels = ({
|
|
|
167
168
|
'connection.changed',
|
|
168
169
|
async (event) => {
|
|
169
170
|
if (event.online) {
|
|
170
|
-
|
|
171
|
+
// Reconnection refreshes should stay silent, but still share the same debounce
|
|
172
|
+
// path as pull-to-refresh.
|
|
173
|
+
await refreshList({ isBackground: true });
|
|
171
174
|
}
|
|
172
175
|
},
|
|
173
176
|
);
|
|
@@ -195,7 +198,7 @@ export const usePaginatedChannels = ({
|
|
|
195
198
|
loadingNextPage: pagination?.isLoadingNext,
|
|
196
199
|
loadNextPage: channelManager.loadNext,
|
|
197
200
|
refreshing: activeQueryType === 'refresh',
|
|
198
|
-
refreshList,
|
|
201
|
+
refreshList: () => refreshList(),
|
|
199
202
|
reloadList,
|
|
200
203
|
staticChannelsActive,
|
|
201
204
|
};
|
|
@@ -37,7 +37,7 @@ type MessageFooterPropsWithContext = Pick<
|
|
|
37
37
|
| 'lastGroupMessage'
|
|
38
38
|
| 'isMessageAIGenerated'
|
|
39
39
|
> &
|
|
40
|
-
Pick<MessagesContextValue, 'MessageStatus'> &
|
|
40
|
+
Pick<MessagesContextValue, 'MessageStatus' | 'MessageTimestamp'> &
|
|
41
41
|
MessageFooterComponentProps;
|
|
42
42
|
|
|
43
43
|
const MessageFooterWithContext = (props: MessageFooterPropsWithContext) => {
|
|
@@ -50,6 +50,7 @@ const MessageFooterWithContext = (props: MessageFooterPropsWithContext) => {
|
|
|
50
50
|
members,
|
|
51
51
|
message,
|
|
52
52
|
MessageStatus,
|
|
53
|
+
MessageTimestamp,
|
|
53
54
|
showMessageStatus,
|
|
54
55
|
} = props;
|
|
55
56
|
const styles = useStyles();
|
|
@@ -77,9 +78,12 @@ const MessageFooterWithContext = (props: MessageFooterPropsWithContext) => {
|
|
|
77
78
|
return (
|
|
78
79
|
<View style={[styles.container, container]} testID='message-status-time'>
|
|
79
80
|
{Object.keys(members).length > 2 && alignment === 'left' && message.user?.name ? (
|
|
80
|
-
<Text style={[styles.name, name]}>
|
|
81
|
+
<Text numberOfLines={1} ellipsizeMode='tail' style={[styles.name, name]}>
|
|
82
|
+
{message.user.name}
|
|
83
|
+
</Text>
|
|
81
84
|
) : null}
|
|
82
|
-
{showMessageStatus
|
|
85
|
+
{showMessageStatus ? <MessageStatus /> : null}
|
|
86
|
+
<MessageTimestamp formattedDate={formattedDate} timestamp={date} />
|
|
83
87
|
{isEdited ? <Text style={[styles.editedText, editedText]}>{t('Edited')}</Text> : null}
|
|
84
88
|
</View>
|
|
85
89
|
);
|
|
@@ -201,6 +205,7 @@ const useStyles = () => {
|
|
|
201
205
|
return useMemo(() => {
|
|
202
206
|
return StyleSheet.create({
|
|
203
207
|
container: {
|
|
208
|
+
maxWidth: '100%',
|
|
204
209
|
alignItems: 'center',
|
|
205
210
|
flexDirection: 'row',
|
|
206
211
|
justifyContent: 'center',
|
|
@@ -208,6 +213,7 @@ const useStyles = () => {
|
|
|
208
213
|
gap: primitives.spacingXs,
|
|
209
214
|
},
|
|
210
215
|
name: {
|
|
216
|
+
flexShrink: 1,
|
|
211
217
|
color: shouldUseOverlayStyles ? semantics.textOnAccent : semantics.chatTextUsername,
|
|
212
218
|
fontSize: primitives.typographyFontSizeXs,
|
|
213
219
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
@@ -6,10 +6,6 @@ import {
|
|
|
6
6
|
MessageContextValue,
|
|
7
7
|
useMessageContext,
|
|
8
8
|
} from '../../../contexts/messageContext/MessageContext';
|
|
9
|
-
import {
|
|
10
|
-
MessagesContextValue,
|
|
11
|
-
useMessagesContext,
|
|
12
|
-
} from '../../../contexts/messagesContext/MessagesContext';
|
|
13
9
|
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
|
|
14
10
|
import { Check } from '../../../icons/Check';
|
|
15
11
|
import { CheckAll } from '../../../icons/CheckAll';
|
|
@@ -21,14 +17,10 @@ import { useShouldUseOverlayStyles } from '../hooks/useShouldUseOverlayStyles';
|
|
|
21
17
|
export type MessageStatusPropsWithContext = Pick<
|
|
22
18
|
MessageContextValue,
|
|
23
19
|
'deliveredToCount' | 'message' | 'readBy'
|
|
24
|
-
|
|
25
|
-
Pick<MessagesContextValue, 'MessageTimestamp'> & {
|
|
26
|
-
formattedDate?: string | Date;
|
|
27
|
-
timestamp?: string | Date;
|
|
28
|
-
};
|
|
20
|
+
>;
|
|
29
21
|
|
|
30
22
|
const MessageStatusWithContext = (props: MessageStatusPropsWithContext) => {
|
|
31
|
-
const { deliveredToCount,
|
|
23
|
+
const { deliveredToCount, message, readBy } = props;
|
|
32
24
|
|
|
33
25
|
const styles = useStyles();
|
|
34
26
|
|
|
@@ -92,7 +84,6 @@ const MessageStatusWithContext = (props: MessageStatusPropsWithContext) => {
|
|
|
92
84
|
{...checkIcon}
|
|
93
85
|
/>
|
|
94
86
|
) : null}
|
|
95
|
-
<MessageTimestamp formattedDate={formattedDate} timestamp={timestamp} />
|
|
96
87
|
</View>
|
|
97
88
|
);
|
|
98
89
|
};
|
|
@@ -101,20 +92,8 @@ const areEqual = (
|
|
|
101
92
|
prevProps: MessageStatusPropsWithContext,
|
|
102
93
|
nextProps: MessageStatusPropsWithContext,
|
|
103
94
|
) => {
|
|
104
|
-
const {
|
|
105
|
-
|
|
106
|
-
message: prevMessage,
|
|
107
|
-
readBy: prevReadBy,
|
|
108
|
-
formattedDate: prevFormattedDate,
|
|
109
|
-
timestamp: prevTimestamp,
|
|
110
|
-
} = prevProps;
|
|
111
|
-
const {
|
|
112
|
-
deliveredToCount: nextDeliveredBy,
|
|
113
|
-
message: nextMessage,
|
|
114
|
-
readBy: nextReadBy,
|
|
115
|
-
formattedDate: nextFormattedDate,
|
|
116
|
-
timestamp: nextTimestamp,
|
|
117
|
-
} = nextProps;
|
|
95
|
+
const { deliveredToCount: prevDeliveredBy, message: prevMessage, readBy: prevReadBy } = prevProps;
|
|
96
|
+
const { deliveredToCount: nextDeliveredBy, message: nextMessage, readBy: nextReadBy } = nextProps;
|
|
118
97
|
|
|
119
98
|
const deliveredByEqual = prevDeliveredBy === nextDeliveredBy;
|
|
120
99
|
if (!deliveredByEqual) {
|
|
@@ -132,16 +111,6 @@ const areEqual = (
|
|
|
132
111
|
return false;
|
|
133
112
|
}
|
|
134
113
|
|
|
135
|
-
const timestampEqual = prevTimestamp === nextTimestamp;
|
|
136
|
-
if (!timestampEqual) {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const formattedDateEqual = prevFormattedDate === nextFormattedDate;
|
|
141
|
-
if (!formattedDateEqual) {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
114
|
return true;
|
|
146
115
|
};
|
|
147
116
|
|
|
@@ -155,7 +124,6 @@ export type MessageStatusProps = Partial<MessageStatusPropsWithContext>;
|
|
|
155
124
|
export const MessageStatus = (props: MessageStatusProps) => {
|
|
156
125
|
const { channel } = useChannelContext();
|
|
157
126
|
const { deliveredToCount, message, readBy } = useMessageContext();
|
|
158
|
-
const { MessageTimestamp } = useMessagesContext();
|
|
159
127
|
|
|
160
128
|
const channelMembersCount = Object.keys(channel?.state.members).length;
|
|
161
129
|
|
|
@@ -167,7 +135,6 @@ export const MessageStatus = (props: MessageStatusProps) => {
|
|
|
167
135
|
deliveredToCount,
|
|
168
136
|
message,
|
|
169
137
|
readBy,
|
|
170
|
-
MessageTimestamp,
|
|
171
138
|
}}
|
|
172
139
|
{...props}
|
|
173
140
|
/>
|
|
@@ -108,7 +108,6 @@ export const AudioRecordingPreview = () => {
|
|
|
108
108
|
const dragEnd = useStableCallback(async (currentProgress: number) => {
|
|
109
109
|
const positionInSeconds = (currentProgress * duration) / ONE_SECOND_IN_MILLISECONDS;
|
|
110
110
|
await audioPlayer.seek(positionInSeconds);
|
|
111
|
-
audioPlayer.play();
|
|
112
111
|
});
|
|
113
112
|
|
|
114
113
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { LocalVoiceRecordingAttachment } from 'stream-chat';
|
|
4
4
|
|
|
@@ -12,14 +12,11 @@ import { resampleWaveformData } from '../utils/audioSampling';
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The hook that controls all the async audio core features including start/stop or recording, player, upload/delete of the recorded audio.
|
|
15
|
-
*
|
|
16
|
-
* FIXME: Change the name to `useAudioRecorder` in the next major version as the hook will only be used for audio recording.
|
|
17
15
|
*/
|
|
18
16
|
export const useAudioRecorder = ({
|
|
19
17
|
audioRecorderManager,
|
|
20
18
|
sendMessage,
|
|
21
19
|
}: Pick<MessageInputContextValue, 'audioRecorderManager' | 'sendMessage'>) => {
|
|
22
|
-
const [isScheduledForSubmit, setIsScheduleForSubmit] = useState(false);
|
|
23
20
|
const { attachmentManager } = useMessageComposer();
|
|
24
21
|
|
|
25
22
|
/**
|
|
@@ -43,13 +40,6 @@ export const useAudioRecorder = ({
|
|
|
43
40
|
[stopVoiceRecording],
|
|
44
41
|
);
|
|
45
42
|
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (isScheduledForSubmit) {
|
|
48
|
-
sendMessage();
|
|
49
|
-
setIsScheduleForSubmit(false);
|
|
50
|
-
}
|
|
51
|
-
}, [isScheduledForSubmit, sendMessage]);
|
|
52
|
-
|
|
53
43
|
/**
|
|
54
44
|
* Function to start voice recording. Will return whether access is granted
|
|
55
45
|
* with regards to the microphone permission as that's how the underlying
|
|
@@ -113,8 +103,8 @@ export const useAudioRecorder = ({
|
|
|
113
103
|
audioRecorderManager.reset();
|
|
114
104
|
|
|
115
105
|
if (sendOnComplete) {
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
attachmentManager.upsertAttachments([audioFile]);
|
|
107
|
+
sendMessage();
|
|
118
108
|
} else {
|
|
119
109
|
await attachmentManager.uploadAttachment(audioFile);
|
|
120
110
|
}
|
|
@@ -122,7 +112,7 @@ export const useAudioRecorder = ({
|
|
|
122
112
|
console.log('Error uploading voice recording: ', error);
|
|
123
113
|
}
|
|
124
114
|
},
|
|
125
|
-
[audioRecorderManager, attachmentManager, stopVoiceRecording],
|
|
115
|
+
[audioRecorderManager, attachmentManager, sendMessage, stopVoiceRecording],
|
|
126
116
|
);
|
|
127
117
|
|
|
128
118
|
return {
|
|
@@ -291,6 +291,7 @@ export const MessageUserReactions = (props: MessageUserReactionsProps) => {
|
|
|
291
291
|
</Text>
|
|
292
292
|
<View style={[styles.reactionSelectorContainer, reactionSelectorContainer]}>
|
|
293
293
|
<FlatList
|
|
294
|
+
showsHorizontalScrollIndicator={false}
|
|
294
295
|
contentContainerStyle={[styles.contentContainer, contentContainer]}
|
|
295
296
|
data={selectorReactions}
|
|
296
297
|
getItemLayout={getItemLayout}
|
|
@@ -9,6 +9,7 @@ import { PollInputDialog } from './PollInputDialog';
|
|
|
9
9
|
import {
|
|
10
10
|
PollContextProvider,
|
|
11
11
|
PollContextValue,
|
|
12
|
+
useChatContext,
|
|
12
13
|
usePollContext,
|
|
13
14
|
useTheme,
|
|
14
15
|
useTranslationContext,
|
|
@@ -27,6 +28,8 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => {
|
|
|
27
28
|
const [showAddCommentDialog, setShowAddCommentDialog] = useState(false);
|
|
28
29
|
const { onPress } = props;
|
|
29
30
|
|
|
31
|
+
const styles = useStyles();
|
|
32
|
+
|
|
30
33
|
const onPressHandler = useCallback(() => {
|
|
31
34
|
if (onPress) {
|
|
32
35
|
onPress({ message, poll });
|
|
@@ -37,10 +40,10 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => {
|
|
|
37
40
|
}, [message, onPress, poll]);
|
|
38
41
|
|
|
39
42
|
return (
|
|
40
|
-
|
|
43
|
+
<View style={styles.inlineButton}>
|
|
41
44
|
<Button
|
|
42
45
|
variant={'secondary'}
|
|
43
|
-
type={'
|
|
46
|
+
type={'ghost'}
|
|
44
47
|
size={'lg'}
|
|
45
48
|
label={ownAnswer ? t('Update your comment') : t('Add a comment')}
|
|
46
49
|
onPress={onPressHandler}
|
|
@@ -54,7 +57,7 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => {
|
|
|
54
57
|
visible={showAddCommentDialog}
|
|
55
58
|
/>
|
|
56
59
|
) : null}
|
|
57
|
-
|
|
60
|
+
</View>
|
|
58
61
|
);
|
|
59
62
|
};
|
|
60
63
|
|
|
@@ -64,6 +67,7 @@ export type PollAnswersListProps = PollContextValue & {
|
|
|
64
67
|
};
|
|
65
68
|
|
|
66
69
|
export const PollAnswerListItem = ({ answer }: { answer: PollAnswer }) => {
|
|
70
|
+
const { client } = useChatContext();
|
|
67
71
|
const { t, tDateTimeParser } = useTranslationContext();
|
|
68
72
|
const { votingVisibility } = usePollState();
|
|
69
73
|
|
|
@@ -87,25 +91,32 @@ export const PollAnswerListItem = ({ answer }: { answer: PollAnswer }) => {
|
|
|
87
91
|
[answer.updated_at, t, tDateTimeParser],
|
|
88
92
|
);
|
|
89
93
|
|
|
94
|
+
const isMyAnswer = client.userID === answer.user?.id;
|
|
95
|
+
|
|
90
96
|
const isAnonymous = useMemo(
|
|
91
|
-
() => votingVisibility === VotingVisibility.anonymous,
|
|
92
|
-
[votingVisibility],
|
|
97
|
+
() => votingVisibility === VotingVisibility.anonymous && !isMyAnswer,
|
|
98
|
+
[votingVisibility, isMyAnswer],
|
|
93
99
|
);
|
|
94
100
|
|
|
101
|
+
const answerAuthorName = isMyAnswer ? t('You') : answer.user?.name;
|
|
102
|
+
|
|
95
103
|
return (
|
|
96
|
-
<View style={[styles.
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
<View style={[styles.
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{
|
|
105
|
-
|
|
104
|
+
<View style={[styles.listItemWrapper, itemStyle.wrapper]}>
|
|
105
|
+
<View style={[styles.listItemContainer, itemStyle.container]}>
|
|
106
|
+
<Text style={[styles.listItemAnswerText, itemStyle.answerText]}>{answer.answer_text}</Text>
|
|
107
|
+
<View style={[styles.listItemInfoContainer, itemStyle.infoContainer]}>
|
|
108
|
+
<View style={[styles.listItemUserInfoContainer, itemStyle.userInfoContainer]}>
|
|
109
|
+
{!isAnonymous && answer.user?.image ? (
|
|
110
|
+
<UserAvatar user={answer.user} size='sm' showBorder />
|
|
111
|
+
) : null}
|
|
112
|
+
<Text style={styles.listItemInfoUserName}>
|
|
113
|
+
{isAnonymous ? t('Anonymous') : answerAuthorName}
|
|
114
|
+
</Text>
|
|
115
|
+
<Text style={styles.listItemInfoDate}>{dateString}</Text>
|
|
116
|
+
</View>
|
|
106
117
|
</View>
|
|
107
|
-
<Text style={styles.listItemInfoDate}>{dateString}</Text>
|
|
108
118
|
</View>
|
|
119
|
+
{isMyAnswer ? <AnswerListAddCommentButton /> : null}
|
|
109
120
|
</View>
|
|
110
121
|
);
|
|
111
122
|
};
|
|
@@ -137,7 +148,6 @@ export const PollAnswersListContent = ({
|
|
|
137
148
|
renderItem={renderPollAnswerListItem}
|
|
138
149
|
{...additionalFlatListProps}
|
|
139
150
|
/>
|
|
140
|
-
<AnswerListAddCommentButton />
|
|
141
151
|
</View>
|
|
142
152
|
);
|
|
143
153
|
};
|
|
@@ -164,14 +174,7 @@ const useStyles = () => {
|
|
|
164
174
|
return useMemo(
|
|
165
175
|
() =>
|
|
166
176
|
StyleSheet.create({
|
|
167
|
-
addCommentButtonContainer: {
|
|
168
|
-
alignItems: 'center',
|
|
169
|
-
borderRadius: 12,
|
|
170
|
-
paddingHorizontal: 16,
|
|
171
|
-
paddingVertical: 18,
|
|
172
|
-
},
|
|
173
177
|
contentContainer: { gap: primitives.spacingMd },
|
|
174
|
-
addCommentButtonText: { fontSize: 16 },
|
|
175
178
|
container: {
|
|
176
179
|
flex: 1,
|
|
177
180
|
padding: primitives.spacingMd,
|
|
@@ -179,31 +182,41 @@ const useStyles = () => {
|
|
|
179
182
|
},
|
|
180
183
|
listItemAnswerText: {
|
|
181
184
|
fontSize: primitives.typographyFontSizeMd,
|
|
182
|
-
lineHeight: primitives.
|
|
183
|
-
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
185
|
+
lineHeight: primitives.typographyLineHeightNormal,
|
|
184
186
|
color: semantics.textPrimary,
|
|
185
187
|
},
|
|
186
|
-
|
|
188
|
+
listItemWrapper: {
|
|
187
189
|
borderRadius: primitives.radiusLg,
|
|
188
|
-
padding: primitives.spacingMd,
|
|
189
190
|
backgroundColor: semantics.backgroundCoreSurfaceCard,
|
|
190
191
|
},
|
|
192
|
+
listItemContainer: {
|
|
193
|
+
padding: primitives.spacingMd,
|
|
194
|
+
gap: primitives.spacingXs,
|
|
195
|
+
},
|
|
191
196
|
listItemInfoContainer: {
|
|
192
197
|
flexDirection: 'row',
|
|
193
198
|
justifyContent: 'space-between',
|
|
194
199
|
alignItems: 'center',
|
|
195
|
-
marginTop: 24,
|
|
196
200
|
},
|
|
197
201
|
listItemInfoUserName: {
|
|
198
|
-
color: semantics.
|
|
202
|
+
color: semantics.chatTextUsername,
|
|
199
203
|
fontSize: primitives.typographyFontSizeSm,
|
|
200
|
-
|
|
204
|
+
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
205
|
+
lineHeight: primitives.typographyLineHeightNormal,
|
|
201
206
|
},
|
|
202
207
|
listItemInfoDate: {
|
|
203
208
|
fontSize: primitives.typographyFontSizeSm,
|
|
204
209
|
color: semantics.textTertiary,
|
|
205
210
|
},
|
|
206
|
-
listItemUserInfoContainer: {
|
|
211
|
+
listItemUserInfoContainer: {
|
|
212
|
+
gap: primitives.spacingXs,
|
|
213
|
+
alignItems: 'center',
|
|
214
|
+
flexDirection: 'row',
|
|
215
|
+
},
|
|
216
|
+
inlineButton: {
|
|
217
|
+
borderColor: semantics.borderCoreDefault,
|
|
218
|
+
borderTopWidth: 1,
|
|
219
|
+
},
|
|
207
220
|
}),
|
|
208
221
|
[semantics],
|
|
209
222
|
);
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
useOwnCapabilitiesContext,
|
|
15
15
|
usePollContext,
|
|
16
16
|
useTheme,
|
|
17
|
+
useTranslationContext,
|
|
17
18
|
} from '../../../contexts';
|
|
18
19
|
|
|
19
20
|
import { Check } from '../../../icons';
|
|
@@ -26,6 +27,7 @@ import { usePollState } from '../hooks/usePollState';
|
|
|
26
27
|
export type PollOptionProps = {
|
|
27
28
|
option: PollOptionClass;
|
|
28
29
|
showProgressBar?: boolean;
|
|
30
|
+
forceIncoming?: boolean;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export type PollAllOptionsContentProps = PollContextValue & {
|
|
@@ -36,6 +38,7 @@ export type PollAllOptionsContentProps = PollContextValue & {
|
|
|
36
38
|
export const PollAllOptionsContent = ({
|
|
37
39
|
additionalScrollViewProps,
|
|
38
40
|
}: Pick<PollAllOptionsContentProps, 'additionalScrollViewProps'>) => {
|
|
41
|
+
const { t } = useTranslationContext();
|
|
39
42
|
const { name, options } = usePollState();
|
|
40
43
|
|
|
41
44
|
const {
|
|
@@ -50,12 +53,13 @@ export const PollAllOptionsContent = ({
|
|
|
50
53
|
return (
|
|
51
54
|
<ScrollView style={[styles.allOptionsWrapper, wrapper]} {...additionalScrollViewProps}>
|
|
52
55
|
<View style={[styles.allOptionsTitleContainer, titleContainer]}>
|
|
56
|
+
<Text style={styles.allOptionsTitleMeta}>{t('Question')}</Text>
|
|
53
57
|
<Text style={[styles.allOptionsTitleText, titleText]}>{name}</Text>
|
|
54
58
|
</View>
|
|
55
59
|
<View style={[styles.allOptionsListContainer, listContainer]}>
|
|
56
60
|
{options?.map((option: PollOptionClass) => (
|
|
57
61
|
<View key={`full_poll_options_${option.id}`} style={styles.optionWrapper}>
|
|
58
|
-
<PollOption key={option.id} option={option}
|
|
62
|
+
<PollOption key={option.id} option={option} forceIncoming />
|
|
59
63
|
</View>
|
|
60
64
|
))}
|
|
61
65
|
</View>
|
|
@@ -78,19 +82,15 @@ export const PollAllOptions = ({
|
|
|
78
82
|
</PollContextProvider>
|
|
79
83
|
);
|
|
80
84
|
|
|
81
|
-
export const PollOption = ({ option, showProgressBar = true }: PollOptionProps) => {
|
|
82
|
-
const { latestVotesByOption,
|
|
85
|
+
export const PollOption = ({ option, showProgressBar = true, forceIncoming }: PollOptionProps) => {
|
|
86
|
+
const { latestVotesByOption, voteCountsByOption, voteCount } = usePollState();
|
|
83
87
|
const styles = useStyles();
|
|
84
88
|
|
|
85
89
|
const relevantVotes = useMemo(
|
|
86
90
|
() => latestVotesByOption?.[option.id] || [],
|
|
87
91
|
[latestVotesByOption, option.id],
|
|
88
92
|
);
|
|
89
|
-
|
|
90
|
-
() =>
|
|
91
|
-
maxVotedOptionIds?.[0] && voteCountsByOption ? voteCountsByOption[maxVotedOptionIds[0]] : 0,
|
|
92
|
-
[maxVotedOptionIds, voteCountsByOption],
|
|
93
|
-
);
|
|
93
|
+
|
|
94
94
|
const votes = voteCountsByOption[option.id] || 0;
|
|
95
95
|
|
|
96
96
|
const {
|
|
@@ -105,13 +105,15 @@ export const PollOption = ({ option, showProgressBar = true }: PollOptionProps)
|
|
|
105
105
|
} = useTheme();
|
|
106
106
|
const isPollCreatedByClient = useIsPollCreatedByCurrentUser();
|
|
107
107
|
|
|
108
|
-
const unFilledColor =
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
const unFilledColor =
|
|
109
|
+
isPollCreatedByClient && !forceIncoming
|
|
110
|
+
? semantics.chatPollProgressTrackOutgoing
|
|
111
|
+
: semantics.chatPollProgressTrackIncoming;
|
|
111
112
|
|
|
112
|
-
const filledColor =
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
const filledColor =
|
|
114
|
+
isPollCreatedByClient && !forceIncoming
|
|
115
|
+
? semantics.chatPollProgressFillOutgoing
|
|
116
|
+
: semantics.chatPollProgressFillIncoming;
|
|
115
117
|
|
|
116
118
|
return (
|
|
117
119
|
<View style={[styles.container, container]}>
|
|
@@ -133,7 +135,7 @@ export const PollOption = ({ option, showProgressBar = true }: PollOptionProps)
|
|
|
133
135
|
{showProgressBar ? (
|
|
134
136
|
<View style={styles.progressBarContainer}>
|
|
135
137
|
<ProgressBar
|
|
136
|
-
progress={votes /
|
|
138
|
+
progress={votes / voteCount}
|
|
137
139
|
filledColor={filledColor}
|
|
138
140
|
emptyColor={unFilledColor}
|
|
139
141
|
/>
|
|
@@ -273,6 +275,13 @@ const useAllOptionStyles = () => {
|
|
|
273
275
|
lineHeight: primitives.typographyLineHeightRelaxed,
|
|
274
276
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
275
277
|
color: semantics.textPrimary,
|
|
278
|
+
paddingTop: primitives.spacingXs,
|
|
279
|
+
},
|
|
280
|
+
allOptionsTitleMeta: {
|
|
281
|
+
fontSize: primitives.typographyFontSizeSm,
|
|
282
|
+
color: semantics.textTertiary,
|
|
283
|
+
lineHeight: primitives.typographyLineHeightNormal,
|
|
284
|
+
fontWeight: primitives.typographyFontWeightMedium,
|
|
276
285
|
},
|
|
277
286
|
allOptionsWrapper: {
|
|
278
287
|
flex: 1,
|
|
@@ -112,7 +112,9 @@ const useStyles = () => {
|
|
|
112
112
|
backgroundColor: semantics.backgroundCoreSurfaceCard,
|
|
113
113
|
borderRadius: primitives.radiusLg,
|
|
114
114
|
marginBottom: primitives.spacingMd,
|
|
115
|
-
|
|
115
|
+
paddingHorizontal: primitives.spacingMd,
|
|
116
|
+
paddingTop: primitives.spacingMd,
|
|
117
|
+
paddingBottom: primitives.spacingXs,
|
|
116
118
|
},
|
|
117
119
|
headerContainer: {
|
|
118
120
|
flexDirection: 'row',
|
|
@@ -133,6 +135,7 @@ const useStyles = () => {
|
|
|
133
135
|
lineHeight: primitives.typographyLineHeightNormal,
|
|
134
136
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
135
137
|
color: semantics.textPrimary,
|
|
138
|
+
paddingTop: primitives.spacingXs,
|
|
136
139
|
marginLeft: primitives.spacingMd,
|
|
137
140
|
},
|
|
138
141
|
}),
|