Merge pull request #38 from matrix-org/kegan/controller-merging2

Phase 2 controller merging (all atoms)
This commit is contained in:
Kegsay 2015-11-27 15:43:58 +00:00
commit 471d016a78
15 changed files with 486 additions and 194 deletions

View file

@ -18,7 +18,8 @@ limitations under the License.
var React = require('react');
module.exports = {
module.exports = React.createClass({
displayName: 'CreateRoomButton',
propTypes: {
onCreateRoom: React.PropTypes.func,
},
@ -32,4 +33,10 @@ module.exports = {
onClick: function() {
this.props.onCreateRoom();
},
};
render: function() {
return (
<button className="mx_CreateRoomButton" onClick={this.onClick}>Create Room</button>
);
}
});

View file

@ -24,7 +24,8 @@ var Presets = {
Custom: "custom",
};
module.exports = {
module.exports = React.createClass({
displayName: 'CreateRoomPresets',
propTypes: {
onChange: React.PropTypes.func,
preset: React.PropTypes.string
@ -37,4 +38,18 @@ module.exports = {
onChange: function() {},
};
},
};
onValueChanged: function(ev) {
this.props.onChange(ev.target.value)
},
render: function() {
return (
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
<option value={this.Presets.PrivateChat}>Private Chat</option>
<option value={this.Presets.PublicChat}>Public Chat</option>
<option value={this.Presets.Custom}>Custom</option>
</select>
);
}
});

View file

@ -0,0 +1,101 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
module.exports = React.createClass({
displayName: 'RoomAlias',
propTypes: {
// Specifying a homeserver will make magical things happen when you,
// e.g. start typing in the room alias box.
homeserver: React.PropTypes.string,
alias: React.PropTypes.string,
onChange: React.PropTypes.func,
},
getDefaultProps: function() {
return {
onChange: function() {},
alias: '',
};
},
getAliasLocalpart: function() {
var room_alias = this.props.alias;
if (room_alias && this.props.homeserver) {
var suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
}
return room_alias;
},
onValueChanged: function(ev) {
this.props.onChange(ev.target.value);
},
onFocus: function(ev) {
var target = ev.target;
var curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "") {
setTimeout(function() {
target.value = "#:" + this.props.homeserver;
target.setSelectionRange(1, 1);
}, 0);
} else {
var suffix = ":" + this.props.homeserver;
setTimeout(function() {
target.setSelectionRange(
curr_val.startsWith("#") ? 1 : 0,
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
);
}, 0);
}
}
},
onBlur: function(ev) {
var curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "#:" + this.props.homeserver) {
ev.target.value = "";
return;
}
if (curr_val != "") {
var new_val = ev.target.value;
var suffix = ":" + this.props.homeserver;
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
ev.target.value = new_val;
}
}
},
render: function() {
return (
<input type="text" className="mx_RoomAlias" placeholder="Alias (optional)"
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
value={this.props.alias}/>
);
}
});

View file

@ -18,7 +18,8 @@ limitations under the License.
var React = require('react');
module.exports = {
module.exports = React.createClass({
displayName: 'EditableText',
propTypes: {
onValueChanged: React.PropTypes.func,
initialValue: React.PropTypes.string,
@ -85,4 +86,54 @@ module.exports = {
onValueChanged: function(shouldSubmit) {
this.props.onValueChanged(this.state.value, shouldSubmit);
},
};
onKeyUp: function(ev) {
if (ev.key == "Enter") {
this.onFinish(ev);
} else if (ev.key == "Escape") {
this.cancelEdit();
}
},
onClickDiv: function() {
this.setState({
phase: this.Phases.Edit,
})
},
onFocus: function(ev) {
ev.target.setSelectionRange(0, ev.target.value.length);
},
onFinish: function(ev) {
if (ev.target.value) {
this.setValue(ev.target.value, ev.key === "Enter");
} else {
this.cancelEdit();
}
},
render: function() {
var editable_el;
if (this.state.phase == this.Phases.Display) {
if (this.state.value) {
editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.state.value}</div>;
} else {
editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.props.label}</div>;
}
} else if (this.state.phase == this.Phases.Edit) {
editable_el = (
<div>
<input type="text" defaultValue={this.state.value} onKeyUp={this.onKeyUp} onFocus={this.onFocus} onBlur={this.onFinish} placeholder={this.props.placeHolder} autoFocus/>
</div>
);
}
return (
<div className="mx_EditableText">
{editable_el}
</div>
);
}
});

View file

@ -15,10 +15,12 @@ limitations under the License.
*/
'use strict';
var sdk = require('../../index');
var dis = require("../../dispatcher");
var React = require("react");
var sdk = require('../../../index');
var dis = require("../../../dispatcher");
module.exports = {
module.exports = React.createClass({
displayName: 'EnableNotificationsButton',
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
@ -55,4 +57,20 @@ module.exports = {
}
this.forceUpdate();
},
};
render: function() {
if (this.enabled()) {
return (
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>
Disable Notifications
</button>
);
} else {
return (
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>
Enable Notifications
</button>
);
}
}
});

