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

BaseCalendarList.js 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { appNavigate } from '../../app';
  5. import {
  6. createCalendarClickedEvent,
  7. createCalendarSelectedEvent,
  8. sendAnalytics
  9. } from '../../analytics';
  10. import { getLocalizedDateFormatter, translate } from '../../base/i18n';
  11. import { NavigateSectionList } from '../../base/react';
  12. import { refreshCalendar } from '../actions';
  13. import { isCalendarEnabled } from '../functions';
  14. import AddMeetingUrlButton from './AddMeetingUrlButton';
  15. import JoinButton from './JoinButton';
  16. /**
  17. * The type of the React {@code Component} props of
  18. * {@link BaseCalendarList}.
  19. */
  20. type Props = {
  21. /**
  22. * The calendar event list.
  23. */
  24. _eventList: Array<Object>,
  25. /**
  26. * Indicates if the list is disabled or not.
  27. */
  28. disabled: boolean,
  29. /**
  30. * The Redux dispatch function.
  31. */
  32. dispatch: Function,
  33. /**
  34. *
  35. */
  36. renderListEmptyComponent: Function,
  37. /**
  38. * The translate function.
  39. */
  40. t: Function
  41. };
  42. /**
  43. * Component to display a list of events from a connected calendar.
  44. */
  45. class BaseCalendarList extends Component<Props> {
  46. /**
  47. * Default values for the component's props.
  48. */
  49. static defaultProps = {
  50. _eventList: []
  51. };
  52. /**
  53. * Public API method for {@code Component}s rendered in
  54. * {@link AbstractPagedList}. When invoked, refreshes the calendar entries
  55. * in the app.
  56. *
  57. * Note: It is a static method as the {@code Component} may not be
  58. * initialized yet when the UI invokes refresh (e.g. {@link TabBarIOS} tab
  59. * change).
  60. *
  61. * @param {Function} dispatch - The Redux dispatch function.
  62. * @param {boolean} isInteractive - If true this refresh was caused by
  63. * direct user interaction, false otherwise.
  64. * @public
  65. * @returns {void}
  66. */
  67. static refresh(dispatch, isInteractive) {
  68. dispatch(refreshCalendar(false, isInteractive));
  69. }
  70. /**
  71. * Initializes a new {@code BaseCalendarList} instance.
  72. *
  73. * @inheritdoc
  74. */
  75. constructor(props) {
  76. super(props);
  77. // Bind event handlers so they are only bound once per instance.
  78. this._onJoinPress = this._onJoinPress.bind(this);
  79. this._onPress = this._onPress.bind(this);
  80. this._onRefresh = this._onRefresh.bind(this);
  81. this._toDateString = this._toDateString.bind(this);
  82. this._toDisplayableItem = this._toDisplayableItem.bind(this);
  83. this._toDisplayableList = this._toDisplayableList.bind(this);
  84. this._toTimeString = this._toTimeString.bind(this);
  85. }
  86. /**
  87. * Implements React's {@link Component#componentDidMount()}. Invoked
  88. * immediately after this component is mounted.
  89. *
  90. * @inheritdoc
  91. * @returns {void}
  92. */
  93. componentDidMount() {
  94. sendAnalytics(createCalendarSelectedEvent());
  95. }
  96. /**
  97. * Implements React's {@link Component#render}.
  98. *
  99. * @inheritdoc
  100. */
  101. render() {
  102. const { disabled, renderListEmptyComponent } = this.props;
  103. return (
  104. <NavigateSectionList
  105. disabled = { disabled }
  106. onPress = { this._onPress }
  107. onRefresh = { this._onRefresh }
  108. renderListEmptyComponent
  109. = { renderListEmptyComponent }
  110. sections = { this._toDisplayableList() } />
  111. );
  112. }
  113. _onJoinPress: (Object, string) => Function;
  114. /**
  115. * Handles the list's navigate action.
  116. *
  117. * @private
  118. * @param {Object} event - The click event.
  119. * @param {string} url - The url string to navigate to.
  120. * @returns {void}
  121. */
  122. _onJoinPress(event, url) {
  123. event.stopPropagation();
  124. this._onPress(url, 'calendar.meeting.join');
  125. }
  126. _onPress: (string, string) => Function;
  127. /**
  128. * Handles the list's navigate action.
  129. *
  130. * @private
  131. * @param {string} url - The url string to navigate to.
  132. * @param {string} analyticsEventName - Тhe name of the analytics event.
  133. * associated with this action.
  134. * @returns {void}
  135. */
  136. _onPress(url, analyticsEventName = 'calendar.meeting.tile') {
  137. sendAnalytics(createCalendarClickedEvent(analyticsEventName));
  138. this.props.dispatch(appNavigate(url));
  139. }
  140. _onRefresh: () => void;
  141. /**
  142. * Callback to execute when the list is doing a pull-to-refresh.
  143. *
  144. * @private
  145. * @returns {void}
  146. */
  147. _onRefresh() {
  148. this.props.dispatch(refreshCalendar(true));
  149. }
  150. _toDateString: Object => string;
  151. /**
  152. * Generates a date string for a given event.
  153. *
  154. * @param {Object} event - The event.
  155. * @private
  156. * @returns {string}
  157. */
  158. _toDateString(event) {
  159. const startDateTime
  160. = getLocalizedDateFormatter(event.startDate).format('MMM Do, YYYY');
  161. return `${startDateTime}`;
  162. }
  163. _toDisplayableItem: Object => Object;
  164. /**
  165. * Creates a displayable object from an event.
  166. *
  167. * @param {Object} event - The calendar event.
  168. * @private
  169. * @returns {Object}
  170. */
  171. _toDisplayableItem(event) {
  172. return {
  173. elementAfter: event.url
  174. ? <JoinButton onPress = { this._onJoinPress } />
  175. : (<AddMeetingUrlButton
  176. calendarId = { event.calendarId }
  177. eventId = { event.id } />),
  178. key: `${event.id}-${event.startDate}`,
  179. lines: [
  180. event.url,
  181. this._toTimeString(event)
  182. ],
  183. title: event.title,
  184. url: event.url
  185. };
  186. }
  187. _toDisplayableList: () => Array<Object>;
  188. /**
  189. * Transforms the event list to a displayable list with sections.
  190. *
  191. * @private
  192. * @returns {Array<Object>}
  193. */
  194. _toDisplayableList() {
  195. const { _eventList, t } = this.props;
  196. const now = new Date();
  197. const { createSection } = NavigateSectionList;
  198. const TODAY_SECTION = 'today';
  199. const sectionMap = new Map();
  200. for (const event of _eventList) {
  201. const displayableEvent = this._toDisplayableItem(event);
  202. const startDate = new Date(event.startDate).getDate();
  203. if (startDate === now.getDate()) {
  204. let todaySection = sectionMap.get(TODAY_SECTION);
  205. if (!todaySection) {
  206. todaySection
  207. = createSection(t('calendarSync.today'), TODAY_SECTION);
  208. sectionMap.set(TODAY_SECTION, todaySection);
  209. }
  210. todaySection.data.push(displayableEvent);
  211. } else if (sectionMap.has(startDate)) {
  212. const section = sectionMap.get(startDate);
  213. if (section) {
  214. section.data.push(displayableEvent);
  215. }
  216. } else {
  217. const newSection
  218. = createSection(this._toDateString(event), startDate);
  219. sectionMap.set(startDate, newSection);
  220. newSection.data.push(displayableEvent);
  221. }
  222. }
  223. return Array.from(sectionMap.values());
  224. }
  225. _toTimeString: Object => string;
  226. /**
  227. * Generates a time (interval) string for a given event.
  228. *
  229. * @param {Object} event - The event.
  230. * @private
  231. * @returns {string}
  232. */
  233. _toTimeString(event) {
  234. const startDateTime
  235. = getLocalizedDateFormatter(event.startDate).format('lll');
  236. const endTime
  237. = getLocalizedDateFormatter(event.endDate).format('LT');
  238. return `${startDateTime} - ${endTime}`;
  239. }
  240. }
  241. /**
  242. * Maps redux state to component props.
  243. *
  244. * @param {Object} state - The redux state.
  245. * @returns {{
  246. * _eventList: Array<Object>
  247. * }}
  248. */
  249. function _mapStateToProps(state: Object) {
  250. return {
  251. _eventList: state['features/calendar-sync'].events
  252. };
  253. }
  254. export default isCalendarEnabled()
  255. ? translate(connect(_mapStateToProps)(BaseCalendarList))
  256. : undefined;