From cc858a6d2cc42b62aba7634a93f0fd593d53ff3e Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Thu, 31 Jul 2025 10:52:19 +0200 Subject: [PATCH] frontend: implement share meeting link component and integrate it into the meeting page --- .../share-meeting-link.component.html | 10 +++ .../share-meeting-link.component.scss | 75 +++++++++++++++++++ .../share-meeting-link.component.spec.ts | 23 ++++++ .../share-meeting-link.component.ts | 17 +++++ .../lib/pages/meeting/meeting.component.html | 19 +---- .../lib/pages/meeting/meeting.component.scss | 70 +---------------- .../lib/pages/meeting/meeting.component.ts | 12 +-- 7 files changed, 140 insertions(+), 86 deletions(-) create mode 100644 frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.html create mode 100644 frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.scss create mode 100644 frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.spec.ts create mode 100644 frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.ts diff --git a/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.html b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.html new file mode 100644 index 0000000..78b58ee --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.html @@ -0,0 +1,10 @@ +
+
Invite others with this meeting link
+ +
+ {{ meetingUrl }} + +
+
diff --git a/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.scss b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.scss new file mode 100644 index 0000000..ee8bbcc --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.scss @@ -0,0 +1,75 @@ +@import '../../../../../../src/assets/styles/design-tokens'; + +.meeting-url-badge { + margin: var(--ov-meet-spacing-sm) auto; + .meeting-url-badge-title { + text-align: center; + font-size: var(--ov-meet-font-size-sm); + font-weight: var(--ov-meet-font-weight-light); + color: var(--ov-meet-text-primary); + margin-bottom: var(--ov-meet-spacing-sm); + } + .meeting-url-badge-container { + @include ov-flex-center; + @include ov-theme-transition; + gap: var(--ov-meet-spacing-sm); + padding: var(--ov-meet-spacing-sm) var(--ov-meet-spacing-md); + background-color: var(--ov-meet-surface-color); + border: 1px solid var(--ov-meet-border-color-light); + border-radius: var(--ov-meet-radius-lg); + max-width: fit-content; + margin: auto; + cursor: pointer; + + &:hover { + background-color: var(--ov-meet-surface-hover); + border-color: var(--ov-meet-border-color); + } + + .meeting-url-text { + font-family: var(--ov-meet-font-family-mono, 'Roboto Mono', monospace); + font-size: var(--ov-meet-font-size-sm); + color: var(--ov-meet-text-secondary); + font-weight: var(--ov-meet-font-weight-medium); + letter-spacing: 0.025em; + user-select: none; + word-break: break-all; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + .copy-url-btn { + @include ov-button-base; + width: 32px; + height: 32px; + min-width: 32px; + padding: 0; + color: var(--ov-meet-text-hint); + cursor: pointer; + background: transparent; + border: 0; + + .mat-icon { + @include ov-icon(sm); + } + } + } +} + +@include ov-mobile-down { + .meeting-url-badge-container { + margin: var(--ov-meet-spacing-md) auto; + padding: var(--ov-meet-spacing-xs) var(--ov-meet-spacing-sm); + + .meeting-url-text { + font-size: var(--ov-meet-font-size-xs); + } + + .copy-url-btn { + width: 28px; + height: 28px; + min-width: 28px; + } + } +} diff --git a/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.spec.ts b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.spec.ts new file mode 100644 index 0000000..5bbba33 --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShareMeetingLinkComponent } from './share-meeting-link.component'; + +describe('ShareMeetingLinkComponent', () => { + let component: ShareMeetingLinkComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ShareMeetingLinkComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ShareMeetingLinkComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.ts b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.ts new file mode 100644 index 0000000..8df4514 --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/components/share-meeting-link/share-meeting-link.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Input } from '@angular/core'; +import { Output, EventEmitter } from '@angular/core'; +import { MatButtonModule, MatIconButton } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; + +@Component({ + selector: 'ov-share-meeting-link', + standalone: true, + imports: [MatButtonModule, MatIconModule, MatIconButton], + templateUrl: './share-meeting-link.component.html', + styleUrl: './share-meeting-link.component.scss' +}) +export class ShareMeetingLinkComponent { + @Input() meetingUrl!: string; + @Output() copyClicked = new EventEmitter(); +} diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html index 11521e4..ce85f63 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html +++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html @@ -192,21 +192,10 @@ @if (features().canModerateRoom) { -
-
Invite others with this meeting link
- -
- {{ hostname }}/{{ roomId }} - -
-
+ } diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.scss b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.scss index 2faf1d9..b87a61d 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.scss +++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.scss @@ -195,57 +195,6 @@ margin-top: auto; } -// Room URL Badge -.room-url-badge { - margin: var(--ov-meet-spacing-sm) auto; - .room-url-badge-title { - text-align: center; - font-size: var(--ov-meet-font-size-sm); - font-weight: var(--ov-meet-font-weight-light); - color: var(--ov-meet-text-primary); - margin-bottom: var(--ov-meet-spacing-sm); - } - .room-url-badge-container { - @include ov-flex-center; - gap: var(--ov-meet-spacing-sm); - padding: var(--ov-meet-spacing-sm) var(--ov-meet-spacing-md); - background-color: var(--ov-meet-surface-color); - border: 1px solid var(--ov-meet-border-color-light); - border-radius: var(--ov-meet-radius-lg); - max-width: fit-content; - @include ov-theme-transition; - - &:hover { - background-color: var(--ov-meet-surface-hover); - border-color: var(--ov-meet-border-color); - } - - .room-url-text { - font-family: var(--ov-meet-font-family-mono, 'Roboto Mono', monospace); - font-size: var(--ov-meet-font-size-sm); - color: var(--ov-meet-text-secondary); - font-weight: var(--ov-meet-font-weight-medium); - letter-spacing: 0.025em; - user-select: all; - cursor: text; - } - - .copy-url-btn { - @include ov-button-base; - width: 32px; - height: 32px; - min-width: 32px; - padding: 0; - color: var(--ov-meet-text-hint); - @include ov-theme-transition; - - .mat-icon { - @include ov-icon(sm); - } - } - } -} - // Quick Actions - Footer actions .quick-actions { @include ov-flex-center; @@ -265,6 +214,10 @@ } } +.share-meeting-link-container { + padding: 10px; +} + // Responsive adjustments @include ov-mobile-down { .room-access-container { @@ -297,21 +250,6 @@ padding: var(--ov-meet-spacing-md); } } - - .room-url-badge-container { - margin: var(--ov-meet-spacing-md) auto; - padding: var(--ov-meet-spacing-xs) var(--ov-meet-spacing-sm); - - .room-url-text { - font-size: var(--ov-meet-font-size-xs); - } - - .copy-url-btn { - width: 28px; - height: 28px; - min-width: 28px; - } - } } // Custom leave button styling (existing functionality) diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts index e9b2eff..1318117 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts @@ -11,6 +11,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; import { MatTooltipModule } from '@angular/material/tooltip'; import { ActivatedRoute } from '@angular/router'; +import { ShareMeetingLinkComponent } from '@lib/components/share-meeting-link/share-meeting-link.component'; import { ErrorReason } from '@lib/models'; import { AppDataService, @@ -69,7 +70,8 @@ import { MatMenuModule, MatDividerModule, MatTooltipModule, - MatRippleModule + MatRippleModule, + ShareMeetingLinkComponent ] }) export class MeetingComponent implements OnInit { @@ -113,6 +115,10 @@ export class MeetingComponent implements OnInit { this.features = this.featureConfService.features; } + get hostname(): string { + return window.location.origin.replace('http://', '').replace('https://', ''); + } + async ngOnInit() { this.roomId = this.roomService.getRoomId(); this.roomSecret = this.roomService.getRoomSecret(); @@ -122,10 +128,6 @@ export class MeetingComponent implements OnInit { await this.initializeParticipantName(); } - get hostname(): string { - return window.location.origin.replace('http://', '').replace('https://', ''); - } - /** * Sets the back button text based on the application mode and user role */