Skip to content

Commit 76f0ed2

Browse files
committed
fix: poll composer crash bug
1 parent 075300d commit 76f0ed2

File tree

18 files changed

+117
-93
lines changed

18 files changed

+117
-93
lines changed

package/src/components/AutoCompleteInput/AutoCompleteInput.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,13 @@ export const AutoCompleteInput = (props: AutoCompleteInputProps) => {
8888
(newText: string) => {
8989
setLocalText(newText);
9090

91-
/**
92-
* This is a hack to ensure the selection is up to date. We should find a better way to do this.
93-
* The onSelectChange event is triggered after the onChangeText event currently which is why the selection value is stale.
94-
*/
95-
setTimeout(() => {
96-
textComposer.handleChange({
97-
selection: textComposer.selection,
98-
text: newText,
99-
});
100-
}, 0);
91+
textComposer.handleChange({
92+
selection: {
93+
end: newText.length,
94+
start: newText.length,
95+
},
96+
text: newText,
97+
});
10198
},
10299
[textComposer],
103100
);
@@ -110,7 +107,7 @@ export const AutoCompleteInput = (props: AutoCompleteInputProps) => {
110107
} = useTheme();
111108

112109
const placeholderText = useMemo(() => {
113-
return command ? t('Search GIFs') : cooldownActive ? t('Slow mode ON') : t('Send a message');
110+
return command ? t('Search') : cooldownActive ? t('Slow mode ON') : t('Send a message');
114111
}, [command, cooldownActive, t]);
115112

