stream-chat-react-native-core 9.0.0-beta.10 → 9.0.0-beta.11
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 +28 -15
- package/lib/commonjs/components/Attachment/Audio/AudioAttachment.js.map +1 -1
- package/lib/commonjs/components/Attachment/Gallery.js +13 -17
- package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
- package/lib/commonjs/components/Attachment/Giphy/Giphy.js +21 -23
- package/lib/commonjs/components/Attachment/Giphy/Giphy.js.map +1 -1
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerContent.js +8 -1
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerContent.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelSwipableWrapper.js +5 -5
- package/lib/commonjs/components/ChannelPreview/ChannelSwipableWrapper.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js +9 -14
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +20 -7
- package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/EmojiPickerList.js +1 -1
- package/lib/commonjs/components/MessageMenu/EmojiPickerList.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/MessageUserReactions.js +8 -9
- package/lib/commonjs/components/MessageMenu/MessageUserReactions.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js +3 -3
- package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/hooks/useFetchReactions.js +80 -25
- package/lib/commonjs/components/MessageMenu/hooks/useFetchReactions.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js +1 -0
- package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollButtons.js +2 -0
- package/lib/commonjs/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollInputDialog.js +2 -1
- package/lib/commonjs/components/Poll/components/PollInputDialog.js.map +1 -1
- package/lib/commonjs/components/ProgressControl/StableDurationLabel.js +45 -0
- package/lib/commonjs/components/ProgressControl/StableDurationLabel.js.map +1 -0
- package/lib/commonjs/components/ProgressControl/WaveProgressBar.js +127 -40
- package/lib/commonjs/components/ProgressControl/WaveProgressBar.js.map +1 -1
- package/lib/commonjs/components/Reply/Reply.js +3 -0
- package/lib/commonjs/components/Reply/Reply.js.map +1 -1
- package/lib/commonjs/components/UIComponents/BottomSheetModal.js +4 -4
- package/lib/commonjs/components/UIComponents/BottomSheetModal.js.map +1 -1
- package/lib/commonjs/i18n/en.json +2 -0
- package/lib/commonjs/i18n/es.json +2 -0
- package/lib/commonjs/i18n/fr.json +2 -0
- package/lib/commonjs/i18n/he.json +2 -0
- package/lib/commonjs/i18n/hi.json +2 -0
- package/lib/commonjs/i18n/it.json +2 -0
- package/lib/commonjs/i18n/ja.json +2 -0
- package/lib/commonjs/i18n/ko.json +2 -0
- package/lib/commonjs/i18n/nl.json +2 -0
- package/lib/commonjs/i18n/pt-br.json +2 -0
- package/lib/commonjs/i18n/ru.json +2 -0
- package/lib/commonjs/i18n/tr.json +2 -0
- package/lib/commonjs/middlewares/attachments.js +5 -4
- package/lib/commonjs/middlewares/attachments.js.map +1 -1
- package/lib/commonjs/state-store/audio-player.js +29 -17
- package/lib/commonjs/state-store/audio-player.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/Audio/AudioAttachment.js +28 -15
- package/lib/module/components/Attachment/Audio/AudioAttachment.js.map +1 -1
- package/lib/module/components/Attachment/Gallery.js +13 -17
- package/lib/module/components/Attachment/Gallery.js.map +1 -1
- package/lib/module/components/Attachment/Giphy/Giphy.js +21 -23
- package/lib/module/components/Attachment/Giphy/Giphy.js.map +1 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentPickerContent.js +8 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentPickerContent.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelSwipableWrapper.js +5 -5
- package/lib/module/components/ChannelPreview/ChannelSwipableWrapper.js.map +1 -1
- package/lib/module/components/Message/Message.js +9 -14
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +20 -7
- package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
- package/lib/module/components/MessageMenu/EmojiPickerList.js +1 -1
- package/lib/module/components/MessageMenu/EmojiPickerList.js.map +1 -1
- package/lib/module/components/MessageMenu/MessageUserReactions.js +8 -9
- package/lib/module/components/MessageMenu/MessageUserReactions.js.map +1 -1
- package/lib/module/components/MessageMenu/MessageUserReactionsItem.js +3 -3
- package/lib/module/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
- package/lib/module/components/MessageMenu/hooks/useFetchReactions.js +80 -25
- package/lib/module/components/MessageMenu/hooks/useFetchReactions.js.map +1 -1
- package/lib/module/components/Poll/components/PollAnswersList.js +1 -0
- package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/module/components/Poll/components/PollButtons.js +2 -0
- package/lib/module/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/module/components/Poll/components/PollInputDialog.js +2 -1
- package/lib/module/components/Poll/components/PollInputDialog.js.map +1 -1
- package/lib/module/components/ProgressControl/StableDurationLabel.js +45 -0
- package/lib/module/components/ProgressControl/StableDurationLabel.js.map +1 -0
- package/lib/module/components/ProgressControl/WaveProgressBar.js +127 -40
- package/lib/module/components/ProgressControl/WaveProgressBar.js.map +1 -1
- package/lib/module/components/Reply/Reply.js +3 -0
- package/lib/module/components/Reply/Reply.js.map +1 -1
- package/lib/module/components/UIComponents/BottomSheetModal.js +4 -4
- package/lib/module/components/UIComponents/BottomSheetModal.js.map +1 -1
- package/lib/module/i18n/en.json +2 -0
- package/lib/module/i18n/es.json +2 -0
- package/lib/module/i18n/fr.json +2 -0
- package/lib/module/i18n/he.json +2 -0
- package/lib/module/i18n/hi.json +2 -0
- package/lib/module/i18n/it.json +2 -0
- package/lib/module/i18n/ja.json +2 -0
- package/lib/module/i18n/ko.json +2 -0
- package/lib/module/i18n/nl.json +2 -0
- package/lib/module/i18n/pt-br.json +2 -0
- package/lib/module/i18n/ru.json +2 -0
- package/lib/module/i18n/tr.json +2 -0
- package/lib/module/middlewares/attachments.js +5 -4
- package/lib/module/middlewares/attachments.js.map +1 -1
- package/lib/module/state-store/audio-player.js +29 -17
- package/lib/module/state-store/audio-player.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/Attachment/Gallery.d.ts.map +1 -1
- package/lib/typescript/components/Attachment/Giphy/Giphy.d.ts.map +1 -1
- package/lib/typescript/components/AttachmentPicker/components/AttachmentPickerContent.d.ts +1 -0
- package/lib/typescript/components/AttachmentPicker/components/AttachmentPickerContent.d.ts.map +1 -1
- package/lib/typescript/components/Message/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
- package/lib/typescript/components/MessageMenu/MessageUserReactions.d.ts.map +1 -1
- package/lib/typescript/components/MessageMenu/hooks/useFetchReactions.d.ts +15 -0
- package/lib/typescript/components/MessageMenu/hooks/useFetchReactions.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollAnswersList.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollButtons.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollInputDialog.d.ts +2 -1
- package/lib/typescript/components/Poll/components/PollInputDialog.d.ts.map +1 -1
- package/lib/typescript/components/ProgressControl/StableDurationLabel.d.ts +19 -0
- package/lib/typescript/components/ProgressControl/StableDurationLabel.d.ts.map +1 -0
- package/lib/typescript/components/ProgressControl/WaveProgressBar.d.ts.map +1 -1
- package/lib/typescript/components/Reply/Reply.d.ts.map +1 -1
- package/lib/typescript/components/UIComponents/BottomSheetModal.d.ts.map +1 -1
- package/lib/typescript/i18n/en.json +2 -0
- package/lib/typescript/i18n/es.json +2 -0
- package/lib/typescript/i18n/fr.json +2 -0
- package/lib/typescript/i18n/he.json +2 -0
- package/lib/typescript/i18n/hi.json +2 -0
- package/lib/typescript/i18n/it.json +2 -0
- package/lib/typescript/i18n/ja.json +2 -0
- package/lib/typescript/i18n/ko.json +2 -0
- package/lib/typescript/i18n/nl.json +2 -0
- package/lib/typescript/i18n/pt-br.json +2 -0
- package/lib/typescript/i18n/ru.json +2 -0
- package/lib/typescript/i18n/tr.json +2 -0
- package/lib/typescript/middlewares/attachments.d.ts +2 -1
- package/lib/typescript/middlewares/attachments.d.ts.map +1 -1
- package/lib/typescript/state-store/audio-player.d.ts +3 -2
- package/lib/typescript/state-store/audio-player.d.ts.map +1 -1
- package/lib/typescript/utils/i18n/Streami18n.d.ts +2 -0
- package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Attachment/Audio/AudioAttachment.tsx +33 -27
- package/src/components/Attachment/Gallery.tsx +1 -8
- package/src/components/Attachment/Giphy/Giphy.tsx +25 -39
- package/src/components/AttachmentPicker/components/AttachmentPickerContent.tsx +1 -0
- package/src/components/ChannelPreview/ChannelSwipableWrapper.tsx +3 -3
- package/src/components/Message/Message.tsx +1 -3
- package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx +27 -19
- package/src/components/MessageMenu/EmojiPickerList.tsx +1 -1
- package/src/components/MessageMenu/MessageUserReactions.tsx +8 -11
- package/src/components/MessageMenu/MessageUserReactionsItem.tsx +3 -3
- package/src/components/MessageMenu/__tests__/MessageUserReactions.test.tsx +4 -3
- package/src/components/MessageMenu/hooks/__tests__/useFetchReactions.test.ts +56 -0
- package/src/components/MessageMenu/hooks/useFetchReactions.ts +96 -11
- package/src/components/Poll/components/PollAnswersList.tsx +1 -0
- package/src/components/Poll/components/PollButtons.tsx +2 -0
- package/src/components/Poll/components/PollInputDialog.tsx +3 -1
- package/src/components/ProgressControl/StableDurationLabel.tsx +63 -0
- package/src/components/ProgressControl/WaveProgressBar.tsx +179 -60
- package/src/components/Reply/Reply.tsx +4 -0
- package/src/components/UIComponents/BottomSheetModal.tsx +3 -3
- package/src/i18n/en.json +2 -0
- package/src/i18n/es.json +2 -0
- package/src/i18n/fr.json +2 -0
- package/src/i18n/he.json +2 -0
- package/src/i18n/hi.json +2 -0
- package/src/i18n/it.json +2 -0
- package/src/i18n/ja.json +2 -0
- package/src/i18n/ko.json +2 -0
- package/src/i18n/nl.json +2 -0
- package/src/i18n/pt-br.json +2 -0
- package/src/i18n/ru.json +2 -0
- package/src/i18n/tr.json +2 -0
- package/src/middlewares/attachments.ts +5 -3
- package/src/state-store/__tests__/audio-player.test.ts +217 -0
- package/src/state-store/audio-player.ts +41 -21
- package/src/version.json +1 -1
|
@@ -64,10 +64,10 @@ export const MessageUserReactionsItem = ({
|
|
|
64
64
|
style={[styles.avatarContainer, avatarContainer]}
|
|
65
65
|
onPress={onPress}
|
|
66
66
|
>
|
|
67
|
-
<MessageUserReactionsAvatar reaction={reaction} size={'
|
|
67
|
+
<MessageUserReactionsAvatar reaction={reaction} size={'md'} />
|
|
68
68
|
<View style={[styles.avatarNameContainer, avatarNameContainer]}>
|
|
69
69
|
<Text numberOfLines={1} style={[styles.avatarName, avatarName]}>
|
|
70
|
-
{name}
|
|
70
|
+
{isOwnReaction ? t('You') : name}
|
|
71
71
|
</Text>
|
|
72
72
|
{isOwnReaction ? (
|
|
73
73
|
<Text numberOfLines={1} style={[styles.avatarSubtitle, null]}>
|
|
@@ -105,7 +105,7 @@ const useStyles = () => {
|
|
|
105
105
|
textAlign: 'left',
|
|
106
106
|
},
|
|
107
107
|
avatarSubtitle: {
|
|
108
|
-
fontSize: primitives.
|
|
108
|
+
fontSize: primitives.typographyFontSizeXs,
|
|
109
109
|
color: semantics.textTertiary,
|
|
110
110
|
},
|
|
111
111
|
avatarNameContainer: {
|
|
@@ -87,16 +87,16 @@ describe('MessageUserReactions when the supportedReactions are defined', () => {
|
|
|
87
87
|
|
|
88
88
|
it('renders reaction buttons', () => {
|
|
89
89
|
const { getByLabelText } = renderComponent();
|
|
90
|
-
const likeReactionButton = getByLabelText('reaction-button-like-
|
|
90
|
+
const likeReactionButton = getByLabelText('reaction-button-like-unselected');
|
|
91
91
|
expect(likeReactionButton).toBeDefined();
|
|
92
92
|
const loveReactionButton = getByLabelText('reaction-button-love-unselected');
|
|
93
93
|
expect(loveReactionButton).toBeDefined();
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it('
|
|
96
|
+
it('starts with no reaction filter selected by default', () => {
|
|
97
97
|
const { getAllByLabelText } = renderComponent();
|
|
98
98
|
const reactionButtons = getAllByLabelText(/\breaction-button[^\s]+/);
|
|
99
|
-
expect(reactionButtons[0].props.accessibilityLabel).toBe('reaction-button-like-
|
|
99
|
+
expect(reactionButtons[0].props.accessibilityLabel).toBe('reaction-button-like-unselected');
|
|
100
100
|
expect(reactionButtons[1].props.accessibilityLabel).toBe('reaction-button-love-unselected');
|
|
101
101
|
});
|
|
102
102
|
|
|
@@ -114,6 +114,7 @@ describe('MessageUserReactions when the supportedReactions are defined', () => {
|
|
|
114
114
|
const { getByText } = renderComponent();
|
|
115
115
|
const reactionItems = getByText('1 like');
|
|
116
116
|
expect(reactionItems).toBeDefined();
|
|
117
|
+
expect(getByText('2 love')).toBeDefined();
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
it('uses provided reactions when passed as a prop', () => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ReactionResponse } from 'stream-chat';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
reconcileUpdatedReactionInList,
|
|
5
|
+
removeReactionFromList,
|
|
6
|
+
upsertReactionInList,
|
|
7
|
+
} from '../useFetchReactions';
|
|
8
|
+
|
|
9
|
+
const makeReaction = (
|
|
10
|
+
params: Partial<ReactionResponse> & Pick<ReactionResponse, 'type' | 'user_id'>,
|
|
11
|
+
) =>
|
|
12
|
+
({
|
|
13
|
+
type: params.type,
|
|
14
|
+
user_id: params.user_id,
|
|
15
|
+
user: params.user ?? { id: params.user_id, name: params.user_id },
|
|
16
|
+
}) as ReactionResponse;
|
|
17
|
+
|
|
18
|
+
describe('useFetchReactions helpers', () => {
|
|
19
|
+
it('upserts reactions in the unfiltered list without duplicating the same user/type pair', () => {
|
|
20
|
+
const existing = makeReaction({ type: 'like', user_id: 'user-1' });
|
|
21
|
+
const updated = makeReaction({ type: 'like', user_id: 'user-1' });
|
|
22
|
+
|
|
23
|
+
const result = upsertReactionInList({
|
|
24
|
+
prevReactions: [existing],
|
|
25
|
+
reaction: updated,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result).toHaveLength(1);
|
|
29
|
+
expect(result[0]).toBe(updated);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('removes only the matching user/type pair in the unfiltered list', () => {
|
|
33
|
+
const likeReaction = makeReaction({ type: 'like', user_id: 'user-1' });
|
|
34
|
+
const loveReaction = makeReaction({ type: 'love', user_id: 'user-1' });
|
|
35
|
+
|
|
36
|
+
const result = removeReactionFromList({
|
|
37
|
+
prevReactions: [likeReaction, loveReaction],
|
|
38
|
+
reaction: likeReaction,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result).toEqual([loveReaction]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('removes the previous filtered reaction when an updated event moves that user to another type', () => {
|
|
45
|
+
const existing = makeReaction({ type: 'like', user_id: 'user-1' });
|
|
46
|
+
const updated = makeReaction({ type: 'love', user_id: 'user-1' });
|
|
47
|
+
|
|
48
|
+
const result = reconcileUpdatedReactionInList({
|
|
49
|
+
prevReactions: [existing],
|
|
50
|
+
reaction: updated,
|
|
51
|
+
reactionType: 'like',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(result).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -11,6 +11,81 @@ export type UseFetchReactionParams = {
|
|
|
11
11
|
sort?: ReactionSort;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
const isSameReaction = (left: ReactionResponse, right: ReactionResponse) =>
|
|
15
|
+
left.user_id === right.user_id && left.type === right.type;
|
|
16
|
+
|
|
17
|
+
export const upsertReactionInList = ({
|
|
18
|
+
prevReactions,
|
|
19
|
+
reaction,
|
|
20
|
+
reactionType,
|
|
21
|
+
}: {
|
|
22
|
+
prevReactions: ReactionResponse[];
|
|
23
|
+
reaction: ReactionResponse;
|
|
24
|
+
reactionType?: string;
|
|
25
|
+
}) => {
|
|
26
|
+
if (!reactionType) {
|
|
27
|
+
return [
|
|
28
|
+
reaction,
|
|
29
|
+
...prevReactions.filter((currentReaction) => !isSameReaction(currentReaction, reaction)),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (reaction.type !== reactionType) {
|
|
34
|
+
return prevReactions;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [
|
|
38
|
+
reaction,
|
|
39
|
+
...prevReactions.filter((currentReaction) => currentReaction.user_id !== reaction.user_id),
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const reconcileUpdatedReactionInList = ({
|
|
44
|
+
prevReactions,
|
|
45
|
+
reaction,
|
|
46
|
+
reactionType,
|
|
47
|
+
}: {
|
|
48
|
+
prevReactions: ReactionResponse[];
|
|
49
|
+
reaction: ReactionResponse;
|
|
50
|
+
reactionType?: string;
|
|
51
|
+
}) => {
|
|
52
|
+
if (!reactionType) {
|
|
53
|
+
return [
|
|
54
|
+
reaction,
|
|
55
|
+
...prevReactions.filter((currentReaction) => !isSameReaction(currentReaction, reaction)),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (reaction.type !== reactionType) {
|
|
60
|
+
return prevReactions.filter((currentReaction) => currentReaction.user_id !== reaction.user_id);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return [
|
|
64
|
+
reaction,
|
|
65
|
+
...prevReactions.filter((currentReaction) => currentReaction.user_id !== reaction.user_id),
|
|
66
|
+
];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const removeReactionFromList = ({
|
|
70
|
+
prevReactions,
|
|
71
|
+
reaction,
|
|
72
|
+
reactionType,
|
|
73
|
+
}: {
|
|
74
|
+
prevReactions: ReactionResponse[];
|
|
75
|
+
reaction: ReactionResponse;
|
|
76
|
+
reactionType?: string;
|
|
77
|
+
}) => {
|
|
78
|
+
if (!reactionType) {
|
|
79
|
+
return prevReactions.filter((currentReaction) => !isSameReaction(currentReaction, reaction));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (reaction.type !== reactionType) {
|
|
83
|
+
return prevReactions;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return prevReactions.filter((currentReaction) => currentReaction.user_id !== reaction.user_id);
|
|
87
|
+
};
|
|
88
|
+
|
|
14
89
|
export const useFetchReactions = ({
|
|
15
90
|
limit = 25,
|
|
16
91
|
message,
|
|
@@ -82,8 +157,14 @@ export const useFetchReactions = ({
|
|
|
82
157
|
client.on('reaction.new', (event) => {
|
|
83
158
|
const { reaction } = event;
|
|
84
159
|
|
|
85
|
-
if (reaction
|
|
86
|
-
setReactions((prevReactions) =>
|
|
160
|
+
if (reaction) {
|
|
161
|
+
setReactions((prevReactions) =>
|
|
162
|
+
upsertReactionInList({
|
|
163
|
+
prevReactions,
|
|
164
|
+
reaction,
|
|
165
|
+
reactionType,
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
87
168
|
}
|
|
88
169
|
}),
|
|
89
170
|
);
|
|
@@ -93,13 +174,13 @@ export const useFetchReactions = ({
|
|
|
93
174
|
const { reaction } = event;
|
|
94
175
|
|
|
95
176
|
if (reaction) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
177
|
+
setReactions((prevReactions) =>
|
|
178
|
+
reconcileUpdatedReactionInList({
|
|
179
|
+
prevReactions,
|
|
180
|
+
reaction,
|
|
181
|
+
reactionType,
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
103
184
|
}
|
|
104
185
|
}),
|
|
105
186
|
);
|
|
@@ -108,9 +189,13 @@ export const useFetchReactions = ({
|
|
|
108
189
|
client.on('reaction.deleted', (event) => {
|
|
109
190
|
const { reaction } = event;
|
|
110
191
|
|
|
111
|
-
if (reaction
|
|
192
|
+
if (reaction) {
|
|
112
193
|
setReactions((prevReactions) =>
|
|
113
|
-
|
|
194
|
+
removeReactionFromList({
|
|
195
|
+
prevReactions,
|
|
196
|
+
reaction,
|
|
197
|
+
reactionType,
|
|
198
|
+
}),
|
|
114
199
|
);
|
|
115
200
|
}
|
|
116
201
|
}),
|
|
@@ -53,6 +53,7 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => {
|
|
|
53
53
|
closeDialog={() => setShowAddCommentDialog(false)}
|
|
54
54
|
initialValue={ownAnswer?.answer_text ?? ''}
|
|
55
55
|
onSubmit={addComment}
|
|
56
|
+
placeholder={t('Your comment')}
|
|
56
57
|
title={t('Add a comment')}
|
|
57
58
|
visible={showAddCommentDialog}
|
|
58
59
|
/>
|
|
@@ -176,6 +176,7 @@ export const SuggestOptionButton = (props: PollButtonProps) => {
|
|
|
176
176
|
<PollInputDialog
|
|
177
177
|
closeDialog={onRequestClose}
|
|
178
178
|
onSubmit={addOption}
|
|
179
|
+
placeholder={t('Enter a new option')}
|
|
179
180
|
title={t('Suggest an option')}
|
|
180
181
|
visible={showAddOptionDialog}
|
|
181
182
|
/>
|
|
@@ -214,6 +215,7 @@ export const AddCommentButton = (props: PollButtonProps) => {
|
|
|
214
215
|
closeDialog={onRequestClose}
|
|
215
216
|
initialValue={ownAnswer?.answer_text ?? ''}
|
|
216
217
|
onSubmit={addComment}
|
|
218
|
+
placeholder={t('Your comment')}
|
|
217
219
|
title={t('Add a comment')}
|
|
218
220
|
visible={showAddCommentDialog}
|
|
219
221
|
/>
|
|
@@ -19,6 +19,7 @@ import { Button } from '../../ui';
|
|
|
19
19
|
export type PollInputDialogProps = {
|
|
20
20
|
closeDialog: () => void;
|
|
21
21
|
onSubmit: (text: string) => void;
|
|
22
|
+
placeholder: string;
|
|
22
23
|
title: string;
|
|
23
24
|
visible: boolean;
|
|
24
25
|
initialValue?: string;
|
|
@@ -28,6 +29,7 @@ export const PollInputDialog = ({
|
|
|
28
29
|
closeDialog,
|
|
29
30
|
initialValue = '',
|
|
30
31
|
onSubmit,
|
|
32
|
+
placeholder,
|
|
31
33
|
title,
|
|
32
34
|
visible,
|
|
33
35
|
}: PollInputDialogProps) => {
|
|
@@ -62,7 +64,7 @@ export const PollInputDialog = ({
|
|
|
62
64
|
<TextInput
|
|
63
65
|
autoFocus={true}
|
|
64
66
|
onChangeText={setDialogInput}
|
|
65
|
-
placeholder={
|
|
67
|
+
placeholder={placeholder}
|
|
66
68
|
placeholderTextColor={semantics.inputTextPlaceholder}
|
|
67
69
|
style={[styles.input, input]}
|
|
68
70
|
value={dialogInput}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
Text,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
View,
|
|
8
|
+
type ViewStyle,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
|
|
11
|
+
type StableDurationLabelProps = {
|
|
12
|
+
accessibilityLabel?: string;
|
|
13
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
14
|
+
reserveLabel: string;
|
|
15
|
+
style?: StyleProp<TextStyle>;
|
|
16
|
+
visibleStyle?: StyleProp<TextStyle>;
|
|
17
|
+
label: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Renders a duration label with a layout-stable width.
|
|
22
|
+
*
|
|
23
|
+
* It reserves space using an invisible max-width label, then absolutely positions
|
|
24
|
+
* the live value on top so playback updates do not cause parent relayout.
|
|
25
|
+
*/
|
|
26
|
+
export const StableDurationLabel = React.memo(
|
|
27
|
+
({
|
|
28
|
+
accessibilityLabel,
|
|
29
|
+
containerStyle,
|
|
30
|
+
reserveLabel,
|
|
31
|
+
style,
|
|
32
|
+
visibleStyle,
|
|
33
|
+
label,
|
|
34
|
+
}: StableDurationLabelProps) => (
|
|
35
|
+
<View style={[styles.container, containerStyle]}>
|
|
36
|
+
<Text accessible={false} style={[style, styles.reserveLabel]}>
|
|
37
|
+
{reserveLabel}
|
|
38
|
+
</Text>
|
|
39
|
+
<Text
|
|
40
|
+
accessibilityLabel={accessibilityLabel}
|
|
41
|
+
style={[style, styles.visibleLabel, visibleStyle]}
|
|
42
|
+
>
|
|
43
|
+
{label}
|
|
44
|
+
</Text>
|
|
45
|
+
</View>
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
StableDurationLabel.displayName = 'StableDurationLabel';
|
|
50
|
+
|
|
51
|
+
const styles = StyleSheet.create({
|
|
52
|
+
container: {
|
|
53
|
+
position: 'relative',
|
|
54
|
+
},
|
|
55
|
+
reserveLabel: {
|
|
56
|
+
opacity: 0,
|
|
57
|
+
},
|
|
58
|
+
visibleLabel: {
|
|
59
|
+
left: 0,
|
|
60
|
+
position: 'absolute',
|
|
61
|
+
top: 0,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import type { ColorValue, StyleProp, ViewStyle } from 'react-native';
|
|
3
4
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
4
|
-
import Animated, {
|
|
5
|
-
runOnJS,
|
|
6
|
-
useAnimatedReaction,
|
|
7
|
-
useAnimatedStyle,
|
|
8
|
-
useSharedValue,
|
|
9
|
-
} from 'react-native-reanimated';
|
|
5
|
+
import Animated, { runOnJS, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
|
|
10
6
|
|
|
11
7
|
import { ProgressControlThumb } from './ProgressThumb';
|
|
12
8
|
|
|
@@ -49,6 +45,43 @@ const WAVEFORM_WIDTH = 2;
|
|
|
49
45
|
const WAVEFORM_GAP = 2;
|
|
50
46
|
const WAVE_MAX_HEIGHT = 20;
|
|
51
47
|
const WAVE_MIN_HEIGHT = 2;
|
|
48
|
+
const DRAG_HIT_SLOP = {
|
|
49
|
+
bottom: 12,
|
|
50
|
+
left: 12,
|
|
51
|
+
right: 12,
|
|
52
|
+
top: 12,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const clampProgress = (progress: number) => {
|
|
56
|
+
'worklet';
|
|
57
|
+
return Math.max(0, Math.min(progress, 1));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type WaveformBarsProps = {
|
|
61
|
+
color: ColorValue;
|
|
62
|
+
heights: number[];
|
|
63
|
+
waveformStyle?: StyleProp<ViewStyle>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const WaveformBars = React.memo(({ color, heights, waveformStyle }: WaveformBarsProps) => (
|
|
67
|
+
<View style={styles.waveformLayer}>
|
|
68
|
+
{heights.map((height, index) => (
|
|
69
|
+
<View
|
|
70
|
+
key={index}
|
|
71
|
+
style={[
|
|
72
|
+
styles.waveform,
|
|
73
|
+
{
|
|
74
|
+
backgroundColor: color,
|
|
75
|
+
height,
|
|
76
|
+
},
|
|
77
|
+
waveformStyle,
|
|
78
|
+
]}
|
|
79
|
+
/>
|
|
80
|
+
))}
|
|
81
|
+
</View>
|
|
82
|
+
));
|
|
83
|
+
|
|
84
|
+
WaveformBars.displayName = 'WaveformBars';
|
|
52
85
|
|
|
53
86
|
export const WaveProgressBar = React.memo(
|
|
54
87
|
(props: WaveProgressBarProps) => {
|
|
@@ -62,30 +95,16 @@ export const WaveProgressBar = React.memo(
|
|
|
62
95
|
progress,
|
|
63
96
|
waveformData,
|
|
64
97
|
} = props;
|
|
98
|
+
const [showInteractiveLayer, setShowInteractiveLayer] = useState(
|
|
99
|
+
() => progress > 0 || isPlaying,
|
|
100
|
+
);
|
|
65
101
|
const eachWaveformWidth = WAVEFORM_WIDTH + WAVEFORM_GAP;
|
|
66
102
|
const fullWidth = (amplitudesCount - 1) * eachWaveformWidth;
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
'worklet';
|
|
73
|
-
const progressInPrecision = Number(progress.toFixed(2));
|
|
74
|
-
const progressInWaveformWidth = Number((progressInPrecision * fullWidth).toFixed(0));
|
|
75
|
-
const progressInWaveformNumber = Math.floor(progressInWaveformWidth / 4);
|
|
76
|
-
runOnJS(setCurrentWaveformProgress)(progressInWaveformNumber);
|
|
77
|
-
},
|
|
78
|
-
[fullWidth],
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
useAnimatedReaction(
|
|
82
|
-
() => progress,
|
|
83
|
-
(newProgress) => {
|
|
84
|
-
state.value = newProgress;
|
|
85
|
-
waveFormNumberFromProgress(newProgress);
|
|
86
|
-
},
|
|
87
|
-
[progress],
|
|
88
|
-
);
|
|
103
|
+
const maxThumbTranslateX = Math.max(fullWidth - eachWaveformWidth, 0);
|
|
104
|
+
const maxProgressWidth = fullWidth + WAVEFORM_WIDTH;
|
|
105
|
+
const dragStartProgress = useSharedValue(0);
|
|
106
|
+
const isDragging = useSharedValue(false);
|
|
107
|
+
const visualProgress = useSharedValue(progress);
|
|
89
108
|
|
|
90
109
|
const {
|
|
91
110
|
theme: {
|
|
@@ -94,29 +113,84 @@ export const WaveProgressBar = React.memo(
|
|
|
94
113
|
},
|
|
95
114
|
} = useTheme();
|
|
96
115
|
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!isDragging.value) {
|
|
118
|
+
visualProgress.value = progress;
|
|
119
|
+
}
|
|
120
|
+
}, [isDragging, progress, visualProgress]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
setShowInteractiveLayer(progress > 0 || isPlaying);
|
|
124
|
+
}, [isPlaying, progress]);
|
|
125
|
+
|
|
126
|
+
const handleStartDrag = useCallback(
|
|
127
|
+
(nextProgress: number) => {
|
|
128
|
+
setShowInteractiveLayer(true);
|
|
129
|
+
onStartDrag?.(nextProgress);
|
|
130
|
+
},
|
|
131
|
+
[onStartDrag],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const handleProgressDrag = useCallback(
|
|
135
|
+
(nextProgress: number) => {
|
|
136
|
+
onProgressDrag?.(nextProgress);
|
|
137
|
+
},
|
|
138
|
+
[onProgressDrag],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const handleEndDrag = useCallback(
|
|
142
|
+
(nextProgress: number) => {
|
|
143
|
+
onEndDrag?.(nextProgress);
|
|
144
|
+
},
|
|
145
|
+
[onEndDrag],
|
|
146
|
+
);
|
|
147
|
+
|
|
97
148
|
const pan = useMemo(
|
|
98
149
|
() =>
|
|
99
150
|
Gesture.Pan()
|
|
151
|
+
.hitSlop(DRAG_HIT_SLOP)
|
|
100
152
|
.maxPointers(1)
|
|
101
153
|
.onStart(() => {
|
|
154
|
+
const nextProgress = clampProgress(visualProgress.value);
|
|
155
|
+
dragStartProgress.value = nextProgress;
|
|
156
|
+
isDragging.value = true;
|
|
102
157
|
if (onStartDrag) {
|
|
103
|
-
runOnJS(
|
|
158
|
+
runOnJS(handleStartDrag)(nextProgress);
|
|
104
159
|
}
|
|
105
160
|
})
|
|
106
161
|
.onUpdate((event) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
162
|
+
if (fullWidth <= 0) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const nextProgress = clampProgress(
|
|
166
|
+
dragStartProgress.value + event.translationX / fullWidth,
|
|
167
|
+
);
|
|
168
|
+
visualProgress.value = nextProgress;
|
|
169
|
+
if (onProgressDrag) {
|
|
170
|
+
runOnJS(handleProgressDrag)(nextProgress);
|
|
171
|
+
}
|
|
110
172
|
})
|
|
111
173
|
.onEnd(() => {
|
|
174
|
+
isDragging.value = false;
|
|
112
175
|
if (onEndDrag) {
|
|
113
|
-
runOnJS(
|
|
176
|
+
runOnJS(handleEndDrag)(visualProgress.value);
|
|
114
177
|
}
|
|
115
178
|
}),
|
|
116
|
-
[
|
|
179
|
+
[
|
|
180
|
+
dragStartProgress,
|
|
181
|
+
fullWidth,
|
|
182
|
+
handleEndDrag,
|
|
183
|
+
handleProgressDrag,
|
|
184
|
+
handleStartDrag,
|
|
185
|
+
isDragging,
|
|
186
|
+
onEndDrag,
|
|
187
|
+
onProgressDrag,
|
|
188
|
+
onStartDrag,
|
|
189
|
+
visualProgress,
|
|
190
|
+
],
|
|
117
191
|
);
|
|
118
192
|
|
|
119
|
-
const stringifiedWaveformData = waveformData.toString();
|
|
193
|
+
const stringifiedWaveformData = useMemo(() => waveformData.toString(), [waveformData]);
|
|
120
194
|
|
|
121
195
|
const resampledWaveformData = useMemo(
|
|
122
196
|
() => resampleWaveformData(waveformData, amplitudesCount),
|
|
@@ -124,12 +198,36 @@ export const WaveProgressBar = React.memo(
|
|
|
124
198
|
[amplitudesCount, stringifiedWaveformData],
|
|
125
199
|
);
|
|
126
200
|
|
|
201
|
+
const waveformHeights = useMemo(
|
|
202
|
+
() =>
|
|
203
|
+
resampledWaveformData.map((waveform) =>
|
|
204
|
+
waveform * WAVE_MAX_HEIGHT > WAVE_MIN_HEIGHT
|
|
205
|
+
? waveform * WAVE_MAX_HEIGHT
|
|
206
|
+
: WAVE_MIN_HEIGHT,
|
|
207
|
+
),
|
|
208
|
+
[resampledWaveformData],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const progressOverlayStyles = useAnimatedStyle(
|
|
212
|
+
() => ({
|
|
213
|
+
width: clampProgress(visualProgress.value) * maxProgressWidth,
|
|
214
|
+
}),
|
|
215
|
+
[maxProgressWidth],
|
|
216
|
+
);
|
|
217
|
+
|
|
127
218
|
const thumbStyles = useAnimatedStyle(
|
|
128
219
|
() => ({
|
|
129
220
|
position: 'absolute',
|
|
130
|
-
transform: [
|
|
221
|
+
transform: [
|
|
222
|
+
{
|
|
223
|
+
translateX: Math.min(
|
|
224
|
+
clampProgress(visualProgress.value) * fullWidth,
|
|
225
|
+
maxThumbTranslateX,
|
|
226
|
+
),
|
|
227
|
+
},
|
|
228
|
+
],
|
|
131
229
|
}),
|
|
132
|
-
[
|
|
230
|
+
[fullWidth, maxThumbTranslateX],
|
|
133
231
|
);
|
|
134
232
|
|
|
135
233
|
return (
|
|
@@ -140,30 +238,33 @@ export const WaveProgressBar = React.memo(
|
|
|
140
238
|
}}
|
|
141
239
|
style={[styles.container, container]}
|
|
142
240
|
>
|
|
143
|
-
|
|
241
|
+
<WaveformBars
|
|
242
|
+
color={semantics.chatWaveformBar}
|
|
243
|
+
heights={waveformHeights}
|
|
244
|
+
waveformStyle={waveformTheme}
|
|
245
|
+
/>
|
|
246
|
+
{showInteractiveLayer ? (
|
|
144
247
|
<Animated.View
|
|
145
|
-
|
|
146
|
-
style={[
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
height:
|
|
154
|
-
waveform * WAVE_MAX_HEIGHT > WAVE_MIN_HEIGHT
|
|
155
|
-
? waveform * WAVE_MAX_HEIGHT
|
|
156
|
-
: WAVE_MIN_HEIGHT,
|
|
157
|
-
},
|
|
158
|
-
waveformTheme,
|
|
159
|
-
]}
|
|
160
|
-
/>
|
|
161
|
-
))}
|
|
162
|
-
{(onEndDrag || onProgressDrag) && (
|
|
163
|
-
<Animated.View style={[thumbStyles, thumb]}>
|
|
164
|
-
<ProgressControlThumb isPlaying={isPlaying} />
|
|
248
|
+
pointerEvents='none'
|
|
249
|
+
style={[styles.progressOverlay, progressOverlayStyles]}
|
|
250
|
+
>
|
|
251
|
+
<WaveformBars
|
|
252
|
+
color={semantics.chatWaveformBarPlaying}
|
|
253
|
+
heights={waveformHeights}
|
|
254
|
+
waveformStyle={waveformTheme}
|
|
255
|
+
/>
|
|
165
256
|
</Animated.View>
|
|
166
|
-
)}
|
|
257
|
+
) : null}
|
|
258
|
+
{(onEndDrag || onProgressDrag) &&
|
|
259
|
+
(showInteractiveLayer ? (
|
|
260
|
+
<Animated.View style={[thumbStyles, thumb]}>
|
|
261
|
+
<ProgressControlThumb isPlaying={isPlaying} />
|
|
262
|
+
</Animated.View>
|
|
263
|
+
) : (
|
|
264
|
+
<View style={[styles.idleThumb, thumb]}>
|
|
265
|
+
<ProgressControlThumb isPlaying={isPlaying} />
|
|
266
|
+
</View>
|
|
267
|
+
))}
|
|
167
268
|
</View>
|
|
168
269
|
</GestureDetector>
|
|
169
270
|
);
|
|
@@ -172,6 +273,9 @@ export const WaveProgressBar = React.memo(
|
|
|
172
273
|
if (prevProps.amplitudesCount !== nextProps.amplitudesCount) {
|
|
173
274
|
return false;
|
|
174
275
|
}
|
|
276
|
+
if (prevProps.isPlaying !== nextProps.isPlaying) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
175
279
|
if (prevProps.progress !== nextProps.progress) {
|
|
176
280
|
return false;
|
|
177
281
|
} else {
|
|
@@ -182,6 +286,21 @@ export const WaveProgressBar = React.memo(
|
|
|
182
286
|
|
|
183
287
|
const styles = StyleSheet.create({
|
|
184
288
|
container: {
|
|
289
|
+
alignItems: 'center',
|
|
290
|
+
flexDirection: 'row',
|
|
291
|
+
position: 'relative',
|
|
292
|
+
},
|
|
293
|
+
idleThumb: {
|
|
294
|
+
left: 0,
|
|
295
|
+
position: 'absolute',
|
|
296
|
+
},
|
|
297
|
+
progressOverlay: {
|
|
298
|
+
left: 0,
|
|
299
|
+
overflow: 'hidden',
|
|
300
|
+
position: 'absolute',
|
|
301
|
+
top: 0,
|
|
302
|
+
},
|
|
303
|
+
waveformLayer: {
|
|
185
304
|
alignItems: 'center',
|
|
186
305
|
flexDirection: 'row',
|
|
187
306
|
gap: WAVEFORM_GAP,
|