stream-chat-react-native-core 9.7.3 → 9.7.4
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/Attachment.js +1 -1
- package/lib/commonjs/components/Attachment/Attachment.js.map +1 -1
- package/lib/commonjs/components/Attachment/Audio/PlayPauseButton.js +1 -1
- package/lib/commonjs/components/Attachment/Audio/PlayPauseButton.js.map +1 -1
- package/lib/commonjs/components/Attachment/UrlPreview/URLPreview.js +16 -18
- package/lib/commonjs/components/Attachment/UrlPreview/URLPreview.js.map +1 -1
- package/lib/commonjs/components/Attachment/UrlPreview/URLPreviewCompact.js +16 -18
- package/lib/commonjs/components/Attachment/UrlPreview/URLPreviewCompact.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js +1 -2
- package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js +3 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +3 -1
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/Poll/Poll.js +5 -3
- package/lib/commonjs/components/Poll/Poll.js.map +1 -1
- package/lib/commonjs/components/Poll/components/Button.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollOption.js +20 -10
- package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
- package/lib/commonjs/components/Reply/Reply.js +19 -10
- package/lib/commonjs/components/Reply/Reply.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/ThemeContext.js +8 -1
- package/lib/commonjs/contexts/themeContext/ThemeContext.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/Attachment.js +1 -1
- package/lib/module/components/Attachment/Attachment.js.map +1 -1
- package/lib/module/components/Attachment/Audio/PlayPauseButton.js +1 -1
- package/lib/module/components/Attachment/Audio/PlayPauseButton.js.map +1 -1
- package/lib/module/components/Attachment/UrlPreview/URLPreview.js +16 -18
- package/lib/module/components/Attachment/UrlPreview/URLPreview.js.map +1 -1
- package/lib/module/components/Attachment/UrlPreview/URLPreviewCompact.js +16 -18
- package/lib/module/components/Attachment/UrlPreview/URLPreviewCompact.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageItemView.js +1 -2
- package/lib/module/components/Message/MessageItemView/MessageItemView.js.map +1 -1
- package/lib/module/components/MessageList/MessageFlashList.js +3 -1
- package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +3 -1
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/Poll/Poll.js +5 -3
- package/lib/module/components/Poll/Poll.js.map +1 -1
- package/lib/module/components/Poll/components/Button.js.map +1 -1
- package/lib/module/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/module/components/Poll/components/PollOption.js +20 -10
- package/lib/module/components/Poll/components/PollOption.js.map +1 -1
- package/lib/module/components/Reply/Reply.js +19 -10
- package/lib/module/components/Reply/Reply.js.map +1 -1
- package/lib/module/contexts/themeContext/ThemeContext.js +8 -1
- package/lib/module/contexts/themeContext/ThemeContext.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Attachment/UrlPreview/URLPreview.d.ts.map +1 -1
- package/lib/typescript/components/Attachment/UrlPreview/URLPreviewCompact.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageItemView.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/Poll/Poll.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/Button.d.ts +5 -0
- package/lib/typescript/components/Poll/components/Button.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollOption.d.ts +1 -1
- package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
- package/lib/typescript/components/Reply/Reply.d.ts +1 -0
- package/lib/typescript/components/Reply/Reply.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/ThemeContext.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Attachment/Attachment.tsx +1 -1
- package/src/components/Attachment/Audio/PlayPauseButton.tsx +1 -1
- package/src/components/Attachment/Audio/__tests__/PlayPauseButton.test.tsx +44 -0
- package/src/components/Attachment/UrlPreview/URLPreview.tsx +4 -8
- package/src/components/Attachment/UrlPreview/URLPreviewCompact.tsx +4 -8
- package/src/components/Attachment/UrlPreview/__tests__/URLPreview.test.tsx +92 -0
- package/src/components/Attachment/__tests__/Attachment.test.tsx +48 -0
- package/src/components/Message/MessageItemView/MessageItemView.tsx +1 -2
- package/src/components/Message/MessageItemView/__tests__/MessageTextContainer.test.tsx +20 -2
- package/src/components/Message/MessageItemView/__tests__/__snapshots__/MessageAuthor.test.tsx.snap +2 -2
- package/src/components/MessageList/MessageFlashList.tsx +4 -2
- package/src/components/MessageList/MessageList.tsx +4 -2
- package/src/components/Poll/Poll.tsx +5 -3
- package/src/components/Poll/__tests__/Poll.test.tsx +70 -0
- package/src/components/Poll/components/Button.tsx +5 -0
- package/src/components/Poll/components/PollButtons.tsx +2 -0
- package/src/components/Poll/components/PollOption.tsx +22 -21
- package/src/components/Poll/components/__tests__/PollOption.test.tsx +99 -0
- package/src/components/Reply/Reply.tsx +20 -13
- package/src/components/Reply/__tests__/Reply.test.tsx +111 -1
- package/src/contexts/themeContext/ThemeContext.tsx +5 -1
- package/src/version.json +1 -1
|
@@ -257,7 +257,7 @@ const useAudioAttachmentStyles = () => {
|
|
|
257
257
|
: semantics.chatBorderOnChatIncoming,
|
|
258
258
|
},
|
|
259
259
|
durationText: {
|
|
260
|
-
color: semantics.chatTextIncoming,
|
|
260
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
261
261
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
262
262
|
},
|
|
263
263
|
leftContainer: {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { render, screen } from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import { mergeThemes, ThemeProvider } from '../../../../contexts/themeContext/ThemeContext';
|
|
7
|
+
import { PlayPauseButton } from '../PlayPauseButton';
|
|
8
|
+
|
|
9
|
+
describe('PlayPauseButton', () => {
|
|
10
|
+
const lightTheme = mergeThemes({ scheme: 'light' });
|
|
11
|
+
|
|
12
|
+
const renderButton = (containerStyle?: Record<string, unknown>) =>
|
|
13
|
+
render(
|
|
14
|
+
<ThemeProvider>
|
|
15
|
+
<PlayPauseButton
|
|
16
|
+
containerStyle={containerStyle}
|
|
17
|
+
isPlaying={false}
|
|
18
|
+
onPress={jest.fn()}
|
|
19
|
+
testID='play-pause-button'
|
|
20
|
+
/>
|
|
21
|
+
</ThemeProvider>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const borderColorOf = () => {
|
|
25
|
+
const style = screen.getByTestId('play-pause-button').props.style;
|
|
26
|
+
return StyleSheet.flatten(typeof style === 'function' ? style({ pressed: false }) : style)
|
|
27
|
+
?.borderColor;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// This control also renders in the composer's attachment previews, which are
|
|
31
|
+
// not a message bubble - so it must not reach for a chat side token.
|
|
32
|
+
it('defaults to the neutral playback toggle border, not a chat bubble border', () => {
|
|
33
|
+
renderButton();
|
|
34
|
+
|
|
35
|
+
expect(borderColorOf()).toBe(lightTheme.semantics.controlPlaybackToggleBorder);
|
|
36
|
+
expect(borderColorOf()).not.toBe(lightTheme.semantics.chatBorderOnChatIncoming);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('lets in-bubble callers override the border with the resolved chat side', () => {
|
|
40
|
+
renderButton({ borderColor: lightTheme.semantics.chatBorderOnChatOutgoing });
|
|
41
|
+
|
|
42
|
+
expect(borderColorOf()).toBe(lightTheme.semantics.chatBorderOnChatOutgoing);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -68,10 +68,6 @@ const URLPreviewWithContext = (props: URLPreviewPropsWithContext) => {
|
|
|
68
68
|
|
|
69
69
|
const { icons } = useComponentsContext();
|
|
70
70
|
|
|
71
|
-
const {
|
|
72
|
-
theme: { semantics },
|
|
73
|
-
} = useTheme();
|
|
74
|
-
|
|
75
71
|
const { image_url, og_scrape_url, text, thumb_url, title, type } = attachment;
|
|
76
72
|
|
|
77
73
|
const {
|
|
@@ -156,7 +152,7 @@ const URLPreviewWithContext = (props: URLPreviewPropsWithContext) => {
|
|
|
156
152
|
</Text>
|
|
157
153
|
) : null}
|
|
158
154
|
<View style={[styles.linkPreview, linkPreview, stylesProp.linkPreview]}>
|
|
159
|
-
<icons.Link height={12} width={12} stroke={
|
|
155
|
+
<icons.Link height={12} width={12} stroke={styles.linkPreviewText.color} />
|
|
160
156
|
<Text
|
|
161
157
|
numberOfLines={1}
|
|
162
158
|
style={[styles.linkPreviewText, linkPreviewText, stylesProp.linkPreviewText]}
|
|
@@ -267,13 +263,13 @@ const useStyles = () => {
|
|
|
267
263
|
padding: primitives.spacingSm,
|
|
268
264
|
},
|
|
269
265
|
title: {
|
|
270
|
-
color: semantics.chatTextIncoming,
|
|
266
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
271
267
|
fontSize: primitives.typographyFontSizeSm,
|
|
272
268
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
273
269
|
lineHeight: primitives.typographyLineHeightTight,
|
|
274
270
|
},
|
|
275
271
|
description: {
|
|
276
|
-
color: semantics.chatTextIncoming,
|
|
272
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
277
273
|
fontSize: primitives.typographyFontSizeXs,
|
|
278
274
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
279
275
|
lineHeight: primitives.typographyLineHeightTight,
|
|
@@ -284,7 +280,7 @@ const useStyles = () => {
|
|
|
284
280
|
gap: primitives.spacingXxs,
|
|
285
281
|
},
|
|
286
282
|
linkPreviewText: {
|
|
287
|
-
color: semantics.chatTextIncoming,
|
|
283
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
288
284
|
fontSize: primitives.typographyFontSizeXs,
|
|
289
285
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
290
286
|
lineHeight: primitives.typographyLineHeightTight,
|
|
@@ -64,10 +64,6 @@ const URLPreviewCompactWithContext = (props: URLPreviewCompactPropsWithContext)
|
|
|
64
64
|
|
|
65
65
|
const { icons } = useComponentsContext();
|
|
66
66
|
|
|
67
|
-
const {
|
|
68
|
-
theme: { semantics },
|
|
69
|
-
} = useTheme();
|
|
70
|
-
|
|
71
67
|
const { image_url, og_scrape_url, text, thumb_url, title: titleText, type } = attachment;
|
|
72
68
|
|
|
73
69
|
const {
|
|
@@ -158,7 +154,7 @@ const URLPreviewCompactWithContext = (props: URLPreviewCompactPropsWithContext)
|
|
|
158
154
|
</Text>
|
|
159
155
|
) : null}
|
|
160
156
|
<View style={[styles.linkPreview, linkPreview, stylesProp.linkPreview]}>
|
|
161
|
-
<icons.Link height={12} width={12} stroke={
|
|
157
|
+
<icons.Link height={12} width={12} stroke={styles.linkPreviewText.color} />
|
|
162
158
|
<Text
|
|
163
159
|
numberOfLines={1}
|
|
164
160
|
style={[styles.linkPreviewText, linkPreviewText, stylesProp.linkPreviewText]}
|
|
@@ -271,13 +267,13 @@ const useStyles = () => {
|
|
|
271
267
|
flexShrink: 1,
|
|
272
268
|
},
|
|
273
269
|
title: {
|
|
274
|
-
color: semantics.chatTextIncoming,
|
|
270
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
275
271
|
fontSize: primitives.typographyFontSizeSm,
|
|
276
272
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
277
273
|
lineHeight: primitives.typographyLineHeightTight,
|
|
278
274
|
},
|
|
279
275
|
description: {
|
|
280
|
-
color: semantics.chatTextIncoming,
|
|
276
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
281
277
|
fontSize: primitives.typographyFontSizeXs,
|
|
282
278
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
283
279
|
lineHeight: primitives.typographyLineHeightTight,
|
|
@@ -288,7 +284,7 @@ const useStyles = () => {
|
|
|
288
284
|
gap: primitives.spacingXxs,
|
|
289
285
|
},
|
|
290
286
|
linkPreviewText: {
|
|
291
|
-
color: semantics.chatTextIncoming,
|
|
287
|
+
color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
292
288
|
fontSize: primitives.typographyFontSizeXs,
|
|
293
289
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
294
290
|
lineHeight: primitives.typographyLineHeightTight,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { cleanup, render, screen, waitFor } from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import type { MessageContextValue } from '../../../../contexts/messageContext/MessageContext';
|
|
7
|
+
import { MessageProvider } from '../../../../contexts/messageContext/MessageContext';
|
|
8
|
+
import type { MessagesContextValue } from '../../../../contexts/messagesContext/MessagesContext';
|
|
9
|
+
import { MessagesProvider } from '../../../../contexts/messagesContext/MessagesContext';
|
|
10
|
+
import { mergeThemes, ThemeProvider } from '../../../../contexts/themeContext/ThemeContext';
|
|
11
|
+
import { generateCardAttachment } from '../../../../mock-builders/generator/attachment';
|
|
12
|
+
import { generateMessage } from '../../../../mock-builders/generator/message';
|
|
13
|
+
import { URLPreview } from '../URLPreview';
|
|
14
|
+
import { URLPreviewCompact } from '../URLPreviewCompact';
|
|
15
|
+
|
|
16
|
+
const lightTheme = mergeThemes({ scheme: 'light' });
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Both URL preview variants resolve every text colour from the same
|
|
20
|
+
* incoming/outgoing pair, so the assertions are shared.
|
|
21
|
+
*/
|
|
22
|
+
describe.each([
|
|
23
|
+
['URLPreview', URLPreview],
|
|
24
|
+
['URLPreviewCompact', URLPreviewCompact],
|
|
25
|
+
])('%s chat text side', (_name, Component) => {
|
|
26
|
+
const renderPreview = (messageContextValue: Partial<MessageContextValue> = {}) => {
|
|
27
|
+
const attachment = generateCardAttachment({ title: 'A title', text: 'A description' });
|
|
28
|
+
|
|
29
|
+
return render(
|
|
30
|
+
<ThemeProvider>
|
|
31
|
+
<MessagesProvider value={{} as unknown as MessagesContextValue}>
|
|
32
|
+
<MessageProvider
|
|
33
|
+
value={
|
|
34
|
+
{
|
|
35
|
+
message: generateMessage(),
|
|
36
|
+
...messageContextValue,
|
|
37
|
+
} as unknown as MessageContextValue
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
<Component attachment={attachment} />
|
|
41
|
+
</MessageProvider>
|
|
42
|
+
</MessagesProvider>
|
|
43
|
+
</ThemeProvider>,
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const colorOf = (text: string) => StyleSheet.flatten(screen.getByText(text).props.style)?.color;
|
|
48
|
+
|
|
49
|
+
afterEach(cleanup);
|
|
50
|
+
|
|
51
|
+
it('uses the outgoing chat text colour on our own message', async () => {
|
|
52
|
+
renderPreview({ isMyMessage: true });
|
|
53
|
+
|
|
54
|
+
await waitFor(() => {
|
|
55
|
+
expect(colorOf('A title')).toBe(lightTheme.semantics.chatTextOutgoing);
|
|
56
|
+
});
|
|
57
|
+
expect(colorOf('A description')).toBe(lightTheme.semantics.chatTextOutgoing);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("uses the incoming chat text colour on another user's message", async () => {
|
|
61
|
+
renderPreview({ isMyMessage: false });
|
|
62
|
+
|
|
63
|
+
await waitFor(() => {
|
|
64
|
+
expect(colorOf('A title')).toBe(lightTheme.semantics.chatTextIncoming);
|
|
65
|
+
});
|
|
66
|
+
expect(colorOf('A description')).toBe(lightTheme.semantics.chatTextIncoming);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('uses the outgoing attachment background on our own message', async () => {
|
|
70
|
+
renderPreview({ isMyMessage: true });
|
|
71
|
+
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(StyleSheet.flatten(screen.getByTestId('card-attachment').props.style)).toEqual(
|
|
74
|
+
expect.objectContaining({
|
|
75
|
+
backgroundColor: lightTheme.semantics.chatBgAttachmentOutgoing,
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('strokes the link icon with the same colour as the link text', async () => {
|
|
82
|
+
renderPreview({ isMyMessage: true });
|
|
83
|
+
|
|
84
|
+
// The URL text and its leading icon must not disagree on the side.
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(screen.getByTestId('card-attachment')).toBeTruthy();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const linkIcon = screen.UNSAFE_getByProps({ stroke: lightTheme.semantics.chatTextOutgoing });
|
|
90
|
+
expect(linkIcon).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -218,6 +218,54 @@ describe('Attachment', () => {
|
|
|
218
218
|
isSoundPackageAvailable.mockReturnValue(false);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
+
it('uses the outgoing audio player background on our own message', async () => {
|
|
222
|
+
const { isSoundPackageAvailable } = require('../../../native');
|
|
223
|
+
isSoundPackageAvailable.mockReturnValue(true);
|
|
224
|
+
const attachment = generateAudioAttachment({ duration: 10, waveform_data: [0.2, 0.6] });
|
|
225
|
+
const message = generateMessage({ attachments: [attachment, generateAudioAttachment()] });
|
|
226
|
+
|
|
227
|
+
const { getByLabelText } = render(
|
|
228
|
+
getAttachmentComponent(
|
|
229
|
+
{ attachment },
|
|
230
|
+
{ isMyMessage: true, message, messageHasOnlySingleAttachment: false },
|
|
231
|
+
),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
const style = StyleSheet.flatten(getByLabelText('audio-attachment-preview').props.style);
|
|
236
|
+
expect(style.backgroundColor).toBe(lightTheme.semantics.chatBgAttachmentOutgoing);
|
|
237
|
+
});
|
|
238
|
+
isSoundPackageAvailable.mockReturnValue(false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('resolves the audio duration label colour from the message side', async () => {
|
|
242
|
+
const { isSoundPackageAvailable } = require('../../../native');
|
|
243
|
+
isSoundPackageAvailable.mockReturnValue(true);
|
|
244
|
+
const attachment = generateAudioAttachment({ duration: 10, waveform_data: [0.2, 0.6] });
|
|
245
|
+
const message = generateMessage({ attachments: [attachment] });
|
|
246
|
+
|
|
247
|
+
const { getByLabelText } = render(
|
|
248
|
+
getAttachmentComponent(
|
|
249
|
+
{ attachment },
|
|
250
|
+
{ isMyMessage: true, message, messageHasOnlySingleAttachment: false },
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// NOTE: `StableDurationLabel` applies its own `visibleStyle` last, which
|
|
255
|
+
// always sets `color` from the playback state - so this asserts the wiring
|
|
256
|
+
// reaching the label, not the final rendered pixel. See the reserve label.
|
|
257
|
+
await waitFor(() => {
|
|
258
|
+
expect(getByLabelText('Progress Duration').props.style).toEqual(
|
|
259
|
+
expect.arrayContaining([
|
|
260
|
+
expect.arrayContaining([
|
|
261
|
+
expect.objectContaining({ color: lightTheme.semantics.chatTextOutgoing }),
|
|
262
|
+
]),
|
|
263
|
+
]),
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
isSoundPackageAvailable.mockReturnValue(false);
|
|
267
|
+
});
|
|
268
|
+
|
|
221
269
|
it('should render UrlPreview component if attachment has title_link or og_scrape_url', async () => {
|
|
222
270
|
const attachment = generateImageAttachment({
|
|
223
271
|
og_scrape_url: uuidv4(),
|
|
@@ -171,7 +171,6 @@ const MessageItemViewWithContext = (props: MessageItemViewPropsWithContext) => {
|
|
|
171
171
|
|
|
172
172
|
const {
|
|
173
173
|
isMessageErrorType,
|
|
174
|
-
isMessageReceivedOrErrorType,
|
|
175
174
|
isMessageTypeDeleted,
|
|
176
175
|
isVeryLastMessage,
|
|
177
176
|
messageGroupedSingleOrBottom,
|
|
@@ -200,7 +199,7 @@ const MessageItemViewWithContext = (props: MessageItemViewPropsWithContext) => {
|
|
|
200
199
|
backgroundColor = 'transparent';
|
|
201
200
|
} else if (hasStandaloneGiphyOrImgur) {
|
|
202
201
|
backgroundColor = 'transparent';
|
|
203
|
-
} else if (
|
|
202
|
+
} else if (!isMyMessage) {
|
|
204
203
|
backgroundColor = semantics.chatBgIncoming;
|
|
205
204
|
}
|
|
206
205
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Text } from 'react-native';
|
|
2
|
+
import { StyleSheet, Text } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { cleanup, render, waitFor } from '@testing-library/react-native';
|
|
5
5
|
|
|
6
6
|
import { WithComponents } from '../../../../contexts/componentsContext/ComponentsContext';
|
|
7
7
|
import { OverlayProvider } from '../../../../contexts/overlayContext/OverlayProvider';
|
|
8
|
-
import { ThemeProvider } from '../../../../contexts/themeContext/ThemeContext';
|
|
8
|
+
import { mergeThemes, ThemeProvider } from '../../../../contexts/themeContext/ThemeContext';
|
|
9
9
|
import { defaultTheme } from '../../../../contexts/themeContext/utils/theme';
|
|
10
10
|
import { getOrCreateChannelApi } from '../../../../mock-builders/api/getOrCreateChannel';
|
|
11
11
|
import { useMockedApis } from '../../../../mock-builders/api/useMockedApis';
|
|
@@ -22,6 +22,8 @@ import { MessageList } from '../../../MessageList/MessageList';
|
|
|
22
22
|
import { MessageTextContainer } from '../MessageTextContainer';
|
|
23
23
|
|
|
24
24
|
describe('MessageTextContainer', () => {
|
|
25
|
+
const lightTheme = mergeThemes({ scheme: 'light' });
|
|
26
|
+
|
|
25
27
|
afterEach(cleanup);
|
|
26
28
|
|
|
27
29
|
it('should render message text container', async () => {
|
|
@@ -110,4 +112,20 @@ describe('MessageTextContainer', () => {
|
|
|
110
112
|
expect(getByText(message.i18n.no_text)).toBeTruthy();
|
|
111
113
|
});
|
|
112
114
|
});
|
|
115
|
+
|
|
116
|
+
it('renders our own message text with the outgoing chat text colour', async () => {
|
|
117
|
+
const message = generateMessage({ user: generateStaticUser(1) });
|
|
118
|
+
|
|
119
|
+
const { getByText } = render(
|
|
120
|
+
<ThemeProvider>
|
|
121
|
+
<MessageTextContainer isMyMessage message={message} />
|
|
122
|
+
</ThemeProvider>,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await waitFor(() => {
|
|
126
|
+
expect(StyleSheet.flatten(getByText(message.text as string).props.style)?.color).toBe(
|
|
127
|
+
lightTheme.semantics.chatTextOutgoing,
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
113
131
|
});
|
package/src/components/Message/MessageItemView/__tests__/__snapshots__/MessageAuthor.test.tsx.snap
CHANGED
|
@@ -22,10 +22,10 @@ exports[`MessageAuthor should render message author 1`] = `
|
|
|
22
22
|
"width": 32,
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
|
-
"backgroundColor": "#
|
|
25
|
+
"backgroundColor": "#a9e4ea",
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
"borderColor": "rgba(
|
|
28
|
+
"borderColor": "rgba(26, 27, 37, 0.1)",
|
|
29
29
|
"borderWidth": 1,
|
|
30
30
|
},
|
|
31
31
|
undefined,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ScrollViewProps,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
View,
|
|
7
|
+
useColorScheme,
|
|
7
8
|
ViewabilityConfig,
|
|
8
9
|
ViewToken,
|
|
9
10
|
} from 'react-native';
|
|
@@ -401,11 +402,12 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
|
|
|
401
402
|
const styles = useStyles();
|
|
402
403
|
|
|
403
404
|
const myMessageThemeString = useMemo(() => JSON.stringify(myMessageTheme), [myMessageTheme]);
|
|
405
|
+
const scheme = useColorScheme();
|
|
404
406
|
|
|
405
407
|
const modifiedTheme = useMemo(
|
|
406
|
-
() => mergeThemes({ style: myMessageTheme, theme }),
|
|
408
|
+
() => mergeThemes({ scheme, style: myMessageTheme, theme }),
|
|
407
409
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
408
|
-
[myMessageThemeString, theme],
|
|
410
|
+
[myMessageThemeString, scheme, theme],
|
|
409
411
|
);
|
|
410
412
|
|
|
411
413
|
const { processedMessageList, rawMessageList, viewabilityChangedCallback } = useMessageList({
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ScrollViewProps,
|
|
8
8
|
StyleSheet,
|
|
9
9
|
View,
|
|
10
|
+
useColorScheme,
|
|
10
11
|
ViewabilityConfig,
|
|
11
12
|
ViewToken,
|
|
12
13
|
} from 'react-native';
|
|
@@ -394,11 +395,12 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
|
|
|
394
395
|
});
|
|
395
396
|
|
|
396
397
|
const myMessageThemeString = useMemo(() => JSON.stringify(myMessageTheme), [myMessageTheme]);
|
|
398
|
+
const scheme = useColorScheme();
|
|
397
399
|
|
|
398
400
|
const modifiedTheme = useMemo(
|
|
399
|
-
() => mergeThemes({ style: myMessageTheme, theme }),
|
|
401
|
+
() => mergeThemes({ scheme, style: myMessageTheme, theme }),
|
|
400
402
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
401
|
-
[myMessageThemeString, theme],
|
|
403
|
+
[myMessageThemeString, scheme, theme],
|
|
402
404
|
);
|
|
403
405
|
|
|
404
406
|
/**
|
|
@@ -6,6 +6,7 @@ import { PollOption as PollOptionClass } from 'stream-chat';
|
|
|
6
6
|
import { PollOption, ShowAllOptionsButton } from './components';
|
|
7
7
|
import { PollUIStateProvider } from './contexts/PollUIStateContext';
|
|
8
8
|
|
|
9
|
+
import { useIsPollCreatedByCurrentUser } from './hook/useIsPollCreatedByCurrentUser';
|
|
9
10
|
import { usePollState } from './hooks/usePollState';
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -105,6 +106,7 @@ const useStyles = () => {
|
|
|
105
106
|
const {
|
|
106
107
|
theme: { semantics },
|
|
107
108
|
} = useTheme();
|
|
109
|
+
const isPollCreatedByClient = useIsPollCreatedByCurrentUser();
|
|
108
110
|
return useMemo(() => {
|
|
109
111
|
return StyleSheet.create({
|
|
110
112
|
container: {
|
|
@@ -114,14 +116,14 @@ const useStyles = () => {
|
|
|
114
116
|
},
|
|
115
117
|
headerContainer: { gap: primitives.spacingXxs },
|
|
116
118
|
headerSubtitle: {
|
|
117
|
-
color: semantics.chatTextIncoming,
|
|
119
|
+
color: isPollCreatedByClient ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
118
120
|
fontSize: primitives.typographyFontSizeSm,
|
|
119
121
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
120
122
|
lineHeight: primitives.typographyLineHeightTight,
|
|
121
123
|
textAlign: 'left',
|
|
122
124
|
},
|
|
123
125
|
headerTitle: {
|
|
124
|
-
color: semantics.chatTextIncoming,
|
|
126
|
+
color: isPollCreatedByClient ? semantics.chatTextOutgoing : semantics.chatTextIncoming,
|
|
125
127
|
fontSize: primitives.typographyFontSizeMd,
|
|
126
128
|
fontWeight: primitives.typographyFontWeightSemiBold,
|
|
127
129
|
lineHeight: primitives.typographyLineHeightNormal,
|
|
@@ -131,5 +133,5 @@ const useStyles = () => {
|
|
|
131
133
|
gap: primitives.spacingMd,
|
|
132
134
|
},
|
|
133
135
|
});
|
|
134
|
-
}, [semantics]);
|
|
136
|
+
}, [isPollCreatedByClient, semantics]);
|
|
135
137
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { render, screen } from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
OwnCapabilitiesProvider,
|
|
8
|
+
PollContextProvider,
|
|
9
|
+
ThemeProvider,
|
|
10
|
+
TranslationProvider,
|
|
11
|
+
} from '../../../contexts';
|
|
12
|
+
import { mergeThemes } from '../../../contexts/themeContext/ThemeContext';
|
|
13
|
+
import { generateMessage } from '../../../mock-builders/generator/message';
|
|
14
|
+
import { PollHeader } from '../Poll';
|
|
15
|
+
|
|
16
|
+
const mockIsPollCreatedByCurrentUser = jest.fn();
|
|
17
|
+
|
|
18
|
+
jest.mock('../hook/useIsPollCreatedByCurrentUser', () => ({
|
|
19
|
+
useIsPollCreatedByCurrentUser: () => mockIsPollCreatedByCurrentUser(),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('../hooks/usePollState', () => ({
|
|
23
|
+
usePollState: () => ({
|
|
24
|
+
enforceUniqueVote: true,
|
|
25
|
+
isClosed: false,
|
|
26
|
+
maxVotesAllowed: undefined,
|
|
27
|
+
name: 'A poll question',
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
describe('PollHeader chat text side', () => {
|
|
32
|
+
const lightTheme = mergeThemes({ scheme: 'light' });
|
|
33
|
+
|
|
34
|
+
const renderHeader = () =>
|
|
35
|
+
render(
|
|
36
|
+
<ThemeProvider>
|
|
37
|
+
<TranslationProvider value={{ t: (key: string) => key } as never}>
|
|
38
|
+
<OwnCapabilitiesProvider value={{} as never}>
|
|
39
|
+
<PollContextProvider
|
|
40
|
+
value={{ message: generateMessage(), poll: { data: {} } as never }}
|
|
41
|
+
>
|
|
42
|
+
<PollHeader />
|
|
43
|
+
</PollContextProvider>
|
|
44
|
+
</OwnCapabilitiesProvider>
|
|
45
|
+
</TranslationProvider>
|
|
46
|
+
</ThemeProvider>,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const colorOf = (text: string) => StyleSheet.flatten(screen.getByText(text).props.style)?.color;
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
mockIsPollCreatedByCurrentUser.mockReset();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('uses the outgoing chat text colour for our own poll', () => {
|
|
56
|
+
mockIsPollCreatedByCurrentUser.mockReturnValue(true);
|
|
57
|
+
renderHeader();
|
|
58
|
+
|
|
59
|
+
expect(colorOf('A poll question')).toBe(lightTheme.semantics.chatTextOutgoing);
|
|
60
|
+
expect(colorOf('Select one')).toBe(lightTheme.semantics.chatTextOutgoing);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("uses the incoming chat text colour for another user's poll", () => {
|
|
64
|
+
mockIsPollCreatedByCurrentUser.mockReturnValue(false);
|
|
65
|
+
renderHeader();
|
|
66
|
+
|
|
67
|
+
expect(colorOf('A poll question')).toBe(lightTheme.semantics.chatTextIncoming);
|
|
68
|
+
expect(colorOf('Select one')).toBe(lightTheme.semantics.chatTextIncoming);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -10,6 +10,11 @@ export type PollButtonProps = {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export type PollVoteButtonProps = {
|
|
13
|
+
/**
|
|
14
|
+
* Render with the incoming chat tokens regardless of poll ownership. Set on
|
|
15
|
+
* surfaces that are not a message bubble, such as the full-options list.
|
|
16
|
+
*/
|
|
17
|
+
forceIncoming?: boolean;
|
|
13
18
|
option: PollOption;
|
|
14
19
|
style?: StyleProp<ViewStyle>;
|
|
15
20
|
} & Pick<PollButtonProps, 'onPress'>;
|
|
@@ -247,6 +247,8 @@ const useStyles = () => {
|
|
|
247
247
|
? semantics.chatBorderOnChatOutgoing
|
|
248
248
|
: semantics.chatBorderOnChatIncoming,
|
|
249
249
|
},
|
|
250
|
+
// NOTE: PollButtons only ever renders on the in-bubble surface (it is what
|
|
251
|
+
// opens the full-options modal), so it needs no `forceIncoming` handling.
|
|
250
252
|
});
|
|
251
253
|
}, [semantics, isPollCreatedByClient]);
|
|
252
254
|
};
|
|
@@ -92,7 +92,11 @@ export const PollAllOptions = ({
|
|
|
92
92
|
export const PollOption = ({ option, showProgressBar = true, forceIncoming }: PollOptionProps) => {
|
|
93
93
|
const { latestVotesByOption, voteCountsByOption, voteCount } = usePollState();
|
|
94
94
|
const { t } = useTranslationContext();
|
|
95
|
-
const
|
|
95
|
+
const isPollCreatedByClient = useIsPollCreatedByCurrentUser();
|
|
96
|
+
// `forceIncoming` is set on the full-options surface, which is a neutral card
|
|
97
|
+
// rather than a bubble - so it wins over poll ownership.
|
|
98
|
+
const isIncoming = !isPollCreatedByClient || !!forceIncoming;
|
|
99
|
+
const styles = useStyles({ isIncoming });
|
|
96
100
|
|
|
97
101
|
const relevantVotes = useMemo(
|
|
98
102
|
() => latestVotesByOption?.[option.id] || [],
|
|
@@ -111,21 +115,17 @@ export const PollOption = ({ option, showProgressBar = true, forceIncoming }: Po
|
|
|
111
115
|
semantics,
|
|
112
116
|
},
|
|
113
117
|
} = useTheme();
|
|
114
|
-
const
|
|
118
|
+
const unFilledColor = isIncoming
|
|
119
|
+
? semantics.chatPollProgressTrackIncoming
|
|
120
|
+
: semantics.chatPollProgressTrackOutgoing;
|
|
115
121
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
: semantics.chatPollProgressTrackIncoming;
|
|
120
|
-
|
|
121
|
-
const filledColor =
|
|
122
|
-
isPollCreatedByClient && !forceIncoming
|
|
123
|
-
? semantics.chatPollProgressFillOutgoing
|
|
124
|
-
: semantics.chatPollProgressFillIncoming;
|
|
122
|
+
const filledColor = isIncoming
|
|
123
|
+
? semantics.chatPollProgressFillIncoming
|
|
124
|
+
: semantics.chatPollProgressFillOutgoing;
|
|
125
125
|
|
|
126
126
|
return (
|
|
127
127
|
<View style={[styles.container, container]}>
|
|
128
|
-
<VoteButton option={option} />
|
|
128
|
+
<VoteButton forceIncoming={forceIncoming} option={option} />
|
|
129
129
|
<View style={[styles.info, info]}>
|
|
130
130
|
<View style={[styles.header, header]}>
|
|
131
131
|
<Text ellipsizeMode='tail' numberOfLines={2} style={[styles.text, text]}>
|
|
@@ -163,7 +163,7 @@ export const PollOption = ({ option, showProgressBar = true, forceIncoming }: Po
|
|
|
163
163
|
);
|
|
164
164
|
};
|
|
165
165
|
|
|
166
|
-
export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => {
|
|
166
|
+
export const VoteButton = ({ forceIncoming, onPress, option }: PollVoteButtonProps) => {
|
|
167
167
|
const { icons } = useComponentsContext();
|
|
168
168
|
const { message, poll } = usePollContext();
|
|
169
169
|
const { isClosed, ownVotesByOptionId } = usePollState();
|
|
@@ -172,7 +172,8 @@ export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => {
|
|
|
172
172
|
theme: { semantics },
|
|
173
173
|
} = useTheme();
|
|
174
174
|
const isPollCreatedByClient = useIsPollCreatedByCurrentUser();
|
|
175
|
-
const
|
|
175
|
+
const isIncoming = !isPollCreatedByClient || !!forceIncoming;
|
|
176
|
+
const styles = useStyles({ isIncoming });
|
|
176
177
|
|
|
177
178
|
const {
|
|
178
179
|
theme: {
|
|
@@ -212,9 +213,9 @@ export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => {
|
|
|
212
213
|
{
|
|
213
214
|
borderWidth: hasVote ? 0 : 1,
|
|
214
215
|
backgroundColor: hasVote ? semantics.accentPrimary : 'transparent',
|
|
215
|
-
borderColor:
|
|
216
|
-
? semantics.
|
|
217
|
-
: semantics.
|
|
216
|
+
borderColor: isIncoming
|
|
217
|
+
? semantics.chatBorderOnChatIncoming
|
|
218
|
+
: semantics.chatBorderOnChatOutgoing,
|
|
218
219
|
},
|
|
219
220
|
voteButtonContainer,
|
|
220
221
|
]}
|
|
@@ -224,7 +225,7 @@ export const VoteButton = ({ onPress, option }: PollVoteButtonProps) => {
|
|
|
224
225
|
) : null;
|
|
225
226
|
};
|
|
226
227
|
|
|
227
|
-
const useStyles = () => {
|
|
228
|
+
const useStyles = ({ isIncoming }: { isIncoming: boolean }) => {
|
|
228
229
|
const {
|
|
229
230
|
theme: { semantics },
|
|
230
231
|
} = useTheme();
|
|
@@ -242,7 +243,7 @@ const useStyles = () => {
|
|
|
242
243
|
minWidth: 0,
|
|
243
244
|
},
|
|
244
245
|
text: {
|
|
245
|
-
color: semantics.chatTextIncoming,
|
|
246
|
+
color: isIncoming ? semantics.chatTextIncoming : semantics.chatTextOutgoing,
|
|
246
247
|
flexGrow: 1,
|
|
247
248
|
flexShrink: 1,
|
|
248
249
|
fontSize: primitives.typographyFontSizeSm,
|
|
@@ -264,7 +265,7 @@ const useStyles = () => {
|
|
|
264
265
|
minHeight: 20,
|
|
265
266
|
},
|
|
266
267
|
votesText: {
|
|
267
|
-
color: semantics.chatTextIncoming,
|
|
268
|
+
color: isIncoming ? semantics.chatTextIncoming : semantics.chatTextOutgoing,
|
|
268
269
|
fontSize: primitives.typographyFontSizeXs,
|
|
269
270
|
fontWeight: primitives.typographyFontWeightRegular,
|
|
270
271
|
lineHeight: primitives.typographyLineHeightTight,
|
|
@@ -281,7 +282,7 @@ const useStyles = () => {
|
|
|
281
282
|
width: 24,
|
|
282
283
|
},
|
|
283
284
|
});
|
|
284
|
-
}, [semantics]);
|
|
285
|
+
}, [isIncoming, semantics]);
|
|
285
286
|
};
|
|
286
287
|
|
|
287
288
|
const useAllOptionStyles = () => {
|