Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

SalesforceLinkDialog.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // @flow
  2. import React, { useCallback } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import { View, SafeAreaView, ScrollView, Text, TextInput, Platform } from 'react-native';
  5. import { Button, withTheme } from 'react-native-paper';
  6. import { useSelector } from 'react-redux';
  7. import { Icon, IconSearch } from '../../../base/icons';
  8. import JitsiScreen from '../../../base/modal/components/JitsiScreen';
  9. import { LoadingIndicator } from '../../../base/react';
  10. import BaseTheme from '../../../base/ui/components/BaseTheme.native';
  11. import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
  12. import { screen } from '../../../mobile/navigation/routes';
  13. import { CONTENT_HEIGHT_OFFSET, LIST_HEIGHT_OFFSET, NOTES_LINES, NOTES_MAX_LENGTH } from '../../constants';
  14. import { useSalesforceLinkDialog } from '../../useSalesforceLinkDialog';
  15. import { RecordItem } from './RecordItem';
  16. import styles from './styles';
  17. /**
  18. * Component that renders the Salesforce link dialog.
  19. *
  20. * @returns {React$Element<any>}
  21. */
  22. const SalesforceLinkDialog = () => {
  23. const { t } = useTranslation();
  24. const { clientHeight } = useSelector(state => state['features/base/responsive-ui']);
  25. const {
  26. hasDetailsErrors,
  27. hasRecordsErrors,
  28. isLoading,
  29. linkMeeting,
  30. notes,
  31. records,
  32. searchTerm,
  33. selectedRecord,
  34. selectedRecordOwner,
  35. setNotes,
  36. setSearchTerm,
  37. setSelectedRecord,
  38. showNoResults,
  39. showSearchResults
  40. } = useSalesforceLinkDialog();
  41. const handlePress = useCallback(() => {
  42. navigate(screen.conference.main);
  43. linkMeeting();
  44. }, [ navigate, linkMeeting ]);
  45. const renderSpinner = () => (
  46. <View style = { [ styles.recordsSpinner, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  47. <LoadingIndicator />
  48. </View>
  49. );
  50. const renderDetailsErrors = () => (
  51. <Text style = { styles.detailsError }>
  52. {t('dialog.searchResultsDetailsError')}
  53. </Text>
  54. );
  55. const renderSelection = () => (
  56. <SafeAreaView>
  57. <ScrollView
  58. bounces = { false }
  59. style = { [ styles.selectedRecord, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  60. <View style = { styles.recordInfo }>
  61. <RecordItem { ...selectedRecord } />
  62. {selectedRecordOwner && <RecordItem { ...selectedRecordOwner } />}
  63. {hasDetailsErrors && renderDetailsErrors()}
  64. </View>
  65. <Text style = { styles.addNote }>
  66. {t('dialog.addOptionalNote')}
  67. </Text>
  68. <TextInput
  69. maxLength = { NOTES_MAX_LENGTH }
  70. minHeight = { Platform.OS === 'ios' && NOTES_LINES ? 20 * NOTES_LINES : null }
  71. multiline = { true }
  72. numberOfLines = { Platform.OS === 'ios' ? null : NOTES_LINES }
  73. /* eslint-disable-next-line react/jsx-no-bind */
  74. onChangeText = { value => setNotes(value) }
  75. placeholder = { t('dialog.addMeetingNote') }
  76. placeholderTextColor = { BaseTheme.palette.text03 }
  77. style = { styles.notes }
  78. value = { notes } />
  79. </ScrollView>
  80. </SafeAreaView>
  81. );
  82. const renderRecordsSearch = () => (
  83. <View style = { styles.recordsSearchContainer }>
  84. <Icon
  85. color = { BaseTheme.palette.icon03 }
  86. src = { IconSearch }
  87. style = { styles.searchIcon } />
  88. <TextInput
  89. maxLength = { NOTES_MAX_LENGTH }
  90. /* eslint-disable-next-line react/jsx-no-bind */
  91. onChangeText = { value => setSearchTerm(value) }
  92. placeholder = { t('dialog.searchInSalesforce') }
  93. placeholderTextColor = { BaseTheme.palette.text03 }
  94. style = { styles.recordsSearch }
  95. value = { searchTerm } />
  96. {(!isLoading && !hasRecordsErrors) && (
  97. <Text style = { styles.resultLabel }>
  98. {showSearchResults
  99. ? t('dialog.searchResults', { count: records.length })
  100. : t('dialog.recentlyUsedObjects')
  101. }
  102. </Text>
  103. )}
  104. </View>
  105. );
  106. const renderNoRecords = () => showNoResults && (
  107. <View style = { [ styles.noRecords, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  108. <Text style = { styles.noRecordsText }>
  109. {t('dialog.searchResultsNotFound')}
  110. </Text>
  111. <Text style = { styles.noRecordsText }>
  112. {t('dialog.searchResultsTryAgain')}
  113. </Text>
  114. </View>
  115. );
  116. const renderRecordsError = () => (
  117. <View style = { [ styles.recordsError, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  118. <Text style = { styles.recordsErrorText }>
  119. {t('dialog.searchResultsError')}
  120. </Text>
  121. </View>
  122. );
  123. const renderContent = () => {
  124. if (isLoading) {
  125. return renderSpinner();
  126. }
  127. if (hasRecordsErrors) {
  128. return renderRecordsError();
  129. }
  130. if (showNoResults) {
  131. return renderNoRecords();
  132. }
  133. if (selectedRecord) {
  134. return renderSelection();
  135. }
  136. return (
  137. <SafeAreaView>
  138. <ScrollView
  139. bounces = { false }
  140. style = { [ styles.recordList, { height: clientHeight - LIST_HEIGHT_OFFSET } ] }>
  141. {records.map(item => (
  142. <RecordItem
  143. key = { `record-${item.id}` }
  144. /* eslint-disable-next-line react/jsx-no-bind */
  145. onClick = { () => setSelectedRecord(item) }
  146. { ...item } />
  147. ))}
  148. </ScrollView>
  149. </SafeAreaView>
  150. );
  151. };
  152. return (
  153. <JitsiScreen style = { styles.salesforceDialogContainer }>
  154. <View>
  155. {!selectedRecord && renderRecordsSearch()}
  156. {renderContent()}
  157. </View>
  158. {
  159. selectedRecord
  160. && <View style = { styles.footer }>
  161. <Button
  162. children = { t('dialog.Cancel') }
  163. mode = 'contained'
  164. /* eslint-disable-next-line react/jsx-no-bind */
  165. onPress = { () => setSelectedRecord(null) }
  166. style = { styles.cancelButton } />
  167. <Button
  168. children = { t('dialog.linkMeeting') }
  169. mode = 'contained'
  170. onPress = { handlePress }
  171. style = { styles.linkButton } />
  172. </View>
  173. }
  174. </JitsiScreen>
  175. );
  176. };
  177. export default withTheme(SalesforceLinkDialog);