Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add tiling window manager for training mode #1150

Merged
merged 2 commits into from
Nov 27, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor: Create floating window mgr component
dominik003 committed Nov 6, 2023

Verified

This commit was signed with the committer’s verified signature.
commit 56540c6431e0ac3dec2bc1f506cda9de64bea642
2 changes: 2 additions & 0 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -107,6 +107,7 @@ import { ProjectOverviewComponent } from './projects/project-overview/project-ov
import { ProjectWrapperComponent } from './projects/project-wrapper/project-wrapper.component';
import { WhitespaceUrlInterceptor } from './services/encoder/encoder.interceptor';
import { DeleteSessionDialogComponent } from './sessions/delete-session-dialog/delete-session-dialog.component';
import { FloatingWindowManagerComponent } from './sessions/session/floating-window-manager/floating-window-manager.component';
import { SessionComponent } from './sessions/session/session.component';
import { SessionOverviewComponent } from './sessions/session-overview/session-overview.component';
import { SessionsComponent } from './sessions/sessions.component';
@@ -172,6 +173,7 @@ import { SettingsComponent } from './settings/settings.component';
EventsComponent,
FileBrowserDialogComponent,
FileExistsDialogComponent,
FloatingWindowManagerComponent,
FooterComponent,
FormFieldSkeletonLoaderComponent,
GitSettingsComponent,
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!--
MoritzWeber0 marked this conversation as resolved.
Show resolved Hide resolved
~ SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors
~ SPDX-License-Identifier: Apache-2.0
-->

<div class="flex gap-0.5">
<div
class="flex w-full flex-col active:z-30"
[ngClass]="{
'z-20': session.focused,
'z-10': !session.focused,
height: (fullscreenService.isFullscreen$ | async) === false,
'h-screen': fullscreenService.isFullscreen$ | async
dominik003 marked this conversation as resolved.
Show resolved Hide resolved
}"
(click)="focusSession(session)"
*ngFor="let session of sessions"
cdkDrag
(cdkDragStarted)="dragStart()"
(cdkDragEnded)="dragStop()"
>
<div
class="flex cursor-grab items-center justify-between gap-2 rounded-t p-1 active:cursor-grabbing"
[ngClass]="session.focused ? 'bg-slate-100' : 'bg-slate-300'"
cdkDragHandle
>
<div class="flex items-center gap-2">
<mat-icon>control_camera</mat-icon>
<span>
{{ session.version?.tool?.name }} {{ session.version?.name }},
{{ session.type }}
<span *ngIf="session.type === 'readonly'"
>(project {{ session.project!.name }})</span
>
</span>
</div>
<div *ngIf="session.focused" class="flex items-center gap-2">
<span>Focused</span><mat-icon>phonelink</mat-icon>
</div>
<div *ngIf="!session.focused" class="flex items-center gap-2">
<span>Not focused</span>
<mat-icon>phonelink_off</mat-icon>
</div>
</div>

<div class="relative grow">
<div
[ngClass]="session.focused ? '' : 'iframe-overlay bg-black opacity-10'"
></div>
<div [ngClass]="draggingActive ? 'iframe-overlay' : ''"></div>
<iframe
[id]="'session-' + session.id"
[src]="session.safeResourceURL"
class="h-full w-full"
allow="clipboard-read; clipboard-write"
>
</iframe>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { Component, HostListener, Input, OnInit } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Session } from 'src/app/schemes';
import { FullscreenService } from '../../service/fullscreen.service';

@Component({
selector: 'app-floating-window-manager',
templateUrl: './floating-window-manager.component.html',
styleUrls: ['./floating-window-manager.component.css'],
})
@UntilDestroy()
export class FloatingWindowManagerComponent implements OnInit {
@Input() sessions: Session[] = [];

draggingActive = false;

private debounceTimer?: number;

constructor(public fullscreenService: FullscreenService) {}

ngOnInit(): void {
this.fullscreenService.isFullscreen$
.pipe(untilDestroyed(this))
.subscribe(() => this.resizeSessions());
}

focusSession(session: Session) {
this.unfocusSession(session);

document.getElementById('session-' + session.id)?.focus();
session.focused = true;
}

unfocusSession(focusedSession: Session) {
this.sessions
.filter((session) => session !== focusedSession)
.map((session) => (session.focused = false));
}

dragStart() {
this.draggingActive = true;
}

dragStop() {
this.draggingActive = false;
}

@HostListener('window:resize', ['$event'])
onResize() {
window.clearTimeout(this.debounceTimer);

this.debounceTimer = window.setTimeout(() => {
this.resizeSessions();
}, 250);
}

resizeSessions() {
Array.from(document.getElementsByTagName('iframe')).forEach((iframe) => {
const session = this.sessions.find(
(session) => 'session-' + session.id === iframe.id,
);

if (session?.reloadToResize) {
this.reloadIFrame(iframe);
}
});
}

reloadIFrame(iframe: HTMLIFrameElement) {
const src = iframe.src;

iframe.removeAttribute('src');

setTimeout(() => {
iframe.src = src;
}, 100);
}
}
65 changes: 11 additions & 54 deletions frontend/src/app/sessions/session/session.component.html
Original file line number Diff line number Diff line change
@@ -49,6 +49,13 @@
server will not let you connect to the sessions. We're working on making
this possible.
</div>
<div class="mt-2">
<mat-radio-group [(ngModel)]="selectedWindowType">
<mat-radio-button value="floating">Floating window</mat-radio-button>
<mat-radio-button value="tailing">Tailing window</mat-radio-button>
</mat-radio-group>
</div>