View file

@ -13,9 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require("react");
var dis = require("../../../dispatcher");
var CallHandler = require("../../../CallHandler");
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
/*
* State vars:
@ -23,14 +25,31 @@ var CallHandler = require("../../../CallHandler");
*
* Props:
* this.props.room = Room (JS SDK)
* this.props.ConferenceHandler = A Conference Handler implementation
* Must have a function signature:
* getConferenceCallForRoom(roomId: string): MatrixCall
*/
module.exports = {
module.exports = React.createClass({
displayName: 'CallView',
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
this._trackedRoom = null;
if (this.props.room) {
this.showCall(this.props.room.roomId);
this._trackedRoom = this.props.room;
this.showCall(this._trackedRoom.roomId);
}
else {
var call = CallHandler.getAnyActiveCall();
if (call) {
console.log(
"Global CallView is now tracking active call in room %s",
call.roomId
);
this._trackedRoom = MatrixClientPeg.get().getRoom(call.roomId);
this.showCall(call.roomId);
}
}
},
@ -39,19 +58,22 @@ module.exports = {
},
onAction: function(payload) {
// if we were given a room_id to track, don't handle anything else.
if (payload.room_id && this.props.room &&
this.props.room.roomId !== payload.room_id) {
return;
}
if (payload.action !== 'call_state') {
// 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;
}
this.showCall(payload.room_id);
},
showCall: function(roomId) {
var call = CallHandler.getCall(roomId);
var call = (
CallHandler.getCallForRoom(roomId) ||
(this.props.ConferenceHandler ?
this.props.ConferenceHandler.getConferenceCallForRoom(roomId) :
null
)
);
if (call) {
call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement());
@ -60,13 +82,29 @@ module.exports = {
call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement());
}
if (call && call.type === "video" && call.state !== 'ended') {
this.getVideoView().getLocalVideoElement().style.display = "initial";
// if this call is a conf call, don't display local video as the
// conference will have us in it
this.getVideoView().getLocalVideoElement().style.display = (
call.confUserId ? "none" : "initial"
);
this.getVideoView().getRemoteVideoElement().style.display = "initial";
}
else {
this.getVideoView().getLocalVideoElement().style.display = "none";
this.getVideoView().getRemoteVideoElement().style.display = "none";
dis.dispatch({action: 'video_fullscreen', fullscreen: false});
}
}
};
},
getVideoView: function() {
return this.refs.video;
},
render: function(){
var VideoView = sdk.getComponent('voip.VideoView');
return (
<VideoView ref="video" onClick={ this.props.onClick }/>
);
}
});

View file

@ -0,0 +1,122 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var dis = require("../../../dispatcher");
var CallHandler = require("../../../CallHandler");
module.exports = React.createClass({
displayName: 'IncomingCallBox',
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
},
getInitialState: function() {
return {
incomingCall: null
}
},
onAction: function(payload) {
if (payload.action !== 'call_state') {
return;
}
var call = CallHandler.getCall(payload.room_id);
if (!call || call.call_state !== 'ringing') {
this.setState({
incomingCall: null,
});
this.getRingAudio().pause();
return;
}
if (call.call_state === "ringing") {
this.getRingAudio().load();
this.getRingAudio().play();
}
else {
this.getRingAudio().pause();
}
this.setState({
incomingCall: call
});
},
onAnswerClick: function() {
dis.dispatch({
action: 'answer',
room_id: this.state.incomingCall.roomId
});
},
onRejectClick: function() {
dis.dispatch({
action: 'hangup',
room_id: this.state.incomingCall.roomId
});
},
getRingAudio: function() {
return this.refs.ringAudio;
},
render: function() {
// NB: This block MUST have a "key" so React doesn't clobber the elements
// between in-call / not-in-call.
var audioBlock = (
<audio ref="ringAudio" key="voip_ring_audio" loop>
<source src="media/ring.ogg" type="audio/ogg" />
<source src="media/ring.mp3" type="audio/mpeg" />
</audio>
);
if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
return (
<div>
{audioBlock}
</div>
);
}
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
return (
<div className="mx_IncomingCallBox">
{audioBlock}
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
<div className="mx_IncomingCallBox_title">
Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
</div>
<div className="mx_IncomingCallBox_buttons">
<div className="mx_IncomingCallBox_buttons_cell">
<div className="mx_IncomingCallBox_buttons_decline" onClick={this.onRejectClick}>
Decline
</div>
</div>
<div className="mx_IncomingCallBox_buttons_cell">
<div className="mx_IncomingCallBox_buttons_accept" onClick={this.onAnswerClick}>
Accept
</div>
</div>
</div>
</div>
);
}
});

