您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

MeetingList.native.js 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // @flow
  2. import React, { Component } from 'react';
  3. import {
  4. SafeAreaView,
  5. SectionList,
  6. Text,
  7. TouchableHighlight,
  8. View
  9. } from 'react-native';
  10. import { connect } from 'react-redux';
  11. import { appNavigate } from '../../app';
  12. import { translate } from '../../base/i18n';
  13. import { getLocalizedDateFormatter } from '../../base/util';
  14. import styles, { UNDERLAY_COLOR } from './styles';
  15. type Props = {
  16. /**
  17. * Indicates if the list is disabled or not.
  18. */
  19. disabled: boolean,
  20. /**
  21. * The Redux dispatch function.
  22. */
  23. dispatch: Function,
  24. /**
  25. * The calendar event list.
  26. */
  27. _eventList: Array<Object>,
  28. /**
  29. * The translate function.
  30. */
  31. t: Function
  32. };
  33. /**
  34. * Component to display a list of events from the (mobile) user's calendar.
  35. */
  36. class MeetingList extends Component<Props> {
  37. /**
  38. * Constructor of the MeetingList component.
  39. *
  40. * @inheritdoc
  41. */
  42. constructor(props) {
  43. super(props);
  44. this._createSection = this._createSection.bind(this);
  45. this._getItemKey = this._getItemKey.bind(this);
  46. this._onJoin = this._onJoin.bind(this);
  47. this._onSelect = this._onSelect.bind(this);
  48. this._renderItem = this._renderItem.bind(this);
  49. this._renderSection = this._renderSection.bind(this);
  50. this._toDisplayableList = this._toDisplayableList.bind(this);
  51. this._toDateString = this._toDateString.bind(this);
  52. }
  53. /**
  54. * Implements the React Components's render method.
  55. *
  56. * @inheritdoc
  57. */
  58. render() {
  59. const { disabled } = this.props;
  60. return (
  61. <SafeAreaView
  62. style = { [
  63. styles.container,
  64. disabled ? styles.containerDisabled : null
  65. ] } >
  66. <SectionList
  67. keyExtractor = { this._getItemKey }
  68. renderItem = { this._renderItem }
  69. renderSectionHeader = { this._renderSection }
  70. sections = { this._toDisplayableList() }
  71. style = { styles.list } />
  72. </SafeAreaView>
  73. );
  74. }
  75. _createSection: string => Object;
  76. /**
  77. * Creates a section object of a list of events.
  78. *
  79. * @private
  80. * @param {string} i18Title - The i18 title of the section.
  81. * @returns {Object}
  82. */
  83. _createSection(i18Title) {
  84. return {
  85. data: [],
  86. key: `key-${i18Title}`,
  87. title: this.props.t(i18Title)
  88. };
  89. }
  90. _getItemKey: (Object, number) => string;
  91. /**
  92. * Generates a unique id to every item.
  93. *
  94. * @private
  95. * @param {Object} item - The item.
  96. * @param {number} index - The item index.
  97. * @returns {string}
  98. */
  99. _getItemKey(item, index) {
  100. return `${index}-${item.id}-${item.startDate}`;
  101. }
  102. _onJoin: string => void;
  103. /**
  104. * Joins the selected URL.
  105. *
  106. * @param {string} url - The URL to join to.
  107. * @returns {void}
  108. */
  109. _onJoin(url) {
  110. const { disabled, dispatch } = this.props;
  111. !disabled && url && dispatch(appNavigate(url));
  112. }
  113. _onSelect: string => Function;
  114. /**
  115. * Creates a function that when invoked, joins the given URL.
  116. *
  117. * @private
  118. * @param {string} url - The URL to join to.
  119. * @returns {Function}
  120. */
  121. _onSelect(url) {
  122. return this._onJoin.bind(this, url);
  123. }
  124. _renderItem: Object => Object;
  125. /**
  126. * Renders a single item in the list.
  127. *
  128. * @private
  129. * @param {Object} listItem - The item to render.
  130. * @returns {Component}
  131. */
  132. _renderItem(listItem) {
  133. const { item } = listItem;
  134. return (
  135. <TouchableHighlight
  136. onPress = { this._onSelect(item.url) }
  137. underlayColor = { UNDERLAY_COLOR }>
  138. <View style = { styles.listItem }>
  139. <View style = { styles.avatarContainer } >
  140. <View style = { styles.avatar } >
  141. <Text style = { styles.avatarContent }>
  142. { item.title.substr(0, 1).toUpperCase() }
  143. </Text>
  144. </View>
  145. </View>
  146. <View style = { styles.listItemDetails }>
  147. <Text
  148. numberOfLines = { 1 }
  149. style = { [
  150. styles.listItemText,
  151. styles.listItemTitle
  152. ] }>
  153. { item.title }
  154. </Text>
  155. <Text
  156. numberOfLines = { 1 }
  157. style = { styles.listItemText }>
  158. { item.url }
  159. </Text>
  160. <Text
  161. numberOfLines = { 1 }
  162. style = { styles.listItemText }>
  163. { this._toDateString(item) }
  164. </Text>
  165. </View>
  166. </View>
  167. </TouchableHighlight>
  168. );
  169. }
  170. _renderSection: Object => Object;
  171. /**
  172. * Renders a section title.
  173. *
  174. * @private
  175. * @param {Object} section - The section being rendered.
  176. * @returns {Component}
  177. */
  178. _renderSection(section) {
  179. return (
  180. <View style = { styles.listSection }>
  181. <Text style = { styles.listSectionText }>
  182. { section.section.title }
  183. </Text>
  184. </View>
  185. );
  186. }
  187. _toDisplayableList: () => Array<Object>
  188. /**
  189. * Transforms the event list to a displayable list
  190. * with sections.
  191. *
  192. * @private
  193. * @returns {Array<Object>}
  194. */
  195. _toDisplayableList() {
  196. const { _eventList } = this.props;
  197. const now = Date.now();
  198. const nowSection = this._createSection('calendarSync.now');
  199. const nextSection = this._createSection('calendarSync.next');
  200. const laterSection = this._createSection('calendarSync.later');
  201. if (_eventList && _eventList.length) {
  202. for (const event of _eventList) {
  203. if (event.startDate < now && event.endDate > now) {
  204. nowSection.data.push(event);
  205. } else if (event.startDate > now) {
  206. if (nextSection.data.length
  207. && nextSection.data[0].startDate !== event.startDate) {
  208. laterSection.data.push(event);
  209. } else {
  210. nextSection.data.push(event);
  211. }
  212. }
  213. }
  214. }
  215. const sectionList = [];
  216. for (const section of [
  217. nowSection,
  218. nextSection,
  219. laterSection
  220. ]) {
  221. if (section.data.length) {
  222. sectionList.push(section);
  223. }
  224. }
  225. return sectionList;
  226. }
  227. _toDateString: Object => string;
  228. /**
  229. * Generates a date (interval) string for a given event.
  230. *
  231. * @private
  232. * @param {Object} event - The event.
  233. * @returns {string}
  234. */
  235. _toDateString(event) {
  236. /* eslint-disable max-len */
  237. return `${getLocalizedDateFormatter(event.startDate).format('lll')} - ${getLocalizedDateFormatter(event.endDate).format('LT')}`;
  238. /* eslint-enable max-len */
  239. }
  240. }
  241. /**
  242. * Maps redux state to component props.
  243. *
  244. * @param {Object} state - The redux state.
  245. * @returns {{
  246. * _eventList: Array
  247. * }}
  248. */
  249. export function _mapStateToProps(state: Object) {
  250. return {
  251. _eventList: state['features/calendar-sync'].events
  252. };
  253. }
  254. export default translate(connect(_mapStateToProps)(MeetingList));