[MM-48926] Fetch new thread posts on websocket reconnect (#6844)

This commit is contained in:
Anurag Shivarathri 2022-12-09 23:47:34 +05:30 committed by GitHub
parent d019f06c0b
commit 7f7bfd7b72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 18 deletions

View file

@ -79,11 +79,19 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
const teamId = channel.teamId || currentTeamId;
let switchingTeams = false;
if (currentTeamId !== teamId) {
if (currentTeamId === teamId) {
const models = await prepareCommonSystemValues(operator, {
currentChannelId: channel.id,
});
if (models.length) {
await operator.batchRecords(models);
}
} else {
const modelPromises: Array<Promise<Model[]>> = [];
switchingTeams = true;
modelPromises.push(addTeamToTeamHistory(operator, teamId, true));
const commonValues: PrepareCommonSystemValuesArgs = {
currentChannelId: channel.id,
currentTeamId: teamId,
};
modelPromises.push(prepareCommonSystemValues(operator, commonValues));

View file

@ -534,6 +534,10 @@ export async function handleEntryAfterLoadNavigation(
const currentTeamIdAfterLoad = await getCurrentTeamId(database);
const currentChannelIdAfterLoad = await getCurrentChannelId(database);
const mountedScreens = NavigationStore.getScreensInStack();
const isChannelScreenMounted = mountedScreens.includes(Screens.CHANNEL);
const isThreadsMounted = mountedScreens.includes(Screens.THREAD);
const tabletDevice = await isTablet();
if (currentTeamIdAfterLoad !== currentTeamId) {
// Switched teams while loading
@ -545,18 +549,14 @@ export async function handleEntryAfterLoadNavigation(
} else if (currentChannelIdAfterLoad !== currentChannelId) {
// Switched channels while loading
if (!channelMembers.find((m) => m.channel_id === currentChannelIdAfterLoad)) {
const tabletDevice = await isTablet();
const navComponents = NavigationStore.getScreensInStack();
if (tabletDevice || navComponents.includes(Screens.CHANNEL) || navComponents.includes(Screens.THREAD)) {
if (tabletDevice || isChannelScreenMounted || isThreadsMounted) {
await handleKickFromChannel(serverUrl, currentChannelIdAfterLoad);
} else {
await setCurrentTeamAndChannelId(operator, initialTeamId, initialChannelId);
}
}
} else if (currentChannelIdAfterLoad !== initialChannelId) {
const tabletDevice = await isTablet();
const navComponents = NavigationStore.getScreensInStack();
if (tabletDevice || navComponents.includes(Screens.CHANNEL) || navComponents.includes(Screens.THREAD)) {
} else if (currentChannelIdAfterLoad && currentChannelIdAfterLoad !== initialChannelId) {
if (tabletDevice || isChannelScreenMounted || isThreadsMounted) {
await handleKickFromChannel(serverUrl, currentChannelIdAfterLoad);
} else {
await setCurrentTeamAndChannelId(operator, initialTeamId, initialChannelId);

View file

@ -3,8 +3,11 @@
import {DeviceEventEmitter} from 'react-native';
import {markChannelAsViewed} from '@actions/local/channel';
import {markChannelAsRead} from '@actions/remote/channel';
import {handleEntryAfterLoadNavigation} from '@actions/remote/entry/common';
import {deferredAppEntryActions, entry} from '@actions/remote/entry/gql_common';
import {fetchPostsForChannel, fetchPostThread} from '@actions/remote/post';
import {fetchStatusByIds} from '@actions/remote/user';
import {loadConfigAndCalls} from '@calls/actions/calls';
import {
@ -27,22 +30,28 @@ import {
handleCallUserVoiceOn,
} from '@calls/connection/websocket_event_handlers';
import {isSupportedServerCalls} from '@calls/utils';
import {Events, WebsocketEvents} from '@constants';
import {Events, Screens, WebsocketEvents} from '@constants';
import {SYSTEM_IDENTIFIERS} from '@constants/database';
import DatabaseManager from '@database/manager';
import AppsManager from '@managers/apps_manager';
import {getActiveServerUrl} from '@queries/app/servers';
import {getCurrentChannel} from '@queries/servers/channel';
import {getLastPostInThread} from '@queries/servers/post';
import {
getConfig,
getCurrentChannelId,
getCurrentUserId,
getLicense,
getWebSocketLastDisconnected,
resetWebSocketLastDisconnected,
} from '@queries/servers/system';
import {getCurrentTeam} from '@queries/servers/team';
import {getIsCRTEnabled} from '@queries/servers/thread';
import {getCurrentUser} from '@queries/servers/user';
import {logInfo} from '@utils/log';
import EphemeralStore from '@store/ephemeral_store';
import NavigationStore from '@store/navigation_store';
import {isTablet} from '@utils/helpers';
import {logDebug, logInfo} from '@utils/log';
import {handleCategoryCreatedEvent, handleCategoryDeletedEvent, handleCategoryOrderUpdatedEvent, handleCategoryUpdatedEvent} from './category';
import {handleChannelConvertedEvent, handleChannelCreatedEvent,
@ -151,6 +160,8 @@ async function doReconnect(serverUrl: string) {
await operator.batchRecords(models);
logInfo('WEBSOCKET RECONNECT MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
await fetchPostDataIfNeeded(serverUrl);
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(database))!;
const license = await getLicense(database);
const config = await getConfig(database);
@ -398,3 +409,41 @@ export async function handleEvent(serverUrl: string, msg: WebSocketMessage) {
break;
}
}
async function fetchPostDataIfNeeded(serverUrl: string) {
try {
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
const currentChannelId = await getCurrentChannelId(database);
const isCRTEnabled = await getIsCRTEnabled(database);
const mountedScreens = NavigationStore.getScreensInStack();
const isChannelScreenMounted = mountedScreens.includes(Screens.CHANNEL);
const isThreadScreenMounted = mountedScreens.includes(Screens.THREAD);
const tabletDevice = await isTablet();
if (isCRTEnabled && isThreadScreenMounted) {
// Fetch new posts in the thread only when CRT is enabled,
// for non-CRT fetchPostsForChannel includes posts in the thread
const rootId = EphemeralStore.getCurrentThreadId();
if (rootId) {
const lastPost = await getLastPostInThread(database, rootId);
if (lastPost) {
if (lastPost) {
const options: FetchPaginatedThreadOptions = {};
options.fromCreateAt = lastPost.createAt;
options.fromPost = lastPost.id;
options.direction = 'down';
await fetchPostThread(serverUrl, rootId, options);
}
}
}
}
if (currentChannelId && (isChannelScreenMounted || tabletDevice)) {
await fetchPostsForChannel(serverUrl, currentChannelId);
markChannelAsRead(serverUrl, currentChannelId);
markChannelAsViewed(serverUrl, currentChannelId);
}
} catch (error) {
logDebug('could not fetch needed post after WS reconnect', error);
}
}

View file

@ -126,7 +126,20 @@ export const getRecentPostsInThread = async (database: Database, rootId: string)
return [];
};
export const queryPostsChunk = (database: Database, id: string, earliest: number, latest: number, inThread = false, includeDeleted = false) => {
export const getLastPostInThread = async (database: Database, rootId: string) => {
const chunks = await queryPostsInThread(database, rootId, true, true).fetch();
if (chunks.length) {
const recent = chunks[0];
const post = await getPostById(database, rootId);
if (post) {
const posts = await queryPostsChunk(database, rootId, recent.earliest, recent.latest, true, true, 1).fetch();
return posts[0];
}
}
return undefined;
};
export const queryPostsChunk = (database: Database, id: string, earliest: number, latest: number, inThread = false, includeDeleted = false, limit = 0) => {
const conditions: Q.Condition[] = [Q.where('create_at', Q.between(earliest, latest))];
if (inThread) {
conditions.push(Q.where('root_id', id));
@ -138,12 +151,18 @@ export const queryPostsChunk = (database: Database, id: string, earliest: number
conditions.push(Q.where('delete_at', Q.eq(0)));
}
return database.get<PostModel>(POST).query(
const clauses: Q.Clause[] = [
Q.and(
...conditions,
),
Q.sortBy('create_at', Q.desc),
);
];
if (limit) {
clauses.push(Q.take(limit));
}
return database.get<PostModel>(POST).query(...clauses);
};
export const getRecentPostsInChannel = async (database: Database, channelId: string, includeDeleted = false) => {

View file

@ -34,7 +34,7 @@ const enhanced = withObservables(['tab', 'teamId', 'forceQueryAfterAppState'], (
teammateNameDisplay: observeTeammateNameDisplay(database),
threads: teamThreadsSyncObserver.pipe(
switchMap((teamThreadsSync) => {
const earliest = teamThreadsSync?.[0]?.earliest;
const earliest = tab === 'all' ? teamThreadsSync?.[0]?.earliest : 0;
return queryThreadsInTeam(database, teamId, getOnlyUnreads, false, true, true, earliest).observe();
}),
),

View file

@ -30,11 +30,11 @@ const alpha = {
};
export function registerNavigationListeners() {
Navigation.events().registerScreenPoppedListener(screenPoppedListener);
Navigation.events().registerCommandListener(registerCommandListener);
Navigation.events().registerScreenPoppedListener(onPoppedListener);
Navigation.events().registerCommandListener(onCommandListener);
}
function registerCommandListener(name: string, params: any) {
function onCommandListener(name: string, params: any) {
switch (name) {
case 'setRoot':
NavigationStore.clearScreensFromStack();
@ -63,9 +63,12 @@ function registerCommandListener(name: string, params: any) {
}
}
function screenPoppedListener({componentId}: ScreenPoppedEvent) {
function onPoppedListener({componentId}: ScreenPoppedEvent) {
// screen pop does not trigger registerCommandListener, but does trigger screenPoppedListener
NavigationStore.removeScreenFromStack(componentId);
if (NavigationStore.getVisibleScreen() === Screens.HOME) {
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true);
}
}
export const loginAnimationOptions = () => {