Fix: add a adapters manager to lock loading while data sync

This commit is contained in:
Tainan Felipe 2024-05-01 08:38:12 -03:00
parent 5f17c76b6a
commit 210eb0911c
6 changed files with 117 additions and 24 deletions

View File

@ -10,11 +10,8 @@ import LoadingScreenHOC from '/imports/ui/components/common/loading-screen/loadi
import IntlLoaderContainer from '/imports/startup/client/intlLoader';
import LocatedErrorBoundary from '/imports/ui/components/common/error-boundary/located-error-boundary/component';
import StartupDataFetch from '/imports/ui/components/connection-manager/startup-data-fetch/component';
import UserGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/userGrapQlMiniMongoAdapter/component';
import VoiceUserGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/voiceUserGraphQlMiniMongoAdapter/component';
import MeetingGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/meetingGrapQlMiniMongoAdapter/component';
import ScreenShareGraphQlMiniMongoAdapterContainer from '/imports/ui/components/components-data/screenshareGraphQlMiniMongoAdapter/component';
import VideoStreamAdapter from '/imports/ui/components/video-provider/video-provider-graphql/adapter';
import GraphqlToMiniMongoAdapterManager from '/imports/ui/components/components-data/graphqlToMiniMongoAdapterManager/component';
const Main: React.FC = () => {
// Meteor.disconnect();
@ -27,12 +24,9 @@ const Main: React.FC = () => {
<LocatedErrorBoundary Fallback={ErrorScreen}>
<ConnectionManager>
<PresenceManager>
<SettingsLoader />
<UserGrapQlMiniMongoAdapter />
<VoiceUserGrapQlMiniMongoAdapter />
<MeetingGrapQlMiniMongoAdapter />
<ScreenShareGraphQlMiniMongoAdapterContainer />
<VideoStreamAdapter />
<GraphqlToMiniMongoAdapterManager>
<SettingsLoader />
</GraphqlToMiniMongoAdapterManager>
</PresenceManager>
</ConnectionManager>
</LocatedErrorBoundary>

View File

@ -0,0 +1,58 @@
import React, {
useCallback,
useMemo,
useRef,
useState,
} from 'react';
import UserGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/userGrapQlMiniMongoAdapter/component';
import VoiceUserGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/voiceUserGraphQlMiniMongoAdapter/component';
import MeetingGrapQlMiniMongoAdapter from '/imports/ui/components/components-data/meetingGrapQlMiniMongoAdapter/component';
import ScreenShareGraphQlMiniMongoAdapterContainer from '/imports/ui/components/components-data/screenshareGraphQlMiniMongoAdapter/component';
import VideoStreamAdapter from '/imports/ui/components/video-provider/video-provider-graphql/adapter';
interface GraphqlToMiniMongoAdapterManagerProps {
children: React.ReactNode;
}
export interface AdapterProps extends GraphqlToMiniMongoAdapterManagerProps {
onReady: (key:string) => void;
}
const GraphqlToMiniMongoAdapterManager: React.FC<GraphqlToMiniMongoAdapterManagerProps> = ({ children }) => {
const [adapterLoaded, setAdapterLoaded] = useState(false);
const loadedComponents = useRef<{
[key: string]: number;
}>({});
const adapterComponents = useRef([
UserGrapQlMiniMongoAdapter,
MeetingGrapQlMiniMongoAdapter,
VideoStreamAdapter,
VoiceUserGrapQlMiniMongoAdapter,
]);
const onReady = useCallback((key: string) => {
loadedComponents.current[key] = 1;
if (Object.keys(loadedComponents.current).length >= adapterComponents.current.length) {
setAdapterLoaded(true);
}
}, []);
const nestAdapters = useMemo(() => {
return adapterComponents.current.reduce((acc, Component) => (
<Component onReady={onReady}>
{acc}
</Component>
), <span />);
}, []);
return (
<>
{/* screenshare loads conditionally so can't be used on lock loading */}
<ScreenShareGraphQlMiniMongoAdapterContainer />
{nestAdapters}
{adapterLoaded ? children : null}
</>
);
};
export default GraphqlToMiniMongoAdapterManager;

View File

@ -1,10 +1,15 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import { useCreateUseSubscription } from '/imports/ui/core/hooks/createUseSubscription';
import MEETING_SUBSCRIPTION from '/imports/ui/core/graphql/queries/meetingSubscription';
import { Meeting } from '/imports/ui/Types/meeting';
import Meetings from '/imports/api/meetings';
import { AdapterProps } from '../graphqlToMiniMongoAdapterManager/component';
const MeetingGrapQlMiniMongoAdapter: React.FC = () => {
const MeetingGrapQlMiniMongoAdapter: React.FC<AdapterProps> = ({
onReady,
children,
}) => {
const ready = useRef(false);
const meetingSubscription = useCreateUseSubscription<Meeting>(MEETING_SUBSCRIPTION, {}, true);
const {
data: meetingData,
@ -12,12 +17,16 @@ const MeetingGrapQlMiniMongoAdapter: React.FC = () => {
useEffect(() => {
if (meetingData) {
if (!ready.current) {
ready.current = true;
onReady('MeetingGrapQlMiniMongoAdapter');
}
const meeting = JSON.parse(JSON.stringify(meetingData[0]));
const { meetingId } = meeting;
Meetings.upsert({ meetingId }, meeting);
}
}, [meetingData]);
return null;
return children;
};
export default MeetingGrapQlMiniMongoAdapter;

View File

@ -1,15 +1,20 @@
import { useSubscription } from '@apollo/client';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import CURRENT_USER_SUBSCRIPTION from '/imports/ui/core/graphql/queries/currentUserSubscription';
import { User } from '/imports/ui/Types/user';
import Users from '/imports/api/users';
import logger from '/imports/startup/client/logger';
import { AdapterProps } from '../graphqlToMiniMongoAdapterManager/component';
interface UserCurrentResponse {
user_current: Array<User>;
}
const UserGrapQlMiniMongoAdapter: React.FC = () => {
const UserGrapQlMiniMongoAdapter: React.FC<AdapterProps> = ({
onReady,
children,
}) => {
const ready = useRef(false);
const {
error,
data,
@ -24,6 +29,10 @@ const UserGrapQlMiniMongoAdapter: React.FC = () => {
useEffect(() => {
if (data && data.user_current) {
if (!ready.current) {
ready.current = true;
onReady('UserGrapQlMiniMongoAdapter');
}
const { userId } = data.user_current[0];
Users.upsert({ userId }, data.user_current[0]);
if (!userDataSetted) {
@ -31,7 +40,7 @@ const UserGrapQlMiniMongoAdapter: React.FC = () => {
}
}
}, [data]);
return null;
return children;
};
export default UserGrapQlMiniMongoAdapter;

View File

@ -1,17 +1,21 @@
import { useSubscription } from '@apollo/client';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import VoiceUsers from '/imports/api/voice-users/';
import logger from '/imports/startup/client/logger';
import { UserVoiceStreamResponse, voiceUserStream } from './queries';
import { AdapterProps } from '../graphqlToMiniMongoAdapterManager/component';
const VoiceUserGrapQlMiniMongoAdapter: React.FC = () => {
const VoiceUserGrapQlMiniMongoAdapter: React.FC<AdapterProps> = ({
onReady,
children,
}) => {
const ready = useRef(false);
const {
loading,
error,
data,
} = useSubscription<UserVoiceStreamResponse>(voiceUserStream);
const [voiceUserDataSetted, setVoiceUserDataSetted] = useState(false);
useEffect(() => {
if (error) {
logger.error('Error in VoiceUserGrapQlMiniMongoAdapter', error);
@ -30,12 +34,17 @@ const VoiceUserGrapQlMiniMongoAdapter: React.FC = () => {
useEffect(() => {
if (loading) {
// loading turns false only first audio join of the meeting, probably because it's a stream
if (!ready.current) {
ready.current = true;
onReady('VoiceUserGrapQlMiniMongoAdapter');
}
if (!voiceUserDataSetted) {
setVoiceUserDataSetted(true);
}
}
}, [loading]);
return null;
return children;
};
export default VoiceUserGrapQlMiniMongoAdapter;

View File

@ -1,14 +1,19 @@
// @ts-nocheck
/* eslint-disable */
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { useSubscription } from '@apollo/client';
import {
VIDEO_STREAMS_SUBSCRIPTION,
VideoStreamsResponse,
} from './queries';
import { setStreams } from './state';
import { AdapterProps } from '../../components-data/graphqlToMiniMongoAdapterManager/component';
const VideoStreamAdapter: React.FC = () => {
const VideoStreamAdapter: React.FC<AdapterProps> = ({
onReady,
children,
}) => {
const ready = useRef(false);
const { data, loading, error } = useSubscription<VideoStreamsResponse>(VIDEO_STREAMS_SUBSCRIPTION);
useEffect(() => {
@ -19,6 +24,8 @@ const VideoStreamAdapter: React.FC = () => {
return;
}
const streams = data.user_camera.map(({ streamId, user, voice }) => ({
stream: streamId,
deviceId: streamId.split('_')[2],
@ -34,7 +41,14 @@ const VideoStreamAdapter: React.FC = () => {
setStreams(streams);
}, [data]);
return null;
useEffect(()=>{
if (!ready.current && !loading) {
ready.current = true;
onReady('VideoStreamAdapter');
}
}, [loading])
return children;
};
export default VideoStreamAdapter;