View file

@ -16,12 +16,16 @@ limitations under the License.
'use strict';
var dis = require("../../dispatcher");
var React = require('react');
module.exports = {
onClick: function() {
dis.dispatch({
action: 'logout'
});
module.exports = React.createClass({
displayName: 'VideoFeed',
render: function() {
return (
<video>
</video>
);
},
};
});

View file

@ -0,0 +1,93 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
var sdk = require('../../../index');
var dis = require('../../../dispatcher');
module.exports = React.createClass({
displayName: 'VideoView',
componentWillMount: function() {
dis.register(this.onAction);
},
getRemoteVideoElement: function() {
return ReactDOM.findDOMNode(this.refs.remote);
},
getRemoteAudioElement: function() {
return this.refs.remoteAudio;
},
getLocalVideoElement: function() {
return ReactDOM.findDOMNode(this.refs.local);
},
setContainer: function(c) {
this.container = c;
},
onAction: function(payload) {
switch (payload.action) {
case 'video_fullscreen':
if (!this.container) {
return;
}
var element = this.container;
if (payload.fullscreen) {
var requestMethod = (
element.requestFullScreen ||
element.webkitRequestFullScreen ||
element.mozRequestFullScreen ||
element.msRequestFullscreen
);
requestMethod.call(element);
}
else {
var exitMethod = (
document.exitFullscreen ||
document.mozCancelFullScreen ||
document.webkitExitFullscreen ||
document.msExitFullscreen
);
if (exitMethod) {
exitMethod.call(document);
}
}
break;
}
},
render: function() {
var VideoFeed = sdk.getComponent('voip.VideoFeed');
return (
<div className="mx_VideoView" ref={this.setContainer} onClick={ this.props.onClick }>
<div className="mx_VideoView_remoteVideoFeed">
<VideoFeed ref="remote"/>
<audio ref="remoteAudio"/>
</div>
<div className="mx_VideoView_localVideoFeed">
<VideoFeed ref="local"/>
</div>
</div>
);
}
});

View file

@ -1,47 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
module.exports = {
propTypes: {
// Specifying a homeserver will make magical things happen when you,
// e.g. start typing in the room alias box.
homeserver: React.PropTypes.string,
alias: React.PropTypes.string,
onChange: React.PropTypes.func,
},
getDefaultProps: function() {
return {
onChange: function() {},
alias: '',
};
},
getAliasLocalpart: function() {
var room_alias = this.props.alias;
if (room_alias && this.props.homeserver) {
var suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
}
return room_alias;
},
};

View file

@ -1,41 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
module.exports = {
propTypes: {
default_name: React.PropTypes.string
},
getDefaultProps: function() {
return {
default_name: '',
};
},
getInitialState: function() {
return {
room_name: this.props.default_name,
}
},
getName: function() {
return this.state.room_name;
},
};

View file

@ -228,7 +228,7 @@ module.exports = {
var d = MatrixClientPeg.get().leave(roomId);
// FIXME: controller shouldn't be loading a view :(
var Loader = sdk.getComponent("atoms.Spinner");
var Loader = sdk.getComponent("elements.Spinner");
var modal = Modal.createDialog(Loader);
d.then(function() {

View file

@ -39,7 +39,7 @@ module.exports = {
var d = MatrixClientPeg.get().leave(roomId);
// FIXME: controller shouldn't be loading a view :(
var Loader = sdk.getComponent("atoms.Spinner");
var Loader = sdk.getComponent("elements.Spinner");
var modal = Modal.createDialog(Loader);
d.then(function() {

View file

@ -1,73 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var dis = require("../../../dispatcher");
var CallHandler = require("../../../CallHandler");
module.exports = {
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
},
getInitialState: function() {
return {
incomingCall: null
}
},
onAction: function(payload) {
if (payload.action !== 'call_state') {
return;
}
var call = CallHandler.getCall(payload.room_id);
if (!call || call.call_state !== 'ringing') {
this.setState({
incomingCall: null,
});
this.getRingAudio().pause();
return;
}
if (call.call_state === "ringing") {
this.getRingAudio().load();
this.getRingAudio().play();
}
else {
this.getRingAudio().pause();
}
this.setState({
incomingCall: call
});
},
onAnswerClick: function() {
dis.dispatch({
action: 'answer',
room_id: this.state.incomingCall.roomId
});
},
onRejectClick: function() {
dis.dispatch({
action: 'hangup',
room_id: this.state.incomingCall.roomId
});
}
};

View file

@ -18,7 +18,11 @@ limitations under the License.
var React = require("react");
var MatrixClientPeg = require("../../MatrixClientPeg");
var PresetValues = require('../atoms/create_room/Presets').Presets;
var PresetValues = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
};
var q = require('q');
var encryption = require("../../encryption");