diff options
author | Akshit Kr Nagpal <akshitkrnagpal@gmail.com> | 2018-07-25 13:36:55 +0200 |
---|---|---|
committer | Saúl Ibarra Corretgé <s@saghul.net> | 2018-07-27 09:03:51 +0200 |
commit | 8156f6cd0786c9980da6546b3e38ad0c51e4cfbf (patch) | |
tree | 2ede6b65b02f769a8eaa77b46284a2bf87713b36 /app | |
parent | d46c60e6884bd7346467184927a87df81a18ccb2 (diff) |
Add recent-list
Diffstat (limited to 'app')
-rw-r--r-- | app/features/recent-list/components/RecentList.js | 122 | ||||
-rw-r--r-- | app/features/recent-list/components/index.js | 1 | ||||
-rw-r--r-- | app/features/recent-list/index.js | 4 | ||||
-rw-r--r-- | app/features/recent-list/reducer.js | 89 | ||||
-rw-r--r-- | app/features/recent-list/styled/ConferenceCard.js | 18 | ||||
-rw-r--r-- | app/features/recent-list/styled/RecentListContainer.js | 9 | ||||
-rw-r--r-- | app/features/recent-list/styled/TruncatedText.js | 8 | ||||
-rw-r--r-- | app/features/recent-list/styled/index.js | 3 | ||||
-rw-r--r-- | app/features/recent-list/types.js | 24 | ||||
-rw-r--r-- | app/features/redux/reducers.js | 2 | ||||
-rw-r--r-- | app/features/redux/store.js | 1 | ||||
-rw-r--r-- | app/features/welcome/components/Welcome.js | 10 | ||||
-rw-r--r-- | app/features/welcome/styled/Body.js | 12 | ||||
-rw-r--r-- | app/features/welcome/styled/Header.js (renamed from app/features/welcome/styled/Content.js) | 2 | ||||
-rw-r--r-- | app/features/welcome/styled/Wrapper.js (renamed from app/features/welcome/styled/WelcomeWrapper.js) | 3 | ||||
-rw-r--r-- | app/features/welcome/styled/index.js | 5 |
16 files changed, 306 insertions, 7 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; +}; 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<Props, State> { <Page navigation = { <Navbar /> }> <AtlasKitThemeProvider mode = 'light'> <Wrapper> - <Content> + <Header> <Form onSubmit = { this._onFormSubmit }> <FieldTextStateless autoFocus = { true } @@ -99,7 +100,10 @@ class Welcome extends Component<Props, State> { type = 'button'> GO </Button> - </Content> + </Header> + <Body> + <RecentList /> + </Body> </Wrapper> </AtlasKitThemeProvider> </Page> 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 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 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'; |