Merge branch 'develop'
28
README.md
|
@ -22,18 +22,34 @@ into the `vector` directory and run your own server.
|
||||||
|
|
||||||
Development
|
Development
|
||||||
===========
|
===========
|
||||||
You can work on any of the source files within Vector with the setup above,
|
|
||||||
and your changes will cause an instant rebuild. If you also need to make
|
|
||||||
changes to the react sdk, you can:
|
|
||||||
|
|
||||||
1. Link the react sdk package into the example:
|
For simple tweaks, you can work on any of the source files within Vector with the
|
||||||
|
setup above, and your changes will cause an instant rebuild.
|
||||||
|
|
||||||
|
However, all serious development on Vector happens on the `develop` branch. This typically
|
||||||
|
depends on the `develop` snapshot versions of `matrix-react-sdk` and `matrix-js-sdk`
|
||||||
|
too, which isn't expressed in Vector's `package.json`. To do this, check out
|
||||||
|
the `develop` branches of these libraries and then use `npm link` to tell Vector
|
||||||
|
about them:
|
||||||
|
|
||||||
|
1. `git clone git@github.com:matrix-org/matrix-react-sdk.git`
|
||||||
|
2. `cd matrix-react-sdk`
|
||||||
|
3. `git checkout develop`
|
||||||
|
4. `npm install`
|
||||||
|
5. `npm start` (to start the dev rebuilder)
|
||||||
|
6. `cd ../vector-web`
|
||||||
|
7. Link the react sdk package into the example:
|
||||||
`npm link path/to/your/react/sdk`
|
`npm link path/to/your/react/sdk`
|
||||||
2. Start the development rebuilder in your react SDK directory:
|
|
||||||
`npm start`
|
Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk`
|
||||||
|
directory.
|
||||||
|
|
||||||
If you add or remove any components from the Vector skin, you will need to rebuild
|
If you add or remove any components from the Vector skin, you will need to rebuild
|
||||||
the skin's index by running, `npm run reskindex`.
|
the skin's index by running, `npm run reskindex`.
|
||||||
|
|
||||||
|
You may need to run `npm i source-map-loader` in matrix-js-sdk if you get errors
|
||||||
|
about "Cannot resolve module 'source-map-loader'" due to shortcomings in webpack.
|
||||||
|
|
||||||
Deployment
|
Deployment
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
"filesize": "^3.1.2",
|
"filesize": "^3.1.2",
|
||||||
"flux": "~2.0.3",
|
"flux": "~2.0.3",
|
||||||
"linkifyjs": "^2.0.0-beta.4",
|
"linkifyjs": "^2.0.0-beta.4",
|
||||||
"matrix-js-sdk": "^0.2.1",
|
"matrix-js-sdk": "^0.3.0",
|
||||||
"matrix-react-sdk": "^0.0.1",
|
"matrix-react-sdk": "^0.0.2",
|
||||||
"q": "^1.4.1",
|
"q": "^1.4.1",
|
||||||
"react": "^0.13.3",
|
"react": "^0.13.3",
|
||||||
"react-loader": "^1.4.0"
|
"react-loader": "^1.4.0"
|
||||||
|
|
|
@ -20,14 +20,17 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
avatarUrlForMember: function(member, width, height, resizeMethod) {
|
avatarUrlForMember: function(member, width, height, resizeMethod) {
|
||||||
var url = MatrixClientPeg.get().getAvatarUrlForMember(
|
var url = member.getAvatarUrl(
|
||||||
member,
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
resizeMethod
|
resizeMethod
|
||||||
);
|
);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
url = this.defaultAvatarUrlForString(member.userId);
|
// member can be null here currently since on invites, the JS SDK
|
||||||
|
// does not have enough info to build a RoomMember object for
|
||||||
|
// the inviter.
|
||||||
|
url = this.defaultAvatarUrlForString(member ? member.userId : '');
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,15 +49,25 @@ module.exports = {
|
||||||
|
|
||||||
var position = {
|
var position = {
|
||||||
top: props.top - 20,
|
top: props.top - 20,
|
||||||
right: props.right + 8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var chevron = null;
|
||||||
|
if (props.left) {
|
||||||
|
chevron = <img className="mx_ContextualMenu_chevron_left" src="img/chevron-left.png" width="9" height="16" />
|
||||||
|
position.left = props.left + 8;
|
||||||
|
} else {
|
||||||
|
chevron = <img className="mx_ContextualMenu_chevron_right" src="img/chevron-right.png" width="9" height="16" />
|
||||||
|
position.right = props.right + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = 'mx_ContextualMenu_wrapper';
|
||||||
|
|
||||||
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
|
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
|
||||||
// property set here so you can't close the menu from a button click!
|
// property set here so you can't close the menu from a button click!
|
||||||
var menu = (
|
var menu = (
|
||||||
<div className="mx_ContextualMenu_wrapper">
|
<div className={className}>
|
||||||
<div className="mx_ContextualMenu" style={position}>
|
<div className="mx_ContextualMenu" style={position}>
|
||||||
<img className="mx_ContextualMenu_chevron" src="img/chevron-right.png" width="9" height="16" />
|
{chevron}
|
||||||
<Element {...props} onFinished={closeMenu}/>
|
<Element {...props} onFinished={closeMenu}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
|
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
|
||||||
|
|
|
@ -74,8 +74,10 @@ module.exports = {
|
||||||
);
|
);
|
||||||
if (call) {
|
if (call) {
|
||||||
call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
|
call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
|
||||||
// N.B. the remote video element is used for playback for audio for voice calls
|
|
||||||
call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement());
|
call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement());
|
||||||
|
// give a separate element for audio stream playback - both for voice calls
|
||||||
|
// and for the voice stream of screen captures
|
||||||
|
call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement());
|
||||||
}
|
}
|
||||||
if (call && call.type === "video" && call.state !== 'ended') {
|
if (call && call.type === "video" && call.state !== 'ended') {
|
||||||
// if this call is a conf call, don't display local video as the
|
// if this call is a conf call, don't display local video as the
|
||||||
|
@ -88,6 +90,7 @@ module.exports = {
|
||||||
else {
|
else {
|
||||||
this.getVideoView().getLocalVideoElement().style.display = "none";
|
this.getVideoView().getLocalVideoElement().style.display = "none";
|
||||||
this.getVideoView().getRemoteVideoElement().style.display = "none";
|
this.getVideoView().getRemoteVideoElement().style.display = "none";
|
||||||
|
dis.dispatch({action: 'video_fullscreen', fullscreen: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,6 +33,8 @@ module.exports = {
|
||||||
cli.on("Room", this.onRoom);
|
cli.on("Room", this.onRoom);
|
||||||
cli.on("Room.timeline", this.onRoomTimeline);
|
cli.on("Room.timeline", this.onRoomTimeline);
|
||||||
cli.on("Room.name", this.onRoomName);
|
cli.on("Room.name", this.onRoomName);
|
||||||
|
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||||
|
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||||
|
|
||||||
var rooms = this.getRoomList();
|
var rooms = this.getRoomList();
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -52,6 +54,11 @@ module.exports = {
|
||||||
case 'call_state':
|
case 'call_state':
|
||||||
this._recheckCallElement(this.props.selectedRoom);
|
this._recheckCallElement(this.props.selectedRoom);
|
||||||
break;
|
break;
|
||||||
|
case 'view_tooltip':
|
||||||
|
this.tooltip = payload.tooltip;
|
||||||
|
this._repositionTooltip();
|
||||||
|
if (this.tooltip) this.tooltip.style.display = 'block';
|
||||||
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -61,6 +68,7 @@ module.exports = {
|
||||||
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
||||||
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
||||||
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
||||||
|
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -105,6 +113,15 @@ module.exports = {
|
||||||
this.refreshRoomList();
|
this.refreshRoomList();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRoomStateEvents: function(ev, state) {
|
||||||
|
setTimeout(this.refreshRoomList, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRoomMemberName: function(ev, member) {
|
||||||
|
setTimeout(this.refreshRoomList, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
refreshRoomList: function() {
|
refreshRoomList: function() {
|
||||||
var rooms = this.getRoomList();
|
var rooms = this.getRoomList();
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -150,6 +167,13 @@ module.exports = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_repositionTooltip: function(e) {
|
||||||
|
if (this.tooltip && this.tooltip.parentElement) {
|
||||||
|
var scroll = this.getDOMNode();
|
||||||
|
this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.scrollTop) + "px";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
makeRoomTiles: function() {
|
makeRoomTiles: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var RoomTile = sdk.getComponent("molecules.RoomTile");
|
var RoomTile = sdk.getComponent("molecules.RoomTile");
|
||||||
|
@ -159,6 +183,7 @@ module.exports = {
|
||||||
<RoomTile
|
<RoomTile
|
||||||
room={room}
|
room={room}
|
||||||
key={room.roomId}
|
key={room.roomId}
|
||||||
|
collapsed={self.props.collapsed}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
unread={self.state.activityMap[room.roomId] === 1}
|
unread={self.state.activityMap[room.roomId] === 1}
|
||||||
highlight={self.state.activityMap[room.roomId] === 2}
|
highlight={self.state.activityMap[room.roomId] === 2}
|
||||||
|
|
|
@ -0,0 +1,503 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
|
||||||
|
var React = require("react");
|
||||||
|
var q = require("q");
|
||||||
|
var ContentMessages = require("matrix-react-sdk/lib//ContentMessages");
|
||||||
|
var WhoIsTyping = require("matrix-react-sdk/lib/WhoIsTyping");
|
||||||
|
var Modal = require("matrix-react-sdk/lib/Modal");
|
||||||
|
var sdk = require('matrix-react-sdk/lib/index');
|
||||||
|
var CallHandler = require('matrix-react-sdk/lib/CallHandler');
|
||||||
|
var VectorConferenceHandler = require('../../modules/VectorConferenceHandler');
|
||||||
|
|
||||||
|
var dis = require("matrix-react-sdk/lib/dispatcher");
|
||||||
|
|
||||||
|
var PAGINATE_SIZE = 20;
|
||||||
|
var INITIAL_SIZE = 20;
|
||||||
|
|
||||||
|
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':
|
||||||
|
case 'message_resend_started':
|
||||||
|
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 !== VectorConferenceHandler.getConferenceUserIdForRoom(member.roomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._updateConfCallNotification();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateConfCallNotification: function() {
|
||||||
|
var room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||||
|
if (!room) return;
|
||||||
|
var confMember = room.getMember(
|
||||||
|
VectorConferenceHandler.getConferenceUserIdForRoom(this.props.roomId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confMember) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var confCall = VectorConferenceHandler.getConferenceCallForRoom(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"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
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 DateSeparator = sdk.getComponent('molecules.DateSeparator');
|
||||||
|
|
||||||
|
var ret = [];
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
var EventTile = sdk.getComponent('molecules.EventTile');
|
||||||
|
|
||||||
|
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
|
||||||
|
var mxEv = this.state.room.timeline[i];
|
||||||
|
|
||||||
|
if (!EventTile.supportsEventType(mxEv.getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline
|
||||||
|
var ts1 = this.state.room.timeline[i].getTs();
|
||||||
|
dateSeparator = <li key={ts1}><DateSeparator ts={ts1}/></li>;
|
||||||
|
continuation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.unshift(
|
||||||
|
<li key={mxEv.getId()}><EventTile 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) {
|
||||||
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Failed to set state",
|
||||||
|
description: err.toString()
|
||||||
|
});
|
||||||
|
}).finally(function() {
|
||||||
|
self.setState({
|
||||||
|
uploadingRoomSettings: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
editingRoomSettings: false,
|
||||||
|
uploadingRoomSettings: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
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 extend = require('matrix-react-sdk/lib/extend');
|
||||||
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var BaseRegisterController = require('matrix-react-sdk/lib/controllers/templates/Register.js');
|
||||||
|
|
||||||
|
var RegisterController = {};
|
||||||
|
extend(RegisterController, BaseRegisterController);
|
||||||
|
|
||||||
|
RegisterController.onRegistered = function(user_id, access_token) {
|
||||||
|
MatrixClientPeg.replaceUsingAccessToken(
|
||||||
|
this.state.hs_url, this.state.is_url, user_id, access_token
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
step: 'profile',
|
||||||
|
busy: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
cli.getProfileInfo(cli.credentials.userId).done(function(result) {
|
||||||
|
self.setState({
|
||||||
|
avatarUrl: result.avatar_url,
|
||||||
|
busy: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
console.err(err);
|
||||||
|
self.setState({
|
||||||
|
busy: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterController.onAccountReady = function() {
|
||||||
|
if (this.props.onLoggedIn) {
|
||||||
|
this.props.onLoggedIn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = RegisterController;
|
|
@ -14,6 +14,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* hack to stop overscroll bounce on OSX and iOS.
|
||||||
|
N.B. Breaks things when we have legitimate horizontal overscroll */
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
|
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -34,6 +41,12 @@ h2 {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
a:link,
|
||||||
|
a:visited {
|
||||||
|
color: #80CEF4;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_ContextualMenu_background {
|
.mx_ContextualMenu_background {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -54,18 +67,29 @@ h2 {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ContextualMenu_chevron {
|
.mx_ContextualMenu_chevron_right {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -21px;
|
right: -21px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ContextualMenu_chevron_left {
|
||||||
|
padding: 12px;
|
||||||
|
position: absolute;
|
||||||
|
left: -21px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_ContextualMenu_field {
|
.mx_ContextualMenu_field {
|
||||||
padding: 3px 6px 3px 6px;
|
padding: 3px 6px 3px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ContextualMenu_spinner {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_Dialog_background {
|
.mx_Dialog_background {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
.mx_RoomDropTarget,
|
.mx_RoomDropTarget,
|
||||||
.mx_RoomList_favourites_label,
|
.mx_RoomList_favourites_label,
|
||||||
.mx_RoomList_archive_label,
|
.mx_RoomList_archive_label,
|
||||||
.mx_LeftPanel_hideButton,
|
|
||||||
.mx_RoomHeader_search,
|
.mx_RoomHeader_search,
|
||||||
.mx_RoomSettings_encrypt,
|
.mx_RoomSettings_encrypt,
|
||||||
.mx_CreateRoom_encrypt,
|
.mx_CreateRoom_encrypt,
|
||||||
|
|
|
@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_MessageTile {
|
.mx_EventTile {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
margin-left: 56px;
|
margin-left: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_avatar {
|
.mx_EventTile_avatar {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
margin-left: -64px;
|
margin-left: -64px;
|
||||||
|
@ -29,17 +29,17 @@ limitations under the License.
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_avatar img {
|
.mx_EventTile_avatar img {
|
||||||
background-color: #dbdbdb;
|
background-color: #dbdbdb;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_continuation {
|
.mx_EventTile_continuation {
|
||||||
margin-top: 8px ! important;
|
margin-top: 8px ! important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile .mx_SenderProfile {
|
.mx_EventTile .mx_SenderProfile {
|
||||||
color: #454545;
|
color: #454545;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -47,35 +47,35 @@ limitations under the License.
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile .mx_MessageTimestamp {
|
.mx_EventTile .mx_MessageTimestamp {
|
||||||
color: #454545;
|
color: #454545;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_content {
|
.mx_EventTile_content {
|
||||||
padding-right: 100px;
|
padding-right: 100px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_notice .mx_MessageTile_content {
|
.mx_EventTile_notice .mx_MessageTile_content {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_sending {
|
.mx_EventTile_sending {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_notSent {
|
.mx_EventTile_notSent {
|
||||||
color: #f11;
|
color: #f11;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_highlight {
|
.mx_EventTile_highlight {
|
||||||
color: #00f;
|
color: #FF0064;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_msgOption {
|
.mx_EventTile_msgOption {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +83,30 @@ limitations under the License.
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile_last .mx_MessageTimestamp {
|
.mx_EventTile_last .mx_MessageTimestamp {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTile:hover .mx_MessageTimestamp {
|
.mx_EventTile:hover .mx_MessageTimestamp {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_editButton {
|
||||||
|
float: right;
|
||||||
|
display: none;
|
||||||
|
border: 0px;
|
||||||
|
outline: none;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile:hover .mx_EventTile_editButton {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile.menu .mx_EventTile_editButton {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile.menu .mx_MessageTimestamp {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_MatrixToolbar {
|
.mx_MatrixToolbar {
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #ff0064;
|
background-color: #ff0064;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -128,3 +128,7 @@ limitations under the License.
|
||||||
.mx_MemberTile_zalgo {
|
.mx_MemberTile_zalgo {
|
||||||
font-family: Helvetica, Arial, Sans-Serif;
|
font-family: Helvetica, Arial, Sans-Serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberTile:hover .mx_MessageTimestamp {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
|
@ -116,13 +116,16 @@ limitations under the License.
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_nameInput {
|
.mx_RoomHeader_name input, .mx_RoomHeader_nameInput {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
border: 1px solid #c7c7c7;
|
border: 1px solid #c7c7c7;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_nameInput {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,3 +163,11 @@ limitations under the License.
|
||||||
.mx_RoomHeader_button img {
|
.mx_RoomHeader_button img {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_voipButton {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_voipButtons {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
|
@ -22,13 +22,13 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomTile_avatar {
|
.mx_RoomTile_avatar {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding-right: 12px;
|
padding-right: 10px;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
padding-left: 16px;
|
padding-left: 10px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 40px;
|
width: 36px;
|
||||||
height: 40px;
|
height: 36px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,10 @@ limitations under the License.
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collapsed .mx_RoomTile_name {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.mx_RoomTile_nameBadge {
|
.mx_RoomTile_nameBadge {
|
||||||
display: table;
|
display: table;
|
||||||
|
|
|
@ -14,7 +14,20 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
.mx_RoomTooltip {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
border: 1px solid #a9dbf4;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 1000;
|
||||||
|
margin-top: 6px;
|
||||||
|
left: 64px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
.mx_RoomTooltip_chevron {
|
||||||
};
|
position: absolute;
|
||||||
|
left: -9px;
|
||||||
|
top: 8px;
|
||||||
|
}
|
|
@ -29,7 +29,9 @@ limitations under the License.
|
||||||
.mx_LeftPanel_hideButton {
|
.mx_LeftPanel_hideButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 0px;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel .mx_RoomList {
|
.mx_LeftPanel .mx_RoomList {
|
||||||
|
@ -39,7 +41,7 @@ limitations under the License.
|
||||||
-webkit-order: 1;
|
-webkit-order: 1;
|
||||||
order: 1;
|
order: 1;
|
||||||
|
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
-webkit-flex: 1 1 0;
|
-webkit-flex: 1 1 0;
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
}
|
}
|
||||||
|
@ -61,10 +63,6 @@ limitations under the License.
|
||||||
color: #378bb4;
|
color: #378bb4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile_avatar {
|
|
||||||
padding-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -57,7 +57,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomDirectory_tableWrapper {
|
.mx_RoomDirectory_tableWrapper {
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
-webkit-flex: 1 1 0;
|
-webkit-flex: 1 1 0;
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,5 @@ limitations under the License.
|
||||||
.mx_RoomList h2 {
|
.mx_RoomList h2 {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
|
@ -106,10 +106,8 @@ limitations under the License.
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 18px;
|
|
||||||
margin-bottom: 18px;
|
|
||||||
|
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_messageListWrapper {
|
.mx_RoomView_messageListWrapper {
|
||||||
|
@ -123,6 +121,10 @@ limitations under the License.
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_MessageList li {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomView_MessageList h2 {
|
.mx_RoomView_MessageList h2 {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
|
@ -210,13 +212,37 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomView_uploadProgressOuter {
|
.mx_RoomView_uploadProgressOuter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: black;
|
background-color: rgba(169, 219, 244, 0.5);
|
||||||
height: 5px;
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadProgressInner {
|
.mx_RoomView_uploadProgressInner {
|
||||||
background-color: blue;
|
background-color: #80cef4;
|
||||||
height: 5px;
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_uploadFilename {
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_uploadIcon {
|
||||||
|
float: left;
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_uploadCancel {
|
||||||
|
float: right;
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_uploadBytes {
|
||||||
|
float: right;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_ongoingConfCallNotification {
|
.mx_RoomView_ongoingConfCallNotification {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.mx_ViewSource pre {
|
||||||
|
text-align: left;
|
||||||
|
}
|
|
@ -73,6 +73,11 @@ limitations under the License.
|
||||||
flex: 0 0 230px;
|
flex: 0 0 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MatrixChat .mx_LeftPanel.collapsed {
|
||||||
|
-webkit-flex: 0 0 60px;
|
||||||
|
flex: 0 0 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MatrixChat .mx_MatrixChat_middlePanel {
|
.mx_MatrixChat .mx_MatrixChat_middlePanel {
|
||||||
-webkit-box-ordinal-group: 2;
|
-webkit-box-ordinal-group: 2;
|
||||||
-moz-box-ordinal-group: 2;
|
-moz-box-ordinal-group: 2;
|
||||||
|
@ -83,7 +88,9 @@ limitations under the License.
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
background-color: #f3f8fa;
|
background-color: #f3f8fa;
|
||||||
width: 100%;
|
|
||||||
|
-webkit-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
/* XXX: Hack: apparently if you try to nest a flex-box
|
/* XXX: Hack: apparently if you try to nest a flex-box
|
||||||
* within a non-flex-box within a flex-box, the height
|
* within a non-flex-box within a flex-box, the height
|
||||||
|
@ -111,3 +118,8 @@ limitations under the License.
|
||||||
-webkit-flex: 0 0 230px;
|
-webkit-flex: 0 0 230px;
|
||||||
flex: 0 0 230px;
|
flex: 0 0 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MatrixChat .mx_RightPanel.collapsed {
|
||||||
|
-webkit-flex: 0 0 72px;
|
||||||
|
flex: 0 0 72px;
|
||||||
|
}
|
||||||
|
|
After Width: | Height: | Size: 146 B |
After Width: | Height: | Size: 146 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 999 B After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 146 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 200 B After Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 1.3 KiB |
|
@ -23,6 +23,9 @@ limitations under the License.
|
||||||
|
|
||||||
var skin = {};
|
var skin = {};
|
||||||
|
|
||||||
|
skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
|
||||||
|
skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
|
||||||
|
skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
|
||||||
skin['atoms.EditableText'] = require('./views/atoms/EditableText');
|
skin['atoms.EditableText'] = require('./views/atoms/EditableText');
|
||||||
skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
|
skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
|
||||||
skin['atoms.ImageView'] = require('./views/atoms/ImageView');
|
skin['atoms.ImageView'] = require('./views/atoms/ImageView');
|
||||||
|
@ -30,32 +33,34 @@ skin['atoms.LogoutButton'] = require('./views/atoms/LogoutButton');
|
||||||
skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
|
skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
|
||||||
skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
|
skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
|
||||||
skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
|
skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
|
||||||
skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
|
|
||||||
skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
|
|
||||||
skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
|
|
||||||
skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
|
skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
|
||||||
skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
|
skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
|
||||||
|
skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile');
|
||||||
skin['molecules.ChangeAvatar'] = require('./views/molecules/ChangeAvatar');
|
skin['molecules.ChangeAvatar'] = require('./views/molecules/ChangeAvatar');
|
||||||
|
skin['molecules.ChangeDisplayName'] = require('./views/molecules/ChangeDisplayName');
|
||||||
skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
|
skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
|
||||||
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
|
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
|
||||||
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
|
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
|
||||||
|
skin['molecules.EventTile'] = require('./views/molecules/EventTile');
|
||||||
|
skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
|
||||||
|
skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
|
||||||
|
skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');
|
||||||
skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
|
skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
|
||||||
|
skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
|
||||||
|
skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu');
|
||||||
|
skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
|
||||||
skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
|
skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
|
||||||
skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
|
skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
|
||||||
skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
|
skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
|
||||||
skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
|
skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
|
||||||
skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
|
skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
|
||||||
skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
|
|
||||||
skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
|
|
||||||
skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');
|
|
||||||
skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
|
|
||||||
skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
|
|
||||||
skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
|
skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
|
||||||
skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
|
skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
|
||||||
skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
|
skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
|
||||||
skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader');
|
skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader');
|
||||||
skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
|
skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
|
||||||
skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
|
skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
|
||||||
|
skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip');
|
||||||
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
|
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
|
||||||
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
|
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
|
||||||
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
|
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
|
||||||
|
@ -63,6 +68,7 @@ skin['molecules.UserSelector'] = require('./views/molecules/UserSelector');
|
||||||
skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView');
|
skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView');
|
||||||
skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox');
|
skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox');
|
||||||
skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView');
|
skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView');
|
||||||
|
skin['organisms.CasLogin'] = require('./views/organisms/CasLogin');
|
||||||
skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom');
|
skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom');
|
||||||
skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog');
|
skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog');
|
||||||
skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel');
|
skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel');
|
||||||
|
@ -75,6 +81,7 @@ skin['organisms.RoomDirectory'] = require('./views/organisms/RoomDirectory');
|
||||||
skin['organisms.RoomList'] = require('./views/organisms/RoomList');
|
skin['organisms.RoomList'] = require('./views/organisms/RoomList');
|
||||||
skin['organisms.RoomView'] = require('./views/organisms/RoomView');
|
skin['organisms.RoomView'] = require('./views/organisms/RoomView');
|
||||||
skin['organisms.UserSettings'] = require('./views/organisms/UserSettings');
|
skin['organisms.UserSettings'] = require('./views/organisms/UserSettings');
|
||||||
|
skin['organisms.ViewSource'] = require('./views/organisms/ViewSource');
|
||||||
skin['pages.MatrixChat'] = require('./views/pages/MatrixChat');
|
skin['pages.MatrixChat'] = require('./views/pages/MatrixChat');
|
||||||
skin['templates.Login'] = require('./views/templates/Login');
|
skin['templates.Login'] = require('./views/templates/Login');
|
||||||
skin['templates.Register'] = require('./views/templates/Register');
|
skin['templates.Register'] = require('./views/templates/Register');
|
||||||
|
|
|
@ -18,11 +18,8 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var ImageViewController = require('../../../../controllers/atoms/ImageView')
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ImageView',
|
displayName: 'ImageView',
|
||||||
mixins: [ImageViewController],
|
|
||||||
|
|
||||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely...
|
// XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely...
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
|
|
@ -18,17 +18,39 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var MessageTimestampController = require('matrix-react-sdk/lib/controllers/atoms/MessageTimestamp')
|
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
|
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MessageTimestamp',
|
displayName: 'MessageTimestamp',
|
||||||
mixins: [MessageTimestampController],
|
|
||||||
|
formatDate: function(date) {
|
||||||
|
// date.toLocaleTimeString is completely system dependent.
|
||||||
|
// just go 24h for now
|
||||||
|
function pad(n) {
|
||||||
|
return (n < 10 ? '0' : '') + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = new Date();
|
||||||
|
if (date.toDateString() === now.toDateString()) {
|
||||||
|
return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
|
||||||
|
return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
else if (now.getFullYear() === date.getFullYear()) {
|
||||||
|
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var date = new Date(this.props.ts);
|
var date = new Date(this.props.ts);
|
||||||
return (
|
return (
|
||||||
<span className="mx_MessageTimestamp">
|
<span className="mx_MessageTimestamp">
|
||||||
{date.toLocaleTimeString()}
|
{ this.formatDate(date) }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,10 +24,31 @@ module.exports = React.createClass({
|
||||||
displayName: 'RoomAvatar',
|
displayName: 'RoomAvatar',
|
||||||
mixins: [RoomAvatarController],
|
mixins: [RoomAvatarController],
|
||||||
|
|
||||||
|
getUrlList: function() {
|
||||||
|
return [
|
||||||
|
this.roomAvatarUrl(),
|
||||||
|
this.getOneToOneAvatar(),
|
||||||
|
this.getFallbackAvatar()
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
getFallbackAvatar: function() {
|
||||||
|
var images = [ '80cef4', '50e2c2', 'f4c371' ];
|
||||||
|
var total = 0;
|
||||||
|
for (var i = 0; i < this.props.room.roomId.length; ++i) {
|
||||||
|
total += this.props.room.roomId.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return 'img/' + images[total % images.length] + '.png';
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var style = {
|
||||||
|
maxWidth: this.props.width,
|
||||||
|
maxHeight: this.props.height,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError}
|
<img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError}
|
||||||
width={this.props.width} height={this.props.height}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
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 = React.createClass({
|
||||||
|
displayName: 'Spinner',
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var w = this.props.w || 32;
|
||||||
|
var h = this.props.h || 32;
|
||||||
|
var imgClass = this.props.imgClassName || "";
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,11 +18,8 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var VideoFeedController = require('matrix-react-sdk/lib/controllers/atoms/voip/VideoFeed')
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'VideoFeed',
|
displayName: 'VideoFeed',
|
||||||
mixins: [VideoFeedController],
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var sdk = require('matrix-react-sdk')
|
||||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
@ -35,28 +35,24 @@ module.exports = React.createClass({
|
||||||
dis.dispatch({action: 'view_create_room'});
|
dis.dispatch({action: 'view_create_room'});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getLabel: function(name) {
|
||||||
|
if (!this.props.collapsed) {
|
||||||
|
return <div className="mx_RoomTile_name">{name}</div>
|
||||||
|
}
|
||||||
|
else if (this.state.hover) {
|
||||||
|
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
||||||
|
return <RoomTooltip name={name}/>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var BottomLeftMenuTile = sdk.getComponent('molecules.BottomLeftMenuTile');
|
||||||
return (
|
return (
|
||||||
<div className="mx_BottomLeftMenu">
|
<div className="mx_BottomLeftMenu">
|
||||||
<div className="mx_BottomLeftMenu_options">
|
<div className="mx_BottomLeftMenu_options">
|
||||||
<div className="mx_RoomTile" onClick={this.onCreateRoomClick}>
|
<BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/create-big.png" label="Create new room" onClick={ this.onCreateRoomClick }/>
|
||||||
<div className="mx_RoomTile_avatar">
|
<BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/directory-big.png" label="Directory" onClick={ this.onRoomDirectoryClick }/>
|
||||||
<img src="img/create-big.png" alt="Create new room" title="Create new room" width="42" height="42"/>
|
<BottomLeftMenuTile collapsed={ this.props.collapsed } img="img/settings-big.png" label="Settings" onClick={ this.onSettingsClick }/>
|
||||||
</div>
|
|
||||||
<div className="mx_RoomTile_name">Create new room</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomTile" onClick={this.onRoomDirectoryClick}>
|
|
||||||
<div className="mx_RoomTile_avatar">
|
|
||||||
<img src="img/directory-big.png" alt="Directory" title="Directory" width="42" height="42"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomTile_name">Directory</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomTile" onClick={this.onSettingsClick}>
|
|
||||||
<div className="mx_RoomTile_avatar">
|
|
||||||
<img src="img/settings-big.png" alt="Settings" title="Settings" width="42" height="42"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomTile_name">Settings</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
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 sdk = require('matrix-react-sdk')
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'BottomLeftMenuTile',
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return( { hover : false });
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseEnter: function() {
|
||||||
|
this.setState( { hover : true });
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave: function() {
|
||||||
|
this.setState( { hover : false });
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var label;
|
||||||
|
if (!this.props.collapsed) {
|
||||||
|
label = <div className="mx_RoomTile_name">{ this.props.label }</div>;
|
||||||
|
}
|
||||||
|
else if (this.state.hover) {
|
||||||
|
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
||||||
|
label = <RoomTooltip bottom={ true } label={ this.props.label }/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_RoomTile" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.props.onClick}>
|
||||||
|
<div className="mx_RoomTile_avatar">
|
||||||
|
<img src={ this.props.img } width="36" height="36"/>
|
||||||
|
</div>
|
||||||
|
{ label }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
|
var sdk = require('matrix-react-sdk')
|
||||||
var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
|
var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
|
||||||
|
|
||||||
var Loader = require("react-loader");
|
var Loader = require("react-loader");
|
||||||
|
@ -28,6 +29,7 @@ module.exports = React.createClass({
|
||||||
mixins: [ChangeAvatarController],
|
mixins: [ChangeAvatarController],
|
||||||
|
|
||||||
onFileSelected: function(ev) {
|
onFileSelected: function(ev) {
|
||||||
|
this.avatarSet = true;
|
||||||
this.setAvatarFromFile(ev.target.files[0]);
|
this.setAvatarFromFile(ev.target.files[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -38,22 +40,33 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
||||||
|
var avatarImg;
|
||||||
|
// Having just set an avatar we just display that since it will take a little
|
||||||
|
// time to propagate through to the RoomAvatar.
|
||||||
|
if (this.props.room && !this.avatarSet) {
|
||||||
|
avatarImg = <RoomAvatar room={this.props.room} width='320' height='240' resizeMethod='scale' />;
|
||||||
|
} else {
|
||||||
|
var style = {
|
||||||
|
maxWidth: 320,
|
||||||
|
maxHeight: 240,
|
||||||
|
};
|
||||||
|
avatarImg = <img src={this.state.avatarUrl} style={style} />;
|
||||||
|
}
|
||||||
|
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case this.Phases.Display:
|
case this.Phases.Display:
|
||||||
case this.Phases.Error:
|
case this.Phases.Error:
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<img src={this.state.avatarUrl}/>
|
{avatarImg}
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
Upload new:
|
Upload new:
|
||||||
<input type="file" onChange={this.onFileSelected}/>
|
<input type="file" onChange={this.onFileSelected}/>
|
||||||
{this.state.errorText}
|
{this.state.errorText}
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_Dialog_buttons">
|
|
||||||
<button onClick={this.props.onFinished}>Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case this.Phases.Uploading:
|
case this.Phases.Uploading:
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
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 sdk = require('matrix-react-sdk');
|
||||||
|
|
||||||
|
var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName");
|
||||||
|
var Loader = require("react-loader");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'ChangeDisplayName',
|
||||||
|
mixins: [ChangeDisplayNameController],
|
||||||
|
|
||||||
|
edit: function() {
|
||||||
|
this.refs.displayname_edit.edit()
|
||||||
|
},
|
||||||
|
|
||||||
|
onValueChanged: function(new_value, shouldSubmit) {
|
||||||
|
if (shouldSubmit) {
|
||||||
|
this.changeDisplayname(new_value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
if (this.state.busy) {
|
||||||
|
return (
|
||||||
|
<Loader />
|
||||||
|
);
|
||||||
|
} else if (this.state.errorString) {
|
||||||
|
return (
|
||||||
|
<div className="error">{this.state.errorString}</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var EditableText = sdk.getComponent('atoms.EditableText');
|
||||||
|
return (
|
||||||
|
<EditableText ref="displayname_edit" initialValue={this.state.displayName} label="Click to set display name." onValueChanged={this.onValueChanged}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,33 +18,24 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var EventAsTextTileController = require('matrix-react-sdk/lib/controllers/molecules/EventAsTextTile')
|
|
||||||
var sdk = require('matrix-react-sdk')
|
|
||||||
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'EventAsTextTile',
|
displayName: 'EventAsTextTile',
|
||||||
mixins: [EventAsTextTileController],
|
|
||||||
|
statics: {
|
||||||
|
needsSenderProfile: function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
|
|
||||||
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
|
|
||||||
|
|
||||||
var text = TextForEvent.textForEvent(this.props.mxEvent);
|
var text = TextForEvent.textForEvent(this.props.mxEvent);
|
||||||
if (text == null || text.length == 0) return null;
|
if (text == null || text.length == 0) return null;
|
||||||
|
|
||||||
var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
|
|
||||||
var avatar = this.props.mxEvent.sender ? <MemberAvatar member={this.props.mxEvent.sender} /> : null;
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
<div className="mx_EventAsTextTile">
|
||||||
<div className="mx_MessageTile_avatar">
|
|
||||||
{ avatar }
|
|
||||||
</div>
|
|
||||||
{ timestamp }
|
|
||||||
<span className="mx_SenderProfile"></span>
|
|
||||||
<span className="mx_MessageTile_content">
|
|
||||||
{TextForEvent.textForEvent(this.props.mxEvent)}
|
{TextForEvent.textForEvent(this.props.mxEvent)}
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
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 classNames = require("classnames");
|
||||||
|
|
||||||
|
var sdk = require('matrix-react-sdk')
|
||||||
|
|
||||||
|
var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
|
||||||
|
var ContextualMenu = require('../../../../ContextualMenu');
|
||||||
|
|
||||||
|
var eventTileTypes = {
|
||||||
|
'm.room.message': 'molecules.MessageTile',
|
||||||
|
'm.room.member' : 'molecules.EventAsTextTile',
|
||||||
|
'm.call.invite' : 'molecules.EventAsTextTile',
|
||||||
|
'm.call.answer' : 'molecules.EventAsTextTile',
|
||||||
|
'm.call.hangup' : 'molecules.EventAsTextTile',
|
||||||
|
'm.room.topic' : 'molecules.EventAsTextTile',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'EventTile',
|
||||||
|
mixins: [EventTileController],
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
supportsEventType: function(et) {
|
||||||
|
return eventTileTypes[et] !== undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {menu: false};
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditClicked: function(e) {
|
||||||
|
var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu');
|
||||||
|
var buttonRect = e.target.getBoundingClientRect()
|
||||||
|
var x = buttonRect.right;
|
||||||
|
var y = buttonRect.top + (e.target.height / 2);
|
||||||
|
var self = this;
|
||||||
|
ContextualMenu.createMenu(MessageContextMenu, {
|
||||||
|
mxEvent: this.props.mxEvent,
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
onFinished: function() {
|
||||||
|
self.setState({menu: false});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({menu: true});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
|
||||||
|
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
|
||||||
|
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
|
||||||
|
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var msgtype = content.msgtype;
|
||||||
|
|
||||||
|
var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]);
|
||||||
|
// This shouldn't happen: the caller should check we support this type
|
||||||
|
// before trying to instantiate us
|
||||||
|
if (!EventTileType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var classes = classNames({
|
||||||
|
mx_EventTile: true,
|
||||||
|
mx_EventTile_sending: ['sending', 'queued'].indexOf(
|
||||||
|
this.props.mxEvent.status
|
||||||
|
) !== -1,
|
||||||
|
mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent',
|
||||||
|
mx_EventTile_highlight: this.shouldHighlight(),
|
||||||
|
mx_EventTile_continuation: this.props.continuation,
|
||||||
|
mx_EventTile_last: this.props.last,
|
||||||
|
menu: this.state.menu
|
||||||
|
});
|
||||||
|
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||||
|
var editButton = (
|
||||||
|
<input
|
||||||
|
type="image" src="img/edit.png" alt="Edit"
|
||||||
|
className="mx_EventTile_editButton" onClick={this.onEditClicked}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
var aux = null;
|
||||||
|
if (msgtype === 'm.image') aux = "sent an image";
|
||||||
|
else if (msgtype === 'm.video') aux = "sent a video";
|
||||||
|
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||||
|
|
||||||
|
var avatar, sender;
|
||||||
|
if (!this.props.continuation) {
|
||||||
|
if (this.props.mxEvent.sender) {
|
||||||
|
avatar = (
|
||||||
|
<div className="mx_EventTile_avatar">
|
||||||
|
<MemberAvatar member={this.props.mxEvent.sender} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (EventTileType.needsSenderProfile()) {
|
||||||
|
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classes}>
|
||||||
|
{ avatar }
|
||||||
|
{ sender }
|
||||||
|
<div>
|
||||||
|
{ timestamp }
|
||||||
|
{ editButton }
|
||||||
|
<EventTileType mxEvent={this.props.mxEvent} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -19,15 +19,12 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var filesize = require('filesize');
|
var filesize = require('filesize');
|
||||||
|
|
||||||
var MImageTileController = require('matrix-react-sdk/lib/controllers/molecules/MImageTile')
|
|
||||||
|
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MImageTile',
|
displayName: 'MImageTile',
|
||||||
mixins: [MImageTileController],
|
|
||||||
|
|
||||||
thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||||
if (!fullWidth || !fullHeight) {
|
if (!fullWidth || !fullHeight) {
|
||||||
|
|
|
@ -18,14 +18,11 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var MRoomMemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MRoomMemberTile')
|
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MRoomMemberTile',
|
displayName: 'MRoomMemberTile',
|
||||||
mixins: [MRoomMemberTileController],
|
|
||||||
|
|
||||||
getMemberEventText: function() {
|
getMemberEventText: function() {
|
||||||
return TextForEvent.textForEvent(this.props.mxEvent);
|
return TextForEvent.textForEvent(this.props.mxEvent);
|
||||||
|
|
|
@ -20,11 +20,8 @@ var React = require('react');
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
|
||||||
var MatrixToolbarController = require('matrix-react-sdk/lib/controllers/molecules/MatrixToolbar')
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MatrixToolbar',
|
displayName: 'MatrixToolbar',
|
||||||
mixins: [MatrixToolbarController],
|
|
||||||
|
|
||||||
hideToolbar: function() {
|
hideToolbar: function() {
|
||||||
var Notifier = sdk.getComponent('organisms.Notifier');
|
var Notifier = sdk.getComponent('organisms.Notifier');
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var Loader = require("../atoms/Spinner");
|
||||||
|
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo')
|
var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo')
|
||||||
|
@ -26,7 +27,7 @@ module.exports = React.createClass({
|
||||||
mixins: [MemberInfoController],
|
mixins: [MemberInfoController],
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var interactButton, kickButton, banButton, muteButton, giveModButton;
|
var interactButton, kickButton, banButton, muteButton, giveModButton, spinner;
|
||||||
if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
|
if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
|
||||||
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onLeaveClick}>Leave room</div>;
|
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onLeaveClick}>Leave room</div>;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +35,10 @@ module.exports = React.createClass({
|
||||||
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onChatClick}>Start chat</div>;
|
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onChatClick}>Start chat</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.creatingRoom) {
|
||||||
|
spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.can.kick) {
|
if (this.state.can.kick) {
|
||||||
kickButton = <div className="mx_ContextualMenu_field" onClick={this.onKick}>
|
kickButton = <div className="mx_ContextualMenu_field" onClick={this.onKick}>
|
||||||
Kick
|
Kick
|
||||||
|
@ -64,6 +69,7 @@ module.exports = React.createClass({
|
||||||
{kickButton}
|
{kickButton}
|
||||||
{banButton}
|
{banButton}
|
||||||
{giveModButton}
|
{giveModButton}
|
||||||
|
{spinner}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,24 @@ module.exports = React.createClass({
|
||||||
displayName: 'MemberTile',
|
displayName: 'MemberTile',
|
||||||
mixins: [MemberTileController],
|
mixins: [MemberTileController],
|
||||||
|
|
||||||
|
shouldComponentUpdate: function(nextProps, nextState) {
|
||||||
|
if (this.state.hover !== nextState.hover) return true;
|
||||||
|
if (
|
||||||
|
this.member_last_modified_time === undefined ||
|
||||||
|
this.member_last_modified_time < nextProps.member.getLastModifiedTime()
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
nextProps.member.user &&
|
||||||
|
(this.user_last_modified_time === undefined ||
|
||||||
|
this.user_last_modified_time < nextProps.member.user.getLastModifiedTime())
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
mouseEnter: function(e) {
|
mouseEnter: function(e) {
|
||||||
this.setState({ 'hover': true });
|
this.setState({ 'hover': true });
|
||||||
},
|
},
|
||||||
|
@ -93,6 +111,11 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
this.member_last_modified_time = this.props.member.getLastModifiedTime();
|
||||||
|
if (this.props.member.user) {
|
||||||
|
this.user_last_modified_time = this.props.member.user.getLastModifiedTime();
|
||||||
|
}
|
||||||
|
|
||||||
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
||||||
|
|
||||||
var power;
|
var power;
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
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('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MessageContextMenu',
|
||||||
|
|
||||||
|
onResendClick: function() {
|
||||||
|
MatrixClientPeg.get().resendEvent(
|
||||||
|
this.props.mxEvent, MatrixClientPeg.get().getRoom(
|
||||||
|
this.props.mxEvent.getRoomId()
|
||||||
|
)
|
||||||
|
).done(function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'message_sent'
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'message_send_failed'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dis.dispatch({action: 'message_resend_started'});
|
||||||
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
|
onViewSourceClick: function() {
|
||||||
|
var ViewSource = sdk.getComponent('organisms.ViewSource');
|
||||||
|
Modal.createDialog(ViewSource, {
|
||||||
|
mxEvent: this.props.mxEvent
|
||||||
|
});
|
||||||
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
|
onRedactClick: function() {
|
||||||
|
MatrixClientPeg.get().redactEvent(
|
||||||
|
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
|
||||||
|
).done(function() {
|
||||||
|
// message should disappear by itself
|
||||||
|
}, function(e) {
|
||||||
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
||||||
|
// display error message stating you couldn't delete this.
|
||||||
|
var code = e.errcode || e.statusCode;
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Error",
|
||||||
|
description: "You cannot delete this message. (" + code + ")"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var resendButton;
|
||||||
|
var viewSourceButton;
|
||||||
|
var redactButton;
|
||||||
|
|
||||||
|
if (this.props.mxEvent.status == 'not_sent') {
|
||||||
|
resendButton = (
|
||||||
|
<div className="mx_ContextualMenu_field" onClick={this.onResendClick}>
|
||||||
|
Resend
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
redactButton = (
|
||||||
|
<div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
|
||||||
|
Delete
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
viewSourceButton = (
|
||||||
|
<div className="mx_ContextualMenu_field" onClick={this.onViewSourceClick}>
|
||||||
|
View Source
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{resendButton}
|
||||||
|
{redactButton}
|
||||||
|
{viewSourceButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,8 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var classNames = require("classnames");
|
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
|
||||||
var MessageTileController = require('matrix-react-sdk/lib/controllers/molecules/MessageTile')
|
var MessageTileController = require('matrix-react-sdk/lib/controllers/molecules/MessageTile')
|
||||||
|
@ -28,11 +26,13 @@ module.exports = React.createClass({
|
||||||
displayName: 'MessageTile',
|
displayName: 'MessageTile',
|
||||||
mixins: [MessageTileController],
|
mixins: [MessageTileController],
|
||||||
|
|
||||||
render: function() {
|
statics: {
|
||||||
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
|
needsSenderProfile: function() {
|
||||||
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
|
return true;
|
||||||
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile');
|
var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile');
|
||||||
|
|
||||||
var tileTypes = {
|
var tileTypes = {
|
||||||
|
@ -49,47 +49,7 @@ module.exports = React.createClass({
|
||||||
if (msgtype && tileTypes[msgtype]) {
|
if (msgtype && tileTypes[msgtype]) {
|
||||||
TileType = tileTypes[msgtype];
|
TileType = tileTypes[msgtype];
|
||||||
}
|
}
|
||||||
var classes = classNames({
|
|
||||||
mx_MessageTile: true,
|
|
||||||
mx_MessageTile_sending: ['sending', 'queued'].indexOf(
|
|
||||||
this.props.mxEvent.status
|
|
||||||
) !== -1,
|
|
||||||
mx_MessageTile_notSent: this.props.mxEvent.status == 'not_sent',
|
|
||||||
mx_MessageTile_highlight: this.shouldHighlight(),
|
|
||||||
mx_MessageTile_continuation: this.props.continuation,
|
|
||||||
mx_MessageTile_last: this.props.last,
|
|
||||||
});
|
|
||||||
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
|
||||||
|
|
||||||
var aux = null;
|
return <TileType mxEvent={this.props.mxEvent} />;
|
||||||
if (msgtype === 'm.image') aux = "sent an image";
|
|
||||||
else if (msgtype === 'm.video') aux = "sent a video";
|
|
||||||
else if (msgtype === 'm.file') aux = "uploaded a file";
|
|
||||||
|
|
||||||
var avatar, sender, resend;
|
|
||||||
if (!this.props.continuation) {
|
|
||||||
if (this.props.mxEvent.sender) {
|
|
||||||
avatar = (
|
|
||||||
<div className="mx_MessageTile_avatar">
|
|
||||||
<MemberAvatar member={this.props.mxEvent.sender} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
|
|
||||||
}
|
|
||||||
if (this.props.mxEvent.status === "not_sent" && !this.state.resending) {
|
|
||||||
resend = <button className="mx_MessageTile_msgOption" onClick={this.onResend}>
|
|
||||||
Resend
|
|
||||||
</button>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes}>
|
|
||||||
{ avatar }
|
|
||||||
{ timestamp }
|
|
||||||
{ resend }
|
|
||||||
{ sender }
|
|
||||||
<TileType mxEvent={this.props.mxEvent} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,9 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||||
|
|
||||||
|
var CallHandler = require('matrix-react-sdk/lib/CallHandler');
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
var RoomHeaderController = require('matrix-react-sdk/lib/controllers/molecules/RoomHeader')
|
var RoomHeaderController = require('matrix-react-sdk/lib/controllers/molecules/RoomHeader')
|
||||||
|
|
||||||
|
@ -36,6 +38,10 @@ module.exports = React.createClass({
|
||||||
return this.refs.name_edit.getDOMNode().value;
|
return this.refs.name_edit.getDOMNode().value;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onFullscreenClick: function() {
|
||||||
|
dis.dispatch({action: 'video_fullscreen', fullscreen: true}, true);
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var EditableText = sdk.getComponent("atoms.EditableText");
|
var EditableText = sdk.getComponent("atoms.EditableText");
|
||||||
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
||||||
|
@ -52,18 +58,41 @@ module.exports = React.createClass({
|
||||||
else {
|
else {
|
||||||
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
||||||
|
|
||||||
var callButtons;
|
var call_buttons;
|
||||||
if (this.state) {
|
var zoom_button;
|
||||||
switch (this.state.call_state) {
|
if (this.state && this.state.call_state != 'ended') {
|
||||||
case "ringback":
|
//var muteVideoButton;
|
||||||
case "connected":
|
var activeCall = (
|
||||||
callButtons = (
|
CallHandler.getCallForRoom(this.props.room.roomId)
|
||||||
<div className="mx_RoomHeader_textButton" onClick={this.onHangupClick}>
|
);
|
||||||
|
/*
|
||||||
|
if (activeCall && activeCall.type === "video") {
|
||||||
|
muteVideoButton = (
|
||||||
|
<div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton"
|
||||||
|
onClick={this.onMuteVideoClick}>
|
||||||
|
{
|
||||||
|
(activeCall.isLocalVideoMuted() ?
|
||||||
|
"Unmute" : "Mute") + " video"
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{muteVideoButton}
|
||||||
|
<div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton"
|
||||||
|
onClick={this.onMuteAudioClick}>
|
||||||
|
{
|
||||||
|
(activeCall && activeCall.isMicrophoneMuted() ?
|
||||||
|
"Unmute" : "Mute") + " audio"
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
*/
|
||||||
|
|
||||||
|
call_buttons = (
|
||||||
|
<div className="mx_RoomHeader_textButton"
|
||||||
|
onClick={this.onHangupClick}>
|
||||||
End call
|
End call
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = null;
|
var name = null;
|
||||||
|
@ -97,7 +126,15 @@ module.exports = React.createClass({
|
||||||
var roomAvatar = null;
|
var roomAvatar = null;
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
roomAvatar = (
|
roomAvatar = (
|
||||||
<RoomAvatar room={this.props.room} />
|
<RoomAvatar room={this.props.room} width="48" height="48" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCall && activeCall.type == "video") {
|
||||||
|
zoom_button = (
|
||||||
|
<div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}>
|
||||||
|
<img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '3px' }}/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,18 +149,19 @@ module.exports = React.createClass({
|
||||||
{ topic_el }
|
{ topic_el }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{callButtons}
|
{call_buttons}
|
||||||
{cancel_button}
|
{cancel_button}
|
||||||
{save_button}
|
{save_button}
|
||||||
<div className="mx_RoomHeader_rightRow">
|
<div className="mx_RoomHeader_rightRow">
|
||||||
{ settings_button }
|
{ settings_button }
|
||||||
|
{ zoom_button }
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_search">
|
<div className="mx_RoomHeader_button mx_RoomHeader_search">
|
||||||
<img src="img/search.png" title="Search" alt="Search" width="32" height="32"/>
|
<img src="img/search.png" title="Search" alt="Search" width="32" height="32"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={this.onVideoClick}>
|
<div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}>
|
||||||
<img src="img/video.png" title="Video call" alt="Video call" width="32" height="32"/>
|
<img src="img/video.png" title="Video call" alt="Video call" width="32" height="32"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={this.onVoiceClick}>
|
<div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}>
|
||||||
<img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32"/>
|
<img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var sdk = require('matrix-react-sdk');
|
||||||
|
|
||||||
var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings')
|
var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings')
|
||||||
|
|
||||||
|
@ -65,6 +66,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
|
||||||
|
|
||||||
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
||||||
if (topic) topic = topic.getContent().topic;
|
if (topic) topic = topic.getContent().topic;
|
||||||
|
|
||||||
|
@ -76,6 +79,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
||||||
|
|
||||||
|
var events_levels = power_levels.events || {};
|
||||||
|
|
||||||
if (power_levels) {
|
if (power_levels) {
|
||||||
power_levels = power_levels.getContent();
|
power_levels = power_levels.getContent();
|
||||||
|
|
||||||
|
@ -91,8 +96,7 @@ module.exports = React.createClass({
|
||||||
if (power_levels.kick == undefined) kick_level = 50;
|
if (power_levels.kick == undefined) kick_level = 50;
|
||||||
if (power_levels.redact == undefined) redact_level = 50;
|
if (power_levels.redact == undefined) redact_level = 50;
|
||||||
|
|
||||||
var user_levels = power_levels.users || [];
|
var user_levels = power_levels.users || {};
|
||||||
var events_levels = power_levels.events || [];
|
|
||||||
|
|
||||||
var user_id = MatrixClientPeg.get().credentials.userId;
|
var user_id = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
|
@ -124,7 +128,21 @@ module.exports = React.createClass({
|
||||||
var can_change_levels = false;
|
var can_change_levels = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var banned = this.props.room.getMembersWithMemership("ban");
|
var room_avatar_level = parseInt(power_levels.state_default || 0);
|
||||||
|
if (events_levels['m.room.avatar'] !== undefined) {
|
||||||
|
room_avatar_level = events_levels['m.room.avatar'];
|
||||||
|
}
|
||||||
|
var can_set_room_avatar = current_user_level >= room_avatar_level;
|
||||||
|
|
||||||
|
var change_avatar;
|
||||||
|
if (can_set_room_avatar) {
|
||||||
|
change_avatar = <div>
|
||||||
|
<h3>Room Icon</h3>
|
||||||
|
<ChangeAvatar room={this.props.room} />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
var banned = this.props.room.getMembersWithMembership("ban");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSettings">
|
<div className="mx_RoomSettings">
|
||||||
|
@ -207,6 +225,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{change_avatar}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,19 @@ var sdk = require('matrix-react-sdk')
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomTile',
|
displayName: 'RoomTile',
|
||||||
mixins: [RoomTileController],
|
mixins: [RoomTileController],
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return( { hover : false });
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseEnter: function() {
|
||||||
|
this.setState( { hover : true });
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave: function() {
|
||||||
|
this.setState( { hover : false });
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
var classes = classNames({
|
var classes = classNames({
|
||||||
|
@ -57,14 +70,24 @@ module.exports = React.createClass({
|
||||||
nameCell = <div className="mx_RoomTile_name">{name}</div>;
|
nameCell = <div className="mx_RoomTile_name">{name}</div>;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var label;
|
||||||
|
if (!this.props.collapsed) {
|
||||||
|
label = <div className="mx_RoomTile_name">{name}</div>;
|
||||||
|
}
|
||||||
|
else if (this.state.hover) {
|
||||||
|
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
||||||
|
label = <RoomTooltip room={this.props.room}/>;
|
||||||
|
}
|
||||||
|
|
||||||
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
|
||||||
return (
|
return (
|
||||||
<div className={classes} onClick={this.onClick}>
|
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<div className="mx_RoomTile_avatar">
|
<div className="mx_RoomTile_avatar">
|
||||||
<RoomAvatar room={this.props.room} />
|
<RoomAvatar room={this.props.room} />
|
||||||
{ badge }
|
{ badge }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomTile_name">{name}</div>
|
{ label }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
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 dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'RoomTooltip',
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
if (!this.props.bottom) {
|
||||||
|
// tell the roomlist about us so it can position us
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_tooltip',
|
||||||
|
tooltip: this.getDOMNode(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var tooltip = this.getDOMNode();
|
||||||
|
tooltip.style.top = tooltip.parentElement.getBoundingClientRect().top + "px";
|
||||||
|
tooltip.style.display = "block";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUnmount: function() {
|
||||||
|
if (!this.props.bottom) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_tooltip',
|
||||||
|
tooltip: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var label = this.props.room ? this.props.room.name : this.props.label;
|
||||||
|
return (
|
||||||
|
<div className="mx_RoomTooltip">
|
||||||
|
<img className="mx_RoomTooltip_chevron" src="img/chevron-left.png" width="9" height="16"/>
|
||||||
|
{ label }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -19,15 +19,12 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var classNames = require("classnames");
|
var classNames = require("classnames");
|
||||||
|
|
||||||
var SenderProfileController = require('matrix-react-sdk/lib/controllers/molecules/SenderProfile')
|
|
||||||
|
|
||||||
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
||||||
// Revert to Arial when this happens, which on OSX works at least.
|
// Revert to Arial when this happens, which on OSX works at least.
|
||||||
var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
|
var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'SenderProfile',
|
displayName: 'SenderProfile',
|
||||||
mixins: [SenderProfileController],
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var mxEvent = this.props.mxEvent;
|
var mxEvent = this.props.mxEvent;
|
||||||
|
|
|
@ -18,11 +18,8 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var UnknownMessageTileController = require('matrix-react-sdk/lib/controllers/molecules/UnknownMessageTile')
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'UnknownMessageTile',
|
displayName: 'UnknownMessageTile',
|
||||||
mixins: [UnknownMessageTileController],
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var content = this.props.mxEvent.getContent();
|
var content = this.props.mxEvent.getContent();
|
||||||
|
|
|
@ -31,24 +31,28 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
|
|
||||||
return (
|
// NB: This block MUST have a "key" so React doesn't clobber the elements
|
||||||
<div>
|
// between in-call / not-in-call.
|
||||||
<audio ref="ringAudio" loop>
|
var audioBlock = (
|
||||||
|
<audio ref="ringAudio" key="voip_ring_audio" loop>
|
||||||
<source src="media/ring.ogg" type="audio/ogg" />
|
<source src="media/ring.ogg" type="audio/ogg" />
|
||||||
<source src="media/ring.mp3" type="audio/mpeg" />
|
<source src="media/ring.mp3" type="audio/mpeg" />
|
||||||
</audio>
|
</audio>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{audioBlock}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
|
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
|
||||||
return (
|
return (
|
||||||
<div className="mx_IncomingCallBox">
|
<div className="mx_IncomingCallBox">
|
||||||
|
{audioBlock}
|
||||||
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
||||||
<audio ref="ringAudio" loop>
|
|
||||||
<source src="media/ring.ogg" type="audio/ogg" />
|
|
||||||
<source src="media/ring.mp3" type="audio/mpeg" />
|
|
||||||
</audio>
|
|
||||||
<div className="mx_IncomingCallBox_title">
|
<div className="mx_IncomingCallBox_title">
|
||||||
Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
|
Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,26 +19,69 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
var VideoViewController = require('matrix-react-sdk/lib/controllers/molecules/voip/VideoView')
|
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'VideoView',
|
displayName: 'VideoView',
|
||||||
mixins: [VideoViewController],
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
dis.register(this.onAction);
|
||||||
|
},
|
||||||
|
|
||||||
getRemoteVideoElement: function() {
|
getRemoteVideoElement: function() {
|
||||||
return this.refs.remote.getDOMNode();
|
return this.refs.remote.getDOMNode();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getRemoteAudioElement: function() {
|
||||||
|
return this.refs.remoteAudio.getDOMNode();
|
||||||
|
},
|
||||||
|
|
||||||
getLocalVideoElement: function() {
|
getLocalVideoElement: function() {
|
||||||
return this.refs.local.getDOMNode();
|
return this.refs.local.getDOMNode();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setContainer: function(c) {
|
||||||
|
this.container = c;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAction: function(payload) {
|
||||||
|
switch (payload.action) {
|
||||||
|
case 'video_fullscreen':
|
||||||
|
if (!this.container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var element = this.container.getDOMNode();
|
||||||
|
if (payload.fullscreen) {
|
||||||
|
var requestMethod = (
|
||||||
|
element.requestFullScreen ||
|
||||||
|
element.webkitRequestFullScreen ||
|
||||||
|
element.mozRequestFullScreen ||
|
||||||
|
element.msRequestFullscreen
|
||||||
|
);
|
||||||
|
requestMethod.call(element);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var exitMethod = (
|
||||||
|
document.exitFullscreen ||
|
||||||
|
document.mozCancelFullScreen ||
|
||||||
|
document.webkitExitFullscreen ||
|
||||||
|
document.msExitFullscreen
|
||||||
|
);
|
||||||
|
if (exitMethod) {
|
||||||
|
exitMethod.call(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var VideoFeed = sdk.getComponent('atoms.voip.VideoFeed');
|
var VideoFeed = sdk.getComponent('atoms.voip.VideoFeed');
|
||||||
return (
|
return (
|
||||||
<div className="mx_VideoView">
|
<div className="mx_VideoView" ref={this.setContainer}>
|
||||||
<div className="mx_VideoView_remoteVideoFeed">
|
<div className="mx_VideoView_remoteVideoFeed">
|
||||||
<VideoFeed ref="remote"/>
|
<VideoFeed ref="remote"/>
|
||||||
|
<audio ref="remoteAudio"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_VideoView_localVideoFeed">
|
<div className="mx_VideoView_localVideoFeed">
|
||||||
<VideoFeed ref="local"/>
|
<VideoFeed ref="local"/>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
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 CasLoginController = require('matrix-react-sdk/lib/controllers/organisms/CasLogin');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'CasLogin',
|
||||||
|
mixins: [CasLoginController],
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={this.onCasClicked}>Sign in with CAS</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
|
@ -18,21 +18,37 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'LeftPanel',
|
displayName: 'LeftPanel',
|
||||||
|
|
||||||
|
onHideClick: function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'hide_left_panel',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var RoomList = sdk.getComponent('organisms.RoomList');
|
var RoomList = sdk.getComponent('organisms.RoomList');
|
||||||
var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu');
|
var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu');
|
||||||
var IncomingCallBox = sdk.getComponent('molecules.voip.IncomingCallBox');
|
var IncomingCallBox = sdk.getComponent('molecules.voip.IncomingCallBox');
|
||||||
|
|
||||||
|
var collapseButton;
|
||||||
|
var classes = "mx_LeftPanel";
|
||||||
|
if (this.props.collapsed) {
|
||||||
|
classes += " collapsed";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="mx_LeftPanel">
|
<aside className={classes}>
|
||||||
<img className="mx_LeftPanel_hideButton" src="img/hide.png" width="32" height="32" alt="<"/>
|
{ collapseButton }
|
||||||
<IncomingCallBox />
|
<IncomingCallBox />
|
||||||
<RoomList selectedRoom={this.props.selectedRoom} />
|
<RoomList selectedRoom={this.props.selectedRoom} collapsed={this.props.collapsed}/>
|
||||||
<BottomLeftMenu />
|
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var classNames = require('classnames');
|
var classNames = require('classnames');
|
||||||
|
var Loader = require('react-loader');
|
||||||
|
|
||||||
var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList')
|
var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList')
|
||||||
|
|
||||||
|
@ -32,12 +33,38 @@ module.exports = React.createClass({
|
||||||
return { editing: false };
|
return { editing: false };
|
||||||
},
|
},
|
||||||
|
|
||||||
makeMemberTiles: function() {
|
memberSort: function(userIdA, userIdB) {
|
||||||
|
var userA = this.memberDict[userIdA].user;
|
||||||
|
var userB = this.memberDict[userIdB].user;
|
||||||
|
|
||||||
|
var presenceMap = {
|
||||||
|
online: 3,
|
||||||
|
unavailable: 2,
|
||||||
|
offline: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var presenceOrdA = userA ? presenceMap[userA.presence] : 0;
|
||||||
|
var presenceOrdB = userB ? presenceMap[userB.presence] : 0;
|
||||||
|
|
||||||
|
if (presenceOrdA != presenceOrdB) {
|
||||||
|
return presenceOrdB - presenceOrdA;
|
||||||
|
}
|
||||||
|
|
||||||
|
var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0;
|
||||||
|
var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0;
|
||||||
|
|
||||||
|
return latB - latA;
|
||||||
|
},
|
||||||
|
|
||||||
|
makeMemberTiles: function(membership) {
|
||||||
var MemberTile = sdk.getComponent("molecules.MemberTile");
|
var MemberTile = sdk.getComponent("molecules.MemberTile");
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return Object.keys(self.state.memberDict).map(function(userId) {
|
return self.state.members.filter(function(userId) {
|
||||||
var m = self.state.memberDict[userId];
|
var m = self.memberDict[userId];
|
||||||
|
return m.membership == membership;
|
||||||
|
}).map(function(userId) {
|
||||||
|
var m = self.memberDict[userId];
|
||||||
return (
|
return (
|
||||||
<MemberTile key={userId} member={m} ref={userId} />
|
<MemberTile key={userId} member={m} ref={userId} />
|
||||||
);
|
);
|
||||||
|
@ -69,6 +96,11 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var EditableText = sdk.getComponent("atoms.EditableText");
|
var EditableText = sdk.getComponent("atoms.EditableText");
|
||||||
|
if (this.state.inviting) {
|
||||||
|
return (
|
||||||
|
<Loader />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className={ classes } onClick={ this.onClickInvite } >
|
<div className={ classes } onClick={ this.onClickInvite } >
|
||||||
<div className="mx_MemberTile_avatar"><img src="img/create-big.png" width="40" height="40" alt=""/></div>
|
<div className="mx_MemberTile_avatar"><img src="img/create-big.png" width="40" height="40" alt=""/></div>
|
||||||
|
@ -77,21 +109,37 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var invitedSection = null;
|
||||||
|
var invitedMemberTiles = this.makeMemberTiles('invite');
|
||||||
|
if (invitedMemberTiles.length > 0) {
|
||||||
|
invitedSection = (
|
||||||
|
<div>
|
||||||
|
<h2>Invited</h2>
|
||||||
|
<div className="mx_MemberList_wrapper">
|
||||||
|
{invitedMemberTiles}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberList">
|
<div className="mx_MemberList">
|
||||||
<div className="mx_MemberList_chevron">
|
<div className="mx_MemberList_chevron">
|
||||||
<img src="img/chevron.png" width="24" height="13"/>
|
<img src="img/chevron.png" width="24" height="13"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MemberList_border">
|
<div className="mx_MemberList_border">
|
||||||
|
<div>
|
||||||
<h2>Members</h2>
|
<h2>Members</h2>
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
{this.makeMemberTiles()}
|
{this.makeMemberTiles('join')}
|
||||||
{this.inviteTile()}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{invitedSection}
|
||||||
|
{this.inviteTile()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,15 +58,16 @@ var NotifierView = {
|
||||||
if (ev.getContent().body) msg = ev.getContent().body;
|
if (ev.getContent().body) msg = ev.getContent().body;
|
||||||
}
|
}
|
||||||
|
|
||||||
var avatarUrl = Avatar.avatarUrlForMember(
|
var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
|
||||||
ev.sender, 40, 40, 'crop'
|
ev.sender, 40, 40, 'crop'
|
||||||
);
|
) : null;
|
||||||
|
|
||||||
var notification = new global.Notification(
|
var notification = new global.Notification(
|
||||||
title,
|
title,
|
||||||
{
|
{
|
||||||
"body": msg,
|
"body": msg,
|
||||||
"icon": avatarUrl
|
"icon": avatarUrl,
|
||||||
|
"tag": "vector"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,12 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RightPanel',
|
displayName: 'RightPanel',
|
||||||
|
|
||||||
Phase : {
|
Phase : {
|
||||||
Blank: 'Blank',
|
|
||||||
None: 'None',
|
|
||||||
MemberList: 'MemberList',
|
MemberList: 'MemberList',
|
||||||
FileList: 'FileList',
|
FileList: 'FileList',
|
||||||
},
|
},
|
||||||
|
@ -36,11 +35,16 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onMemberListButtonClick: function() {
|
onMemberListButtonClick: function() {
|
||||||
if (this.state.phase == this.Phase.None) {
|
if (this.props.collapsed) {
|
||||||
this.setState({ phase: this.Phase.MemberList });
|
this.setState({ phase: this.Phase.MemberList });
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'show_right_panel',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.setState({ phase: this.Phase.None });
|
dis.dispatch({
|
||||||
|
action: 'hide_right_panel',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -48,6 +52,7 @@ module.exports = React.createClass({
|
||||||
var MemberList = sdk.getComponent('organisms.MemberList');
|
var MemberList = sdk.getComponent('organisms.MemberList');
|
||||||
var buttonGroup;
|
var buttonGroup;
|
||||||
var panel;
|
var panel;
|
||||||
|
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
buttonGroup =
|
buttonGroup =
|
||||||
<div className="mx_RightPanel_headerButtonGroup">
|
<div className="mx_RightPanel_headerButtonGroup">
|
||||||
|
@ -59,13 +64,18 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
if (this.state.phase == this.Phase.MemberList) {
|
if (!this.props.collapsed && this.state.phase == this.Phase.MemberList) {
|
||||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var classes = "mx_RightPanel";
|
||||||
|
if (this.props.collapsed) {
|
||||||
|
classes += " collapsed";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="mx_RightPanel">
|
<aside className={classes}>
|
||||||
<div className="mx_RightPanel_header">
|
<div className="mx_RightPanel_header">
|
||||||
{ buttonGroup }
|
{ buttonGroup }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
var RoomListController = require('../../../../controllers/organisms/RoomList')
|
var RoomListController = require('../../../../controllers/organisms/RoomList')
|
||||||
|
|
||||||
|
@ -25,6 +26,12 @@ module.exports = React.createClass({
|
||||||
displayName: 'RoomList',
|
displayName: 'RoomList',
|
||||||
mixins: [RoomListController],
|
mixins: [RoomListController],
|
||||||
|
|
||||||
|
onShowClick: function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'show_left_panel',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var CallView = sdk.getComponent('molecules.voip.CallView');
|
var CallView = sdk.getComponent('molecules.voip.CallView');
|
||||||
var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
|
var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
|
||||||
|
@ -34,13 +41,17 @@ module.exports = React.createClass({
|
||||||
callElement = <CallView className="mx_MatrixChat_callView"/>
|
callElement = <CallView className="mx_MatrixChat_callView"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var recentsLabel = this.props.collapsed ?
|
||||||
|
<img style={{cursor: 'pointer'}} onClick={ this.onShowClick } src="img/menu.png" width="27" height="20" alt=">"/> :
|
||||||
|
"Recents";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomList">
|
<div className="mx_RoomList" onScroll={this._repositionTooltip}>
|
||||||
{callElement}
|
{callElement}
|
||||||
<h2 className="mx_RoomList_favourites_label">Favourites</h2>
|
<h2 className="mx_RoomList_favourites_label">Favourites</h2>
|
||||||
<RoomDropTarget text="Drop here to favourite"/>
|
<RoomDropTarget text="Drop here to favourite"/>
|
||||||
|
|
||||||
<h2 className="mx_RoomList_recents_label">Recents</h2>
|
<h2 className="mx_RoomList_recents_label">{ recentsLabel }</h2>
|
||||||
<div className="mx_RoomList_recents">
|
<div className="mx_RoomList_recents">
|
||||||
{this.makeRoomTiles()}
|
{this.makeRoomTiles()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,12 +19,13 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
var classNames = require("classnames");
|
var classNames = require("classnames");
|
||||||
var filesize = require('filesize');
|
var filesize = require('filesize');
|
||||||
|
|
||||||
var RoomViewController = require('matrix-react-sdk/lib/controllers/organisms/RoomView')
|
var RoomViewController = require('../../../../controllers/organisms/RoomView')
|
||||||
|
|
||||||
var Loader = require("react-loader");
|
var Loader = require("react-loader");
|
||||||
|
|
||||||
|
@ -62,6 +63,14 @@ module.exports = React.createClass({
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onConferenceNotificationClick: function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'place_call',
|
||||||
|
type: "video",
|
||||||
|
room_id: this.props.roomId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getUnreadMessagesString: function() {
|
getUnreadMessagesString: function() {
|
||||||
if (!this.state.numUnreadMessages) {
|
if (!this.state.numUnreadMessages) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -129,19 +138,33 @@ module.exports = React.createClass({
|
||||||
<div />
|
<div />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// for testing UI...
|
||||||
|
// this.state.upload = {
|
||||||
|
// uploadedBytes: 123493,
|
||||||
|
// totalBytes: 347534,
|
||||||
|
// fileName: "testing_fooble.jpg",
|
||||||
|
// }
|
||||||
|
|
||||||
if (this.state.upload) {
|
if (this.state.upload) {
|
||||||
var innerProgressStyle = {
|
var innerProgressStyle = {
|
||||||
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
|
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
|
||||||
};
|
};
|
||||||
|
var uploadedSize = filesize(this.state.upload.uploadedBytes);
|
||||||
|
var totalSize = filesize(this.state.upload.totalBytes);
|
||||||
|
if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) {
|
||||||
|
uploadedSize = uploadedSize.replace(/ .*/, '');
|
||||||
|
}
|
||||||
statusBar = (
|
statusBar = (
|
||||||
<div className="mx_RoomView_uploadBar">
|
<div className="mx_RoomView_uploadBar">
|
||||||
<span className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</span>
|
|
||||||
<span className="mx_RoomView_uploadBytes">
|
|
||||||
{filesize(this.state.upload.uploadedBytes)} / {filesize(this.state.upload.totalBytes)}
|
|
||||||
</span>
|
|
||||||
<div className="mx_RoomView_uploadProgressOuter">
|
<div className="mx_RoomView_uploadProgressOuter">
|
||||||
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
||||||
</div>
|
</div>
|
||||||
|
<img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="40" height="40"/>
|
||||||
|
<img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="40" height="40"/>
|
||||||
|
<div className="mx_RoomView_uploadBytes">
|
||||||
|
{ uploadedSize } / { totalSize }
|
||||||
|
</div>
|
||||||
|
<div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,7 +30,15 @@ module.exports = React.createClass({
|
||||||
editAvatar: function() {
|
editAvatar: function() {
|
||||||
var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
|
var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
|
||||||
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
|
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
|
||||||
Modal.createDialog(ChangeAvatar, {initialAvatarUrl: url});
|
var avatarDialog = (
|
||||||
|
<div>
|
||||||
|
<ChangeAvatar initialAvatarUrl={url} />
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<button onClick={this.onAvatarDialogCancel}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
this.avatarDialog = Modal.createDialogWithElement(avatarDialog);
|
||||||
},
|
},
|
||||||
|
|
||||||
addEmail: function() {
|
addEmail: function() {
|
||||||
|
@ -55,12 +63,16 @@ module.exports = React.createClass({
|
||||||
this.logoutModal.closeDialog();
|
this.logoutModal.closeDialog();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onAvatarDialogCancel: function() {
|
||||||
|
this.avatarDialog.close();
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case this.Phases.Loading:
|
case this.Phases.Loading:
|
||||||
return <Loader />
|
return <Loader />
|
||||||
case this.Phases.Display:
|
case this.Phases.Display:
|
||||||
var EditableText = sdk.getComponent('atoms.EditableText');
|
var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName');
|
||||||
var EnableNotificationsButton = sdk.getComponent('atoms.EnableNotificationsButton');
|
var EnableNotificationsButton = sdk.getComponent('atoms.EnableNotificationsButton');
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserSettings">
|
<div className="mx_UserSettings">
|
||||||
|
@ -74,13 +86,13 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_DisplayName">
|
<div className="mx_UserSettings_DisplayName">
|
||||||
<EditableText ref="displayname" initialValue={this.state.displayName} label="Click to set display name." onValueChanged={this.changeDisplayname}/>
|
<ChangeDisplayName ref="displayname" />
|
||||||
<div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div>
|
<div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_3pids">
|
<div className="mx_UserSettings_3pids">
|
||||||
{this.state.threepids.map(function(val) {
|
{this.state.threepids.map(function(val) {
|
||||||
return <div>{val.address}</div>;
|
return <div key={val.address}>{val.address}</div>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
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 = React.createClass({
|
||||||
|
displayName: 'ViewSource',
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="mx_ViewSource">
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(this.props.mxEvent.event, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -25,12 +25,66 @@ var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/Matri
|
||||||
var Loader = require("react-loader");
|
var Loader = require("react-loader");
|
||||||
|
|
||||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
var Matrix = require("matrix-js-sdk");
|
||||||
|
var ContextualMenu = require("../../../../ContextualMenu");
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MatrixChat',
|
displayName: 'MatrixChat',
|
||||||
mixins: [MatrixChatController],
|
mixins: [MatrixChatController],
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
width: 10000,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
window.addEventListener('resize', this.handleResize);
|
||||||
|
this.handleResize();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
window.removeEventListener('resize', this.handleResize);
|
||||||
|
},
|
||||||
|
|
||||||
|
onAliasClick: function(event, alias) {
|
||||||
|
event.preventDefault();
|
||||||
|
dis.dispatch({action: 'view_room_alias', room_alias: alias});
|
||||||
|
},
|
||||||
|
|
||||||
|
onUserClick: function(event, userId) {
|
||||||
|
event.preventDefault();
|
||||||
|
var MemberInfo = sdk.getComponent('molecules.MemberInfo');
|
||||||
|
var member = new Matrix.RoomMember(null, userId);
|
||||||
|
ContextualMenu.createMenu(MemberInfo, {
|
||||||
|
member: member,
|
||||||
|
right: window.innerWidth - event.pageX,
|
||||||
|
top: event.pageY
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleResize: function(e) {
|
||||||
|
var hideLhsThreshold = 1000;
|
||||||
|
var showLhsThreshold = 1000;
|
||||||
|
var hideRhsThreshold = 820;
|
||||||
|
var showRhsThreshold = 820;
|
||||||
|
|
||||||
|
if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
|
||||||
|
dis.dispatch({ action: 'hide_left_panel' });
|
||||||
|
}
|
||||||
|
if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) {
|
||||||
|
dis.dispatch({ action: 'show_left_panel' });
|
||||||
|
}
|
||||||
|
if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) {
|
||||||
|
dis.dispatch({ action: 'hide_right_panel' });
|
||||||
|
}
|
||||||
|
if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) {
|
||||||
|
dis.dispatch({ action: 'show_right_panel' });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({width: window.innerWidth});
|
||||||
|
},
|
||||||
|
|
||||||
onRoomCreated: function(room_id) {
|
onRoomCreated: function(room_id) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_room",
|
action: "view_room",
|
||||||
|
@ -57,19 +111,19 @@ module.exports = React.createClass({
|
||||||
switch (this.state.page_type) {
|
switch (this.state.page_type) {
|
||||||
case this.PageTypes.RoomView:
|
case this.PageTypes.RoomView:
|
||||||
page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
|
page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
|
||||||
right_panel = <RightPanel roomId={this.state.currentRoom} />
|
right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} />
|
||||||
break;
|
break;
|
||||||
case this.PageTypes.UserSettings:
|
case this.PageTypes.UserSettings:
|
||||||
page_element = <UserSettings />
|
page_element = <UserSettings />
|
||||||
right_panel = <RightPanel/>
|
right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
|
||||||
break;
|
break;
|
||||||
case this.PageTypes.CreateRoom:
|
case this.PageTypes.CreateRoom:
|
||||||
page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/>
|
page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/>
|
||||||
right_panel = <RightPanel/>
|
right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
|
||||||
break;
|
break;
|
||||||
case this.PageTypes.RoomDirectory:
|
case this.PageTypes.RoomDirectory:
|
||||||
page_element = <RoomDirectory />
|
page_element = <RoomDirectory />
|
||||||
right_panel = <RightPanel/>
|
right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +133,7 @@ module.exports = React.createClass({
|
||||||
<div className="mx_MatrixChat_wrapper">
|
<div className="mx_MatrixChat_wrapper">
|
||||||
<MatrixToolbar />
|
<MatrixToolbar />
|
||||||
<div className="mx_MatrixChat mx_MatrixChat_toolbarShowing">
|
<div className="mx_MatrixChat mx_MatrixChat_toolbarShowing">
|
||||||
<LeftPanel selectedRoom={this.state.currentRoom} />
|
<LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} />
|
||||||
<main className="mx_MatrixChat_middlePanel">
|
<main className="mx_MatrixChat_middlePanel">
|
||||||
{page_element}
|
{page_element}
|
||||||
</main>
|
</main>
|
||||||
|
@ -91,7 +145,7 @@ module.exports = React.createClass({
|
||||||
else {
|
else {
|
||||||
return (
|
return (
|
||||||
<div className="mx_MatrixChat">
|
<div className="mx_MatrixChat">
|
||||||
<LeftPanel selectedRoom={this.state.currentRoom} />
|
<LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} />
|
||||||
<main className="mx_MatrixChat_middlePanel">
|
<main className="mx_MatrixChat_middlePanel">
|
||||||
{page_element}
|
{page_element}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -141,6 +141,11 @@ module.exports = React.createClass({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
case 'stage_m.login.cas':
|
||||||
|
var CasLogin = sdk.getComponent('organisms.CasLogin');
|
||||||
|
return (
|
||||||
|
<CasLogin />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,15 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
|
||||||
|
|
||||||
var Loader = require("react-loader");
|
var Loader = require("react-loader");
|
||||||
|
|
||||||
var RegisterController = require('matrix-react-sdk/lib/controllers/templates/Register')
|
var RegisterController = require('../../../../controllers/templates/Register')
|
||||||
|
|
||||||
|
var config = require('../../../../../config.json');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
DEFAULT_HS_URL: 'https://matrix.org',
|
|
||||||
DEFAULT_IS_URL: 'https://vector.im',
|
|
||||||
|
|
||||||
displayName: 'Register',
|
displayName: 'Register',
|
||||||
mixins: [RegisterController],
|
mixins: [RegisterController],
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this.customHsUrl = this.DEFAULT_HS_URL;
|
this.customHsUrl = config.default_hs_url;
|
||||||
this.customIsUrl = this.DEFAULT_IS_URL;
|
this.customIsUrl = config.default_is_url;
|
||||||
},
|
},
|
||||||
|
|
||||||
getRegFormVals: function() {
|
getRegFormVals: function() {
|
||||||
|
@ -55,7 +55,7 @@ module.exports = React.createClass({
|
||||||
if (this.state.serverConfigVisible) {
|
if (this.state.serverConfigVisible) {
|
||||||
return this.customHsUrl;
|
return this.customHsUrl;
|
||||||
} else {
|
} else {
|
||||||
return this.DEFAULT_HS_URL;
|
return config.default_hs_url;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ module.exports = React.createClass({
|
||||||
if (this.state.serverConfigVisible) {
|
if (this.state.serverConfigVisible) {
|
||||||
return this.customIsUrl;
|
return this.customIsUrl;
|
||||||
} else {
|
} else {
|
||||||
return this.DEFAULT_IS_URL;
|
return config.default_is_url;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -79,6 +79,10 @@ module.exports = React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onProfileContinueClicked: function() {
|
||||||
|
this.onAccountReady();
|
||||||
|
},
|
||||||
|
|
||||||
componentForStep: function(step) {
|
componentForStep: function(step) {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 'initial':
|
case 'initial':
|
||||||
|
@ -127,6 +131,18 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<Loader />
|
<Loader />
|
||||||
);
|
);
|
||||||
|
} else if (this.state.step == 'profile') {
|
||||||
|
var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName');
|
||||||
|
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
|
||||||
|
return (
|
||||||
|
<div className="mx_Login_profile">
|
||||||
|
Set a display name:
|
||||||
|
<ChangeDisplayName />
|
||||||
|
Upload an avatar:
|
||||||
|
<ChangeAvatar initialAvatarUrl={MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl)} />
|
||||||
|
<button onClick={this.onProfileContinueClicked}>Continue</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -156,6 +172,12 @@ module.exports = React.createClass({
|
||||||
case this.FieldErrors.InUse:
|
case this.FieldErrors.InUse:
|
||||||
strings.push(keys[i]+" is already taken");
|
strings.push(keys[i]+" is already taken");
|
||||||
break;
|
break;
|
||||||
|
case this.FieldErrors.Length:
|
||||||
|
strings.push(keys[i] + " is not long enough.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("Unhandled FieldError: %s", bad[keys[i]]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var errtxt = strings.join(', ');
|
var errtxt = strings.join(', ');
|
||||||
|
|
|
@ -21,24 +21,29 @@ var sdk = require("matrix-react-sdk");
|
||||||
sdk.loadSkin(require('../skins/vector/skindex'));
|
sdk.loadSkin(require('../skins/vector/skindex'));
|
||||||
sdk.loadModule(require('../modules/VectorConferenceHandler'));
|
sdk.loadModule(require('../modules/VectorConferenceHandler'));
|
||||||
|
|
||||||
|
var qs = require("querystring");
|
||||||
|
|
||||||
var lastLocationHashSet = null;
|
var lastLocationHashSet = null;
|
||||||
|
|
||||||
|
|
||||||
|
// We want to support some name / value pairs in the fragment
|
||||||
|
// so we're re-using query string like format
|
||||||
|
function parseQsFromFragment(location) {
|
||||||
|
var hashparts = location.hash.split('?');
|
||||||
|
if (hashparts.length > 1) {
|
||||||
|
return qs.parse(hashparts[1]);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Here, we do some crude URL analysis to allow
|
// Here, we do some crude URL analysis to allow
|
||||||
// deep-linking. We only support registration
|
// deep-linking. We only support registration
|
||||||
// deep-links in this example.
|
// deep-links in this example.
|
||||||
function routeUrl(location) {
|
function routeUrl(location) {
|
||||||
if (location.hash.indexOf('#/register') == 0) {
|
if (location.hash.indexOf('#/register') == 0) {
|
||||||
var hashparts = location.hash.split('?');
|
window.matrixChat.showScreen('register', parseQsFromFragment(location));
|
||||||
var params = {};
|
} else if (location.hash.indexOf('#/login/cas') == 0) {
|
||||||
if (hashparts.length == 2) {
|
window.matrixChat.showScreen('cas_login', parseQsFromFragment(location));
|
||||||
var pairs = hashparts[1].split('&');
|
|
||||||
for (var i = 0; i < pairs.length; ++i) {
|
|
||||||
var parts = pairs[i].split('=');
|
|
||||||
if (parts.length != 2) continue;
|
|
||||||
params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window.matrixChat.showScreen('register', params);
|
|
||||||
} else {
|
} else {
|
||||||
window.matrixChat.showScreen(location.hash.substring(2));
|
window.matrixChat.showScreen(location.hash.substring(2));
|
||||||
}
|
}
|
||||||
|
@ -53,14 +58,18 @@ function onHashChange(ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var loaded = false;
|
var loaded = false;
|
||||||
|
var lastLoadedScreen = null;
|
||||||
|
|
||||||
// This will be called whenever the SDK changes screens,
|
// This will be called whenever the SDK changes screens,
|
||||||
// so a web page can update the URL bar appropriately.
|
// so a web page can update the URL bar appropriately.
|
||||||
var onNewScreen = function(screen) {
|
var onNewScreen = function(screen) {
|
||||||
if (!loaded) return;
|
if (!loaded) {
|
||||||
|
lastLoadedScreen = screen;
|
||||||
|
} else {
|
||||||
var hash = '#/' + screen;
|
var hash = '#/' + screen;
|
||||||
lastLocationHashSet = hash;
|
lastLocationHashSet = hash;
|
||||||
window.location.hash = hash;
|
window.location.hash = hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use this to work out what URL the SDK should
|
// We use this to work out what URL the SDK should
|
||||||
|
@ -85,5 +94,9 @@ window.addEventListener('hashchange', onHashChange);
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
routeUrl(window.location);
|
routeUrl(window.location);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
if (lastLoadedScreen) {
|
||||||
|
onNewScreen(lastLoadedScreen);
|
||||||
|
lastLoadedScreen = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,19 @@ module.exports = {
|
||||||
{ test: /\.js$/, loader: "babel", include: path.resolve('./src') },
|
{ test: /\.js$/, loader: "babel", include: path.resolve('./src') },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
devtoolModuleFilenameTemplate: function(info) {
|
||||||
|
// Reading input source maps gives only relative paths here for
|
||||||
|
// everything. Until I figure out how to fix this, this is a
|
||||||
|
// workaround.
|
||||||
|
// We use the relative resource path with any '../'s on the front
|
||||||
|
// removed which gives a tree with matrix-react-sdk and vector
|
||||||
|
// trees smashed together, but this fixes everything being under
|
||||||
|
// various levels of '.' and '..'
|
||||||
|
// Also, sometimes the resource path is absolute.
|
||||||
|
return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, '');
|
||||||
|
}
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
// alias any requires to the react module to the one in our path, otherwise
|
// alias any requires to the react module to the one in our path, otherwise
|
||||||
|
@ -19,7 +32,12 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.IgnorePlugin(/^olm/)
|
new webpack.IgnorePlugin(/^olm/),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||||
|
}
|
||||||
|
})
|
||||||
],
|
],
|
||||||
devtool: 'source-map'
|
devtool: 'source-map'
|
||||||
};
|
};
|
||||||
|
|