Merge remote-tracking branch 'origin/develop' into dbkr/e2e_backups
This commit is contained in:
commit
fa30707eae
38 changed files with 1067 additions and 593 deletions
|
@ -140,9 +140,9 @@ export default class BugReportDialog extends React.Component {
|
|||
"not contain messages.",
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
<p><b>
|
||||
{ _t(
|
||||
"Riot bugs are tracked on GitHub: <a>create a GitHub issue</a>.",
|
||||
"Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.",
|
||||
{},
|
||||
{
|
||||
a: (sub) => <a
|
||||
|
@ -153,13 +153,13 @@ export default class BugReportDialog extends React.Component {
|
|||
</a>,
|
||||
},
|
||||
) }
|
||||
</p>
|
||||
</b></p>
|
||||
<div className="mx_BugReportDialog_field_container">
|
||||
<label
|
||||
htmlFor="mx_BugReportDialog_issueUrl"
|
||||
className="mx_BugReportDialog_field_label"
|
||||
>
|
||||
{ _t("GitHub issue link:") }
|
||||
{ _t("What GitHub issue are these logs for?") }
|
||||
</label>
|
||||
<input
|
||||
id="mx_BugReportDialog_issueUrl"
|
||||
|
@ -167,7 +167,7 @@ export default class BugReportDialog extends React.Component {
|
|||
className="mx_BugReportDialog_field_input"
|
||||
onChange={this._onIssueUrlChange}
|
||||
value={this.state.issueUrl}
|
||||
placeholder="https://github.com/vector-im/riot-web/issues/1337"
|
||||
placeholder="https://github.com/vector-im/riot-web/issues/..."
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_BugReportDialog_field_container">
|
||||
|
|
32
src/components/views/dialogs/LazyLoadingResyncDialog.js
Normal file
32
src/components/views/dialogs/LazyLoadingResyncDialog.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2018 New Vector 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import QuestionDialog from './QuestionDialog';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default (props) => {
|
||||
const description =
|
||||
_t("Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!");
|
||||
|
||||
return (<QuestionDialog
|
||||
hasCancelButton={false}
|
||||
title={_t("Updating Riot")}
|
||||
description={<div>{description}</div>}
|
||||
button={_t("OK")}
|
||||
onFinished={props.onFinished}
|
||||
/>);
|
||||
};
|
|
@ -182,12 +182,16 @@ module.exports = React.createClass({
|
|||
});
|
||||
}
|
||||
const emailValid = email === '' || Email.looksValid(email);
|
||||
this.markFieldValid(field_id, emailValid, "RegistrationForm.ERR_EMAIL_INVALID");
|
||||
if (this._authStepIsRequired('m.login.email.identity') && (!emailValid || email === '')) {
|
||||
this.markFieldValid(field_id, false, "RegistrationForm.ERR_MISSING_EMAIL");
|
||||
} else this.markFieldValid(field_id, emailValid, "RegistrationForm.ERR_EMAIL_INVALID");
|
||||
break;
|
||||
case FIELD_PHONE_NUMBER:
|
||||
const phoneNumber = this.refs.phoneNumber ? this.refs.phoneNumber.value : '';
|
||||
const phoneNumberValid = phoneNumber === '' || phoneNumberLooksValid(phoneNumber);
|
||||
this.markFieldValid(field_id, phoneNumberValid, "RegistrationForm.ERR_PHONE_NUMBER_INVALID");
|
||||
if (this._authStepIsRequired('m.login.msisdn') && (!phoneNumberValid || phoneNumber === '')) {
|
||||
this.markFieldValid(field_id, false, "RegistrationForm.ERR_MISSING_PHONE_NUMBER");
|
||||
} else this.markFieldValid(field_id, phoneNumberValid, "RegistrationForm.ERR_PHONE_NUMBER_INVALID");
|
||||
break;
|
||||
case FIELD_USERNAME:
|
||||
// XXX: SPEC-1
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -97,18 +98,19 @@ module.exports = React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// save new canonical alias
|
||||
let oldCanonicalAlias = null;
|
||||
if (this.props.canonicalAliasEvent) {
|
||||
oldCanonicalAlias = this.props.canonicalAliasEvent.getContent().alias;
|
||||
}
|
||||
if (oldCanonicalAlias !== this.state.canonicalAlias) {
|
||||
|
||||
let newCanonicalAlias = this.state.canonicalAlias;
|
||||
|
||||
if (this.props.canSetCanonicalAlias && oldCanonicalAlias !== newCanonicalAlias) {
|
||||
console.log("AliasSettings: Updating canonical alias");
|
||||
promises = [Promise.all(promises).then(
|
||||
MatrixClientPeg.get().sendStateEvent(
|
||||
this.props.roomId, "m.room.canonical_alias", {
|
||||
alias: this.state.canonicalAlias,
|
||||
alias: newCanonicalAlias,
|
||||
}, "",
|
||||
),
|
||||
)];
|
||||
|
@ -145,6 +147,7 @@ module.exports = React.createClass({
|
|||
if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases
|
||||
|
||||
const localDomain = MatrixClientPeg.get().getDomain();
|
||||
if (!alias.includes(':')) alias += ':' + localDomain;
|
||||
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
|
||||
this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || [];
|
||||
this.state.domainToAliases[localDomain].push(alias);
|
||||
|
@ -161,11 +164,18 @@ module.exports = React.createClass({
|
|||
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.props.canonicalAlias) {
|
||||
this.setState({
|
||||
canonicalAlias: alias
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onLocalAliasChanged: function(alias, index) {
|
||||
if (alias === "") return; // hit the delete button to delete please
|
||||
const localDomain = MatrixClientPeg.get().getDomain();
|
||||
if (!alias.includes(':')) alias += ':' + localDomain;
|
||||
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
|
||||
this.state.domainToAliases[localDomain][index] = alias;
|
||||
} else {
|
||||
|
@ -184,10 +194,15 @@ module.exports = React.createClass({
|
|||
// promptly setState anyway, it's just about acceptable. The alternative
|
||||
// would be to arbitrarily deepcopy to a temp variable and then setState
|
||||
// that, but why bother when we can cut this corner.
|
||||
this.state.domainToAliases[localDomain].splice(index, 1);
|
||||
const alias = this.state.domainToAliases[localDomain].splice(index, 1);
|
||||
this.setState({
|
||||
domainToAliases: this.state.domainToAliases,
|
||||
});
|
||||
if (this.props.canonicalAlias === alias) {
|
||||
this.setState({
|
||||
canonicalAlias: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onCanonicalAliasChange: function(event) {
|
||||
|
@ -204,12 +219,14 @@ module.exports = React.createClass({
|
|||
|
||||
let canonical_alias_section;
|
||||
if (this.props.canSetCanonicalAlias) {
|
||||
let found = false;
|
||||
canonical_alias_section = (
|
||||
<select onChange={this.onCanonicalAliasChange} defaultValue={this.state.canonicalAlias}>
|
||||
<select onChange={this.onCanonicalAliasChange} value={this.state.canonicalAlias}>
|
||||
<option value="" key="unset">{ _t('not specified') }</option>
|
||||
{
|
||||
Object.keys(self.state.domainToAliases).map(function(domain, i) {
|
||||
return self.state.domainToAliases[domain].map(function(alias, j) {
|
||||
Object.keys(self.state.domainToAliases).map((domain, i) => {
|
||||
return self.state.domainToAliases[domain].map((alias, j) => {
|
||||
if (alias === this.state.canonicalAlias) found = true;
|
||||
return (
|
||||
<option value={alias} key={i + "_" + j}>
|
||||
{ alias }
|
||||
|
@ -218,6 +235,12 @@ module.exports = React.createClass({
|
|||
});
|
||||
})
|
||||
}
|
||||
{
|
||||
found || !this.stateCanonicalAlias ? '' :
|
||||
<option value={ this.state.canonicalAlias } key='arbitrary'>
|
||||
{ this.state.canonicalAlias }
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -47,6 +47,9 @@ const eventTileTypes = {
|
|||
};
|
||||
|
||||
const stateEventTileTypes = {
|
||||
'm.room.aliases': 'messages.TextualEvent',
|
||||
// 'm.room.aliases': 'messages.RoomAliasesEvent', // too complex
|
||||
'm.room.canonical_alias': 'messages.TextualEvent',
|
||||
'm.room.create': 'messages.RoomCreate',
|
||||
'm.room.member': 'messages.TextualEvent',
|
||||
'm.room.name': 'messages.TextualEvent',
|
||||
|
@ -58,7 +61,6 @@ const stateEventTileTypes = {
|
|||
'm.room.power_levels': 'messages.TextualEvent',
|
||||
'm.room.pinned_events': 'messages.TextualEvent',
|
||||
'm.room.server_acl': 'messages.TextualEvent',
|
||||
|
||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
||||
};
|
||||
|
||||
|
@ -484,7 +486,9 @@ module.exports = withMatrixClient(React.createClass({
|
|||
const eventType = this.props.mxEvent.getType();
|
||||
|
||||
// Info messages are basically information about commands processed on a room
|
||||
const isInfoMessage = (eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType != 'm.room.create');
|
||||
const isInfoMessage = (
|
||||
eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType != 'm.room.create'
|
||||
);
|
||||
|
||||
const tileHandler = getHandlerTile(this.props.mxEvent);
|
||||
// This shouldn't happen: the caller should check we support this type
|
||||
|
|
|
@ -34,18 +34,23 @@ module.exports = React.createClass({
|
|||
getInitialState: function() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli.hasLazyLoadMembersEnabled()) {
|
||||
return {loading: true};
|
||||
// show an empty list
|
||||
return this._getMembersState([]);
|
||||
} else {
|
||||
return this._getMembersState();
|
||||
return this._getMembersState(this.roomMembers());
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._mounted = false;
|
||||
this._mounted = true;
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (!cli.hasLazyLoadMembersEnabled()) {
|
||||
if (cli.hasLazyLoadMembersEnabled()) {
|
||||
this._showMembersAccordingToMembershipWithLL();
|
||||
cli.on("Room.myMembership", this.onMyMembership);
|
||||
} else {
|
||||
this._listenForMembersChanges();
|
||||
}
|
||||
cli.on("Room", this.onRoom); // invites & joining after peek
|
||||
const enablePresenceByHsUrl = SdkConfig.get()["enable_presence_by_hs_url"];
|
||||
const hsUrl = MatrixClientPeg.get().baseUrl;
|
||||
this._showPresence = true;
|
||||
|
@ -59,7 +64,6 @@ module.exports = React.createClass({
|
|||
cli.on("RoomState.members", this.onRoomStateMember);
|
||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||
cli.on("RoomState.events", this.onRoomStateEvent);
|
||||
cli.on("Room", this.onRoom); // invites
|
||||
// We listen for changes to the lastPresenceTs which is essentially
|
||||
// listening for all presence events (we display most of not all of
|
||||
// the information contained in presence events).
|
||||
|
@ -67,47 +71,50 @@ module.exports = React.createClass({
|
|||
// cli.on("Room.timeline", this.onRoomTimeline);
|
||||
},
|
||||
|
||||
componentDidMount: async function() {
|
||||
this._mounted = true;
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli.hasLazyLoadMembersEnabled()) {
|
||||
await this._waitForMembersToLoad();
|
||||
if (this._mounted) {
|
||||
this.setState(this._getMembersState());
|
||||
this._listenForMembersChanges();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._mounted = false;
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.removeListener("RoomState.members", this.onRoomStateMember);
|
||||
cli.removeListener("RoomMember.name", this.onRoomMemberName);
|
||||
cli.removeListener("Room.myMembership", this.onMyMembership);
|
||||
cli.removeListener("RoomState.events", this.onRoomStateEvent);
|
||||
cli.removeListener("Room", this.onRoom);
|
||||
cli.removeListener("User.lastPresenceTs", this.onUserLastPresenceTs);
|
||||
// cli.removeListener("Room.timeline", this.onRoomTimeline);
|
||||
}
|
||||
|
||||
// cancel any pending calls to the rate_limited_funcs
|
||||
this._updateList.cancelPendingCall();
|
||||
},
|
||||
|
||||
_waitForMembersToLoad: async function() {
|
||||
if (!this.props.roomId) return {};
|
||||
/**
|
||||
* If lazy loading is enabled, either:
|
||||
* show a spinner and load the members if the user is joined,
|
||||
* or show the members available so far if the user is invited
|
||||
*/
|
||||
_showMembersAccordingToMembershipWithLL: async function() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(this.props.roomId);
|
||||
if (room) {
|
||||
try {
|
||||
await room.loadMembersIfNeeded();
|
||||
} catch(ex) {/* already logged in RoomView */}
|
||||
if (cli.hasLazyLoadMembersEnabled()) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(this.props.roomId);
|
||||
const membership = room && room.getMyMembership();
|
||||
if (membership === "join") {
|
||||
this.setState({loading: true});
|
||||
try {
|
||||
await room.loadMembersIfNeeded();
|
||||
} catch (ex) {/* already logged in RoomView */}
|
||||
if (this._mounted) {
|
||||
this.setState(this._getMembersState(this.roomMembers()));
|
||||
this._listenForMembersChanges();
|
||||
}
|
||||
} else if (membership === "invite") {
|
||||
// show the members we've got when invited
|
||||
this.setState(this._getMembersState(this.roomMembers()));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getMembersState: function() {
|
||||
const members = this.roomMembers();
|
||||
_getMembersState: function(members) {
|
||||
// set the state after determining _showPresence to make sure it's
|
||||
// taken into account while rerendering
|
||||
return {
|
||||
|
@ -124,34 +131,6 @@ module.exports = React.createClass({
|
|||
};
|
||||
},
|
||||
|
||||
/*
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
||||
// ignore anything but real-time updates at the end of the room:
|
||||
// updates from pagination will happen when the paginate completes.
|
||||
if (toStartOfTimeline || !data || !data.liveEvent) return;
|
||||
|
||||
// treat any activity from a user as implicit presence to update the
|
||||
// ordering of the list whenever someone says something.
|
||||
// Except right now we're not tiebreaking "active now" users in this way
|
||||
// so don't bother for now.
|
||||
if (ev.getSender()) {
|
||||
// console.log("implicit presence from " + ev.getSender());
|
||||
|
||||
var tile = this.refs[ev.getSender()];
|
||||
if (tile) {
|
||||
// work around a race where you might have a room member object
|
||||
// before the user object exists. XXX: why does this ever happen?
|
||||
var all_members = room.currentState.members;
|
||||
var userId = ev.getSender();
|
||||
if (all_members[userId].user === null) {
|
||||
all_members[userId].user = MatrixClientPeg.get().getUser(userId);
|
||||
}
|
||||
this._updateList(); // reorder the membership list
|
||||
}
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
onUserLastPresenceTs(event, user) {
|
||||
// Attach a SINGLE listener for global presence changes then locate the
|
||||
// member tile and re-render it. This is more efficient than every tile
|
||||
|
@ -170,7 +149,13 @@ module.exports = React.createClass({
|
|||
// We listen for room events because when we accept an invite
|
||||
// we need to wait till the room is fully populated with state
|
||||
// before refreshing the member list else we get a stale list.
|
||||
this._updateList();
|
||||
this._showMembersAccordingToMembershipWithLL();
|
||||
},
|
||||
|
||||
onMyMembership: function(room, membership, oldMembership) {
|
||||
if (room.roomId === this.props.roomId && membership === "join") {
|
||||
this._showMembersAccordingToMembershipWithLL();
|
||||
}
|
||||
},
|
||||
|
||||
onRoomStateMember: function(ev, state, member) {
|
||||
|
@ -197,6 +182,7 @@ module.exports = React.createClass({
|
|||
_updateList: new rate_limited_func(function() {
|
||||
// console.log("Updating memberlist");
|
||||
const newState = {
|
||||
loading: false,
|
||||
members: this.roomMembers(),
|
||||
};
|
||||
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
|
||||
|
|
|
@ -273,6 +273,7 @@ module.exports = React.createClass({
|
|||
});
|
||||
|
||||
let name = this.state.roomName;
|
||||
if (name == undefined || name == null) name = '';
|
||||
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
||||
|
||||
let badgeContent;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue