frontend: enhance share recording dialog with error handling and clipboard support
This commit is contained in:
parent
110f66aca2
commit
253c7cb37b
@ -1,21 +1,42 @@
|
|||||||
<h2 mat-dialog-title>Share Recording</h2>
|
<h2 mat-dialog-title>Share Recording</h2>
|
||||||
<mat-dialog-content class="dialog-content">
|
<mat-dialog-content class="dialog-content">
|
||||||
<mat-radio-group [(ngModel)]="accessType">
|
@if (!recordingUrl) {
|
||||||
<mat-radio-button value="private">Private (authenticated users)</mat-radio-button>
|
@if (erroMessage) {
|
||||||
<mat-radio-button value="public">Public (anyone with the link)</mat-radio-button>
|
<div class="error-text">
|
||||||
</mat-radio-group>
|
<mat-icon color="warn">error</mat-icon>
|
||||||
|
{{ erroMessage }}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="generate-btn-container">
|
<mat-radio-group [(ngModel)]="accessType">
|
||||||
<button mat-raised-button color="primary" (click)="getRecordingUrl()">Generate Link</button>
|
<mat-radio-button value="public">Public (anyone with the link)</mat-radio-button>
|
||||||
</div>
|
<mat-radio-button value="private">Private (authenticated users)</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<div class="generate-btn-container">
|
||||||
|
@if (loading) {
|
||||||
|
<mat-spinner diameter="24"></mat-spinner>
|
||||||
|
} @else {
|
||||||
|
<button mat-flat-button color="primary" (click)="getRecordingUrl()" [disabled]="loading">
|
||||||
|
Generate Link
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (recordingUrl) {
|
@if (recordingUrl) {
|
||||||
<div class="recording-url-container">
|
<div class="recording-url-container">
|
||||||
<mat-form-field appearance="outline" class="url-field">
|
<mat-form-field appearance="outline" class="url-field">
|
||||||
<mat-label>Shareable URL</mat-label>
|
<mat-label>Shareable URL</mat-label>
|
||||||
<input matInput [value]="recordingUrl" readonly />
|
<input matInput [value]="recordingUrl" readonly />
|
||||||
<button mat-icon-button matSuffix (click)="copyToClipboard()">
|
<button
|
||||||
<mat-icon>content_copy</mat-icon>
|
mat-icon-button
|
||||||
|
matSuffix
|
||||||
|
(click)="copyToClipboard()"
|
||||||
|
[matTooltip]="copied ? 'Copied!' : 'Copy to clipboard'"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
>
|
||||||
|
<mat-icon>{{ copied ? 'check' : 'content_copy' }}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
mat-radio-group {
|
mat-radio-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -10,20 +11,26 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
mat-radio-button {
|
mat-radio-button {
|
||||||
color: var(--ov-text-primary-color);
|
color: var(--ov-text-surface-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-btn-container {
|
.error-text {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--ov-error-color);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-btn-container {
|
||||||
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recording-url-container {
|
.recording-url-container {
|
||||||
display: flex;
|
width: 100%;
|
||||||
flex-direction: column;
|
margin-top: 8px;
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.url-field {
|
.url-field {
|
||||||
@ -31,6 +38,6 @@
|
|||||||
|
|
||||||
input {
|
input {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--ov-text-primary-color);
|
color: var(--ov-text-surface-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
@ -11,7 +12,9 @@ import {
|
|||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatRadioModule } from '@angular/material/radio';
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { HttpService } from 'shared-meet-components';
|
import { HttpService } from 'shared-meet-components';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -27,29 +30,56 @@ import { HttpService } from 'shared-meet-components';
|
|||||||
MatDialogTitle,
|
MatDialogTitle,
|
||||||
MatDialogContent,
|
MatDialogContent,
|
||||||
MatDialogActions,
|
MatDialogActions,
|
||||||
MatDialogClose
|
MatDialogClose,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatProgressSpinnerModule
|
||||||
],
|
],
|
||||||
templateUrl: './share-recording-dialog.component.html',
|
templateUrl: './share-recording-dialog.component.html',
|
||||||
styleUrl: './share-recording-dialog.component.scss'
|
styleUrl: './share-recording-dialog.component.scss'
|
||||||
})
|
})
|
||||||
export class ShareRecordingDialogComponent {
|
export class ShareRecordingDialogComponent {
|
||||||
accessType: 'private' | 'public' = 'private';
|
accessType: 'private' | 'public' = 'public';
|
||||||
recordingUrl: string | undefined;
|
recordingUrl?: string;
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
erroMessage?: string;
|
||||||
|
copied = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { recordingId: string },
|
@Inject(MAT_DIALOG_DATA) public data: { recordingId: string; recordingUrl?: string },
|
||||||
private httpService: HttpService
|
private httpService: HttpService,
|
||||||
) {}
|
private clipboard: Clipboard
|
||||||
|
) {
|
||||||
|
this.recordingUrl = data.recordingUrl;
|
||||||
|
}
|
||||||
|
|
||||||
async getRecordingUrl() {
|
async getRecordingUrl() {
|
||||||
const privateAccess = this.accessType === 'private';
|
this.loading = true;
|
||||||
const { url } = await this.httpService.generateRecordingUrl(this.data.recordingId, privateAccess);
|
this.erroMessage = undefined;
|
||||||
this.recordingUrl = url;
|
|
||||||
|
try {
|
||||||
|
const privateAccess = this.accessType === 'private';
|
||||||
|
const { url } = await this.httpService.generateRecordingUrl(this.data.recordingId, privateAccess);
|
||||||
|
this.recordingUrl = url;
|
||||||
|
} catch (error) {
|
||||||
|
this.erroMessage = 'Failed to generate recording URL. Please try again later.';
|
||||||
|
console.error('Error generating recording URL:', error);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copyToClipboard() {
|
copyToClipboard() {
|
||||||
if (this.recordingUrl) {
|
if (!this.recordingUrl) {
|
||||||
navigator.clipboard.writeText(this.recordingUrl);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.clipboard.copy(this.recordingUrl!);
|
||||||
|
this.copied = true;
|
||||||
|
|
||||||
|
// Reset copied state after 2 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copied = false;
|
||||||
|
}, 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user