You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SalesforceLinkDialog.js 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import React, { useCallback } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { Platform, SafeAreaView, ScrollView, Text, View } from 'react-native';
  4. import { useSelector } from 'react-redux';
  5. import { IconSearch } from '../../../base/icons';
  6. import JitsiScreen from '../../../base/modal/components/JitsiScreen';
  7. import { LoadingIndicator } from '../../../base/react';
  8. import Button from '../../../base/ui/components/native/Button';
  9. import Input from '../../../base/ui/components/native/Input';
  10. import { BUTTON_TYPES } from '../../../base/ui/constants.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. selectedRecord && 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. <Input
  69. customStyles = {{ container: styles.notes }}
  70. maxLength = { NOTES_MAX_LENGTH }
  71. minHeight = { Platform.OS === 'ios' && NOTES_LINES ? 20 * NOTES_LINES : null }
  72. multiline = { true }
  73. numberOfLines = { Platform.OS === 'ios' ? null : NOTES_LINES }
  74. /* eslint-disable-next-line react/jsx-no-bind */
  75. onChange = { value => setNotes(value) }
  76. placeholder = { t('dialog.addMeetingNote') }
  77. value = { notes } />
  78. </ScrollView>
  79. </SafeAreaView>
  80. );
  81. const renderRecordsSearch = () => (
  82. <View style = { styles.recordsSearchContainer }>
  83. <Input
  84. icon = { IconSearch }
  85. maxLength = { NOTES_MAX_LENGTH }
  86. /* eslint-disable-next-line react/jsx-no-bind */
  87. onChange = { value => setSearchTerm(value) }
  88. placeholder = { t('dialog.searchInSalesforce') }
  89. value = { searchTerm } />
  90. {(!isLoading && !hasRecordsErrors) && (
  91. <Text style = { styles.resultLabel }>
  92. {showSearchResults
  93. ? t('dialog.searchResults', { count: records.length })
  94. : t('dialog.recentlyUsedObjects')
  95. }
  96. </Text>
  97. )}
  98. </View>
  99. );
  100. const renderNoRecords = () => showNoResults && (
  101. <View style = { [ styles.noRecords, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  102. <Text style = { styles.noRecordsText }>
  103. {t('dialog.searchResultsNotFound')}
  104. </Text>
  105. <Text style = { styles.noRecordsText }>
  106. {t('dialog.searchResultsTryAgain')}
  107. </Text>
  108. </View>
  109. );
  110. const renderRecordsError = () => (
  111. <View style = { [ styles.recordsError, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
  112. <Text style = { styles.recordsErrorText }>
  113. {t('dialog.searchResultsError')}
  114. </Text>
  115. </View>
  116. );
  117. const renderContent = () => {
  118. if (isLoading) {
  119. return renderSpinner();
  120. }
  121. if (hasRecordsErrors) {
  122. return renderRecordsError();
  123. }
  124. if (showNoResults) {
  125. return renderNoRecords();
  126. }
  127. if (selectedRecord) {
  128. return renderSelection();
  129. }
  130. return (
  131. <SafeAreaView>
  132. <ScrollView
  133. bounces = { false }
  134. style = { [ styles.recordList, { height: clientHeight - LIST_HEIGHT_OFFSET } ] }>
  135. {records.map(item => (
  136. <RecordItem
  137. key = { `record-${item.id}` }
  138. /* eslint-disable-next-line react/jsx-no-bind */
  139. onClick = { () => setSelectedRecord(item) }
  140. { ...item } />
  141. ))}
  142. </ScrollView>
  143. </SafeAreaView>
  144. );
  145. };
  146. return (
  147. <JitsiScreen style = { styles.salesforceDialogContainer }>
  148. <View>
  149. {!selectedRecord && renderRecordsSearch()}
  150. {renderContent()}
  151. </View>
  152. {
  153. selectedRecord
  154. && <View style = { styles.footer }>
  155. <Button
  156. labelKey = 'dialog.Cancel'
  157. /* eslint-disable-next-line react/jsx-no-bind */
  158. onClick = { () => setSelectedRecord(null) }
  159. style = { styles.cancelButton }
  160. type = { BUTTON_TYPES.SECONDARY } />
  161. <Button
  162. labelKey = 'dialog.linkMeeting'
  163. onClick = { handlePress }
  164. style = { styles.linkButton }
  165. type = { BUTTON_TYPES.PRIMARY } />
  166. </View>
  167. }
  168. </JitsiScreen>
  169. );
  170. };
  171. export default SalesforceLinkDialog;