blob: 097d4161562be17411a017b250a60e3733f472ef [file] [log] [blame]
simonf929a362022-11-18 16:53:45 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
idillon3e378fd2022-12-23 11:48:12 -050018import { ComposingStatus, ConversationInfos, ConversationView, WebSocketMessageType } from 'jami-web-common';
idillona4b96ab2023-02-01 15:30:12 -050019import { useCallback, useEffect, useMemo, useState } from 'react';
simonf929a362022-11-18 16:53:45 -050020
21import LoadingPage from '../components/Loading';
simon09fe4822022-11-30 23:36:25 -050022import { createOptionalContext } from '../hooks/createOptionalContext';
idillon07d31cc2022-12-06 22:40:14 -050023import { useConversationDisplayName } from '../hooks/useConversationDisplayName';
simonf929a362022-11-18 16:53:45 -050024import { useUrlParams } from '../hooks/useUrlParams';
idillon07d31cc2022-12-06 22:40:14 -050025import { ConversationMember } from '../models/conversation-member';
simonf929a362022-11-18 16:53:45 -050026import { ConversationRouteParams } from '../router';
idillon07d31cc2022-12-06 22:40:14 -050027import { useConversationInfosQuery, useMembersQuery } from '../services/conversationQueries';
simonf929a362022-11-18 16:53:45 -050028import { WithChildren } from '../utils/utils';
29import { useAuthContext } from './AuthProvider';
idillona4b96ab2023-02-01 15:30:12 -050030import { useWebSocketContext } from './WebSocketProvider';
simonf929a362022-11-18 16:53:45 -050031
simon09fe4822022-11-30 23:36:25 -050032interface IConversationContext {
simonf929a362022-11-18 16:53:45 -050033 conversationId: string;
idillon07d31cc2022-12-06 22:40:14 -050034 conversationDisplayName: string;
35 conversationInfos: ConversationInfos;
36 members: ConversationMember[];
idillon3e378fd2022-12-23 11:48:12 -050037 composingMembers: ConversationMember[];
simonf929a362022-11-18 16:53:45 -050038}
39
simon09fe4822022-11-30 23:36:25 -050040const optionalConversationContext = createOptionalContext<IConversationContext>('ConversationContext');
41const ConversationContext = optionalConversationContext.Context;
42export const useConversationContext = optionalConversationContext.useOptionalContext;
simonf929a362022-11-18 16:53:45 -050043
44export default ({ children }: WithChildren) => {
45 const {
46 urlParams: { conversationId },
47 } = useUrlParams<ConversationRouteParams>();
idillon07d31cc2022-12-06 22:40:14 -050048 const { accountId, account } = useAuthContext();
idillona4b96ab2023-02-01 15:30:12 -050049 const webSocket = useWebSocketContext();
idillon3e378fd2022-12-23 11:48:12 -050050 const [composingMembers, setComposingMembers] = useState<ConversationMember[]>([]);
simonf929a362022-11-18 16:53:45 -050051
idillon07d31cc2022-12-06 22:40:14 -050052 const conversationInfosQuery = useConversationInfosQuery(conversationId!);
53 const membersQuery = useMembersQuery(conversationId!);
54
55 const isError = useMemo(
56 () => conversationInfosQuery.isError || membersQuery.isError,
57 [conversationInfosQuery.isError, membersQuery.isError]
58 );
59
60 const isLoading = useMemo(
61 () => conversationInfosQuery.isLoading || membersQuery.isLoading,
62 [conversationInfosQuery.isLoading, membersQuery.isLoading]
63 );
64
65 const conversationInfos = conversationInfosQuery.data;
66 const members = membersQuery.data;
67
idillon9e542ca2022-12-15 17:54:07 -050068 const conversationDisplayName = useConversationDisplayName(account, conversationInfos, members);
simonf929a362022-11-18 16:53:45 -050069
idillon3e378fd2022-12-23 11:48:12 -050070 const onComposingStatusChanged = useCallback(
71 (data: ComposingStatus) => {
72 // FIXME: data.conversationId is an empty string. Don't know why. Should not be.
73 // Good enough for now, but will be a problem if the user has more than one conversation with the same contact.
74 // if (data.conversationId === conversationId)
75 {
76 setComposingMembers((composingMembers) => {
77 if (!data.isWriting) {
78 return composingMembers.filter(({ contact }) => contact.uri !== data.contactId);
79 }
80
81 const isAlreadyIncluded = composingMembers.find((member) => member.contact.uri === data.contactId);
82 if (isAlreadyIncluded) {
83 return composingMembers;
84 }
85
86 const member = members?.find((member) => member.contact.uri === data.contactId);
87 if (!member) {
88 return composingMembers;
89 }
90
91 return [...composingMembers, member];
92 });
93 }
94 },
95 [/*conversationId,*/ members]
96 );
97
simonf929a362022-11-18 16:53:45 -050098 useEffect(() => {
idillona4b96ab2023-02-01 15:30:12 -050099 if (!conversationInfos || !conversationId) {
simonf929a362022-11-18 16:53:45 -0500100 return;
101 }
Misha Krieger-Raynauld20cf1c82022-11-23 20:26:50 -0500102
103 const conversationView: ConversationView = {
104 conversationId,
105 };
106
107 webSocket.send(WebSocketMessageType.ConversationView, conversationView);
idillonbad51972023-01-18 17:00:38 -0500108 webSocket.bind(WebSocketMessageType.ComposingStatus, onComposingStatusChanged);
idillon3e378fd2022-12-23 11:48:12 -0500109
idillonbad51972023-01-18 17:00:38 -0500110 return () => webSocket.unbind(WebSocketMessageType.ComposingStatus, onComposingStatusChanged);
idillon3e378fd2022-12-23 11:48:12 -0500111 }, [accountId, conversationInfos, conversationId, onComposingStatusChanged, webSocket]);
simonf929a362022-11-18 16:53:45 -0500112
simon5c677962022-12-02 16:51:54 -0500113 const value = useMemo(() => {
idillon07d31cc2022-12-06 22:40:14 -0500114 if (!conversationId || !conversationDisplayName || !conversationInfos || !members) {
simon5c677962022-12-02 16:51:54 -0500115 return;
116 }
117
118 return {
119 conversationId,
idillon07d31cc2022-12-06 22:40:14 -0500120 conversationDisplayName,
121 conversationInfos,
122 members,
idillon3e378fd2022-12-23 11:48:12 -0500123 composingMembers,
simon5c677962022-12-02 16:51:54 -0500124 };
idillon3e378fd2022-12-23 11:48:12 -0500125 }, [conversationId, conversationDisplayName, conversationInfos, members, composingMembers]);
simon5c677962022-12-02 16:51:54 -0500126
simonf929a362022-11-18 16:53:45 -0500127 if (isLoading) {
128 return <LoadingPage />;
129 }
simon5c677962022-12-02 16:51:54 -0500130 if (isError || !value) {
simonf929a362022-11-18 16:53:45 -0500131 return <div>Error loading conversation: {conversationId}</div>;
132 }
133
simon5c677962022-12-02 16:51:54 -0500134 return <ConversationContext.Provider value={value}>{children}</ConversationContext.Provider>;
simonf929a362022-11-18 16:53:45 -0500135};