<div class="mt-2">
<button
mat-raised-button
@@ -62,60 +69,10 @@
</div>
</div>

<div class="flex gap-0.5" *ngIf="selectedSessions.length">
<div
class="flex w-full flex-col active:z-30"
[ngClass]="{
'z-20': session.focused,
'z-10': !session.focused,
height: (fullscreenService.isFullscreen$ | async) === false,
'h-screen': fullscreenService.isFullscreen$ | async
}"
(click)="focusSession(session)"
*ngFor="let session of selectedSessions"
cdkDrag
(cdkDragStarted)="dragStart()"
(cdkDragEnded)="dragStop()"
>
<div
class="flex cursor-grab items-center justify-between gap-2 rounded-t p-1 active:cursor-grabbing"
[ngClass]="session.focused ? 'bg-slate-100' : 'bg-slate-300'"
cdkDragHandle
>
<div class="flex items-center gap-2">
<mat-icon>control_camera</mat-icon>
<span>
{{ session.version?.tool?.name }} {{ session.version?.name }},
{{ session.type }}
<span *ngIf="session.type === 'readonly'"
>(project {{ session.project!.name }})</span
>
</span>
</div>
<div *ngIf="session.focused" class="flex items-center gap-2">
<span>Focused</span><mat-icon>phonelink</mat-icon>
</div>
<div *ngIf="!session.focused" class="flex items-center gap-2">
<span>Not focused</span>
<mat-icon>phonelink_off</mat-icon>
</div>
</div>

<div class="relative grow">
<div
[ngClass]="session.focused ? '' : 'iframe-overlay bg-black opacity-10'"
></div>
<div [ngClass]="draggingActive ? 'iframe-overlay' : ''"></div>
<iframe
[id]="'session-' + session.id"
[src]="session.safeResourceURL"
class="h-full w-full"
allow="clipboard-read; clipboard-write"
>
</iframe>
</div>
</div>
</div>
<app-floating-window-manager
*ngIf="selectedSessions.length"
[sessions]="selectedSessions"
></app-floating-window-manager>

<div class="fixed bottom-4 right-4 z-50">
<button
76 changes: 11 additions & 65 deletions frontend/src/app/sessions/session/session.component.ts
Original file line number Diff line number Diff line change
@@ -3,10 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { Component, HostListener, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { MatLegacyCheckboxChange as MatCheckboxChange } from '@angular/material/legacy-checkbox';
import { DomSanitizer } from '@angular/platform-browser';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { filter, take } from 'rxjs';
import { LocalStorageService } from 'src/app/general/auth/local-storage/local-storage.service';
import { Session } from 'src/app/schemes';
@@ -18,15 +17,12 @@ import { UserSessionService } from 'src/app/sessions/service/user-session.servic
@Component({
selector: 'app-session',
templateUrl: './session.component.html',
styleUrls: ['./session.component.css'],
})
@UntilDestroy()
export class SessionComponent implements OnInit {
cachedSessions?: CachedSession[] = undefined;
selectedSessions: Session[] = [];
private debounceTimer?: number;

draggingActive = false;
selectedSessions: Session[] = [];
selectedWindowType: string = 'floating';

constructor(
public userSessionService: UserSessionService,
@@ -43,16 +39,20 @@ export class SessionComponent implements OnInit {
return this.cachedSessions?.filter((session) => session.checked);
}

get isTailingWindow(): boolean {
return this.selectedWindowType === 'tailing';
}

get isFloatingWindow() {
return this.selectedWindowType === 'floating';
}

changeSessionSelection(event: MatCheckboxChange, session: CachedSession) {
session.checked = event.checked;
}

ngOnInit(): void {
this.initializeCachedSessions();

this.fullscreenService.isFullscreen$
.pipe(untilDestroyed(this))
.subscribe(() => this.resizeSessions());
}

initializeCachedSessions() {
@@ -88,60 +88,6 @@ export class SessionComponent implements OnInit {
}
});
}

focusSession(session: Session) {
this.unfocusSession(session);

document.getElementById('session-' + session.id)?.focus();
session.focused = true;
}

unfocusSession(focusedSession: Session) {
this.selectedSessions
.filter((session) => session !== focusedSession)
.map((session) => {
session.focused = false;
});
}

dragStart() {
this.draggingActive = true;
}

dragStop() {
this.draggingActive = false;
}

@HostListener('window:resize', ['$event'])
onResize() {
window.clearTimeout(this.debounceTimer);

this.debounceTimer = window.setTimeout(() => {
this.resizeSessions();
}, 250);
}

resizeSessions() {
Array.from(document.getElementsByTagName('iframe')).forEach((iframe) => {
const session = this.selectedSessions.find(
(session) => 'session-' + session.id === iframe.id,
);

if (session?.reloadToResize) {
this.reloadIFrame(iframe);
}
});
}

reloadIFrame(iframe: HTMLIFrameElement) {
const src = iframe.src;

iframe.removeAttribute('src');

setTimeout(() => {
iframe.src = src;
}, 100);
}
}

type CachedSession = Session & {