diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index 82c295756b..bbd714fa57 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -92,6 +92,7 @@ class ContentMessages {
this.inprogress.push(upload);
dis.dispatch({action: 'upload_started'});
+ var error;
var self = this;
return def.promise.then(function() {
upload.promise = matrixClient.uploadContent(file);
@@ -103,11 +104,10 @@ class ContentMessages {
dis.dispatch({action: 'upload_progress', upload: upload});
}
}).then(function(url) {
- dis.dispatch({action: 'upload_finished', upload: upload});
content.url = url;
return matrixClient.sendMessage(roomId, content);
}, function(err) {
- dis.dispatch({action: 'upload_failed', upload: upload});
+ error = err;
if (!upload.canceled) {
var desc = "The file '"+upload.fileName+"' failed to upload.";
if (err.http_status == 413) {
@@ -128,6 +128,12 @@ class ContentMessages {
break;
}
}
+ if (error) {
+ dis.dispatch({action: 'upload_failed', upload: upload});
+ }
+ else {
+ dis.dispatch({action: 'upload_finished', upload: upload});
+ }
});
}
diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js
index 0b7f17b2b2..6db2b08fd1 100644
--- a/src/HtmlUtils.js
+++ b/src/HtmlUtils.js
@@ -17,7 +17,6 @@ limitations under the License.
'use strict';
var React = require('react');
-var ReactDOMServer = require('react-dom/server')
var sanitizeHtml = require('sanitize-html');
var highlight = require('highlight.js');
@@ -50,14 +49,23 @@ var sanitizeHtmlParams = {
},
};
-class Highlighter {
- constructor(html, highlightClass, onHighlightClick) {
- this.html = html;
+class BaseHighlighter {
+ constructor(highlightClass, highlightLink) {
this.highlightClass = highlightClass;
- this.onHighlightClick = onHighlightClick;
- this._key = 0;
+ this.highlightLink = highlightLink;
}
+ /**
+ * apply the highlights to a section of text
+ *
+ * @param {string} safeSnippet The snippet of text to apply the highlights
+ * to.
+ * @param {string[]} safeHighlights A list of substrings to highlight,
+ * sorted by descending length.
+ *
+ * returns a list of results (strings for HtmlHighligher, react nodes for
+ * TextHighlighter).
+ */
applyHighlights(safeSnippet, safeHighlights) {
var lastOffset = 0;
var offset;
@@ -71,10 +79,12 @@ class Highlighter {
nodes = nodes.concat(this._applySubHighlights(subSnippet, safeHighlights));
}
- // do highlight
- nodes.push(this._createSpan(safeHighlight, true));
+ // do highlight. use the original string rather than safeHighlight
+ // to preserve the original casing.
+ var endOffset = offset + safeHighlight.length;
+ nodes.push(this._processSnippet(safeSnippet.substring(offset, endOffset), true));
- lastOffset = offset + safeHighlight.length;
+ lastOffset = endOffset;
}
// handle postamble
@@ -92,31 +102,62 @@ class Highlighter {
}
else {
// no more highlights to be found, just return the unhighlighted string
- return [this._createSpan(safeSnippet, false)];
+ return [this._processSnippet(safeSnippet, false)];
}
}
+}
+
+class HtmlHighlighter extends BaseHighlighter {
+ /* highlight the given snippet if required
+ *
+ * snippet: content of the span; must have been sanitised
+ * highlight: true to highlight as a search match
+ *
+ * returns an HTML string
+ */
+ _processSnippet(snippet, highlight) {
+ if (!highlight) {
+ // nothing required here
+ return snippet;
+ }
+
+ var span = ""
+ + snippet + "";
+
+ if (this.highlightLink) {
+ span = ""
+ +span+"";
+ }
+ return span;
+ }
+}
+
+class TextHighlighter extends BaseHighlighter {
+ constructor(highlightClass, highlightLink) {
+ super(highlightClass, highlightLink);
+ this._key = 0;
+ }
/* create a node to hold the given content
*
- * spanBody: content of the span. If html, must have been sanitised
+ * snippet: content of the span
* highlight: true to highlight as a search match
+ *
+ * returns a React node
*/
- _createSpan(spanBody, highlight) {
- var spanProps = {
- key: this._key++,
- };
+ _processSnippet(snippet, highlight) {
+ var key = this._key++;
- if (highlight) {
- spanProps.onClick = this.onHighlightClick;
- spanProps.className = this.highlightClass;
+ var node =
+
+ { snippet }
+ ;
+
+ if (highlight && this.highlightLink) {
+ node = {node}
}
- if (this.html) {
- return ();
- }
- else {
- return ({ spanBody });
- }
+ return node;
}
}
@@ -128,8 +169,7 @@ module.exports = {
*
* highlights: optional list of words to highlight, ordered by longest word first
*
- * opts.onHighlightClick: optional callback function to be called when a
- * highlighted word is clicked
+ * opts.highlightLink: optional href to add to highlights
*/
bodyToHtml: function(content, highlights, opts) {
opts = opts || {};
@@ -144,18 +184,13 @@ module.exports = {
// by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either
try {
if (highlights && highlights.length > 0) {
- var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick);
+ var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
var safeHighlights = highlights.map(function(highlight) {
return sanitizeHtml(highlight, sanitizeHtmlParams);
});
// XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure.
sanitizeHtmlParams.textFilter = function(safeText) {
- return highlighter.applyHighlights(safeText, safeHighlights).map(function(span) {
- // XXX: rather clunky conversion from the react nodes returned by applyHighlights
- // (which need to be nodes for the non-html highlighting case), to convert them
- // back into raw HTML given that's what sanitize-html works in terms of.
- return ReactDOMServer.renderToString(span);
- }).join('');
+ return highlighter.applyHighlights(safeText, safeHighlights).join('');
};
}
safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
@@ -167,7 +202,7 @@ module.exports = {
} else {
safeBody = content.body;
if (highlights && highlights.length > 0) {
- var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick);
+ var highlighter = new TextHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
return highlighter.applyHighlights(safeBody, highlights);
}
else {
diff --git a/src/Notifier.js b/src/Notifier.js
index e52fd252fe..b64a001a5f 100644
--- a/src/Notifier.js
+++ b/src/Notifier.js
@@ -182,6 +182,9 @@ var Notifier = {
if (state === "PREPARED" || state === "SYNCING") {
this.isPrepared = true;
}
+ else if (state === "STOPPED" || state === "ERROR") {
+ this.isPrepared = false;
+ }
},
onRoomTimeline: function(ev, room, toStartOfTimeline) {
diff --git a/src/Tinter.js b/src/Tinter.js
index b258930425..a83ccdce74 100644
--- a/src/Tinter.js
+++ b/src/Tinter.js
@@ -64,6 +64,7 @@ var cssAttrs = [
"borderColor",
"borderTopColor",
"borderBottomColor",
+ "borderLeftColor",
];
var svgAttrs = [
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 5869c8ef33..0a9231247c 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -175,7 +175,7 @@ module.exports = React.createClass({
guest: true
});
}, function(err) {
- console.error(err.data);
+ console.error("Failed to register as guest: " + err + " " + err.data);
self._setAutoRegisterAsGuest(false);
});
},
@@ -970,7 +970,9 @@ module.exports = React.createClass({
onRegisterClick={this.onRegisterClick}
homeserverUrl={this.props.config.default_hs_url}
identityServerUrl={this.props.config.default_is_url}
- onForgotPasswordClick={this.onForgotPasswordClick} />
+ onForgotPasswordClick={this.onForgotPasswordClick}
+ onLoginAsGuestClick={this.props.enableGuest && this.props.config && this.props.config.default_hs_url ? this._registerAsGuest: undefined}
+ />
);
}
}
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index f2d2cf901b..45b6f5ea20 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -51,11 +51,6 @@ module.exports = React.createClass({
// for more details.
stickyBottom: React.PropTypes.bool,
- // callback to determine if a user is the magic freeswitch conference
- // user. Takes one parameter, which is a user id. Should return true if
- // the user is the conference user.
- isConferenceUser: React.PropTypes.func,
-
// callback which is called when the panel is scrolled.
onScroll: React.PropTypes.func,
@@ -163,54 +158,20 @@ module.exports = React.createClass({
this.eventNodes = {};
- // we do two passes over the events list; first of all, we figure out
- // which events we want to show, and where the read markers fit into
- // the list; then we actually create the event tiles. This allows us to
- // behave slightly differently for the last event in the list.
- //
- // (Arguably we could do this when the events are added to this.props,
- // but that would make it trickier to keep in sync with the read marker, given
- // the read marker isn't necessarily on an event which we will show).
- //
- var eventsToShow = [];
+ var i;
- // the index in 'eventsToShow' of the event *before* which we put the
- // read marker or its ghost. (Note that it may be equal to
- // eventsToShow.length, which means it would be at the end of the timeline)
- var ghostIndex, readMarkerIndex;
-
- for (var i = 0; i < this.props.events.length; i++) {
+ // first figure out which is the last event in the list which we're
+ // actually going to show; this allows us to behave slightly
+ // differently for the last event in the list.
+ for (i = this.props.events.length-1; i >= 0; i--) {
var mxEv = this.props.events[i];
- var wantTile = true;
-
if (!EventTile.haveTileForEvent(mxEv)) {
- wantTile = false;
+ continue;
}
- if (this.props.isConferenceUser && mxEv.getType() === "m.room.member") {
- if (this.props.isConferenceUser(mxEv.getSender()) ||
- this.props.isConferenceUser(mxEv.getStateKey())) {
- wantTile = false; // suppress conf user join/parts
- }
- }
-
- if (wantTile) {
- eventsToShow.push(mxEv);
- }
-
- var eventId = mxEv.getId();
- if (eventId == this.props.readMarkerEventId) {
- readMarkerIndex = eventsToShow.length;
- } else if (eventId == this.currentReadMarkerEventId && !this.currentGhostEventId) {
- // there is currently a read-up-to marker at this point, but no
- // more. Show an animation of it disappearing.
- ghostIndex = eventsToShow.length;
- this.currentGhostEventId = eventId;
- } else if (eventId == this.currentGhostEventId) {
- // if we're showing an animation, continue to show it.
- ghostIndex = eventsToShow.length;
- }
+ break;
}
+ var lastShownEventIndex = i;
var ret = [];
@@ -219,42 +180,54 @@ module.exports = React.createClass({
// assume there is no read marker until proven otherwise
var readMarkerVisible = false;
- for (var i = 0; i < eventsToShow.length; i++) {
- var mxEv = eventsToShow[i];
+ for (i = 0; i < this.props.events.length; i++) {
+ var mxEv = this.props.events[i];
var wantTile = true;
+ var eventId = mxEv.getId();
- // insert the read marker if appropriate.
- if (i == readMarkerIndex) {
+ if (!EventTile.haveTileForEvent(mxEv)) {
+ wantTile = false;
+ }
+
+ var last = (i == lastShownEventIndex);
+
+ if (wantTile) {
+ ret.push(this._getTilesForEvent(prevEvent, mxEv, last));
+ } else if (!mxEv.status) {
+ // if we aren't showing the event, put in a dummy scroll token anyway, so
+ // that we can scroll to the right place.
+ ret.push(
);
+ }
+
+ if (eventId == this.props.readMarkerEventId) {
var visible = this.props.readMarkerVisible;
- // XXX is this still needed?
- // suppress the read marker if the next event is sent by us; this
- // is a nonsensical and temporary situation caused by the delay between
- // us sending a message and receiving the synthesized receipt.
- if (mxEv.sender && mxEv.sender.userId == this.props.ourUserId) {
+ // if the read marker comes at the end of the timeline, we don't want
+ // to show it, but we still want to create the
for it so that the
+ // algorithms which depend on its position on the screen aren't confused.
+ if (i >= lastShownEventIndex) {
visible = false;
+ } else {
+ // XXX is this still needed?
+ // suppress the read marker if the next event is sent by us; this
+ // is a nonsensical and temporary situation caused by the delay between
+ // us sending a message and receiving the synthesized receipt.
+ var nextEvent = this.props.events[i+1];
+ if (nextEvent.sender && nextEvent.sender.userId == this.props.ourUserId) {
+ visible = false;
+ }
}
ret.push(this._getReadMarkerTile(visible));
readMarkerVisible = visible;
- } else if (i == ghostIndex) {
+ } else if (eventId == this.currentReadMarkerEventId && !this.currentGhostEventId) {
+ // there is currently a read-up-to marker at this point, but no
+ // more. Show an animation of it disappearing.
+ ret.push(this._getReadMarkerGhostTile());
+ this.currentGhostEventId = eventId;
+ } else if (eventId == this.currentGhostEventId) {
+ // if we're showing an animation, continue to show it.
ret.push(this._getReadMarkerGhostTile());
}
-
- var last = false;
- if (i == eventsToShow.length - 1) {
- last = true;
- }
-
- // add the tiles for this event
- ret.push(this._getTilesForEvent(prevEvent, mxEv, last));
- prevEvent = mxEv;
- }
-
- // if the read marker comes at the end of the timeline, we don't want
- // to show it, but we still want to create the for it so that the
- // algorithms which depend on its position on the screen aren't confused.
- if (i == readMarkerIndex) {
- ret.push(this._getReadMarkerTile(false));
}
this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null;
@@ -298,7 +271,8 @@ module.exports = React.createClass({
ref={this._collectEventNode.bind(this, eventId)}
data-scroll-token={scrollToken}>
+ last={last} isSelectedEvent={highlight}
+ onImageLoad={this._onImageLoad} />
);
@@ -353,6 +327,16 @@ module.exports = React.createClass({
this.eventNodes[eventId] = node;
},
+
+ // once images in the events load, make the scrollPanel check the
+ // scroll offsets.
+ _onImageLoad: function() {
+ var scrollPanel = this.refs.messagePanel;
+ if (scrollPanel) {
+ scrollPanel.checkScroll();
+ }
+ },
+
render: function() {
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
return (
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 9ff3925b10..2e0897e3d0 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -51,6 +51,11 @@ module.exports = React.createClass({
// callback for when the user clicks on the 'scroll to bottom' button
onScrollToBottomClick: React.PropTypes.func,
+
+ // callback for when we do something that changes the size of the
+ // status bar. This is used to trigger a re-layout in the parent
+ // component.
+ onResize: React.PropTypes.func,
},
getInitialState: function() {
@@ -63,8 +68,17 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
},
+ componentDidUpdate: function(prevProps, prevState) {
+ if(this.props.onResize && this._checkForResize(prevProps, prevState)) {
+ this.props.onResize();
+ }
+ },
+
componentWillUnmount: function() {
- MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
+ // we may have entirely lost our client as we're logging out before clicking login on the guest bar...
+ if (MatrixClientPeg.get()) {
+ MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
+ }
},
onSyncStateChange: function(state, prevState) {
@@ -76,7 +90,85 @@ module.exports = React.createClass({
});
},
- render: function() {
+ // determine if we need to call onResize
+ _checkForResize: function(prevProps, prevState) {
+ // figure out the old height and the new height of the status bar. We
+ // don't need the actual height - just whether it is likely to have
+ // changed - so we use '0' to indicate normal size, and other values to
+ // indicate other sizes.
+ var oldSize, newSize;
+
+ if (prevState.syncState === "ERROR") {
+ oldSize = 1;
+ } else if (prevProps.tabCompleteEntries) {
+ oldSize = 0;
+ } else if (prevProps.hasUnsentMessages) {
+ oldSize = 2;
+ } else {
+ oldSize = 0;
+ }
+
+ if (this.state.syncState === "ERROR") {
+ newSize = 1;
+ } else if (this.props.tabCompleteEntries) {
+ newSize = 0;
+ } else if (this.props.hasUnsentMessages) {
+ newSize = 2;
+ } else {
+ newSize = 0;
+ }
+
+ return newSize != oldSize;
+ },
+
+ // return suitable content for the image on the left of the status bar.
+ //
+ // if wantPlaceholder is true, we include a "..." placeholder if
+ // there is nothing better to put in.
+ _getIndicator: function(wantPlaceholder) {
+ if (this.props.numUnreadMessages) {
+ return (
+
+ );
+ }
+
+ return null;
+ },
+
+
+ // return suitable content for the main (text) part of the status bar.
+ _getContent: function() {
var TabCompleteBar = sdk.getComponent('rooms.TabCompleteBar');
var TintableSvg = sdk.getComponent("elements.TintableSvg");
@@ -86,15 +178,13 @@ module.exports = React.createClass({
// a connection!
if (this.state.syncState === "ERROR") {
return (
-
+
-
-
- Connectivity to the server has been lost.
-
-
- Sent messages will be stored until your connection has returned.
-
+
+ Connectivity to the server has been lost.
+
+
+ Sent messages will be stored until your connection has returned.
@@ -1090,10 +1114,6 @@ module.exports = React.createClass({
} else {
var inviteEvent = myMember.events.member;
var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
- // XXX: Leaving this intentionally basic for now because invites are about to change totally
- // FIXME: This comment is now outdated - what do we need to fix? ^
- var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
- var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : "";
// We deliberately don't try to peek into invites, even if we have permission to peek
// as they could be a spam vector.
@@ -1109,8 +1129,6 @@ module.exports = React.createClass({
canJoin={ true } canPreview={ false }
spinner={this.state.joining}
/>
-
diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js
index 7269defe55..030904cb57 100644
--- a/src/components/structures/ScrollPanel.js
+++ b/src/components/structures/ScrollPanel.js
@@ -124,10 +124,9 @@ module.exports = React.createClass({
// after adding event tiles, we may need to tweak the scroll (either to
// keep at the bottom of the timeline, or to maintain the view after
// adding events to the top).
- this._restoreSavedScrollState();
-
- // we also re-check the fill state, in case the paginate was inadequate
- this.checkFillState();
+ //
+ // This will also re-check the fill state, in case the paginate was inadequate
+ this.checkScroll();
},
componentWillUnmount: function() {
@@ -178,6 +177,13 @@ module.exports = React.createClass({
this.checkFillState();
},
+ // after an update to the contents of the panel, check that the scroll is
+ // where it ought to be, and set off pagination requests if necessary.
+ checkScroll: function() {
+ this._restoreSavedScrollState();
+ this.checkFillState();
+ },
+
// return true if the content is fully scrolled down right now; else false.
//
// note that this is independent of the 'stuckAtBottom' state - it is simply
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 175faea1f3..a1fb1e065c 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -72,11 +72,6 @@ var TimelinePanel = React.createClass({
// 1/3 of the way down the viewport.
eventPixelOffset: React.PropTypes.number,
- // callback to determine if a user is the magic freeswitch conference
- // user. Takes one parameter, which is a user id. Should return true if
- // the user is the conference user.
- isConferenceUser: React.PropTypes.func,
-
// callback which is called when the panel is scrolled.
onScroll: React.PropTypes.func,
@@ -118,6 +113,7 @@ var TimelinePanel = React.createClass({
this.dispatcherRef = dis.register(this.onAction);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
+ MatrixClientPeg.get().on("Room.redaction", this.onRoomRedaction);
this._initTimeline(this.props);
},
@@ -146,6 +142,7 @@ var TimelinePanel = React.createClass({
var client = MatrixClientPeg.get();
if (client) {
client.removeListener("Room.timeline", this.onRoomTimeline);
+ client.removeListener("Room.redaction", this.onRoomRedaction);
}
},
@@ -238,10 +235,21 @@ var TimelinePanel = React.createClass({
}
},
+ onRoomRedaction: function(ev, room) {
+ if (this.unmounted) return;
+
+ // ignore events for other rooms
+ if (room !== this.props.room) return;
+
+ // we could skip an update if the event isn't in our timeline,
+ // but that's probably an early optimisation.
+ this.forceUpdate();
+ },
+
sendReadReceipt: function() {
if (!this.refs.messagePanel) return;
- var currentReadUpToEventId = this._getCurrentReadReceipt();
+ var currentReadUpToEventId = this._getCurrentReadReceipt(true);
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
// We want to avoid sending out read receipts when we are looking at
@@ -531,15 +539,20 @@ var TimelinePanel = React.createClass({
/**
* get the id of the event corresponding to our user's latest read-receipt.
+ *
+ * @param {Boolean} ignoreSynthesized If true, return only receipts that
+ * have been sent by the server, not
+ * implicit ones generated by the JS
+ * SDK.
*/
- _getCurrentReadReceipt: function() {
+ _getCurrentReadReceipt: function(ignoreSynthesized) {
var client = MatrixClientPeg.get();
// the client can be null on logout
if (client == null)
return null;
var myUserId = client.credentials.userId;
- return this.props.room.getEventReadUpTo(myUserId);
+ return this.props.room.getEventReadUpTo(myUserId, ignoreSynthesized);
},
_setReadMarker: function(eventId, eventTs) {
@@ -601,7 +614,6 @@ var TimelinePanel = React.createClass({
suppressFirstDateSeparator={ this.state.canBackPaginate }
ourUserId={ MatrixClientPeg.get().credentials.userId }
stickyBottom={ stickyBottom }
- isConferenceUser={ this.props.isConferenceUser }
onScroll={ this.onMessageListScroll }
onFillRequest={ this.onMessageListFillRequest }
/>
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index b853b8fd95..ef6b095da0 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -35,7 +35,8 @@ module.exports = React.createClass({displayName: 'Login',
// login shouldn't know or care how registration is done.
onRegisterClick: React.PropTypes.func.isRequired,
// login shouldn't care how password recovery is done.
- onForgotPasswordClick: React.PropTypes.func
+ onForgotPasswordClick: React.PropTypes.func,
+ onLoginAsGuestClick: React.PropTypes.func,
},
getDefaultProps: function() {
@@ -128,11 +129,30 @@ module.exports = React.createClass({displayName: 'Login',
if (!errCode && err.httpStatus) {
errCode = "HTTP " + err.httpStatus;
}
- this.setState({
- errorText: (
- "Error: Problem communicating with the given homeserver " +
+
+ var errorText = "Error: Problem communicating with the given homeserver " +
(errCode ? "(" + errCode + ")" : "")
- )
+
+ if (err.cors === 'rejected') {
+ if (window.location.protocol === 'https:' &&
+ (this.state.enteredHomeserverUrl.startsWith("http:") ||
+ !this.state.enteredHomeserverUrl.startsWith("http")))
+ {
+ errorText =
+ Can't connect to homeserver via HTTP when using a vector served by HTTPS.
+ Either use HTTPS or enable unsafe scripts
+ ;
+ }
+ else {
+ errorText =
+ Can't connect to homeserver - please check your connectivity and ensure
+ your homeserver's SSL certificate is trusted.
+ ;
+ }
+ }
+
+ this.setState({
+ errorText: errorText
});
},
@@ -167,6 +187,13 @@ module.exports = React.createClass({displayName: 'Login',
var LoginFooter = sdk.getComponent("login.LoginFooter");
var loader = this.state.busy ?
: null;
+ var loginAsGuestJsx;
+ if (this.props.onLoginAsGuestClick) {
+ loginAsGuestJsx =
+
+ Login as guest
+
+ }
return (
@@ -188,6 +215,7 @@ module.exports = React.createClass({displayName: 'Login',
Create a new account
+ { loginAsGuestJsx }
- Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?
+ Your display name is how you'll appear to others when you speak in rooms.
+ What would you like it to be?