Merge branch 'rxl881/apps' into rob/apps

This commit is contained in:
Robert Swain 2017-06-13 15:41:52 +02:00
commit 03ba3bd431
29 changed files with 1263 additions and 335 deletions

View file

@ -13,7 +13,7 @@ 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.
*/
import ModularWidgets from 'ModularWidgets';
import ModularWidgets from './ModularWidgets';
class AppWidget {
constructor(type, url, options) {

View file

@ -223,10 +223,8 @@ export default React.createClass({
ref='roomView'
autoJoin={this.props.autoJoin}
onRegistered={this.props.onRegistered}
eventId={this.props.initialEventId}
thirdPartyInvite={this.props.thirdPartyInvite}
oobData={this.props.roomOobData}
highlightedEventId={this.props.highlightedEventId}
eventPixelOffset={this.props.initialEventPixelOffset}
key={this.props.currentRoomId || 'roomview'}
opacity={this.props.middleOpacity}

View file

@ -466,7 +466,7 @@ module.exports = React.createClass({
this.notifyNewScreen('home');
break;
case 'view_set_mxid':
this._setMxId();
this._setMxId(payload);
break;
case 'view_start_chat_or_reuse':
this._chatCreateOrReuse(payload.user_id);
@ -570,6 +570,15 @@ module.exports = React.createClass({
const allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms(),
);
// If there are 0 rooms or 1 room, view the home page because otherwise
// if there are 0, we end up trying to index into an empty array, and
// if there is 1, we end up viewing the same room.
if (allRooms.length < 2) {
dis.dispatch({
action: 'view_home_page',
});
return;
}
let roomIndex = -1;
for (let i = 0; i < allRooms.length; ++i) {
if (allRooms[i].roomId == this.state.currentRoomId) {
@ -607,6 +616,8 @@ module.exports = React.createClass({
// @param {boolean=} roomInfo.show_settings Makes RoomView show the room settings dialog.
// @param {string=} roomInfo.event_id ID of the event in this room to show: this will cause a switch to the
// context of that particular event.
// @param {boolean=} roomInfo.highlighted If true, add event_id to the hash of the URL
// and alter the EventTile to appear highlighted.
// @param {Object=} roomInfo.third_party_invite Object containing data about the third party
// we received to join the room, if any.
// @param {string=} roomInfo.third_party_invite.inviteSignUrl 3pid invite sign URL
@ -618,40 +629,20 @@ module.exports = React.createClass({
this.focusComposer = true;
const newState = {
initialEventId: roomInfo.event_id,
highlightedEventId: roomInfo.event_id,
initialEventPixelOffset: undefined,
page_type: PageTypes.RoomView,
thirdPartyInvite: roomInfo.third_party_invite,
roomOobData: roomInfo.oob_data,
currentRoomAlias: roomInfo.room_alias,
autoJoin: roomInfo.auto_join,
};
if (!roomInfo.room_alias) {
newState.currentRoomId = roomInfo.room_id;
}
// if we aren't given an explicit event id, look for one in the
// scrollStateMap.
//
// TODO: do this in RoomView rather than here
if (!roomInfo.event_id && this.refs.loggedInView) {
const scrollState = this.refs.loggedInView.getScrollStateForRoom(roomInfo.room_id);
if (scrollState) {
newState.initialEventId = scrollState.focussedEvent;
newState.initialEventPixelOffset = scrollState.pixelOffset;
}
}
if (roomInfo.room_alias) {
console.log(
`Switching to room alias ${roomInfo.room_alias} at event ` +
newState.initialEventId,
roomInfo.event_id,
);
} else {
console.log(`Switching to room id ${roomInfo.room_id} at event ` +
newState.initialEventId,
roomInfo.event_id,
);
}
@ -680,7 +671,7 @@ module.exports = React.createClass({
}
}
if (roomInfo.event_id) {
if (roomInfo.event_id && roomInfo.highlighted) {
presentedId += "/" + roomInfo.event_id;
}
this.notifyNewScreen('room/' + presentedId);
@ -689,7 +680,7 @@ module.exports = React.createClass({
});
},
_setMxId: function() {
_setMxId: function(payload) {
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const close = Modal.createDialog(SetMxIdDialog, {
homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(),
@ -698,6 +689,11 @@ module.exports = React.createClass({
dis.dispatch({
action: 'cancel_after_sync_prepared',
});
if (payload.go_home_on_cancel) {
dis.dispatch({
action: 'view_home_page',
});
}
return;
}
this.onRegistered(credentials);
@ -778,6 +774,11 @@ module.exports = React.createClass({
}
dis.dispatch({
action: 'view_set_mxid',
// If the set_mxid dialog is cancelled, view /home because if the browser
// was pointing at /user/@someone:domain?action=chat, the URL needs to be
// reset so that they can revisit /user/.. // (and trigger
// `_chatCreateOrReuse` again)
go_home_on_cancel: true,
});
return;
}
@ -1137,6 +1138,10 @@ module.exports = React.createClass({
const payload = {
action: 'view_room',
event_id: eventId,
// If an event ID is given in the URL hash, notify RoomViewStore to mark
// it as highlighted, which will propagate to RoomView and highlight the
// associated EventTile.
highlighted: Boolean(eventId),
third_party_invite: thirdPartyInvite,
oob_data: oobData,
};
@ -1321,7 +1326,7 @@ module.exports = React.createClass({
});
} else {
dis.dispatch({
action: 'view_room_directory',
action: 'view_home_page',
});
}
},

View file

@ -83,36 +83,8 @@ module.exports = React.createClass({
// * invited us tovthe room
oobData: React.PropTypes.object,
// id of an event to jump to. If not given, will go to the end of the
// live timeline.
eventId: React.PropTypes.string,
// where to position the event given by eventId, in pixels from the
// bottom of the viewport. If not given, will try to put the event
// 1/3 of the way down the viewport.
eventPixelOffset: React.PropTypes.number,
// ID of an event to highlight. If undefined, no event will be highlighted.
// Typically this will either be the same as 'eventId', or undefined.
highlightedEventId: React.PropTypes.string,
// is the RightPanel collapsed?
collapsedRhs: React.PropTypes.bool,
// a map from room id to scroll state, which will be updated on unmount.
//
// If there is no special scroll state (ie, we are following the live
// timeline), the scroll state is null. Otherwise, it is an object with
// the following properties:
//
// focussedEvent: the ID of the 'focussed' event. Typically this is
// the last event fully visible in the viewport, though if we
// have done an explicit scroll to an explicit event, it will be
// that event.
//
// pixelOffset: the number of pixels the window is scrolled down
// from the focussedEvent.
scrollStateMap: React.PropTypes.object,
},
getInitialState: function() {
@ -123,6 +95,13 @@ module.exports = React.createClass({
roomLoading: true,
peekLoading: false,
// The event to be scrolled to initially
initialEventId: null,
// The offset in pixels from the event with which to scroll vertically
initialEventPixelOffset: null,
// Whether to highlight the event scrolled to
isInitialEventHighlighted: null,
forwardingEvent: null,
editingRoomSettings: false,
uploadingRoomSettings: false,
@ -182,13 +161,32 @@ module.exports = React.createClass({
if (this.unmounted) {
return;
}
this.setState({
const newState = {
roomId: RoomViewStore.getRoomId(),
roomAlias: RoomViewStore.getRoomAlias(),
roomLoading: RoomViewStore.isRoomLoading(),
roomLoadError: RoomViewStore.getRoomLoadError(),
joining: RoomViewStore.isJoining(),
}, () => {
initialEventId: RoomViewStore.getInitialEventId(),
initialEventPixelOffset: RoomViewStore.getInitialEventPixelOffset(),
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
};
// Clear the search results when clicking a search result (which changes the
// currently scrolled to event, this.state.initialEventId).
if (this.state.initialEventId !== newState.initialEventId) {
newState.searchResults = null;
}
// Store the scroll state for the previous room so that we can return to this
// position when viewing this room in future.
if (this.state.roomId !== newState.roomId) {
this._updateScrollMap(this.state.roomId);
}
this.setState(newState, () => {
// At this point, this.state.roomId could be null (e.g. the alias might not
// have been resolved yet) so anything called here must handle this case.
this._onHaveRoom();
this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId));
});
@ -295,13 +293,6 @@ module.exports = React.createClass({
}
},
componentWillReceiveProps: function(newProps) {
if (newProps.eventId != this.props.eventId) {
// when we change focussed event id, hide the search results.
this.setState({searchResults: null});
}
},
shouldComponentUpdate: function(nextProps, nextState) {
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
!ObjectUtils.shallowEqual(this.state, nextState));
@ -327,7 +318,7 @@ module.exports = React.createClass({
this.unmounted = true;
// update the scroll map before we get unmounted
this._updateScrollMap();
this._updateScrollMap(this.state.roomId);
if (this.refs.roomView) {
// disconnect the D&D event listeners from the room view. This
@ -611,6 +602,18 @@ module.exports = React.createClass({
});
},
_updateScrollMap(roomId) {
// No point updating scroll state if the room ID hasn't been resolved yet
if (!roomId) {
return;
}
dis.dispatch({
action: 'update_scroll_state',
room_id: roomId,
scroll_state: this._getScrollState(),
});
},
onRoom: function(room) {
if (!room || room.roomId !== this.state.roomId) {
return;
@ -1253,21 +1256,6 @@ module.exports = React.createClass({
}
},
// update scrollStateMap on unmount
_updateScrollMap: function() {
if (!this.state.room) {
// we were instantiated on a room alias and haven't yet joined the room.
return;
}
if (!this.props.scrollStateMap) return;
var roomId = this.state.room.roomId;
var state = this._getScrollState();
this.props.scrollStateMap[roomId] = state;
},
// get the current scroll position of the room, so that it can be
// restored when we switch back to it.
//
@ -1698,6 +1686,14 @@ module.exports = React.createClass({
hideMessagePanel = true;
}
const shouldHighlight = this.state.isInitialEventHighlighted;
let highlightedEventId = null;
if (this.state.forwardingEvent) {
highlightedEventId = this.state.forwardingEvent.getId();
} else if (shouldHighlight) {
highlightedEventId = this.state.initialEventId;
}
// console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
var messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef}
@ -1705,9 +1701,9 @@ module.exports = React.createClass({
manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
manageReadMarkers={true}
hidden={hideMessagePanel}
highlightedEventId={this.state.forwardingEvent ? this.state.forwardingEvent.getId() : this.props.highlightedEventId}
eventId={this.props.eventId}
eventPixelOffset={this.props.eventPixelOffset}
highlightedEventId={highlightedEventId}
eventId={this.state.initialEventId}
eventPixelOffset={this.state.initialEventPixelOffset}
onScroll={ this.onMessageListScroll }
onReadMarkerUpdated={ this._updateTopUnreadMessagesBar }
showUrlPreview = { this.state.showUrlPreview }

View file

@ -87,7 +87,27 @@ module.exports = React.createClass({
).then((data) => {
this.props.onLoggedIn(data);
}, (error) => {
this._setStateFromError(error, true);
let errorText;
// Some error strings only apply for logging in
const usingEmail = username.indexOf("@") > 0;
if (error.httpStatus == 400 && usingEmail) {
errorText = _t('This Home Server does not support login using email address.');
} else if (error.httpStatus === 401 || error.httpStatus === 403) {
errorText = _t('Incorrect username and/or password.');
} else {
// other errors, not specific to doing a password login
errorText = this._errorTextFromError(error);
}
this.setState({
errorText: errorText,
// 401 would be the sensible status code for 'incorrect password'
// but the login API gives a 403 https://matrix.org/jira/browse/SYN-744
// mentions this (although the bug is for UI auth which is not this)
// We treat both as an incorrect password
loginIncorrect: error.httpStatus === 401 || error.httpStatus == 403,
});
}).finally(() => {
this.setState({
busy: false
@ -110,7 +130,16 @@ module.exports = React.createClass({
this._loginLogic.loginAsGuest().then(function(data) {
self.props.onLoggedIn(data);
}, function(error) {
self._setStateFromError(error, true);
let errorText;
if (error.httpStatus === 403) {
errorText = _t("Guest access is disabled on this Home Server.");
} else {
errorText = self._errorTextFromError(error);
}
self.setState({
errorText: errorText,
loginIncorrect: false,
});
}).finally(function() {
self.setState({
busy: false
@ -183,31 +212,22 @@ module.exports = React.createClass({
currentFlow: self._getCurrentFlowStep(),
});
}, function(err) {
self._setStateFromError(err, false);
self.setState({
errorText: self._errorTextFromError(err),
loginIncorrect: false,
});
}).finally(function() {
self.setState({
busy: false,
});
});
}).done();
},
_getCurrentFlowStep: function() {
return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null;
},
_setStateFromError: function(err, isLoginAttempt) {
this.setState({
errorText: this._errorTextFromError(err),
// https://matrix.org/jira/browse/SYN-744
loginIncorrect: isLoginAttempt && (err.httpStatus == 401 || err.httpStatus == 403)
});
},
_errorTextFromError(err) {
if (err.friendlyText) {
return err.friendlyText;
}
let errCode = err.errcode;
if (!errCode && err.httpStatus) {
errCode = "HTTP " + err.httpStatus;
@ -219,8 +239,8 @@ module.exports = React.createClass({
if (err.cors === 'rejected') {
if (window.location.protocol === 'https:' &&
(this.state.enteredHomeserverUrl.startsWith("http:") ||
!this.state.enteredHomeserverUrl.startsWith("http")))
{
!this.state.enteredHomeserverUrl.startsWith("http"))
) {
errorText = <span>
{ _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
"Either use HTTPS or <a>enable unsafe scripts</a>.",
@ -228,10 +248,9 @@ module.exports = React.createClass({
(sub) => { return <a href="https://www.google.com/search?&q=enable%20unsafe%20scripts">{ sub }</a>; }
)}
</span>;
}
else {
} else {
errorText = <span>
{ _tJsx("Can't connect to homeserver - please check your connectivity and ensure your <a>homeserver's SSL certificate</a> is trusted.",
{ _tJsx("Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.",
/<a>(.*?)<\/a>/,
(sub) => { return <a href={this.state.enteredHomeserverUrl}>{ sub }</a>; }
)}