Up until now we relied on implicit loading of middlewares and reducers, through having imports in each feature's index.js. This leads to many complex import cycles which result in (sometimes) hard to fix bugs in addition to (often) breaking mobile because a web-only feature gets imported on mobile too, thanks to the implicit loading. This PR changes that to make the process explicit. Both middlewares and reducers are imported in a single place, the app entrypoint. They have been divided into 3 categories: any, web and native, which represent each of the platforms respectively. Ideally no feature should have an index.js exporting actions, action types and components, but that's a larger ordeal, so this is just the first step in getting there. In order to both set example and avoid large cycles the app feature has been refactored to not have an idex.js itself.
174 lines
4.2 KiB
JavaScript
174 lines
4.2 KiB
JavaScript
// @flow
|
||
|
||
import React, { Component } from 'react';
|
||
|
||
import {
|
||
createCalendarClickedEvent,
|
||
createCalendarSelectedEvent,
|
||
sendAnalytics
|
||
} from '../../analytics';
|
||
import { appNavigate } from '../../app/actions';
|
||
import { MeetingsList } from '../../base/react';
|
||
import { connect } from '../../base/redux';
|
||
|
||
import AddMeetingUrlButton from './AddMeetingUrlButton';
|
||
import JoinButton from './JoinButton';
|
||
|
||
/**
|
||
* The type of the React {@code Component} props of
|
||
* {@link CalendarListContent}.
|
||
*/
|
||
type Props = {
|
||
|
||
/**
|
||
* The calendar event list.
|
||
*/
|
||
_eventList: Array<Object>,
|
||
|
||
/**
|
||
* Indicates if the list is disabled or not.
|
||
*/
|
||
disabled: boolean,
|
||
|
||
/**
|
||
* The Redux dispatch function.
|
||
*/
|
||
dispatch: Function,
|
||
|
||
/**
|
||
*
|
||
*/
|
||
listEmptyComponent: React$Node,
|
||
};
|
||
|
||
/**
|
||
* Component to display a list of events from a connected calendar.
|
||
*/
|
||
class CalendarListContent extends Component<Props> {
|
||
/**
|
||
* Default values for the component's props.
|
||
*/
|
||
static defaultProps = {
|
||
_eventList: []
|
||
};
|
||
|
||
/**
|
||
* Initializes a new {@code CalendarListContent} instance.
|
||
*
|
||
* @inheritdoc
|
||
*/
|
||
constructor(props: Props) {
|
||
super(props);
|
||
|
||
// Bind event handlers so they are only bound once per instance.
|
||
this._onJoinPress = this._onJoinPress.bind(this);
|
||
this._onPress = this._onPress.bind(this);
|
||
this._toDisplayableItem = this._toDisplayableItem.bind(this);
|
||
}
|
||
|
||
/**
|
||
* Implements React's {@link Component#componentDidMount()}. Invoked
|
||
* immediately after this component is mounted.
|
||
*
|
||
* @inheritdoc
|
||
* @returns {void}
|
||
*/
|
||
componentDidMount() {
|
||
sendAnalytics(createCalendarSelectedEvent());
|
||
}
|
||
|
||
/**
|
||
* Implements React's {@link Component#render}.
|
||
*
|
||
* @inheritdoc
|
||
*/
|
||
render() {
|
||
const { disabled, listEmptyComponent } = this.props;
|
||
const { _eventList = [] } = this.props;
|
||
const meetings = _eventList.map(this._toDisplayableItem);
|
||
|
||
return (
|
||
<MeetingsList
|
||
disabled = { disabled }
|
||
listEmptyComponent = { listEmptyComponent }
|
||
meetings = { meetings }
|
||
onPress = { this._onPress } />
|
||
);
|
||
}
|
||
|
||
_onJoinPress: (Object, string) => Function;
|
||
|
||
/**
|
||
* Handles the list's navigate action.
|
||
*
|
||
* @private
|
||
* @param {Object} event - The click event.
|
||
* @param {string} url - The url string to navigate to.
|
||
* @returns {void}
|
||
*/
|
||
_onJoinPress(event, url) {
|
||
event.stopPropagation();
|
||
|
||
this._onPress(url, 'calendar.meeting.join');
|
||
}
|
||
|
||
_onPress: (string, ?string) => Function;
|
||
|
||
/**
|
||
* Handles the list's navigate action.
|
||
*
|
||
* @private
|
||
* @param {string} url - The url string to navigate to.
|
||
* @param {string} analyticsEventName - Тhe name of the analytics event
|
||
* associated with this action.
|
||
* @returns {void}
|
||
*/
|
||
_onPress(url, analyticsEventName = 'calendar.meeting.tile') {
|
||
sendAnalytics(createCalendarClickedEvent(analyticsEventName));
|
||
|
||
this.props.dispatch(appNavigate(url));
|
||
}
|
||
|
||
_toDisplayableItem: Object => Object;
|
||
|
||
/**
|
||
* Creates a displayable object from an event.
|
||
*
|
||
* @param {Object} event - The calendar event.
|
||
* @private
|
||
* @returns {Object}
|
||
*/
|
||
_toDisplayableItem(event) {
|
||
return {
|
||
elementAfter: event.url
|
||
? <JoinButton
|
||
onPress = { this._onJoinPress }
|
||
url = { event.url } />
|
||
: (<AddMeetingUrlButton
|
||
calendarId = { event.calendarId }
|
||
eventId = { event.id } />),
|
||
date: event.startDate,
|
||
time: [ event.startDate, event.endDate ],
|
||
description: event.url,
|
||
title: event.title,
|
||
url: event.url
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Maps redux state to component props.
|
||
*
|
||
* @param {Object} state - The redux state.
|
||
* @returns {{
|
||
* _eventList: Array<Object>
|
||
* }}
|
||
*/
|
||
function _mapStateToProps(state: Object) {
|
||
return {
|
||
_eventList: state['features/calendar-sync'].events
|
||
};
|
||
}
|
||
|
||
export default connect(_mapStateToProps)(CalendarListContent);
|