Skip to content

Commit f6a20b6

Browse files
committed
fix: improve poll composer integration
1 parent e9ebac4 commit f6a20b6

File tree

6 files changed

+207
-168
lines changed

6 files changed

+207
-168
lines changed

package/src/components/AutoCompleteInput/AutoCompleteInput.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useMemo, useState } from 'react';
1+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
22
import {
33
I18nManager,
44
NativeSyntheticEvent,
@@ -57,12 +57,17 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext)
5757
t,
5858
...rest
5959
} = props;
60+
const [localText, setLocalText] = useState('');
6061
const [textHeight, setTextHeight] = useState(0);
6162
const messageComposer = useMessageComposer();
6263
const { customDataManager, textComposer } = messageComposer;
6364
const { text } = useStateStore(textComposer.state, textComposerStateSelector);
6465
const { command } = useStateStore(customDataManager.state, customComposerDataSelector);
6566

67+
useEffect(() => {
68+
setLocalText(text);
69+
}, [text]);
70+
6671
const handleSelectionChange = useCallback(
6772
(e: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
6873
const { selection } = e.nativeEvent;
@@ -78,7 +83,8 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext)
7883
return;
7984
}
8085

81-
textComposer.setText(newText);
86+
setLocalText(newText);
87+
8288
/**
8389
* This is a hack to ensure the selection is up to date. We should find a better way to do this.
8490
* The onSelectChange event is triggered after the onChangeText event currently which is why the selection value is stale.
@@ -134,7 +140,7 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext)
134140
inputBox,
135141
]}
136142
testID='auto-complete-text-input'
137-
value={text}
143+
value={localText}
138144
{...rest}
139145
/>
140146
);

package/src/components/Poll/CreatePollContent.tsx

Lines changed: 30 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
import React, { useCallback, useEffect, useState } from 'react';
2-
import { Pressable, StyleSheet, Switch, Text, View } from 'react-native';
1+
import React, { useCallback, useEffect } from 'react';
2+
import { StyleSheet, Switch, Text, View } from 'react-native';
33

44
import { ScrollView } from 'react-native-gesture-handler';
55
import { useSharedValue } from 'react-native-reanimated';
66

77
import { PollComposerState, VotingVisibility } from 'stream-chat';
88

9-
import { CreatePollOptions, CurrentOptionPositionsCache, PollModalHeader } from './components';
9+
import { CreatePollOptions, CurrentOptionPositionsCache } from './components';
1010

11+
import { CreatePollHeader } from './components/CreatePollHeader';
1112
import { MultipleAnswersField } from './components/MultipleAnswersField';
1213
import { NameField } from './components/NameField';
1314

14-
import { useCanCreatePoll } from './hooks/useCanCreatePoll';
15-
1615
import {
1716
CreatePollContentContextValue,
1817
CreatePollContentProvider,
@@ -23,7 +22,6 @@ import {
2322
} from '../../contexts';
2423
import { useMessageComposer } from '../../contexts/messageInputContext/hooks/useMessageComposer';
2524
import { useStateStore } from '../../hooks/useStateStore';
26-
import { SendPoll } from '../../icons';
2725

2826
const pollComposerStateSelector = (state: PollComposerState) => ({
2927
allow_answers: state.data.allow_answers,
@@ -35,127 +33,68 @@ const pollComposerStateSelector = (state: PollComposerState) => ({
3533
voting_visibility: state.data.voting_visibility,
3634
});
3735

36+
export const POLL_OPTION_HEIGHT = 71;
37+
3838
export const CreatePollContent = () => {
3939
const { t } = useTranslationContext();
40-
// const [pollOptions, setPollOptions] = useState<PollOptionData[]>([{ text: '' }]);
4140

42-
const [duplicates, setDuplicates] = useState<string[]>([]);
4341
const messageComposer = useMessageComposer();
44-
const canCreatePoll = useCanCreatePoll();
4542
const { pollComposer } = messageComposer;
4643
const { allow_answers, allow_user_suggested_options, options, voting_visibility } = useStateStore(
4744
pollComposer.state,
4845
pollComposerStateSelector,
4946
);
5047

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

5452
// positions and index lookup map
5553
// TODO: Please rethink the structure of this, bidirectional data flow is not great
5654
const currentOptionPositions = useSharedValue<CurrentOptionPositionsCache>({
57-
inverseIndexCache: { 0: 0 },
58-
positionCache: { 0: { updatedIndex: 0, updatedTop: 0 } },
55+
inverseIndexCache: { 0: firstOption.id },
56+
positionCache: { [firstOption.id]: { updatedIndex: 0, updatedTop: 0 } },
5957
});
6058

6159
const {
6260
theme: {
6361
colors: { bg_user, black, white },
6462
poll: {
65-
createContent: {
66-
addComment,
67-
anonymousPoll,
68-
headerContainer,
69-
scrollView,
70-
sendButton,
71-
suggestOption,
72-
},
63+
createContent: { addComment, anonymousPoll, scrollView, suggestOption },
7364
},
7465
},
7566
} = useTheme();
7667

7768
useEffect(() => {
7869
if (!createPollOptionHeight) return;
79-
const newIndex = options.length;
80-
currentOptionPositions.value = {
81-
inverseIndexCache: {
82-
...currentOptionPositions.value.inverseIndexCache,
83-
[newIndex]: newIndex,
84-
},
85-
positionCache: {
86-
...currentOptionPositions.value.positionCache,
87-
[newIndex]: {
88-
updatedIndex: newIndex,
89-
updatedTop: newIndex * createPollOptionHeight,
70+
options.forEach((option, index) => {
71+
currentOptionPositions.value = {
72+
...currentOptionPositions.value,
73+
inverseIndexCache: {
74+
...currentOptionPositions.value.inverseIndexCache,
75+
[index]: option.id,
9076
},
91-
},
92-
};
77+
positionCache: {
78+
...currentOptionPositions.value.positionCache,
79+
[option.id]: {
80+
updatedIndex: index,
81+
updatedTop: index * createPollOptionHeight,
82+
},
83+
},
84+
};
85+
});
9386
// eslint-disable-next-line react-hooks/exhaustive-deps
9487
}, [createPollOptionHeight, options.length]);
9588

96-
useEffect(() => {
97-
const seenTexts = new Set<string>();
98-
const duplicateTexts = new Set<string>();
99-
for (const option of options) {
100-
const { text } = option;
101-
if (seenTexts.has(text)) {
102-
duplicateTexts.add(text);
103-
}
104-
if (text.length > 0) {
105-
seenTexts.add(text);
106-
}
107-
}
108-
109-
setDuplicates(Array.from(duplicateTexts));
110-
}, [options]);
111-
11289
return (
11390
<>
114-
<View style={[styles.headerContainer, { backgroundColor: white }, headerContainer]}>
115-
<PollModalHeader
116-
onPress={() => {
117-
pollComposer.initState();
118-
closePollCreationDialog?.();
119-
}}
120-
title={t('Create Poll')}
121-
/>
122-
<Pressable
123-
disabled={!canCreatePoll}
124-
onPress={async () => {
125-
const currentPollOptions = Object.assign({}, options);
126-
const reorderedPollOptions = [];
127-
128-
for (let i = 0; i < options.length; i++) {
129-
const currentOption =
130-
currentPollOptions[currentOptionPositions.value.inverseIndexCache[i]];
131-
if (currentOption.text.length > 0) {
132-
reorderedPollOptions.push(currentOption);
133-
}
134-
}
135-
await pollComposer.updateFields({
136-
options: reorderedPollOptions,
137-
});
138-
await createAndSendPoll();
139-
}}
140-
style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }, styles.sendButton, sendButton]}
141-
>
142-
<SendPoll
143-
height={24}
144-
pathFill={canCreatePoll ? '#005DFF' : '#B4BBBA'}
145-
viewBox='0 0 24 24'
146-
width={24}
147-
/>
148-
</Pressable>
149-
</View>
91+
<CreatePollHeader />
15092
<ScrollView
15193
contentContainerStyle={{ paddingBottom: 70 }}
15294
style={[styles.scrollView, { backgroundColor: white }, scrollView]}
15395
>
15496
<NameField />
155-
<CreatePollOptions
156-
currentOptionPositions={currentOptionPositions}
157-
duplicates={duplicates}
158-
/>
97+
<CreatePollOptions currentOptionPositions={currentOptionPositions} />
15998
<MultipleAnswersField />
16099
<View
161100
style={[styles.textInputWrapper, { backgroundColor: bg_user }, anonymousPoll.wrapper]}
@@ -202,7 +141,7 @@ export const CreatePollContent = () => {
202141
export const CreatePoll = ({
203142
closePollCreationDialog,
204143
CreatePollContent: CreatePollContentOverride,
205-
createPollOptionHeight = 71,
144+
createPollOptionHeight = POLL_OPTION_HEIGHT,
206145
sendMessage,
207146
}: Pick<
208147
CreatePollContentContextValue,
@@ -233,9 +172,7 @@ export const CreatePoll = ({
233172
};
234173

235174
const styles = StyleSheet.create({
236-
headerContainer: { flexDirection: 'row', justifyContent: 'space-between' },
237175
scrollView: { flex: 1, padding: 16 },
238-
sendButton: { paddingHorizontal: 16, paddingVertical: 18 },
239176
text: { fontSize: 16 },
240177
textInputWrapper: {
241178
alignItems: 'center',
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from 'react';
2+
import { Pressable, StyleSheet, View } from 'react-native';
3+
4+
import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/useMessageComposer';
5+
import { useCreatePollContentContext } from '../../../contexts/pollContext/createPollContentContext';
6+
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
7+
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
8+
import { SendPoll } from '../../../icons';
9+
import { PollModalHeader } from '../components';
10+
import { useCanCreatePoll } from '../hooks/useCanCreatePoll';
11+
12+
export const CreatePollHeader = () => {
13+
const { t } = useTranslationContext();
14+
15+
const messageComposer = useMessageComposer();
16+
const canCreatePoll = useCanCreatePoll();
17+
const { pollComposer } = messageComposer;
18+
const { closePollCreationDialog, createAndSendPoll } = useCreatePollContentContext();
19+
20+
const {
21+
theme: {
22+
colors: { white },
23+
poll: {
24+
createContent: { headerContainer, sendButton },
25+
},
26+
},
27+
} = useTheme();
28+
29+
return (
30+
<View style={[styles.headerContainer, { backgroundColor: white }, headerContainer]}>
31+
<PollModalHeader
32+
onPress={() => {
33+
pollComposer.initState();
34+
closePollCreationDialog?.();
35+
}}
36+
title={t('Create Poll')}
37+
/>
38+
<Pressable
39+
disabled={!canCreatePoll}
40+
onPress={async () => {
41+
await createAndSendPoll();
42+
}}
43+
style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }, styles.sendButton, sendButton]}
44+
>
45+
<SendPoll
46+
height={24}
47+
pathFill={canCreatePoll ? '#005DFF' : '#B4BBBA'}
48+
viewBox='0 0 24 24'
49+
width={24}
50+
/>
51+
</Pressable>
52+
</View>
53+
);
54+
};
55+
56+
const styles = StyleSheet.create({
57+
headerContainer: { flexDirection: 'row', justifyContent: 'space-between' },
58+
sendButton: { paddingHorizontal: 16, paddingVertical: 18 },
59+
});

0 commit comments

Comments
 (0)