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
*/