Merge pull request #1709 from matrix-org/luke/fix-group-store-redundant-requests

Fix group store redundant requests
This commit is contained in:
David Baker 2018-01-17 17:10:49 +00:00 committed by GitHub
commit bd81ed6587
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -19,6 +19,14 @@ import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
import FlairStore from './FlairStore';
import MatrixClientPeg from '../MatrixClientPeg';
function parseMembersResponse(response) {
return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember));
}
function parseRoomsResponse(response) {
return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
}
/**
* Stores the group summary for a room and provides an API to change it and
* other useful group APIs that may have an effect on the group summary.
@ -38,65 +46,68 @@ export default class GroupStore extends EventEmitter {
throw new Error('GroupStore needs a valid groupId to be created');
}
this.groupId = groupId;
this._summary = {};
this._rooms = [];
this._members = [];
this._invitedMembers = [];
this._state = {};
this._state[GroupStore.STATE_KEY.Summary] = {};
this._state[GroupStore.STATE_KEY.GroupRooms] = [];
this._state[GroupStore.STATE_KEY.GroupMembers] = [];
this._state[GroupStore.STATE_KEY.GroupInvitedMembers] = [];
this._ready = {};
this._fetchResourcePromise = {};
this._resourceFetcher = {
[GroupStore.STATE_KEY.Summary]: () => {
return MatrixClientPeg.get()
.getGroupSummary(this.groupId);
},
[GroupStore.STATE_KEY.GroupRooms]: () => {
return MatrixClientPeg.get()
.getGroupRooms(this.groupId)
.then(parseRoomsResponse);
},
[GroupStore.STATE_KEY.GroupMembers]: () => {
return MatrixClientPeg.get()
.getGroupUsers(this.groupId)
.then(parseMembersResponse);
},
[GroupStore.STATE_KEY.GroupInvitedMembers]: () => {
return MatrixClientPeg.get()
.getGroupInvitedUsers(this.groupId)
.then(parseMembersResponse);
},
};
this.on('error', (err) => {
console.error(`GroupStore for ${this.groupId} encountered error`, err);
});
}
_fetchMembers() {
MatrixClientPeg.get().getGroupUsers(this.groupId).then((result) => {
this._members = result.chunk.map((apiMember) => {
return groupMemberFromApiObject(apiMember);
});
this._ready[GroupStore.STATE_KEY.GroupMembers] = true;
this._notifyListeners();
}).catch((err) => {
console.error("Failed to get group member list: " + err);
this.emit('error', err);
});
_fetchResource(stateKey) {
// Ongoing request, ignore
if (this._fetchResourcePromise[stateKey]) return;
MatrixClientPeg.get().getGroupInvitedUsers(this.groupId).then((result) => {
this._invitedMembers = result.chunk.map((apiMember) => {
return groupMemberFromApiObject(apiMember);
});
this._ready[GroupStore.STATE_KEY.GroupInvitedMembers] = true;
const clientPromise = this._resourceFetcher[stateKey]();
// Indicate ongoing request
this._fetchResourcePromise[stateKey] = clientPromise;
clientPromise.then((result) => {
this._state[stateKey] = result;
this._ready[stateKey] = true;
this._notifyListeners();
}).catch((err) => {
// Invited users not visible to non-members
if (err.httpStatus === 403) {
if (stateKey === GroupStore.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
return;
}
console.error("Failed to get group invited member list: " + err);
this.emit('error', err);
});
}
_fetchSummary() {
MatrixClientPeg.get().getGroupSummary(this.groupId).then((resp) => {
this._summary = resp;
this._ready[GroupStore.STATE_KEY.Summary] = true;
this._notifyListeners();
}).catch((err) => {
console.error("Failed to get resource " + stateKey + ":" + err);
this.emit('error', err);
}).finally(() => {
// Indicate finished request, allow for future fetches
delete this._fetchResourcePromise[stateKey];
});
}
_fetchRooms() {
MatrixClientPeg.get().getGroupRooms(this.groupId).then((resp) => {
this._rooms = resp.chunk.map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
});
this._ready[GroupStore.STATE_KEY.GroupRooms] = true;
this._notifyListeners();
}).catch((err) => {
this.emit('error', err);
});
return clientPromise;
}
_notifyListeners() {
@ -108,10 +119,9 @@ export default class GroupStore extends EventEmitter {
* immediately triggers an update to send the current state of the
* store (which could be the initial state).
*
* XXX: This also causes a fetch of all group data, which effectively
* causes 4 separate HTTP requests. This is bad, we should at least
* deduplicate these in order to fix:
* https://github.com/vector-im/riot-web/issues/5901
* This also causes a fetch of all group data, which might cause
* 4 separate HTTP requests, but only said requests aren't already
* ongoing.
*
* @param {function} fn the function to call when the store updates.
* @return {Object} tok a registration "token" with a single
@ -123,9 +133,11 @@ export default class GroupStore extends EventEmitter {
this.on('update', fn);
// Call to set initial state (before fetching starts)
this.emit('update');
this._fetchSummary();
this._fetchRooms();
this._fetchMembers();
this._fetchResource(GroupStore.STATE_KEY.Summary);
this._fetchResource(GroupStore.STATE_KEY.GroupRooms);
this._fetchResource(GroupStore.STATE_KEY.GroupMembers);
this._fetchResource(GroupStore.STATE_KEY.GroupInvitedMembers);
// Similar to the Store of flux/utils, we return a "token" that
// can be used to unregister the listener.
@ -145,90 +157,94 @@ export default class GroupStore extends EventEmitter {
}
getSummary() {
return this._summary;
return this._state[GroupStore.STATE_KEY.Summary];
}
getGroupRooms() {
return this._rooms;
return this._state[GroupStore.STATE_KEY.GroupRooms];
}
getGroupMembers( ) {
return this._members;
getGroupMembers() {
return this._state[GroupStore.STATE_KEY.GroupMembers];
}
getGroupInvitedMembers( ) {
return this._invitedMembers;
getGroupInvitedMembers() {
return this._state[GroupStore.STATE_KEY.GroupInvitedMembers];
}
getGroupPublicity() {
return this._summary.user ? this._summary.user.is_publicised : null;
return this._state[GroupStore.STATE_KEY.Summary].user ?
this._state[GroupStore.STATE_KEY.Summary].user.is_publicised : null;
}
isUserPrivileged() {
return this._summary.user ? this._summary.user.is_privileged : null;
return this._state[GroupStore.STATE_KEY.Summary].user ?
this._state[GroupStore.STATE_KEY.Summary].user.is_privileged : null;
}
addRoomToGroup(roomId, isPublic) {
return MatrixClientPeg.get()
.addRoomToGroup(this.groupId, roomId, isPublic)
.then(this._fetchRooms.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
}
updateGroupRoomVisibility(roomId, isPublic) {
return MatrixClientPeg.get()
.updateGroupRoomVisibility(this.groupId, roomId, isPublic)
.then(this._fetchRooms.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
}
removeRoomFromGroup(roomId) {
return MatrixClientPeg.get()
.removeRoomFromGroup(this.groupId, roomId)
// Room might be in the summary, refresh just in case
.then(this._fetchSummary.bind(this))
.then(this._fetchRooms.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary))
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
}
inviteUserToGroup(userId) {
return MatrixClientPeg.get().inviteUserToGroup(this.groupId, userId)
.then(this._fetchMembers.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupInvitedMembers));
}
acceptGroupInvite() {
return MatrixClientPeg.get().acceptGroupInvite(this.groupId)
// The user might be able to see more rooms now
.then(this._fetchRooms.bind(this))
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms))
// The user should now appear as a member
.then(this._fetchMembers.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupMembers))
// The user should now not appear as an invited member
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupInvitedMembers));
}
addRoomToGroupSummary(roomId, categoryId) {
return MatrixClientPeg.get()
.addRoomToGroupSummary(this.groupId, roomId, categoryId)
.then(this._fetchSummary.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
}
addUserToGroupSummary(userId, roleId) {
return MatrixClientPeg.get()
.addUserToGroupSummary(this.groupId, userId, roleId)
.then(this._fetchSummary.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
}
removeRoomFromGroupSummary(roomId) {
return MatrixClientPeg.get()
.removeRoomFromGroupSummary(this.groupId, roomId)
.then(this._fetchSummary.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
}
removeUserFromGroupSummary(userId) {
return MatrixClientPeg.get()
.removeUserFromGroupSummary(this.groupId, userId)
.then(this._fetchSummary.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
}
setGroupPublicity(isPublished) {
return MatrixClientPeg.get()
.setGroupPublicity(this.groupId, isPublished)
.then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
.then(this._fetchSummary.bind(this));
.then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
}
}