From 684255044aff2bcc2ca1e2b2ab2cf9105b0ee7c2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 10 Jan 2016 12:56:45 +0000 Subject: [PATCH 01/45] switch EditableText to be built on contentEditable rather than switching divs and inputs, so that it can be used for managing multiline content like topics and room names, and use it in RoomHeader/RoomSettings --- src/components/structures/RoomView.js | 4 +- src/components/views/elements/EditableText.js | 133 +++++++++++------- src/components/views/rooms/RoomHeader.js | 95 +++++++++---- src/components/views/rooms/RoomSettings.js | 6 +- 4 files changed, 158 insertions(+), 80 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 08dfe75584..bc1758c26f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -767,7 +767,7 @@ module.exports = React.createClass({ var deferreds = []; - if (old_name != new_name && new_name != undefined && new_name) { + if (old_name != new_name && new_name != undefined) { deferreds.push( MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) ); @@ -900,7 +900,7 @@ module.exports = React.createClass({ }); var new_name = this.refs.header.getRoomName(); - var new_topic = this.refs.room_settings.getTopic(); + var new_topic = this.refs.header.getTopic(); var new_join_rule = this.refs.room_settings.getJoinRules(); var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); var new_power_levels = this.refs.room_settings.getPowerLevels(); diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js index beedfc35c8..5d509a7831 100644 --- a/src/components/views/elements/EditableText.js +++ b/src/components/views/elements/EditableText.js @@ -18,13 +18,21 @@ limitations under the License. var React = require('react'); +const KEY_TAB = 9; +const KEY_SHIFT = 16; +const KEY_WINDOWS = 91; + module.exports = React.createClass({ displayName: 'EditableText', propTypes: { onValueChanged: React.PropTypes.func, initialValue: React.PropTypes.string, label: React.PropTypes.string, - placeHolder: React.PropTypes.string, + placeholder: React.PropTypes.string, + className: React.PropTypes.string, + labelClassName: React.PropTypes.string, + placeholderClassName: React.PropTypes.string, + blurToCancel: React.PropTypes.bool, }, Phases: { @@ -32,42 +40,51 @@ module.exports = React.createClass({ Edit: "edit", }, + value: '', + placeholder: false, + getDefaultProps: function() { return { onValueChanged: function() {}, initialValue: '', - label: 'Click to set', + label: '', placeholder: '', }; }, getInitialState: function() { return { - value: this.props.initialValue, phase: this.Phases.Display, } }, componentWillReceiveProps: function(nextProps) { - this.setState({ - value: nextProps.initialValue - }); + this.value = nextProps.initialValue; + }, + + componentDidMount: function() { + this.value = this.props.initialValue; + if (this.refs.editable_div) { + this.showPlaceholder(!this.value); + } + }, + + showPlaceholder: function(show) { + if (show) { + this.refs.editable_div.textContent = this.props.placeholder; + this.refs.editable_div.setAttribute("class", this.props.className + " " + this.props.placeholderClassName); + this.placeholder = true; + this.value = ''; + } + else { + this.refs.editable_div.textContent = this.value; + this.refs.editable_div.setAttribute("class", this.props.className); + this.placeholder = false; + } }, getValue: function() { - return this.state.value; - }, - - setValue: function(val, shouldSubmit, suppressListener) { - var self = this; - this.setState({ - value: val, - phase: this.Phases.Display, - }, function() { - if (!suppressListener) { - self.onValueChanged(shouldSubmit); - } - }); + return this.value; }, edit: function() { @@ -84,61 +101,83 @@ module.exports = React.createClass({ }, onValueChanged: function(shouldSubmit) { - this.props.onValueChanged(this.state.value, shouldSubmit); + this.props.onValueChanged(this.value, shouldSubmit); + }, + + onKeyDown: function(ev) { + // console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); + + if (this.placeholder) { + if (ev.keyCode !== KEY_SHIFT && !ev.metaKey && !ev.ctrlKey && !ev.altKey && ev.keyCode !== KEY_WINDOWS && ev.keyCode !== KEY_TAB) { + this.showPlaceholder(false); + } + } + + if (ev.key == "Enter") { + ev.stopPropagation(); + ev.preventDefault(); + } + + // console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); }, onKeyUp: function(ev) { + // console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); + + if (!ev.target.textContent) { + this.showPlaceholder(true); + } + else if (!this.placeholder) { + this.value = ev.target.textContent; + } + if (ev.key == "Enter") { this.onFinish(ev); } else if (ev.key == "Escape") { this.cancelEdit(); } + + // console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder); }, - onClickDiv: function() { + onClickDiv: function(ev) { this.setState({ phase: this.Phases.Edit, }) }, onFocus: function(ev) { - ev.target.setSelectionRange(0, ev.target.value.length); + //ev.target.setSelectionRange(0, ev.target.textContent.length); }, onFinish: function(ev) { - if (ev.target.value) { - this.setValue(ev.target.value, ev.key === "Enter"); - } else { - this.cancelEdit(); - } + var self = this; + this.setState({ + phase: this.Phases.Display, + }, function() { + self.onValueChanged(ev.key === "Enter"); + }); }, - onBlur: function() { - this.cancelEdit(); + onBlur: function(ev) { + if (this.props.blurToCancel) + this.cancelEdit(); + else + this.onFinish(ev) }, render: function() { var editable_el; - if (this.state.phase == this.Phases.Display) { - if (this.state.value) { - editable_el =
{this.state.value}
; - } else { - editable_el =
{this.props.label}
; - } - } else if (this.state.phase == this.Phases.Edit) { - editable_el = ( -
- -
- ); + if (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value) { + // show the label + editable_el =
{this.props.label}
; + } else { + // show the content editable div, but manually manage its contents as react and contentEditable don't play nice together + editable_el =
; } - return ( -
- {editable_el} -
- ); + return editable_el; } }); diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index cdfbc0bfc8..072397526b 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -41,6 +41,17 @@ module.exports = React.createClass({ }; }, + componentWillReceiveProps: function(newProps) { + if (newProps.editing) { + var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); + + this.setState({ + name: this.props.room.name, + topic: topic ? topic.getContent().topic : '', + }); + } + }, + onVideoClick: function(e) { dis.dispatch({ action: 'place_call', @@ -57,14 +68,20 @@ module.exports = React.createClass({ }); }, - onNameChange: function(new_name) { - if (this.props.room.name != new_name && new_name) { - MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name); - } + onNameChanged: function(value) { + this.setState({ name : value }); + }, + + onTopicChanged: function(value) { + this.setState({ topic : value }); }, getRoomName: function() { - return this.refs.name_edit.value; + return this.state.name; + }, + + getTopic: function() { + return this.state.topic; }, render: function() { @@ -76,7 +93,7 @@ module.exports = React.createClass({ if (this.props.simpleHeader) { var cancel; if (this.props.onCancelClick) { - cancel = Close + cancel = Close } header =
@@ -87,27 +104,45 @@ module.exports = React.createClass({
} else { - var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - var name = null; var searchStatus = null; var topic_el = null; var cancel_button = null; var save_button = null; var settings_button = null; - var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); - if (actual_name) actual_name = actual_name.getContent().name; + // var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); + // if (actual_name) actual_name = actual_name.getContent().name; if (this.props.editing) { - name = -
- -
+ // name = + //
+ // + //
// if (topic) topic_el =
- cancel_button =
Cancel
- save_button =
Save Changes
+ + name = +
+ +
+ + topic_el = + + + save_button =
Save
+ cancel_button =
Cancel
} else { // - var searchStatus; // don't display the search count until the search completes and // gives us a valid (possibly zero) searchCount. @@ -123,7 +158,9 @@ module.exports = React.createClass({ - if (topic) topic_el =
{ topic.getContent().topic }
; + + var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); + if (topic) topic_el =
{ topic.getContent().topic }
; } var roomAvatar = null; @@ -149,6 +186,18 @@ module.exports = React.createClass({ ; } + var right_row; + if (!this.props.editing) { + right_row = +
+ { forget_button } + { leave_button } +
+ +
+
; + } + header =
@@ -160,15 +209,9 @@ module.exports = React.createClass({ { topic_el }
- {cancel_button} {save_button} -
- { forget_button } - { leave_button } -
- -
-
+ {cancel_button} + {right_row} } diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 0864dc15c7..c601858757 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -136,9 +136,6 @@ module.exports = React.createClass({ render: function() { var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); - var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - if (topic) topic = topic.getContent().topic; - var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', ''); if (join_rule) join_rule = join_rule.getContent().join_rule; @@ -216,7 +213,7 @@ module.exports = React.createClass({ ./ } - var boundClick = self.onColorSchemeChanged.bind(this, i) + var boundClick = self.onColorSchemeChanged.bind(self, i) return (
-
+ var placeholderName = this.state.initialName ? "Unnamed Room" : this.props.room.name; + name =
From 6351258b0e6d5677f2a84460af0d6533bf401bb4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 10 Jan 2016 20:01:30 +0000 Subject: [PATCH 05/45] use room.getImplicitRoomName() from matthew/roomsettings2 branch of matrix-js-sdk for the placeholder roomname --- src/components/views/rooms/RoomHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 77ab01afb1..719695bc36 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -48,7 +48,7 @@ module.exports = React.createClass({ this.setState({ name: name ? name.getContent().name : '', - initialName: name ? name.getContent().name : '', + implicitName: this.props.room.getImplicitRoomName(MatrixClientPeg.get().credentials.userId), topic: topic ? topic.getContent().topic : '', }); } @@ -121,7 +121,7 @@ module.exports = React.createClass({ //
// if (topic) topic_el =
- var placeholderName = this.state.initialName ? "Unnamed Room" : this.props.room.name; + var placeholderName = this.state.implicitName || "Unnamed Room"; name =
From ddd8838b24f734514f825307d2898912284a2384 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 11 Jan 2016 12:46:12 +0000 Subject: [PATCH 06/45] linkify topics --- src/components/views/rooms/RoomHeader.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 719695bc36..bf0cc103f8 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -21,6 +21,12 @@ var sdk = require('../../../index'); var dis = require("../../../dispatcher"); var MatrixClientPeg = require('../../../MatrixClientPeg'); +var linkify = require('linkifyjs'); +var linkifyElement = require('linkifyjs/element'); +var linkifyMatrix = require('../../../linkify-matrix'); + +linkifyMatrix(linkify); + module.exports = React.createClass({ displayName: 'RoomHeader', @@ -54,6 +60,12 @@ module.exports = React.createClass({ } }, + componentDidUpdate: function() { + if (this.refs.topic) { + linkifyElement(this.refs.topic, linkifyMatrix.options); + } + }, + onVideoClick: function(e) { dis.dispatch({ action: 'place_call', @@ -121,7 +133,10 @@ module.exports = React.createClass({ //
// if (topic) topic_el =
- var placeholderName = this.state.implicitName || "Unnamed Room"; + var placeholderName = "Unnamed Room"; + if (this.state.implicitName && this.state.implicitName !== '?') { + placeholderName += " (" + this.state.implicitName + ")"; + } name =
@@ -158,13 +173,13 @@ module.exports = React.createClass({
{ this.props.room.name }
{ searchStatus } -
+
var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - if (topic) topic_el =
{ topic.getContent().topic }
; + if (topic) topic_el =
{ topic.getContent().topic }
; } var roomAvatar = null; @@ -204,7 +219,7 @@ module.exports = React.createClass({ header =
-
+
{ roomAvatar }
From 953e13831799d9827e67dea70e71a40abe35df49 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 11 Jan 2016 17:05:27 +0100 Subject: [PATCH 07/45] Settings page: Made the push notifications settings a separate component. This component is currently implemented at the app level --- src/components/structures/UserSettings.js | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ddf4229170..9196782070 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -145,10 +145,6 @@ module.exports = React.createClass({ this.logoutModal.closeDialog(); }, - onEnableNotificationsChange: function(event) { - UserSettingsStore.setEnableNotifications(event.target.checked); - }, - render: function() { switch (this.state.phase) { case "UserSettings.LOADING": @@ -166,6 +162,7 @@ module.exports = React.createClass({ var ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName"); var ChangePassword = sdk.getComponent("views.settings.ChangePassword"); var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); + var Notifications = sdk.getComponent("settings.Notifications"); var avatarUrl = ( this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null ); @@ -253,22 +250,7 @@ module.exports = React.createClass({

Notifications

-
-
-
- -
-
- -
-
-
+

Advanced

From bd226609d047ab72fb95a9a47ff6afd17f27718c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 11 Jan 2016 18:44:36 +0000 Subject: [PATCH 08/45] fix onclick for all of room name --- src/components/views/rooms/RoomHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index bf0cc103f8..be227bfe9d 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -170,10 +170,10 @@ module.exports = React.createClass({ } name = -
+
{ this.props.room.name }
{ searchStatus } -
+
From c9c496f0e51a07b894f9a3ab06fd65a74d2b61a9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 13 Jan 2016 13:15:13 +0000 Subject: [PATCH 09/45] WIP all new roomsettings --- src/SlashCommands.js | 2 +- src/component-index.js | 1 + src/components/structures/MatrixChat.js | 2 +- src/components/structures/RoomView.js | 6 +- src/components/views/elements/EditableText.js | 8 +- src/components/views/messages/TextualBody.js | 3 + src/components/views/rooms/RoomSettings.js | 206 +++++++++++++----- 7 files changed, 161 insertions(+), 67 deletions(-) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 35216aa403..1dd7ecb08f 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -57,7 +57,7 @@ var commands = { } return success( MatrixClientPeg.get().setRoomAccountData( - room_id, "m.room.color_scheme", colorScheme + room_id, "org.matrix.room.color_scheme", colorScheme ) ); } diff --git a/src/component-index.js b/src/component-index.js index 0c08d70b73..7975b9a360 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -40,6 +40,7 @@ module.exports.components['views.dialogs.ErrorDialog'] = require('./components/v module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt'); module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog'); module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText'); +module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector'); module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar'); module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg'); module.exports.components['views.elements.UserSelector'] = require('./components/views/elements/UserSelector'); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 56885ce499..ef77787035 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -360,7 +360,7 @@ module.exports = React.createClass({ var theAlias = MatrixTools.getCanonicalAliasForRoom(room); if (theAlias) presentedId = theAlias; - var color_scheme_event = room.getAccountData("m.room.color_scheme"); + var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme"); var color_scheme = {}; if (color_scheme_event) { color_scheme = color_scheme_event.getContent(); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 3851682b6f..9249a26351 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -244,7 +244,7 @@ module.exports = React.createClass({ var room = MatrixClientPeg.get().getRoom(this.props.roomId); if (!room) return; - var color_scheme_event = room.getAccountData("m.room.color_scheme"); + var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme"); var color_scheme = {}; if (color_scheme_event) { color_scheme = color_scheme_event.getContent(); @@ -255,7 +255,7 @@ module.exports = React.createClass({ onRoomAccountData: function(room, event) { if (room.roomId == this.props.roomId) { - if (event.getType === "m.room.color_scheme") { + if (event.getType === "org.matrix.room.color_scheme") { var color_scheme = event.getContent(); // XXX: we should validate the event Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); @@ -810,7 +810,7 @@ module.exports = React.createClass({ if (new_color_scheme) { deferreds.push( MatrixClientPeg.get().setRoomAccountData( - this.state.room.roomId, "m.room.color_scheme", new_color_scheme + this.state.room.roomId, "org.matrix.room.color_scheme", new_color_scheme ) ); } diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js index 6c6be899f5..ff41f26f42 100644 --- a/src/components/views/elements/EditableText.js +++ b/src/components/views/elements/EditableText.js @@ -33,6 +33,7 @@ module.exports = React.createClass({ labelClassName: React.PropTypes.string, placeholderClassName: React.PropTypes.string, blurToCancel: React.PropTypes.bool, + editable: React.PropTypes.bool, }, Phases: { @@ -49,6 +50,7 @@ module.exports = React.createClass({ initialValue: '', label: '', placeholder: '', + editable: true, }; }, @@ -141,6 +143,8 @@ module.exports = React.createClass({ }, onClickDiv: function(ev) { + if (!this.props.editable) return; + this.setState({ phase: this.Phases.Edit, }) @@ -178,9 +182,9 @@ module.exports = React.createClass({ render: function() { var editable_el; - if (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value) { + if (!this.props.editable || (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value)) { // show the label - editable_el =
{this.props.label}
; + editable_el =
{ this.props.label || this.props.initialValue }
; } else { // show the content editable div, but manually manage its contents as react and contentEditable don't play nice together editable_el =
+

Directory

+
+ { alias_events.length ? "This room is accessible via:" : "This room has no aliases." } +
+
+ { alias_events.map(function(alias_event, i) { + return alias_event.getContent().aliases.map(function(alias, j) { + var deleteButton; + if (alias_event && alias_event.getStateKey() === domain) { + deleteButton = Delete; + } + return ( +
+ +
+ { deleteButton } +
+
+ ); + }); + })} + +
+ +
+ Add +
+
+
+
The canonical entry is  + +
+
; + var room_colors_section =

Room Colour

@@ -236,19 +316,19 @@ module.exports = React.createClass({
; } - var banned = this.props.room.getMembersWithMembership("ban"); - - var events_levels_section; - if (events_levels.length) { - events_levels_section = + var user_levels_section; + if (user_levels.length) { + user_levels_section =
-

Event levels

-
- {Object.keys(events_levels).map(function(event_type, i) { +
+ Users with specific roles are: +
+
+ {Object.keys(user_levels).map(function(user, i) { return ( -
- - +
+ { user } is a +
); })} @@ -256,6 +336,7 @@ module.exports = React.createClass({
; } + var banned = this.props.room.getMembersWithMembership("ban"); var banned_users_section; if (banned.length) { banned_users_section = @@ -273,69 +354,74 @@ module.exports = React.createClass({
; } + // TODO: support editing custom events_levels + // TODO: support editing custom user_levels + return (


+
{ room_colors_section } -

Power levels

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
+ { aliases_section } -

User levels

-
- {Object.keys(user_levels).map(function(user, i) { +

Permissions

+
+
+ The default level for new room members is + +
+
+ To send messages, you must be a + +
+
+ To invite users into the room, you must be a + +
+
+ To configure the room (set room state), you must be a + +
+
+ To kick users, you must be a + +
+
+ To ban users, you must be a + +
+
+ To redact messages, you must be a + +
+ + {Object.keys(events_levels).map(function(event_type, i) { return ( -
- - +
+ To send events of type { event_type }, you must be a +
); })}
- { events_levels_section } +

Users

+
+
+ You are a +
+ + { user_levels_section } +
+ { banned_users_section } + { change_avatar } +
); } From 1b7d80a8cd2feaf6a3c9423d1c3d39742920597d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 13 Jan 2016 14:04:00 +0000 Subject: [PATCH 10/45] s/getImplicitRoomName/getDefaultRoomName/ # as kegan doesn't like the word 'implicit' --- src/components/views/rooms/RoomHeader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index be227bfe9d..0e69e24ede 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -54,7 +54,7 @@ module.exports = React.createClass({ this.setState({ name: name ? name.getContent().name : '', - implicitName: this.props.room.getImplicitRoomName(MatrixClientPeg.get().credentials.userId), + defaultName: this.props.room.getDefaultRoomName(MatrixClientPeg.get().credentials.userId), topic: topic ? topic.getContent().topic : '', }); } @@ -134,8 +134,8 @@ module.exports = React.createClass({ // if (topic) topic_el =
var placeholderName = "Unnamed Room"; - if (this.state.implicitName && this.state.implicitName !== '?') { - placeholderName += " (" + this.state.implicitName + ")"; + if (this.state.defaultName && this.state.defaultName !== '?') { + placeholderName += " (" + this.state.defaultName + ")"; } name = From 79854138932b511e4f5f59449053669f9f91b59c Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 13 Jan 2016 16:32:41 +0100 Subject: [PATCH 11/45] Created TextInputDialog In css, created a generic mx_Dialog_title class for all dialogs --- src/component-index.js | 9 +- src/components/views/dialogs/ErrorDialog.js | 2 +- .../views/dialogs/QuestionDialog.js | 2 +- .../views/dialogs/TextInputDialog.js | 94 +++++++++++++++++++ .../views/login/CustomServerDialog.js | 2 +- 5 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 src/components/views/dialogs/TextInputDialog.js diff --git a/src/component-index.js b/src/component-index.js index 0c08d70b73..bfe1beca71 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -23,14 +23,14 @@ limitations under the License. module.exports.components = {}; module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom'); +module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); +module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); +module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat'); module.exports.components['structures.RoomView'] = require('./components/structures/RoomView'); module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel'); module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings'); -module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); -module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); -module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar'); module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar'); module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton'); @@ -39,6 +39,7 @@ module.exports.components['views.create_room.RoomAlias'] = require('./components module.exports.components['views.dialogs.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog'); module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt'); module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog'); +module.exports.components['views.dialogs.TextInputDialog'] = require('./components/views/dialogs/TextInputDialog'); module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText'); module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar'); module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg'); @@ -51,10 +52,10 @@ module.exports.components['views.login.LoginHeader'] = require('./components/vie module.exports.components['views.login.PasswordLogin'] = require('./components/views/login/PasswordLogin'); module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm'); module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig'); +module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent'); module.exports.components['views.messages.MFileBody'] = require('./components/views/messages/MFileBody'); module.exports.components['views.messages.MImageBody'] = require('./components/views/messages/MImageBody'); module.exports.components['views.messages.MVideoBody'] = require('./components/views/messages/MVideoBody'); -module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent'); module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody'); module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent'); module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody'); diff --git a/src/components/views/dialogs/ErrorDialog.js b/src/components/views/dialogs/ErrorDialog.js index ed9364df60..d06cf2de84 100644 --- a/src/components/views/dialogs/ErrorDialog.js +++ b/src/components/views/dialogs/ErrorDialog.js @@ -48,7 +48,7 @@ module.exports = React.createClass({ render: function() { return (
-
+
{this.props.title}
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js index f415201e45..4eeecd64b3 100644 --- a/src/components/views/dialogs/QuestionDialog.js +++ b/src/components/views/dialogs/QuestionDialog.js @@ -46,7 +46,7 @@ module.exports = React.createClass({ render: function() { return (
-
+
{this.props.title}
diff --git a/src/components/views/dialogs/TextInputDialog.js b/src/components/views/dialogs/TextInputDialog.js new file mode 100644 index 0000000000..3cda852449 --- /dev/null +++ b/src/components/views/dialogs/TextInputDialog.js @@ -0,0 +1,94 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +var React = require("react"); + +module.exports = React.createClass({ + displayName: 'TextInputDialog', + propTypes: { + title: React.PropTypes.string, + description: React.PropTypes.string, + value: React.PropTypes.string, + button: React.PropTypes.string, + focus: React.PropTypes.bool, + onFinished: React.PropTypes.func.isRequired + }, + + getDefaultProps: function() { + return { + title: "", + value: "", + description: "", + button: "OK", + focus: true + }; + }, + + componentDidMount: function() { + if (this.props.focus) { + // Set the cursor at the end of the text input + this.refs.textinput.value = this.props.value; + } + }, + + onOk: function() { + this.props.onFinished(true, this.refs.textinput.value); + }, + + onCancel: function() { + this.props.onFinished(false); + }, + + onKeyDown: function(e) { + if (e.keyCode === 27) { // escape + e.stopPropagation(); + e.preventDefault(); + this.props.onFinished(false); + } + else if (e.keyCode === 13) { // enter + e.stopPropagation(); + e.preventDefault(); + this.props.onFinished(true, this.refs.textinput.value); + } + }, + + render: function() { + return ( +
+
+ {this.props.title} +
+
+
+ +
+
+ +
+
+
+ + + +
+
+ ); + } +}); diff --git a/src/components/views/login/CustomServerDialog.js b/src/components/views/login/CustomServerDialog.js index 8a67dfd7e6..dc6a49abd6 100644 --- a/src/components/views/login/CustomServerDialog.js +++ b/src/components/views/login/CustomServerDialog.js @@ -22,7 +22,7 @@ module.exports = React.createClass({ render: function() { return (
-
+
Custom Server Options
From 05c789187444d0f8d594df106da46435409d3f08 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 13 Jan 2016 17:54:33 +0000 Subject: [PATCH 12/45] fix NPE --- src/components/views/rooms/RoomSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index f3b3b356df..9324824087 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -86,7 +86,7 @@ module.exports = React.createClass({ }, getTopic: function() { - return this.refs.topic.value; + return this.refs.topic ? this.refs.topic.value : ""; }, getJoinRules: function() { From 123b134d87b54da0cd70bd1fec52d22c577d85a6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 13 Jan 2016 18:15:59 +0000 Subject: [PATCH 13/45] use getDomain() --- src/SlashCommands.js | 6 ++---- src/components/structures/CreateRoom.js | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 3de3943b9e..ba4b08c3d0 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -117,8 +117,7 @@ var commands = { return reject("Usage: /join #alias:domain"); } if (!room_alias.match(/:/)) { - var domain = MatrixClientPeg.get().credentials.userId.replace(/^.*:/, ''); - room_alias += ':' + domain; + room_alias += ':' + MatrixClientPeg.get().getDomain(); } // Try to find a room with this alias @@ -164,8 +163,7 @@ var commands = { return reject("Usage: /part [#alias:domain]"); } if (!room_alias.match(/:/)) { - var domain = MatrixClientPeg.get().credentials.userId.replace(/^.*:/, ''); - room_alias += ':' + domain; + room_alias += ':' + MatrixClientPeg.get().getDomain(); } // Try to find a room with this alias diff --git a/src/components/structures/CreateRoom.js b/src/components/structures/CreateRoom.js index c21bc80c6b..116202d324 100644 --- a/src/components/structures/CreateRoom.js +++ b/src/components/structures/CreateRoom.js @@ -251,7 +251,7 @@ module.exports = React.createClass({ var UserSelector = sdk.getComponent("elements.UserSelector"); var RoomHeader = sdk.getComponent("rooms.RoomHeader"); - var domain = MatrixClientPeg.get().credentials.userId.replace(/^.*:/, ''); + var domain = MatrixClientPeg.get().getDomain(); return (
From d767e724696a66317fcef6273d25cbb42b6f18c5 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 14 Jan 2016 17:02:56 +0000 Subject: [PATCH 14/45] hide hoverover for 3pids --- src/components/views/rooms/MemberTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 4752c4d539..58d9d59276 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -130,7 +130,7 @@ module.exports = React.createClass({ } var nameEl; - if (this.state.hover) { + if (this.state.hover && this.props.member) { var presenceState = (member && member.user) ? member.user.presence : null; var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); nameEl = ( From 2899082cbaa3b34cd26426d522914727e8bac4a3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 14 Jan 2016 17:24:52 +0000 Subject: [PATCH 15/45] deselect editabletext when tabbing away --- src/components/views/elements/EditableText.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js index ff41f26f42..78d9d3d0c2 100644 --- a/src/components/views/elements/EditableText.js +++ b/src/components/views/elements/EditableText.js @@ -173,6 +173,9 @@ module.exports = React.createClass({ }, onBlur: function(ev) { + var sel = window.getSelection(); + sel.removeAllRanges(); + if (this.props.blurToCancel) this.cancelEdit(); else From e4671205d86ffc427c1b1a752322695a2e934830 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 14 Jan 2016 17:24:57 +0000 Subject: [PATCH 16/45] right imagery --- src/components/views/rooms/RoomSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index e74df23540..0bb27658ff 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -248,7 +248,7 @@ module.exports = React.createClass({ return alias_event.getContent().aliases.map(function(alias, j) { var deleteButton; if (alias_event && alias_event.getStateKey() === domain) { - deleteButton = Delete; + deleteButton = Delete; } return (
@@ -277,7 +277,7 @@ module.exports = React.createClass({ blurToCancel={ false } onValueChanged={ self.onAliasAdded } />
- Add + Add
From f868f7813b031969ec42ab832fd86cf697d682de Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 14 Jan 2016 19:06:48 +0100 Subject: [PATCH 17/45] Room settings: Added notifications mute checkbox --- src/components/structures/RoomView.js | 8 ++++++++ src/components/views/rooms/RoomSettings.js | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c622a7d769..25d95fdc41 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -804,6 +804,13 @@ module.exports = React.createClass({ ); } + // setRoomMutePushRule will do nothing if there is no change + deferreds.push( + MatrixClientPeg.get().setRoomMutePushRule( + "global", this.state.room.roomId, newVals.are_notifications_muted + ) + ); + if (newVals.power_levels) { deferreds.push( MatrixClientPeg.get().sendStateEvent( @@ -908,6 +915,7 @@ module.exports = React.createClass({ topic: this.refs.room_settings.getTopic(), join_rule: this.refs.room_settings.getJoinRules(), history_visibility: this.refs.room_settings.getHistoryVisibility(), + are_notifications_muted: this.refs.room_settings.areNotificationsMuted(), power_levels: this.refs.room_settings.getPowerLevels(), guest_join: this.refs.room_settings.canGuestsJoin(), guest_read: this.refs.room_settings.canGuestsRead() diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 9e07385d65..e402ac8488 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -51,6 +51,10 @@ module.exports = React.createClass({ return this.refs.share_history.checked ? "shared" : "invited"; }, + areNotificationsMuted: function() { + return this.refs.are_notifications_muted.checked; + }, + getPowerLevels: function() { if (!this.state.power_levels_changed) return undefined; @@ -96,6 +100,14 @@ module.exports = React.createClass({ guest_access = guest_access.getContent().guest_access; } + var are_notifications_muted; + var roomPushRule = MatrixClientPeg.get().getRoomPushRule("global", this.props.room.roomId); + if (roomPushRule) { + if (0 <= roomPushRule.actions.indexOf("dont_notify")) { + are_notifications_muted = true; + } + } + var events_levels = power_levels.events || {}; if (power_levels) { @@ -176,6 +188,11 @@ module.exports = React.createClass({

+

Notifications

+
+ +
+

Power levels

From 8e9e33fa2a45a646fcd2ef87e7b85f2634ef65c3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 12:34:53 +0000 Subject: [PATCH 18/45] fix NPE and make enter work again --- src/components/views/elements/EditableText.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js index 78d9d3d0c2..7f64496393 100644 --- a/src/components/views/elements/EditableText.js +++ b/src/components/views/elements/EditableText.js @@ -154,21 +154,24 @@ module.exports = React.createClass({ //ev.target.setSelectionRange(0, ev.target.textContent.length); var node = ev.target.childNodes[0]; - var range = document.createRange(); - range.setStart(node, 0); - range.setEnd(node, node.length); - - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); + if (node) { + var range = document.createRange(); + range.setStart(node, 0); + range.setEnd(node, node.length); + + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } }, onFinish: function(ev) { var self = this; + var submit = (ev.key === "Enter"); this.setState({ phase: this.Phases.Display, }, function() { - self.onValueChanged(ev.key === "Enter"); + self.onValueChanged(submit); }); }, From 84b46dae4e7dcb3ca0d76c0e12415bdd2e190f88 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 12:35:18 +0000 Subject: [PATCH 19/45] make ChangeAvatar customisable size... --- src/components/views/settings/ChangeAvatar.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/ChangeAvatar.js b/src/components/views/settings/ChangeAvatar.js index f5ec6a0467..7f19b193f7 100644 --- a/src/components/views/settings/ChangeAvatar.js +++ b/src/components/views/settings/ChangeAvatar.js @@ -25,6 +25,8 @@ module.exports = React.createClass({ room: React.PropTypes.object, // if false, you need to call changeAvatar.onFileSelected yourself. showUploadSection: React.PropTypes.bool, + width: React.PropTypes.number, + height: React.PropTypes.number, className: React.PropTypes.string }, @@ -37,7 +39,9 @@ module.exports = React.createClass({ getDefaultProps: function() { return { showUploadSection: true, - className: "mx_Dialog_content" // FIXME - shouldn't be this by default + className: "mx_Dialog_content", // FIXME - shouldn't be this by default + width: 80, + height: 80, }; }, @@ -111,13 +115,14 @@ module.exports = React.createClass({ // 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 = ; + avatarImg = ; } else { var style = { - maxWidth: 240, - maxHeight: 240, + width: this.props.width, + height: this.props.height, objectFit: 'cover', }; + // FIXME: surely we should be using MemberAvatar or UserAvatar or something here... avatarImg = ; } From 828b1f48377e8ff23c723b1e18829ae09fc339a0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 12:35:30 +0000 Subject: [PATCH 20/45] fix up look and feel of UserSettings a bit more --- src/components/structures/UserSettings.js | 25 ++++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ddf4229170..def215ceb3 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -83,6 +83,12 @@ module.exports = React.createClass({ } }, + onAvatarPickerClick: function(ev) { + if (this.refs.file_label) { + this.refs.file_label.click(); + } + }, + onAvatarSelected: function(ev) { var self = this; var changeAvatar = this.refs.changeAvatar; @@ -175,7 +181,7 @@ module.exports = React.createClass({ if (MatrixClientPeg.get().isGuest()) { accountJsx = (
- Upgrade (It's free!) + Create an account
); } @@ -224,14 +230,14 @@ module.exports = React.createClass({ })}
-
+
-
@@ -241,13 +247,12 @@ module.exports = React.createClass({

Account

- {accountJsx} -
- -
-
+ +
Log out
+ + {accountJsx}

Notifications

From e1e46be220c285f3cb139982fef672d5a3d74b40 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 13:11:14 +0000 Subject: [PATCH 21/45] apply gemini scrollbar --- src/components/structures/UserSettings.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index def215ceb3..07c5a680c6 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -21,6 +21,7 @@ var dis = require("../../dispatcher"); var q = require('q'); var version = require('../../../package.json').version; var UserSettingsStore = require('../../UserSettingsStore'); +var GeminiScrollbar = require('react-gemini-scrollbar'); module.exports = React.createClass({ displayName: 'UserSettings', @@ -202,6 +203,8 @@ module.exports = React.createClass({
+ +

Profile

@@ -286,6 +289,8 @@ module.exports = React.createClass({ Version {this.state.clientVersion}
+ +
); } From f2dc1e835b817f3c09349c669f0be4839ff7b413 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 13:11:37 +0000 Subject: [PATCH 22/45] placeholder for displayname --- src/components/views/settings/ChangeDisplayName.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/ChangeDisplayName.js b/src/components/views/settings/ChangeDisplayName.js index 8b31fbf1e3..ed5eb3fa42 100644 --- a/src/components/views/settings/ChangeDisplayName.js +++ b/src/components/views/settings/ChangeDisplayName.js @@ -99,7 +99,9 @@ module.exports = React.createClass({ var EditableText = sdk.getComponent('elements.EditableText'); return ( ); } From 7cc5925ec4ad4fb0f85000d1d4c0731bbbea4c75 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 13:28:41 +0000 Subject: [PATCH 23/45] increase initialSyncLimit enormously to avoid slow gappy /syncs for now --- src/components/structures/MatrixChat.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e5af2a86b5..df74cf5de1 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -522,7 +522,9 @@ module.exports = React.createClass({ UserActivity.start(); Presence.start(); cli.startClient({ - pendingEventOrdering: "end" + pendingEventOrdering: "end", + // deliberately huge limit for now to avoid hitting gappy /sync's until gappy /sync performance improves + initialSyncLimit: 250, }); }, From 4a2c2d9656bc5c08512e7e452f2cbca86e3738e4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 14:22:17 +0000 Subject: [PATCH 24/45] fix broken merge and revert some of 243b2e45 and document evil magic numbers --- src/components/structures/RoomView.js | 16 +++---- src/components/views/rooms/RoomHeader.js | 52 ++++++++++++++++++++-- src/components/views/rooms/RoomSettings.js | 8 ++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 1f409da81e..4f97e1f82f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1189,7 +1189,11 @@ module.exports = React.createClass({ // a maxHeight on the underlying remote video tag. // header + footer + status + give us at least 100px of scrollback at all times. - var auxPanelMaxHeight = window.innerHeight - (83 + 72 + 36 + 100); + var auxPanelMaxHeight = window.innerHeight - + (83 + // height of RoomHeader + 36 + // height of the status area + 72 + // minimum height of the message compmoser + 100); // amount of desired scrollback // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway // but it's better than the video going missing entirely @@ -1198,16 +1202,6 @@ module.exports = React.createClass({ if (this.refs.callView) { var video = this.refs.callView.getVideoView().getRemoteVideoElement(); - // header + footer + status + give us at least 100px of scrollback at all times. - auxPanelMaxHeight = window.innerHeight - - (83 + 72 + - sdk.getComponent('rooms.MessageComposer').MAX_HEIGHT + - 100); - - // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway - // but it's better than the video going missing entirely - if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; - video.style.maxHeight = auxPanelMaxHeight + "px"; // the above might have made the video panel resize itself, so now diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 0e69e24ede..41b1b364e2 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -90,6 +90,32 @@ module.exports = React.createClass({ this.setState({ topic : value }); }, + onAvatarPickerClick: function(ev) { + if (this.refs.file_label) { + this.refs.file_label.click(); + } + }, + + onAvatarSelected: function(ev) { + var self = this; + var changeAvatar = this.refs.changeAvatar; + if (!changeAvatar) { + console.error("No ChangeAvatar found to upload image to!"); + return; + } + changeAvatar.onFileSelected(ev).done(function() { + // dunno if the avatar changed, re-check it. + self._refreshFromServer(); + }, function(err) { + var errMsg = (typeof err === "string") ? err : (err.error || ""); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Error", + description: "Failed to set avatar. " + errMsg + }); + }); + }, + getRoomName: function() { return this.state.name; }, @@ -100,7 +126,8 @@ module.exports = React.createClass({ render: function() { var EditableText = sdk.getComponent("elements.EditableText"); - var RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); + var RoomAvatar = sdk.getComponent("avatars.RoomAvatar"); + var ChangeAvatar = sdk.getComponent("settings.ChangeAvatar"); var TintableSvg = sdk.getComponent("elements.TintableSvg"); var header; @@ -184,9 +211,26 @@ module.exports = React.createClass({ var roomAvatar = null; if (this.props.room) { - roomAvatar = ( - - ); + if (this.props.editing) { + roomAvatar = ( +
+ +
+ + +
+
+ ); + } + else { + roomAvatar = ( + + ); + } } var leave_button; diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 0bb27658ff..359842ce00 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -366,6 +366,12 @@ module.exports = React.createClass({
; } + var create_event = this.props.room.currentState.getStateEvents('m.room.create', ''); + var unfederatable_section; + if (create_event.getContent()["m.federate"] === false) { + unfederatable_section =
Ths room is not accessible by remote Matrix servers
. + } + // TODO: support editing custom events_levels // TODO: support editing custom user_levels @@ -383,6 +389,8 @@ module.exports = React.createClass({
+ { unfederatable_section } + { room_colors_section } { aliases_section } From b8afccd445b352d26c9e55be9b9f7c0296d8d1b7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 15:23:02 +0000 Subject: [PATCH 25/45] fix droptarget behaviour --- src/components/structures/RoomView.js | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4f97e1f82f..36773672b1 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -142,16 +142,16 @@ module.exports = React.createClass({ // (We could use isMounted, but facebook have deprecated that.) this.unmounted = true; - if (this.refs.messagePanel) { - // disconnect the D&D event listeners from the message panel. This - // is really just for hygiene - the messagePanel is going to be + if (this.refs.roomView) { + // disconnect the D&D event listeners from the room view. This + // is really just for hygiene - we're going to be // deleted anyway, so it doesn't matter if the event listeners // don't get cleaned up. - var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); - messagePanel.removeEventListener('drop', this.onDrop); - messagePanel.removeEventListener('dragover', this.onDragOver); - messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd); - messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd); + var roomView = ReactDOM.findDOMNode(this.refs.roomView); + roomView.removeEventListener('drop', this.onDrop); + roomView.removeEventListener('dragover', this.onDragOver); + roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); + roomView.removeEventListener('dragend', this.onDragLeaveOrEnd); } dis.unregister(this.dispatcherRef); if (MatrixClientPeg.get()) { @@ -414,6 +414,14 @@ module.exports = React.createClass({ window.addEventListener('resize', this.onResize); this.onResize(); + if (this.refs.roomView) { + var roomView = ReactDOM.findDOMNode(this.refs.roomView); + roomView.addEventListener('drop', this.onDrop); + roomView.addEventListener('dragover', this.onDragOver); + roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); + roomView.addEventListener('dragend', this.onDragLeaveOrEnd); + } + this._updateTabCompleteList(this.state.room); }, @@ -432,11 +440,6 @@ module.exports = React.createClass({ var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); this.refs.messagePanel.initialised = true; - messagePanel.addEventListener('drop', this.onDrop); - messagePanel.addEventListener('dragover', this.onDragOver); - messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd); - messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd); - this.scrollToBottom(); this.sendReadReceipt(); @@ -1439,7 +1442,7 @@ module.exports = React.createClass({ fileDropTarget =

- Drop File Here + Drop file here to upload
; } @@ -1540,7 +1543,7 @@ module.exports = React.createClass({ ); return ( -
+
- { fileDropTarget }
+ { fileDropTarget } { conferenceCallNotification } From aefecfc645aff7eab5c57415bdd1f17d3534c25c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 15:23:41 +0000 Subject: [PATCH 26/45] tweak roomheader layout when editing --- src/components/views/rooms/RoomHeader.js | 8 +++++--- src/components/views/settings/ChangeAvatar.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 41b1b364e2..79eac7b252 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -213,7 +213,7 @@ module.exports = React.createClass({ if (this.props.room) { if (this.props.editing) { roomAvatar = ( -
+
; - var change_avatar; - if (can_set_room_avatar) { - change_avatar = -
-

Room Icon

- -
; - } - var user_levels_section; if (user_levels.length) { user_levels_section = @@ -369,7 +359,7 @@ module.exports = React.createClass({ var create_event = this.props.room.currentState.getStateEvents('m.room.create', ''); var unfederatable_section; if (create_event.getContent()["m.federate"] === false) { - unfederatable_section =
Ths room is not accessible by remote Matrix servers
. + unfederatable_section =
Ths room is not accessible by remote Matrix servers.
} // TODO: support editing custom events_levels @@ -447,8 +437,6 @@ module.exports = React.createClass({ { banned_users_section } - { change_avatar } -
); } From 0caf5e0cdc18874ec26a21ed891bc4654dfb10fd Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 15 Jan 2016 15:51:40 +0000 Subject: [PATCH 28/45] manage permissions on whether people can actually edit name, topic, avatar etc or not --- src/components/views/rooms/RoomHeader.js | 78 +++++++++++++++++------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 79eac7b252..606603d451 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -151,20 +151,53 @@ module.exports = React.createClass({ var cancel_button = null; var save_button = null; var settings_button = null; - // var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); - // if (actual_name) actual_name = actual_name.getContent().name; if (this.props.editing) { - // name = - //
- // - //
- // if (topic) topic_el =
+ + // calculate permissions. XXX: this should be done on mount or something, and factored out with RoomSettings + var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); + var events_levels = power_levels.events || {}; + var user_id = MatrixClientPeg.get().credentials.userId; + + if (power_levels) { + power_levels = power_levels.getContent(); + var default_user_level = parseInt(power_levels.users_default || 0); + var user_levels = power_levels.users || {}; + var current_user_level = user_levels[user_id]; + if (current_user_level == undefined) current_user_level = default_user_level; + } else { + var default_user_level = 0; + var user_levels = []; + var current_user_level = 0; + } + + 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 room_name_level = parseInt(power_levels.state_default || 0); + if (events_levels['m.room.name'] !== undefined) { + room_name_level = events_levels['m.room.name']; + } + var can_set_room_name = current_user_level >= room_name_level; + + var room_topic_level = parseInt(power_levels.state_default || 0); + if (events_levels['m.room.topic'] !== undefined) { + room_topic_level = events_levels['m.room.topic']; + } + var can_set_room_topic = current_user_level >= room_topic_level; var placeholderName = "Unnamed Room"; if (this.state.defaultName && this.state.defaultName !== '?') { placeholderName += " (" + this.state.defaultName + ")"; } + save_button =
Save
+ cancel_button =
Cancel
+ } + + if (can_set_room_name) { name =
- - topic_el = - - - save_button =
Save
- cancel_button =
Cancel
- } else { - // + } + else { var searchStatus; // don't display the search count until the search completes and // gives us a valid (possibly zero) searchCount. @@ -204,17 +225,28 @@ module.exports = React.createClass({
+ } + if (can_set_room_topic) { + topic_el = + + } else { var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); if (topic) topic_el =
{ topic.getContent().topic }
; } var roomAvatar = null; if (this.props.room) { - if (this.props.editing) { + if (can_set_room_avatar) { roomAvatar = (
- +
-
- +
+
+ +