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