Add recent-list

This commit is contained in:
Akshit Kr Nagpal 2018-07-25 13:36:55 +02:00 committed by Saúl Ibarra Corretgé
parent d46c60e688
commit 8156f6cd07
18 changed files with 312 additions and 7 deletions

View File

@ -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);

View File

@ -0,0 +1 @@
export { default as RecentList } from './RecentList';

View File

@ -0,0 +1,4 @@
export * from './components';
export * from './styled';
export { default as reducer } from './reducer';

View File

@ -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;
}

View File

@ -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;
}
`;

View File

@ -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;
`;

View File

@ -0,0 +1,8 @@
// @flow
import styled from 'styled-components';
export default styled.span`
overflow: hidden;
text-overflow: ellipsis;
`;

View File

@ -0,0 +1,3 @@
export { default as ConferenceCard } from './ConferenceCard';
export { default as RecentListContainer } from './RecentListContainer';
export { default as TruncatedText } from './TruncatedText';

View File

@ -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;
};

View File

@ -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
});

View File

@ -11,6 +11,7 @@ const persistConfig = {
key: 'root',
storage: createElectronStorage(),
whitelist: [
'recentList',
'settings'
]
};

View File

@ -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>

View File

@ -0,0 +1,12 @@
// @flow
import styled from 'styled-components';
export default styled.div`
margin: 0 12.5%;
overflow: scroll;
::-webkit-scrollbar {
display: none;
}
`;

View File

@ -6,5 +6,5 @@ export default styled.div`
align-items: center;
display: flex;
margin: 0 auto;
padding: 30px;
padding: 8em;
`;

View File

@ -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;
`;

View File

@ -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';

5
package-lock.json generated
View File

@ -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",

View File

@ -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",