Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Do not load unnecessary CachedCollection data for embedded layout #34138

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ class CachedChatSubscription extends CachedCollection<SubscriptionWithRoom, ISub
};
}

async upsertSubscription(record: ISubscription): Promise<void> {
return this.handleRecordEvent('changed', record);
}

protected deserializeFromCache(record: unknown) {
const deserialized = super.deserializeFromCache(record);

Expand Down
5 changes: 2 additions & 3 deletions apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator';
import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent';
import { getConfig } from '../../../../client/lib/utils/getConfig';
import { callbacks } from '../../../../lib/callbacks';
import { CachedChatRoom, Messages, Subscriptions, CachedChatSubscription } from '../../../models/client';
import { Messages, Subscriptions, CachedChatSubscription } from '../../../models/client';
import { sdk } from '../../../utils/client/lib/SDKClient';

const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen') ?? '5') || 5;
Expand Down Expand Up @@ -79,8 +79,7 @@ function getOpenedRoomByRid(rid: IRoom['_id']) {
}

const computation = Tracker.autorun(() => {
const ready = CachedChatRoom.ready.get() && mainReady.get();
if (ready !== true) {
if (!mainReady.get()) {
return;
}
Tracker.nonreactive(() =>
Expand Down
68 changes: 37 additions & 31 deletions apps/meteor/client/lib/cachedCollections/CachedCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,6 @@ export class CachedCollection<T extends { _id: string }, U = T> {
: () => undefined;

CachedCollectionManager.register(this);

if (!userRelated) {
void this.init();
return;
}

if (process.env.NODE_ENV === 'test') {
return;
}

onLoggedIn(() => {
void this.init();
});

Accounts.onLogout(() => {
this.ready.set(false);
});
}

protected get eventName(): `${Name}-changed` | `${string}/${Name}-changed` {
Expand Down Expand Up @@ -239,23 +222,27 @@ export class CachedCollection<T extends { _id: string }, U = T> {
async setupListener() {
sdk.stream(this.eventType, [this.eventName], (async (action: 'removed' | 'changed', record: any) => {
this.log('record received', action, record);
const newRecord = this.handleReceived(record, action);
await this.handleRecordEvent(action, record);
}) as (...args: unknown[]) => void);
}

if (!hasId(newRecord)) {
return;
}
protected async handleRecordEvent(action: 'removed' | 'changed', record: any) {
const newRecord = this.handleReceived(record, action);

if (action === 'removed') {
this.collection.remove(newRecord._id);
} else {
const { _id } = newRecord;
if (!_id) {
return;
}
this.collection.upsert({ _id } as any, newRecord);
if (!hasId(newRecord)) {
return;
}

if (action === 'removed') {
this.collection.remove(newRecord._id);
} else {
const { _id } = newRecord;
if (!_id) {
return;
}
await this.save();
}) as (...args: unknown[]) => void);
this.collection.upsert({ _id } as any, newRecord);
}
await this.save();
}

trySync(delay = 10) {
Expand Down Expand Up @@ -368,4 +355,23 @@ export class CachedCollection<T extends { _id: string }, U = T> {
}

private reconnectionComputation: Tracker.Computation | undefined;

listen() {
if (!this.userRelated) {
void this.init();
return;
}

if (process.env.NODE_ENV === 'test') {
return;
}

onLoggedIn(() => {
void this.init();
});

Accounts.onLogout(() => {
this.ready.set(false);
});
}
}
6 changes: 5 additions & 1 deletion apps/meteor/client/providers/AuthorizationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ReactNode } from 'react';
import React, { useCallback, useEffect } from 'react';

import { hasPermission, hasAtLeastOnePermission, hasAllPermission, hasRole } from '../../app/authorization/client';
import { Roles } from '../../app/models/client/models/Roles';
import { Roles, AuthzCachedCollection } from '../../app/models/client';
import { useReactiveValue } from '../hooks/useReactiveValue';
import { createReactiveSubscriptionFactory } from '../lib/createReactiveSubscriptionFactory';

Expand Down Expand Up @@ -48,6 +48,10 @@ const AuthorizationProvider = ({ children }: AuthorizationProviderProps) => {
),
);

useEffect(() => {
AuthzCachedCollection.listen();
}, []);

useEffect(() => {
contextValue.roleStore.roles = roles;
contextValue.roleStore.emit('change', roles);
Expand Down
112 changes: 112 additions & 0 deletions apps/meteor/client/views/room/RoomOpenerEmbedded.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { ISubscription, RoomType } from '@rocket.chat/core-typings';
import { Box, States, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage';
import { FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React, { lazy, Suspense } from 'react';
import { useTranslation } from 'react-i18next';

import RoomSkeleton from './RoomSkeleton';
import RoomSidepanel from './Sidepanel/RoomSidepanel';
import { useOpenRoom } from './hooks/useOpenRoom';
import { CachedChatSubscription } from '../../../app/models/client';
import { FeaturePreviewSidePanelNavigation } from '../../components/FeaturePreviewSidePanelNavigation';
import { Header } from '../../components/Header';
import { getErrorMessage } from '../../lib/errorHandling';
import { NotAuthorizedError } from '../../lib/errors/NotAuthorizedError';
import { OldUrlRoomError } from '../../lib/errors/OldUrlRoomError';
import { RoomNotFoundError } from '../../lib/errors/RoomNotFoundError';

const RoomProvider = lazy(() => import('./providers/RoomProvider'));
const RoomNotFound = lazy(() => import('./RoomNotFound'));
const Room = lazy(() => import('./Room'));
const RoomLayout = lazy(() => import('./layout/RoomLayout'));
const NotAuthorizedPage = lazy(() => import('../notAuthorized/NotAuthorizedPage'));

type RoomOpenerProps = {
type: RoomType;
reference: string;
};

const isDirectOrOmnichannelRoom = (type: RoomType) => type === 'd' || type === 'l';

const RoomOpenerEmbedded = ({ type, reference }: RoomOpenerProps): ReactElement => {
const { data, error, isSuccess, isError, isLoading } = useOpenRoom({ type, reference });

const getSubscription = useEndpoint('GET', '/v1/subscriptions.getOne');

const rid = data?.rid;
useQuery(
['subscriptions', rid],
() => {
if (!rid) {
throw new Error('Room not found');
}
return getSubscription({ roomId: rid });
},
{
enabled: !!rid,
suspense: true,
onSuccess({ subscription }) {
if (!subscription) {
throw new Error('Room not found');
}
CachedChatSubscription.upsertSubscription(subscription as unknown as ISubscription);
},
},
);

const { t } = useTranslation();

return (
<Box display='flex' w='full' h='full'>
{!isDirectOrOmnichannelRoom(type) && (
<FeaturePreviewSidePanelNavigation>
<FeaturePreviewOff>{null}</FeaturePreviewOff>
<FeaturePreviewOn>
<RoomSidepanel />
</FeaturePreviewOn>
</FeaturePreviewSidePanelNavigation>
)}

<Suspense fallback={<RoomSkeleton />}>
{isLoading && <RoomSkeleton />}
{isSuccess && (
<RoomProvider rid={data.rid}>
<Room />
</RoomProvider>
)}
{isError &&
(() => {
if (error instanceof OldUrlRoomError) {
return <RoomSkeleton />;
}

if (error instanceof RoomNotFoundError) {
return <RoomNotFound />;
}

if (error instanceof NotAuthorizedError) {
return <NotAuthorizedPage />;
}

return (
<RoomLayout
header={<Header />}
body={
<States>
<StatesIcon name='circle-exclamation' variation='danger' />
<StatesTitle>{t('core.Error')}</StatesTitle>
<StatesSubtitle>{getErrorMessage(error)}</StatesSubtitle>
</States>
}
/>
);
})()}
</Suspense>
</Box>
);
};

export default RoomOpenerEmbedded;
8 changes: 8 additions & 0 deletions apps/meteor/client/views/room/RoomRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useRouter } from '@rocket.chat/ui-contexts';
import React, { useLayoutEffect, useState } from 'react';

import RoomOpener from './RoomOpener';
import RoomOpenerEmbedded from './RoomOpenerEmbedded';
import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout';

type RoomRouteProps = {
extractOpenRoomParams: (routeParams: Record<string, string | null | undefined>) => {
Expand All @@ -15,6 +17,8 @@ const RoomRoute = ({ extractOpenRoomParams }: RoomRouteProps) => {
const router = useRouter();
const [params, setParams] = useState(() => extractOpenRoomParams(router.getRouteParameters()));

const isEmbeddedLayout = useEmbeddedLayout();

useLayoutEffect(
() =>
router.subscribeToRouteChange(() => {
Expand All @@ -23,6 +27,10 @@ const RoomRoute = ({ extractOpenRoomParams }: RoomRouteProps) => {
[extractOpenRoomParams, router],
);

if (isEmbeddedLayout) {
return <RoomOpenerEmbedded {...params} />;
}

return <RoomOpener {...params} />;
};

Expand Down
36 changes: 36 additions & 0 deletions apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useUserId } from '@rocket.chat/ui-contexts';
import type { ReactElement, ReactNode } from 'react';
import React, { useEffect } from 'react';

import { CachedChatRoom, CachedChatSubscription } from '../../../../app/models/client';
import { settings } from '../../../../app/settings/client';
import { mainReady } from '../../../../app/ui-utils/client';
import { useReactiveVar } from '../../../hooks/useReactiveVar';
import { isSyncReady } from '../../../lib/userData';
import PageLoading from '../PageLoading';

const EmbeddedPreload = ({ children }: { children: ReactNode }): ReactElement => {
const uid = useUserId();
const subscriptionsReady = useReactiveVar(CachedChatSubscription.ready);
const settingsReady = useReactiveVar(settings.cachedCollection.ready);
const userDataReady = useReactiveVar(isSyncReady);

const ready = !uid || (userDataReady && subscriptionsReady && settingsReady);

useEffect(() => {
mainReady.set(ready);
}, [ready]);

useEffect(() => {
CachedChatSubscription.ready.set(true);
CachedChatRoom.ready.set(true);
}, [ready]);

if (!ready) {
return <PageLoading />;
}

return <>{children}</>;
};

export default EmbeddedPreload;
14 changes: 14 additions & 0 deletions apps/meteor/client/views/root/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import type { ReactElement, ReactNode } from 'react';
import React, { Suspense } from 'react';

import AuthenticationCheck from './AuthenticationCheck';
import EmbeddedPreload from './EmbeddedPreload';
import Preload from './Preload';
import { useCustomScript } from './useCustomScript';
import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout';

type MainLayoutProps = {
children?: ReactNode;
Expand All @@ -12,6 +14,18 @@ type MainLayoutProps = {
const MainLayout = ({ children = null }: MainLayoutProps): ReactElement => {
useCustomScript();

const isEmbeddedLayout = useEmbeddedLayout();

if (isEmbeddedLayout) {
return (
<EmbeddedPreload>
<AuthenticationCheck>
<Suspense fallback={null}>{children}</Suspense>
</AuthenticationCheck>
</EmbeddedPreload>
);
}

return (
<Preload>
<AuthenticationCheck>
Expand Down
7 changes: 6 additions & 1 deletion apps/meteor/client/views/root/MainLayout/Preload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useUserId } from '@rocket.chat/ui-contexts';
import type { ReactElement, ReactNode } from 'react';
import React, { useEffect } from 'react';

import { CachedChatSubscription } from '../../../../app/models/client';
import { CachedChatRoom, CachedChatSubscription } from '../../../../app/models/client';
import { settings } from '../../../../app/settings/client';
import { mainReady } from '../../../../app/ui-utils/client';
import { useReactiveVar } from '../../../hooks/useReactiveVar';
Expand All @@ -21,6 +21,11 @@ const Preload = ({ children }: { children: ReactNode }): ReactElement => {
mainReady.set(ready);
}, [ready]);

useEffect(() => {
CachedChatSubscription.listen();
CachedChatRoom.listen();
}, []);

if (!ready) {
return <PageLoading />;
}
Expand Down
Loading