mirror of https://github.com/vector-im/riot-web
				
				
				
			implement the 'correct' voip calling design
							parent
							
								
									e81a401bba
								
							
						
					
					
						commit
						39c628d4a1
					
				|  | @ -59,6 +59,7 @@ module.exports = React.createClass({ | |||
|             searchResults: null, | ||||
|             syncState: MatrixClientPeg.get().getSyncState(), | ||||
|             hasUnsentMessages: this._hasUnsentMessages(room), | ||||
|             callState: null, | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -114,7 +115,17 @@ module.exports = React.createClass({ | |||
|                 this.forceUpdate(); | ||||
|                 break; | ||||
|             case 'call_state': | ||||
|                 if (CallHandler.getCallForRoom(this.props.roomId)) { | ||||
|                 // don't filter out payloads for room IDs other than props.room because
 | ||||
|                 // we may be interested in the conf 1:1 room
 | ||||
| 
 | ||||
|                 if (!payload.room_id) { | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 var call = CallHandler.getCallForRoom(payload.room_id); | ||||
|                 var callState; | ||||
| 
 | ||||
|                 if (call) { | ||||
|                     // Call state has changed so we may be loading video elements
 | ||||
|                     // which will obscure the message log.
 | ||||
|                     // scroll to bottom
 | ||||
|  | @ -122,11 +133,20 @@ module.exports = React.createClass({ | |||
|                     if (scrollNode) { | ||||
|                         scrollNode.scrollTop = scrollNode.scrollHeight; | ||||
|                     } | ||||
|                     callState = call.call_state; | ||||
|                 } | ||||
|                 else { | ||||
|                     callState = "ended"; | ||||
|                 } | ||||
| 
 | ||||
|                 // possibly remove the conf call notification if we're now in
 | ||||
|                 // the conf
 | ||||
|                 this._updateConfCallNotification(); | ||||
| 
 | ||||
|                 this.setState({ | ||||
|                     callState: callState | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
|             case 'user_activity': | ||||
|                 this.sendReadReceipt(); | ||||
|  | @ -276,6 +296,12 @@ module.exports = React.createClass({ | |||
|             this.fillSpace(); | ||||
|         } | ||||
| 
 | ||||
|         var call = CallHandler.getCallForRoom(this.props.roomId); | ||||
|         var callState = call ? call.call_state : "ended"; | ||||
|         this.setState({ | ||||
|             callState: callState | ||||
|         }); | ||||
| 
 | ||||
|         this._updateConfCallNotification(); | ||||
| 
 | ||||
|         window.addEventListener('resize', this.onResize); | ||||
|  | @ -972,6 +998,30 @@ module.exports = React.createClass({ | |||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onMuteAudioClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.roomId); | ||||
|         if (!call) { | ||||
|             return; | ||||
|         } | ||||
|         var newState = !call.isMicrophoneMuted(); | ||||
|         call.setMicrophoneMuted(newState); | ||||
|         this.setState({ | ||||
|             audioMuted: newState | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onMuteVideoClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.roomId); | ||||
|         if (!call) { | ||||
|             return; | ||||
|         } | ||||
|         var newState = !call.isLocalVideoMuted(); | ||||
|         call.setLocalVideoMuted(newState); | ||||
|         this.setState({ | ||||
|             videoMuted: newState | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var RoomHeader = sdk.getComponent('rooms.RoomHeader'); | ||||
|         var MessageComposer = sdk.getComponent('rooms.MessageComposer'); | ||||
|  | @ -1029,9 +1079,7 @@ module.exports = React.createClass({ | |||
|                 loading: this.state.paginating | ||||
|             }); | ||||
| 
 | ||||
|             var statusBar = ( | ||||
|                 <div /> | ||||
|             ); | ||||
|             var statusBar; | ||||
| 
 | ||||
|             // for testing UI...
 | ||||
|             // this.state.upload = {
 | ||||
|  | @ -1043,7 +1091,7 @@ module.exports = React.createClass({ | |||
|             if (ContentMessages.getCurrentUploads().length > 0) { | ||||
|                 var UploadBar = sdk.getComponent('structures.UploadBar'); | ||||
|                 statusBar = <UploadBar room={this.state.room} /> | ||||
|             } else { | ||||
|             } else if (!this.state.searchResults) { | ||||
|                 var typingString = this.getWhoIsTypingString(); | ||||
|                 // typingString = "S͚͍̭̪̤͙̱͙̖̥͙̥̤̻̙͕͓͂̌ͬ͐̂k̜̝͎̰̥̻̼̂̌͛͗͊̅̒͂̊̍̍͌̈̈́͌̋̊ͬa͉̯͚̺̗̳̩ͪ̋̑͌̓̆̍̂̉̏̅̆ͧ̌̑v̲̲̪̝ͥ̌ͨͮͭ̊͆̾ͮ̍ͮ͑̚e̮̙͈̱̘͕̼̮͒ͩͨͫ̃͗̇ͩ͒ͣͦ͒̄̍͐ͣ̿ͥṘ̗̺͇̺̺͔̄́̊̓͊̍̃ͨ̚ā̼͎̘̟̼͎̜̪̪͚̋ͨͨͧ̓ͦͯͤ̄͆̋͂ͩ͌ͧͅt̙̙̹̗̦͖̞ͫͪ͑̑̅ͪ̃̚ͅ is typing...";
 | ||||
|                 var unreadMsgs = this.getUnreadMessagesString(); | ||||
|  | @ -1142,7 +1190,7 @@ module.exports = React.createClass({ | |||
|             var messageComposer, searchInfo; | ||||
|             if (!this.state.searchResults) { | ||||
|                 messageComposer = | ||||
|                     <MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} /> | ||||
|                     <MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} callState={this.state.callState} /> | ||||
|             } | ||||
|             else { | ||||
|                 searchInfo = { | ||||
|  | @ -1152,8 +1200,43 @@ module.exports = React.createClass({ | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             var call = CallHandler.getCallForRoom(this.props.roomId); | ||||
|             var inCall = false; | ||||
|             if (call && this.state.callState != 'ended') { | ||||
|                 inCall = true; | ||||
|                 //var muteVideoButton;
 | ||||
|                 var voiceMuteButton, videoMuteButton; | ||||
| 
 | ||||
|                 if (call.type === "video") { | ||||
|                     videoMuteButton = | ||||
|                         <div className="mx_RoomView_muteButton" onClick={this.onMuteVideoClick}> | ||||
|                             <img src={call.isLocalVideoMuted() ? "img/video-unmute.svg" : "img/video-mute.svg"} width="31" height="27"/> | ||||
|                         </div> | ||||
|                 } | ||||
|                 voiceMuteButton = | ||||
|                     <div className="mx_RoomView_muteButton" onClick={this.onMuteAudioClick}> | ||||
|                         <img src={call.isMicrophoneMuted() ? "img/voice-unmute.svg" : "img/voice-mute.svg"} width="21" height="26"/> | ||||
|                     </div> | ||||
| 
 | ||||
|                 if (!statusBar) { | ||||
|                     statusBar = | ||||
|                         <div className="mx_RoomView_callBar"> | ||||
|                             <img src="img/sound-indicator.svg" width="23" height="20" alt=""/> | ||||
|                             <b>Active call</b> | ||||
|                         </div>; | ||||
|                 } | ||||
| 
 | ||||
|                 statusBar = | ||||
|                     <div className="mx_RoomView_callStatusBar"> | ||||
|                         { voiceMuteButton } | ||||
|                         { videoMuteButton } | ||||
|                         { statusBar } | ||||
|                         <img className="mx_RoomView_voipChevron" src="img/voip-chevron.svg" width="22" height="17"/> | ||||
|                     </div> | ||||
|             } | ||||
| 
 | ||||
|             return ( | ||||
|                 <div className="mx_RoomView"> | ||||
|                 <div className={ "mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "") }> | ||||
|                     <RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick} | ||||
|                         onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} onLeaveClick={this.onLeaveClick} /> | ||||
|                     { fileDropTarget }     | ||||
|  | @ -1174,7 +1257,7 @@ module.exports = React.createClass({ | |||
|                     <div className="mx_RoomView_statusArea"> | ||||
|                         <div className="mx_RoomView_statusAreaBox"> | ||||
|                             <div className="mx_RoomView_statusAreaBox_line"></div> | ||||
|                             { this.state.searchResults ? null : statusBar } | ||||
|                             { statusBar } | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     { messageComposer } | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| var React = require("react"); | ||||
| 
 | ||||
| var marked = require("marked"); | ||||
| marked.setOptions({ | ||||
|     renderer: new marked.Renderer(), | ||||
|  | @ -25,9 +26,11 @@ marked.setOptions({ | |||
|     smartLists: true, | ||||
|     smartypants: false | ||||
| }); | ||||
| 
 | ||||
| var MatrixClientPeg = require("../../../MatrixClientPeg"); | ||||
| var SlashCommands = require("../../../SlashCommands"); | ||||
| var Modal = require("../../../Modal"); | ||||
| var CallHandler = require('../../../CallHandler'); | ||||
| var sdk = require('../../../index'); | ||||
| 
 | ||||
| var dis = require("../../../dispatcher"); | ||||
|  | @ -524,6 +527,19 @@ module.exports = React.createClass({ | |||
|         this.refs.uploadInput.value = null; | ||||
|     }, | ||||
| 
 | ||||
|     onHangupClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|         if (!call) { | ||||
|             return; | ||||
|         } | ||||
|         dis.dispatch({ | ||||
|             action: 'hangup', | ||||
|             // hangup the call for this room, which may not be the room in props
 | ||||
|             // (e.g. conferences which will hangup the 1:1 room instead)
 | ||||
|             room_id: call.roomId | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onCallClick: function(ev) { | ||||
|         dis.dispatch({ | ||||
|             action: 'place_call', | ||||
|  | @ -544,6 +560,26 @@ module.exports = React.createClass({ | |||
|         var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); | ||||
|         var uploadInputStyle = {display: 'none'}; | ||||
|         var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); | ||||
| 
 | ||||
|         var callButton, videoCallButton, hangupButton; | ||||
|         var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|         if (this.props.callState && this.props.callState !== 'ended') { | ||||
|             hangupButton = | ||||
|                 <div className="mx_MessageComposer_hangup" onClick={this.onHangupClick}> | ||||
|                     <img src="img/hangup.svg" alt="Hangup" title="Hangup" width="25" height="26"/> | ||||
|                 </div>; | ||||
|         } | ||||
|         else { | ||||
|             callButton = | ||||
|                 <div className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick}> | ||||
|                     <img src="img/voice.svg" alt="Voice call" title="Voice call" width="16" height="26"/> | ||||
|                 </div> | ||||
|             videoCallButton = | ||||
|                 <div className="mx_MessageComposer_videocall" onClick={this.onCallClick}> | ||||
|                     <img src="img/call.svg" alt="Video call" title="Video call" width="30" height="22"/> | ||||
|                 </div> | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|         <div className="mx_MessageComposer"> | ||||
|             <div className="mx_MessageComposer_wrapper"> | ||||
|  | @ -558,12 +594,9 @@ module.exports = React.createClass({ | |||
|                         <img src="img/upload.svg" alt="Upload file" title="Upload file" width="19" height="24"/> | ||||
|                         <input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} /> | ||||
|                     </div> | ||||
|                     <div className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick}> | ||||
|                         <img src="img/voice.svg" alt="Voice call" title="Voice call" width="16" height="26"/> | ||||
|                     </div> | ||||
|                     <div className="mx_MessageComposer_videocall" onClick={this.onCallClick}> | ||||
|                         <img src="img/call.svg" alt="Video call" title="Video call" width="30" height="22"/> | ||||
|                     </div> | ||||
|                     { hangupButton } | ||||
|                     { callButton } | ||||
|                     { videoCallButton } | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|  |  | |||
|  | @ -16,15 +16,9 @@ limitations under the License. | |||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /* | ||||
|  * State vars: | ||||
|  * this.state.call_state = the UI state of the call (see CallHandler) | ||||
|  */ | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var sdk = require('../../../index'); | ||||
| var dis = require("../../../dispatcher"); | ||||
| var CallHandler = require("../../../CallHandler"); | ||||
| var MatrixClientPeg = require('../../../MatrixClientPeg'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|  | @ -47,34 +41,6 @@ module.exports = React.createClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         if (this.props.room) { | ||||
|             var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|             var callState = call ? call.call_state : "ended"; | ||||
|             this.setState({ | ||||
|                 call_state: callState | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|     }, | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|         // don't filter out payloads for room IDs other than props.room because
 | ||||
|         // we may be interested in the conf 1:1 room
 | ||||
|         if (payload.action !== 'call_state' || !payload.room_id) { | ||||
|             return; | ||||
|         } | ||||
|         var call = CallHandler.getCallForRoom(payload.room_id); | ||||
|         var callState = call ? call.call_state : "ended"; | ||||
|         this.setState({ | ||||
|             call_state: callState | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onVideoClick: function(e) { | ||||
|         dis.dispatch({ | ||||
|             action: 'place_call', | ||||
|  | @ -82,6 +48,7 @@ module.exports = React.createClass({ | |||
|             room_id: this.props.room.roomId | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onVoiceClick: function() { | ||||
|         dis.dispatch({ | ||||
|             action: 'place_call', | ||||
|  | @ -89,38 +56,6 @@ module.exports = React.createClass({ | |||
|             room_id: this.props.room.roomId | ||||
|         }); | ||||
|     }, | ||||
|     onHangupClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|         if (!call) { return; } | ||||
|         dis.dispatch({ | ||||
|             action: 'hangup', | ||||
|             // hangup the call for this room, which may not be the room in props
 | ||||
|             // (e.g. conferences which will hangup the 1:1 room instead)
 | ||||
|             room_id: call.roomId | ||||
|         }); | ||||
|     }, | ||||
|     onMuteAudioClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|         if (!call) { | ||||
|             return; | ||||
|         } | ||||
|         var newState = !call.isMicrophoneMuted(); | ||||
|         call.setMicrophoneMuted(newState); | ||||
|         this.setState({ | ||||
|             audioMuted: newState | ||||
|         }); | ||||
|     }, | ||||
|     onMuteVideoClick: function() { | ||||
|         var call = CallHandler.getCallForRoom(this.props.room.roomId); | ||||
|         if (!call) { | ||||
|             return; | ||||
|         } | ||||
|         var newState = !call.isLocalVideoMuted(); | ||||
|         call.setLocalVideoMuted(newState); | ||||
|         this.setState({ | ||||
|             videoMuted: newState | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onNameChange: function(new_name) { | ||||
|         if (this.props.room.name != new_name && new_name) { | ||||
|  | @ -152,42 +87,6 @@ module.exports = React.createClass({ | |||
|         else { | ||||
|             var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); | ||||
| 
 | ||||
|             var call_buttons; | ||||
|             if (this.state && this.state.call_state != 'ended') { | ||||
|                 //var muteVideoButton;
 | ||||
|                 var activeCall = ( | ||||
|                     CallHandler.getCallForRoom(this.props.room.roomId) | ||||
|                 ); | ||||
| /*                 | ||||
|                 if (activeCall && activeCall.type === "video") { | ||||
|                     muteVideoButton = ( | ||||
|                         <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton" | ||||
|                                 onClick={this.onMuteVideoClick}> | ||||
|                             { | ||||
|                                 (activeCall.isLocalVideoMuted() ? | ||||
|                                     "Unmute" : "Mute") + " video" | ||||
|                             } | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|                         {muteVideoButton} | ||||
|                         <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton" | ||||
|                                 onClick={this.onMuteAudioClick}> | ||||
|                             { | ||||
|                                 (activeCall && activeCall.isMicrophoneMuted() ? | ||||
|                                     "Unmute" : "Mute") + " audio" | ||||
|                             } | ||||
|                         </div> | ||||
| */                 | ||||
| 
 | ||||
|                 call_buttons = ( | ||||
|                     <div className="mx_RoomHeader_textButton" | ||||
|                             onClick={this.onHangupClick}> | ||||
|                         End call | ||||
|                     </div> | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             var name = null; | ||||
|             var searchStatus = null; | ||||
|             var topic_el = null; | ||||
|  | @ -230,32 +129,9 @@ module.exports = React.createClass({ | |||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             var zoom_button, video_button, voice_button; | ||||
|             if (activeCall) { | ||||
|                 if (activeCall.type == "video") { | ||||
|                     zoom_button = ( | ||||
|                         <div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}> | ||||
|                             <img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '-5px' }}/> | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|                 video_button =  | ||||
|                         <div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}> | ||||
|                             <img src="img/video.png" title="Video call" alt="Video call" width="32" height="32" style={{ 'marginTop': '-8px' }}/> | ||||
|                         </div>; | ||||
|                 var img = "img/voip.png"; | ||||
|                 if (activeCall.isMicrophoneMuted()) { | ||||
|                         img = "img/voip-mute.png"; | ||||
|                 } | ||||
|                 voice_button = | ||||
|                         <div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}> | ||||
|                             <img src={img} title="VoIP call" alt="VoIP call" width="32" height="32" style={{ 'marginTop': '-8px' }}/> | ||||
|                         </div>; | ||||
|             } | ||||
| 
 | ||||
|             var exit_button; | ||||
|             var leave_button; | ||||
|             if (this.props.onLeaveClick) { | ||||
|                 exit_button = | ||||
|                 leave_button = | ||||
|                     <div className="mx_RoomHeader_button mx_RoomHeader_leaveButton"> | ||||
|                         <img src="img/leave.svg" title="Leave room" alt="Leave room" width="26" height="20" onClick={this.props.onLeaveClick}/> | ||||
|                     </div>; | ||||
|  | @ -272,14 +148,10 @@ module.exports = React.createClass({ | |||
|                             { topic_el } | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     {call_buttons} | ||||
|                     {cancel_button} | ||||
|                     {save_button} | ||||
|                     <div className="mx_RoomHeader_rightRow"> | ||||
|                         { video_button } | ||||
|                         { voice_button } | ||||
|                         { zoom_button } | ||||
|                         { exit_button } | ||||
|                         { leave_button } | ||||
|                         <div className="mx_RoomHeader_button"> | ||||
|                             <img src="img/search.svg" title="Search" alt="Search" width="21" height="19" onClick={this.props.onSearchClick}/> | ||||
|                         </div> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Matthew Hodgson
						Matthew Hodgson