116113
const handleContentSizeChange = useCallback(

package/src/components/Poll/CreatePollContent.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,14 @@ export const CreatePollContent = () => {
4545
pollComposerStateSelector,
4646
);
4747

48-
const firstOption = options[0];
49-
50-
const { createPollOptionHeight } = useCreatePollContentContext();
48+
const { createPollOptionHeight, closePollCreationDialog, createAndSendPoll } =
49+
useCreatePollContentContext();
5150

5251
// positions and index lookup map
5352
// TODO: Please rethink the structure of this, bidirectional data flow is not great
5453
const currentOptionPositions = useSharedValue<CurrentOptionPositionsCache>({
55-
inverseIndexCache: { 0: firstOption.id },
56-
positionCache: { [firstOption.id]: { updatedIndex: 0, updatedTop: 0 } },
54+
inverseIndexCache: {},
55+
positionCache: {},
5756
});
5857

5958
const {
@@ -86,9 +85,21 @@ export const CreatePollContent = () => {
8685
// eslint-disable-next-line react-hooks/exhaustive-deps
8786
}, [createPollOptionHeight, options.length]);
8887

88+
const onBackPressHandler = useCallback(() => {
89+
pollComposer.initState();
90+
closePollCreationDialog?.();
91+
}, [pollComposer, closePollCreationDialog]);
92+
93+
const onCreatePollPressHandler = useCallback(async () => {
94+
await createAndSendPoll();
95+
}, [createAndSendPoll]);
96+
8997
return (
9098
<>
91-
<CreatePollHeader />
99+
<CreatePollHeader
100+
onBackPressHandler={onBackPressHandler}
101+
onCreatePollPressHandler={onCreatePollPressHandler}
102+
/>
92103
<ScrollView
93104
contentContainerStyle={{ paddingBottom: 70 }}
94105
style={[styles.scrollView, { backgroundColor: white }, scrollView]}
@@ -149,18 +160,16 @@ export const CreatePoll = ({
149160
> &
150161
Pick<InputMessageInputContextValue, 'CreatePollContent'>) => {
151162
const messageComposer = useMessageComposer();
152-
const { pollComposer } = messageComposer;
153163

154164
const createAndSendPoll = useCallback(async () => {
155165
try {
156166
await messageComposer.createPoll();
157167
await sendMessage();
158-
await pollComposer.initState();
159168
closePollCreationDialog?.();
160169
} catch (error) {
161170
console.log('error', error);
162171
}
163-
}, [messageComposer, sendMessage, pollComposer, closePollCreationDialog]);
172+
}, [messageComposer, sendMessage, closePollCreationDialog]);
164173

165174
return (
166175
<CreatePollContentProvider

package/src/components/Poll/components/CreatePollHeader.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
import React from 'react';
22
import { Pressable, StyleSheet, View } from 'react-native';
33

4-
import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/useMessageComposer';
5-
import { useCreatePollContentContext } from '../../../contexts/pollContext/createPollContentContext';
64
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
75
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
86
import { SendPoll } from '../../../icons';
97
import { PollModalHeader } from '../components';
108
import { useCanCreatePoll } from '../hooks/useCanCreatePoll';
119

12-
export const CreatePollHeader = () => {
10+
export type CreatePollHeaderProps = {
11+
/**
12+
* Handler for back button press
13+
* @returns void
14+
*/
15+
onBackPressHandler: () => void;
16+
/**
17+
* Handler for create poll button press
18+
* @returns void
19+
*/
20+
onCreatePollPressHandler: () => void;
21+
};
22+
23+
export const CreatePollHeader = ({
24+
onBackPressHandler,
25+
onCreatePollPressHandler,
26+
}: CreatePollHeaderProps) => {
1327
const { t } = useTranslationContext();
1428

15-
const messageComposer = useMessageComposer();
1629
const canCreatePoll = useCanCreatePoll();
17-
const { pollComposer } = messageComposer;
18-
const { closePollCreationDialog, createAndSendPoll } = useCreatePollContentContext();
1930

2031
const {
2132
theme: {
@@ -28,18 +39,10 @@ export const CreatePollHeader = () => {
2839

2940
return (
3041
<View style={[styles.headerContainer, { backgroundColor: white }, headerContainer]}>
31-
<PollModalHeader
32-
onPress={() => {
33-
pollComposer.initState();
34-
closePollCreationDialog?.();
35-
}}
36-
title={t('Create Poll')}
37-
/>
42+
<PollModalHeader onPress={onBackPressHandler} title={t<string>('Create Poll')} />
3843
<Pressable
3944
disabled={!canCreatePoll}
40-
onPress={async () => {
41-
await createAndSendPoll();
42-
}}
45+
onPress={onCreatePollPressHandler}
4346
style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }, styles.sendButton, sendButton]}
4447
>
4548
<SendPoll

package/src/components/Poll/components/CreatePollOptions.tsx

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const CreatePollOption = ({
6363
option,
6464
onNewOrder,
6565
}: CreatePollOptionType) => {
66+
const { t } = useTranslationContext();
6667
const { createPollOptionHeight = POLL_OPTION_HEIGHT } = useCreatePollContentContext();
6768
const top = useSharedValue(index * createPollOptionHeight);
6869
const isDraggingDerived = useDerivedValue(() => isDragging.value);
@@ -93,14 +94,19 @@ export const CreatePollOption = ({
9394
// used for swapping with newIndex
9495
const currentIndex = useSharedValue<number | null>(null);
9596

97+
// The sanity check for position cache updated index, is added because after a poll is sent its been reset
98+
// by the composer and it throws an undefined error. This can be removed in future.
9699
useAnimatedReaction(
97-
() => currentOptionPositionsDerived.value.positionCache[option.id].updatedIndex,
100+
() =>
101+
currentOptionPositionsDerived.value.positionCache[option.id]
102+
? currentOptionPositionsDerived.value.positionCache[option.id].updatedIndex
103+
: 0,
98104
(currentValue, previousValue) => {
99105
if (currentValue !== previousValue) {
100-
top.value = withSpring(
101-
currentOptionPositionsDerived.value.positionCache[option.id].updatedIndex *
102-
createPollOptionHeight,
103-
);
106+
const updatedIndex = currentOptionPositionsDerived.value.positionCache[option.id]
107+
? currentOptionPositionsDerived.value.positionCache[option.id].updatedIndex
108+
: 0;
109+
top.value = withSpring(updatedIndex * createPollOptionHeight);
104110
}
105111
},
106112
);
@@ -209,9 +215,12 @@ export const CreatePollOption = ({
209215
},
210216
} = useTheme();
211217

212-
const onChangeTextHandler = (newText: string) => {
213-
handleChangeText(newText, index);
214-
};
218+
const onChangeTextHandler = useCallback(
219+
(newText: string) => {
220+
handleChangeText(newText, index);
221+
},
222+
[handleChangeText, index],
223+
);
215224

216225
return (
217226
<Animated.View
@@ -235,13 +244,13 @@ export const CreatePollOption = ({
235244
optionStyle.validationErrorText,
236245
]}
237246
>
238-
{error}
247+
{t<string>(error)}
239248
</Text>
240249
) : null}
241250
<TextInput
242251
onBlur={handleBlur}
243252
onChangeText={onChangeTextHandler}
244-
placeholder={'Add an option'}
253+
placeholder={t<string>('Add an option')}
245254
style={[styles.optionInput, { color: black }, optionStyle.input]}
246255
/>
247256
<GestureDetector gesture={gesture}>
@@ -270,6 +279,7 @@ export const CreatePollOptions = ({ currentOptionPositions }: CreatePollOptionsP
270279
const { pollComposer } = messageComposer;
271280
const { errors, options } = useStateStore(pollComposer.state, pollComposerStateSelector);
272281
const { createPollOptionHeight = POLL_OPTION_HEIGHT } = useCreatePollContentContext();
282+
273283
const updateOption = useCallback(
274284
(newText: string, index: number) => {
275285
pollComposer.updateFields({
@@ -305,20 +315,23 @@ export const CreatePollOptions = ({ currentOptionPositions }: CreatePollOptionsP
305315
},
306316
} = useTheme();
307317

308-
const onNewOrderChange = async (newOrder: CurrentOptionPositionsCache['inverseIndexCache']) => {
309-
const reorderedPollOptions = [];
318+
const onNewOrderChange = useCallback(
319+
async (newOrder: CurrentOptionPositionsCache['inverseIndexCache']) => {
320+
const reorderedPollOptions = [];
310321

311-
for (let i = 0; i < options.length; i++) {
312-
const currentOption = options.find((option) => option.id === newOrder[i]);
313-
if (currentOption) {
314-
reorderedPollOptions.push(currentOption);
322+
for (let i = 0; i < options.length; i++) {
323+
const currentOption = options.find((option) => option.id === newOrder[i]);
324+
if (currentOption) {
325+
reorderedPollOptions.push(currentOption);
326+
}
315327
}
316-
}
317328

318-
await pollComposer.updateFields({
319-
options: reorderedPollOptions,
320-
});
321-
};
329+
await pollComposer.updateFields({
330+
options: reorderedPollOptions,
331+
});
332+
},
333+
[options, pollComposer],
334+
);
322335

323336
return (
324337
<View style={[styles.container, container]}>

package/src/components/Poll/components/MultipleAnswersField.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const MultipleAnswersField = () => {
1717
const { t } = useTranslationContext();
1818
const messageComposer = useMessageComposer();
1919
const { pollComposer } = messageComposer;
20+
const { handleFieldBlur, updateFields } = pollComposer;
2021
const { enforce_unique_vote, error, max_votes_allowed } = useStateStore(
2122
pollComposer.state,
2223
pollComposerStateSelector,
@@ -33,21 +34,21 @@ export const MultipleAnswersField = () => {
3334

3435
const onEnforceUniqueVoteHandler = useCallback(
3536
async (value: boolean) => {
36-
await pollComposer.updateFields({ enforce_unique_vote: !value });
37+
await updateFields({ enforce_unique_vote: !value });
3738
},
38-
[pollComposer],
39+
[updateFields],
3940
);
4041

4142
const onChangeTextHandler = useCallback(
4243
async (newText: string) => {
43-
await pollComposer.updateFields({ max_votes_allowed: newText });
44+
await updateFields({ max_votes_allowed: newText });
4445
},
45-
[pollComposer],
46+
[updateFields],
4647
);
4748

4849
const onBlurHandler = useCallback(async () => {
49-
await pollComposer.handleFieldBlur('max_votes_allowed');
50-
}, [pollComposer]);
50+
await handleFieldBlur('max_votes_allowed');
51+
}, [handleFieldBlur]);
5152

5253
return (
5354
<View
@@ -69,15 +70,15 @@ export const MultipleAnswersField = () => {
6970
maxVotes.validationText,
7071
]}
7172
>
72-
{error}
73+
{t<string>(error)}
7374
</Text>
7475
) : null}
7576
<View style={{ flexDirection: 'row' }}>
7677
<TextInput
7778
inputMode='numeric'
7879
onBlur={onBlurHandler}
7980
onChangeText={onChangeTextHandler}
80-
placeholder={t('Maximum votes per person')}
81+
placeholder={t<string>('Maximum votes per person')}
8182
style={[styles.maxVotesInput, { color: black }, maxVotes.input]}
8283
/>
8384
</View>

package/src/components/Poll/components/NameField.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const NameField = () => {
88
const { t } = useTranslationContext();
99
const messageComposer = useMessageComposer();
1010
const { pollComposer } = messageComposer;
11+
const { handleFieldBlur, updateFields } = pollComposer;
1112

1213
const {
1314
theme: {
@@ -20,14 +21,14 @@ export const NameField = () => {
2021

2122
const onChangeText = useCallback(
2223
async (newText: string) => {
23-
await pollComposer.updateFields({ name: newText });
24+
await updateFields({ name: newText });
2425
},
25-
[pollComposer],
26+
[updateFields],
2627
);
2728

2829
const onBlur = useCallback(async () => {
29-
await pollComposer.handleFieldBlur('name');
30-
}, [pollComposer]);
30+
await handleFieldBlur('name');
31+
}, [handleFieldBlur]);
3132

3233
return (
3334
<View>

package/src/i18n/en.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"Only visible to you": "Only visible to you",
6969
"Open Settings": "Open Settings",
7070
"Option": "Option",
71+
"Option already exists": "Option already exists",
7172
"Options": "Options",
7273
"Photo": "Photo",
7374
"Photos and Videos": "Photos and Videos",
@@ -85,7 +86,7 @@
8586
"Reply to Message": "Reply to Message",
8687
"Resend": "Resend",
8788
"SEND": "SEND",
88-
"Search GIFs": "Search GIFs",
89+
"Search": "Search",
8990
"See all {{count}} options_one": "See all {{count}} options",
9091
"See all {{count}} options_other": "See all {{count}} options",
9192
"Select More Photos": "Select More Photos",
@@ -103,7 +104,6 @@
103104
"The message has been reported to a moderator.": "The message has been reported to a moderator.",
104105
"The source message was deleted": "The source message was deleted",
105106
"Thinking...": "Thinking...",
106-
"This is already an option": "This is already an option",
107107
"This reply was deleted": "This reply was deleted",
108108
"Thread Reply": "Thread Reply",
109109
"Type a number from 2 to 10": "Type a number from 2 to 10",

package/src/i18n/es.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"Only visible to you": "Solo visible para ti",
6969
"Open Settings": "Configuración abierta",
7070
"Option": "Opción",
71+
"Option already exists": "La opción ya existe",
7172
"Options": "Opciones",
7273
"Photo": "Foto",
7374
"Photos and Videos": "Fotos y videos",
@@ -85,7 +86,7 @@
8586
"Reply to Message": "Responder al mensaje",
8687
"Resend": "Reenviar",
8788
"SEND": "ENVIAR",
88-
"Search GIFs": "Buscar GIFs",
89+
"Search": "Buscar",
8990
"See all {{count}} options_many": "Ver las {{count}} opciones",
9091
"See all {{count}} options_one": "Ver las {{count}} opciones",
9192
"See all {{count}} options_other": "Ver las {{count}} opciones",
@@ -105,7 +106,6 @@
105106
"The message has been reported to a moderator.": "El mensaje ha sido reportado a un moderador.",
106107
"The source message was deleted": "El mensaje original fue eliminado",
107108
"Thinking...": "Pensando...",
108-
"This is already an option": "Esto ya es una opción",
109109
"This reply was deleted": "Esta respuesta fue eliminada",
110110
"Thread Reply": "Respuesta de hilo",
111111
"Type a number from 2 to 10": "Escribe un número de 2 a 10",

0 commit comments

Comments
 (0)