mirror of
https://github.com/netzbegruenung/jitsi-meet-electron.git
synced 2024-05-05 02:03:41 +02:00
Add recent-list
This commit is contained in:
parent
d46c60e688
commit
8156f6cd07
122
app/features/recent-list/components/RecentList.js
Normal file
122
app/features/recent-list/components/RecentList.js
Normal 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);
|
1
app/features/recent-list/components/index.js
Normal file
1
app/features/recent-list/components/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default as RecentList } from './RecentList';
|
4
app/features/recent-list/index.js
Normal file
4
app/features/recent-list/index.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './components';
|
||||||
|
export * from './styled';
|
||||||
|
|
||||||
|
export { default as reducer } from './reducer';
|
89
app/features/recent-list/reducer.js
Normal file
89
app/features/recent-list/reducer.js
Normal 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;
|
||||||
|
}
|
18
app/features/recent-list/styled/ConferenceCard.js
Normal file
18
app/features/recent-list/styled/ConferenceCard.js
Normal 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;
|
||||||
|
}
|
||||||
|
`;
|
9
app/features/recent-list/styled/RecentListContainer.js
Normal file
9
app/features/recent-list/styled/RecentListContainer.js
Normal 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;
|
||||||
|
`;
|
8
app/features/recent-list/styled/TruncatedText.js
Normal file
8
app/features/recent-list/styled/TruncatedText.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export default styled.span`
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
`;
|
3
app/features/recent-list/styled/index.js
Normal file
3
app/features/recent-list/styled/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as ConferenceCard } from './ConferenceCard';
|
||||||
|
export { default as RecentListContainer } from './RecentListContainer';
|
||||||
|
export { default as TruncatedText } from './TruncatedText';
|
24
app/features/recent-list/types.js
Normal file
24
app/features/recent-list/types.js
Normal 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;
|
||||||
|
};
|
|
@ -3,11 +3,13 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
|
|
||||||
import { reducer as navbarReducer } from '../navbar';
|
import { reducer as navbarReducer } from '../navbar';
|
||||||
|
import { reducer as recentListReducer } from '../recent-list';
|
||||||
import { reducer as routerReducer } from '../router';
|
import { reducer as routerReducer } from '../router';
|
||||||
import { reducer as settingsReducer } from '../settings';
|
import { reducer as settingsReducer } from '../settings';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
navbar: navbarReducer,
|
navbar: navbarReducer,
|
||||||
|
recentList: recentListReducer,
|
||||||
router: routerReducer,
|
router: routerReducer,
|
||||||
settings: settingsReducer
|
settings: settingsReducer
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@ const persistConfig = {
|
||||||
key: 'root',
|
key: 'root',
|
||||||
storage: createElectronStorage(),
|
storage: createElectronStorage(),
|
||||||
whitelist: [
|
whitelist: [
|
||||||
|
'recentList',
|
||||||
'settings'
|
'settings'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,9 +11,10 @@ import { connect } from 'react-redux';
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
|
|
||||||
import { Navbar } from '../../navbar';
|
import { Navbar } from '../../navbar';
|
||||||
|
import { RecentList } from '../../recent-list';
|
||||||
import { normalizeServerURL } from '../../utils';
|
import { normalizeServerURL } from '../../utils';
|
||||||
|
|
||||||
import { WelcomeWrapper as Wrapper, Content, Form } from '../styled';
|
import { Body, Form, Header, Wrapper } from '../styled';
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -82,7 +83,7 @@ class Welcome extends Component<Props, State> {
|
||||||
<Page navigation = { <Navbar /> }>
|
<Page navigation = { <Navbar /> }>
|
||||||
<AtlasKitThemeProvider mode = 'light'>
|
<AtlasKitThemeProvider mode = 'light'>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Content>
|
<Header>
|
||||||
<Form onSubmit = { this._onFormSubmit }>
|
<Form onSubmit = { this._onFormSubmit }>
|
||||||
<FieldTextStateless
|
<FieldTextStateless
|
||||||
autoFocus = { true }
|
autoFocus = { true }
|
||||||
|
@ -99,7 +100,10 @@ class Welcome extends Component<Props, State> {
|
||||||
type = 'button'>
|
type = 'button'>
|
||||||
GO
|
GO
|
||||||
</Button>
|
</Button>
|
||||||
</Content>
|
</Header>
|
||||||
|
<Body>
|
||||||
|
<RecentList />
|
||||||
|
</Body>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</AtlasKitThemeProvider>
|
</AtlasKitThemeProvider>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
12
app/features/welcome/styled/Body.js
Normal file
12
app/features/welcome/styled/Body.js
Normal 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;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -6,5 +6,5 @@ export default styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 30px;
|
padding: 8em;
|
||||||
`;
|
`;
|
|
@ -3,7 +3,8 @@
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export default styled.div`
|
export default styled.div`
|
||||||
background: linear-gradient(#165ecc,#44A5FF);
|
background: #1D69D4;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
height: 100vh;
|
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 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
5
package-lock.json
generated
|
@ -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": {
|
"mousetrap": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.2.tgz",
|
||||||
|
|
|
@ -93,6 +93,7 @@
|
||||||
"history": "4.7.2",
|
"history": "4.7.2",
|
||||||
"jitsi-meet-electron-utils": "github:jitsi/jitsi-meet-electron-utils#1972c3bf0884ace68eb496894dabae593d6dbf49",
|
"jitsi-meet-electron-utils": "github:jitsi/jitsi-meet-electron-utils#1972c3bf0884ace68eb496894dabae593d6dbf49",
|
||||||
"js-utils": "github:jitsi/js-utils#0c53500a5120be2aa3fc590f0f932a0d4771920f",
|
"js-utils": "github:jitsi/js-utils#0c53500a5120be2aa3fc590f0f932a0d4771920f",
|
||||||
|
"moment": "2.22.2",
|
||||||
"mousetrap": "1.6.2",
|
"mousetrap": "1.6.2",
|
||||||
"react": "16.4.1",
|
"react": "16.4.1",
|
||||||
"react-dom": "16.4.1",
|
"react-dom": "16.4.1",
|
||||||
|
|
Loading…
Reference in a new issue