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.
Files changed (108) hide show
  1. package/lib/commonjs/components/Attachment/Audio/AudioAttachment.js +0 -1
  2. package/lib/commonjs/components/Attachment/Audio/AudioAttachment.js.map +1 -1
  3. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +16 -9
  4. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  5. package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js +6 -1
  6. package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js.map +1 -1
  7. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +7 -29
  8. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  9. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +0 -1
  10. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
  11. package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js +3 -14
  12. package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
  13. package/lib/commonjs/components/MessageMenu/MessageUserReactions.js +1 -0
  14. package/lib/commonjs/components/MessageMenu/MessageUserReactions.js.map +1 -1
  15. package/lib/commonjs/components/Poll/components/PollAnswersList.js +52 -45
  16. package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
  17. package/lib/commonjs/components/Poll/components/PollOption.js +24 -14
  18. package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
  19. package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js +4 -1
  20. package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
  21. package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js +46 -19
  22. package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  23. package/lib/commonjs/components/Poll/hooks/usePollState.js +6 -3
  24. package/lib/commonjs/components/Poll/hooks/usePollState.js.map +1 -1
  25. package/lib/commonjs/components/ThreadList/ThreadListItem.js +46 -15
  26. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
  27. package/lib/commonjs/components/ThreadList/ThreadListItemMessagePreview.js +23 -5
  28. package/lib/commonjs/components/ThreadList/ThreadListItemMessagePreview.js.map +1 -1
  29. package/lib/commonjs/contexts/themeContext/utils/theme.js +4 -1
  30. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  31. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
  32. package/lib/commonjs/version.json +1 -1
  33. package/lib/module/components/Attachment/Audio/AudioAttachment.js +0 -1
  34. package/lib/module/components/Attachment/Audio/AudioAttachment.js.map +1 -1
  35. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +16 -9
  36. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  37. package/lib/module/components/Message/MessageItemView/MessageFooter.js +6 -1
  38. package/lib/module/components/Message/MessageItemView/MessageFooter.js.map +1 -1
  39. package/lib/module/components/Message/MessageItemView/MessageStatus.js +7 -29
  40. package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  41. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +0 -1
  42. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
  43. package/lib/module/components/MessageInput/hooks/useAudioRecorder.js +3 -14
  44. package/lib/module/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
  45. package/lib/module/components/MessageMenu/MessageUserReactions.js +1 -0
  46. package/lib/module/components/MessageMenu/MessageUserReactions.js.map +1 -1
  47. package/lib/module/components/Poll/components/PollAnswersList.js +52 -45
  48. package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
  49. package/lib/module/components/Poll/components/PollOption.js +24 -14
  50. package/lib/module/components/Poll/components/PollOption.js.map +1 -1
  51. package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js +4 -1
  52. package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
  53. package/lib/module/components/Poll/components/PollResults/PollResultItem.js +46 -19
  54. package/lib/module/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  55. package/lib/module/components/Poll/hooks/usePollState.js +6 -3
  56. package/lib/module/components/Poll/hooks/usePollState.js.map +1 -1
  57. package/lib/module/components/ThreadList/ThreadListItem.js +46 -15
  58. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
  59. package/lib/module/components/ThreadList/ThreadListItemMessagePreview.js +23 -5
  60. package/lib/module/components/ThreadList/ThreadListItemMessagePreview.js.map +1 -1
  61. package/lib/module/contexts/themeContext/utils/theme.js +4 -1
  62. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  63. package/lib/module/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
  64. package/lib/module/version.json +1 -1
  65. package/lib/typescript/components/Attachment/Audio/AudioAttachment.d.ts.map +1 -1
  66. package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts +1 -1
  67. package/lib/typescript/components/ChannelList/hooks/usePaginatedChannels.d.ts.map +1 -1
  68. package/lib/typescript/components/Message/MessageItemView/MessageFooter.d.ts.map +1 -1
  69. package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts +1 -5
  70. package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
  71. package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
  72. package/lib/typescript/components/MessageInput/hooks/useAudioRecorder.d.ts +0 -2
  73. package/lib/typescript/components/MessageInput/hooks/useAudioRecorder.d.ts.map +1 -1
  74. package/lib/typescript/components/MessageMenu/MessageUserReactions.d.ts.map +1 -1
  75. package/lib/typescript/components/Poll/components/PollAnswersList.d.ts.map +1 -1
  76. package/lib/typescript/components/Poll/components/PollOption.d.ts +2 -1
  77. package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
  78. package/lib/typescript/components/Poll/components/PollResults/PollResultItem.d.ts.map +1 -1
  79. package/lib/typescript/components/Poll/hooks/usePollState.d.ts +1 -0
  80. package/lib/typescript/components/Poll/hooks/usePollState.d.ts.map +1 -1
  81. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts.map +1 -1
  82. package/lib/typescript/components/ThreadList/ThreadListItemMessagePreview.d.ts.map +1 -1
  83. package/lib/typescript/contexts/themeContext/ThemeContext.d.ts +3 -0
  84. package/lib/typescript/contexts/themeContext/ThemeContext.d.ts.map +1 -1
  85. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +3 -0
  86. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  87. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts +2 -1
  88. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts.map +1 -1
  89. package/package.json +1 -1
  90. package/src/components/Attachment/Audio/AudioAttachment.tsx +0 -1
  91. package/src/components/ChannelList/__tests__/ChannelList.test.js +43 -0
  92. package/src/components/ChannelList/hooks/usePaginatedChannels.ts +8 -5
  93. package/src/components/Message/MessageItemView/MessageFooter.tsx +9 -3
  94. package/src/components/Message/MessageItemView/MessageStatus.tsx +4 -37
  95. package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx +0 -1
  96. package/src/components/MessageInput/hooks/useAudioRecorder.tsx +4 -14
  97. package/src/components/MessageMenu/MessageUserReactions.tsx +1 -0
  98. package/src/components/Poll/components/PollAnswersList.tsx +45 -32
  99. package/src/components/Poll/components/PollOption.tsx +24 -15
  100. package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx +4 -1
  101. package/src/components/Poll/components/PollResults/PollResultItem.tsx +36 -12
  102. package/src/components/Poll/hooks/usePollState.ts +4 -0
  103. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +52 -0
  104. package/src/components/ThreadList/ThreadListItem.tsx +56 -10
  105. package/src/components/ThreadList/ThreadListItemMessagePreview.tsx +18 -3
  106. package/src/contexts/themeContext/utils/theme.ts +6 -0
  107. package/src/contexts/threadsContext/ThreadListItemContext.tsx +2 -1
  108. 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.6",
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
- await refreshList();
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]}>{message.user.name}</Text>
81
+ <Text numberOfLines={1} ellipsizeMode='tail' style={[styles.name, name]}>
82
+ {message.user.name}
83
+ </Text>
81
84
  ) : null}
