blob: f844c0c54ac10fc11d5a716198bd840dff05353e [file] [log] [blame]
idillon18283ac2023-01-07 12:06:42 -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 */
18
19import { IConversationRequest, IConversationSummary, Message, WebSocketMessageType } from 'jami-web-common';
20import { Container, Service } from 'typedi';
21
22import { ConversationRequestMetadata } from '../jamid/conversation-request-metadata.js';
23import { Jamid } from '../jamid/jamid.js';
24import { WebSocketServer } from '../websocket/websocket-server.js';
25import { ContactService } from './ContactService.js';
26
27const jamid = Container.get(Jamid);
28const webSocketServer = Container.get(WebSocketServer);
29const contactService = Container.get(ContactService);
30
31@Service()
32export class ConversationService {
33 constructor() {
34 jamid.events.onConversationRequestReceived.subscribe(async (signal) => {
35 const conversationRequest = await this.createConversationRequest(signal.accountId, signal.metadata);
36 webSocketServer.send(signal.accountId, WebSocketMessageType.ConversationRequest, conversationRequest);
37 });
38 }
39
40 async createConversationRequest(
41 accountId: string,
42 jamidRequest: ConversationRequestMetadata
43 ): Promise<IConversationRequest> {
44 const contact = await contactService.getContactFromUri(accountId, jamidRequest.from);
45 const membersNames = await this.getMembersNames(accountId, jamidRequest.id);
46
47 // Currently, this does not actually works.
48 // It seems the members can't be accessed yet. Same on jami-android
49 if (membersNames.length === 0) {
50 membersNames.push(contact.registeredName || contact.uri);
51 }
52
53 return {
54 conversationId: jamidRequest.id,
55 received: jamidRequest.received,
56 from: contact,
57 infos: {
58 // Build a dataUrl from the avatar
59 // TODO: Host the image and use a "normal" url instead
idillon9dd36502023-01-20 01:07:54 -050060 avatar: jamidRequest.avatar ? `data:image/jpeg;base64,${jamidRequest.avatar}` : undefined,
idillon18283ac2023-01-07 12:06:42 -050061 description: jamidRequest.description,
62 mode: jamidRequest.mode,
63 title: jamidRequest.title,
64 },
65 membersNames,
66 };
67 }
68
69 async createConversationSummary(
70 accountId: string,
71 conversationId: string
72 ): Promise<IConversationSummary | undefined> {
73 const infos = jamid.getConversationInfos(accountId, conversationId);
74 if (Object.keys(infos).length === 0) {
75 return undefined;
76 }
77 // Build a dataUrl from the avatar
78 // TODO: Host the image and use a "normal" url instead
idillon9dd36502023-01-20 01:07:54 -050079 infos.avatar = infos.avatar ? `data:image/jpeg;base64,${infos.avatar}` : undefined;
idillon18283ac2023-01-07 12:06:42 -050080
81 const membersNames = await this.getMembersNames(accountId, conversationId);
82 let lastMessage: Message | undefined;
83 // Skip "merge" type since they are of no interest for the user
84 // Should we add some protection to prevent infinite loop?
85 while (!lastMessage || lastMessage.type === 'merge') {
86 lastMessage = (
87 await jamid.getConversationMessages(accountId, conversationId, lastMessage?.linearizedParent || '', 1)
88 )[0];
89 }
90
91 return {
92 id: conversationId,
93 avatar: infos.avatar,
94 title: infos.title,
95 membersNames,
96 lastMessage,
97 };
98 }
99
100 async getMembersNames(accountId: string, conversationId: string): Promise<string[]> {
101 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
102 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
103
104 const members = jamid.getConversationMembers(accountId, conversationId);
105 const membersNames: string[] = [];
106 for (const member of members) {
107 // Exclude current user from returned conversation members
108 if (member.uri === accountUri) {
109 continue;
110 }
111
112 // Add usernames for conversation members
113 const { username } = await jamid.lookupAddress(member.uri, accountId);
114 membersNames.push(username || member.uri);
115 }
116
117 return membersNames;
118 }
119}