frontend: enhance share recording dialog with improved layout, error handling, and new access options
This commit is contained in:
parent
ff741e08fe
commit
156e63e04a
@ -1,47 +1,108 @@
|
|||||||
<h2 mat-dialog-title>Share Recording</h2>
|
<div class="share-recording-dialog">
|
||||||
<mat-dialog-content class="dialog-content">
|
<h2 mat-dialog-title class="dialog-title">
|
||||||
|
<mat-icon class="title-icon ov-recording-icon">share</mat-icon>
|
||||||
|
Share Recording
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<mat-dialog-content class="dialog-content ov-theme-transition">
|
||||||
@if (!recordingUrl) {
|
@if (!recordingUrl) {
|
||||||
|
<div class="content-section fade-in">
|
||||||
@if (erroMessage) {
|
@if (erroMessage) {
|
||||||
<div class="error-text">
|
<div class="error-message">
|
||||||
<mat-icon color="warn">error</mat-icon>
|
<mat-icon class="error-icon">error</mat-icon>
|
||||||
{{ erroMessage }}
|
<span class="error-text">{{ erroMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<mat-radio-group [(ngModel)]="accessType">
|
<div class="access-type-section">
|
||||||
<mat-radio-button value="public">Public (anyone with the link)</mat-radio-button>
|
<h3 class="section-label">Access Type</h3>
|
||||||
<mat-radio-button value="private">Private (authenticated users)</mat-radio-button>
|
<mat-radio-group [(ngModel)]="accessType" class="access-options">
|
||||||
|
<mat-radio-button value="public" class="access-option">
|
||||||
|
<div class="option-content">
|
||||||
|
<mat-icon class="option-icon">public</mat-icon>
|
||||||
|
<div class="option-details">
|
||||||
|
<span class="option-title">Public Access</span>
|
||||||
|
<span class="option-description">Anyone with the link can view</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-radio-button>
|
||||||
|
<mat-radio-button value="private" class="access-option">
|
||||||
|
<div class="option-content">
|
||||||
|
<mat-icon class="option-icon">lock</mat-icon>
|
||||||
|
<div class="option-details">
|
||||||
|
<span class="option-title">Private Access</span>
|
||||||
|
<span class="option-description">Only authenticated users can view</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-radio-button>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="generate-btn-container">
|
<div class="generate-section">
|
||||||
@if (loading) {
|
@if (loading) {
|
||||||
<mat-spinner diameter="24"></mat-spinner>
|
<div class="loading-state">
|
||||||
|
<mat-spinner diameter="20"></mat-spinner>
|
||||||
|
<span class="loading-text">Generating secure link...</span>
|
||||||
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<button mat-flat-button color="primary" (click)="getRecordingUrl()" [disabled]="loading">
|
<button
|
||||||
Generate Link
|
mat-flat-button
|
||||||
|
color="primary"
|
||||||
|
(click)="getRecordingUrl()"
|
||||||
|
[disabled]="loading"
|
||||||
|
class="generate-button"
|
||||||
|
>
|
||||||
|
<mat-icon>link</mat-icon>
|
||||||
|
Generate Shareable Link
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (recordingUrl) {
|
@if (recordingUrl) {
|
||||||
<div class="recording-url-container">
|
<div class="url-section fade-in">
|
||||||
|
<h3 class="section-label">Shareable Link</h3>
|
||||||
|
<div class="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>Recording URL</mat-label>
|
||||||
<input matInput [value]="recordingUrl" readonly />
|
<input matInput [value]="recordingUrl" readonly class="url-input" />
|
||||||
|
<mat-icon matPrefix class="url-prefix-icon">link</mat-icon>
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
matSuffix
|
matSuffix
|
||||||
(click)="copyToClipboard()"
|
(click)="copyToClipboard()"
|
||||||
[matTooltip]="copied ? 'Copied!' : 'Copy to clipboard'"
|
[matTooltip]="copied ? 'Copied!' : 'Copy to clipboard'"
|
||||||
matTooltipPosition="above"
|
matTooltipPosition="above"
|
||||||
|
class="copy-button"
|
||||||
|
[class.copied]="copied"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ copied ? 'check' : 'content_copy' }}</mat-icon>
|
<mat-icon>{{ copied ? 'check' : 'content_copy' }}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@if (copied) {
|
||||||
|
<div class="copy-success-message fade-in">
|
||||||
|
<mat-icon>check_circle</mat-icon>
|
||||||
|
Link copied to clipboard!
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="url-actions">
|
||||||
|
<button mat-button class="back-button" (click)="goBack()">
|
||||||
|
<mat-icon>arrow_back</mat-icon>
|
||||||
|
<span>Go back</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
|
||||||
<button mat-button mat-dialog-close>Close</button>
|
<mat-dialog-actions class="dialog-actions">
|
||||||
|
<button mat-button mat-dialog-close class="close-button">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
</div>
|
||||||
|
|||||||
@ -1,43 +1,292 @@
|
|||||||
|
@import '../../../../../../../src/assets/styles/design-tokens';
|
||||||
|
|
||||||
|
.share-recording-dialog {
|
||||||
|
@include ov-theme-transition;
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
@include ov-flex-center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--ov-meet-spacing-lg) var(--ov-meet-spacing-xl);
|
||||||
|
font-size: var(--ov-meet-font-size-xl);
|
||||||
|
font-weight: var(--ov-meet-font-weight-semibold);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
|
||||||
|
.title-icon {
|
||||||
|
@include ov-icon(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
|
@include ov-theme-transition;
|
||||||
|
padding: var(--ov-meet-spacing-xl);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: var(--ov-meet-spacing-lg);
|
||||||
min-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
mat-radio-group {
|
@include ov-tablet-down {
|
||||||
|
min-width: 400px;
|
||||||
|
padding: var(--ov-meet-spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include ov-mobile-down {
|
||||||
|
min-width: 320px;
|
||||||
|
padding: var(--ov-meet-spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: var(--ov-meet-spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
mat-radio-button {
|
.section-label {
|
||||||
color: var(--ov-text-surface-color);
|
margin: 0 0 var(--ov-meet-spacing-sm) 0;
|
||||||
}
|
font-size: var(--ov-meet-font-size-md);
|
||||||
|
font-weight: var(--ov-meet-font-weight-semibold);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
@include ov-flex-center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
padding: var(--ov-meet-spacing-md);
|
||||||
|
background: rgba(var(--ov-meet-color-error), 0.1);
|
||||||
|
border: 1px solid var(--ov-meet-color-error);
|
||||||
|
border-radius: var(--ov-meet-radius-md);
|
||||||
|
color: var(--ov-meet-color-error);
|
||||||
|
|
||||||
|
.error-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-text {
|
.error-text {
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.access-type-section {
|
||||||
|
.access-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: var(--ov-meet-spacing-sm);
|
||||||
color: var(--ov-error-color);
|
|
||||||
font-weight: 500;
|
.access-option {
|
||||||
|
@include ov-theme-transition;
|
||||||
|
border: 1px solid var(--ov-meet-border-color);
|
||||||
|
border-radius: var(--ov-meet-radius-sm);
|
||||||
|
padding: var(--ov-meet-spacing-md);
|
||||||
|
background: var(--ov-meet-surface-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--ov-meet-surface-hover);
|
||||||
|
border-color: var(--ov-meet-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-btn-container {
|
&.mat-mdc-radio-button-checked {
|
||||||
align-self: flex-start;
|
border-color: var(--ov-meet-color-primary);
|
||||||
|
background: rgba(var(--ov-meet-color-primary), 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.recording-url-container {
|
.option-content {
|
||||||
width: 100%;
|
@include ov-flex-center;
|
||||||
margin-top: 8px;
|
justify-content: flex-start;
|
||||||
|
gap: var(--ov-meet-spacing-md);
|
||||||
|
margin-left: var(--ov-meet-spacing-lg);
|
||||||
|
|
||||||
|
.option-icon {
|
||||||
|
@include ov-icon(md);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.option-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--ov-meet-spacing-xs);
|
||||||
|
|
||||||
|
.option-title {
|
||||||
|
font-size: var(--ov-meet-font-size-md);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-description {
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-section {
|
||||||
|
@include ov-flex-center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.loading-state {
|
||||||
|
@include ov-flex-center;
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-button {
|
||||||
|
@include ov-button-base;
|
||||||
|
@include ov-flex-center;
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--ov-meet-spacing-md);
|
||||||
|
|
||||||
|
.url-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.url-field {
|
.url-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
input {
|
.mat-mdc-form-field-subscript-wrapper {
|
||||||
font-size: 14px;
|
display: none;
|
||||||
color: var(--ov-text-surface-color);
|
}
|
||||||
|
|
||||||
|
.url-input {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
padding-right: var(--ov-meet-spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-prefix-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
margin-right: var(--ov-meet-spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
@include ov-theme-transition;
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--ov-meet-color-primary);
|
||||||
|
background: rgba(var(--ov-meet-color-primary), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
color: var(--ov-meet-color-success);
|
||||||
|
background: rgba(var(--ov-meet-color-success), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-success-message {
|
||||||
|
@include ov-flex-center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--ov-meet-spacing-xs);
|
||||||
|
margin-top: var(--ov-meet-spacing-sm);
|
||||||
|
padding: var(--ov-meet-spacing-sm);
|
||||||
|
background: rgba(var(--ov-meet-color-success), 0.1);
|
||||||
|
border: 1px solid var(--ov-meet-color-success);
|
||||||
|
border-radius: var(--ov-meet-radius-sm);
|
||||||
|
color: var(--ov-meet-color-success);
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-actions {
|
||||||
|
@include ov-flex-center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: var(--ov-meet-spacing-md);
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
@include ov-flex-center;
|
||||||
|
gap: var(--ov-meet-spacing-xs);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
background-color: var(--ov-meet-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-actions {
|
||||||
|
@include ov-theme-transition;
|
||||||
|
padding: var(--ov-meet-spacing-md) var(--ov-meet-spacing-xl) var(--ov-meet-spacing-lg);
|
||||||
|
border-top: 1px solid var(--ov-meet-border-color-light);
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
@include ov-flex-center;
|
||||||
|
gap: var(--ov-meet-spacing-xs);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
background: var(--ov-meet-surface-hover);
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@include ov-mobile-down {
|
||||||
|
.share-recording-dialog {
|
||||||
|
.dialog-content {
|
||||||
|
min-width: 280px;
|
||||||
|
padding: var(--ov-meet-spacing-md);
|
||||||
|
|
||||||
|
.access-type-section .access-options .access-option {
|
||||||
|
padding: var(--ov-meet-spacing-sm);
|
||||||
|
|
||||||
|
.option-content {
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
|
||||||
|
.option-details {
|
||||||
|
.option-title {
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-description {
|
||||||
|
font-size: var(--ov-meet-font-size-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-section .url-actions .back-button {
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
padding: var(--ov-meet-spacing-xs) var(--ov-meet-spacing-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,4 +82,10 @@ export class ShareRecordingDialogComponent {
|
|||||||
this.copied = false;
|
this.copied = false;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
this.recordingUrl = undefined;
|
||||||
|
this.copied = false;
|
||||||
|
this.erroMessage = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -263,11 +263,12 @@ export class RecordingManagerService {
|
|||||||
*/
|
*/
|
||||||
openShareRecordingDialog(recordingId: string, recordingUrl?: string) {
|
openShareRecordingDialog(recordingId: string, recordingUrl?: string) {
|
||||||
this.dialog.open(ShareRecordingDialogComponent, {
|
this.dialog.open(ShareRecordingDialogComponent, {
|
||||||
width: '400px',
|
width: '450px',
|
||||||
data: {
|
data: {
|
||||||
recordingId,
|
recordingId,
|
||||||
recordingUrl
|
recordingUrl
|
||||||
}
|
},
|
||||||
|
panelClass: 'ov-meet-dialog'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,10 @@
|
|||||||
.ov-flex-center {
|
.ov-flex-center {
|
||||||
@include ov-flex-center;
|
@include ov-flex-center;
|
||||||
}
|
}
|
||||||
|
.ov-meet-dialog {
|
||||||
|
border-radius: var(--ov-meet-spacing-md);
|
||||||
|
background: var(--ov-meet-surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
// Spacing classes
|
// Spacing classes
|
||||||
.ov-mt-xs {
|
.ov-mt-xs {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user