Files
mattermost-mobile/app/actions/local/channel.ts
Daniel Espino García 1e355ee684 [Gekidou] Handle post related websocket events (#5851)
* Handle post related websocket events

* Address feedback and several fixes

* Fix lint

* Address feedback

* LastPostAt as latest post create_at, and lastViewedAt as date.now()

* Address feedback

* Change database for operator and add type imports
2022-01-24 12:43:21 +01:00

340 lines
12 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Model, Q} from '@nozbe/watermelondb';
import {DeviceEventEmitter} from 'react-native';
import {General, Navigation as NavigationConstants, Preferences, Screens} from '@constants';
import {MM_TABLES} from '@constants/database';
import DatabaseManager from '@database/manager';
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
import {prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannelIds, queryChannelsById, queryMyChannel} from '@queries/servers/channel';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, queryCommonSystemValues, queryCurrentTeamId, setCurrentChannelId} from '@queries/servers/system';
import {addChannelToTeamHistory, addTeamToTeamHistory, removeChannelFromTeamHistory} from '@queries/servers/team';
import {queryCurrentUser} from '@queries/servers/user';
import {dismissAllModalsAndPopToRoot, dismissAllModalsAndPopToScreen} from '@screens/navigation';
import {isTablet} from '@utils/helpers';
import {displayGroupMessageName, displayUsername} from '@utils/user';
import type ChannelModel from '@typings/database/models/servers/channel';
import type UserModel from '@typings/database/models/servers/user';
const {SERVER: {CHANNEL_MEMBERSHIP, USER}} = MM_TABLES;
export const switchToChannel = async (serverUrl: string, channelId: string, teamId?: string, prepareRecordsOnly = false) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return {error: `${serverUrl} database not found`};
}
const models: Model[] = [];
try {
const dt = Date.now();
const isTabletDevice = await isTablet();
const system = await queryCommonSystemValues(database);
const member = await queryMyChannel(database, channelId);
if (member) {
const channel: ChannelModel = await member.channel.fetch();
const {operator} = DatabaseManager.serverDatabases[serverUrl];
const commonValues: PrepareCommonSystemValuesArgs = {currentChannelId: channelId};
if (isTabletDevice) {
// On tablet, the channel is being rendered, by setting the channel to empty first we speed up
// the switch by ~3x
await setCurrentChannelId(operator, '');
}
if (teamId && system.currentTeamId !== teamId) {
commonValues.currentTeamId = teamId;
const history = await addTeamToTeamHistory(operator, teamId, true);
models.push(...history);
}
const common = await prepareCommonSystemValues(operator, commonValues);
if (common) {
models.push(...common);
}
if (system.currentChannelId !== channelId) {
const history = await addChannelToTeamHistory(operator, channel.teamId || system.currentTeamId, channelId, true);
models.push(...history);
}
const {member: viewedAt} = await markChannelAsViewed(serverUrl, channelId, true);
if (viewedAt) {
models.push(viewedAt);
}
if (models.length && !prepareRecordsOnly) {
await operator.batchRecords(models);
}
if (isTabletDevice) {
dismissAllModalsAndPopToRoot();
DeviceEventEmitter.emit(NavigationConstants.NAVIGATION_HOME);
} else {
dismissAllModalsAndPopToScreen(Screens.CHANNEL, '', undefined, {topBar: {visible: false}});
}
console.log('channel switch to', channel?.displayName, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line
}
} catch (error) {
return {error};
}
return {models};
};
export const removeCurrentUserFromChannel = async (serverUrl: string, channelId: string, prepareRecordsOnly = false) => {
const serverDatabase = DatabaseManager.serverDatabases[serverUrl];
if (!serverDatabase) {
return {error: `${serverUrl} database not found`};
}
const {operator, database} = serverDatabase;
const models: Model[] = [];
const myChannel = await queryMyChannel(database, channelId);
if (myChannel) {
const channel = await myChannel.channel.fetch() as ChannelModel;
models.push(...await prepareDeleteChannel(channel));
let teamId = channel.teamId;
if (teamId) {
teamId = await queryCurrentTeamId(database);
}
const system = await removeChannelFromTeamHistory(operator, teamId, channel.id, true);
if (system) {
models.push(...system);
}
if (models.length && !prepareRecordsOnly) {
try {
await operator.batchRecords(models);
} catch {
// eslint-disable-next-line no-console
console.log('FAILED TO BATCH CHANGES FOR REMOVE USER FROM CHANNEL');
}
}
}
return {models};
};
export const setChannelDeleteAt = async (serverUrl: string, channelId: string, deleteAt: number) => {
const serverDatabase = DatabaseManager.serverDatabases[serverUrl];
if (!serverDatabase) {
return;
}
const {operator, database} = serverDatabase;
const channels = await queryChannelsById(database, [channelId]);
if (!channels?.length) {
return;
}
const channel = channels[0];
const model = channel.prepareUpdate((c) => {
c.deleteAt = deleteAt;
});
try {
await operator.batchRecords([model]);
} catch {
// eslint-disable-next-line no-console
console.log('FAILED TO BATCH CHANGES FOR CHANNEL DELETE AT');
}
};
export const selectAllMyChannelIds = async (serverUrl: string) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return [];
}
return queryAllMyChannelIds(database);
};
export const markChannelAsViewed = async (serverUrl: string, channelId: string, prepareRecordsOnly = false) => {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}
const member = await queryMyChannel(operator.database, channelId);
if (!member) {
return {error: 'not a member'};
}
member.prepareUpdate((m) => {
m.isUnread = false;
m.mentionsCount = 0;
m.manuallyUnread = false;
m.viewedAt = member.lastViewedAt;
m.lastViewedAt = Date.now();
});
try {
if (!prepareRecordsOnly) {
await operator.batchRecords([member]);
}
return {member};
} catch (error) {
return {error};
}
};
export const markChannelAsUnread = async (serverUrl: string, channelId: string, messageCount: number, mentionsCount: number, manuallyUnread: boolean, lastViewed: number, prepareRecordsOnly = false) => {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}
const member = await queryMyChannel(operator.database, channelId);
if (!member) {
return {error: 'not a member'};
}
member.prepareUpdate((m) => {
m.viewedAt = lastViewed;
m.lastViewedAt = lastViewed;
m.messageCount = messageCount;
m.mentionsCount = mentionsCount;
m.manuallyUnread = manuallyUnread;
});
try {
if (!prepareRecordsOnly) {
await operator.batchRecords([member]);
}
return {member};
} catch (error) {
return {error};
}
};
export const resetMessageCount = async (serverUrl: string, channelId: string) => {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}
const member = await queryMyChannel(operator.database, channelId);
if (!member) {
return {error: 'not a member'};
}
try {
member.prepareUpdate((m) => {
m.messageCount = 0;
});
await operator.batchRecords([member]);
return member;
} catch (error) {
return {error};
}
};
export const storeMyChannelsForTeam = async (serverUrl: string, teamId: string, channels: Channel[], memberships: ChannelMembership[], prepareRecordsOnly = false) => {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}
const modelPromises: Array<Promise<Model[]>> = [];
const prepare = await prepareMyChannelsForTeam(operator, teamId, channels, memberships);
if (prepare) {
modelPromises.push(...prepare);
}
const models = await Promise.all(modelPromises);
const flattenedModels = models.flat() as Model[];
if (prepareRecordsOnly) {
return {models: flattenedModels};
}
if (flattenedModels?.length > 0) {
try {
await operator.batchRecords(flattenedModels);
} catch (error) {
// eslint-disable-next-line no-console
console.log('FAILED TO BATCH CHANNELS');
return {error};
}
}
return {models: flattenedModels};
};
export const updateLastPostAt = async (serverUrl: string, channelId: string, lastPostAt: number, prepareRecordsOnly = false) => {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}
const member = await queryMyChannel(operator.database, channelId);
if (!member) {
return {error: 'not a member'};
}
const models: Model[] = [];
if (lastPostAt > member.lastPostAt) {
member.prepareUpdate((m) => {
m.lastPostAt = lastPostAt;
});
models.push();
}
try {
if (!prepareRecordsOnly && models.length) {
await operator.batchRecords([member]);
}
} catch (error) {
return {error};
}
return {models};
};
export async function updateChannelsDisplayName(serverUrl: string, channels: ChannelModel[], user: UserProfile, prepareRecordsOnly = false) {
const database = DatabaseManager.serverDatabases[serverUrl];
if (!database) {
return {};
}
const currentUser = await queryCurrentUser(database.database);
if (!currentUser) {
return {};
}
const {config, license} = await queryCommonSystemValues(database.database);
const preferences = await queryPreferencesByCategoryAndName(database.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT);
const displaySettings = getTeammateNameDisplaySetting(preferences, config, license);
const models: Model[] = [];
channels?.forEach(async (channel) => {
let newDisplayName = '';
if (channel.type === General.DM_CHANNEL) {
newDisplayName = displayUsername(user, currentUser.locale, displaySettings);
} else {
const dbProfiles = await database.database.get<UserModel>(USER).query(Q.on(CHANNEL_MEMBERSHIP, Q.where('channel_id', channel.id))).fetch();
const newProfiles: Array<UserModel|UserProfile> = dbProfiles.filter((u) => u.id !== user.id);
newProfiles.push(user);
newDisplayName = displayGroupMessageName(newProfiles, currentUser.locale, displaySettings, currentUser.id);
}
if (channel.displayName !== newDisplayName) {
channel.prepareUpdate((c) => {
c.displayName = newDisplayName;
});
models.push(channel);
}
});
if (models.length && !prepareRecordsOnly) {
database.operator.batchRecords(models);
}
return {models};
}