Handled websocket notification race condition

This commit is contained in:
harshil Sharma 2023-11-07 11:39:16 +05:30
parent aff310471a
commit 082b3c7f1e
8 changed files with 42 additions and 15 deletions

View file

@ -9,7 +9,7 @@ import {queryMyTeams} from '@queries/servers/team';
import {isDMorGM} from '@utils/channel';
import {logDebug, logError} from '@utils/log';
import type {Database} from '@nozbe/watermelondb';
import type {Database, Model} from '@nozbe/watermelondb';
import type ChannelModel from '@typings/database/models/servers/channel';
export const deleteCategory = async (serverUrl: string, categoryId: string) => {
@ -116,8 +116,10 @@ async function prepareAddNonGMDMChannelToDefaultCategory(database: Database, tea
const channelCategory = categories.find((category) => category.type === CHANNELS_CATEGORY);
if (channelCategory) {
const cwc = await channelCategory.toCategoryWithChannels();
cwc.channel_ids.unshift(channelId);
return cwc;
if (!cwc.channel_ids.indexOf(channelId)) {
cwc.channel_ids.unshift(channelId);
return cwc;
}
}
return undefined;
@ -127,9 +129,15 @@ export async function putGMInCorrectCategory(serverUrl: string, channelId: strin
try {
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
const categoryChannels = await queryCategoryChannelsByChannelId(database, channelId).fetch();
categoryChannels.forEach((categoryChannel) => categoryChannel.prepareDestroyPermanently());
const models: any[] = categoryChannels;
const categories = await queryCategoriesByTeamIds(database, [targetTeamID]).fetch();
const channelCategory = categories.find((category) => category.type === CHANNELS_CATEGORY);
categoryChannels.
filter((categoryChannel) => categoryChannel.categoryId !== channelCategory?.id).
forEach((categoryChannel) => categoryChannel.prepareDestroyPermanently());
const models: Model[] = categoryChannels;
const cwc = await prepareAddNonGMDMChannelToDefaultCategory(database, targetTeamID, channelId);
if (cwc) {

View file

@ -1277,7 +1277,7 @@ export const getGroupMessageMembersCommonTeams = async (serverUrl: string, chann
}
};
export const convertGroupMessageToPrivateChannel = async (serverUrl: string, channelId: string, teamId: string, displayName: string) => {
export const convertGroupMessageToPrivateChannel = async (serverUrl: string, channelId: string, targetTeamId: string, displayName: string) => {
try {
const name = generateChannelNameFromDisplayName(displayName);
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
@ -1288,18 +1288,19 @@ export const convertGroupMessageToPrivateChannel = async (serverUrl: string, cha
}
const client = NetworkManager.getClient(serverUrl);
const updatedChannel = await client.convertGroupMessageToPrivateChannel(channelId, teamId, displayName, name);
const updatedChannel = await client.convertGroupMessageToPrivateChannel(channelId, targetTeamId, displayName, name);
if (existingChannel) {
existingChannel.prepareUpdate((channel) => {
channel.type = General.PRIVATE_CHANNEL;
channel.displayName = displayName;
channel.name = name;
channel.teamId = targetTeamId;
});
const models: any[] = [existingChannel];
const {models: categoryUpdateModels} = await putGMInCorrectCategory(serverUrl, channelId, teamId, true);
const {models: categoryUpdateModels} = await putGMInCorrectCategory(serverUrl, channelId, targetTeamId, true);
if (categoryUpdateModels) {
models.push(...categoryUpdateModels);
}

View file

@ -7,7 +7,7 @@ import {
storeMyChannelsForTeam, updateChannelInfoFromChannel, updateMyChannelFromWebsocket,
} from '@actions/local/channel';
import {storePostsForChannel} from '@actions/local/post';
import {fetchMissingDirectChannelsInfo, fetchMyChannel, fetchChannelStats, fetchChannelById, handleKickFromChannel} from '@actions/remote/channel';
import {fetchMissingDirectChannelsInfo, fetchMyChannel, fetchChannelStats, fetchChannelById, handleKickFromChannel, goToNPSChannel, switchToChannelById} from '@actions/remote/channel';
import {fetchPostsForChannel} from '@actions/remote/post';
import {fetchRolesIfNeeded} from '@actions/remote/role';
import {fetchUsersByIds, updateUsersNoLongerVisible} from '@actions/remote/user';
@ -15,7 +15,7 @@ import {loadCallForChannel} from '@calls/actions/calls';
import {Events, General} from '@constants';
import DatabaseManager from '@database/manager';
import {deleteChannelMembership, getChannelById, prepareMyChannelsForTeam, getCurrentChannel} from '@queries/servers/channel';
import {getConfig, getCurrentChannelId} from '@queries/servers/system';
import {getConfig, getCurrentChannelId, getCurrentTeamId} from '@queries/servers/system';
import {getCurrentUser, getTeammateNameDisplay, getUserById} from '@queries/servers/user';
import EphemeralStore from '@store/ephemeral_store';
import MyChannelModel from '@typings/database/models/servers/my_channel';
@ -95,9 +95,10 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) {
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
const updatedChannel = JSON.parse(msg.data.channel) as Channel;
// if (EphemeralStore.isConvertingChannel(updatedChannel.id)) {
// return;
// }
if (EphemeralStore.isConvertingChannel(updatedChannel.id)) {
logDebug('Already processing.....');
return;
}
logDebug(`updatedChannel.id: ${updatedChannel.id}`);
@ -112,9 +113,18 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) {
}
operator.batchRecords(models, 'handleChannelUpdatedEvent');
// This indicates a GM was converted to a private channel
if (existingChannelType === General.GM_CHANNEL && updatedChannel.type === General.PRIVATE_CHANNEL) {
logDebug('Yo yoyo!');
await putGMInCorrectCategory(serverUrl, updatedChannel.id, updatedChannel.team_id);
const currentChannelId = await getCurrentChannelId(database);
const currentTeamId = await getCurrentTeamId(database);
// Making sure user is not only in the correct channel, but also
// in the correct team.
if (currentChannelId === updatedChannel.id && currentTeamId !== updatedChannel.team_id) {
await switchToChannelById(serverUrl, updatedChannel.id, updatedChannel.team_id);
}
}
} catch {
// Do nothing

View file

@ -163,6 +163,8 @@ async function doReconnect(serverUrl: string) {
}
export async function handleEvent(serverUrl: string, msg: WebSocketMessage) {
logDebug(msg);
switch (msg.event) {
case WebsocketEvents.POSTED:
case WebsocketEvents.EPHEMERAL_MESSAGE:

View file

@ -70,6 +70,8 @@ const launchAppFromNotification = async (notification: NotificationWithData, col
* @returns a redirection to a screen, either onboarding, add_server, login or home depending on the scenario
*/
// LOL
const launchApp = async (props: LaunchProps) => {
let serverUrl: string | undefined;
switch (props?.launchType) {

View file

@ -86,7 +86,7 @@ export const ConvertGMToChannelForm = ({
return;
}
switchToChannelById(serverUrl, updatedChannel.id, selectedTeam.id);
await switchToChannelById(serverUrl, updatedChannel.id, selectedTeam.id);
}, [selectedTeam]);
const handleOnChannelNameChange = useCallback((newName: string) => {

View file

@ -6,6 +6,7 @@ import {FlatList} from 'react-native';
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import {fetchDirectChannelsInfo} from '@actions/remote/channel';
import {logDebug} from '@app/utils/log';
import ChannelItem from '@components/channel_item';
import {ROW_HEIGHT as CHANNEL_ROW_HEIGHT} from '@components/channel_item/channel_item';
import {useServerUrl} from '@context/server';
@ -25,6 +26,8 @@ type Props = {
const extractKey = (item: ChannelModel) => item.id;
const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, onChannelSwitch}: Props) => {
sortedChannels.forEach((c) => logDebug(c.displayName));
const serverUrl = useServerUrl();
const ids = useMemo(() => {
const filteredChannels = unreadsOnTop ? sortedChannels.filter((c) => !unreadIds.has(c.id)) : sortedChannels;

View file

@ -6,6 +6,7 @@ import {useIntl} from 'react-intl';
import {FlatList, StyleSheet, View} from 'react-native';
import {switchToChannelById} from '@actions/remote/channel';
import {logDebug} from '@app/utils/log';
import Loading from '@components/loading';
import {useServerUrl} from '@context/server';
import {useIsTablet} from '@hooks/device';