[Gekidou] handlers perf (#6365)

* Improve handlers to only create/update needed recors and add loading indicator while posts are being loaded

* Tweak team handlers

* check for delete_at as falsey

* more handler tweaks

* Preference handler

* Small refactor on category body

* Add WS reconnect FETCHING_POSTS event

* Fix handleChannelViewedEvent when channel screen is not visible

* Replace blinking dot with spinner

* update snapshots to reflect the new spinner
This commit is contained in:
Elias Nahum
2022-06-09 13:09:54 -04:00
committed by GitHub
parent e26d77811b
commit cfef5332c9
18 changed files with 467 additions and 69 deletions

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Database, Q} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import {
transformCategoryChannelRecord,
@@ -43,7 +45,33 @@ const CategoryHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: categories, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: categories, key: 'id'}) as Category[];
const ids = uniqueRaws.map((c) => c.id);
const db: Database = this.database;
const exists = await db.get<CategoryModel>(CATEGORY).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const categoryMap = new Map<String, CategoryModel>(exists.map((c) => [c.id, c]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: Category[], c) => {
const e = categoryMap.get(c.id);
if (!e) {
res.push(c);
} else if (
e.displayName !== c.display_name ||
e.muted !== c.muted ||
e.sortOrder !== (c.sort_order / 10) ||
e.sorting !== (c.sorting || 'recent') ||
e.teamId !== c.team_id ||
e.type !== c.type
) {
res.push(c);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Database, Q} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import {
buildMyChannelKey,
@@ -57,7 +59,30 @@ const ChannelHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: channels, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: channels, key: 'id'}) as Channel[];
const keys = uniqueRaws.map((c) => c.id);
const db: Database = this.database;
const existing = await db.get<ChannelModel>(CHANNEL).query(
Q.where('id', Q.oneOf(keys)),
).fetch();
const channelMap = new Map<string, ChannelModel>(existing.map((c) => [c.id, c]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: Channel[], c) => {
const e = channelMap.get(c.id);
if (!e) {
res.push(c);
return res;
}
if (e.updateAt !== c.update_at || e.deleteAt !== c.delete_at || c.fake) {
res.push(c);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',
@@ -86,7 +111,36 @@ const ChannelHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: settings, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: settings, key: 'id'}) as ChannelMembership[];
const keys = uniqueRaws.map((c) => c.channel_id);
const db: Database = this.database;
const existing = await db.get<MyChannelSettingsModel>(MY_CHANNEL_SETTINGS).query(
Q.where('id', Q.oneOf(keys)),
).fetch();
const channelMap = new Map<string, MyChannelSettingsModel>(existing.map((c) => [c.id, c]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: ChannelMembership[], c) => {
const e = channelMap.get(c.channel_id);
if (!e) {
res.push(c);
return res;
}
try {
const current = JSON.stringify(e.notifyProps);
const newer = JSON.stringify(c.notify_props);
if (current !== newer) {
res.push(c);
}
} catch {
//skip;
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',
@@ -116,10 +170,39 @@ const ChannelHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({
const uniqueRaws = getUniqueRawsBy({
raws: channelInfos as ChannelInfo[],
key: 'id',
});
}) as ChannelInfo[];
const keys = uniqueRaws.map((ci) => ci.id);
const db: Database = this.database;
const existing = await db.get<ChannelInfoModel>(CHANNEL_INFO).query(
Q.where('id', Q.oneOf(keys)),
).fetch();
const channelMap = new Map<string, ChannelInfoModel>(existing.map((ci) => [ci.id, ci]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: ChannelInfo[], ci) => {
const e = channelMap.get(ci.id);
if (!e) {
res.push(ci);
return res;
}
if (
ci.guest_count !== e.guestCount ||
ci.member_count !== e.memberCount ||
ci.header !== e.header ||
ci.pinned_post_count !== e.pinnedPostCount ||
ci.purpose !== e.purpose
) {
res.push(ci);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',
@@ -173,14 +256,42 @@ const ChannelHandler = (superclass: any) => class extends superclass {
my.msg_count = msgCount;
my.mention_count = isCRT ? my.mention_count_root! : my.mention_count;
my.is_unread = msgCount > 0;
my.last_post_at = (isCRT ? (my.last_root_post_at || my.last_post_at) : my.last_post_at) || 0;
my.last_post_at = (isCRT ? (channel.last_root_post_at || channel.last_post_at) : channel.last_post_at) || 0;
}
}
const createOrUpdateRawValues = getUniqueRawsBy({
const uniqueRaws = getUniqueRawsBy({
raws: myChannels,
key: 'id',
});
}) as ChannelMembership[];
const ids = uniqueRaws.map((cm: ChannelMembership) => cm.channel_id);
const db: Database = this.database;
const existing = await db.get<MyChannelModel>(MY_CHANNEL).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const membershipMap = new Map<string, MyChannelModel>(existing.map((member) => [member.id, member]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: ChannelMembership[], my) => {
const e = membershipMap.get(my.channel_id);
if (!e) {
res.push(my);
return res;
}
const chan = channelMap[my.channel_id];
const lastPostAt = (isCRT ? chan.last_root_post_at : chan.last_post_at) || 0;
if ((chan && e.lastPostAt < lastPostAt) ||
e.isUnread !== my.is_unread || e.lastViewedAt < my.last_viewed_at ||
e.roles !== my.roles
) {
res.push(my);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',
@@ -215,7 +326,30 @@ const ChannelHandler = (superclass: any) => class extends superclass {
id: `${m.channel_id}-${m.user_id}`,
}));
const createOrUpdateRawValues = getUniqueRawsBy({raws: memberships, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: memberships, key: 'id'}) as ChannelMember[];
const ids = uniqueRaws.map((cm: ChannelMember) => cm.channel_id);
const db: Database = this.database;
const existing = await db.get<ChannelMembershipModel>(CHANNEL_MEMBERSHIP).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const membershipMap = new Map<string, ChannelMembershipModel>(existing.map((member) => [member.id, member]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: ChannelMember[], cm) => {
const e = membershipMap.get(cm.channel_id);
if (!e) {
res.push(cm);
return res;
}
if (cm.scheme_admin !== e.schemeAdmin) {
res.push(cm);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'user_id',

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Database, Q} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import {
buildTeamMembershipKey,
@@ -64,7 +66,30 @@ const TeamHandler = (superclass: any) => class extends superclass {
id: `${m.team_id}-${m.user_id}`,
}));
const createOrUpdateRawValues = getUniqueRawsBy({raws: memberships, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: memberships, key: 'id'})as TeamMembership[];
const ids = uniqueRaws.map((t) => t.id!);
const db: Database = this.database;
const existing = await db.get<TeamMembershipModel>(TEAM_MEMBERSHIP).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const membershipMap = new Map<String, TeamMembershipModel>(existing.map((e) => [e.id, e]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: TeamMembership[], t) => {
const e = membershipMap.get(t.id!);
if (!e && !t.delete_at) {
res.push(t);
return res;
}
if (e && e.schemeAdmin !== t.scheme_admin) {
res.push(t);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'user_id',
@@ -93,7 +118,30 @@ const TeamHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: teams, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: teams, key: 'id'}) as Team[];
const ids = uniqueRaws.map((t) => t.id);
const db: Database = this.database;
const existing = await db.get<TeamModel>(TEAM).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const teamMap = new Map<String, TeamModel>(existing.map((e) => [e.id, e]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: Team[], t) => {
const e = teamMap.get(t.id);
if (!e && !t.delete_at) {
res.push(t);
return res;
}
if (e && e.updateAt !== t.update_at) {
res.push(t);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',
@@ -178,7 +226,30 @@ const TeamHandler = (superclass: any) => class extends superclass {
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: myTeams, key: 'id'});
const uniqueRaws = getUniqueRawsBy({raws: myTeams, key: 'id'}) as MyTeam[];
const ids = uniqueRaws.map((t) => t.id);
const db: Database = this.database;
const existing = await db.get<MyTeamModel>(MY_TEAM).query(
Q.where('id', Q.oneOf(ids)),
).fetch();
const myTeamMap = new Map<String, MyTeamModel>(existing.map((e) => [e.id, e]));
const createOrUpdateRawValues = uniqueRaws.reduce((res: MyTeam[], mt) => {
const e = myTeamMap.get(mt.id!);
if (!e) {
res.push(mt);
return res;
}
if (e.roles !== mt.roles) {
res.push(mt);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
return this.handleRecords({
fieldName: 'id',

View File

@@ -43,14 +43,13 @@ const UserHandler = (superclass: any) => class extends superclass {
// WE NEED TO SYNC THE PREFS FROM WHAT WE GOT AND WHAT WE HAVE
const deleteValues: PreferenceModel[] = [];
const stored = await this.database.get(PREFERENCE).query().fetch() as PreferenceModel[];
const preferenesMap = new Map(stored.map((p) => {
return [`${p.category}-${p.name}`, p];
}));
if (sync) {
const stored = await this.database.get(PREFERENCE).query().fetch() as PreferenceModel[];
const preferenceMap = preferences.reduce((r: Record<string, boolean>, p) => {
r[`${p.category}-${p.name}`] = true;
return r;
}, {});
for (const pref of stored) {
const exists = preferenceMap[`${pref.category}-${pref.name}`];
const exists = preferenesMap.get(`${pref.category}-${pref.name}`);
if (!exists) {
pref.prepareDestroyPermanently();
deleteValues.push(pref);
@@ -58,12 +57,31 @@ const UserHandler = (superclass: any) => class extends superclass {
}
}
const createOrUpdateRawValues = preferences.reduce((res: PreferenceType[], p) => {
const id = `${p.category}-${p.name}`;
const exist = preferenesMap.get(id);
if (!exist) {
res.push(p);
return res;
}
if (p.category !== exist.category || p.name !== exist.name || p.value !== exist.value) {
res.push(p);
}
return res;
}, []);
if (!createOrUpdateRawValues.length) {
return [];
}
const records: PreferenceModel[] = await this.handleRecords({
fieldName: 'user_id',
buildKeyRecordBy: buildPreferenceKey,
transformer: transformPreferenceRecord,
prepareRecordsOnly: true,
createOrUpdateRawValues: preferences,
createOrUpdateRawValues,
tableName: PREFERENCE,
});