Merge
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
commit
b5ed08eba2
93 changed files with 4188 additions and 653 deletions
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import Promise from 'bluebird';
|
||||
|
||||
const BULK_REQUEST_DEBOUNCE_MS = 200;
|
||||
|
@ -28,8 +29,9 @@ const GROUP_PROFILES_CACHE_BUST_MS = 1800000; // 30 mins
|
|||
/**
|
||||
* Stores data used by <Flair/>
|
||||
*/
|
||||
class FlairStore {
|
||||
class FlairStore extends EventEmitter {
|
||||
constructor(matrixClient) {
|
||||
super();
|
||||
this._matrixClient = matrixClient;
|
||||
this._userGroups = {
|
||||
// $userId: ['+group1:domain', '+group2:domain', ...]
|
||||
|
@ -152,19 +154,29 @@ class FlairStore {
|
|||
return this._groupProfiles[groupId];
|
||||
}
|
||||
|
||||
// No request yet, start one
|
||||
if (!this._groupProfilesPromise[groupId]) {
|
||||
this._groupProfilesPromise[groupId] = matrixClient.getGroupProfile(groupId);
|
||||
// A request is ongoing, wait for it to complete and return the group profile.
|
||||
if (this._groupProfilesPromise[groupId]) {
|
||||
try {
|
||||
await this._groupProfilesPromise[groupId];
|
||||
} catch (e) {
|
||||
// Don't log the error; this is done below
|
||||
return null;
|
||||
}
|
||||
return this._groupProfiles[groupId];
|
||||
}
|
||||
|
||||
// No request yet, start one
|
||||
console.log('FlairStore: Request group profile of ' + groupId);
|
||||
this._groupProfilesPromise[groupId] = matrixClient.getGroupProfile(groupId);
|
||||
|
||||
let profile;
|
||||
try {
|
||||
profile = await this._groupProfilesPromise[groupId];
|
||||
} catch (e) {
|
||||
console.log('Failed to get group profile for ' + groupId, e);
|
||||
console.log('FlairStore: Failed to get group profile for ' + groupId, e);
|
||||
// Don't retry, but allow a retry when the profile is next requested
|
||||
delete this._groupProfilesPromise[groupId];
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
this._groupProfiles[groupId] = {
|
||||
|
@ -175,12 +187,24 @@ class FlairStore {
|
|||
};
|
||||
delete this._groupProfilesPromise[groupId];
|
||||
|
||||
/// XXX: This is verging on recreating a third "Flux"-looking Store. We really
|
||||
/// should replace FlairStore with a Flux store and some async actions.
|
||||
console.log('FlairStore: Emit updateGroupProfile for ' + groupId);
|
||||
this.emit('updateGroupProfile');
|
||||
|
||||
setTimeout(() => {
|
||||
delete this._groupProfiles[groupId];
|
||||
this.refreshGroupProfile(matrixClient, groupId);
|
||||
}, GROUP_PROFILES_CACHE_BUST_MS);
|
||||
|
||||
return this._groupProfiles[groupId];
|
||||
}
|
||||
|
||||
refreshGroupProfile(matrixClient, groupId) {
|
||||
// Invalidate the cache
|
||||
delete this._groupProfiles[groupId];
|
||||
// Fetch new profile data, and cache it
|
||||
return this.getGroupProfileCached(matrixClient, groupId);
|
||||
}
|
||||
}
|
||||
|
||||
if (global.singletonFlairStore === undefined) {
|
||||
|
|
|
@ -27,6 +27,48 @@ function parseRoomsResponse(response) {
|
|||
return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
|
||||
}
|
||||
|
||||
// The number of ongoing group requests
|
||||
let ongoingRequestCount = 0;
|
||||
|
||||
// This has arbitrarily been set to a small number to lower the priority
|
||||
// of doing group-related requests because we care about other important
|
||||
// requests like hitting /sync.
|
||||
const LIMIT = 3; // Maximum number of ongoing group requests
|
||||
|
||||
// FIFO queue of functions to call in the backlog
|
||||
const backlogQueue = [
|
||||
// () => {...}
|
||||
];
|
||||
|
||||
// Pull from the FIFO queue
|
||||
function checkBacklog() {
|
||||
const item = backlogQueue.shift();
|
||||
if (typeof item === 'function') item();
|
||||
}
|
||||
|
||||
// Limit the maximum number of ongoing promises returned by fn to LIMIT and
|
||||
// use a FIFO queue to handle the backlog.
|
||||
function limitConcurrency(fn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const item = () => {
|
||||
ongoingRequestCount++;
|
||||
resolve();
|
||||
};
|
||||
if (ongoingRequestCount >= LIMIT) {
|
||||
// Enqueue this request for later execution
|
||||
backlogQueue.push(item);
|
||||
} else {
|
||||
item();
|
||||
}
|
||||
})
|
||||
.then(fn)
|
||||
.then((result) => {
|
||||
ongoingRequestCount--;
|
||||
checkBacklog();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -56,23 +98,24 @@ export default class GroupStore extends EventEmitter {
|
|||
this._fetchResourcePromise = {};
|
||||
this._resourceFetcher = {
|
||||
[GroupStore.STATE_KEY.Summary]: () => {
|
||||
return MatrixClientPeg.get()
|
||||
.getGroupSummary(this.groupId);
|
||||
return limitConcurrency(
|
||||
() => MatrixClientPeg.get().getGroupSummary(this.groupId),
|
||||
);
|
||||
},
|
||||
[GroupStore.STATE_KEY.GroupRooms]: () => {
|
||||
return MatrixClientPeg.get()
|
||||
.getGroupRooms(this.groupId)
|
||||
.then(parseRoomsResponse);
|
||||
return limitConcurrency(
|
||||
() => MatrixClientPeg.get().getGroupRooms(this.groupId).then(parseRoomsResponse),
|
||||
);
|
||||
},
|
||||
[GroupStore.STATE_KEY.GroupMembers]: () => {
|
||||
return MatrixClientPeg.get()
|
||||
.getGroupUsers(this.groupId)
|
||||
.then(parseMembersResponse);
|
||||
return limitConcurrency(
|
||||
() => MatrixClientPeg.get().getGroupUsers(this.groupId).then(parseMembersResponse),
|
||||
);
|
||||
},
|
||||
[GroupStore.STATE_KEY.GroupInvitedMembers]: () => {
|
||||
return MatrixClientPeg.get()
|
||||
.getGroupInvitedUsers(this.groupId)
|
||||
.then(parseMembersResponse);
|
||||
return limitConcurrency(
|
||||
() => MatrixClientPeg.get().getGroupInvitedUsers(this.groupId).then(parseMembersResponse),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@ import Unread from '../Unread';
|
|||
* the RoomList.
|
||||
*/
|
||||
class RoomListStore extends Store {
|
||||
|
||||
static _listOrders = {
|
||||
"m.favourite": "manual",
|
||||
"im.vector.fake.invite": "recent",
|
||||
"im.vector.fake.recent": "recent",
|
||||
"im.vector.fake.direct": "recent",
|
||||
"m.lowpriority": "recent",
|
||||
"im.vector.fake.archived": "recent",
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super(dis);
|
||||
|
||||
|
@ -68,6 +78,42 @@ class RoomListStore extends Store {
|
|||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.timeline': {
|
||||
if (!this._state.ready ||
|
||||
!payload.isLiveEvent ||
|
||||
!payload.isLiveUnfilteredRoomTimelineEvent ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
// When an event is decrypted, it could mean we need to reorder the room
|
||||
// list because we now know the type of the event.
|
||||
case 'MatrixActions.Event.decrypted': {
|
||||
// We may not have synced or done an initial generation of the lists
|
||||
if (!this._matrixClient || !this._state.ready) break;
|
||||
|
||||
const roomId = payload.event.getRoomId();
|
||||
|
||||
// We may have decrypted an event without a roomId (e.g to_device)
|
||||
if (!roomId) break;
|
||||
|
||||
const room = this._matrixClient.getRoom(roomId);
|
||||
|
||||
// We somehow decrypted an event for a room our client is unaware of
|
||||
if (!room) break;
|
||||
|
||||
const liveTimeline = room.getLiveTimeline();
|
||||
const eventTimeline = room.getTimelineForEvent(payload.event.getId());
|
||||
|
||||
// Either this event was not added to the live timeline (e.g. pagination)
|
||||
// or it doesn't affect the ordering of the room list.
|
||||
if (liveTimeline !== eventTimeline ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.accountData': {
|
||||
if (payload.event_type !== 'm.direct') break;
|
||||
this._generateRoomLists();
|
||||
|
@ -78,6 +124,14 @@ class RoomListStore extends Store {
|
|||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
// This could be a new room that we've been invited to, joined or created
|
||||
// we won't get a RoomMember.membership for these cases if we're not already
|
||||
// a member.
|
||||
case 'MatrixActions.Room': {
|
||||
if (!this._state.ready || !this._matrixClient.credentials.userId) break;
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
case 'RoomListActions.tagRoom.pending': {
|
||||
// XXX: we only show one optimistic update at any one time.
|
||||
// Ideally we should be making a list of in-flight requests
|
||||
|
@ -159,18 +213,9 @@ class RoomListStore extends Store {
|
|||
}
|
||||
});
|
||||
|
||||
const listOrders = {
|
||||
"m.favourite": "manual",
|
||||
"im.vector.fake.invite": "recent",
|
||||
"im.vector.fake.recent": "recent",
|
||||
"im.vector.fake.direct": "recent",
|
||||
"m.lowpriority": "recent",
|
||||
"im.vector.fake.archived": "recent",
|
||||
};
|
||||
|
||||
Object.keys(lists).forEach((listKey) => {
|
||||
let comparator;
|
||||
switch (listOrders[listKey]) {
|
||||
switch (RoomListStore._listOrders[listKey]) {
|
||||
case "recent":
|
||||
comparator = this._recentsComparator;
|
||||
break;
|
||||
|
@ -188,13 +233,17 @@ class RoomListStore extends Store {
|
|||
});
|
||||
}
|
||||
|
||||
_eventTriggersRecentReorder(ev) {
|
||||
return ev.getTs() && (
|
||||
Unread.eventTriggersUnreadCount(ev) ||
|
||||
ev.getSender() === this._matrixClient.credentials.userId
|
||||
);
|
||||
}
|
||||
|
||||
_tsOfNewestEvent(room) {
|
||||
for (let i = room.timeline.length - 1; i >= 0; --i) {
|
||||
const ev = room.timeline[i];
|
||||
if (ev.getTs() &&
|
||||
(Unread.eventTriggersUnreadCount(ev) ||
|
||||
(ev.getSender() === this._matrixClient.credentials.userId))
|
||||
) {
|
||||
if (this._eventTriggersRecentReorder(ev)) {
|
||||
return ev.getTs();
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +259,8 @@ class RoomListStore extends Store {
|
|||
}
|
||||
|
||||
_recentsComparator(roomA, roomB) {
|
||||
// XXX: We could use a cache here and update it when we see new
|
||||
// events that trigger a reorder
|
||||
return this._tsOfNewestEvent(roomB) - this._tsOfNewestEvent(roomA);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue