2024-07-11 10:28:21 +01:00
|
|
|
import * as signalR from '@microsoft/signalr';
|
|
|
|
|
import { BehaviorSubject, Observable } from 'rxjs';
|
|
|
|
|
import { ok, Result, err } from 'neverthrow';
|
|
|
|
|
import { SessionStore } from 'src/app/store/session.service';
|
|
|
|
|
import { filter, first } from 'rxjs/operators';
|
2024-07-17 16:39:18 +01:00
|
|
|
import { v4 as uuidv4 } from 'uuid'
|
2024-07-18 16:19:30 +01:00
|
|
|
import { UserTypingDTO } from '../../data/dto/typing/typingInputDTO';
|
|
|
|
|
import { MessageOutPutDataDTO } from '../../data/dto/message/messageOutputDTO';
|
2024-07-31 17:23:44 +01:00
|
|
|
import { MessageDeleteInputDTO } from '../../data/dto/message/messageDeleteInputDTO';
|
2024-08-18 13:27:57 +01:00
|
|
|
import { MessageReactionInput } from '../../domain/use-case/message-reaction-by-id-use-case.service';
|
2024-08-02 11:34:57 +01:00
|
|
|
import { ISignalRInput } from './signal-r.service';
|
2024-07-26 16:54:32 +01:00
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
export class SignalRConnection {
|
|
|
|
|
|
|
|
|
|
private hubConnection: signalR.HubConnection;
|
2024-07-18 16:19:30 +01:00
|
|
|
private messageSubject: BehaviorSubject<MessageOutPutDataDTO> = new BehaviorSubject<MessageOutPutDataDTO>(null);
|
2024-07-31 17:23:44 +01:00
|
|
|
private messageDelete: BehaviorSubject<MessageOutPutDataDTO> = new BehaviorSubject<MessageOutPutDataDTO>(null);
|
|
|
|
|
private messageUPdateSubject: BehaviorSubject<MessageOutPutDataDTO> = new BehaviorSubject<MessageOutPutDataDTO>(null);
|
2024-07-18 16:19:30 +01:00
|
|
|
private typingSubject: BehaviorSubject<UserTypingDTO> = new BehaviorSubject<UserTypingDTO>(null);
|
|
|
|
|
private readAtSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
2024-07-11 10:28:21 +01:00
|
|
|
private connectionStateSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
|
|
|
private disconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
|
|
|
private reconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
|
|
|
private sendLaterSubject: BehaviorSubject<Object> = new BehaviorSubject<Object>(false);
|
|
|
|
|
private reconnect = true
|
2024-08-02 11:34:57 +01:00
|
|
|
|
2024-08-06 11:24:00 +01:00
|
|
|
private sendDataSubject: BehaviorSubject<{method: string, data: any}> = new BehaviorSubject<{method: string, data: any}>(null);
|
2024-08-07 11:18:41 +01:00
|
|
|
private pendingRequests: Map<string, { resolve: Function; reject: Function }> = new Map();
|
2024-07-11 10:28:21 +01:00
|
|
|
url: string
|
2024-08-07 15:23:23 +01:00
|
|
|
private hasConnectOnce = false
|
2024-07-11 10:28:21 +01:00
|
|
|
|
|
|
|
|
constructor({url}) {
|
|
|
|
|
this.url = url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
establishConnection(): Promise<Result<signalR.HubConnection, false>> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
const hubConnection = new signalR.HubConnectionBuilder()
|
|
|
|
|
.withUrl(this.url)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
this.hubConnection = hubConnection
|
|
|
|
|
|
|
|
|
|
hubConnection
|
|
|
|
|
.start()
|
|
|
|
|
.then(() => {
|
2024-08-15 10:26:20 +01:00
|
|
|
this.hubConnection = hubConnection
|
2024-08-07 15:23:23 +01:00
|
|
|
this.hasConnectOnce = true
|
2024-07-11 10:28:21 +01:00
|
|
|
console.log('Connection started');
|
|
|
|
|
this.connectionStateSubject.next(true);
|
|
|
|
|
this.join()
|
2024-07-18 16:19:30 +01:00
|
|
|
this.addMessageListener()
|
2024-07-11 10:28:21 +01:00
|
|
|
resolve(ok(hubConnection))
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.log('Error while starting connection: ' + error);
|
|
|
|
|
this.connectionStateSubject.next(false);
|
2024-08-07 15:23:23 +01:00
|
|
|
|
|
|
|
|
if(this.hasConnectOnce) {
|
|
|
|
|
setTimeout(()=> {
|
|
|
|
|
this.attempReconnect();
|
|
|
|
|
}, 2000)
|
|
|
|
|
}
|
|
|
|
|
resolve(err(false))
|
2024-07-11 10:28:21 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
hubConnection.onclose(() => {
|
|
|
|
|
console.log('Connection closed');
|
|
|
|
|
this.connectionStateSubject.next(false);
|
|
|
|
|
this.disconnectSubject.next(true)
|
2024-08-07 11:18:41 +01:00
|
|
|
|
|
|
|
|
this.pendingRequests.forEach((_, requestId) => {
|
|
|
|
|
const { reject } = this.pendingRequests.get(requestId);
|
2024-08-07 15:23:23 +01:00
|
|
|
reject(err(false));
|
2024-08-07 11:18:41 +01:00
|
|
|
this.pendingRequests.delete(requestId);
|
|
|
|
|
});
|
|
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
if(this.reconnect) {
|
|
|
|
|
this.attempReconnect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async attempReconnect() {
|
|
|
|
|
const attempConnection = await this.establishConnection()
|
|
|
|
|
|
|
|
|
|
if(attempConnection.isOk()) {
|
|
|
|
|
this.reconnectSubject.next(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public join() {
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
2024-07-17 16:39:18 +01:00
|
|
|
|
2024-07-18 16:19:30 +01:00
|
|
|
console.log('join=================')
|
2024-07-31 17:23:44 +01:00
|
|
|
this.hubConnection.invoke("Join", SessionStore.user.UserId, SessionStore.user.FullName);
|
2024-07-17 16:39:18 +01:00
|
|
|
//this.hubConnection.invoke("Join", 105, "UserFirefox");
|
2024-07-11 10:28:21 +01:00
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args:["Join", 312, "Daniel"]})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async sendMessage(data: Object & { requestId}):Promise<Result<any, any>> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
2024-07-18 16:19:30 +01:00
|
|
|
console.log('sendMessage', data)
|
2024-07-31 17:23:44 +01:00
|
|
|
this.hubConnection.invoke("SendMessage", data)
|
2024-07-11 10:28:21 +01:00
|
|
|
|
2024-08-07 11:18:41 +01:00
|
|
|
//
|
|
|
|
|
this.pendingRequests.set(data.requestId, { resolve, reject: (data) => resolve(data) });
|
|
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
this.messageSubject.pipe(
|
|
|
|
|
filter((message: any) => data.requestId == message?.requestId),
|
|
|
|
|
first()
|
|
|
|
|
).subscribe(value => {
|
|
|
|
|
resolve(ok(value))
|
|
|
|
|
console.log('Received valid value:', value);
|
2024-08-07 11:18:41 +01:00
|
|
|
//
|
|
|
|
|
this.pendingRequests.delete(data.requestId);
|
2024-07-11 10:28:21 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args: data})
|
|
|
|
|
return reject(err(false))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 17:23:44 +01:00
|
|
|
public async deleteMessage(data: MessageDeleteInputDTO) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
|
|
|
|
console.log('delete message', data)
|
|
|
|
|
this.hubConnection.invoke("DeleteMessage", data)
|
|
|
|
|
|
|
|
|
|
this.messageSubject.pipe(
|
|
|
|
|
filter((message: any) => data.requestId == message?.requestId),
|
2024-08-02 11:34:57 +01:00
|
|
|
first()
|
2024-07-31 17:23:44 +01:00
|
|
|
).subscribe((value) => {
|
|
|
|
|
resolve(ok(value))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 08:51:04 +01:00
|
|
|
public async typing(data: Object & { roomId, UserName, userId }):Promise<Result<any, any>> {
|
2024-07-17 16:39:18 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
const requestId = uuidv4()
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
2024-07-25 08:51:04 +01:00
|
|
|
console.log('send typing', data)
|
2024-07-17 16:39:18 +01:00
|
|
|
|
|
|
|
|
try {
|
2024-07-31 17:23:44 +01:00
|
|
|
this.hubConnection.invoke("Typing", data)
|
2024-07-17 16:39:18 +01:00
|
|
|
|
|
|
|
|
} catch (error) {}
|
|
|
|
|
|
|
|
|
|
this.typingSubject.pipe(
|
|
|
|
|
filter((message: any) => {
|
|
|
|
|
return requestId == message?.requestId
|
|
|
|
|
}),
|
|
|
|
|
first()
|
|
|
|
|
).subscribe(value => {
|
|
|
|
|
resolve(ok(value));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args: data})
|
|
|
|
|
return reject(err(false))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-18 16:19:30 +01:00
|
|
|
public async sendReadAt(data: Object & { roomId, memberId, chatMessageId}):Promise<Result<any, any>> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
const requestId = uuidv4()
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
|
|
|
|
|
|
|
|
|
try {
|
2024-07-31 17:23:44 +01:00
|
|
|
this.hubConnection.invoke("ReadAt", { roomId: data.roomId, memberId: data.memberId, requestId, messageId: data.chatMessageId} as any)
|
2024-07-18 16:19:30 +01:00
|
|
|
|
|
|
|
|
} catch (error) {}
|
|
|
|
|
|
|
|
|
|
this.readAtSubject.pipe(
|
|
|
|
|
filter((message: any) => {
|
|
|
|
|
return requestId == message?.requestId
|
|
|
|
|
}),
|
|
|
|
|
first()
|
|
|
|
|
).subscribe(value => {
|
|
|
|
|
resolve(ok(value));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args: data})
|
|
|
|
|
return reject(err(false))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 11:34:57 +01:00
|
|
|
|
|
|
|
|
public async sendReactMessage(data: MessageReactionInput):Promise<Result<any, any>> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
const requestId = uuidv4()
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
this.hubConnection.invoke("ReactMessage", { roomId: data.roomId, memberId: data.memberId, requestId, messageId: data.messageId} as any)
|
|
|
|
|
|
|
|
|
|
} catch (error) {}
|
|
|
|
|
|
|
|
|
|
this.messageUPdateSubject.pipe(
|
|
|
|
|
filter((message: any) => {
|
|
|
|
|
return requestId == message?.requestId
|
|
|
|
|
}),
|
|
|
|
|
first()
|
|
|
|
|
).subscribe(value => {
|
|
|
|
|
resolve(ok(value));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args: data})
|
|
|
|
|
return reject(err(false))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-08-15 14:50:00 +01:00
|
|
|
sendData(input: ISignalRInput): Promise<Result<any, any>> {
|
2024-08-02 11:34:57 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
if(this.connectionStateSubject.value == true) {
|
|
|
|
|
|
|
|
|
|
this.hubConnection.invoke(input.method, input.data)
|
|
|
|
|
|
|
|
|
|
this.sendDataSubject.pipe(
|
|
|
|
|
filter((message: any) => input.data.requestId == message?.requestId),
|
|
|
|
|
first()
|
|
|
|
|
).subscribe(value => {
|
|
|
|
|
resolve(ok(value))
|
|
|
|
|
console.log('Received valid value:', value);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this.sendLaterSubject.next({method: 'SendMessage', args: input})
|
|
|
|
|
return reject(err(false))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
private addMessageListener(): void {
|
2024-07-18 16:19:30 +01:00
|
|
|
console.log('listening')
|
|
|
|
|
this.hubConnection.on('ReceiveMessage', (message: MessageOutPutDataDTO) => {
|
2024-07-11 10:28:21 +01:00
|
|
|
console.log('ReceiveMessage', message)
|
|
|
|
|
this.messageSubject.next(message);
|
2024-08-02 11:34:57 +01:00
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'ReceiveMessage',
|
|
|
|
|
data: message
|
|
|
|
|
})
|
2024-07-11 10:28:21 +01:00
|
|
|
});
|
2024-07-17 16:39:18 +01:00
|
|
|
|
2024-07-25 11:27:39 +01:00
|
|
|
this.hubConnection.on('TypingMessage', (_typing: UserTypingDTO) => {
|
2024-07-18 16:19:30 +01:00
|
|
|
console.log('Typing', _typing)
|
|
|
|
|
this.typingSubject.next(_typing);
|
2024-08-02 11:34:57 +01:00
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'ReceiveMessage',
|
|
|
|
|
data: _typing
|
|
|
|
|
})
|
2024-07-18 16:19:30 +01:00
|
|
|
});
|
|
|
|
|
|
2024-08-06 11:24:00 +01:00
|
|
|
this.hubConnection.on('AvailableUsers', (data: any) => {
|
|
|
|
|
console.log('AvailableUsers', data)
|
|
|
|
|
this.typingSubject.next(data);
|
|
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'AvailableUsers',
|
|
|
|
|
data: data
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
2024-07-18 16:19:30 +01:00
|
|
|
this.hubConnection.on('ReadAt', (_message) => {
|
|
|
|
|
console.log('ReadAt', _message)
|
|
|
|
|
this.readAtSubject.next(_message);
|
2024-08-02 11:34:57 +01:00
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'ReceiveMessage',
|
|
|
|
|
data: _message
|
|
|
|
|
})
|
2024-07-17 16:39:18 +01:00
|
|
|
});
|
2024-07-18 16:19:30 +01:00
|
|
|
|
2024-07-31 17:23:44 +01:00
|
|
|
this.hubConnection.on('DeleteMessage', (_message) => {
|
|
|
|
|
console.log('DeleteMessage', _message)
|
|
|
|
|
this.messageDelete.next(_message);
|
2024-08-02 11:34:57 +01:00
|
|
|
this.sendDataSubject.next({
|
2024-08-13 10:52:35 +01:00
|
|
|
method: 'DeleteMessage',
|
2024-08-02 11:34:57 +01:00
|
|
|
data: _message
|
|
|
|
|
})
|
2024-07-31 17:23:44 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.hubConnection.on('UpdateMessage', (_message) => {
|
|
|
|
|
console.log('UpdateMessage', _message)
|
|
|
|
|
this.messageUPdateSubject.next(_message);
|
2024-08-02 11:34:57 +01:00
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'ReceiveMessage',
|
|
|
|
|
data: _message
|
|
|
|
|
})
|
2024-07-31 17:23:44 +01:00
|
|
|
})
|
|
|
|
|
|
2024-08-15 14:50:00 +01:00
|
|
|
|
|
|
|
|
this.hubConnection.on('GroupAddedMembers', (_message) => {
|
|
|
|
|
console.log('GroupAddedMembers', _message)
|
|
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'GroupAddedMembers',
|
|
|
|
|
data: _message
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.hubConnection.on('GroupDeletedMembers', (_message) => {
|
|
|
|
|
console.log('GroupDeletedMembers', _message)
|
|
|
|
|
this.sendDataSubject.next({
|
|
|
|
|
method: 'GroupDeletedMembers',
|
|
|
|
|
data: _message
|
|
|
|
|
})
|
|
|
|
|
})
|
2024-07-31 17:23:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getMessageUpdateSubject() {
|
|
|
|
|
return this.messageUPdateSubject.asObservable()
|
2024-07-11 10:28:21 +01:00
|
|
|
}
|
|
|
|
|
|
2024-07-18 16:19:30 +01:00
|
|
|
public getMessages() {
|
2024-07-11 10:28:21 +01:00
|
|
|
return this.messageSubject.asObservable()
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-18 16:19:30 +01:00
|
|
|
public getTyping() {
|
2024-07-17 16:39:18 +01:00
|
|
|
return this.typingSubject.asObservable()
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
public getConnectionState(): Observable<boolean> {
|
|
|
|
|
return this.connectionStateSubject.asObservable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getDisconnectTrigger(): Observable<boolean> {
|
|
|
|
|
return this.disconnectSubject.asObservable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getSendLater() {
|
|
|
|
|
return this.sendLaterSubject.asObservable();
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 17:23:44 +01:00
|
|
|
public getMessageDelete() {
|
|
|
|
|
return this.messageDelete.asObservable()
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 11:34:57 +01:00
|
|
|
public getData() {
|
|
|
|
|
return this.sendDataSubject.asObservable()
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 10:28:21 +01:00
|
|
|
public closeConnection(): void {
|
|
|
|
|
this.reconnect = false
|
|
|
|
|
if (this.hubConnection) {
|
|
|
|
|
this.hubConnection
|
|
|
|
|
.stop()
|
|
|
|
|
.then(() => {
|
|
|
|
|
console.log('Connection closed by user');
|
|
|
|
|
this.connectionStateSubject.next(false);
|
|
|
|
|
})
|
|
|
|
|
.catch(err => console.log('Error while closing connection: ' + err));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|