Skip to content

Commit c23a382

Browse files
asithademauriciozanettisalomao
authored andcommitted
fix(meeting-join): refactor auto-join logic to prevent infinite loop (#126)
* fix(meeting-join): refactor auto-join logic to prevent infinite loop - Remove OnDestroy lifecycle and timeout cleanup - Simplify auto-join using RxJS filter and take(1) operators - Create fetchedJoinUrl signal for preemptive URL fetching - Convert join buttons to use href instead of click handlers - Add security attributes to button component links - Fix project logo centering and sizing LFXV2-669 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(ui): pass in link target and rel for buttons Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(ui): pass in link target and rel for buttons Signed-off-by: Asitha de Silva <asithade@gmail.com> * fix(meeting-join): add loading and error handling for join URL LFXV2-669 - Add loading spinner while fetching join URL - Display error messages when join URL fetch fails - Separate authenticated and unauthenticated user flows - Add debouncing (300ms) to form status changes to prevent rapid re-fetches - Improve user feedback with loading states in both authenticated and form sections - Reset error state on each fetch attempt Signed-off-by: Asitha de Silva <asithade@gmail.com> * refactor(meeting-join): simplify bindings and eliminate duplication Simplified button href and disabled bindings by removing redundant canJoinMeeting() checks since fetchedJoinUrl() already returns undefined when the meeting cannot be joined. Refactored initializeFetchedJoinUrl() to eliminate code duplication: - Extracted common join URL fetching logic into fetchJoinUrl() method - Removed duplicate API call handling for authenticated/unauthenticated flows - Removed signal mutation anti-pattern (meeting.join_url assignment) - Improved maintainability with single source of truth for fetch logic Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com> --------- Signed-off-by: Asitha de Silva <asithade@gmail.com> Signed-off-by: Mauricio Zanetti Salomao <msalomao@contractor.linuxfoundation.org>
1 parent b025caf commit c23a382

File tree

5 files changed

+178
-177
lines changed

5 files changed

+178
-177
lines changed

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"litellm",
2121
"networkidle",
2222
"nonexistentproject",
23+
"noopener",
24+
"noreferrer",
2325
"PostgreSQL",
2426
"PostgREST",
2527
"primeng",

apps/lfx-one/src/app/modules/meeting/meeting-join/meeting-join.component.html

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
<div class="max-w-4xl mx-auto">
99
<!-- Project Logo -->
1010
@if (project()?.logo_url) {
11-
<img src="{{ project()?.logo_url }}" alt="{{ project()?.name }}" class="w-full h-20 mb-6" />
11+
<div class="flex items-center justify-center">
12+
<img src="{{ project()?.logo_url }}" alt="{{ project()?.name }}" class="w-auto h-20 mb-6" />
13+
</div>
1214
}
1315

1416
<!-- Meeting Information Card -->
@@ -210,40 +212,55 @@ <h4 class="font-medium text-gray-900 font-sans">{{ user.name }}</h4>
210212
</div>
211213
</div>
212214

213-
<lfx-message
214-
[severity]="messageSeverity()"
215-
[icon]="messageIcon()"
216-
styleClass="flex items-center justify-between w-full"
217-
[attr.data-testid]="'join-status-message'">
218-
<ng-template #content>
219-
<div class="flex items-center justify-between w-full">
220-
<div class="flex items-center gap-3 text-sm">
221-
@if (hasAutoJoined()) {
222-
<span class="font-sans">Meeting opened in a new tab. If it didn't open, use the button to join manually.</span>
223-
} @else if (canJoinMeeting()) {
224-
<span class="font-sans">Ready to join as {{ user.name }}</span>
225-
} @else {
226-
<div class="flex flex-col">
227-
<span class="font-sans"
228-
>You may only join the meeting up to <span class="font-bold">{{ meeting().early_join_time_minutes || 10 }} minutes</span> before
229-
the start time.</span
230-
>
215+
@if (joinUrlError()) {
216+
<lfx-message severity="error" icon="fa-light fa-circle-exclamation" styleClass="w-full" [attr.data-testid]="'join-url-error-message'">
217+
<ng-template #content>
218+
<span class="font-sans text-sm">{{ joinUrlError() }}</span>
219+
</ng-template>
220+
</lfx-message>
221+
} @else {
222+
<lfx-message
223+
[severity]="messageSeverity()"
224+
[icon]="messageIcon()"
225+
styleClass="flex items-center justify-between w-full"
226+
[attr.data-testid]="'join-status-message'">
227+
<ng-template #content>
228+
<div class="flex items-center justify-between w-full">
229+
<div class="flex items-center gap-3 text-sm">
230+
@if (isLoadingJoinUrl()) {
231+
<div class="flex items-center gap-2">
232+
<i class="fa-light fa-spinner-third fa-spin text-primary"></i>
233+
<span class="text-sm text-gray-600">Loading meeting link...</span>
234+
</div>
235+
} @else if (canJoinMeeting()) {
236+
<span class="font-sans">Ready to join as {{ user.name }}</span>
237+
} @else {
238+
<div class="flex flex-col">
239+
<span class="font-sans"
240+
>You may only join the meeting up to <span class="font-bold">{{ meeting().early_join_time_minutes || 10 }} minutes</span> before
241+
the start time.</span
242+
>
243+
</div>
244+
}
245+
</div>
246+
@if (!isLoadingJoinUrl() && !joinUrlError()) {
247+
<div class="ml-4">
248+
<lfx-button
249+
size="small"
250+
severity="primary"
251+
label="Join Meeting"
252+
[href]="fetchedJoinUrl()"
253+
[disabled]="!fetchedJoinUrl()"
254+
icon="fa-light fa-sign-in"
255+
target="_blank"
256+
rel="noopener noreferrer"
257+
[attr.data-testid]="'join-meeting-button-authenticated'"></lfx-button>
231258
</div>
232259
}
233260
</div>
234-
<div class="ml-4">
235-
<lfx-button
236-
size="small"
237-
severity="primary"
238-
label="Join Meeting"
239-
[disabled]="!canJoinMeeting()"
240-
icon="fa-light fa-sign-in"
241-
[attr.data-testid]="'join-meeting-button-authenticated'"
242-
(click)="onJoinMeeting()"></lfx-button>
243-
</div>
244-
</div>
245-
</ng-template>
246-
</lfx-message>
261+
</ng-template>
262+
</lfx-message>
263+
}
247264

248265
<div class="text-center">
249266
<p class="text-xs text-gray-500 font-sans flex gap-1 items-center justify-center">
@@ -332,15 +349,20 @@ <h4 class="font-medium text-gray-900 font-sans">Enter your information</h4>
332349
</div>
333350
</div>
334351
}
335-
<div class="flex items-center">
352+
<div class="flex items-center gap-2">
353+
@if (joinUrlError()) {
354+
<span class="text-xs text-red-500" [attr.data-testid]="'join-url-error-form'">{{ joinUrlError() }}</span>
355+
}
336356
<lfx-button
337357
size="small"
338358
severity="primary"
339359
label="Join Meeting"
340-
[disabled]="joinForm.invalid || !canJoinMeeting()"
341-
icon="fa-light fa-sign-in"
342-
[attr.data-testid]="'join-meeting-button-form'"
343-
(click)="onJoinMeeting()"></lfx-button>
360+
[href]="fetchedJoinUrl()"
361+
[disabled]="!fetchedJoinUrl() || isLoadingJoinUrl()"
362+
[icon]="isLoadingJoinUrl() ? 'fa-light fa-spinner-third fa-spin' : 'fa-light fa-sign-in'"
363+
target="_blank"
364+
rel="noopener noreferrer"
365+
[attr.data-testid]="'join-meeting-button-form'"></lfx-button>
344366
</div>
345367
</div>
346368
</form>

0 commit comments

Comments
 (0)