mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: simplify STT operations with single a connectionId
parent
ce4b783ef0
commit
0d73bdc462
|
@ -149,11 +149,12 @@ public class ProtocolElements {
|
||||||
public static final String FORCIBLYRECONNECTSUBSCRIBER_SDPOFFER_PARAM = "sdpOffer";
|
public static final String FORCIBLYRECONNECTSUBSCRIBER_SDPOFFER_PARAM = "sdpOffer";
|
||||||
|
|
||||||
public static final String SUBSCRIBETOSPEECHTOTEXT_METHOD = "subscribeToSpeechToText";
|
public static final String SUBSCRIBETOSPEECHTOTEXT_METHOD = "subscribeToSpeechToText";
|
||||||
public static final String SUBSCRIBETOSPEECHTOTEXT_CONNECTIONIDS_PARAM = "connectionIds";
|
public static final String SUBSCRIBETOSPEECHTOTEXT_CONNECTIONID_PARAM = "connectionId";
|
||||||
public static final String SUBSCRIBETOSPEECHTOTEXT_LANG_PARAM = "lang";
|
public static final String SUBSCRIBETOSPEECHTOTEXT_LANG_PARAM = "lang";
|
||||||
|
|
||||||
public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_METHOD = "unsubscribeFromSpeechToText";
|
public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_METHOD = "unsubscribeFromSpeechToText";
|
||||||
public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_CONNECTIONIDS_PARAM = "connectionIds";
|
public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_CONNECTIONID_PARAM = "connectionId";
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
|
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
@ -189,10 +188,10 @@ public abstract class SessionManager {
|
||||||
Boolean videoActive, Boolean audioActive);
|
Boolean videoActive, Boolean audioActive);
|
||||||
|
|
||||||
public abstract void onSubscribeToSpeechToText(Participant participant, Integer transactionId, String lang,
|
public abstract void onSubscribeToSpeechToText(Participant participant, Integer transactionId, String lang,
|
||||||
JsonArray connectionIds);
|
String connectionId);
|
||||||
|
|
||||||
public abstract void onUnsubscribeFromSpeechToText(Participant participant, Integer transactionId,
|
public abstract void onUnsubscribeFromSpeechToText(Participant participant, Integer transactionId,
|
||||||
JsonArray connectionIds);
|
String connectionId);
|
||||||
|
|
||||||
public void onEcho(String participantPrivateId, Integer requestId) {
|
public void onEcho(String participantPrivateId, Integer requestId) {
|
||||||
sessionEventsHandler.onEcho(participantPrivateId, requestId);
|
sessionEventsHandler.onEcho(participantPrivateId, requestId);
|
||||||
|
|
|
@ -42,7 +42,6 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
@ -1202,13 +1201,13 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribeToSpeechToText(Participant participant, Integer transactionId, String lang,
|
public void onSubscribeToSpeechToText(Participant participant, Integer transactionId, String lang,
|
||||||
JsonArray connectionIds) {
|
String connectionId) {
|
||||||
sessionEventsHandler.onUnsubscribeToSpeechToText(participant, transactionId, new OpenViduException(
|
sessionEventsHandler.onUnsubscribeToSpeechToText(participant, transactionId, new OpenViduException(
|
||||||
Code.WRONG_OPENVIDU_EDITION_ERROR_CODE, "Speech To Text requires OpenVidu Pro/Enterprise edition"));
|
Code.WRONG_OPENVIDU_EDITION_ERROR_CODE, "Speech To Text requires OpenVidu Pro/Enterprise edition"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnsubscribeFromSpeechToText(Participant participant, Integer transactionId, JsonArray connectionIds) {
|
public void onUnsubscribeFromSpeechToText(Participant participant, Integer transactionId, String connectionId) {
|
||||||
sessionEventsHandler.onUnsubscribeToSpeechToText(participant, transactionId, new OpenViduException(
|
sessionEventsHandler.onUnsubscribeToSpeechToText(participant, transactionId, new OpenViduException(
|
||||||
Code.WRONG_OPENVIDU_EDITION_ERROR_CODE, "Speech To Text requires OpenVidu Pro/Enterprise edition"));
|
Code.WRONG_OPENVIDU_EDITION_ERROR_CODE, "Speech To Text requires OpenVidu Pro/Enterprise edition"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
@ -733,17 +732,17 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
|
||||||
|
|
||||||
private void subscribeToSpeechToText(RpcConnection rpcConnection, Request<JsonObject> request) {
|
private void subscribeToSpeechToText(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||||
Participant participant = sanityCheckOfSession(rpcConnection, "subscribeToSpeechToText");
|
Participant participant = sanityCheckOfSession(rpcConnection, "subscribeToSpeechToText");
|
||||||
JsonArray connectionIds = (JsonArray) RpcHandler.getParam(request,
|
String connectionId = RpcHandler.getStringParam(request,
|
||||||
ProtocolElements.SUBSCRIBETOSPEECHTOTEXT_CONNECTIONIDS_PARAM);
|
ProtocolElements.SUBSCRIBETOSPEECHTOTEXT_CONNECTIONID_PARAM);
|
||||||
String lang = RpcHandler.getStringParam(request, ProtocolElements.SUBSCRIBETOSPEECHTOTEXT_LANG_PARAM);
|
String lang = RpcHandler.getStringParam(request, ProtocolElements.SUBSCRIBETOSPEECHTOTEXT_LANG_PARAM);
|
||||||
sessionManager.onSubscribeToSpeechToText(participant, request.getId(), lang, connectionIds);
|
sessionManager.onSubscribeToSpeechToText(participant, request.getId(), lang, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unsubscribeFromSpeechToText(RpcConnection rpcConnection, Request<JsonObject> request) {
|
private void unsubscribeFromSpeechToText(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||||
Participant participant = sanityCheckOfSession(rpcConnection, "unsubscribeFromSpeechToText");
|
Participant participant = sanityCheckOfSession(rpcConnection, "unsubscribeFromSpeechToText");
|
||||||
JsonArray connectionIds = (JsonArray) RpcHandler.getParam(request,
|
String connectionId = RpcHandler.getStringParam(request,
|
||||||
ProtocolElements.UNSUBSCRIBEFROMSPEECHTOTEXT_CONNECTIONIDS_PARAM);
|
ProtocolElements.UNSUBSCRIBEFROMSPEECHTOTEXT_CONNECTIONID_PARAM);
|
||||||
sessionManager.onUnsubscribeFromSpeechToText(participant, request.getId(), connectionIds);
|
sessionManager.onUnsubscribeFromSpeechToText(participant, request.getId(), connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void echo(RpcConnection rpcConnection, Request<JsonObject> request) {
|
private void echo(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||||
|
|
|
@ -124,9 +124,9 @@
|
||||||
<version>${version.testcontainers}</version>
|
<version>${version.testcontainers}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>info.debatty</groupId>
|
||||||
<artifactId>commons-text</artifactId>
|
<artifactId>java-string-similarity</artifactId>
|
||||||
<version>${version.commons-text}</version>
|
<version>2.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -338,7 +338,7 @@ public class OpenViduTestE2e {
|
||||||
case "chromeFakeAudio":
|
case "chromeFakeAudio":
|
||||||
container = chromeContainer("selenium/standalone-chrome:" + CHROME_VERSION, 2147483648L, 1, true);
|
container = chromeContainer("selenium/standalone-chrome:" + CHROME_VERSION, 2147483648L, 1, true);
|
||||||
setupBrowserAux(BrowserNames.CHROME, container, false);
|
setupBrowserAux(BrowserNames.CHROME, container, false);
|
||||||
browserUser = new ChromeUser("TestUser", 50, null, Paths.get("/opt/openvidu/stt-test2.wav"));
|
browserUser = new ChromeUser("TestUser", 50, null, Paths.get("/opt/openvidu/stt-test.wav"));
|
||||||
break;
|
break;
|
||||||
case "chromeVirtualBackgroundFakeVideo":
|
case "chromeVirtualBackgroundFakeVideo":
|
||||||
container = chromeContainer("selenium/standalone-chrome:" + CHROME_VERSION, 2147483648L, 1, false);
|
container = chromeContainer("selenium/standalone-chrome:" + CHROME_VERSION, 2147483648L, 1, false);
|
||||||
|
|
|
@ -2168,12 +2168,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
filterTypeInput.sendKeys("NotAllowedFilter");
|
filterTypeInput.sendKeys("NotAllowedFilter");
|
||||||
|
|
||||||
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Error [You don't have permissions to apply a filter]"));
|
"Error [You don't have permissions to apply a filter]"));
|
||||||
|
|
||||||
// Try to execute method over not applied filter
|
// Try to execute method over not applied filter
|
||||||
user.getDriver().findElement(By.id("exec-filter-btn")).click();
|
user.getDriver().findElement(By.id("exec-filter-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"has no filter applied in session"));
|
"has no filter applied in session"));
|
||||||
|
|
||||||
// Apply allowed video filter
|
// Apply allowed video filter
|
||||||
|
@ -2184,11 +2184,11 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
filterOptionsInput.sendKeys("{\"command\": \"videobalance saturation=0.0\"}");
|
filterOptionsInput.sendKeys("{\"command\": \"videobalance saturation=0.0\"}");
|
||||||
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
||||||
user.getWaiter().until(
|
user.getWaiter().until(
|
||||||
ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value", "Filter applied"));
|
ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value", "Filter applied"));
|
||||||
|
|
||||||
// Try to apply another filter
|
// Try to apply another filter
|
||||||
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Error [There is already a filter applied"));
|
"Error [There is already a filter applied"));
|
||||||
|
|
||||||
// Analyze Chrome fake video stream with gray filter (GRAY color)
|
// Analyze Chrome fake video stream with gray filter (GRAY color)
|
||||||
|
@ -2206,7 +2206,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
filterParamsInput.clear();
|
filterParamsInput.clear();
|
||||||
filterParamsInput.sendKeys("{\"propertyName\":\"saturation\",\"propertyValue\":\"1.0\"}");
|
filterParamsInput.sendKeys("{\"propertyName\":\"saturation\",\"propertyValue\":\"1.0\"}");
|
||||||
user.getDriver().findElement(By.id("exec-filter-btn")).click();
|
user.getDriver().findElement(By.id("exec-filter-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Filter method executed"));
|
"Filter method executed"));
|
||||||
|
|
||||||
// Analyze Chrome fake video stream without gray filter (GREEN color)
|
// Analyze Chrome fake video stream without gray filter (GREEN color)
|
||||||
|
@ -2246,7 +2246,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
user.getDriver().findElement(By.id("remove-filter-btn")).click();
|
user.getDriver().findElement(By.id("remove-filter-btn")).click();
|
||||||
user.getWaiter().until(
|
user.getWaiter().until(
|
||||||
ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value", "Filter removed"));
|
ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value", "Filter removed"));
|
||||||
user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 6);
|
user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 6);
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
@ -2318,7 +2318,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
input.sendKeys("{}");
|
input.sendKeys("{}");
|
||||||
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
||||||
user.getWaiter().until(
|
user.getWaiter().until(
|
||||||
ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value", "Filter applied"));
|
ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value", "Filter applied"));
|
||||||
|
|
||||||
user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 2);
|
user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 2);
|
||||||
|
|
||||||
|
@ -2327,14 +2327,14 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
input.clear();
|
input.clear();
|
||||||
input.sendKeys("CodeFound");
|
input.sendKeys("CodeFound");
|
||||||
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Filter event listener added"));
|
"Filter event listener added"));
|
||||||
|
|
||||||
user.getEventManager().waitUntilEventReaches("CodeFound", 2);
|
user.getEventManager().waitUntilEventReaches("CodeFound", 2);
|
||||||
|
|
||||||
// Publisher unsubscribes from "CodeFound" filter event
|
// Publisher unsubscribes from "CodeFound" filter event
|
||||||
user.getDriver().findElement(By.id("unsub-filter-event-btn")).click();
|
user.getDriver().findElement(By.id("unsub-filter-event-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Filter event listener removed"));
|
"Filter event listener removed"));
|
||||||
|
|
||||||
// In case some filter event was receive while waiting for unsubscription
|
// In case some filter event was receive while waiting for unsubscription
|
||||||
|
@ -2359,7 +2359,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
input.clear();
|
input.clear();
|
||||||
input.sendKeys("CodeFound");
|
input.sendKeys("CodeFound");
|
||||||
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Filter event listener added"));
|
"Filter event listener added"));
|
||||||
|
|
||||||
user.getEventManager().waitUntilEventReaches("CodeFound", 1);
|
user.getEventManager().waitUntilEventReaches("CodeFound", 1);
|
||||||
|
@ -2367,7 +2367,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
// Moderator removes the Publisher's filter
|
// Moderator removes the Publisher's filter
|
||||||
user.getDriver().findElement(By.id("remove-filter-btn")).click();
|
user.getDriver().findElement(By.id("remove-filter-btn")).click();
|
||||||
user.getWaiter().until(
|
user.getWaiter().until(
|
||||||
ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value", "Filter removed"));
|
ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value", "Filter removed"));
|
||||||
|
|
||||||
// In case some filter event was receive while waiting for filter removal
|
// In case some filter event was receive while waiting for filter removal
|
||||||
user.getEventManager().clearCurrentEvents("CodeFound");
|
user.getEventManager().clearCurrentEvents("CodeFound");
|
||||||
|
@ -4068,7 +4068,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
input.clear();
|
input.clear();
|
||||||
input.sendKeys("CodeFound");
|
input.sendKeys("CodeFound");
|
||||||
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
user.getDriver().findElement(By.id("sub-filter-event-btn")).click();
|
||||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("filter-response-text-area"), "value",
|
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||||
"Filter event listener added"));
|
"Filter event listener added"));
|
||||||
CustomWebhook.waitForEvent("filterEventDispatched", 2);
|
CustomWebhook.waitForEvent("filterEventDispatched", 2);
|
||||||
user.getDriver().findElement(By.id("unsub-filter-event-btn")).click();
|
user.getDriver().findElement(By.id("unsub-filter-event-btn")).click();
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-dialog-content button, mat-divider {
|
mat-dialog-content button,
|
||||||
|
mat-divider {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,4 +27,10 @@ mat-dialog-content button {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
margin-top: 13px
|
margin-top: 13px
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-response-text-area-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
|
@ -23,9 +23,6 @@
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<label class="label">Remove filter</label>
|
<label class="label">Remove filter</label>
|
||||||
<button mat-button id="remove-filter-btn" (click)="remove()">Remove</button>
|
<button mat-button id="remove-filter-btn" (click)="remove()">Remove</button>
|
||||||
<mat-form-field *ngIf="!!response" id="response-text-area" appearance="fill">
|
|
||||||
<textarea id="filter-response-text-area" [(ngModel)]="response" matInput readonly></textarea>
|
|
||||||
</mat-form-field>
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -55,6 +52,10 @@
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<mat-form-field id="response-text-area" appearance="fill">
|
||||||
|
<textarea id="operation-response-text-area" [(ngModel)]="response" matInput readonly></textarea>
|
||||||
|
<button mat-button id="clear-response-text-area-btn" (click)="response=''">Clear</button>
|
||||||
|
</mat-form-field>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button id="close-dialog-btn" [mat-dialog-close]="{session: session}">CLOSE</button>
|
<button mat-button id="close-dialog-btn" [mat-dialog-close]="{session: session}">CLOSE</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
Loading…
Reference in New Issue