Add recent-list
This commit is contained in:
parent
d46c60e688
commit
8156f6cd07
|
@ -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);
|
|
@ -0,0 +1 @@
|
|||
export { default as RecentList } from './RecentList';
|
|
@ -0,0 +1,4 @@
|
|||
export * from './components';
|
||||
export * from './styled';
|
||||
|
||||
export { default as reducer } from './reducer';
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
`;
|
|
@ -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;
|
||||
`;
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
export default styled.span`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
|
@ -0,0 +1,3 @@
|
|||
export { default as ConferenceCard } from './ConferenceCard';
|
||||
export { default as RecentListContainer } from './RecentListContainer';
|
||||
export { default as TruncatedText } from './TruncatedText';
|
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ const persistConfig = {
|
|||
key: 'root',
|
||||
storage: createElectronStorage(),
|
||||
whitelist: [
|
||||
'recentList',
|
||||
'settings'
|
||||
]
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
export default styled.div`
|
||||
margin: 0 12.5%;
|
||||
overflow: scroll;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
|
@ -6,5 +6,5 @@ export default styled.div`
|
|||
align-items: center;
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
padding: 8em;
|
||||
`;
|
|
@ -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;
|
||||
`;
|
|
@ -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';
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue