Merge remote-tracking branch 'origin/develop' into dbkr/scalar
This commit is contained in:
commit
fdcebe1e56
21 changed files with 243 additions and 54 deletions
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,3 +1,43 @@
|
|||
Changes in [0.6.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.3) (2016-06-03)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.2...v0.6.3)
|
||||
|
||||
* Change invite text field wording
|
||||
* Fix bug with new email invite UX where the invite could get wedged
|
||||
* Label app versions sensibly in UserSettings
|
||||
|
||||
Changes in [0.6.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.2) (2016-06-02)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.1...v0.6.2)
|
||||
|
||||
* Correctly bump dep on matrix-js-sdk 0.5.4
|
||||
|
||||
Changes in [0.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.1) (2016-06-02)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.0...v0.6.1)
|
||||
|
||||
* Fix focusing race in new UX for 3pid invites
|
||||
* Fix jenkins.sh
|
||||
|
||||
Changes in [0.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.0) (2016-06-02)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.5.2...v0.6.0)
|
||||
|
||||
* implement new UX for 3pid invites
|
||||
[\#297](https://github.com/matrix-org/matrix-react-sdk/pull/297)
|
||||
* multiple URL preview support
|
||||
[\#290](https://github.com/matrix-org/matrix-react-sdk/pull/290)
|
||||
* Add a fallback home server to log into
|
||||
[\#293](https://github.com/matrix-org/matrix-react-sdk/pull/293)
|
||||
* Hopefully fix memory leak with velocity
|
||||
[\#291](https://github.com/matrix-org/matrix-react-sdk/pull/291)
|
||||
* Support for enabling email notifications
|
||||
[\#289](https://github.com/matrix-org/matrix-react-sdk/pull/289)
|
||||
* Correct Readme instructions how to customize the UI
|
||||
[\#286](https://github.com/matrix-org/matrix-react-sdk/pull/286)
|
||||
* Avoid rerendering during Room unmount
|
||||
[\#285](https://github.com/matrix-org/matrix-react-sdk/pull/285)
|
||||
|
||||
Changes in [0.5.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.5.2) (2016-04-22)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.5.1...v0.5.2)
|
||||
|
|
|
@ -8,9 +8,6 @@ nvm use 4
|
|||
|
||||
set -x
|
||||
|
||||
# install the version of js-sdk provided to us by jenkins
|
||||
npm install ./node_modules/matrix-js-sdk-*.tgz
|
||||
|
||||
# install the other dependencies
|
||||
npm install
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.3",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
|
@ -31,7 +31,7 @@
|
|||
"highlight.js": "^8.9.1",
|
||||
"linkifyjs": "^2.0.0-beta.4",
|
||||
"marked": "^0.3.5",
|
||||
"matrix-js-sdk": "^0.5.2",
|
||||
"matrix-js-sdk": "^0.5.4",
|
||||
"optimist": "^0.6.1",
|
||||
"q": "^1.4.1",
|
||||
"react": "^15.0.1",
|
||||
|
|
|
@ -293,8 +293,9 @@ class Register extends Signup {
|
|||
|
||||
|
||||
class Login extends Signup {
|
||||
constructor(hsUrl, isUrl) {
|
||||
constructor(hsUrl, isUrl, fallbackHsUrl) {
|
||||
super(hsUrl, isUrl);
|
||||
this._fallbackHsUrl = fallbackHsUrl;
|
||||
this._currentFlowIndex = 0;
|
||||
this._flows = [];
|
||||
}
|
||||
|
@ -359,6 +360,30 @@ class Login extends Signup {
|
|||
error.friendlyText = (
|
||||
'Incorrect username and/or password.'
|
||||
);
|
||||
if (self._fallbackHsUrl) {
|
||||
// as per elsewhere, it would be much nicer to not replace the global
|
||||
// client just to try an alternate HS
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
self._fallbackHsUrl,
|
||||
self._isUrl
|
||||
);
|
||||
return MatrixClientPeg.get().login('m.login.password', loginParams).then(function(data) {
|
||||
return q({
|
||||
homeserverUrl: self._fallbackHsUrl,
|
||||
identityServerUrl: self._isUrl,
|
||||
userId: data.user_id,
|
||||
accessToken: data.access_token
|
||||
});
|
||||
}, function(fallback_error) {
|
||||
// We also have to put the default back again if it fails...
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this._hsUrl,
|
||||
this._isUrl
|
||||
);
|
||||
// throw the original error
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
error.friendlyText = (
|
||||
|
|
|
@ -330,7 +330,7 @@ module.exports = {
|
|||
* Returns null if the input didn't match a command.
|
||||
*/
|
||||
processInput: function(roomId, input) {
|
||||
// trim any trailing whitespace, as it can confuse the parser for
|
||||
// trim any trailing whitespace, as it can confuse the parser for
|
||||
// IRC-style commands
|
||||
input = input.replace(/\s+$/, "");
|
||||
if (input[0] === "/" && input[1] !== "/") {
|
||||
|
|
|
@ -100,7 +100,7 @@ module.exports = {
|
|||
return this.getEmailPusher(pushers, address) !== undefined;
|
||||
},
|
||||
|
||||
addEmailPusher: function(address) {
|
||||
addEmailPusher: function(address, data) {
|
||||
return MatrixClientPeg.get().setPusher({
|
||||
kind: 'email',
|
||||
app_id: "m.email",
|
||||
|
@ -108,7 +108,7 @@ module.exports = {
|
|||
app_display_name: 'Email Notifications',
|
||||
device_display_name: address,
|
||||
lang: navigator.language,
|
||||
data: {},
|
||||
data: data,
|
||||
append: true, // We always append for email pushers since we don't want to stop other accounts notifying to the same email address
|
||||
});
|
||||
},
|
||||
|
|
|
@ -106,6 +106,18 @@ module.exports = React.createClass({
|
|||
});
|
||||
|
||||
//console.log("enter: "+JSON.stringify(node.props._restingStyle));
|
||||
} else if (node === null) {
|
||||
// Velocity stores data on elements using the jQuery .data()
|
||||
// method, and assumes you'll be using jQuery's .remove() to
|
||||
// remove the element, but we don't use jQuery, so we need to
|
||||
// blow away the element's data explicitly otherwise it will leak.
|
||||
// This uses Velocity's internal jQuery compatible wrapper.
|
||||
// See the bug at
|
||||
// https://github.com/julianshapiro/velocity/issues/300
|
||||
// and the FAQ entry, "Preventing memory leaks when
|
||||
// creating/destroying large numbers of elements"
|
||||
// (https://github.com/julianshapiro/velocity/issues/47)
|
||||
Velocity.Utilities.removeData(this.nodes[k]);
|
||||
}
|
||||
this.nodes[k] = node;
|
||||
},
|
||||
|
|
|
@ -104,6 +104,10 @@ module.exports = React.createClass({
|
|||
return "https://matrix.org";
|
||||
},
|
||||
|
||||
getFallbackHsUrl: function() {
|
||||
return this.props.config.fallback_hs_url;
|
||||
},
|
||||
|
||||
getCurrentIsUrl: function() {
|
||||
if (this.state.register_is_url) {
|
||||
return this.state.register_is_url;
|
||||
|
@ -490,6 +494,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
type: 'm.room.guest_access',
|
||||
state_key: '',
|
||||
visibility: 'private',
|
||||
}
|
||||
],
|
||||
}).done(function(res) {
|
||||
|
@ -1157,6 +1162,7 @@ module.exports = React.createClass({
|
|||
guestAccessToken={this.state.guestAccessToken}
|
||||
defaultHsUrl={this.props.config.default_hs_url}
|
||||
defaultIsUrl={this.props.config.default_is_url}
|
||||
brand={this.props.config.brand}
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
registrationUrl={this.props.registrationUrl}
|
||||
|
@ -1185,6 +1191,7 @@ module.exports = React.createClass({
|
|||
defaultIsUrl={this.props.config.default_is_url}
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||
onLoginAsGuestClick={this.props.enableGuest && this.props.config && this.props.config.default_hs_url ? this._registerAsGuest.bind(this, true) : undefined}
|
||||
onCancelClick={ this.state.guestCreds ? this.onReturnToGuestClick : null }
|
||||
|
|
|
@ -677,6 +677,16 @@ module.exports = React.createClass({
|
|||
|
||||
uploadFile: function(file) {
|
||||
var self = this;
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guest users can't upload files. Please register to upload."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ContentMessages.sendContentToRoom(
|
||||
file, this.state.room.roomId, MatrixClientPeg.get()
|
||||
).done(undefined, function(error) {
|
||||
|
|
|
@ -401,7 +401,7 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// if we are scrolled to the bottom, do a quick-reset of our unreadNotificationCount
|
||||
// to avoid having to wait from the remote echo from the homeserver.
|
||||
if (this.getScrollState().stuckAtBottom) {
|
||||
if (this.isAtEndOfLiveTimeline()) {
|
||||
this.props.room.setUnreadNotificationCount('total', 0);
|
||||
this.props.room.setUnreadNotificationCount('highlight', 0);
|
||||
// XXX: i'm a bit surprised we don't have to emit an event or dispatch to get this picked up
|
||||
|
|
|
@ -299,7 +299,7 @@ module.exports = React.createClass({
|
|||
onValueChanged={ this.onAddThreepidClicked } />
|
||||
</div>
|
||||
<div className="mx_UserSettings_addThreepid">
|
||||
<img src="img/plus.svg" width="14" height="14" alt="Add" onClick={ this.onAddThreepidClicked }/>
|
||||
<img src="img/plus.svg" width="14" height="14" alt="Add" onClick={ this.onAddThreepidClicked.bind(this, undefined, true) }/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -397,9 +397,14 @@ module.exports = React.createClass({
|
|||
Logged in as {this._me}
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Version {this.state.clientVersion}
|
||||
<br />
|
||||
{this.props.version}
|
||||
Homeserver is { MatrixClientPeg.get().getHomeserverUrl() }
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
Identity Server is { MatrixClientPeg.get().getIdentityServerUrl() }
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
matrix-react-sdk version: {this.state.clientVersion}<br/>
|
||||
vector-web version: {this.props.version}<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ module.exports = React.createClass({displayName: 'Login',
|
|||
customIsUrl: React.PropTypes.string,
|
||||
defaultHsUrl: React.PropTypes.string,
|
||||
defaultIsUrl: React.PropTypes.string,
|
||||
// Secondary HS which we try to log into if the user is using
|
||||
// the default HS but login fails. Useful for migrating to a
|
||||
// different home server without confusing users.
|
||||
fallbackHsUrl: React.PropTypes.string,
|
||||
|
||||
// login shouldn't know or care how registration is done.
|
||||
onRegisterClick: React.PropTypes.func.isRequired,
|
||||
|
@ -105,7 +109,9 @@ module.exports = React.createClass({displayName: 'Login',
|
|||
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
|
||||
isUrl = isUrl || this.state.enteredIdentityServerUrl;
|
||||
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl);
|
||||
var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
|
||||
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl, fallbackHsUrl);
|
||||
this._loginLogic = loginLogic;
|
||||
|
||||
loginLogic.getFlows().then(function(flows) {
|
||||
|
|
|
@ -22,6 +22,7 @@ var sdk = require('../../../index');
|
|||
var dis = require('../../../dispatcher');
|
||||
var Signup = require("../../../Signup");
|
||||
var ServerConfig = require("../../views/login/ServerConfig");
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var RegistrationForm = require("../../views/login/RegistrationForm");
|
||||
var CaptchaForm = require("../../views/login/CaptchaForm");
|
||||
|
||||
|
@ -40,6 +41,7 @@ module.exports = React.createClass({
|
|||
customIsUrl: React.PropTypes.string,
|
||||
defaultHsUrl: React.PropTypes.string,
|
||||
defaultIsUrl: React.PropTypes.string,
|
||||
brand: React.PropTypes.string,
|
||||
email: React.PropTypes.string,
|
||||
username: React.PropTypes.string,
|
||||
guestAccessToken: React.PropTypes.string,
|
||||
|
@ -145,6 +147,26 @@ module.exports = React.createClass({
|
|||
identityServerUrl: self.registerLogic.getIdentityServerUrl(),
|
||||
accessToken: response.access_token
|
||||
});
|
||||
|
||||
if (self.props.brand) {
|
||||
MatrixClientPeg.get().getPushers().done((resp)=>{
|
||||
var pushers = resp.pushers;
|
||||
for (var i = 0; i < pushers.length; ++i) {
|
||||
if (pushers[i].kind == 'email') {
|
||||
var emailPusher = pushers[i];
|
||||
emailPusher.data = { brand: self.props.brand };
|
||||
MatrixClientPeg.get().setPusher(emailPusher).done(() => {
|
||||
console.log("Set email branding to " + self.props.brand);
|
||||
}, (error) => {
|
||||
console.error("Couldn't set email branding: " + error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, (error) => {
|
||||
console.error("Couldn't get pushers: " + error);
|
||||
});
|
||||
}
|
||||
|
||||
}, function(err) {
|
||||
if (err.message) {
|
||||
self.setState({
|
||||
|
|
|
@ -39,11 +39,11 @@ module.exports = React.createClass({
|
|||
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;
|
||||
// Set the cursor at the end of the text input
|
||||
this.refs.textinput.value = this.props.value;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,13 +83,12 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onOk}>
|
||||
{this.props.button}
|
||||
</button>
|
||||
|
||||
<button onClick={this.onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
<button onClick={this.onOk}>
|
||||
{this.props.button}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -45,9 +45,9 @@ module.exports = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
// the URL (if any) to be previewed with a LinkPreviewWidget
|
||||
// the URLs (if any) to be previewed with a LinkPreviewWidget
|
||||
// inside this TextualBody.
|
||||
link: null,
|
||||
links: [],
|
||||
|
||||
// track whether the preview widget is hidden
|
||||
widgetHidden: false,
|
||||
|
@ -57,9 +57,11 @@ module.exports = React.createClass({
|
|||
componentDidMount: function() {
|
||||
linkifyElement(this.refs.content, linkifyMatrix.options);
|
||||
|
||||
var link = this.findLink(this.refs.content.children);
|
||||
if (link) {
|
||||
this.setState({ link: link.getAttribute("href") });
|
||||
var links = this.findLinks(this.refs.content.children);
|
||||
if (links.length) {
|
||||
this.setState({ links: links.map((link)=>{
|
||||
return link.getAttribute("href");
|
||||
})});
|
||||
|
||||
// lazy-load the hidden state of the preview widget from localstorage
|
||||
if (global.localStorage) {
|
||||
|
@ -74,27 +76,32 @@ module.exports = React.createClass({
|
|||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
// exploit that events are immutable :)
|
||||
// ...and that .links is only ever set in componentDidMount and never changes
|
||||
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
|
||||
nextProps.highlights !== this.props.highlights ||
|
||||
nextProps.highlightLink !== this.props.highlightLink ||
|
||||
nextState.link !== this.state.link ||
|
||||
nextState.links !== this.state.links ||
|
||||
nextState.widgetHidden !== this.state.widgetHidden);
|
||||
},
|
||||
|
||||
findLink: function(nodes) {
|
||||
findLinks: function(nodes) {
|
||||
var links = [];
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (node.tagName === "A" && node.getAttribute("href"))
|
||||
{
|
||||
return this.isLinkPreviewable(node) ? node : undefined;
|
||||
if (this.isLinkPreviewable(node)) {
|
||||
links.push(node);
|
||||
}
|
||||
}
|
||||
else if (node.tagName === "PRE" || node.tagName === "CODE") {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
else if (node.children && node.children.length) {
|
||||
return this.findLink(node.children)
|
||||
links = links.concat(this.findLinks(node.children));
|
||||
}
|
||||
}
|
||||
return links;
|
||||
},
|
||||
|
||||
isLinkPreviewable: function(node) {
|
||||
|
@ -117,7 +124,7 @@ module.exports = React.createClass({
|
|||
else {
|
||||
var url = node.getAttribute("href");
|
||||
var host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
|
||||
if (node.textContent.trim().startsWith(host)) {
|
||||
if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) {
|
||||
// it's a "foo.pl" style link
|
||||
return;
|
||||
}
|
||||
|
@ -160,14 +167,17 @@ module.exports = React.createClass({
|
|||
{highlightLink: this.props.highlightLink});
|
||||
|
||||
|
||||
var widget;
|
||||
if (this.state.link && !this.state.widgetHidden) {
|
||||
var widgets;
|
||||
if (this.state.links.length && !this.state.widgetHidden) {
|
||||
var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget');
|
||||
widget = <LinkPreviewWidget
|
||||
link={ this.state.link }
|
||||
mxEvent={ this.props.mxEvent }
|
||||
onCancelClick={ this.onCancelClick }
|
||||
onWidgetLoad={ this.props.onWidgetLoad }/>;
|
||||
widgets = this.state.links.map((link)=>{
|
||||
return <LinkPreviewWidget
|
||||
key={ link }
|
||||
link={ link }
|
||||
mxEvent={ this.props.mxEvent }
|
||||
onCancelClick={ this.onCancelClick }
|
||||
onWidgetLoad={ this.props.onWidgetLoad }/>;
|
||||
});
|
||||
}
|
||||
|
||||
switch (content.msgtype) {
|
||||
|
@ -176,21 +186,21 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<span ref="content" className="mx_MEmoteBody mx_EventTile_content">
|
||||
* { name } { body }
|
||||
{ widget }
|
||||
{ widgets }
|
||||
</span>
|
||||
);
|
||||
case "m.notice":
|
||||
return (
|
||||
<span ref="content" className="mx_MNoticeBody mx_EventTile_content">
|
||||
{ body }
|
||||
{ widget }
|
||||
{ widgets }
|
||||
</span>
|
||||
);
|
||||
default: // including "m.text"
|
||||
return (
|
||||
<span ref="content" className="mx_MTextBody mx_EventTile_content">
|
||||
{ body }
|
||||
{ widget }
|
||||
{ widgets }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ module.exports = React.createClass({
|
|||
propTypes: {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onInvite: React.PropTypes.func.isRequired, // fn(inputText)
|
||||
onThirdPartyInvite: React.PropTypes.func.isRequired, // fn(inputText)
|
||||
onSearchQueryChanged: React.PropTypes.func // fn(inputText)
|
||||
},
|
||||
|
||||
|
@ -49,10 +50,19 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// initialise the email tile
|
||||
this.onSearchQueryChanged('');
|
||||
},
|
||||
|
||||
onInvite: function(ev) {
|
||||
this.props.onInvite(this._input);
|
||||
},
|
||||
|
||||
onThirdPartyInvite: function(ev) {
|
||||
this.props.onThirdPartyInvite(this._input);
|
||||
},
|
||||
|
||||
onSearchQueryChanged: function(input) {
|
||||
this._input = input;
|
||||
var EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||
|
@ -68,9 +78,10 @@ module.exports = React.createClass({
|
|||
|
||||
this._emailEntity = new Entities.newEntity(
|
||||
<EntityTile key="dynamic_invite_tile" suppressOnHover={true} showInviteButton={true}
|
||||
avatarJsx={ <BaseAvatar name="@" width={36} height={36} /> }
|
||||
className="mx_EntityTile_invitePlaceholder"
|
||||
presenceState="online" onClick={this.onInvite} name={label} />,
|
||||
avatarJsx={ <BaseAvatar name="@" width={36} height={36} /> }
|
||||
className="mx_EntityTile_invitePlaceholder"
|
||||
presenceState="online" onClick={this.onThirdPartyInvite} name={"Invite by email"}
|
||||
/>,
|
||||
function(query) {
|
||||
return true; // always show this
|
||||
}
|
||||
|
@ -89,7 +100,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<SearchableEntityList searchPlaceholderText={"Invite/search by name, email, id"}
|
||||
<SearchableEntityList searchPlaceholderText={"Search/invite by name, email, id"}
|
||||
onSubmit={this.props.onInvite}
|
||||
onQueryChanged={this.onSearchQueryChanged}
|
||||
entities={entities}
|
||||
|
|
|
@ -340,6 +340,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
type: 'm.room.guest_access',
|
||||
state_key: '',
|
||||
visibility: 'private',
|
||||
}
|
||||
],
|
||||
}).then(
|
||||
|
@ -367,7 +368,7 @@ module.exports = React.createClass({
|
|||
action: 'leave_room',
|
||||
room_id: this.props.member.roomId,
|
||||
});
|
||||
this.props.onFinished();
|
||||
this.props.onFinished();
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
|
|
@ -166,6 +166,25 @@ module.exports = React.createClass({
|
|||
});
|
||||
}, 500),
|
||||
|
||||
onThirdPartyInvite: function(inputText) {
|
||||
var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
||||
Modal.createDialog(TextInputDialog, {
|
||||
title: "Invite members by email",
|
||||
description: "Please enter one or more email addresses",
|
||||
value: inputText,
|
||||
button: "Invite",
|
||||
onFinished: (should_invite, addresses)=>{
|
||||
if (should_invite) {
|
||||
// defer the actual invite to the next event loop to give this
|
||||
// Modal a chance to unmount in case onInvite() triggers a new one
|
||||
setTimeout(()=>{
|
||||
this.onInvite(addresses);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onInvite: function(inputText) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
|
@ -514,6 +533,7 @@ module.exports = React.createClass({
|
|||
inviteMemberListSection = (
|
||||
<InviteMemberList roomId={this.props.roomId}
|
||||
onSearchQueryChanged={this.onSearchQueryChanged}
|
||||
onThirdPartyInvite={this.onThirdPartyInvite}
|
||||
onInvite={this.onInvite} />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,15 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onUploadClick: function(ev) {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Please Register",
|
||||
description: "Guest users can't upload files. Please register to upload."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.refs.uploadInput.click();
|
||||
},
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ module.exports = React.createClass({
|
|||
getInitialState: function() {
|
||||
var tags = {};
|
||||
Object.keys(this.props.room.tags).forEach(function(tagName) {
|
||||
tags[tagName] = {};
|
||||
tags[tagName] = ['yep'];
|
||||
});
|
||||
|
||||
var areNotifsMuted = false;
|
||||
|
@ -186,7 +186,7 @@ module.exports = React.createClass({
|
|||
// tags
|
||||
if (this.state.tags_changed) {
|
||||
var tagDiffs = ObjectUtils.getKeyValueArrayDiffs(originalState.tags, this.state.tags);
|
||||
// [ {place: add, key: "m.favourite", val: "yep"} ]
|
||||
// [ {place: add, key: "m.favourite", val: ["yep"]} ]
|
||||
tagDiffs.forEach(function(diff) {
|
||||
switch (diff.place) {
|
||||
case "add":
|
||||
|
|
|
@ -48,6 +48,7 @@ var SearchableEntityList = React.createClass({
|
|||
getInitialState: function() {
|
||||
return {
|
||||
query: "",
|
||||
focused: false,
|
||||
truncateAt: this.props.truncateAt,
|
||||
results: this.getSearchResults("", this.props.entities)
|
||||
};
|
||||
|
@ -101,7 +102,7 @@ var SearchableEntityList = React.createClass({
|
|||
|
||||
getSearchResults: function(query, entities) {
|
||||
if (!query || query.length === 0) {
|
||||
return this.props.emptyQueryShowsAll ? entities : []
|
||||
return this.props.emptyQueryShowsAll ? entities : [ entities[0] ]
|
||||
}
|
||||
return entities.filter(function(e) {
|
||||
return e.matches(query);
|
||||
|
@ -134,13 +135,27 @@ var SearchableEntityList = React.createClass({
|
|||
<form onSubmit={this.onQuerySubmit} autoComplete="off">
|
||||
<input className="mx_SearchableEntityList_query" id="mx_SearchableEntityList_query" type="text"
|
||||
onChange={this.onQueryChanged} value={this.state.query}
|
||||
onFocus={ ()=>{
|
||||
if (this._blurTimeout) {
|
||||
clearTimeout(this.blurTimeout);
|
||||
}
|
||||
this.setState({ focused: true });
|
||||
} }
|
||||
onBlur={ ()=>{
|
||||
// nasty setTimeout heuristic to avoid the 'invite by email' prompt disappearing
|
||||
// due to the onBlur before we can click on it
|
||||
this._blurTimeout = setTimeout(
|
||||
()=>{ this.setState({ focused: false }) },
|
||||
300
|
||||
);
|
||||
} }
|
||||
placeholder={this.props.searchPlaceholderText} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
var list;
|
||||
if (this.state.results.length) {
|
||||
if (this.state.results.length > 1 || this.state.focused) {
|
||||
if (this.props.truncateAt) { // caller wants list truncated
|
||||
var TruncatedList = sdk.getComponent("elements.TruncatedList");
|
||||
list = (
|
||||
|
@ -172,10 +187,10 @@ var SearchableEntityList = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={ "mx_SearchableEntityList " + (this.state.query.length ? "mx_SearchableEntityList_expanded" : "") }>
|
||||
<div className={ "mx_SearchableEntityList " + (list ? "mx_SearchableEntityList_expanded" : "") }>
|
||||
{ inputBox }
|
||||
{ list }
|
||||
{ this.state.query.length ? <div className="mx_SearchableEntityList_hrWrapper"><hr/></div> : '' }
|
||||
{ list ? <div className="mx_SearchableEntityList_hrWrapper"><hr/></div> : '' }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue