Room.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import {
  2. Actions,
  3. Connected,
  4. Connecting,
  5. Disconnected,
  6. LocalMediaList,
  7. Provider,
  8. RemoteAudioPlayer,
  9. Room
  10. } from '@andyet/simplewebrtc';
  11. import React, { Component } from 'react';
  12. import { connect } from 'react-redux';
  13. import { RouteComponentProps } from 'react-router';
  14. import styled from 'styled-components';
  15. import ChatContainer from '../components/ChatContainer';
  16. import ChatToggle from '../components/ChatToggle';
  17. import Haircheck from '../components/Haircheck';
  18. import PasswordEntry from '../components/PasswordEntry';
  19. import PeerGrid from '../components/PeerGrid';
  20. import Sidebar from '../components/Sidebar';
  21. import mq from '../styles/media-queries';
  22. const PasswordEntryContainer = styled.div({
  23. display: 'flex',
  24. alignItems: 'center',
  25. justifyContent: 'center',
  26. width: '100vw',
  27. height: '100vh'
  28. });
  29. const Container = styled.div({
  30. display: 'flex',
  31. position: 'relative',
  32. flexDirection: 'column',
  33. minHeight: '100vh',
  34. [mq.SMALL_DESKTOP]: {
  35. flexDirection: 'row'
  36. }
  37. });
  38. const LoadingState = styled.div({
  39. width: '100vw',
  40. height: '100vh',
  41. display: 'flex',
  42. justifyContent: 'center',
  43. alignItems: 'center'
  44. });
  45. interface MatchParams {
  46. roomName: string;
  47. }
  48. interface Props extends RouteComponentProps<MatchParams> {
  49. configUrl: string;
  50. mute?: () => void;
  51. unmute?: () => void;
  52. }
  53. interface State {
  54. activeSpeakerView: boolean;
  55. pttMode: boolean;
  56. sendRtt: boolean;
  57. password?: string;
  58. chatOpen: boolean;
  59. }
  60. class Index extends Component<Props, State> {
  61. constructor(props: Props) {
  62. super(props);
  63. this.state = {
  64. activeSpeakerView: false,
  65. password: undefined,
  66. pttMode: false,
  67. sendRtt: false,
  68. chatOpen: false
  69. };
  70. }
  71. public render() {
  72. const roomName = this.props.match.params.roomName;
  73. return (
  74. <Provider configUrl={this.props.configUrl}>
  75. <LocalMediaList
  76. render={({ media }) => (
  77. <>
  78. {media.filter(m => m.shared).length === 0 ? (
  79. <Haircheck />
  80. ) : (
  81. <>
  82. <RemoteAudioPlayer />
  83. <Connecting configUrl="">
  84. <LoadingState>
  85. <h1>Connecting...</h1>
  86. </LoadingState>
  87. </Connecting>
  88. <Disconnected configUrl="">
  89. <LoadingState>
  90. <h1>Lost connection. Reattmpting to join...</h1>
  91. </LoadingState>
  92. </Disconnected>
  93. <Connected configUrl="">
  94. <Room password={this.state.password} name={roomName}>
  95. {({ room }) => {
  96. if (!room.joined) {
  97. if (room.passwordRequired) {
  98. return (
  99. <PasswordEntryContainer>
  100. <PasswordEntry
  101. setting={false}
  102. passwordIsIncorrect={!!this.state.password}
  103. setPassword={this.setPassword}
  104. />
  105. </PasswordEntryContainer>
  106. );
  107. }
  108. return (
  109. <LoadingState>
  110. <h1>Joining room...</h1>
  111. </LoadingState>
  112. );
  113. }
  114. return (
  115. <Container>
  116. <Sidebar
  117. roomAddress={room.address!}
  118. activeSpeakerView={this.state.activeSpeakerView}
  119. toggleActiveSpeakerView={
  120. this.toggleActiveSpeakerView
  121. }
  122. pttMode={this.state.pttMode}
  123. togglePttMode={this.togglePttMode}
  124. setPassword={this.setPassword}
  125. passwordRequired={room.passwordRequired}
  126. roomId={room.id!}
  127. />
  128. <PeerGrid
  129. roomAddress={room.address!}
  130. activeSpeakerView={this.state.activeSpeakerView}
  131. />
  132. {this.state.chatOpen ? (
  133. <ChatContainer
  134. roomAddress={room.address!}
  135. sendRtt={this.state.sendRtt}
  136. toggleRtt={this.toggleRtt}
  137. toggleChat={this.toggleChat}
  138. />
  139. ) : (
  140. <ChatToggle
  141. roomAddress={room.address!}
  142. onClick={this.toggleChat}
  143. />
  144. )}
  145. </Container>
  146. );
  147. }}
  148. </Room>
  149. </Connected>
  150. </>
  151. )}
  152. </>
  153. )}
  154. />
  155. </Provider>
  156. );
  157. }
  158. private toggleActiveSpeakerView = () => {
  159. this.setState({ activeSpeakerView: !this.state.activeSpeakerView });
  160. };
  161. private toggleRtt = () => {
  162. this.setState({ sendRtt: !this.state.sendRtt });
  163. };
  164. private togglePttMode = (e: React.SyntheticEvent) => {
  165. this.setState({ pttMode: !this.state.pttMode }, () => {
  166. if (this.state.pttMode) {
  167. document.addEventListener('keydown', this.unmute);
  168. document.addEventListener('keyup', this.mute);
  169. this.props.mute!();
  170. } else {
  171. document.removeEventListener('keydown', this.unmute);
  172. document.removeEventListener('keyup', this.mute);
  173. this.props.unmute!();
  174. }
  175. });
  176. if (e.target) {
  177. (e.target as HTMLElement).blur();
  178. }
  179. };
  180. private mute = (e: KeyboardEvent) => {
  181. if (e.key === ' ') {
  182. this.props.mute!();
  183. }
  184. };
  185. private unmute = (e: KeyboardEvent) => {
  186. if (e.key === ' ') {
  187. this.props.unmute!();
  188. }
  189. };
  190. private setPassword = (password: string) => {
  191. this.setState({ password });
  192. };
  193. private toggleChat = () => {
  194. this.setState({ chatOpen: !this.state.chatOpen });
  195. };
  196. }
  197. function mapDispatchToProps(dispatch: any, props: Props): Props {
  198. return {
  199. ...props,
  200. mute: () => dispatch(Actions.muteSelf()),
  201. unmute: () => dispatch(Actions.unmuteSelf())
  202. };
  203. }
  204. export default connect(
  205. null,
  206. mapDispatchToProps
  207. )(Index);