Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

PollCreate.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // @flow
  2. import React, { useCallback, useEffect, useRef, useState } from 'react';
  3. import { View, TextInput, FlatList, TouchableOpacity } from 'react-native';
  4. import { Button } from 'react-native-paper';
  5. import { Icon, IconClose } from '../../../base/icons';
  6. import BaseTheme from '../../../base/ui/components/BaseTheme.native';
  7. import { BUTTON_MODES } from '../../../chat/constants';
  8. import { CHAR_LIMIT } from '../../constants';
  9. import AbstractPollCreate from '../AbstractPollCreate';
  10. import type { AbstractProps } from '../AbstractPollCreate';
  11. import { chatStyles, dialogStyles } from './styles';
  12. const PollCreate = (props: AbstractProps) => {
  13. const {
  14. addAnswer,
  15. answers,
  16. isSubmitDisabled,
  17. onSubmit,
  18. question,
  19. removeAnswer,
  20. setAnswer,
  21. setCreateMode,
  22. setQuestion,
  23. t
  24. } = props;
  25. const answerListRef = useRef(null);
  26. /*
  27. * This ref stores the Array of answer input fields, allowing us to focus on them.
  28. * This array is maintained by registerfieldRef and the useEffect below.
  29. */
  30. const answerInputs = useRef([]);
  31. const registerFieldRef = useCallback((i, input) => {
  32. if (input === null) {
  33. return;
  34. }
  35. answerInputs.current[i] = input;
  36. },
  37. [ answerInputs ]
  38. );
  39. useEffect(() => {
  40. answerInputs.current = answerInputs.current.slice(0, answers.length);
  41. }, [ answers ]);
  42. /*
  43. * This state allows us to requestFocus asynchronously, without having to worry
  44. * about whether a newly created input field has been rendered yet or not.
  45. */
  46. const [ lastFocus, requestFocus ] = useState(null);
  47. useEffect(() => {
  48. if (lastFocus === null) {
  49. return;
  50. }
  51. const input = answerInputs.current[lastFocus];
  52. if (input === undefined) {
  53. return;
  54. }
  55. input.focus();
  56. }, [ answerInputs, lastFocus ]);
  57. const onQuestionKeyDown = useCallback(() => {
  58. answerInputs.current[0].focus();
  59. });
  60. // Called on keypress in answer fields
  61. const onAnswerKeyDown = useCallback((index: number, ev) => {
  62. const { key } = ev.nativeEvent;
  63. const currentText = answers[index];
  64. if (key === 'Backspace' && currentText === '' && answers.length > 1) {
  65. removeAnswer(index);
  66. requestFocus(index > 0 ? index - 1 : 0);
  67. }
  68. }, [ answers, addAnswer, removeAnswer, requestFocus ]);
  69. /* eslint-disable react/no-multi-comp */
  70. const createIconButton = (icon, onPress, style) => (
  71. <TouchableOpacity
  72. activeOpacity = { 0.8 }
  73. onPress = { onPress }
  74. style = { [ dialogStyles.buttonContainer, style ] }>
  75. <Icon
  76. size = { 24 }
  77. src = { icon }
  78. style = { dialogStyles.icon } />
  79. </TouchableOpacity>
  80. );
  81. /* eslint-disable react/jsx-no-bind */
  82. const renderListItem = ({ index }: { index: number }) =>
  83. // padding to take into account the two default options
  84. (
  85. <View
  86. style = { dialogStyles.optionContainer }>
  87. <TextInput
  88. blurOnSubmit = { false }
  89. maxLength = { CHAR_LIMIT }
  90. multiline = { true }
  91. onChangeText = { text => setAnswer(index, text) }
  92. onKeyPress = { ev => onAnswerKeyDown(index, ev) }
  93. placeholder = { t('polls.create.answerPlaceholder', { index: index + 1 }) }
  94. placeholderTextColor = { BaseTheme.palette.text03 }
  95. ref = { input => registerFieldRef(index, input) }
  96. selectionColor = { BaseTheme.palette.text03 }
  97. style = { dialogStyles.field }
  98. value = { answers[index] } />
  99. {
  100. answers.length > 2
  101. && createIconButton(IconClose, () => removeAnswer(index))
  102. }
  103. </View>
  104. );
  105. return (
  106. <View style = { chatStyles.pollCreateContainer }>
  107. <View style = { chatStyles.pollCreateSubContainer }>
  108. <TextInput
  109. autoFocus = { true }
  110. blurOnSubmit = { false }
  111. maxLength = { CHAR_LIMIT }
  112. multiline = { true }
  113. onChangeText = { setQuestion }
  114. onSubmitEditing = { onQuestionKeyDown }
  115. placeholder = { t('polls.create.questionPlaceholder') }
  116. placeholderTextColor = { BaseTheme.palette.text03 }
  117. selectionColor = { BaseTheme.palette.text03 }
  118. style = { dialogStyles.question }
  119. value = { question } />
  120. <FlatList
  121. blurOnSubmit = { true }
  122. data = { answers }
  123. extraData = { answers }
  124. keyExtractor = { (item, index) => index.toString() }
  125. ref = { answerListRef }
  126. renderItem = { renderListItem } />
  127. <View style = { chatStyles.pollCreateButtonsContainer }>
  128. <Button
  129. color = { BaseTheme.palette.action02 }
  130. mode = { BUTTON_MODES.CONTAINED }
  131. onPress = { () => {
  132. // adding and answer
  133. addAnswer();
  134. requestFocus(answers.length);
  135. } }
  136. style = { chatStyles.pollCreateAddButton }>
  137. {t('polls.create.addOption')}
  138. </Button>
  139. <View
  140. style = { chatStyles.buttonRow }>
  141. <Button
  142. color = { BaseTheme.palette.action02 }
  143. mode = { BUTTON_MODES.CONTAINED }
  144. onPress = { () => setCreateMode(false) }
  145. style = { chatStyles.pollCreateButton } >
  146. {t('polls.create.cancel')}
  147. </Button>
  148. <Button
  149. color = { BaseTheme.palette.screen01Header }
  150. disabled = { isSubmitDisabled }
  151. labelStyle = {
  152. isSubmitDisabled
  153. ? chatStyles.pollSendDisabledLabel
  154. : chatStyles.pollSendLabel
  155. }
  156. mode = { BUTTON_MODES.CONTAINED }
  157. onPress = { onSubmit }
  158. style = { chatStyles.pollCreateButton } >
  159. {t('polls.create.send')}
  160. </Button>
  161. </View>
  162. </View>
  163. </View>
  164. </View>
  165. );
  166. };
  167. /*
  168. * We apply AbstractPollCreate to fill in the AbstractProps common
  169. * to both the web and native implementations.
  170. */
  171. // eslint-disable-next-line new-cap
  172. export default AbstractPollCreate(PollCreate);