mirror of
https://github.com/element-hq/element-web
synced 2024-11-23 01:35:49 +03:00
Merge pull request #2118 from matrix-org/bwindels/feature_lazyloading
Lazy loading of room members
This commit is contained in:
commit
cead4096d8
21 changed files with 191 additions and 137 deletions
|
@ -99,6 +99,10 @@ class MatrixClientPeg {
|
||||||
// the react sdk doesn't work without this, so don't allow
|
// the react sdk doesn't work without this, so don't allow
|
||||||
opts.pendingEventOrdering = "detached";
|
opts.pendingEventOrdering = "detached";
|
||||||
|
|
||||||
|
if (SettingsStore.isFeatureEnabled('feature_lazyloading')) {
|
||||||
|
opts.lazyLoadMembers = true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const promise = this.matrixClient.store.startup();
|
const promise = this.matrixClient.store.startup();
|
||||||
console.log(`MatrixClientPeg: waiting for MatrixClient store to initialise`);
|
console.log(`MatrixClientPeg: waiting for MatrixClient store to initialise`);
|
||||||
|
@ -115,7 +119,7 @@ class MatrixClientPeg {
|
||||||
MatrixActionCreators.start(this.matrixClient);
|
MatrixActionCreators.start(this.matrixClient);
|
||||||
|
|
||||||
console.log(`MatrixClientPeg: really starting MatrixClient`);
|
console.log(`MatrixClientPeg: really starting MatrixClient`);
|
||||||
this.get().startClient(opts);
|
await this.get().startClient(opts);
|
||||||
console.log(`MatrixClientPeg: MatrixClient started`);
|
console.log(`MatrixClientPeg: MatrixClient started`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,8 +194,7 @@ function _getDirectMessageRooms(addr) {
|
||||||
const rooms = dmRooms.filter((dmRoom) => {
|
const rooms = dmRooms.filter((dmRoom) => {
|
||||||
const room = MatrixClientPeg.get().getRoom(dmRoom);
|
const room = MatrixClientPeg.get().getRoom(dmRoom);
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
return room.getMyMembership() === 'join';
|
||||||
return me && me.membership == 'join';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return rooms;
|
return rooms;
|
||||||
|
|
52
src/Rooms.js
52
src/Rooms.js
|
@ -31,26 +31,26 @@ export function getDisplayAliasForRoom(room) {
|
||||||
* If the room contains only two members including the logged-in user,
|
* If the room contains only two members including the logged-in user,
|
||||||
* return the other one. Otherwise, return null.
|
* return the other one. Otherwise, return null.
|
||||||
*/
|
*/
|
||||||
export function getOnlyOtherMember(room, me) {
|
export function getOnlyOtherMember(room, myUserId) {
|
||||||
const joinedMembers = room.getJoinedMembers();
|
|
||||||
|
|
||||||
if (joinedMembers.length === 2) {
|
if (room.currentState.getJoinedMemberCount() === 2) {
|
||||||
return joinedMembers.filter(function(m) {
|
return room.getJoinedMembers().filter(function(m) {
|
||||||
return m.userId !== me.userId;
|
return m.userId !== myUserId;
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isConfCallRoom(room, me, conferenceHandler) {
|
function _isConfCallRoom(room, myUserId, conferenceHandler) {
|
||||||
if (!conferenceHandler) return false;
|
if (!conferenceHandler) return false;
|
||||||
|
|
||||||
if (me.membership != "join") {
|
const myMembership = room.getMyMembership();
|
||||||
|
if (myMembership != "join") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherMember = getOnlyOtherMember(room, me);
|
const otherMember = getOnlyOtherMember(room, myUserId);
|
||||||
if (otherMember === null) {
|
if (otherMember === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -68,29 +68,31 @@ const isConfCallRoomCache = {
|
||||||
// $roomId: bool
|
// $roomId: bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isConfCallRoom(room, me, conferenceHandler) {
|
export function isConfCallRoom(room, myUserId, conferenceHandler) {
|
||||||
if (isConfCallRoomCache[room.roomId] !== undefined) {
|
if (isConfCallRoomCache[room.roomId] !== undefined) {
|
||||||
return isConfCallRoomCache[room.roomId];
|
return isConfCallRoomCache[room.roomId];
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = _isConfCallRoom(room, me, conferenceHandler);
|
const result = _isConfCallRoom(room, myUserId, conferenceHandler);
|
||||||
|
|
||||||
isConfCallRoomCache[room.roomId] = result;
|
isConfCallRoomCache[room.roomId] = result;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function looksLikeDirectMessageRoom(room, me) {
|
export function looksLikeDirectMessageRoom(room, myUserId) {
|
||||||
if (me.membership == "join" || me.membership === "ban" ||
|
const myMembership = room.getMyMembership();
|
||||||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
|
const me = room.getMember(myUserId);
|
||||||
|
|
||||||
|
if (myMembership == "join" || myMembership === "ban" || (me && me.isKicked())) {
|
||||||
// Used to split rooms via tags
|
// Used to split rooms via tags
|
||||||
const tagNames = Object.keys(room.tags);
|
const tagNames = Object.keys(room.tags);
|
||||||
// Used for 1:1 direct chats
|
// Used for 1:1 direct chats
|
||||||
const members = room.currentState.getMembers();
|
|
||||||
|
|
||||||
// Show 1:1 chats in seperate "Direct Messages" section as long as they haven't
|
// Show 1:1 chats in seperate "Direct Messages" section as long as they haven't
|
||||||
// been moved to a different tag section
|
// been moved to a different tag section
|
||||||
if (members.length === 2 && !tagNames.length) {
|
const totalMemberCount = room.currentState.getJoinedMemberCount() +
|
||||||
|
room.currentState.getInvitedMemberCount();
|
||||||
|
if (totalMemberCount === 2 && !tagNames.length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,10 +102,10 @@ export function looksLikeDirectMessageRoom(room, me) {
|
||||||
export function guessAndSetDMRoom(room, isDirect) {
|
export function guessAndSetDMRoom(room, isDirect) {
|
||||||
let newTarget;
|
let newTarget;
|
||||||
if (isDirect) {
|
if (isDirect) {
|
||||||
const guessedTarget = guessDMRoomTarget(
|
const guessedUserId = guessDMRoomTargetId(
|
||||||
room, room.getMember(MatrixClientPeg.get().credentials.userId),
|
room, MatrixClientPeg.get().getUserId()
|
||||||
);
|
);
|
||||||
newTarget = guessedTarget.userId;
|
newTarget = guessedUserId;
|
||||||
} else {
|
} else {
|
||||||
newTarget = null;
|
newTarget = null;
|
||||||
}
|
}
|
||||||
|
@ -159,15 +161,15 @@ export function setDMRoom(roomId, userId) {
|
||||||
* Given a room, estimate which of its members is likely to
|
* Given a room, estimate which of its members is likely to
|
||||||
* be the target if the room were a DM room and return that user.
|
* be the target if the room were a DM room and return that user.
|
||||||
*/
|
*/
|
||||||
export function guessDMRoomTarget(room, me) {
|
function guessDMRoomTargetId(room, myUserId) {
|
||||||
let oldestTs;
|
let oldestTs;
|
||||||
let oldestUser;
|
let oldestUser;
|
||||||
|
|
||||||
// Pick the joined user who's been here longest (and isn't us),
|
// Pick the joined user who's been here longest (and isn't us),
|
||||||
for (const user of room.getJoinedMembers()) {
|
for (const user of room.getJoinedMembers()) {
|
||||||
if (user.userId == me.userId) continue;
|
if (user.userId == myUserId) continue;
|
||||||
|
|
||||||
if (oldestTs === undefined || user.events.member.getTs() < oldestTs) {
|
if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) {
|
||||||
oldestUser = user;
|
oldestUser = user;
|
||||||
oldestTs = user.events.member.getTs();
|
oldestTs = user.events.member.getTs();
|
||||||
}
|
}
|
||||||
|
@ -176,14 +178,14 @@ export function guessDMRoomTarget(room, me) {
|
||||||
|
|
||||||
// if there are no joined members other than us, use the oldest member
|
// if there are no joined members other than us, use the oldest member
|
||||||
for (const user of room.currentState.getMembers()) {
|
for (const user of room.currentState.getMembers()) {
|
||||||
if (user.userId == me.userId) continue;
|
if (user.userId == myUserId) continue;
|
||||||
|
|
||||||
if (oldestTs === undefined || user.events.member.getTs() < oldestTs) {
|
if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) {
|
||||||
oldestUser = user;
|
oldestUser = user;
|
||||||
oldestTs = user.events.member.getTs();
|
oldestTs = user.events.member.getTs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldestUser === undefined) return me;
|
if (oldestUser === undefined) return myUserId;
|
||||||
return oldestUser;
|
return oldestUser;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
|
||||||
preset: "private_chat",
|
preset: "private_chat",
|
||||||
invite: [this.confUserId]
|
invite: [this.confUserId]
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
return new Room(res.room_id);
|
return new Room(res.room_id, null, client.getUserId());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -309,9 +309,21 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (room) {
|
} else if (room) {
|
||||||
|
//viewing a previously joined room, try to lazy load members
|
||||||
|
|
||||||
// Stop peeking because we have joined this room previously
|
// Stop peeking because we have joined this room previously
|
||||||
MatrixClientPeg.get().stopPeeking();
|
MatrixClientPeg.get().stopPeeking();
|
||||||
this.setState({isPeeking: false});
|
this.setState({isPeeking: false});
|
||||||
|
|
||||||
|
// lazy load members if enabled
|
||||||
|
if (SettingsStore.isFeatureEnabled('feature_lazyloading')) {
|
||||||
|
room.loadMembersIfNeeded().catch((err) => {
|
||||||
|
const errorMessage = `Fetching room members for ${this.roomId} failed.` +
|
||||||
|
" Room members will appear incomplete.";
|
||||||
|
console.error(errorMessage);
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -746,40 +758,13 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateDMState() {
|
_updateDMState() {
|
||||||
const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId);
|
const room = this.state.room;
|
||||||
if (!me || me.membership !== "join") {
|
if (room.getMyMembership() != "join") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const dmInviter = room.getDMInviter();
|
||||||
// The user may have accepted an invite with is_direct set
|
if (dmInviter) {
|
||||||
if (me.events.member.getPrevContent().membership === "invite" &&
|
Rooms.setDMRoom(room.roomId, dmInviter);
|
||||||
me.events.member.getPrevContent().is_direct
|
|
||||||
) {
|
|
||||||
// This is a DM with the sender of the invite event (which we assume
|
|
||||||
// preceded the join event)
|
|
||||||
Rooms.setDMRoom(
|
|
||||||
this.state.room.roomId,
|
|
||||||
me.events.member.getUnsigned().prev_sender,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invitedMembers = this.state.room.getMembersWithMembership("invite");
|
|
||||||
const joinedMembers = this.state.room.getMembersWithMembership("join");
|
|
||||||
|
|
||||||
// There must be one invited member and one joined member
|
|
||||||
if (invitedMembers.length !== 1 || joinedMembers.length !== 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user may have sent an invite with is_direct sent
|
|
||||||
const other = invitedMembers[0];
|
|
||||||
if (other &&
|
|
||||||
other.membership === "invite" &&
|
|
||||||
other.events.member.getContent().is_direct
|
|
||||||
) {
|
|
||||||
Rooms.setDMRoom(this.state.room.roomId, other.userId);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -844,8 +844,16 @@ module.exports = React.createClass({
|
||||||
SettingsStore.getLabsFeatures().forEach((featureId) => {
|
SettingsStore.getLabsFeatures().forEach((featureId) => {
|
||||||
// TODO: this ought to be a separate component so that we don't need
|
// TODO: this ought to be a separate component so that we don't need
|
||||||
// to rebind the onChange each time we render
|
// to rebind the onChange each time we render
|
||||||
const onChange = (e) => {
|
const onChange = async (e) => {
|
||||||
SettingsStore.setFeatureEnabled(featureId, e.target.checked);
|
const checked = e.target.checked;
|
||||||
|
if (featureId === "feature_lazyloading") {
|
||||||
|
const confirmed = await this._onLazyLoadChanging(checked);
|
||||||
|
if (!confirmed) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await SettingsStore.setFeatureEnabled(featureId, checked);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -855,7 +863,7 @@ module.exports = React.createClass({
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id={featureId}
|
id={featureId}
|
||||||
name={featureId}
|
name={featureId}
|
||||||
defaultChecked={SettingsStore.isFeatureEnabled(featureId)}
|
checked={SettingsStore.isFeatureEnabled(featureId)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={featureId}>{ SettingsStore.getDisplayName(featureId) }</label>
|
<label htmlFor={featureId}>{ SettingsStore.getDisplayName(featureId) }</label>
|
||||||
|
@ -878,6 +886,30 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onLazyLoadChanging: async function(enabling) {
|
||||||
|
// don't prevent turning LL off when not supported
|
||||||
|
if (enabling) {
|
||||||
|
const supported = await MatrixClientPeg.get().doesServerSupportLazyLoading();
|
||||||
|
if (!supported) {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
Modal.createDialog(QuestionDialog, {
|
||||||
|
title: _t("Lazy loading members not supported"),
|
||||||
|
description:
|
||||||
|
<div>
|
||||||
|
{ _t("Lazy loading is not supported by your " +
|
||||||
|
"current homeserver.") }
|
||||||
|
</div>,
|
||||||
|
button: _t("OK"),
|
||||||
|
onFinished: resolve,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
_renderDeactivateAccount: function() {
|
_renderDeactivateAccount: function() {
|
||||||
return <div>
|
return <div>
|
||||||
<h3>{ _t("Deactivate Account") }</h3>
|
<h3>{ _t("Deactivate Account") }</h3>
|
||||||
|
|
|
@ -54,8 +54,8 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
for (const roomId of dmRooms) {
|
for (const roomId of dmRooms) {
|
||||||
const room = client.getRoom(roomId);
|
const room = client.getRoom(roomId);
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = room.getMember(client.credentials.userId);
|
const isInvite = room.getMyMembership() === "invite";
|
||||||
const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === "invite";
|
const highlight = room.getUnreadNotificationCount('highlight') > 0 || isInvite;
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<RoomTile key={room.roomId} room={room}
|
<RoomTile key={room.roomId} room={room}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
|
@ -63,7 +63,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
selected={false}
|
selected={false}
|
||||||
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
isInvite={me.membership === "invite"}
|
isInvite={isInvite}
|
||||||
onClick={this.onRoomTileClick}
|
onClick={this.onRoomTileClick}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -187,6 +187,9 @@ const Pill = React.createClass({
|
||||||
getContent: () => {
|
getContent: () => {
|
||||||
return {avatar_url: resp.avatar_url};
|
return {avatar_url: resp.avatar_url};
|
||||||
},
|
},
|
||||||
|
getDirectionalContent: function() {
|
||||||
|
return this.getContent();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
this.setState({member});
|
this.setState({member});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -598,7 +598,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
|
|
||||||
onMemberAvatarClick: function() {
|
onMemberAvatarClick: function() {
|
||||||
const member = this.props.member;
|
const member = this.props.member;
|
||||||
const avatarUrl = member.user ? member.user.avatarUrl : member.events.member.getContent().avatar_url;
|
const avatarUrl = member.getMxcAvatarUrl();
|
||||||
if (!avatarUrl) return;
|
if (!avatarUrl) return;
|
||||||
|
|
||||||
const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
|
const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
|
||||||
|
@ -774,15 +774,15 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
for (const roomId of dmRooms) {
|
for (const roomId of dmRooms) {
|
||||||
const room = this.props.matrixClient.getRoom(roomId);
|
const room = this.props.matrixClient.getRoom(roomId);
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
const myMembership = room.getMyMembership();
|
||||||
|
|
||||||
// not a DM room if we have are not joined
|
// not a DM room if we have are not joined
|
||||||
if (!me.membership || me.membership !== 'join') continue;
|
if (myMembership !== 'join') continue;
|
||||||
// not a DM room if they are not joined
|
|
||||||
const them = this.props.member;
|
const them = this.props.member;
|
||||||
|
// not a DM room if they are not joined
|
||||||
if (!them.membership || them.membership !== 'join') continue;
|
if (!them.membership || them.membership !== 'join') continue;
|
||||||
|
|
||||||
const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === 'invite';
|
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
||||||
|
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<RoomTile key={room.roomId} room={room}
|
<RoomTile key={room.roomId} room={room}
|
||||||
|
@ -791,7 +791,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
selected={false}
|
selected={false}
|
||||||
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
isInvite={me.membership === "invite"}
|
isInvite={false}
|
||||||
onClick={this.onRoomTileClick}
|
onClick={this.onRoomTileClick}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -347,8 +347,8 @@ module.exports = React.createClass({
|
||||||
if (!taggedRoom) {
|
if (!taggedRoom) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const me = taggedRoom.getMember(MatrixClientPeg.get().credentials.userId);
|
const myUserId = MatrixClientPeg.get().getUserId();
|
||||||
if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(taggedRoom, me, this.props.ConferenceHandler)) {
|
if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(taggedRoom, myUserId, this.props.ConferenceHandler)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,15 +98,11 @@ module.exports = React.createClass({
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const myMember = this.props.room ? this.props.room.currentState.members[
|
const myMember = this.props.room ?
|
||||||
MatrixClientPeg.get().credentials.userId
|
this.props.room.getMember(MatrixClientPeg.get().getUserId()) :
|
||||||
] : null;
|
null;
|
||||||
const kicked = (
|
const kicked = myMember && myMember.isKicked();
|
||||||
myMember &&
|
const banned = myMember && myMember && myMember.membership == 'ban';
|
||||||
myMember.membership == 'leave' &&
|
|
||||||
myMember.events.member.getSender() != MatrixClientPeg.get().credentials.userId
|
|
||||||
);
|
|
||||||
const banned = myMember && myMember.membership == 'ban';
|
|
||||||
|
|
||||||
if (this.props.inviterName) {
|
if (this.props.inviterName) {
|
||||||
let emailMatchBlock;
|
let emailMatchBlock;
|
||||||
|
|
|
@ -243,9 +243,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
const isInvite = this.props.room.getMyMembership() === "invite";
|
||||||
const me = this.props.room.currentState.members[myUserId];
|
|
||||||
|
|
||||||
const notificationCount = this.state.notificationCount;
|
const notificationCount = this.state.notificationCount;
|
||||||
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
||||||
|
|
||||||
|
@ -259,7 +257,7 @@ module.exports = React.createClass({
|
||||||
'mx_RoomTile_unread': this.props.unread,
|
'mx_RoomTile_unread': this.props.unread,
|
||||||
'mx_RoomTile_unreadNotify': notifBadges,
|
'mx_RoomTile_unreadNotify': notifBadges,
|
||||||
'mx_RoomTile_highlight': mentionBadges,
|
'mx_RoomTile_highlight': mentionBadges,
|
||||||
'mx_RoomTile_invited': (me && me.membership === 'invite'),
|
'mx_RoomTile_invited': isInvite,
|
||||||
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
|
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
|
||||||
'mx_RoomTile_noBadges': !badges,
|
'mx_RoomTile_noBadges': !badges,
|
||||||
'mx_RoomTile_transparent': this.props.transparent,
|
'mx_RoomTile_transparent': this.props.transparent,
|
||||||
|
|
|
@ -43,27 +43,26 @@ export function markAllDevicesKnown(matrixClient, devices) {
|
||||||
* @return {Promise} A promise which resolves to a map userId->deviceId->{@link
|
* @return {Promise} A promise which resolves to a map userId->deviceId->{@link
|
||||||
* module:crypto~DeviceInfo|DeviceInfo}.
|
* module:crypto~DeviceInfo|DeviceInfo}.
|
||||||
*/
|
*/
|
||||||
export function getUnknownDevicesForRoom(matrixClient, room) {
|
export async function getUnknownDevicesForRoom(matrixClient, room) {
|
||||||
const roomMembers = room.getEncryptionTargetMembers().map((m) => {
|
const roomMembers = await room.getEncryptionTargetMembers().map((m) => {
|
||||||
return m.userId;
|
return m.userId;
|
||||||
});
|
});
|
||||||
return matrixClient.downloadKeys(roomMembers, false).then((devices) => {
|
const devices = await matrixClient.downloadKeys(roomMembers, false);
|
||||||
const unknownDevices = {};
|
const unknownDevices = {};
|
||||||
// This is all devices in this room, so find the unknown ones.
|
// This is all devices in this room, so find the unknown ones.
|
||||||
Object.keys(devices).forEach((userId) => {
|
Object.keys(devices).forEach((userId) => {
|
||||||
Object.keys(devices[userId]).map((deviceId) => {
|
Object.keys(devices[userId]).map((deviceId) => {
|
||||||
const device = devices[userId][deviceId];
|
const device = devices[userId][deviceId];
|
||||||
|
|
||||||
if (device.isUnverified() && !device.isKnown()) {
|
if (device.isUnverified() && !device.isKnown()) {
|
||||||
if (unknownDevices[userId] === undefined) {
|
if (unknownDevices[userId] === undefined) {
|
||||||
unknownDevices[userId] = {};
|
unknownDevices[userId] = {};
|
||||||
}
|
|
||||||
unknownDevices[userId][deviceId] = device;
|
|
||||||
}
|
}
|
||||||
});
|
unknownDevices[userId][deviceId] = device;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return unknownDevices;
|
|
||||||
});
|
});
|
||||||
|
return unknownDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusComposer() {
|
function focusComposer() {
|
||||||
|
|
|
@ -1231,5 +1231,8 @@
|
||||||
"Import": "Import",
|
"Import": "Import",
|
||||||
"Failed to set direct chat tag": "Failed to set direct chat tag",
|
"Failed to set direct chat tag": "Failed to set direct chat tag",
|
||||||
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
||||||
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"
|
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
|
||||||
|
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
|
||||||
|
"Lazy loading members not supported": "Lazy load members not supported",
|
||||||
|
"Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver."
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
NotificationBodyEnabledController,
|
NotificationBodyEnabledController,
|
||||||
NotificationsEnabledController,
|
NotificationsEnabledController,
|
||||||
} from "./controllers/NotificationControllers";
|
} from "./controllers/NotificationControllers";
|
||||||
|
import LazyLoadingController from "./controllers/LazyLoadingController";
|
||||||
|
|
||||||
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
||||||
const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config'];
|
const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config'];
|
||||||
|
@ -83,6 +83,13 @@ export const SETTINGS = {
|
||||||
supportedLevels: LEVELS_FEATURE,
|
supportedLevels: LEVELS_FEATURE,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
"feature_lazyloading": {
|
||||||
|
isFeature: true,
|
||||||
|
displayName: _td("Increase performance by only loading room members on first view"),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
controller: new LazyLoadingController(),
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"MessageComposerInput.dontSuggestEmoji": {
|
"MessageComposerInput.dontSuggestEmoji": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
displayName: _td('Disable Emoji suggestions while typing'),
|
displayName: _td('Disable Emoji suggestions while typing'),
|
||||||
|
|
|
@ -248,7 +248,7 @@ export default class SettingsStore {
|
||||||
if (actualValue !== undefined && actualValue !== null) return actualValue;
|
if (actualValue !== undefined && actualValue !== null) return actualValue;
|
||||||
return calculatedValue;
|
return calculatedValue;
|
||||||
}
|
}
|
||||||
|
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
|
||||||
/**
|
/**
|
||||||
* Sets the value for a setting. The room ID is optional if the setting is not being
|
* Sets the value for a setting. The room ID is optional if the setting is not being
|
||||||
* set for a particular room, otherwise it should be supplied. The value may be null
|
* set for a particular room, otherwise it should be supplied. The value may be null
|
||||||
|
@ -260,7 +260,8 @@ export default class SettingsStore {
|
||||||
* @param {*} value The new value of the setting, may be null.
|
* @param {*} value The new value of the setting, may be null.
|
||||||
* @return {Promise} Resolves when the setting has been changed.
|
* @return {Promise} Resolves when the setting has been changed.
|
||||||
*/
|
*/
|
||||||
static setValue(settingName, roomId, level, value) {
|
/* eslint-enable valid-jsdoc */
|
||||||
|
static async setValue(settingName, roomId, level, value) {
|
||||||
// Verify that the setting is actually a setting
|
// Verify that the setting is actually a setting
|
||||||
if (!SETTINGS[settingName]) {
|
if (!SETTINGS[settingName]) {
|
||||||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||||
|
@ -275,11 +276,12 @@ export default class SettingsStore {
|
||||||
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
|
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.setValue(settingName, roomId, value).then(() => {
|
await handler.setValue(settingName, roomId, value);
|
||||||
const controller = SETTINGS[settingName].controller;
|
|
||||||
if (!controller) return;
|
const controller = SETTINGS[settingName].controller;
|
||||||
|
if (controller) {
|
||||||
controller.onChange(level, roomId, value);
|
controller.onChange(level, roomId, value);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
29
src/settings/controllers/LazyLoadingController.js
Normal file
29
src/settings/controllers/LazyLoadingController.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 New Vector
|
||||||
|
|
||||||
|
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 SettingController from "./SettingController";
|
||||||
|
import MatrixClientPeg from "../../MatrixClientPeg";
|
||||||
|
import PlatformPeg from "../../PlatformPeg";
|
||||||
|
|
||||||
|
export default class LazyLoadingController extends SettingController {
|
||||||
|
async onChange(level, roomId, newValue) {
|
||||||
|
if (!PlatformPeg.get()) return;
|
||||||
|
|
||||||
|
MatrixClientPeg.get().stopClient();
|
||||||
|
await MatrixClientPeg.get().store.deleteAllData();
|
||||||
|
PlatformPeg.get().reload();
|
||||||
|
}
|
||||||
|
}
|
|
@ -175,13 +175,13 @@ class RoomListStore extends Store {
|
||||||
if (!this._matrixClient) return;
|
if (!this._matrixClient) return;
|
||||||
|
|
||||||
this._matrixClient.getRooms().forEach((room, index) => {
|
this._matrixClient.getRooms().forEach((room, index) => {
|
||||||
const me = room.getMember(this._matrixClient.credentials.userId);
|
const myUserId = this._matrixClient.getUserId();
|
||||||
if (!me) return;
|
const membership = room.getMyMembership();
|
||||||
|
const me = room.getMember(myUserId);
|
||||||
|
|
||||||
if (me.membership == "invite") {
|
if (membership == "invite") {
|
||||||
lists["im.vector.fake.invite"].push(room);
|
lists["im.vector.fake.invite"].push(room);
|
||||||
} else if (me.membership == "join" || me.membership === "ban" ||
|
} else if (membership == "join" || membership === "ban" || (me && me.isKicked())) {
|
||||||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
|
|
||||||
// Used to split rooms via tags
|
// Used to split rooms via tags
|
||||||
let tagNames = Object.keys(room.tags);
|
let tagNames = Object.keys(room.tags);
|
||||||
|
|
||||||
|
@ -213,10 +213,10 @@ class RoomListStore extends Store {
|
||||||
} else {
|
} else {
|
||||||
lists["im.vector.fake.recent"].push(room);
|
lists["im.vector.fake.recent"].push(room);
|
||||||
}
|
}
|
||||||
} else if (me.membership === "leave") {
|
} else if (membership === "leave") {
|
||||||
lists["im.vector.fake.archived"].push(room);
|
lists["im.vector.fake.archived"].push(room);
|
||||||
} else {
|
} else {
|
||||||
console.error("unrecognised membership: " + me.membership + " - this should never happen");
|
console.error("unrecognised membership: " + membership + " - this should never happen");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -97,14 +97,7 @@ export default class DMRoomMap {
|
||||||
// no entry? if the room is an invite, look for the is_direct hint.
|
// no entry? if the room is an invite, look for the is_direct hint.
|
||||||
const room = this.matrixClient.getRoom(roomId);
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = room.getMember(this.matrixClient.credentials.userId);
|
return room.getDMInviter();
|
||||||
if (me.membership == 'invite') {
|
|
||||||
// The 'direct' hihnt is there, so declare that this is a DM room for
|
|
||||||
// whoever invited us.
|
|
||||||
if (me.events.member.getContent().is_direct) {
|
|
||||||
return me.events.member.getSender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.roomToUser[roomId];
|
return this.roomToUser[roomId];
|
||||||
|
|
|
@ -77,8 +77,7 @@ export default class WidgetUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const member = room.getMember(me);
|
if (room.getMyMembership() !== "join") {
|
||||||
if (!member || member.membership !== "join") {
|
|
||||||
console.warn(`User ${me} is not in room ${roomId}`);
|
console.warn(`User ${me} is not in room ${roomId}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,21 +14,22 @@ import dis from '../../../../src/dispatcher';
|
||||||
import DMRoomMap from '../../../../src/utils/DMRoomMap.js';
|
import DMRoomMap from '../../../../src/utils/DMRoomMap.js';
|
||||||
import GroupStore from '../../../../src/stores/GroupStore.js';
|
import GroupStore from '../../../../src/stores/GroupStore.js';
|
||||||
|
|
||||||
import { Room, RoomMember } from 'matrix-js-sdk';
|
import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk';
|
||||||
|
|
||||||
function generateRoomId() {
|
function generateRoomId() {
|
||||||
return '!' + Math.random().toString().slice(2, 10) + ':domain';
|
return '!' + Math.random().toString().slice(2, 10) + ':domain';
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRoom(opts) {
|
|
||||||
const room = new Room(generateRoomId());
|
|
||||||
if (opts) {
|
|
||||||
Object.assign(room, opts);
|
|
||||||
}
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('RoomList', () => {
|
describe('RoomList', () => {
|
||||||
|
function createRoom(opts) {
|
||||||
|
const room = new Room(generateRoomId(), null, client.getUserId());
|
||||||
|
if (opts) {
|
||||||
|
Object.assign(room, opts);
|
||||||
|
}
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
let parentDiv = null;
|
let parentDiv = null;
|
||||||
let sandbox = null;
|
let sandbox = null;
|
||||||
let client = null;
|
let client = null;
|
||||||
|
@ -48,6 +49,8 @@ describe('RoomList', () => {
|
||||||
sandbox = TestUtils.stubClient(sandbox);
|
sandbox = TestUtils.stubClient(sandbox);
|
||||||
client = MatrixClientPeg.get();
|
client = MatrixClientPeg.get();
|
||||||
client.credentials = {userId: myUserId};
|
client.credentials = {userId: myUserId};
|
||||||
|
//revert this to prototype method as the test-utils monkey-patches this to return a hardcoded value
|
||||||
|
client.getUserId = MatrixClient.prototype.getUserId;
|
||||||
|
|
||||||
clock = lolex.install();
|
clock = lolex.install();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue