ov-components: Add encryption key mismatch warning in chat panel and update translations

master
Carlos Santos 2025-12-17 17:39:04 +01:00
parent ddc7226b64
commit d229da9e47
14 changed files with 106 additions and 20 deletions

View File

@ -6,6 +6,13 @@
</button>
</div>
@if (hasEncryptionKeyMismatch()) {
<div class="encryption-warning">
<mat-icon>warning</mat-icon>
<p>{{ 'PANEL.CHAT.ENCRYPTION_KEY_MISMATCH' | translate }}</p>
</div>
}
<div class="text-container">
<p class="text-info">{{ 'PANEL.CHAT.SUBTITLE' | translate }}</p>
</div>

View File

@ -1,3 +1,29 @@
.encryption-warning {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px;
margin: 10px;
background-color: rgba(255, 152, 0, 0.1);
border: 1px solid rgba(255, 152, 0, 0.5);
border-radius: var(--ov-surface-radius);
color: var(--ov-warn-color, #ff9800);
font-size: 13px;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
}
p {
margin: 0;
line-height: 1.4;
width: fit-content;
}
}
.text-container {
color: var(--ov-text-primary-color);
text-align: center;

View File

@ -1,9 +1,11 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { ChatMessage } from '../../../models/chat.model';
import { PanelType } from '../../../models/panel.model';
import { ChatService } from '../../../services/chat/chat.service';
import { E2eeService } from '../../../services/e2ee/e2ee.service';
import { PanelService } from '../../../services/panel/panel.service';
import { ParticipantService } from '../../../services/participant/participant.service';
/**
*
@ -20,15 +22,15 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
/**
* @ignore
*/
@ViewChild('chatScroll') chatScroll: ElementRef;
@ViewChild('chatScroll') chatScroll: ElementRef = new ElementRef(null);
/**
* @ignore
*/
@ViewChild('chatInput') chatInput: ElementRef;
@ViewChild('chatInput') chatInput: ElementRef = new ElementRef(null);
/**
* @ignore
*/
message: string;
message: string = '';
/**
* @ignore
*/
@ -42,8 +44,10 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
constructor(
private chatService: ChatService,
private panelService: PanelService,
private cd: ChangeDetectorRef
) {}
private cd: ChangeDetectorRef,
private e2eeService: E2eeService,
private participantService: ParticipantService
) { }
/**
* @ignore
@ -73,7 +77,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
/**
* @ignore
*/
eventKeyPress(event) {
eventKeyPress(event: KeyboardEvent): void {
// Pressed 'Enter' key
if (event && event.keyCode === 13) {
event.preventDefault();
@ -109,6 +113,19 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
this.panelService.togglePanel(PanelType.CHAT);
}
/**
* @ignore
*/
hasEncryptionKeyMismatch = computed(() => {
if (!this.e2eeService.isEnabled) {
return false;
}
const remoteParticipants = this.participantService.remoteParticipantsSignal();
return remoteParticipants.some(p => p.hasEncryptionError);
});
private subscribeToMessages() {
this.chatService.chatMessages$.pipe(takeUntil(this.destroy$)).subscribe((messages: ChatMessage[]) => {
this.messageList = messages;

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "发送消息...",
"SEND": "发送",
"MESSAGE_SENT_NOTIFICATION": "消息已发送",
"OPEN_CHAT": "打开"
"OPEN_CHAT": "打开",
"ENCRYPTION_KEY_MISMATCH": "有参与者使用不同的加密密钥连接。您将不会收到他们的消息。"
},
"ACTIVITIES": {
"TITLE": "活动"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Eine Nachricht senden...",
"SEND": "Senden",
"MESSAGE_SENT_NOTIFICATION": "Nachricht gesendet",
"OPEN_CHAT": "ÖFFNEN"
"OPEN_CHAT": "ÖFFNEN",
"ENCRYPTION_KEY_MISMATCH": "Es gibt Teilnehmer, die mit einem anderen Verschlüsselungsschlüssel verbunden sind. Sie werden deren Nachrichten nicht empfangen."
},
"ACTIVITIES": {
"TITLE": "Aktivitäten"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Send a message...",
"SEND": "Send",
"MESSAGE_SENT_NOTIFICATION": "message sent",
"OPEN_CHAT": "OPEN"
"OPEN_CHAT": "OPEN",
"ENCRYPTION_KEY_MISMATCH": "There are participants connected with a different encryption key. You will not receive messages from them."
},
"PARTICIPANTS": {
"TITLE": "Participants",

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Enviar mensaje...",
"SEND": "Enviar",
"MESSAGE_SENT_NOTIFICATION": "mensaje enviado",
"OPEN_CHAT": "ABRIR"
"OPEN_CHAT": "ABRIR",
"ENCRYPTION_KEY_MISMATCH": "Hay participantes conectados con una clave de cifrado diferente a la tuya. No recibirás mensajes de ellos."
},
"ACTIVITIES": {
"TITLE": "Actividades"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Envoyer un message...",
"SEND": "Envoyer",
"MESSAGE_SENT_NOTIFICATION": "message envoyé",
"OPEN_CHAT": "OUVRIR"
"OPEN_CHAT": "OUVRIR",
"ENCRYPTION_KEY_MISMATCH": "Il y a des participants connectés avec une clé de chiffrement différente. Vous ne recevrez pas leurs messages."
},
"ACTIVITIES": {
"TITLE": "Activités"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "एक संदेश भेजें ...",
"SEND": "भेजें",
"MESSAGE_SENT_NOTIFICATION": "संदेश भेजा गया",
"OPEN_CHAT": "खोलें"
"OPEN_CHAT": "खोलें",
"ENCRYPTION_KEY_MISMATCH": "कुछ सदस्य एक अलग एन्क्रिप्शन कुंजी के साथ जुड़े हैं। आपको उनके संदेश प्राप्त नहीं होंगे।"
},
"ACTIVITIES": {
"TITLE": "गतिविधियाँ"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Invia un messaggio...",
"SEND": "Invia",
"MESSAGE_SENT_NOTIFICATION": "messaggio inviato",
"OPEN_CHAT": "APRI"
"OPEN_CHAT": "APRI",
"ENCRYPTION_KEY_MISMATCH": "Ci sono partecipanti connessi con una chiave di crittografia diversa. Non riceverai i loro messaggi."
},
"ACTIVITIES": {
"TITLE": "Attività"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "メッセージを送信...",
"SEND": "送信する",
"MESSAGE_SENT_NOTIFICATION": "メッセージを送信しました",
"OPEN_CHAT": "開く"
"OPEN_CHAT": "開く",
"ENCRYPTION_KEY_MISMATCH": "異なる暗号化キーで接続している参加者がいます。彼らからのメッセージは受信できません。"
},
"ACTIVITIES": {
"TITLE": "アクティビティ"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Stuur een bericht ...",
"SEND": "Versturen",
"MESSAGE_SENT_NOTIFICATION": "bericht verzonden",
"OPEN_CHAT": "OPENEN"
"OPEN_CHAT": "OPENEN",
"ENCRYPTION_KEY_MISMATCH": "Er zijn deelnemers verbonden met een andere versleutelingssleutel. U ontvangt hun berichten niet."
},
"ACTIVITIES": {
"TITLE": "Activiteiten"

View File

@ -85,7 +85,8 @@
"PLACEHOLDER": "Enviar uma mensagem...",
"SEND": "Enviar",
"MESSAGE_SENT_NOTIFICATION": "mensagem enviada",
"OPEN_CHAT": "ABRIR"
"OPEN_CHAT": "ABRIR",
"ENCRYPTION_KEY_MISMATCH": "Existem participantes conectados com uma chave de criptografia diferente. Você não receberá mensagens deles."
},
"ACTIVITIES": {
"TITLE": "Atividades"

View File

@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
import { Injectable, Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { BehaviorSubject, Observable } from 'rxjs';
import { ILogger } from '../../models/logger.model';
import { ParticipantModel, ParticipantProperties } from '../../models/participant.model';
@ -6,7 +7,6 @@ import { OpenViduComponentsConfigService } from '../config/directive-config.serv
import { GlobalConfigService } from '../config/global-config.service';
import { LoggerService } from '../logger/logger.service';
import { OpenViduService } from '../openvidu/openvidu.service';
import {
AudioCaptureOptions,
DataPublishOptions,
@ -19,8 +19,9 @@ import {
VideoCaptureOptions,
VideoPresets
} from 'livekit-client';
import { StorageService } from '../storage/storage.service';
import { E2eeService } from '../e2ee/e2ee.service';
import { OpenViduService } from '../openvidu/openvidu.service';
import { StorageService } from '../storage/storage.service';
@Injectable({
providedIn: 'root'
@ -28,6 +29,7 @@ import { E2eeService } from '../e2ee/e2ee.service';
export class ParticipantService {
/**
* Local participant Observable which pushes the local participant object in every update.
* @deprecated Please prefer `localParticipantSignal` for reactive updates and `localParticipant$` when using RxJS.
*/
localParticipant$: Observable<ParticipantModel | undefined>;
private localParticipantBS: BehaviorSubject<ParticipantModel | undefined> = new BehaviorSubject<ParticipantModel | undefined>(
@ -36,9 +38,25 @@ export class ParticipantService {
/**
* Remote participants Observable which pushes the remote participants array in every update.
* @deprecated Please prefer `remoteParticipantsSignal` for reactive updates and `remoteParticipants$` when using RxJS.
*/
remoteParticipants$: Observable<ParticipantModel[]>;
private remoteParticipantsBS: BehaviorSubject<ParticipantModel[]> = new BehaviorSubject<ParticipantModel[]>([]);
/**
* Local participant Signal for reactive programming with Angular signals.
* This is a modern alternative to localParticipant$ Observable.
* @since Angular 16+
*/
localParticipantSignal: Signal<ParticipantModel | undefined>;
/**
* Remote participants Signal for reactive programming with Angular signals.
* This is a modern alternative to remoteParticipants$ Observable.
* @since Angular 16+
*/
remoteParticipantsSignal: Signal<ParticipantModel[]>;
private localParticipant: ParticipantModel | undefined;
private remoteParticipants: ParticipantModel[] = [];
private log: ILogger;
@ -57,6 +75,10 @@ export class ParticipantService {
this.log = this.loggerSrv.get('ParticipantService');
this.localParticipant$ = this.localParticipantBS.asObservable();
this.remoteParticipants$ = this.remoteParticipantsBS.asObservable();
// Create signals from observables for modern reactive programming
this.localParticipantSignal = toSignal(this.localParticipant$, { initialValue: undefined });
this.remoteParticipantsSignal = toSignal(this.remoteParticipants$, { initialValue: [] });
}
/**
@ -81,6 +103,8 @@ export class ParticipantService {
/**
* Returns the local participant object.
*
* @deprecated Please prefer `localParticipantSignal()` for reactive updates and `localParticipant$` when using RxJS.
*/
getLocalParticipant(): ParticipantModel | undefined {
return this.localParticipant;
@ -435,6 +459,8 @@ export class ParticipantService {
/**
* Returns all remote participants in the room.
*
* @deprecated Please prefer `remoteParticipantsSignal()` for automatic reactive updates or `remoteParticipants$` when using Observables.
*/
getRemoteParticipants(): ParticipantModel[] {
return this.remoteParticipants;