Remove controllers that all come from react-sdk

This commit is contained in:
David Baker
2015-09-22 17:20:22 +01:00
parent 2f0df6d37e
commit 09b81f46b0
66 changed files with 11 additions and 5467 deletions
-88
View File
@@ -1,88 +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: {
onValueChanged: React.PropTypes.func,
initialValue: React.PropTypes.string,
label: React.PropTypes.string,
placeHolder: React.PropTypes.string,
},
Phases: {
Display: "display",
Edit: "edit",
},
getDefaultProps: function() {
return {
onValueChanged: function() {},
initialValue: '',
label: 'Click to set',
placeholder: '',
};
},
getInitialState: function() {
return {
value: this.props.initialValue,
phase: this.Phases.Display,
}
},
componentWillReceiveProps: function(nextProps) {
this.setState({
value: nextProps.initialValue
});
},
getValue: function() {
return this.state.value;
},
setValue: function(val, shouldSubmit, suppressListener) {
var self = this;
this.setState({
value: val,
phase: this.Phases.Display,
}, function() {
if (!suppressListener) {
self.onValueChanged(shouldSubmit);
}
});
},
edit: function() {
this.setState({
phase: this.Phases.Edit,
});
},
cancelEdit: function() {
this.setState({
phase: this.Phases.Display,
});
this.onValueChanged(false);
},
onValueChanged: function(shouldSubmit) {
this.props.onValueChanged(this.state.value, shouldSubmit);
},
};
@@ -1,57 +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 ComponentBroker = require("../../ComponentBroker");
var Notifier = ComponentBroker.get('organisms/Notifier');
var dis = require("../../dispatcher");
module.exports = {
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
},
onAction: function(payload) {
if (payload.action !== "notifier_enabled") {
return;
}
this.forceUpdate();
},
enabled: function() {
return Notifier.isEnabled();
},
onClick: function() {
var self = this;
if (!Notifier.supportsDesktopNotifications()) {
return;
}
if (!Notifier.isEnabled()) {
Notifier.setEnabled(true, function() {
self.forceUpdate();
});
} else {
Notifier.setEnabled(false);
}
this.forceUpdate();
},
};
-20
View File
@@ -1,20 +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';
module.exports = {
};
-27
View File
@@ -1,27 +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 dis = require("../../dispatcher");
module.exports = {
onClick: function() {
dis.dispatch({
action: 'logout'
});
},
};
-64
View File
@@ -1,64 +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 Avatar = require('../../Avatar');
var React = require('react');
module.exports = {
propTypes: {
member: React.PropTypes.object.isRequired,
width: React.PropTypes.number,
height: React.PropTypes.number,
resizeMethod: React.PropTypes.string,
},
getDefaultProps: function() {
return {
width: 40,
height: 40,
resizeMethod: 'crop'
}
},
defaultAvatarUrl: function(member) {
return Avatar.defaultAvatarUrlForString(
member.userId
);
},
onError: function(ev) {
// don't tightloop if the browser can't load a data url
if (ev.target.src == this.defaultAvatarUrl(this.props.member)) {
return;
}
this.setState({
imageUrl: this.defaultAvatarUrl(this.props.member)
});
},
getInitialState: function() {
return {
imageUrl: Avatar.avatarUrlForMember(
this.props.member,
this.props.width, this.props.height,
this.props.resizeMethod
)
};
}
};
-21
View File
@@ -1,21 +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';
module.exports = {
};
-64
View File
@@ -1,64 +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 MatrixClientPeg = require('../../MatrixClientPeg');
var Avatar = require('../../Avatar');
module.exports = {
getDefaultProps: function() {
return {
width: 40,
height: 40,
resizeMethod: 'crop'
}
},
avatarUrlForRoom: function(room) {
var url = MatrixClientPeg.get().getAvatarUrlForRoom(
room,
this.props.width, this.props.height, this.props.resizeMethod,
false
);
if (url === null) {
url = this.defaultAvatarUrl(room);
}
return url;
},
defaultAvatarUrl: function(room) {
return Avatar.defaultAvatarUrlForString(
this.props.room.roomId
);
},
onError: function(ev) {
// don't tightloop if the browser can't load a data url
if (ev.target.src == this.defaultAvatarUrl(this.props.room)) {
return;
}
this.setState({
imageUrl: this.defaultAvatarUrl(this.props.room)
});
},
getInitialState: function() {
return {
imageUrl: this.avatarUrlForRoom(this.props.room)
};
}
};
@@ -1,35 +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: {
onCreateRoom: React.PropTypes.func,
},
getDefaultProps: function() {
return {
onCreateRoom: function() {},
};
},
onClick: function() {
this.props.onCreateRoom();
},
};
@@ -1,40 +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');
var Presets = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
};
module.exports = {
propTypes: {
onChange: React.PropTypes.func,
preset: React.PropTypes.string
},
Presets: Presets,
getDefaultProps: function() {
return {
onChange: function() {},
};
},
};
@@ -1,49 +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: {
// 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;
},
};
-21
View File
@@ -1,21 +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';
module.exports = {
};
-71
View File
@@ -1,71 +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');
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
module.exports = {
propTypes: {
onFinished: React.PropTypes.func,
initialAvatarUrl: React.PropTypes.string.isRequired,
},
Phases: {
Display: "display",
Uploading: "uploading",
Error: "error",
},
getDefaultProps: function() {
return {
onFinished: function() {},
};
},
getInitialState: function() {
return {
avatarUrl: this.props.initialAvatarUrl,
phase: this.Phases.Display,
}
},
setAvatarFromFile: function(file) {
var newUrl = null;
this.setState({
phase: this.Phases.Uploading
});
var self = this;
MatrixClientPeg.get().uploadContent(file).then(function(url) {
newUrl = url;
return MatrixClientPeg.get().setAvatarUrl(url);
}).done(function() {
self.setState({
phase: self.Phases.Display,
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl)
});
}, function(error) {
self.setState({
phase: this.Phases.Error
});
self.onError(error);
});
},
}
@@ -1,78 +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');
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
module.exports = {
propTypes: {
onFinished: React.PropTypes.func,
},
Phases: {
Edit: "edit",
Uploading: "uploading",
Error: "error",
Success: "Success"
},
getDefaultProps: function() {
return {
onFinished: function() {},
};
},
getInitialState: function() {
return {
phase: this.Phases.Edit,
errorString: ''
}
},
changePassword: function(old_password, new_password) {
var cli = MatrixClientPeg.get();
var authDict = {
type: 'm.login.password',
user: cli.credentials.userId,
password: old_password
};
this.setState({
phase: this.Phases.Uploading,
errorString: '',
})
var d = cli.setPassword(authDict, new_password);
var self = this;
d.then(function() {
self.setState({
phase: self.Phases.Success,
errorString: '',
})
}, function(err) {
self.setState({
phase: self.Phases.Error,
errorString: err.toString()
})
});
},
}
@@ -1,21 +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';
module.exports = {
};
-30
View File
@@ -1,30 +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 linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../linkify-matrix');
linkifyMatrix(linkify);
module.exports = {
componentDidMount: function() {
linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
}
};
-44
View File
@@ -1,44 +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 filesize = require('filesize');
module.exports = {
presentableTextForFile: function(content) {
var linkText = 'Attachment';
if (content.body && content.body.length > 0) {
linkText = content.body;
}
var additionals = [];
if (content.info) {
if (content.info.mimetype && content.info.mimetype.length > 0) {
additionals.push(content.info.mimetype);
}
if (content.info.size) {
additionals.push(filesize(content.info.size));
}
}
if (additionals.length > 0) {
linkText += ' (' + additionals.join(', ') + ')';
}
return linkText;
}
};
-21
View File
@@ -1,21 +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';
module.exports = {
};
-28
View File
@@ -1,28 +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 linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../linkify-matrix.js');
linkifyMatrix(linkify);
module.exports = {
componentDidMount: function() {
linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
}
};
-30
View File
@@ -1,30 +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 linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../linkify-matrix');
linkifyMatrix(linkify);
module.exports = {
componentDidMount: function() {
linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
}
};
@@ -1,21 +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';
module.exports = {
};
-319
View File
@@ -1,319 +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.
*/
/*
* State vars:
* 'can': {
* kick: boolean,
* ban: boolean,
* mute: boolean,
* modifyLevel: boolean
* },
* 'muted': boolean,
* 'isTargetMod': boolean
*/
'use strict';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var QuestionDialog = ComponentBroker.get("organisms/QuestionDialog");
var Loader = require("react-loader");
module.exports = {
componentDidMount: function() {
var self = this;
// work out the current state
if (this.props.member) {
var usr = MatrixClientPeg.get().getUser(this.props.member.userId) || {};
var memberState = this._calculateOpsPermissions();
this.setState(memberState);
}
},
onKick: function() {
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var self = this;
MatrixClientPeg.get().kick(roomId, target).done(function() {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console.log("Kick success");
}, function(err) {
Modal.createDialog(ErrorDialog, {
title: "Kick error",
description: err.message
});
});
this.props.onFinished();
},
onBan: function() {
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var self = this;
MatrixClientPeg.get().ban(roomId, target).done(function() {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console.log("Ban success");
}, function(err) {
Modal.createDialog(ErrorDialog, {
title: "Ban error",
description: err.message
});
});
this.props.onFinished();
},
onMuteToggle: function() {
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var self = this;
var room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
this.props.onFinished();
return;
}
var powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", ""
);
if (!powerLevelEvent) {
this.props.onFinished();
return;
}
var isMuted = this.state.muted;
var powerLevels = powerLevelEvent.getContent();
var levelToSend = (
(powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
powerLevels.events_default
);
var level;
if (isMuted) { // unmute
level = levelToSend;
}
else { // mute
level = levelToSend - 1;
}
MatrixClientPeg.get().setPowerLevel(roomId, target, level, powerLevelEvent).done(
function() {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console.log("Mute toggle success");
}, function(err) {
Modal.createDialog(ErrorDialog, {
title: "Mute error",
description: err.message
});
});
this.props.onFinished();
},
onModToggle: function() {
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
this.props.onFinished();
return;
}
var powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", ""
);
if (!powerLevelEvent) {
this.props.onFinished();
return;
}
var me = room.getMember(MatrixClientPeg.get().credentials.userId);
if (!me) {
this.props.onFinished();
return;
}
var defaultLevel = powerLevelEvent.getContent().users_default;
var modLevel = me.powerLevel - 1;
// toggle the level
var newLevel = this.state.isTargetMod ? defaultLevel : modLevel;
MatrixClientPeg.get().setPowerLevel(roomId, target, newLevel, powerLevelEvent).done(
function() {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console.log("Mod toggle success");
}, function(err) {
Modal.createDialog(ErrorDialog, {
title: "Mod error",
description: err.message
});
});
this.props.onFinished();
},
onChatClick: function() {
// check if there are any existing rooms with just us and them (1:1)
// If so, just view that room. If not, create a private room with them.
var rooms = MatrixClientPeg.get().getRooms();
var userIds = [
this.props.member.userId,
MatrixClientPeg.get().credentials.userId
];
var existingRoomId = null;
for (var i = 0; i < rooms.length; i++) {
var members = rooms[i].getJoinedMembers();
if (members.length === 2) {
var hasTargetUsers = true;
for (var j = 0; j < members.length; j++) {
if (userIds.indexOf(members[j].userId) === -1) {
hasTargetUsers = false;
break;
}
}
if (hasTargetUsers) {
existingRoomId = rooms[i].roomId;
break;
}
}
}
if (existingRoomId) {
dis.dispatch({
action: 'view_room',
room_id: existingRoomId
});
}
else {
MatrixClientPeg.get().createRoom({
invite: [this.props.member.userId],
preset: "private_chat"
}).done(function(res) {
dis.dispatch({
action: 'view_room',
room_id: res.room_id
});
}, function(err) {
console.error(
"Failed to create room: %s", JSON.stringify(err)
);
});
}
this.props.onFinished();
},
// FIXME: this is horribly duplicated with MemberTile's onLeaveClick.
// Not sure what the right solution to this is.
onLeaveClick: function() {
var roomId = this.props.member.roomId;
Modal.createDialog(QuestionDialog, {
title: "Leave room",
description: "Are you sure you want to leave the room?",
onFinished: function(should_leave) {
if (should_leave) {
var d = MatrixClientPeg.get().leave(roomId);
var modal = Modal.createDialog(Loader);
d.then(function() {
modal.close();
dis.dispatch({action: 'view_next_room'});
}, function(err) {
modal.close();
Modal.createDialog(ErrorDialog, {
title: "Failed to leave room",
description: err.toString()
});
});
}
}
});
this.props.onFinished();
},
getInitialState: function() {
return {
can: {
kick: false,
ban: false,
mute: false,
modifyLevel: false
},
muted: false,
isTargetMod: false
}
},
_calculateOpsPermissions: function() {
var defaultPerms = {
can: {},
muted: false,
modifyLevel: false
};
var room = MatrixClientPeg.get().getRoom(this.props.member.roomId);
if (!room) {
return defaultPerms;
}
var powerLevels = room.currentState.getStateEvents(
"m.room.power_levels", ""
);
if (!powerLevels) {
return defaultPerms;
}
var me = room.getMember(MatrixClientPeg.get().credentials.userId);
var them = this.props.member;
return {
can: this._calculateCanPermissions(
me, them, powerLevels.getContent()
),
muted: this._isMuted(them, powerLevels.getContent()),
isTargetMod: them.powerLevel > powerLevels.getContent().users_default
};
},
_calculateCanPermissions: function(me, them, powerLevels) {
var can = {
kick: false,
ban: false,
mute: false,
modifyLevel: false
};
var canAffectUser = them.powerLevel < me.powerLevel;
if (!canAffectUser) {
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
return can;
}
var editPowerLevel = (
(powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
powerLevels.state_default
);
can.kick = me.powerLevel >= powerLevels.kick;
can.ban = me.powerLevel >= powerLevels.ban;
can.mute = me.powerLevel >= editPowerLevel;
can.modifyLevel = me.powerLevel > them.powerLevel;
return can;
},
_isMuted: function(member, powerLevelContent) {
if (!powerLevelContent || !member) {
return false;
}
var levelToSend = (
(powerLevelContent.events ? powerLevelContent.events["m.room.message"] : null) ||
powerLevelContent.events_default
);
return member.powerLevel < levelToSend;
}
};
-69
View File
@@ -1,69 +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 dis = require("../../dispatcher");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var QuestionDialog = ComponentBroker.get("organisms/QuestionDialog");
var Loader = require("react-loader");
var MatrixClientPeg = require("../../MatrixClientPeg");
module.exports = {
// onClick: function() {
// dis.dispatch({
// action: 'view_user',
// user_id: this.props.member.userId
// });
// },
getInitialState: function() {
return {
hover: false,
menu: false,
}
},
onLeaveClick: function(ev) {
ev.stopPropagation();
ev.preventDefault();
var roomId = this.props.member.roomId;
Modal.createDialog(QuestionDialog, {
title: "Leave room",
description: "Are you sure you want to leave the room?",
onFinished: function(should_leave) {
if (should_leave) {
var d = MatrixClientPeg.get().leave(roomId);
var modal = Modal.createDialog(Loader);
d.then(function() {
modal.close();
dis.dispatch({action: 'view_next_room'});
}, function(err) {
modal.close();
Modal.createDialog(ErrorDialog, {
title: "Failed to leave room",
description: err.toString()
});
});
}
}
});
}
};
@@ -1,414 +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 MatrixClientPeg = require("../../MatrixClientPeg");
var SlashCommands = require("../../SlashCommands");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var dis = require("../../dispatcher");
var KeyCode = {
ENTER: 13,
TAB: 9,
SHIFT: 16,
UP: 38,
DOWN: 40
};
var TYPING_USER_TIMEOUT = 10000;
var TYPING_SERVER_TIMEOUT = 30000;
module.exports = {
componentWillMount: function() {
this.tabStruct = {
completing: false,
original: null,
index: 0
};
this.sentHistory = {
// The list of typed messages. Index 0 is more recent
data: [],
// The position in data currently displayed
position: -1,
// The room the history is for.
roomId: null,
// The original text before they hit UP
originalText: null,
// The textarea element to set text to.
element: null,
init: function(element, roomId) {
this.roomId = roomId;
this.element = element;
this.position = -1;
var storedData = window.sessionStorage.getItem(
"history_" + roomId
);
if (storedData) {
this.data = JSON.parse(storedData);
}
if (this.roomId) {
this.setLastTextEntry();
}
},
push: function(text) {
// store a message in the sent history
this.data.unshift(text);
window.sessionStorage.setItem(
"history_" + this.roomId,
JSON.stringify(this.data)
);
// reset history position
this.position = -1;
this.originalText = null;
},
// move in the history. Returns true if we managed to move.
next: function(offset) {
if (this.position === -1) {
// user is going into the history, save the current line.
this.originalText = this.element.value;
}
else {
// user may have modified this line in the history; remember it.
this.data[this.position] = this.element.value;
}
if (offset > 0 && this.position === (this.data.length - 1)) {
// we've run out of history
return false;
}
// retrieve the next item (bounded).
var newPosition = this.position + offset;
newPosition = Math.max(-1, newPosition);
newPosition = Math.min(newPosition, this.data.length - 1);
this.position = newPosition;
if (this.position !== -1) {
// show the message
this.element.value = this.data[this.position];
}
else if (this.originalText !== undefined) {
// restore the original text the user was typing.
this.element.value = this.originalText;
}
return true;
},
saveLastTextEntry: function() {
// save the currently entered text in order to restore it later.
// NB: This isn't 'originalText' because we want to restore
// sent history items too!
var text = this.element.value;
window.sessionStorage.setItem("input_" + this.roomId, text);
},
setLastTextEntry: function() {
var text = window.sessionStorage.getItem("input_" + this.roomId);
if (text) {
this.element.value = text;
}
}
};
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
this.sentHistory.init(
this.refs.textarea.getDOMNode(),
this.props.room.roomId
);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
this.sentHistory.saveLastTextEntry();
},
onAction: function(payload) {
switch (payload.action) {
case 'focus_composer':
this.refs.textarea.getDOMNode().focus();
break;
}
},
onKeyDown: function (ev) {
if (ev.keyCode === KeyCode.ENTER) {
var input = this.refs.textarea.getDOMNode().value;
if (input.length === 0) {
ev.preventDefault();
return;
}
this.sentHistory.push(input);
this.onEnter(ev);
}
else if (ev.keyCode === KeyCode.TAB) {
var members = [];
if (this.props.room) {
members = this.props.room.getJoinedMembers();
}
this.onTab(ev, members);
}
else if (ev.keyCode === KeyCode.UP || ev.keyCode === KeyCode.DOWN) {
this.sentHistory.next(
ev.keyCode === KeyCode.UP ? 1 : -1
);
ev.preventDefault();
}
else if (ev.keyCode !== KeyCode.SHIFT && this.tabStruct.completing) {
// they're resuming typing; reset tab complete state vars.
this.tabStruct.completing = false;
this.tabStruct.index = 0;
}
var self = this;
setTimeout(function() {
if (self.refs.textarea && self.refs.textarea.getDOMNode().value != '') {
self.onTypingActivity();
} else {
self.onFinishedTyping();
}
}, 10); // XXX: what is this 10ms setTimeout doing? Looks hacky :(
},
onEnter: function(ev) {
var contentText = this.refs.textarea.getDOMNode().value;
var cmd = SlashCommands.processInput(this.props.room.roomId, contentText);
if (cmd) {
ev.preventDefault();
if (!cmd.error) {
this.refs.textarea.getDOMNode().value = '';
}
if (cmd.promise) {
cmd.promise.done(function() {
console.log("Command success.");
}, function(err) {
console.error("Command failure: %s", err);
Modal.createDialog(ErrorDialog, {
title: "Server error",
description: err.message
});
});
}
else if (cmd.error) {
console.error(cmd.error);
Modal.createDialog(ErrorDialog, {
title: "Command error",
description: cmd.error
});
}
return;
}
var content = null;
if (/^\/me /i.test(contentText)) {
content = {
msgtype: 'm.emote',
body: contentText.substring(4)
};
} else {
content = {
msgtype: 'm.text',
body: contentText
};
}
MatrixClientPeg.get().sendMessage(this.props.room.roomId, content).then(function() {
dis.dispatch({
action: 'message_sent'
});
}, function() {
dis.dispatch({
action: 'message_send_failed'
});
});
this.refs.textarea.getDOMNode().value = '';
ev.preventDefault();
},
onTab: function(ev, sortedMembers) {
var textArea = this.refs.textarea.getDOMNode();
if (!this.tabStruct.completing) {
this.tabStruct.completing = true;
this.tabStruct.index = 0;
// cache starting text
this.tabStruct.original = textArea.value;
}
// loop in the right direction
if (ev.shiftKey) {
this.tabStruct.index --;
if (this.tabStruct.index < 0) {
// wrap to the last search match, and fix up to a real index
// value after we've matched.
this.tabStruct.index = Number.MAX_VALUE;
}
}
else {
this.tabStruct.index++;
}
var searchIndex = 0;
var targetIndex = this.tabStruct.index;
var text = this.tabStruct.original;
var search = /@?([a-zA-Z0-9_\-:\.]+)$/.exec(text);
// console.log("Searched in '%s' - got %s", text, search);
if (targetIndex === 0) { // 0 is always the original text
textArea.value = text;
}
else if (search && search[1]) {
// console.log("search found: " + search+" from "+text);
var expansion;
// FIXME: could do better than linear search here
for (var i=0; i<sortedMembers.length; i++) {
var member = sortedMembers[i];
if (member.name && searchIndex < targetIndex) {
if (member.name.toLowerCase().indexOf(search[1].toLowerCase()) === 0) {
expansion = member.name;
searchIndex++;
}
}
}
if (searchIndex < targetIndex) { // then search raw mxids
for (var i=0; i<sortedMembers.length; i++) {
if (searchIndex >= targetIndex) {
break;
}
var userId = sortedMembers[i].userId;
// === 1 because mxids are @username
if (userId.toLowerCase().indexOf(search[1].toLowerCase()) === 1) {
expansion = userId;
searchIndex++;
}
}
}
if (searchIndex === targetIndex ||
targetIndex === Number.MAX_VALUE) {
// xchat-style tab complete, add a colon if tab
// completing at the start of the text
if (search[0].length === text.length) {
expansion += ": ";
}
else {
expansion += " ";
}
textArea.value = text.replace(
/@?([a-zA-Z0-9_\-:\.]+)$/, expansion
);
// cancel blink
textArea.style["background-color"] = "";
if (targetIndex === Number.MAX_VALUE) {
// wrap the index around to the last index found
this.tabStruct.index = searchIndex;
targetIndex = searchIndex;
}
}
else {
// console.log("wrapped!");
textArea.style["background-color"] = "#faa";
setTimeout(function() {
textArea.style["background-color"] = "";
}, 150);
textArea.value = text;
this.tabStruct.index = 0;
}
}
else {
this.tabStruct.index = 0;
}
// prevent the default TAB operation (typically focus shifting)
ev.preventDefault();
},
onTypingActivity: function() {
this.isTyping = true;
if (!this.userTypingTimer) {
this.sendTyping(true);
}
this.startUserTypingTimer();
this.startServerTypingTimer();
},
onFinishedTyping: function() {
this.isTyping = false;
this.sendTyping(false);
this.stopUserTypingTimer();
this.stopServerTypingTimer();
},
startUserTypingTimer: function() {
this.stopUserTypingTimer();
var self = this;
this.userTypingTimer = setTimeout(function() {
self.isTyping = false;
self.sendTyping(self.isTyping);
self.userTypingTimer = null;
}, TYPING_USER_TIMEOUT);
},
stopUserTypingTimer: function() {
if (this.userTypingTimer) {
clearTimeout(this.userTypingTimer);
this.userTypingTimer = null;
}
},
startServerTypingTimer: function() {
if (!this.serverTypingTimer) {
var self = this;
this.serverTypingTimer = setTimeout(function() {
if (self.isTyping) {
self.sendTyping(self.isTyping);
self.startServerTypingTimer();
}
}, TYPING_SERVER_TIMEOUT / 2);
}
},
stopServerTypingTimer: function() {
if (this.serverTypingTimer) {
clearTimeout(this.servrTypingTimer);
this.serverTypingTimer = null;
}
},
sendTyping: function(isTyping) {
MatrixClientPeg.get().sendTyping(
this.props.room.roomId,
this.isTyping, TYPING_SERVER_TIMEOUT
).done();
},
refreshTyping: function() {
if (this.typingTimeout) {
clearTimeout(this.typingTimeout);
this.typingTimeout = null;
}
}
};
-50
View File
@@ -1,50 +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 MatrixClientPeg = require("../../MatrixClientPeg");
module.exports = {
shouldHighlight: function() {
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
return actions.tweaks.highlight;
},
getInitialState: function() {
return {
resending: false
};
},
onResend: function() {
var self = this;
self.setState({
resending: true
});
MatrixClientPeg.get().resendEvent(
this.props.mxEvent, MatrixClientPeg.get().getRoom(
this.props.mxEvent.getRoomId()
)
).finally(function() {
self.setState({
resending: false
});
})
}
};
-26
View File
@@ -1,26 +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: {
value: React.PropTypes.number,
max: React.PropTypes.number
},
};
-99
View File
@@ -1,99 +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';
/*
* State vars:
* this.state.call_state = the UI state of the call (see CallHandler)
*
* Props:
* room (JS SDK Room)
*/
var React = require('react');
var dis = require("../../dispatcher");
var CallHandler = require("../../CallHandler");
module.exports = {
propTypes: {
room: React.PropTypes.object.isRequired,
editing: React.PropTypes.bool,
onSettingsClick: React.PropTypes.func,
onSaveClick: React.PropTypes.func,
},
getDefaultProps: function() {
return {
editing: false,
onSettingsClick: function() {},
onSaveClick: function() {},
};
},
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() {
dis.dispatch({
action: 'place_call',
type: "video",
room_id: this.props.room.roomId
});
},
onVoiceClick: function() {
dis.dispatch({
action: 'place_call',
type: "voice",
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
});
}
};
-31
View File
@@ -1,31 +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: {
room: React.PropTypes.object.isRequired,
},
getInitialState: function() {
return {
power_levels_changed: false
};
}
};
-28
View File
@@ -1,28 +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 dis = require("../../dispatcher");
module.exports = {
onClick: function() {
dis.dispatch({
action: 'view_room',
room_id: this.props.room.roomId
});
},
};
@@ -1,21 +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';
module.exports = {
};
-64
View File
@@ -1,64 +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: {
onHsUrlChanged: React.PropTypes.func,
onIsUrlChanged: React.PropTypes.func,
default_hs_url: React.PropTypes.string,
default_is_url: React.PropTypes.string
},
getDefaultProps: function() {
return {
onHsUrlChanged: function() {},
onIsUrlChanged: function() {},
defaultHsUrl: 'https://matrix.org/',
defaultIsUrl: 'https://matrix.org/'
};
},
getInitialState: function() {
return {
hs_url: this.props.defaultHsUrl,
is_url: this.props.defaultIsUrl,
}
},
hsChanged: function(ev) {
this.setState({hs_url: ev.target.value}, function() {
this.props.onHsUrlChanged(this.state.hs_url);
});
},
isChanged: function(ev) {
this.setState({is_url: ev.target.value}, function() {
this.props.onIsUrlChanged(this.state.is_url);
});
},
getHsUrl: function() {
return this.state.hs_url;
},
getIsUrl: function() {
return this.state.is_url;
},
};
@@ -1,20 +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';
module.exports = {
};
-45
View File
@@ -1,45 +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: {
onChange: React.PropTypes.func,
selected_users: React.PropTypes.arrayOf(React.PropTypes.string),
},
getDefaultProps: function() {
return {
onChange: function() {},
selected: [],
};
},
addUser: function(user_id) {
if (this.props.selected_users.indexOf(user_id == -1)) {
this.props.onChange(this.props.selected_users.concat([user_id]));
}
},
removeUser: function(user_id) {
this.props.onChange(this.props.selected_users.filter(function(e) {
return e != user_id;
}));
},
};
@@ -1,75 +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 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
});
}
};
@@ -1,21 +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';
module.exports = {
};
-135
View File
@@ -1,135 +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");
var MatrixClientPeg = require("../../MatrixClientPeg");
var PresetValues = require('../atoms/create_room/Presets').Presets;
var q = require('q');
var encryption = require("../../encryption");
module.exports = {
propTypes: {
onRoomCreated: React.PropTypes.func,
},
phases: {
CONFIG: "CONFIG", // We're waiting for user to configure and hit create.
CREATING: "CREATING", // We're sending the request.
CREATED: "CREATED", // We successfully created the room.
ERROR: "ERROR", // There was an error while trying to create room.
},
getDefaultProps: function() {
return {
onRoomCreated: function() {},
};
},
getInitialState: function() {
return {
phase: this.phases.CONFIG,
error_string: "",
is_private: true,
share_history: false,
default_preset: PresetValues.PrivateChat,
topic: '',
room_name: '',
invited_users: [],
};
},
onCreateRoom: function() {
var options = {};
if (this.state.room_name) {
options.name = this.state.room_name;
}
if (this.state.topic) {
options.topic = this.state.topic;
}
if (this.state.preset) {
if (this.state.preset != PresetValues.Custom) {
options.preset = this.state.preset;
} else {
options.initial_state = [
{
type: "m.room.join_rules",
content: {
"join_rules": this.state.is_private ? "invite" : "public"
}
},
{
type: "m.room.history_visibility",
content: {
"history_visibility": this.state.share_history ? "shared" : "invited"
}
},
];
}
}
options.invite = this.state.invited_users;
var alias = this.getAliasLocalpart();
if (alias) {
options.room_alias_name = alias;
}
var cli = MatrixClientPeg.get();
if (!cli) {
// TODO: Error.
console.error("Cannot create room: No matrix client.");
return;
}
var deferred = cli.createRoom(options);
var response;
if (this.state.encrypt) {
deferred = deferred.then(function(res) {
response = res;
return encryption.enableEncryption(
cli, response.roomId, options.invite
);
}).then(function() {
return q(response) }
);
}
this.setState({
phase: this.phases.CREATING,
});
var self = this;
deferred.then(function (resp) {
self.setState({
phase: self.phases.CREATED,
});
self.props.onRoomCreated(resp.room_id);
}, function(err) {
self.setState({
phase: self.phases.ERROR,
error_string: err.toString(),
});
});
}
};
-39
View File
@@ -1,39 +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: {
title: React.PropTypes.string,
description: React.PropTypes.string,
button: React.PropTypes.string,
focus: React.PropTypes.bool,
onFinished: React.PropTypes.func.isRequired,
},
getDefaultProps: function() {
var self = this;
return {
title: "Error",
description: "An error has occurred.",
button: "OK",
focus: true,
};
},
};
-35
View File
@@ -1,35 +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 dis = require("../../dispatcher");
module.exports = {
logOut: function() {
dis.dispatch({action: 'logout'});
if (this.props.onFinished) {
this.props.onFinished();
}
},
cancelPrompt: function() {
if (this.props.onFinished) {
this.props.onFinished();
}
}
};
-164
View File
@@ -1,164 +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");
var MatrixClientPeg = require("../../MatrixClientPeg");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var INITIAL_LOAD_NUM_MEMBERS = 50;
module.exports = {
getInitialState: function() {
var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS);
return {
memberDict: members
};
},
componentWillMount: function() {
var cli = MatrixClientPeg.get();
cli.on("RoomState.members", this.onRoomStateMember);
cli.on("Room", this.onRoom); // invites
},
componentWillUnmount: function() {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room", this.onRoom);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().removeListener("User.presence", this.userPresenceFn);
}
},
componentDidMount: function() {
var self = this;
setTimeout(function() {
if (!self.isMounted()) return;
self.setState({
memberDict: self.roomMembers()
});
}, 50);
// Attach a SINGLE listener for global presence changes then locate the
// member tile and re-render it. This is more efficient than every tile
// evar attaching their own listener.
function updateUserState(event, user) {
var tile = self.refs[user.userId];
if (tile) {
// update the whole list to get the order right, not just this cell...
self.forceUpdate();
// tile.forceUpdate();
}
}
MatrixClientPeg.get().on("User.presence", updateUserState);
this.userPresenceFn = updateUserState;
},
// Remember to set 'key' on a MemberList to the ID of the room it's for
/*componentWillReceiveProps: function(newProps) {
},*/
onRoom: function(room) {
if (room.roomId !== this.props.roomId) {
return;
}
// We listen for room events because when we accept an invite
// we need to wait till the room is fully populated with state
// before refreshing the member list else we get a stale list.
this._updateList();
},
onRoomStateMember: function(ev, state, member) {
this._updateList();
},
_updateList: function() {
var members = this.roomMembers();
this.setState({
memberDict: members
});
},
onInvite: function(inputText) {
var self = this;
// sanity check the input
inputText = inputText.trim(); // react requires es5-shim so we know trim() exists
if (inputText[0] !== '@' || inputText.indexOf(":") === -1) {
console.error("Bad user ID to invite: %s", inputText);
Modal.createDialog(ErrorDialog, {
title: "Invite Error",
description: "Malformed user ID. Should look like '@localpart:domain'"
});
return;
}
self.setState({
inviting: true
});
console.log("Invite %s to %s", inputText, this.props.roomId);
MatrixClientPeg.get().invite(this.props.roomId, inputText).done(
function(res) {
console.log("Invited");
self.setState({
inviting: false
});
}, function(err) {
console.error("Failed to invite: %s", JSON.stringify(err));
Modal.createDialog(ErrorDialog, {
title: "Server error whilst inviting",
description: err.message
});
self.setState({
inviting: false
});
});
},
roomMembers: function(limit) {
if (!this.props.roomId) return {};
var cli = MatrixClientPeg.get();
var room = cli.getRoom(this.props.roomId);
if (!room) return {};
var all_members = room.currentState.members;
var all_user_ids = Object.keys(all_members);
all_user_ids.sort(function(userIdA, userIdB) {
var userA = all_members[userIdA].user;
var userB = all_members[userIdB].user;
var latA = userA ? userA.lastActiveAgo || Number.MAX_VALUE : Number.MAX_VALUE;
var latB = userB ? userB.lastActiveAgo || Number.MAX_VALUE : Number.MAX_VALUE;
return latA - latB;
});
var to_display = {};
var count = 0;
for (var i = 0; i < all_user_ids.length && (limit === undefined || count < limit); ++i) {
var user_id = all_user_ids[i];
var m = all_members[user_id];
if (m.membership == 'join' || m.membership == 'invite') {
to_display[user_id] = m;
++count;
}
}
return to_display;
}
};
-129
View File
@@ -1,129 +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 MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
/*
* Dispatches:
* {
* action: "notifier_enabled",
* value: boolean
* }
*/
// XXX: This isn't an organism surely in the atomic sense of the word
// what on earth is it doing here?!
module.exports = {
start: function() {
this.boundOnRoomTimeline = this.onRoomTimeline.bind(this);
MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
this.state = { 'toolbarHidden' : false };
},
stop: function() {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline);
}
},
supportsDesktopNotifications: function() {
return !!global.Notification;
},
havePermission: function() {
if (!this.supportsDesktopNotifications()) return false;
return global.Notification.permission == 'granted';
},
setEnabled: function(enable, callback) {
console.log("Notifier.setEnabled => %s", enable);
if(enable) {
if (!this.havePermission()) {
var self = this;
global.Notification.requestPermission(function() {
if (callback) {
callback();
dis.dispatch({
action: "notifier_enabled",
value: true
});
}
});
}
if (!global.localStorage) return;
global.localStorage.setItem('notifications_enabled', 'true');
if (this.havePermission) {
dis.dispatch({
action: "notifier_enabled",
value: true
});
}
}
else {
if (!global.localStorage) return;
global.localStorage.setItem('notifications_enabled', 'false');
dis.dispatch({
action: "notifier_enabled",
value: false
});
}
this.setToolbarHidden(false);
},
isEnabled: function() {
if (!this.havePermission()) return false;
if (!global.localStorage) return true;
var enabled = global.localStorage.getItem('notifications_enabled');
if (enabled === null) return true;
return enabled === 'true';
},
setToolbarHidden: function(hidden) {
this.state.toolbarHidden = hidden;
dis.dispatch({
action: "notifier_enabled",
value: this.isEnabled()
});
},
isToolbarHidden: function() {
return this.state.toolbarHidden;
},
onRoomTimeline: function(ev, room, toStartOfTimeline) {
if (toStartOfTimeline) return;
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;
if (!this.isEnabled()) {
return;
}
var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
if (actions && actions.notify) {
this.displayNotification(ev, room);
}
}
};
@@ -1,39 +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: {
title: React.PropTypes.string,
description: React.PropTypes.string,
button: React.PropTypes.string,
focus: React.PropTypes.bool,
onFinished: React.PropTypes.func.isRequired,
},
getDefaultProps: function() {
var self = this;
return {
title: "",
description: "",
button: "OK",
focus: true,
};
},
};
-170
View File
@@ -1,170 +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");
var MatrixClientPeg = require("../../MatrixClientPeg");
var RoomListSorter = require("../../RoomListSorter");
var dis = require("../../dispatcher");
var ComponentBroker = require('../../ComponentBroker');
var ConferenceHandler = require("../../ConferenceHandler");
var CallHandler = require("../../CallHandler");
var RoomTile = ComponentBroker.get("molecules/RoomTile");
var HIDE_CONFERENCE_CHANS = true;
module.exports = {
componentWillMount: function() {
var cli = MatrixClientPeg.get();
cli.on("Room", this.onRoom);
cli.on("Room.timeline", this.onRoomTimeline);
cli.on("Room.name", this.onRoomName);
var rooms = this.getRoomList();
this.setState({
roomList: rooms,
activityMap: {}
});
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
onAction: function(payload) {
switch (payload.action) {
// listen for call state changes to prod the render method, which
// may hide the global CallView if the call it is tracking is dead
case 'call_state':
this._recheckCallElement(this.props.selectedRoom);
break;
}
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room", this.onRoom);
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
}
},
componentWillReceiveProps: function(newProps) {
this.state.activityMap[newProps.selectedRoom] = undefined;
this._recheckCallElement(newProps.selectedRoom);
this.setState({
activityMap: this.state.activityMap
});
},
onRoom: function(room) {
this.refreshRoomList();
},
onRoomTimeline: function(ev, room, toStartOfTimeline) {
if (toStartOfTimeline) return;
var newState = {
roomList: this.getRoomList()
};
if (
room.roomId != this.props.selectedRoom &&
ev.getSender() != MatrixClientPeg.get().credentials.userId)
{
var hl = 1;
var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
if (actions && actions.tweaks && actions.tweaks.highlight) {
hl = 2;
}
// obviously this won't deep copy but this shouldn't be necessary
var amap = this.state.activityMap;
amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl);
newState.activityMap = amap;
}
this.setState(newState);
},
onRoomName: function(room) {
this.refreshRoomList();
},
refreshRoomList: function() {
var rooms = this.getRoomList();
this.setState({
roomList: rooms
});
},
getRoomList: function() {
return RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms().filter(function(room) {
var me = room.getMember(MatrixClientPeg.get().credentials.userId);
var shouldShowRoom = (
me && (me.membership == "join" || me.membership == "invite")
);
// hiding conf rooms only ever toggles shouldShowRoom to false
if (shouldShowRoom && HIDE_CONFERENCE_CHANS) {
// we want to hide the 1:1 conf<->user room and not the group chat
var joinedMembers = room.getJoinedMembers();
if (joinedMembers.length === 2) {
var otherMember = joinedMembers.filter(function(m) {
return m.userId !== me.userId
})[0];
if (ConferenceHandler.isConferenceUser(otherMember)) {
// console.log("Hiding conference 1:1 room %s", room.roomId);
shouldShowRoom = false;
}
}
}
return shouldShowRoom;
})
);
},
_recheckCallElement: function(selectedRoomId) {
// if we aren't viewing a room with an ongoing call, but there is an
// active call, show the call element - we need to do this to make
// audio/video not crap out
var activeCall = CallHandler.getAnyActiveCall();
var callForRoom = CallHandler.getCallForRoom(selectedRoomId);
var showCall = (activeCall && !callForRoom);
this.setState({
show_call_element: showCall
});
},
makeRoomTiles: function() {
var self = this;
return this.state.roomList.map(function(room) {
var selected = room.roomId == self.props.selectedRoom;
return (
<RoomTile
room={room}
key={room.roomId}
selected={selected}
unread={self.state.activityMap[room.roomId] === 1}
highlight={self.state.activityMap[room.roomId] === 2}
/>
);
});
}
};
-510
View File
@@ -1,510 +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 MatrixClientPeg = require("../../MatrixClientPeg");
var React = require("react");
var q = require("q");
var ContentMessages = require("../../ContentMessages");
var WhoIsTyping = require("../../WhoIsTyping");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var dis = require("../../dispatcher");
var PAGINATE_SIZE = 20;
var INITIAL_SIZE = 100;
var ConferenceHandler = require("../../ConferenceHandler");
var CallHandler = require("../../CallHandler");
var Notifier = ComponentBroker.get('organisms/Notifier');
var tileTypes = {
'm.room.message': ComponentBroker.get('molecules/MessageTile'),
'm.room.member' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.invite' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.answer' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.hangup' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.room.topic' : ComponentBroker.get('molecules/EventAsTextTile'),
};
var DateSeparator = ComponentBroker.get('molecules/DateSeparator');
module.exports = {
getInitialState: function() {
return {
room: this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null,
messageCap: INITIAL_SIZE,
editingRoomSettings: false,
uploadingRoomSettings: false,
numUnreadMessages: 0,
draggingFile: false,
}
},
componentWillMount: function() {
this.dispatcherRef = dis.register(this.onAction);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().on("Room.name", this.onRoomName);
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
this.atBottom = true;
},
componentWillUnmount: function() {
if (this.refs.messageWrapper) {
var messageWrapper = this.refs.messageWrapper.getDOMNode();
messageWrapper.removeEventListener('drop', this.onDrop);
messageWrapper.removeEventListener('dragover', this.onDragOver);
messageWrapper.removeEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.removeEventListener('dragend', this.onDragLeaveOrEnd);
}
dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
}
},
onAction: function(payload) {
switch (payload.action) {
case 'message_send_failed':
case 'message_sent':
this.setState({
room: MatrixClientPeg.get().getRoom(this.props.roomId)
});
this.forceUpdate();
break;
case 'notifier_enabled':
this.forceUpdate();
break;
case 'call_state':
if (CallHandler.getCallForRoom(this.props.roomId)) {
// Call state has changed so we may be loading video elements
// which will obscure the message log.
// scroll to bottom
var messageWrapper = this.refs.messageWrapper;
if (messageWrapper) {
messageWrapper = messageWrapper.getDOMNode();
messageWrapper.scrollTop = messageWrapper.scrollHeight;
}
}
// possibly remove the conf call notification if we're now in
// the conf
this._updateConfCallNotification();
break;
}
},
// MatrixRoom still showing the messages from the old room?
// Set the key to the room_id. Sadly you can no longer get at
// the key from inside the component, or we'd check this in code.
/*componentWillReceiveProps: function(props) {
},*/
onRoomTimeline: function(ev, room, toStartOfTimeline) {
if (!this.isMounted()) return;
// ignore anything that comes in whilst pagingating: we get one
// event for each new matrix event so this would cause a huge
// number of UI updates. Just update the UI when the paginate
// call returns.
if (this.state.paginating) return;
// no point handling anything while we're waiting for the join to finish:
// we'll only be showing a spinner.
if (this.state.joining) return;
if (room.roomId != this.props.roomId) return;
if (this.refs.messageWrapper) {
var messageWrapper = this.refs.messageWrapper.getDOMNode();
this.atBottom = (
messageWrapper.scrollHeight - messageWrapper.scrollTop <=
(messageWrapper.clientHeight + 150)
);
}
var currentUnread = this.state.numUnreadMessages;
if (!toStartOfTimeline &&
(ev.getSender() !== MatrixClientPeg.get().credentials.userId)) {
// update unread count when scrolled up
if (this.atBottom) {
currentUnread = 0;
}
else {
currentUnread += 1;
}
}
this.setState({
room: MatrixClientPeg.get().getRoom(this.props.roomId),
numUnreadMessages: currentUnread
});
if (toStartOfTimeline && !this.state.paginating) {
this.fillSpace();
}
},
onRoomName: function(room) {
if (room.roomId == this.props.roomId) {
this.setState({
room: room
});
}
},
onRoomMemberTyping: function(ev, member) {
this.forceUpdate();
},
onRoomStateMember: function(ev, state, member) {
if (member.roomId !== this.props.roomId ||
member.userId !== ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) {
return;
}
this._updateConfCallNotification();
},
_updateConfCallNotification: function() {
var confMember = MatrixClientPeg.get().getRoom(this.props.roomId).getMember(
ConferenceHandler.getConferenceUserIdForRoom(this.props.roomId)
);
if (!confMember) {
return;
}
var confCall = CallHandler.getConferenceCall(confMember.roomId);
// A conf call notification should be displayed if there is an ongoing
// conf call but this cilent isn't a part of it.
this.setState({
displayConfCallNotification: (
(!confCall || confCall.call_state === "ended") &&
confMember.membership === "join"
)
});
},
onConferenceNotificationClick: function() {
dis.dispatch({
action: 'place_call',
type: "video",
room_id: this.props.roomId
});
},
componentDidMount: function() {
if (this.refs.messageWrapper) {
var messageWrapper = this.refs.messageWrapper.getDOMNode();
messageWrapper.addEventListener('drop', this.onDrop);
messageWrapper.addEventListener('dragover', this.onDragOver);
messageWrapper.addEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.addEventListener('dragend', this.onDragLeaveOrEnd);
messageWrapper.scrollTop = messageWrapper.scrollHeight;
this.fillSpace();
}
this._updateConfCallNotification();
},
componentDidUpdate: function() {
if (!this.refs.messageWrapper) return;
var messageWrapper = this.refs.messageWrapper.getDOMNode();
if (this.state.paginating && !this.waiting_for_paginate) {
var heightGained = messageWrapper.scrollHeight - this.oldScrollHeight;
messageWrapper.scrollTop += heightGained;
this.oldScrollHeight = undefined;
if (!this.fillSpace()) {
this.setState({paginating: false});
}
} else if (this.atBottom) {
messageWrapper.scrollTop = messageWrapper.scrollHeight;
if (this.state.numUnreadMessages !== 0) {
this.setState({numUnreadMessages: 0});
}
}
},
fillSpace: function() {
if (!this.refs.messageWrapper) return;
var messageWrapper = this.refs.messageWrapper.getDOMNode();
if (messageWrapper.scrollTop < messageWrapper.clientHeight && this.state.room.oldState.paginationToken) {
this.setState({paginating: true});
this.oldScrollHeight = messageWrapper.scrollHeight;
if (this.state.messageCap < this.state.room.timeline.length) {
this.waiting_for_paginate = false;
var cap = Math.min(this.state.messageCap + PAGINATE_SIZE, this.state.room.timeline.length);
this.setState({messageCap: cap, paginating: true});
} else {
this.waiting_for_paginate = true;
var cap = this.state.messageCap + PAGINATE_SIZE;
this.setState({messageCap: cap, paginating: true});
var self = this;
MatrixClientPeg.get().scrollback(this.state.room, PAGINATE_SIZE).finally(function() {
self.waiting_for_paginate = false;
if (self.isMounted()) {
self.setState({
room: MatrixClientPeg.get().getRoom(self.props.roomId)
});
}
// wait and set paginating to false when the component updates
});
}
return true;
}
return false;
},
onJoinButtonClicked: function(ev) {
var self = this;
MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() {
self.setState({
joining: false,
room: MatrixClientPeg.get().getRoom(self.props.roomId)
});
}, function(error) {
self.setState({
joining: false,
joinError: error
});
});
this.setState({
joining: true
});
},
onMessageListScroll: function(ev) {
if (this.refs.messageWrapper) {
var messageWrapper = this.refs.messageWrapper.getDOMNode();
var wasAtBottom = this.atBottom;
this.atBottom = messageWrapper.scrollHeight - messageWrapper.scrollTop <= messageWrapper.clientHeight;
if (this.atBottom && !wasAtBottom) {
this.forceUpdate(); // remove unread msg count
}
}
if (!this.state.paginating) this.fillSpace();
},
onDragOver: function(ev) {
ev.stopPropagation();
ev.preventDefault();
ev.dataTransfer.dropEffect = 'none';
var items = ev.dataTransfer.items;
if (items.length == 1) {
if (items[0].kind == 'file') {
this.setState({ draggingFile : true });
ev.dataTransfer.dropEffect = 'copy';
}
}
},
onDrop: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
var files = ev.dataTransfer.files;
if (files.length == 1) {
this.uploadFile(files[0]);
}
},
onDragLeaveOrEnd: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
},
uploadFile: function(file) {
this.setState({
upload: {
fileName: file.name,
uploadedBytes: 0,
totalBytes: file.size
}
});
var self = this;
ContentMessages.sendContentToRoom(
file, this.props.roomId, MatrixClientPeg.get()
).progress(function(ev) {
//console.log("Upload: "+ev.loaded+" / "+ev.total);
self.setState({
upload: {
fileName: file.name,
uploadedBytes: ev.loaded,
totalBytes: ev.total
}
});
}).finally(function() {
self.setState({
upload: undefined
});
}).done(undefined, function() {
// display error message
});
},
getWhoIsTypingString: function() {
return WhoIsTyping.whoIsTypingString(this.state.room);
},
getEventTiles: function() {
var ret = [];
var count = 0;
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
var mxEv = this.state.room.timeline[i];
var TileType = tileTypes[mxEv.getType()];
var continuation = false;
var last = false;
var dateSeparator = null;
if (i == this.state.room.timeline.length - 1) {
last = true;
}
if (i > 0 && count < this.state.messageCap - 1) {
if (this.state.room.timeline[i].sender &&
this.state.room.timeline[i - 1].sender &&
(this.state.room.timeline[i].sender.userId ===
this.state.room.timeline[i - 1].sender.userId) &&
(this.state.room.timeline[i].getType() ==
this.state.room.timeline[i - 1].getType())
)
{
continuation = true;
}
var ts0 = this.state.room.timeline[i - 1].getTs();
var ts1 = this.state.room.timeline[i].getTs();
if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) {
dateSeparator = <DateSeparator key={ts1} ts={ts1}/>;
continuation = false;
}
}
if (!TileType) continue;
ret.unshift(
// XXX: don't wrap everything in a needless li - make the TileType a li if we must :(
<li key={mxEv.getId()}><TileType mxEvent={mxEv} continuation={continuation} last={last}/></li>
);
if (dateSeparator) {
ret.unshift(dateSeparator);
}
++count;
}
return ret;
},
uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) {
var old_name = this.state.room.name;
var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', '');
if (old_topic) {
old_topic = old_topic.getContent().topic;
} else {
old_topic = "";
}
var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', '');
if (old_join_rule) {
old_join_rule = old_join_rule.getContent().join_rule;
} else {
old_join_rule = "invite";
}
var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', '');
if (old_history_visibility) {
old_history_visibility = old_history_visibility.getContent().history_visibility;
} else {
old_history_visibility = "shared";
}
var deferreds = [];
if (old_name != new_name && new_name != undefined && new_name) {
deferreds.push(
MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name)
);
}
if (old_topic != new_topic && new_topic != undefined) {
deferreds.push(
MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic)
);
}
if (old_join_rule != new_join_rule && new_join_rule != undefined) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.join_rules", {
join_rule: new_join_rule,
}, ""
)
);
}
if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.history_visibility", {
history_visibility: new_history_visibility,
}, ""
)
);
}
if (new_power_levels) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.power_levels", new_power_levels, ""
)
);
}
if (deferreds.length) {
var self = this;
q.all(deferreds).fail(function(err) {
Modal.createDialog(ErrorDialog, {
title: "Failed to set state",
description: err.toString()
});
}).finally(function() {
self.setState({
uploadingRoomSettings: false,
});
});
} else {
this.setState({
editingRoomSettings: false,
uploadingRoomSettings: false,
});
}
}
};
-72
View File
@@ -1,72 +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 MatrixClientPeg = require("../../MatrixClientPeg");
var React = require("react");
var q = require('q');
var dis = require("../../dispatcher");
var version = require('../../../package.json').version;
var ComponentBroker = require('../../ComponentBroker');
module.exports = {
Phases: {
Loading: "loading",
Display: "display",
},
getInitialState: function() {
return {
displayName: null,
avatarUrl: null,
threePids: [],
clientVersion: version,
phase: this.Phases.Loading,
};
},
changeDisplayname: function(new_displayname) {
if (this.state.displayName == new_displayname) return;
var self = this;
return MatrixClientPeg.get().setDisplayName(new_displayname).then(
function() { self.setState({displayName: new_displayname}); },
function(err) { console.err(err); }
);
},
componentWillMount: function() {
var self = this;
var cli = MatrixClientPeg.get();
var profile_d = cli.getProfileInfo(cli.credentials.userId);
var threepid_d = cli.getThreePids();
q.all([profile_d, threepid_d]).then(
function(resps) {
self.setState({
displayName: resps[0].displayname,
avatarUrl: resps[0].avatar_url,
threepids: resps[1].threepids,
phase: self.Phases.Display,
});
},
function(err) { console.err(err); }
);
}
}
-332
View File
@@ -1,332 +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';
// should be atomised
var Loader = require("react-loader");
var MatrixClientPeg = require("../../MatrixClientPeg");
var RoomListSorter = require("../../RoomListSorter");
var Presence = require("../../Presence");
var dis = require("../../dispatcher");
var q = require("q");
var ComponentBroker = require('../../ComponentBroker');
var Notifier = ComponentBroker.get('organisms/Notifier');
var MatrixTools = require('../../MatrixTools');
module.exports = {
PageTypes: {
RoomView: "room_view",
UserSettings: "user_settings",
CreateRoom: "create_room",
RoomDirectory: "room_directory",
},
AuxPanel: {
RoomSettings: "room_settings",
},
getInitialState: function() {
var s = {
logged_in: !!(MatrixClientPeg.get() && MatrixClientPeg.get().credentials),
ready: false,
aux_panel: null,
};
if (s.logged_in) {
if (MatrixClientPeg.get().getRooms().length) {
s.page_type = this.PageTypes.RoomView;
} else {
s.page_type = this.PageTypes.RoomDirectory;
}
}
return s;
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
if (this.state.logged_in) {
this.startMatrixClient();
}
this.focusComposer = false;
document.addEventListener("keydown", this.onKeyDown);
window.addEventListener("focus", this.onFocus);
if (this.state.logged_in) {
this.notifyNewScreen('');
} else {
this.notifyNewScreen('login');
}
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
document.removeEventListener("keydown", this.onKeyDown);
window.removeEventListener("focus", this.onFocus);
},
componentDidUpdate: function() {
if (this.focusComposer) {
dis.dispatch({action: 'focus_composer'});
this.focusComposer = false;
}
},
onAction: function(payload) {
var roomIndexDelta = 1;
switch (payload.action) {
case 'logout':
this.replaceState({
logged_in: false,
ready: false
});
if (window.localStorage) {
window.localStorage.clear();
}
Notifier.stop();
Presence.stop();
MatrixClientPeg.get().stopClient();
MatrixClientPeg.get().removeAllListeners();
MatrixClientPeg.unset();
this.notifyNewScreen('');
break;
case 'start_registration':
if (this.state.logged_in) return;
var newState = payload.params || {};
newState.screen = 'register';
if (
payload.params &&
payload.params.client_secret &&
payload.params.session_id &&
payload.params.hs_url &&
payload.params.is_url &&
payload.params.sid
) {
newState.register_client_secret = payload.params.client_secret;
newState.register_session_id = payload.params.session_id;
newState.register_hs_url = payload.params.hs_url;
newState.register_is_url = payload.params.is_url;
newState.register_id_sid = payload.params.sid;
}
this.replaceState(newState);
this.notifyNewScreen('register');
break;
case 'start_login':
if (this.state.logged_in) return;
this.replaceState({
screen: 'login'
});
this.notifyNewScreen('login');
break;
case 'view_room':
this.focusComposer = true;
this.setState({
currentRoom: payload.room_id,
page_type: this.PageTypes.RoomView,
});
if (this.sdkReady) {
// if the SDK is not ready yet, remember what room
// we're supposed to be on but don't notify about
// the new screen yet (we won't be showing it yet)
// The normal case where this happens is navigating
// to the room in the URL bar on page load.
var presentedId = payload.room_id;
var room = MatrixClientPeg.get().getRoom(payload.room_id);
if (room) {
var theAlias = MatrixTools.getCanonicalAliasForRoom(room);
if (theAlias) presentedId = theAlias;
}
this.notifyNewScreen('room/'+presentedId);
}
break;
case 'view_prev_room':
roomIndexDelta = -1;
case 'view_next_room':
var allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms()
);
var roomIndex = -1;
for (var i = 0; i < allRooms.length; ++i) {
if (allRooms[i].roomId == this.state.currentRoom) {
roomIndex = i;
break;
}
}
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
if (roomIndex < 0) roomIndex = allRooms.length - 1;
this.focusComposer = true;
this.setState({
currentRoom: allRooms[roomIndex].roomId
});
this.notifyNewScreen('room/'+allRooms[roomIndex].roomId);
break;
case 'view_indexed_room':
var allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms()
);
var roomIndex = payload.roomIndex;
if (allRooms[roomIndex]) {
this.focusComposer = true;
this.setState({
currentRoom: allRooms[roomIndex].roomId
});
this.notifyNewScreen('room/'+allRooms[roomIndex].roomId);
}
break;
case 'view_user_settings':
this.setState({
page_type: this.PageTypes.UserSettings,
});
break;
case 'view_create_room':
this.setState({
page_type: this.PageTypes.CreateRoom,
});
break;
case 'view_room_directory':
this.setState({
page_type: this.PageTypes.RoomDirectory,
});
break;
case 'notifier_enabled':
this.forceUpdate();
break;
}
},
onLoggedIn: function() {
this.setState({
screen: undefined,
logged_in: true
});
this.startMatrixClient();
this.notifyNewScreen('');
},
startMatrixClient: function() {
var cli = MatrixClientPeg.get();
var self = this;
cli.on('syncComplete', function() {
self.sdkReady = true;
if (!self.state.currentRoom) {
var firstRoom = null;
if (cli.getRooms() && cli.getRooms().length) {
firstRoom = RoomListSorter.mostRecentActivityFirst(
cli.getRooms()
)[0].roomId;
self.setState({ready: true, currentRoom: firstRoom, page_type: self.PageTypes.RoomView});
} else {
self.setState({ready: true, page_type: self.PageTypes.RoomDirectory});
}
} else {
self.setState({ready: true, currentRoom: self.state.currentRoom});
}
// we notifyNewScreen now because now the room will actually be displayed,
// and (mostly) now we can get the correct alias.
var presentedId = self.state.currentRoom;
var room = MatrixClientPeg.get().getRoom(self.state.currentRoom);
if (room) {
var theAlias = MatrixTools.getCanonicalAliasForRoom(room);
if (theAlias) presentedId = theAlias;
}
self.notifyNewScreen('room/'+presentedId);
dis.dispatch({action: 'focus_composer'});
});
cli.on('Call.incoming', function(call) {
dis.dispatch({
action: 'incoming_call',
call: call
});
});
Notifier.start();
Presence.start();
cli.startClient();
},
onKeyDown: function(ev) {
if (ev.altKey) {
if (ev.ctrlKey && ev.keyCode > 48 && ev.keyCode < 58) {
dis.dispatch({
action: 'view_indexed_room',
roomIndex: ev.keyCode - 49,
});
ev.stopPropagation();
ev.preventDefault();
return;
}
switch (ev.keyCode) {
case 38:
dis.dispatch({action: 'view_prev_room'});
ev.stopPropagation();
ev.preventDefault();
break;
case 40:
dis.dispatch({action: 'view_next_room'});
ev.stopPropagation();
ev.preventDefault();
break;
}
}
},
onFocus: function(ev) {
dis.dispatch({action: 'focus_composer'});
},
showScreen: function(screen, params) {
if (screen == 'register') {
dis.dispatch({
action: 'start_registration',
params: params
});
} else if (screen == 'login') {
dis.dispatch({
action: 'start_login',
params: params
});
} else if (screen.indexOf('room/') == 0) {
var roomString = screen.split('/')[1];
var defer = q.defer();
if (roomString[0] == '#') {
var self = this;
MatrixClientPeg.get().getRoomIdForAlias(roomString).done(function(result) {
if (self.sdkReady) self.setState({ready: true});
defer.resolve(result.room_id);
}, function() {
if (self.sdkReady) self.setState({ready: true});
defer.resolve(null);
});
this.setState({ready: false});
} else {
defer.resolve(roomString);
}
defer.promise.done(function(roomId) {
dis.dispatch({
action: 'view_room',
room_id: roomId
});
});
}
},
notifyNewScreen: function(screen) {
if (this.props.onNewScreen) {
this.props.onNewScreen(screen);
}
}
};
-114
View File
@@ -1,114 +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');
var MatrixClientPeg = require("../../MatrixClientPeg");
var Matrix = require("matrix-js-sdk");
var dis = require("../../dispatcher");
var ComponentBroker = require("../../ComponentBroker");
module.exports = {
getInitialState: function() {
return {
step: 'choose_hs',
busy: false,
currentStep: 0,
totalSteps: 1
};
},
setStep: function(step) {
this.setState({ step: step, busy: false });
},
onHSChosen: function() {
MatrixClientPeg.replaceUsingUrls(
this.getHsUrl(),
this.getIsUrl()
);
this.setState({
hs_url: this.getHsUrl(),
is_url: this.getIsUrl(),
});
this.setStep("fetch_stages");
var cli = MatrixClientPeg.get();
this.setState({
busy: true,
errorText: "",
});
var self = this;
cli.loginFlows().done(function(result) {
self.setState({
flows: result.flows,
currentStep: 1,
totalSteps: result.flows.length+1
});
self.setStep('stage_'+result.flows[0].type);
}, function(error) {
self.setStep("choose_hs");
self.setState({errorText: 'Unable to contact the given Home Server'});
});
},
onUserPassEntered: function(ev) {
ev.preventDefault();
this.setState({
busy: true,
errorText: "",
});
var self = this;
var formVals = this.getFormVals();
var loginParams = {
password: formVals.password
};
if (formVals.username.indexOf('@') > 0) {
loginParams.medium = 'email';
loginParams.address = formVals.username;
} else {
loginParams.user = formVals.username;
}
MatrixClientPeg.get().login('m.login.password', loginParams).done(function(data) {
MatrixClientPeg.replaceUsingAccessToken(
self.state.hs_url, self.state.is_url,
data.user_id, data.access_token
);
if (self.props.onLoggedIn) {
self.props.onLoggedIn();
}
}, function(error) {
self.setStep("stage_m.login.password");
if (error.httpStatus == 400 && loginParams.medium) {
self.setState({errorText: 'This Home Server does not support login using email address.'});
} else {
self.setState({errorText: 'Login failed.'});
}
});
},
showRegister: function(ev) {
ev.preventDefault();
dis.dispatch({
action: 'start_registration'
});
}
};
-347
View File
@@ -1,347 +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');
var MatrixClientPeg = require("../../MatrixClientPeg");
var Matrix = require("matrix-js-sdk");
var dis = require("../../dispatcher");
var ComponentBroker = require("../../ComponentBroker");
module.exports = {
FieldErrors: {
PasswordMismatch: 'PasswordMismatch',
TooShort: 'TooShort',
Missing: 'Missing',
InUse: 'InUse'
},
getInitialState: function() {
return {
step: 'initial',
busy: false,
currentStep: 0,
totalSteps: 1
};
},
componentWillMount: function() {
this.savedParams = {
email: '',
username: '',
password: '',
confirmPassword: ''
};
this.readNewProps();
},
componentWillReceiveProps: function() {
this.readNewProps();
},
readNewProps: function() {
if (this.props.clientSecret && this.props.hsUrl &&
this.props.isUrl && this.props.sessionId &&
this.props.idSid) {
this.authSessionId = this.props.sessionId;
MatrixClientPeg.replaceUsingUrls(
this.props.hsUrl,
this.props.isUrl
);
this.setState({
hs_url: this.props.hsUrl,
is_url: this.props.isUrl
});
this.savedParams = {client_secret: this.props.clientSecret};
this.setState({busy: true});
var isLocation = document.createElement('a');
isLocation.href = this.props.isUrl;
var auth = {
type: 'm.login.email.identity',
threepid_creds: {
sid: this.props.idSid,
client_secret: this.savedParams.client_secret,
id_server: isLocation.host
}
};
this.tryRegister(auth);
}
},
componentDidUpdate: function() {
// Just putting a script tag into the returned jsx doesn't work, annoyingly,
// so we do this instead.
if (this.refs.recaptchaContainer) {
var scriptTag = document.createElement('script');
window.mx_on_recaptcha_loaded = this.onCaptchaLoaded;
scriptTag.setAttribute('src', "https://www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit");
this.refs.recaptchaContainer.getDOMNode().appendChild(scriptTag);
}
},
setStep: function(step) {
this.setState({ step: step, errorText: '', busy: false });
},
getSupportedStageTypes: function() {
return ['m.login.email.identity', 'm.login.recaptcha'];
},
chooseFlow: function(flows) {
// this is fairly simple right now
var supportedTypes = this.getSupportedStageTypes();
var emailFlow = null;
var otherFlow = null;
for (var flowI = 0; flowI < flows.length; ++flowI) {
var flow = flows[flowI];
var flowHasEmail = false;
var flowSupported = true;
for (var stageI = 0; stageI < flow.stages.length; ++stageI) {
var stage = flow.stages[stageI];
if (supportedTypes.indexOf(stage) == -1) {
flowSupported = false;
}
if (stage == 'm.login.email.identity') {
flowHasEmail = true;
}
}
if (flowSupported) {
if (flowHasEmail) {
emailFlow = flow;
} else {
otherFlow = flow;
}
}
}
if (
this.savedParams.email != '' ||
this.completedStages.indexOf('m.login.email.identity') > -1
) {
return emailFlow;
} else {
return otherFlow;
}
},
firstUncompletedStageIndex: function(flow) {
if (this.completedStages === undefined) return 0;
for (var i = 0; i < flow.stages.length; ++i) {
if (this.completedStages.indexOf(flow.stages[i]) == -1) {
return i;
}
}
},
numCompletedStages: function(flow) {
if (this.completedStages === undefined) return 0;
var nCompleted = 0;
for (var i = 0; i < flow.stages.length; ++i) {
if (this.completedStages.indexOf(flow.stages[i]) > -1) {
++nCompleted;
}
}
return nCompleted;
},
onInitialStageSubmit: function(ev) {
ev.preventDefault();
var formVals = this.getRegFormVals();
this.savedParams = formVals;
var badFields = {};
if (formVals.password != formVals.confirmPassword) {
badFields.confirmPassword = this.FieldErrors.PasswordMismatch;
}
if (formVals.password == '') {
badFields.password = this.FieldErrors.Missing;
} else if (formVals.password.length < 6) {
badFields.password = this.FieldErrors.Length;
}
if (formVals.username == '') {
badFields.username = this.FieldErrors.Missing;
}
if (Object.keys(badFields).length > 0) {
this.onBadFields(badFields);
return;
}
MatrixClientPeg.replaceUsingUrls(
this.getHsUrl(),
this.getIsUrl()
);
this.setState({
hs_url: this.getHsUrl(),
is_url: this.getIsUrl()
});
var cli = MatrixClientPeg.get();
this.setState({busy: true});
var self = this;
this.tryRegister();
},
startStage: function(stageName) {
var self = this;
this.setStep('stage_'+stageName);
switch(stageName) {
case 'm.login.email.identity':
self.setState({
busy: true
});
var cli = MatrixClientPeg.get();
this.savedParams.client_secret = cli.generateClientSecret();
this.savedParams.send_attempt = 1;
var nextLink = this.props.registrationUrl +
'?client_secret=' +
encodeURIComponent(this.savedParams.client_secret) +
"&hs_url=" +
encodeURIComponent(this.state.hs_url) +
"&is_url=" +
encodeURIComponent(this.state.is_url) +
"&session_id=" +
encodeURIComponent(this.authSessionId);
cli.requestEmailToken(
this.savedParams.email,
this.savedParams.client_secret,
this.savedParams.send_attempt,
nextLink
).done(function(response) {
self.setState({
busy: false,
});
self.setStep('stage_m.login.email.identity');
}, function(error) {
self.setStep('initial');
var newState = {busy: false};
if (error.errcode == 'THREEPID_IN_USE') {
self.onBadFields({email: self.FieldErrors.InUse});
} else {
newState.errorText = 'Unable to contact the given Home Server';
}
self.setState(newState);
});
break;
case 'm.login.recaptcha':
if (!this.authParams || !this.authParams['m.login.recaptcha'].public_key) {
this.setState({
errorText: "This server has not supplied enough information for Recaptcha authentication"
});
}
break;
}
},
onRegistered: function(user_id, access_token) {
MatrixClientPeg.replaceUsingAccessToken(
this.state.hs_url, this.state.is_url, user_id, access_token
);
if (this.props.onLoggedIn) {
this.props.onLoggedIn();
}
},
onCaptchaLoaded: function() {
if (this.refs.recaptchaContainer) {
var sitekey = this.authParams['m.login.recaptcha'].public_key;
global.grecaptcha.render('mx_recaptcha', {
'sitekey': sitekey,
'callback': this.onCaptchaDone
});
}
},
onCaptchaDone: function(captcha_response) {
this.tryRegister({
type: 'm.login.recaptcha',
response: captcha_response
});
},
tryRegister: function(auth) {
var self = this;
MatrixClientPeg.get().register(
this.savedParams.username,
this.savedParams.password,
this.authSessionId,
auth
).done(function(result) {
self.onRegistered(result.user_id, result.access_token);
}, function(error) {
if (error.httpStatus == 401 && error.data.flows) {
self.authParams = error.data.params;
self.authSessionId = error.data.session;
self.completedStages = error.data.completed || [];
var flow = self.chooseFlow(error.data.flows);
var flowStage = self.firstUncompletedStageIndex(flow);
var numDone = self.numCompletedStages(flow);
self.setState({
busy: false,
flows: flow,
currentStep: 1+numDone,
totalSteps: flow.stages.length+1,
flowStage: flowStage
});
self.startStage(flow.stages[flowStage]);
} else {
self.setStep("initial");
var newState = {
busy: false,
errorText: "Unable to contact the given Home Server"
};
if (error.name == 'M_USER_IN_USE') {
delete newState.errorText;
self.onBadFields({
username: self.FieldErrors.InUse
});
} else if (error.httpStatus == 401) {
newState.errorText = "Authorisation failed!";
} else if (error.httpStatus >= 400 && error.httpStatus < 500) {
newState.errorText = "Registration failed!";
} else if (error.httpStatus >= 500 && error.httpStatus < 600) {
newState.errorText = "Server error during registration!";
} else if (error.name == "M_MISSING_PARAM") {
// The HS hasn't remembered the login params from
// the first try when the login email was sent.
newState.errorText = "This home server does not support resuming registration.";
}
self.setState(newState);
}
});
},
showLogin: function(ev) {
ev.preventDefault();
dis.dispatch({
action: 'start_login'
});
}
};