82
- {showMessageStatus && <MessageStatus formattedDate={formattedDate} timestamp={date} />}
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, formattedDate, message, readBy, timestamp, MessageTimestamp } = props;
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
- deliveredToCount: prevDeliveredBy,
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, useState } from 'react';
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
- await attachmentManager.uploadAttachment(audioFile);
117
- setIsScheduleForSubmit(true);
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={'outline'}
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.listItemContainer, itemStyle.container]}>
97
- <Text style={[styles.listItemAnswerText, itemStyle.answerText]}>{answer.answer_text}</Text>
98
- <View style={[styles.listItemInfoContainer, itemStyle.infoContainer]}>
99
- <View style={[styles.listItemUserInfoContainer, itemStyle.userInfoContainer]}>
100
- {!isAnonymous && answer.user?.image ? (
101
- <UserAvatar user={answer.user} size='md' showBorder />
102
- ) : null}
103
- <Text style={styles.listItemInfoUserName}>
104
- {isAnonymous ? t('Anonymous') : answer.user?.name}
105
- </Text>
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.typographyLineHeightRelaxed,
183
- fontWeight: primitives.typographyFontWeightSemiBold,
185
+ lineHeight: primitives.typographyLineHeightNormal,
184
186
  color: semantics.textPrimary,
185
187
  },
186
- listItemContainer: {
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.textPrimary,
202
+ color: semantics.chatTextUsername,
199
203
  fontSize: primitives.typographyFontSizeSm,
200
- marginLeft: primitives.spacingXxs,
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: { alignItems: 'center', flexDirection: 'row' },
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} showProgressBar={false} />
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, maxVotedOptionIds, voteCountsByOption } = usePollState();
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
- const maxVotes = useMemo(
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 = isPollCreatedByClient
109
- ? semantics.chatPollProgressTrackOutgoing
110
- : semantics.chatPollProgressTrackIncoming;
108
+ const unFilledColor =
109
+ isPollCreatedByClient && !forceIncoming
110
+ ? semantics.chatPollProgressTrackOutgoing
111
+ : semantics.chatPollProgressTrackIncoming;
111
112
 
112
- const filledColor = isPollCreatedByClient
113
- ? semantics.chatPollProgressFillOutgoing
114
- : semantics.chatPollProgressFillIncoming;
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 / maxVotes}
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
- padding: primitives.spacingMd,
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
  }),