From 8156f6cd0786c9980da6546b3e38ad0c51e4cfbf Mon Sep 17 00:00:00 2001 From: Akshit Kr Nagpal Date: Wed, 25 Jul 2018 13:36:55 +0200 Subject: [PATCH] Add recent-list --- .../recent-list/components/RecentList.js | 122 ++++++++++++++++++ app/features/recent-list/components/index.js | 1 + app/features/recent-list/index.js | 4 + app/features/recent-list/reducer.js | 89 +++++++++++++ .../recent-list/styled/ConferenceCard.js | 18 +++ .../recent-list/styled/RecentListContainer.js | 9 ++ .../recent-list/styled/TruncatedText.js | 8 ++ app/features/recent-list/styled/index.js | 3 + app/features/recent-list/types.js | 24 ++++ app/features/redux/reducers.js | 2 + app/features/redux/store.js | 1 + app/features/welcome/components/Welcome.js | 10 +- app/features/welcome/styled/Body.js | 12 ++ .../welcome/styled/{Content.js => Header.js} | 2 +- .../styled/{WelcomeWrapper.js => Wrapper.js} | 3 +- app/features/welcome/styled/index.js | 5 +- package-lock.json | 5 + package.json | 1 + 18 files changed, 312 insertions(+), 7 deletions(-) create mode 100644 app/features/recent-list/components/RecentList.js create mode 100644 app/features/recent-list/components/index.js create mode 100644 app/features/recent-list/index.js create mode 100644 app/features/recent-list/reducer.js create mode 100644 app/features/recent-list/styled/ConferenceCard.js create mode 100644 app/features/recent-list/styled/RecentListContainer.js create mode 100644 app/features/recent-list/styled/TruncatedText.js create mode 100644 app/features/recent-list/styled/index.js create mode 100644 app/features/recent-list/types.js create mode 100644 app/features/welcome/styled/Body.js rename app/features/welcome/styled/{Content.js => Header.js} (88%) rename app/features/welcome/styled/{WelcomeWrapper.js => Wrapper.js} (69%) 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; +}; + +/** + * Recent List Component. + */ +class RecentList extends Component { + /** + * Render function of component. + * + * @returns {ReactElement} + */ + render() { + return ( + + { + this.props._recentList.map( + conference => this._renderRecentListEntry(conference) + ) + } + + ); + } + + /** + * 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 ( + + + { conference.room } + + + { this._renderServerURL(conference.serverURL) } + + + { this._renderTimeAndDuration(conference) } + + + ); + } + + /** + * 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 + * }} + */ +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; +}; + +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} recentList - Previous recent list array. + * @param {RecentListItem} newConference - Conference that has to be added + * to recent list. + * @returns {Array} - Updated recent list array. + */ +function _insertConference( + recentList: Array, + 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} recentList - Previous recent list array. + * @param {RecentListItem} conference - Conference for which endtime has to + * be updated. + * @returns {Array} - Updated recent list array. + */ +function _updateEndtimeOfConference( + recentList: Array, + 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; +}; diff --git a/app/features/redux/reducers.js b/app/features/redux/reducers.js index 667b922..2f532e6 100644 --- a/app/features/redux/reducers.js +++ b/app/features/redux/reducers.js @@ -3,11 +3,13 @@ import { combineReducers } from 'redux'; import { reducer as navbarReducer } from '../navbar'; +import { reducer as recentListReducer } from '../recent-list'; import { reducer as routerReducer } from '../router'; import { reducer as settingsReducer } from '../settings'; export default combineReducers({ navbar: navbarReducer, + recentList: recentListReducer, router: routerReducer, settings: settingsReducer }); diff --git a/app/features/redux/store.js b/app/features/redux/store.js index 992e802..472cdbd 100644 --- a/app/features/redux/store.js +++ b/app/features/redux/store.js @@ -11,6 +11,7 @@ const persistConfig = { key: 'root', storage: createElectronStorage(), whitelist: [ + 'recentList', 'settings' ] }; diff --git a/app/features/welcome/components/Welcome.js b/app/features/welcome/components/Welcome.js index 648d0ef..519016d 100644 --- a/app/features/welcome/components/Welcome.js +++ b/app/features/welcome/components/Welcome.js @@ -11,9 +11,10 @@ import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import { Navbar } from '../../navbar'; +import { RecentList } from '../../recent-list'; import { normalizeServerURL } from '../../utils'; -import { WelcomeWrapper as Wrapper, Content, Form } from '../styled'; +import { Body, Form, Header, Wrapper } from '../styled'; type Props = { @@ -82,7 +83,7 @@ class Welcome extends Component { }> - +
{ type = 'button'> GO - +
+ + +
diff --git a/app/features/welcome/styled/Body.js b/app/features/welcome/styled/Body.js new file mode 100644 index 0000000..ad74e1c --- /dev/null +++ b/app/features/welcome/styled/Body.js @@ -0,0 +1,12 @@ +// @flow + +import styled from 'styled-components'; + +export default styled.div` + margin: 0 12.5%; + overflow: scroll; + + ::-webkit-scrollbar { + display: none; + } +`; diff --git a/app/features/welcome/styled/Content.js b/app/features/welcome/styled/Header.js similarity index 88% rename from app/features/welcome/styled/Content.js rename to app/features/welcome/styled/Header.js index 33fcb67..e05f5d4 100644 --- a/app/features/welcome/styled/Content.js +++ b/app/features/welcome/styled/Header.js @@ -6,5 +6,5 @@ export default styled.div` align-items: center; display: flex; margin: 0 auto; - padding: 30px; + padding: 8em; `; diff --git a/app/features/welcome/styled/WelcomeWrapper.js b/app/features/welcome/styled/Wrapper.js similarity index 69% rename from app/features/welcome/styled/WelcomeWrapper.js rename to app/features/welcome/styled/Wrapper.js index 3fe50b7..5771c30 100644 --- a/app/features/welcome/styled/WelcomeWrapper.js +++ b/app/features/welcome/styled/Wrapper.js @@ -3,7 +3,8 @@ import styled from 'styled-components'; export default styled.div` - background: linear-gradient(#165ecc,#44A5FF); + background: #1D69D4; display: flex; + flex-direction: column; height: 100vh; `; diff --git a/app/features/welcome/styled/index.js b/app/features/welcome/styled/index.js index 6b132d9..191461c 100644 --- a/app/features/welcome/styled/index.js +++ b/app/features/welcome/styled/index.js @@ -1,3 +1,4 @@ -export { default as Content } from './Content'; +export { default as Body } from './Body'; export { default as Form } from './Form'; -export { default as WelcomeWrapper } from './WelcomeWrapper'; +export { default as Header } from './Header'; +export { default as Wrapper } from './Wrapper'; diff --git a/package-lock.json b/package-lock.json index 14c8a4d..d68d542 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7129,6 +7129,11 @@ } } }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, "mousetrap": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.2.tgz", diff --git a/package.json b/package.json index a5c7234..27fdc8d 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "history": "4.7.2", "jitsi-meet-electron-utils": "github:jitsi/jitsi-meet-electron-utils#1972c3bf0884ace68eb496894dabae593d6dbf49", "js-utils": "github:jitsi/js-utils#0c53500a5120be2aa3fc590f0f932a0d4771920f", + "moment": "2.22.2", "mousetrap": "1.6.2", "react": "16.4.1", "react-dom": "16.4.1",