Divide Conversation into ConversationInfos, ConversationMember, and ConversationSummary
- ConversationSummary is used to display ConversationList.
- Having the three separated will help managing queries.
- Adding ConversationSummary required to solve some inconsistencies in ConversationList, which was mixing contacts and conversations. ContactSearchResultList has been added as a quick fix . It will need more work.
- Some tools to uniformize conversation names have been introduced. They will need more work.
Note the diplaying of ConversationList is left broken in this commit.
Change-Id: I29337906cc43781a9c4790735490a6ee2cc51cb0
diff --git a/client/src/contexts/CallManagerProvider.tsx b/client/src/contexts/CallManagerProvider.tsx
index c04010f..b99df69 100644
--- a/client/src/contexts/CallManagerProvider.tsx
+++ b/client/src/contexts/CallManagerProvider.tsx
@@ -15,7 +15,7 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { CallBegin, WebSocketMessageType } from 'jami-web-common';
+import { CallBegin, ConversationInfos, WebSocketMessageType } from 'jami-web-common';
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
@@ -23,9 +23,9 @@
import { AlertSnackbar } from '../components/AlertSnackbar';
import { RemoteVideoOverlay } from '../components/VideoOverlay';
import { useUrlParams } from '../hooks/useUrlParams';
-import { Conversation } from '../models/conversation';
+import { ConversationMember } from '../models/conversation-member';
import { ConversationRouteParams } from '../router';
-import { useConversationQuery } from '../services/conversationQueries';
+import { useConversationInfosQuery, useMembersQuery } from '../services/conversationQueries';
import { SetState, WithChildren } from '../utils/utils';
import CallProvider, { CallRole } from './CallProvider';
import WebRtcProvider from './WebRtcProvider';
@@ -39,16 +39,16 @@
type ICallManagerContext = {
callData: CallData | undefined;
- callConversation: Conversation | undefined;
-
+ callConversationInfos: ConversationInfos | undefined;
+ callMembers: ConversationMember[] | undefined;
startCall: SetState<CallData | undefined>;
exitCall: () => void;
};
const defaultCallManagerContext: ICallManagerContext = {
callData: undefined,
- callConversation: undefined,
-
+ callConversationInfos: undefined,
+ callMembers: undefined,
startCall: () => {},
exitCall: () => {},
};
@@ -60,7 +60,8 @@
const [callData, setCallData] = useState<CallData>();
const webSocket = useContext(WebSocketContext);
const navigate = useNavigate();
- const { conversation } = useConversationQuery(callData?.conversationId);
+ const { data: conversationInfos } = useConversationInfosQuery(callData?.conversationId);
+ const { data: members } = useMembersQuery(callData?.conversationId);
const { urlParams } = useUrlParams<ConversationRouteParams>();
const [missedCallConversationId, setMissedCallConversationId] = useState<string>();
const { t } = useTranslation();
@@ -108,10 +109,11 @@
() => ({
startCall,
callData,
- callConversation: conversation,
+ callConversationInfos: conversationInfos,
+ callMembers: members,
exitCall,
}),
- [startCall, callData, conversation, exitCall]
+ [startCall, callData, conversationInfos, members, exitCall]
);
return (
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index 7517133..9570ee5 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -19,7 +19,7 @@
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { createOptionalContext } from '../hooks/createOptionalContext';
-import { Conversation } from '../models/conversation';
+import { ConversationMember } from '../models/conversation-member';
import { callTimeoutMs } from '../utils/constants';
import { AsyncSetState, SetState, WithChildren } from '../utils/utils';
import { CallData, CallManagerContext } from './CallManagerProvider';
@@ -75,19 +75,19 @@
export default ({ children }: WithChildren) => {
const webSocket = useContext(WebSocketContext);
- const { callConversation, callData, exitCall } = useContext(CallManagerContext);
+ const { callMembers, callData, exitCall } = useContext(CallManagerContext);
const webRtcContext = useWebRtcContext(true);
const dependencies = useMemo(
() => ({
webSocket,
webRtcContext,
- callConversation,
+ callMembers,
callData,
exitCall,
conversationId: callData?.conversationId,
}),
- [webSocket, webRtcContext, callConversation, callData, exitCall]
+ [webSocket, webRtcContext, callMembers, callData, exitCall]
);
return (
@@ -104,7 +104,7 @@
const CallProvider = ({
webRtcContext,
- callConversation,
+ callMembers,
callData,
exitCall,
conversationId,
@@ -112,7 +112,7 @@
}: {
webSocket: IWebSocketContext;
webRtcContext: IWebRtcContext;
- callConversation: Conversation;
+ callMembers: ConversationMember[];
callData: CallData;
exitCall: () => void;
conversationId: string;
@@ -147,7 +147,7 @@
// TODO: This logic will have to change to support multiple people in a call. Could we move this logic to the server?
// The client could make a single request with the conversationId, and the server would be tasked with sending
// all the individual requests to the members of the conversation.
- const contactUri = useMemo(() => callConversation.getFirstMember().contact.uri, [callConversation]);
+ const contactUri = useMemo(() => callMembers[0].contact.uri, [callMembers]);
useEffect(() => {
if (callStatus !== CallStatus.InCall) {
diff --git a/client/src/contexts/ConversationProvider.tsx b/client/src/contexts/ConversationProvider.tsx
index 8488feb..b6d2f4a 100644
--- a/client/src/contexts/ConversationProvider.tsx
+++ b/client/src/contexts/ConversationProvider.tsx
@@ -15,22 +15,25 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { ConversationView, WebSocketMessageType } from 'jami-web-common';
+import { ConversationInfos, ConversationView, WebSocketMessageType } from 'jami-web-common';
import { useContext, useEffect, useMemo } from 'react';
import LoadingPage from '../components/Loading';
import { createOptionalContext } from '../hooks/createOptionalContext';
+import { useConversationDisplayName } from '../hooks/useConversationDisplayName';
import { useUrlParams } from '../hooks/useUrlParams';
-import { Conversation } from '../models/conversation';
+import { ConversationMember } from '../models/conversation-member';
import { ConversationRouteParams } from '../router';
-import { useConversationQuery } from '../services/conversationQueries';
+import { useConversationInfosQuery, useMembersQuery } from '../services/conversationQueries';
import { WithChildren } from '../utils/utils';
import { useAuthContext } from './AuthProvider';
import { WebSocketContext } from './WebSocketProvider';
interface IConversationContext {
conversationId: string;
- conversation: Conversation;
+ conversationDisplayName: string;
+ conversationInfos: ConversationInfos;
+ members: ConversationMember[];
}
const optionalConversationContext = createOptionalContext<IConversationContext>('ConversationContext');
@@ -41,13 +44,29 @@
const {
urlParams: { conversationId },
} = useUrlParams<ConversationRouteParams>();
- const { accountId } = useAuthContext();
+ const { accountId, account } = useAuthContext();
const webSocket = useContext(WebSocketContext);
- const { conversation, isLoading, isError } = useConversationQuery(conversationId!);
+ const conversationInfosQuery = useConversationInfosQuery(conversationId!);
+ const membersQuery = useMembersQuery(conversationId!);
+
+ const isError = useMemo(
+ () => conversationInfosQuery.isError || membersQuery.isError,
+ [conversationInfosQuery.isError, membersQuery.isError]
+ );
+
+ const isLoading = useMemo(
+ () => conversationInfosQuery.isLoading || membersQuery.isLoading,
+ [conversationInfosQuery.isLoading, membersQuery.isLoading]
+ );
+
+ const conversationInfos = conversationInfosQuery.data;
+ const members = membersQuery.data;
+
+ const conversationDisplayName = useConversationDisplayName(account, conversationId, conversationInfos, members);
useEffect(() => {
- if (!conversation || !conversationId || !webSocket) {
+ if (!conversationInfos || !conversationId || !webSocket) {
return;
}
@@ -56,18 +75,20 @@
};
webSocket.send(WebSocketMessageType.ConversationView, conversationView);
- }, [accountId, conversation, conversationId, webSocket]);
+ }, [accountId, conversationInfos, conversationId, webSocket]);
const value = useMemo(() => {
- if (!conversation || !conversationId) {
+ if (!conversationId || !conversationDisplayName || !conversationInfos || !members) {
return;
}
return {
conversationId,
- conversation,
+ conversationDisplayName,
+ conversationInfos,
+ members,
};
- }, [conversationId, conversation]);
+ }, [conversationId, conversationDisplayName, conversationInfos, members]);
if (isLoading) {
return <LoadingPage />;
diff --git a/client/src/contexts/MessengerProvider.tsx b/client/src/contexts/MessengerProvider.tsx
index c02a1b3..8a405bb 100644
--- a/client/src/contexts/MessengerProvider.tsx
+++ b/client/src/contexts/MessengerProvider.tsx
@@ -15,32 +15,25 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { ConversationMessage, IConversation, LookupResult, WebSocketMessageType } from 'jami-web-common';
+import { ConversationMessage, IConversationSummary, LookupResult, WebSocketMessageType } from 'jami-web-common';
import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { Contact } from '../models/contact';
-import { Conversation } from '../models/conversation';
-import { setRefreshFromSlice } from '../redux/appSlice';
-import { useAppDispatch, useAppSelector } from '../redux/hooks';
+import { useConversationsSummariesQuery, useRefreshConversationsSummaries } from '../services/conversationQueries';
import { SetState } from '../utils/utils';
import { useAuthContext } from './AuthProvider';
import { WebSocketContext } from './WebSocketProvider';
export interface IMessengerContext {
- conversations: Conversation[] | undefined;
+ conversationsSummaries: IConversationSummary[] | undefined;
setSearchQuery: SetState<string | undefined>;
- searchResult: Conversation | undefined;
-
- newContactId: string | undefined;
- setNewContactId: SetState<string | undefined>;
+ searchResult: Contact[] | undefined;
}
const defaultMessengerContext: IMessengerContext = {
- conversations: undefined,
- newContactId: undefined,
- setNewContactId: () => {},
+ conversationsSummaries: undefined,
setSearchQuery: () => {},
searchResult: undefined,
};
@@ -48,29 +41,15 @@
export const MessengerContext = createContext<IMessengerContext>(defaultMessengerContext);
export default ({ children }: { children: ReactNode }) => {
- const { refresh } = useAppSelector((state) => state.userInfo);
- const dispatch = useAppDispatch();
const { accountId, axiosInstance } = useAuthContext();
const webSocket = useContext(WebSocketContext);
- const [conversations, setConversations] = useState<Conversation[] | undefined>(undefined);
const [searchQuery, setSearchQuery] = useState<string>();
- const [searchResult, setSearchResults] = useState<Conversation | undefined>(undefined);
- const [newContactId, setNewContactId] = useState<string>();
+ const [searchResult, setSearchResults] = useState<Contact[] | undefined>(undefined);
- useEffect(() => {
- const controller = new AbortController();
- axiosInstance
- .get<IConversation[]>('/conversations', {
- signal: controller.signal,
- })
- .then(({ data }) => {
- setConversations(
- Object.values(data).map((conversationInterface) => Conversation.fromInterface(conversationInterface))
- );
- });
- // return () => controller.abort()
- }, [axiosInstance, accountId, refresh]);
+ const conversationsSummariesQuery = useConversationsSummariesQuery();
+ const conversationsSummaries = conversationsSummariesQuery.data;
+ const refreshConversationsSummaries = useRefreshConversationsSummaries();
useEffect(() => {
if (!webSocket) {
@@ -78,7 +57,7 @@
}
const conversationMessageListener = (_data: ConversationMessage) => {
- dispatch(setRefreshFromSlice());
+ refreshConversationsSummaries();
};
webSocket.bind(WebSocketMessageType.ConversationMessage, conversationMessageListener);
@@ -86,7 +65,7 @@
return () => {
webSocket.unbind(WebSocketMessageType.ConversationMessage, conversationMessageListener);
};
- }, [webSocket, dispatch]);
+ }, [refreshConversationsSummaries, webSocket]);
useEffect(() => {
if (!searchQuery) return;
@@ -97,7 +76,7 @@
})
.then(({ data }) => {
const contact = new Contact(data.address, data.username);
- setSearchResults(contact ? Conversation.fromSingleContact(contact) : undefined);
+ setSearchResults([contact]);
})
.catch(() => {
setSearchResults(undefined);
@@ -105,15 +84,13 @@
// return () => controller.abort() // crash on React18
}, [accountId, searchQuery, axiosInstance]);
- const value = useMemo(
+ const value = useMemo<IMessengerContext>(
() => ({
- conversations,
+ conversationsSummaries,
setSearchQuery,
searchResult,
- newContactId,
- setNewContactId,
}),
- [conversations, setSearchQuery, searchResult, newContactId, setNewContactId]
+ [conversationsSummaries, setSearchQuery, searchResult]
);
return <MessengerContext.Provider value={value}>{children}</MessengerContext.Provider>;
diff --git a/client/src/contexts/WebRtcProvider.tsx b/client/src/contexts/WebRtcProvider.tsx
index 55a3a1d..e35df0c 100644
--- a/client/src/contexts/WebRtcProvider.tsx
+++ b/client/src/contexts/WebRtcProvider.tsx
@@ -20,7 +20,7 @@
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { createOptionalContext } from '../hooks/createOptionalContext';
-import { Conversation } from '../models/conversation';
+import { ConversationMember } from '../models/conversation-member';
import { WithChildren } from '../utils/utils';
import { useAuthContext } from './AuthProvider';
import { CallManagerContext } from './CallManagerProvider';
@@ -52,7 +52,7 @@
const { account } = useAuthContext();
const [webRtcConnection, setWebRtcConnection] = useState<RTCPeerConnection | undefined>();
const webSocket = useContext(WebSocketContext);
- const { callConversation, callData } = useContext(CallManagerContext);
+ const { callConversationInfos, callMembers, callData } = useContext(CallManagerContext);
useEffect(() => {
if (webRtcConnection && !callData) {
@@ -85,10 +85,11 @@
() => ({
webRtcConnection,
webSocket,
- conversation: callConversation,
+ conversationInfos: callConversationInfos,
+ members: callMembers,
conversationId: callData?.conversationId,
}),
- [webRtcConnection, webSocket, callConversation, callData?.conversationId]
+ [webRtcConnection, webSocket, callConversationInfos, callMembers, callData?.conversationId]
);
return (
@@ -104,14 +105,14 @@
};
const useWebRtcContextValue = ({
- conversation,
+ members,
conversationId,
webRtcConnection,
webSocket,
}: {
webRtcConnection: RTCPeerConnection;
webSocket: IWebSocketContext;
- conversation: Conversation;
+ members: ConversationMember[];
conversationId: string;
}) => {
const [localStream, setLocalStream] = useState<MediaStream>();
@@ -133,7 +134,7 @@
const [iceCandidateQueue, setIceCandidateQueue] = useState<RTCIceCandidate[]>([]);
// TODO: This logic will have to change to support multiple people in a call
- const contactUri = useMemo(() => conversation.getFirstMember().contact.uri, [conversation]);
+ const contactUri = useMemo(() => members[0]?.contact.uri, [members]);
const getMediaDevices = useCallback(async (): Promise<MediaDevicesInfo> => {
try {