diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 2172c39..2794819 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -64,6 +64,7 @@ import { PurchaseSuccessComponent } from './purchase-success/purchase-success.co import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.component'; import { TicketSmallComponent } from './ticket-small/ticket-small.component'; import { TicketListComponent } from './ticket-list/ticket-list.component'; +import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component'; @NgModule({ @@ -107,6 +108,7 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; PurchaseFailedComponent, TicketSmallComponent, TicketListComponent, + ZoomWarningComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index 6f48c5e..e5692a4 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -12,7 +12,7 @@ import { AuthGuard } from './auth.guard'; const routes: Routes = [ // Seiten ohne Layout { path: 'landing', component: HomeComponent }, - { path: 'poc-model', component: PocModelComponent }, + { path: 'poc-model', component: PocModelComponent, data: { allowMobile: true } }, // Seiten mit MainLayout { diff --git a/src/app/app.html b/src/app/app.html index 67e7bd4..bc230ce 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1 +1,2 @@ + diff --git a/src/app/device-detection.service.ts b/src/app/device-detection.service.ts new file mode 100644 index 0000000..c4035ea --- /dev/null +++ b/src/app/device-detection.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class DeviceDetectionService { + private _isMobile: boolean; + + constructor() { + this._isMobile = this.checkIfMobile(); + } + + isMobile(): boolean { + return this._isMobile; + } + + private checkIfMobile(): boolean { + const userAgent = navigator.userAgent.toLowerCase(); + const isMobileUA = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent); + const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + const isSmallScreen = window.innerWidth < 768; + + return isMobileUA || (isTouchDevice && isSmallScreen); + } + + recheckDevice(): void { + this._isMobile = this.checkIfMobile(); + } +} diff --git a/src/app/zoom-detection.service.ts b/src/app/zoom-detection.service.ts new file mode 100644 index 0000000..d9e3311 --- /dev/null +++ b/src/app/zoom-detection.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class ZoomDetectionService { + private zoomLevel$ = new BehaviorSubject(this.getZoomLevel()); + + constructor() { + // Zoom-Änderungen überwachen + fromEvent(window, 'resize') + .pipe(debounceTime(200)) + .subscribe(() => { + this.zoomLevel$.next(this.getZoomLevel()); + }); + } + + getZoomLevel(): number { + const devicePixelRatio = window.devicePixelRatio || 1; + return devicePixelRatio; + } + + getZoomLevel$() { + return this.zoomLevel$.asObservable(); + } + + isZoomOutOfRange(minZoom: number = 0.95, maxZoom: number = 1.05): boolean { + const currentZoom = this.getZoomLevel(); + return currentZoom < minZoom || currentZoom > maxZoom; + } +} diff --git a/src/app/zoom-warning/zoom-warning.component.css b/src/app/zoom-warning/zoom-warning.component.css new file mode 100644 index 0000000..21fcb9c --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.css @@ -0,0 +1,14 @@ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(20px) scale(0.9); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.zoom-info-box { + animation: slideIn 0.3s ease-out; +} diff --git a/src/app/zoom-warning/zoom-warning.component.html b/src/app/zoom-warning/zoom-warning.component.html new file mode 100644 index 0000000..ea79125 --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.html @@ -0,0 +1,81 @@ +@if (isOutOfRange && !isDismissed && !isMobile) { +
+ + +
+ + screenshot_monitor + + + warning + +
+ +

Browser-Zoom nicht optimal!

+

Ihr Browser-Zoom ist auf {{ currentZoomPercentage }}% eingestellt.
Für die beste Darstellung empfehlen wir 100%.

+ +
+
+

Browser-Zoom zurücksetzen:

+
+

+ Strg + / + + + + 0 + (Windows, Linux / Mac) +

+
+
+ +
+

Windows-Skalierung prüfen:

+
    +
  1. Öffnen Sie die Windows-Einstellungen
  2. +
  3. Navigieren Sie zu SystemBildschirm
  4. +
  5. Setzen Sie unter "Skalierung" den Wert auf 100%
  6. +
+
+
+
+} + +@if (showMobileWarning) { +
+
+ +

InfiniMotion

+
+
+
+
+
+ + screenshot_monitor + + + warning + +
+ +

Nur am PC verfügbar

+ +

+ Diese Anwendung ist für die Nutzung am Desktop-PC optimiert und kann auf mobilen Geräten nicht verwendet werden. +

+ +
+

Deine derzeitige URL:

+

+ {{ currentUrl }} +

+
+
+
+} diff --git a/src/app/zoom-warning/zoom-warning.component.ts b/src/app/zoom-warning/zoom-warning.component.ts new file mode 100644 index 0000000..8802167 --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.ts @@ -0,0 +1,100 @@ +import { DeviceDetectionService } from './../device-detection.service'; +import { Component, HostListener, inject, OnDestroy, OnInit } from '@angular/core'; +import { filter, Subject, takeUntil } from 'rxjs'; +import { ZoomDetectionService } from '../zoom-detection.service'; +import { NavigationEnd, Router } from '@angular/router'; + +@Component({ + selector: 'app-zoom-warning', + standalone: false, + templateUrl: './zoom-warning.component.html', + styleUrl: './zoom-warning.component.css', +}) +export class ZoomWarningComponent implements OnInit, OnDestroy { + currentZoomPercentage = 100; + isOutOfRange = false; + isDismissed = false; + isMobile = false; + showMobileWarning = false; + currentUrl = ''; + + private destroy$ = new Subject(); + private currentZoomLevel = 1; + private lastZoomLevel = 0; + + zoomDetectionService = inject(ZoomDetectionService); + deviceDetectionService = inject(DeviceDetectionService) + + constructor(private router: Router) { + this.isMobile = this.deviceDetectionService.isMobile(); + this.currentUrl = window.location.href; + this.checkIfShouldShowMobileWarning(); + } + + ngOnInit() { + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + takeUntil(this.destroy$) + ) + .subscribe(() => { + this.checkIfShouldShowMobileWarning(); + }); + + if (!this.isMobile) { + this.updateZoomInfo(); + + this.zoomDetectionService.getZoomLevel$() + .pipe(takeUntil(this.destroy$)) + .subscribe((newZoomLevel) => { + if (Math.abs(newZoomLevel - this.lastZoomLevel) > 0.01) { + this.lastZoomLevel = newZoomLevel; + this.isDismissed = false; + this.updateZoomInfo(); + } + }); + } + } + + private checkIfShouldShowMobileWarning() { + if (!this.isMobile) { + this.showMobileWarning = false; + return; + } + + const currentRoute = this.router.routerState.root; + const allowMobile = this.getRouteData(currentRoute, 'allowMobile'); + + this.showMobileWarning = !allowMobile; + } + + private getRouteData(route: any, key: string): any { + while (route) { + if (route.snapshot?.data?.[key] !== undefined) { + return route.snapshot.data[key]; + } + route = route.firstChild; + } + return false; + } + + private updateZoomInfo() { + this.currentZoomLevel = this.zoomDetectionService.getZoomLevel(); + this.currentZoomPercentage = Math.round(this.currentZoomLevel * 100); + this.isOutOfRange = this.zoomDetectionService.isZoomOutOfRange(); + } + + getCompensationTransform(): string { + const scale = 1 / this.currentZoomLevel; + return `scale(${scale})`; + } + + dismissWarning() { + this.isDismissed = true; + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } +}