aboutsummaryrefslogtreecommitdiff
path: root/app/features/recent-list
diff options
context:
space:
mode:
authorAkshit Kr Nagpal <akshitkrnagpal@gmail.com>2018-07-25 13:36:55 +0200
committerSaúl Ibarra Corretgé <s@saghul.net>2018-07-27 09:03:51 +0200
commit8156f6cd0786c9980da6546b3e38ad0c51e4cfbf (patch)
tree2ede6b65b02f769a8eaa77b46284a2bf87713b36 /app/features/recent-list
parentd46c60e6884bd7346467184927a87df81a18ccb2 (diff)
Add recent-list
Diffstat (limited to 'app/features/recent-list')
-rw-r--r--app/features/recent-list/components/RecentList.js122
-rw-r--r--app/features/recent-list/components/index.js1
-rw-r--r--app/features/recent-list/index.js4
-rw-r--r--app/features/recent-list/reducer.js89
-rw-r--r--app/features/recent-list/styled/ConferenceCard.js18
-rw-r--r--app/features/recent-list/styled/RecentListContainer.js9
-rw-r--r--app/features/recent-list/styled/TruncatedText.js8
-rw-r--r--app/features/recent-list/styled/index.js3
-rw-r--r--app/features/recent-list/types.js24
9 files changed, 278 insertions, 0 deletions
diff --git a/app/features/recent-list/components/RecentList.js b/app/features/recent-list/components/RecentList.js
new file mode 100644
index 0000000..bc6819a
--- /dev/null
+++ b/app/features/recent-list/components/RecentList.js
@@ -0,0 +1,122 @@
+// @flow
+
+import moment from 'moment';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import type { Dispatch } from 'redux';
+import { push } from 'react-router-redux';
+
+import { ConferenceCard, RecentListContainer, TruncatedText } from '../styled';
+import type { RecentListItem } from '../types';
+
+type Props = {
+
+ /**
+ * Redux dispatch.
+ */
+ dispatch: Dispatch<*>;
+
+ /**
+ * Array of recent conferences.
+ */
+ _recentList: Array<RecentListItem>;
+};
+
+/**
+ * Recent List Component.
+ */
+class RecentList extends Component<Props, *> {
+ /**
+ * Render function of component.
+ *
+ * @returns {ReactElement}
+ */
+ render() {
+ return (
+ <RecentListContainer>
+ {
+ this.props._recentList.map(
+ conference => this._renderRecentListEntry(conference)
+ )
+ }
+ </RecentListContainer>
+ );
+ }
+
+ /**
+ * Creates a handler for navigatint to a conference.
+ *
+ * @param {RecentListItem} conference - Conference Details.
+ * @returns {void}
+ */
+ _onNavigateToConference(conference: RecentListItem) {
+ return () => this.props.dispatch(push('/conference', conference));
+ }
+
+ /**
+ * Renders the conference card.
+ *
+ * @param {RecentListItem} conference - Conference Details.
+ * @returns {ReactElement}
+ */
+ _renderRecentListEntry(conference: RecentListItem) {
+ return (
+ <ConferenceCard
+ key = { conference.startTime }
+ onClick = { this._onNavigateToConference(conference) }>
+ <TruncatedText>
+ { conference.room }
+ </TruncatedText>
+ <TruncatedText>
+ { this._renderServerURL(conference.serverURL) }
+ </TruncatedText>
+ <TruncatedText>
+ { this._renderTimeAndDuration(conference) }
+ </TruncatedText>
+ </ConferenceCard>
+ );
+ }
+
+ /**
+ * Returns formatted Server URL.
+ *
+ * @param {string} serverURL - Server URL.
+ * @returns {string} - Formatted server URL.
+ */
+ _renderServerURL(serverURL: string) {
+ // Strip protocol to make it cleaner.
+ return `${serverURL.replace('https://', '')}`;
+
+ }
+
+ /**
+ * Returns Date/Time and Duration of the conference in string format.
+ *
+ * @param {RecentListItem} conference - Conference Details.
+ * @returns {string} - Date/Time and Duration.
+ */
+ _renderTimeAndDuration(conference: RecentListItem) {
+ const { startTime, endTime } = conference;
+ const start = moment(startTime);
+ const end = moment(endTime);
+ const duration = moment.duration(end.diff(start)).humanize();
+
+ return `${start.calendar()}, ${duration}`;
+ }
+}
+
+/**
+ * Maps (parts of) the redux state to the React props.
+ *
+ * @param {Object} state - The redux state.
+ * @returns {{
+ * _recentList: Array<RecentListItem>
+ * }}
+ */
+function _mapStateToProps(state: Object) {
+ return {
+ _recentList: state.recentList.recentList
+ };
+}
+
+export default connect(_mapStateToProps)(RecentList);
diff --git a/app/features/recent-list/components/index.js b/app/features/recent-list/components/index.js
new file mode 100644
index 0000000..03545a4
--- /dev/null
+++ b/app/features/recent-list/components/index.js
@@ -0,0 +1 @@
+export { default as RecentList } from './RecentList';
diff --git a/app/features/recent-list/index.js b/app/features/recent-list/index.js
new file mode 100644
index 0000000..d04b4c3
--- /dev/null
+++ b/app/features/recent-list/index.js
@@ -0,0 +1,4 @@
+export * from './components';
+export * from './styled';
+
+export { default as reducer } from './reducer';
diff --git a/app/features/recent-list/reducer.js b/app/features/recent-list/reducer.js
new file mode 100644
index 0000000..13c15ac
--- /dev/null
+++ b/app/features/recent-list/reducer.js
@@ -0,0 +1,89 @@
+// @flow
+
+import { CONFERENCE_ENDED, CONFERENCE_JOINED } from '../conference';
+
+import type { RecentListItem } from './types';
+
+type State = {
+ recentList: Array<RecentListItem>;
+};
+
+const DEFAULT_STATE = {
+ recentList: []
+};
+
+/**
+ * Reduces redux actions for features/recent-list.
+ *
+ * @param {State} state - Current reduced redux state.
+ * @param {Object} action - Action which was dispatched.
+ * @returns {State} - Updated reduced redux state.
+ */
+export default (state: State = DEFAULT_STATE, action: Object) => {
+ switch (action.type) {
+ case CONFERENCE_ENDED:
+ return {
+ ...state,
+ recentList:
+ _updateEndtimeOfConference(state.recentList, action.conference)
+ };
+
+ case CONFERENCE_JOINED:
+ return {
+ ...state,
+ recentList: _insertConference(state.recentList, action.conference)
+ };
+
+ default:
+ return state;
+ }
+};
+
+/**
+ * Insert Conference details in the recent list array.
+ *
+ * @param {Array<RecentListItem>} recentList - Previous recent list array.
+ * @param {RecentListItem} newConference - Conference that has to be added
+ * to recent list.
+ * @returns {Array<RecentListItem>} - Updated recent list array.
+ */
+function _insertConference(
+ recentList: Array<RecentListItem>,
+ newConference: RecentListItem
+) {
+ // Add start time to conference.
+ newConference.startTime = Date.now();
+
+ // Remove same conference.
+ const newRecentList = recentList.filter(
+ (conference: RecentListItem) => conference.room !== newConference.room
+ || conference.serverURL !== newConference.serverURL);
+
+ // Add the conference at the beginning.
+ newRecentList.unshift(newConference);
+
+ return newRecentList;
+}
+
+/**
+ * Update the EndTime of the last conference.
+ *
+ * @param {Array<RecentListItem>} recentList - Previous recent list array.
+ * @param {RecentListItem} conference - Conference for which endtime has to
+ * be updated.
+ * @returns {Array<RecentListItem>} - Updated recent list array.
+ */
+function _updateEndtimeOfConference(
+ recentList: Array<RecentListItem>,
+ conference: RecentListItem
+) {
+ for (const item of recentList) {
+ if (item.room === conference.room
+ && item.serverURL === conference.serverURL) {
+ item.endTime = Date.now();
+ break;
+ }
+ }
+
+ return recentList;
+}
diff --git a/app/features/recent-list/styled/ConferenceCard.js b/app/features/recent-list/styled/ConferenceCard.js
new file mode 100644
index 0000000..fff3f75
--- /dev/null
+++ b/app/features/recent-list/styled/ConferenceCard.js
@@ -0,0 +1,18 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.div`
+ background: #1754A9;
+ border-radius: 0.5em;
+ color: white;
+ display: flex;
+ flex-direction: column;
+ font-size: 0.9em;
+ margin: 0.5em;
+ padding: 1em;
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
diff --git a/app/features/recent-list/styled/RecentListContainer.js b/app/features/recent-list/styled/RecentListContainer.js
new file mode 100644
index 0000000..b97ba86
--- /dev/null
+++ b/app/features/recent-list/styled/RecentListContainer.js
@@ -0,0 +1,9 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.div`
+ display: grid;
+ grid-template-columns: repeat(3, 33.3%);
+ padding: 0.5em;
+`;
diff --git a/app/features/recent-list/styled/TruncatedText.js b/app/features/recent-list/styled/TruncatedText.js
new file mode 100644
index 0000000..3a27ea7
--- /dev/null
+++ b/app/features/recent-list/styled/TruncatedText.js
@@ -0,0 +1,8 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.span`
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
diff --git a/app/features/recent-list/styled/index.js b/app/features/recent-list/styled/index.js
new file mode 100644
index 0000000..1affa12
--- /dev/null
+++ b/app/features/recent-list/styled/index.js
@@ -0,0 +1,3 @@
+export { default as ConferenceCard } from './ConferenceCard';
+export { default as RecentListContainer } from './RecentListContainer';
+export { default as TruncatedText } from './TruncatedText';
diff --git a/app/features/recent-list/types.js b/app/features/recent-list/types.js
new file mode 100644
index 0000000..1fef7fd
--- /dev/null
+++ b/app/features/recent-list/types.js
@@ -0,0 +1,24 @@
+// @flow
+
+export type RecentListItem = {
+
+ /**
+ * Timestamp of ending time of conference.
+ */
+ endTime: number;
+
+ /**
+ * Conference Room Name.
+ */
+ room: string;
+
+ /**
+ * Conference Server URL.
+ */
+ serverURL: string;
+
+ /**
+ * Timestamp of starting time of conference.
+ */
+ startTime: number;
+};