From 6059ca8aaaac23c37294adaf738eafbedf4662a8 Mon Sep 17 00:00:00 2001 From: Ari Lerner Date: Mon, 13 Jun 2016 15:02:48 -0700 Subject: [PATCH] Moved things around to set up mulitple pages --- package.json | 1 + src/app.js | 1 + src/components/Chat/styles.module.css | 3 - src/components/{Chat => Rooms}/ChatBox.js | 0 src/components/Rooms/Room.js | 64 ++++++++++ src/components/{Chat => Rooms}/VideoView.js | 10 +- src/components/Rooms/styles.module.css | 108 +++++++++++++++++ src/components/Video/VideoView.js | 40 ++++++ src/components/Video/styles.module.css | 9 ++ src/redux/modules/webrtc.js | 35 ++++-- src/styles/buttons.css | 27 +++++ src/styles/forms.css | 35 ++++++ src/views/main/Container.js | 2 + src/views/main/indexPage/Page.js | 92 ++++++-------- src/views/main/indexPage/styles.module.css | 127 ++++---------------- src/views/main/routes.js | 2 + src/views/main/styles.module.css | 5 + src/views/main/video/Page.js | 81 +++++++++++++ src/views/main/video/styles.module.css | 0 webpack.config.js | 2 +- 20 files changed, 465 insertions(+), 179 deletions(-) delete mode 100644 src/components/Chat/styles.module.css rename src/components/{Chat => Rooms}/ChatBox.js (100%) create mode 100644 src/components/Rooms/Room.js rename src/components/{Chat => Rooms}/VideoView.js (86%) create mode 100644 src/components/Rooms/styles.module.css create mode 100644 src/components/Video/VideoView.js create mode 100644 src/components/Video/styles.module.css create mode 100644 src/styles/buttons.css create mode 100644 src/styles/forms.css create mode 100644 src/views/main/video/Page.js create mode 100644 src/views/main/video/styles.module.css diff --git a/package.json b/package.json index 814fd0c..1d165f8 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "webpack": "^1.13.0" }, "dependencies": { + "chance": "^1.0.3", "classnames": "^2.2.5", "font-awesome": "^4.6.1", "freeice": "^2.2.0", diff --git a/src/app.js b/src/app.js index 783276b..97b8dbc 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom' require('file?name=favicon.ico!./favicon.ico') import 'font-awesome/css/font-awesome.css' + import './app.css' import App from 'containers/App/App' diff --git a/src/components/Chat/styles.module.css b/src/components/Chat/styles.module.css deleted file mode 100644 index a8e2242..0000000 --- a/src/components/Chat/styles.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.container { - flex: 1; -} diff --git a/src/components/Chat/ChatBox.js b/src/components/Rooms/ChatBox.js similarity index 100% rename from src/components/Chat/ChatBox.js rename to src/components/Rooms/ChatBox.js diff --git a/src/components/Rooms/Room.js b/src/components/Rooms/Room.js new file mode 100644 index 0000000..2aaadfd --- /dev/null +++ b/src/components/Rooms/Room.js @@ -0,0 +1,64 @@ +import React from 'react' + +import VideoView from 'components/Video/VideoView' +import styles from './styles.module.css' + +export class Room extends React.Component { + leaveRoom(room, evt) { + this.props.leaveRoom(room); + } + render() { + const {name, ready, peers, localStream} = this.props; + + return ( +
+
+
{name}
+
+ +
+
+
+ +
+ +
+ +
+
+ + Invite +
+
+ {peers.map(peer => { + return ( +
+ +
+ ) + })} +
+
+ +
+
+ + San Francisco, CA +
+
+ + 2:00pm - 2:30pm +
+
+ +
+
+ ) + } +} + +export default Room; diff --git a/src/components/Chat/VideoView.js b/src/components/Rooms/VideoView.js similarity index 86% rename from src/components/Chat/VideoView.js rename to src/components/Rooms/VideoView.js index 4ef715e..e5546a3 100644 --- a/src/components/Chat/VideoView.js +++ b/src/components/Rooms/VideoView.js @@ -1,15 +1,13 @@ import React, { PropTypes as T } from 'react' import ReactDOM from 'react-dom' +import attachMediaStream from 'attachmediastream' import styles from './styles.module.css'; -import attachMediaStream from 'attachmediastream' - export class VideoView extends React.Component { static propTypes = { ready: T.bool, - stream: T.object, - peer: T.object + stream: T.object } componentDidMount() { @@ -31,9 +29,9 @@ export class VideoView extends React.Component { render() { return ( -
+
+ ref='videoView' />
) } diff --git a/src/components/Rooms/styles.module.css b/src/components/Rooms/styles.module.css new file mode 100644 index 0000000..ac7c869 --- /dev/null +++ b/src/components/Rooms/styles.module.css @@ -0,0 +1,108 @@ +@import url('../../styles/base.css'); +@import url('../../styles/colors.css'); + +:root { + --person-video-height: 75px; +} + +.container { + border-radius: 12px; + overflow: hidden; + margin: 60px; + box-shadow: 0px 2px 2px var(--dark); + border: 0.5px solid var(--dark); + padding: 0; + + width: 450px; + display: flex; + flex-direction: column; + + i { + color: var(--light-gray); + } + + .header { + flex: 1; + margin: 10px; + padding: 0; + display: flex; + justify-content: space-between; + align-items: center; + + .controls { + order: 2; + text-align: right; + i { + font-size: 12px; + } + } + .title { + order: 1; + } + } + + .peers { + display: flex; + margin: 10px; + padding: 0px; + align-items: center; + justify-content: space-between; + z-index: -1; + + i { + margin: 0 10px; + } + + .invited { + flex: 1; + } + + .people { + /*flex: 1;*/ + flex-direction: column; + justify-content: flex-end; + align-items: flex-end; + + .person { + display: inline-block; + background-color: var(--dark); + height: var(--person-video-height); + width: var(--person-video-height); + border-radius: calc(var(--person-video-height)/2); + margin-left: var(--padding); + + video { + background-color: var(--dark); + border-radius: calc(var(--person-video-height)/2); + width: var(--person-video-height); + height: var(--person-video-height); + } + } + } + } + + .footer { + min-height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px; + box-shadow: 0px -2px 1px var(--dark); + + .location, .time { + flex: 1; + padding: 10px; + font-weight: normal; + i { + margin: 0 10px; + font-size: 12px; + } + } + + .location { + } + .time { + text-align: right; + } + } +} diff --git a/src/components/Video/VideoView.js b/src/components/Video/VideoView.js new file mode 100644 index 0000000..e5546a3 --- /dev/null +++ b/src/components/Video/VideoView.js @@ -0,0 +1,40 @@ +import React, { PropTypes as T } from 'react' +import ReactDOM from 'react-dom' +import attachMediaStream from 'attachmediastream' + +import styles from './styles.module.css'; + +export class VideoView extends React.Component { + static propTypes = { + ready: T.bool, + stream: T.object + } + + componentDidMount() { + if (this.props.ready && this.props.stream) { + this.attachVideo(this.props.stream); + } + } + + componentWillReceiveProps(nextProps) { + if (nextProps.ready && nextProps.stream !== this.props.stream) { + this.attachVideo(nextProps.stream); + } + } + + attachVideo(stream) { + let node = ReactDOM.findDOMNode(this.refs.videoView); + attachMediaStream(stream, node); + } + + render() { + return ( +
+
+ ) + } +} + +export default VideoView diff --git a/src/components/Video/styles.module.css b/src/components/Video/styles.module.css new file mode 100644 index 0000000..408b45a --- /dev/null +++ b/src/components/Video/styles.module.css @@ -0,0 +1,9 @@ +.videoContainer { + flex: 1; + justify-content: center; + align-items: center; + + video { + width: 100%; + } +} diff --git a/src/redux/modules/webrtc.js b/src/redux/modules/webrtc.js index ef5f8c1..dd57911 100644 --- a/src/redux/modules/webrtc.js +++ b/src/redux/modules/webrtc.js @@ -2,6 +2,7 @@ import {createConstants, createReducer} from 'redux-module-builder' import SimpleWebRTC from 'SimpleWebRTC' import freeice from 'freeice' +let rtc = null; export const types = createConstants('webrtc')( 'INIT', 'NEW_PEER_ADDED', @@ -12,7 +13,9 @@ export const types = createConstants('webrtc')( 'PEER_STREAM_ADDED', 'PEER_STREAM_REMOVED', 'CONNECTION_READY', - 'JOIN_ROOM' + + 'JOIN_ROOM', + 'LEAVE_ROOM' ) export const reducer = createReducer({ @@ -37,20 +40,30 @@ export const reducer = createReducer({ }), [types.PEER_STREAM_ADDED]: (state, {payload}) => { - const peers = state.webrtc.webrtc.getPeers(); + const peers = rtc.webrtc.getPeers(); return {...state, peers} }, [types.PEER_STREAM_REMOVED]: (state, {payload}) => { - const peers = state.webrtc.webrtc.getPeers(); + const peers = rtc.webrtc.getPeers(); return {...state, peers} }, [types.JOIN_ROOM]: (state, {payload}) => ({ ...state, - currentRoom: payload - }) + currentRooms: state.currentRooms.concat(payload) + }), + [types.LEAVE_ROOM]: (state, {payload}) => { + let currentRooms = [].concat(state.currentRooms); + const idx = currentRooms.indexOf(payload); + if (idx >= 0) { + currentRooms.splice(idx, 1); + } + return { + ...state, + currentRooms + } + } }) -let rtc = null; export const actions = { init: (cfg) => (dispatch, getState) => { rtc = new SimpleWebRTC({ @@ -73,18 +86,19 @@ export const actions = { }); dispatch({type: types.INIT, payload: rtc}); }, - newPeer: (peer) => ({type: types.NEW_PEER_ADDED, payload: peer}), - removePeer: (peer) => ({type: types.PEER_STREAM_REMOVED, payload: peer}), joinRoom: (room) => { rtc.joinRoom(room); return {type: types.JOIN_ROOM, payload: room} }, + leaveRoom: (room) => { + return {type: types.LEAVE_ROOM, payload: room} + }, startLocalMedia: (config = {}) => (dispatch) => { const cfg = Object.assign({}, rtc.config.media, config) rtc.webrtc.startLocalMedia(cfg, (err, stream) => { if (err) { - webrtc.emit('localMediaError', err); - dispatch({type: types.LOCAL_MEDIA_ERROR, payload: err}) + webrtc.emit('localMediaError', err); + dispatch({type: types.LOCAL_MEDIA_ERROR, payload: err}) } else { dispatch({type: types.LOCAL_MEDIA_START, payload: stream}) } @@ -95,6 +109,7 @@ export const actions = { export const initialState = { ready: false, peers: [], + currentRooms: [], id: null, webrtc: null } diff --git a/src/styles/buttons.css b/src/styles/buttons.css new file mode 100644 index 0000000..c0e9117 --- /dev/null +++ b/src/styles/buttons.css @@ -0,0 +1,27 @@ +@import url('./colors.css'); + +/*buttons*/ +.btn { + display: inline-block; + margin: 10px; + padding: 15px 45px; + font-size: 28px; + line-height: 1.8; + appearance: none; + box-shadow: none; + border: none; + border-radius: 50px; + + color: #fff; + background-color: var(--highlight); + text-shadow: -1px 1px #417cb8; + + &:hover { + background-color: #346392; + text-shadow: -1px 1px #27496d; + } + + &:focus { + outline: none; + } +} diff --git a/src/styles/forms.css b/src/styles/forms.css new file mode 100644 index 0000000..89395be --- /dev/null +++ b/src/styles/forms.css @@ -0,0 +1,35 @@ +@import url('./base.css'); +@import url('./colors.css'); + +/*buttons*/ +.btn { + display: inline-block; + margin: 10px; + padding: 15px 45px; + font-size: 28px; + line-height: 1.8; + appearance: none; + box-shadow: none; + border: none; + border-radius: 50px; + + color: #fff; + background-color: var(--highlight); + text-shadow: -1px 1px #417cb8; + + &:hover { + background-color: #346392; + text-shadow: -1px 1px #27496d; + } + + &:focus { + outline: none; + } +} + +.form { + input { + font-size: 20px; + padding: var(--padding); + } +} diff --git a/src/views/main/Container.js b/src/views/main/Container.js index fd051b7..c2a44fb 100644 --- a/src/views/main/Container.js +++ b/src/views/main/Container.js @@ -1,5 +1,7 @@ import React, { PropTypes as T } from 'react' import Header from 'components/Header/Header' + +import btnStyles from 'styles/buttons.css' import styles from './styles.module.css' export class Container extends React.Component { diff --git a/src/views/main/indexPage/Page.js b/src/views/main/indexPage/Page.js index 8f52b0a..cc7849b 100644 --- a/src/views/main/indexPage/Page.js +++ b/src/views/main/indexPage/Page.js @@ -1,26 +1,16 @@ import React, { PropTypes as T } from 'react' import ReactDOM from 'react-dom' + +import formStyles from 'styles/forms.css' import styles from './styles.module.css' +import Chance from 'chance' import {connect} from 'react-redux' -import VideoView from 'components/Chat/VideoView' +import Room from 'components/Rooms/Room' export class Index extends React.Component { componentDidMount() { - const {actions} = this.props; - actions.webrtc.init({debug: false}); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.ready && !this.props.ready) { - this.startLocalMedia(); - } - } - - startLocalMedia() { - const {webrtc, actions} = this.props; - actions.webrtc.joinRoom('fullstackio'); - actions.webrtc.startLocalMedia(); + const {actions, params} = this.props; } goToAbout(evt) { @@ -30,54 +20,41 @@ export class Index extends React.Component { return false; } + newChat(evt) { + evt.preventDefault(); + const {actions} = this.props; + let roomName = this.refs.roomName && this.refs.roomName.value; + roomName = roomName || chance.word({length: 8}); + actions.routing.navigateTo(`room/${roomName}`) + return false; + } + + leaveRoom(roomName) { + const {actions} = this.props; + actions.webrtc.leaveRoom(roomName); + } + render() { - const {ready, peers, localStream} = this.props; + const {currentRooms} = this.props; return (
-
-
Video chat
-
- -
-
-
- -
- -
- -
-
- - Invite -
-
- {peers.map(peer => { - return ( -
- -
- ) - })} -
-
- -
-
- - San Francisco, CA -
-
- - 2:00pm - 2:30pm +
+
+
+
+ + +
-
) @@ -94,4 +71,5 @@ export default connect(state => ({ localStream: state.webrtc.localStream, ready: state.webrtc.ready, peers: state.webrtc.peers, + currentRooms: state.webrtc.currentRooms }))(Index); diff --git a/src/views/main/indexPage/styles.module.css b/src/views/main/indexPage/styles.module.css index 2dd1bc6..7246816 100644 --- a/src/views/main/indexPage/styles.module.css +++ b/src/views/main/indexPage/styles.module.css @@ -1,117 +1,40 @@ @import url('../../../styles/base.css'); @import url('../../../styles/colors.css'); -:root { - --person-video-height: 75px; +.room { } -.container { - border-radius: 12px; - overflow: hidden; - margin: 60px; - box-shadow: 0px 2px 2px var(--dark); - border: 0.5px solid var(--dark); - padding: 0; - width: 450px; - display: flex; - flex-direction: column; - - i { - color: var(--light-gray); - } - - .header { - flex: 1; - margin: 10px; - padding: 0; - display: flex; - justify-content: space-between; - align-items: center; - - .controls { - order: 2; - text-align: right; - i { - font-size: 12px; - } - } - .title { - order: 1; - } - } - - .peers { - display: flex; - margin: 10px; - padding: 0px; - align-items: center; - justify-content: space-between; - z-index: -1; - - i { - margin: 0 10px; - } +.actions { + text-align: center; +} - .invited { - flex: 1; - } +.grid { +} - .people { - /*flex: 1;*/ - flex-direction: column; - justify-content: flex-end; - align-items: flex-end; +.row { + display: flex; + flex-direction: row; + flex-wrap: wrap; +} - .person { - display: inline-block; - background-color: var(--dark); - height: var(--person-video-height); - width: var(--person-video-height); - border-radius: calc(var(--person-video-height)/2); - margin-left: var(--padding); +.room { + flex: 1; +} - video { - background-color: var(--dark); - border-radius: calc(var(--person-video-height)/2); - width: var(--person-video-height); - height: var(--person-video-height); - } - } - } +@media all and ( min-width: 480px ) { + .smallRow { + flex-direction: row; } +} - .footer { - min-height: 50px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 5px; - box-shadow: 0px -2px 1px var(--dark); - - .location, .time { - flex: 1; - padding: 10px; - font-weight: normal; - i { - margin: 0 10px; - font-size: 12px; - } - } - - .location { - } - .time { - text-align: right; - } +@media all and ( min-width: 720px ) { + .row-md { + flex-direction: row; } +} - .videoContainer { - flex: 1; - justify-content: center; - align-items: center; - - video { - width: 100%; - } +@media all and ( min-width: 960px ) { + .row-lg { + flex-direction: row; } } diff --git a/src/views/main/routes.js b/src/views/main/routes.js index c45823e..91606bb 100644 --- a/src/views/main/routes.js +++ b/src/views/main/routes.js @@ -4,11 +4,13 @@ import {Route, IndexRoute} from 'react-router' import Container from './Container' import About from './about/About' import IndexPage from './indexPage/Page' +import VideoPage from './video/Page' export const makeMainRoutes = () => { return ( + ) diff --git a/src/views/main/styles.module.css b/src/views/main/styles.module.css index cc5096e..c847bd2 100644 --- a/src/views/main/styles.module.css +++ b/src/views/main/styles.module.css @@ -30,6 +30,11 @@ padding: var(--padding); } + .action { + text-align: center; + margin: 10px auto; + } + @media (--screen-phone-lg) { flex: 2; order: 2; diff --git a/src/views/main/video/Page.js b/src/views/main/video/Page.js new file mode 100644 index 0000000..deb404c --- /dev/null +++ b/src/views/main/video/Page.js @@ -0,0 +1,81 @@ +import React, { PropTypes as T } from 'react' +import ReactDOM from 'react-dom' + +import btnStyles from 'styles/buttons.css'; +import styles from './styles.module.css' + +import Chance from 'chance' +import {connect} from 'react-redux' +import Room from 'components/Rooms/Room' + +export class VideoIndex extends React.Component { + componentDidMount() { + const {actions, params} = this.props; + actions.webrtc.init({debug: false}); + + this.startLocalMedia(this.props.ready); + + if (params && params.roomName) { + this.joinRoom(params.roomName); + } + } + + componentWillReceiveProps(nextProps) { + if (nextProps.ready !== this.props.ready) { + this.startLocalMedia(nextProps.ready); + } + + if (nextProps.params && nextProps.params.roomName && + nextProps.params.roomName !== this.props.params.roomName) { + const {webrtc, actions} = this.props; + this.joinRoom(nextProps.params.roomName); + } + } + + joinRoom(roomName) { + const {webrtc, actions} = this.props; + actions.webrtc.joinRoom(roomName); + } + + startLocalMedia(ready) { + if (ready) { + const {webrtc, actions} = this.props; + actions.webrtc.startLocalMedia(); + } + } + + leaveRoom(roomName) { + const {actions} = this.props; + actions.webrtc.leaveRoom(roomName); + actions.routing.navigateTo('/') + } + + render() { + const {params} = this.props; + const roomName = params.roomName; + + return ( +
+
+
+ +
+
+
+ ) + } +} + +VideoIndex.contextTypes = { + router: T.object +} + +export default connect(state => ({ + id: state.webrtc.id, + localStream: state.webrtc.localStream, + ready: state.webrtc.ready, + peers: state.webrtc.peers +}))(VideoIndex); diff --git a/src/views/main/video/styles.module.css b/src/views/main/video/styles.module.css new file mode 100644 index 0000000..e69de29 diff --git a/webpack.config.js b/webpack.config.js index 20f946e..a6c0c77 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -84,7 +84,7 @@ cssloader.loader = newloader.loader; config.module.loaders.push({ test: /\.css$/, - include: [modules, css], + include: [modules], loader: 'style!css' });