mirror of https://github.com/OpenVidu/openvidu.git
ov-components: Improve prejoin card styles
parent
68d855a245
commit
f92ee9b886
|
@ -1,14 +1,11 @@
|
|||
.prejoin-mode {
|
||||
margin-top: 0px;
|
||||
margin: 0 10px 0px 10px;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
.effects-container {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
.background-title {
|
||||
color: var(--ov-text-surface-color);
|
||||
margin: 10px 0;
|
||||
}
|
||||
.effects-container {
|
||||
display: block !important;
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Main Content with Side Panel Layout -->
|
||||
<!-- Main Content -->
|
||||
<div class="prejoin-content">
|
||||
<!-- Main Card -->
|
||||
<div class="prejoin-main">
|
||||
<!-- Video Preview Section -->
|
||||
<div class="video-preview-section">
|
||||
<div class="video-preview-container">
|
||||
<div class="video-preview-container" [@containerResize]="showBackgroundPanel ? 'compact' : 'normal'">
|
||||
<div class="video-frame">
|
||||
<ov-media-element
|
||||
[track]="videoTrack"
|
||||
|
@ -63,8 +63,9 @@
|
|||
class="background-button"
|
||||
(click)="toggleBackgroundPanel()"
|
||||
[matTooltip]="'Virtual Backgrounds'"
|
||||
[disabled]="!isVideoEnabled"
|
||||
>
|
||||
<mat-icon>auto_fix_high</mat-icon>
|
||||
<mat-icon class="material-symbols-outlined">background_replace</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,7 +74,9 @@
|
|||
</div>
|
||||
|
||||
@if (showBackgroundPanel) {
|
||||
<div class="vb-container" [@slideInOut]>
|
||||
<ov-background-effects-panel [mode]="'prejoin'" (onClose)="closeBackgroundPanel()"> </ov-background-effects-panel>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Configuration Section -->
|
||||
<div class="configuration-section">
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
.prejoin-main {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
max-height: 544px;
|
||||
background: var(--ov-surface-color, #ffffff);
|
||||
border-radius: var(--ov-surface-radius);
|
||||
overflow: hidden;
|
||||
|
@ -93,18 +94,16 @@
|
|||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
// Video Preview Section (moved to top with no padding)
|
||||
// Video Preview Section
|
||||
.video-preview-section {
|
||||
padding: 0;
|
||||
|
||||
.video-preview-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3; // Changed from 16/9 to 4/3 for taller video
|
||||
border-radius: var(--ov-surface-radius) var(--ov-surface-radius) 0 0; // Only top corners rounded
|
||||
aspect-ratio: 4/3;
|
||||
border-radius: var(--ov-surface-radius) var(--ov-surface-radius) 0 0;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
|
||||
.video-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -156,16 +155,23 @@
|
|||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: #333333;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
transform: translateZ(0);
|
||||
|
||||
&:active {
|
||||
transform: translateY(-1px);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
&.mat-mdc-button-disabled {
|
||||
background: rgba(255, 255, 255, 0.137);
|
||||
color: rgba(233, 233, 233, 0.5);
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
font-size: 22px;
|
||||
width: 22px;
|
||||
|
@ -183,6 +189,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.vb-container {
|
||||
height: fit-content;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Configuration Section
|
||||
.configuration-section {
|
||||
padding: 24px 24px 24px; // Added top padding since video has no padding
|
||||
|
@ -393,8 +404,8 @@
|
|||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-main {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { filter, Subject, takeUntil, tap } from 'rxjs';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
||||
|
@ -28,7 +29,47 @@ import { LangOption } from '../../models/lang.model';
|
|||
templateUrl: './pre-join.component.html',
|
||||
styleUrls: ['./pre-join.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false
|
||||
standalone: false,
|
||||
animations: [
|
||||
trigger('containerResize', [
|
||||
state(
|
||||
'normal',
|
||||
style({
|
||||
height: '*'
|
||||
})
|
||||
),
|
||||
state(
|
||||
'compact',
|
||||
style({
|
||||
height: '28vh'
|
||||
})
|
||||
),
|
||||
transition('normal => compact', [animate('250ms cubic-bezier(0.25, 0.8, 0.25, 1)')]),
|
||||
transition('compact => normal', [animate('350ms cubic-bezier(0.25, 0.8, 0.25, 1)')])
|
||||
]),
|
||||
trigger('slideInOut', [
|
||||
transition(':enter', [
|
||||
style({
|
||||
opacity: 0
|
||||
}),
|
||||
animate(
|
||||
'300ms cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||
style({
|
||||
opacity: 1
|
||||
})
|
||||
)
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate(
|
||||
'200ms cubic-bezier(0.25, 0.46, 0.45, 0.94)',
|
||||
style({
|
||||
opacity: 0,
|
||||
transform: 'translateY(-10px)'
|
||||
})
|
||||
)
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
export class PreJoinComponent implements OnInit, OnDestroy {
|
||||
@Input() set error(error: { name: string; message: string } | undefined) {
|
||||
|
@ -61,6 +102,7 @@ export class PreJoinComponent implements OnInit, OnDestroy {
|
|||
|
||||
videoTrack: LocalTrack | undefined;
|
||||
audioTrack: LocalTrack | undefined;
|
||||
isVideoEnabled: boolean = false;
|
||||
private tracks: LocalTrack[];
|
||||
private log: ILogger;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
@ -195,12 +237,16 @@ export class PreJoinComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
async videoEnabledChanged(enabled: boolean) {
|
||||
if (enabled && !this.videoTrack) {
|
||||
this.isVideoEnabled = enabled;
|
||||
if (!enabled) {
|
||||
this.closeBackgroundPanel();
|
||||
} else if (!this.videoTrack) {
|
||||
const newVideoTrack = await this.openviduService.createLocalTracks(true, false);
|
||||
this.videoTrack = newVideoTrack[0];
|
||||
this.tracks.push(this.videoTrack);
|
||||
this.openviduService.setLocalTracks(this.tracks);
|
||||
}
|
||||
|
||||
this.onVideoEnabledChanged.emit(enabled);
|
||||
}
|
||||
|
||||
|
@ -215,19 +261,32 @@ export class PreJoinComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Toggle virtual background panel visibility
|
||||
* Toggle virtual background panel visibility with smooth animation
|
||||
*/
|
||||
toggleBackgroundPanel() {
|
||||
this.showBackgroundPanel = !this.showBackgroundPanel;
|
||||
// Add a small delay to ensure smooth transition
|
||||
if (!this.showBackgroundPanel) {
|
||||
// Opening panel
|
||||
this.showBackgroundPanel = true;
|
||||
this.changeDetector.markForCheck();
|
||||
} else {
|
||||
// Closing panel - add slight delay for smooth animation
|
||||
setTimeout(() => {
|
||||
this.showBackgroundPanel = false;
|
||||
this.changeDetector.markForCheck();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close virtual background panel
|
||||
* Close virtual background panel with smooth animation
|
||||
*/
|
||||
closeBackgroundPanel() {
|
||||
// Add animation delay for smooth closing
|
||||
setTimeout(() => {
|
||||
this.showBackgroundPanel = false;
|
||||
this.changeDetector.markForCheck();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,6 +308,8 @@ export class PreJoinComponent implements OnInit, OnDestroy {
|
|||
this.openviduService.setLocalTracks(this.tracks);
|
||||
this.videoTrack = this.tracks.find((track) => track.kind === 'video');
|
||||
this.audioTrack = this.tracks.find((track) => track.kind === 'audio');
|
||||
this.isVideoEnabled = this.openviduService.isVideoTrackEnabled();
|
||||
|
||||
return; // Success, exit retry loop
|
||||
} catch (error) {
|
||||
this.log.w(`Device initialization attempt ${attempt} failed:`, error);
|
||||
|
|
|
@ -87,8 +87,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
|||
// Constants
|
||||
private static readonly PARTICIPANT_NAME_TIMEOUT_MS = 1000;
|
||||
private static readonly ANIMATION_DURATION_MS = 300;
|
||||
private static readonly MATERIAL_ICONS_URL =
|
||||
'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined';
|
||||
private static readonly MATERIAL_ICONS_URL = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined';
|
||||
private static readonly MATERIAL_ICONS_SELECTOR = 'link[href*="Material+Symbols+Outlined"]';
|
||||
private static readonly SPINNER_DIAMETER = 50;
|
||||
// *** Toolbar ***
|
||||
|
@ -690,6 +689,8 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
|||
) {
|
||||
this.log = this.loggerSrv.get('VideoconferenceComponent');
|
||||
|
||||
this.addMaterialIconsIfNeeded();
|
||||
|
||||
// Initialize state
|
||||
this.updateComponentState({
|
||||
state: VideoconferenceState.INITIALIZING,
|
||||
|
@ -713,7 +714,6 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
|||
* @internal
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
this.addMaterialIconsIfNeeded();
|
||||
this.setupTemplates();
|
||||
this.deviceSrv.initializeDevices().then(() => {
|
||||
this.updateComponentState({
|
||||
|
|
|
@ -238,23 +238,20 @@ export class OpenViduService {
|
|||
videoDeviceId: string | boolean | undefined = undefined,
|
||||
audioDeviceId: string | boolean | undefined = undefined
|
||||
): Promise<LocalTrack[]> {
|
||||
// If video and audio device IDs are not provided, check if they are enabled and use the default devices
|
||||
if (videoDeviceId === undefined) videoDeviceId = this.deviceService.isCameraEnabled();
|
||||
if (audioDeviceId === undefined) audioDeviceId = this.deviceService.isMicrophoneEnabled();
|
||||
// Default values: true if device is enabled, false otherwise
|
||||
videoDeviceId ??= this.deviceService.isCameraEnabled();
|
||||
audioDeviceId ??= this.deviceService.isMicrophoneEnabled();
|
||||
|
||||
let options: CreateLocalTracksOptions = {
|
||||
const options: CreateLocalTracksOptions = {
|
||||
audio: { echoCancellation: true, noiseSuppression: true },
|
||||
video: {}
|
||||
};
|
||||
|
||||
// Video device
|
||||
if (videoDeviceId === true) {
|
||||
if (this.deviceService.hasVideoDeviceAvailable()) {
|
||||
videoDeviceId = this.deviceService.getCameraSelected()?.device || 'default';
|
||||
(options.video as VideoCaptureOptions).deviceId = videoDeviceId;
|
||||
} else {
|
||||
options.video = false;
|
||||
}
|
||||
options.video = this.deviceService.hasVideoDeviceAvailable()
|
||||
? { deviceId: this.deviceService.getCameraSelected()?.device || 'default' }
|
||||
: false;
|
||||
} else if (videoDeviceId === false) {
|
||||
options.video = false;
|
||||
} else {
|
||||
|
@ -279,13 +276,13 @@ export class OpenViduService {
|
|||
if (options.audio || options.video) {
|
||||
this.log.d('Creating local tracks with options', options);
|
||||
newLocalTracks = await createLocalTracks(options);
|
||||
|
||||
// Mute tracks if devices are disabled
|
||||
if (!this.deviceService.isCameraEnabled()) {
|
||||
const videoTrack = newLocalTracks.find((track) => track.kind === Track.Kind.Video);
|
||||
if (videoTrack) videoTrack.mute();
|
||||
newLocalTracks.find((t) => t.kind === Track.Kind.Video)?.mute();
|
||||
}
|
||||
if (!this.deviceService.isMicrophoneEnabled()) {
|
||||
const audioTrack = newLocalTracks.find((track) => track.kind === Track.Kind.Audio);
|
||||
if (audioTrack) audioTrack.mute();
|
||||
newLocalTracks.find((t) => t.kind === Track.Kind.Audio)?.mute();
|
||||
}
|
||||
}
|
||||
return newLocalTracks;
|
||||
|
@ -331,7 +328,7 @@ export class OpenViduService {
|
|||
return this.deviceService.isCameraEnabled();
|
||||
}
|
||||
const videoTrack = this.localTracks.find((track) => track.kind === Track.Kind.Video);
|
||||
return !!videoTrack && !videoTrack.isMuted;
|
||||
return !!videoTrack && !videoTrack.isMuted && videoTrack?.mediaStreamTrack?.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,7 +341,7 @@ export class OpenViduService {
|
|||
return this.deviceService.isMicrophoneEnabled();
|
||||
}
|
||||
const audioTrack = this.localTracks.find((track) => track.kind === Track.Kind.Audio);
|
||||
return !!audioTrack && !audioTrack.isMuted;
|
||||
return !!audioTrack && !audioTrack.isMuted && audioTrack?.mediaStreamTrack?.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue