@3dsource/angular-unreal-module 0.0.110-dev.0 → 0.0.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,20 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, ChangeDetectionStrategy, Component, Injectable, DestroyRef, signal, Pipe, input, ElementRef, viewChild, effect, untracked, afterNextRender, HostListener, computed, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
2
+ import { InjectionToken, inject, ChangeDetectionStrategy, Component, Injectable, DestroyRef, effect, untracked, signal, Pipe, input, ElementRef, viewChild, afterNextRender, HostListener, computed, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
3
3
  import { SourceButtonComponent, SourceLoadingComponent } from '@3dsource/source-ui-native';
4
- import { map as map$1, distinctUntilChanged, takeWhile, filter, tap, withLatestFrom, exhaustMap, catchError, takeUntil, switchMap as switchMap$1, timeout as timeout$1, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1, delay, skip as skip$1 } from 'rxjs/operators';
5
- import { Logger, calculateMedian, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, whereNot, Truthy, Falsy, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
6
4
  import { merge, map, scan, concatMap, of, animationFrameScheduler, interval, Observable, debounceTime, switchMap, take, fromEvent, EMPTY, timer, timeout, mergeMap, Subject, skip, share, from, throwError, defer, retry, tap as tap$1, startWith, takeUntil as takeUntil$1, throttleTime, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, pairwise, Subscription, bufferTime, BehaviorSubject, combineLatestWith, distinctUntilChanged as distinctUntilChanged$1, concat } from 'rxjs';
7
5
  import { createAction, props, createReducer, on, createFeature, createSelector, Store, provideState } from '@ngrx/store';
8
6
  import { Actions, createEffect, ofType, provideEffects } from '@ngrx/effects';
7
+ import { map as map$1, distinctUntilChanged, takeWhile, filter, tap, withLatestFrom, exhaustMap, catchError, takeUntil, switchMap as switchMap$1, timeout as timeout$1, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1, delay, skip as skip$1 } from 'rxjs/operators';
8
+ import { Logger, calculateMedian, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, whereNot, Truthy, Falsy, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
9
9
  import { concatLatestFrom } from '@ngrx/operators';
10
10
  import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
11
11
  import { HttpClient } from '@angular/common/http';
12
12
  import { ScrollStrategyOptions } from '@angular/cdk/overlay';
13
13
  import { MetaBoxCommand, EPayloadVersion } from '@3dsource/types-unreal';
14
+ import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
14
15
  import { v4 } from 'uuid';
15
16
  import { DomSanitizer } from '@angular/platform-browser';
16
17
  import { NgOptimizedImage, AsyncPipe, JsonPipe } from '@angular/common';
17
- import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
18
18
  import * as i1 from '@angular/forms';
19
19
  import { FormsModule } from '@angular/forms';
20
20
 
@@ -1657,29 +1657,42 @@ class SubService {
1657
1657
  class AFKService extends SubService {
1658
1658
  constructor() {
1659
1659
  super();
1660
- // Optionally detect if the user is not interacting (AFK) and disconnect them.
1661
- this.enabled = true; // Set to true to enable the AFK system.
1662
- this.closeTimeout = DEFAULT_AFK_TIMEOUT_PERIOD; // The time after the warning when we disconnect the user.
1663
1660
  this.active = false; // Whether the AFK system is currently looking for inactivity.
1664
1661
  this.countdown = 0; // The inactivity warning overlay has a countdown to show time until disconnect.
1665
- this.selectWarnTimeout = this.store.selectSignal(selectWarnTimeout);
1662
+ this.warnTimeout = this.store.selectSignal(selectWarnTimeout);
1666
1663
  this.isViewportReady = this.store.selectSignal(unrealFeature.selectViewportReady);
1667
1664
  this.unrealConfig = inject(UNREAL_CONFIG, { optional: true });
1665
+ /** Ticks every time warnTimeout changes or resetAfk action fires */
1666
+ this.afkResetTrigger = toSignal(merge(this.store.select(selectWarnTimeout), this.actions$.pipe(ofType(resetAfk))));
1667
+ /** Ticks when cirrus disconnects */
1668
+ this.disconnected = toSignal(this.disconnect$);
1668
1669
  this.destroyRef.onDestroy(() => this.clearTimers());
1669
- this.init();
1670
- }
1671
- init() {
1672
1670
  if (this.unrealConfig?.playwright) {
1673
1671
  return;
1674
1672
  }
1675
- merge(this.store.select(selectWarnTimeout), this.actions$.pipe(ofType(resetAfk)))
1676
- .pipe(withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady), takeUntil(this.destroy$))
1677
- .subscribe(() => this.resetAfkWarningTimer());
1678
- this.store
1679
- .select(unrealFeature.selectViewportReady)
1680
- .pipe(distinctUntilChanged(), filter(Truthy), takeUntil(this.destroy$))
1681
- .subscribe(() => this.startAfkWarningTimer());
1682
- this.disconnect$.subscribe(() => this.stop());
1673
+ // Reset AFK warning timer when warnTimeout changes or resetAfk action fires
1674
+ effect(() => {
1675
+ this.afkResetTrigger();
1676
+ untracked(() => {
1677
+ if (this.isViewportReady()) {
1678
+ this.resetAfkWarningTimer();
1679
+ }
1680
+ });
1681
+ });
1682
+ // Start AFK warning timer when viewport becomes ready
1683
+ effect(() => {
1684
+ const ready = this.isViewportReady();
1685
+ if (ready) {
1686
+ untracked(() => this.startAfkWarningTimer());
1687
+ }
1688
+ });
1689
+ // Stop on disconnect
1690
+ effect(() => {
1691
+ const disconnected = this.disconnected();
1692
+ if (disconnected !== undefined) {
1693
+ untracked(() => this.stop());
1694
+ }
1695
+ });
1683
1696
  }
1684
1697
  hideOverlay() {
1685
1698
  this.store.dispatch(setAfkTimerHide());
@@ -1688,7 +1701,7 @@ class AFKService extends SubService {
1688
1701
  * Start a timer which when elapsed will warn the user they are inactive.
1689
1702
  */
1690
1703
  startAfkWarningTimer() {
1691
- this.active = this.enabled;
1704
+ this.active = true;
1692
1705
  this.resetAfkWarningTimer();
1693
1706
  }
1694
1707
  /**
@@ -1699,7 +1712,7 @@ class AFKService extends SubService {
1699
1712
  this.clearTimers();
1700
1713
  this.warnTimer = setTimeout(() => {
1701
1714
  this.showAfkOverlay();
1702
- }, (this.selectWarnTimeout() ?? 0) * 1000);
1715
+ }, (this.warnTimeout() || DEFAULT_AFK_TIMEOUT) * 1000);
1703
1716
  }
1704
1717
  }
1705
1718
  /**
@@ -1728,7 +1741,7 @@ class AFKService extends SubService {
1728
1741
  }
1729
1742
  // Pause the timer while the user is looking at the inactivity warning overlay.
1730
1743
  this.active = false;
1731
- this.countdown = this.closeTimeout;
1744
+ this.countdown = DEFAULT_AFK_TIMEOUT_PERIOD;
1732
1745
  this.store.dispatch(setAfkTimerVisible());
1733
1746
  this.updateCountDown(this.countdown);
1734
1747
  if (InputOptions.controlScheme === ControlScheme.LockedMouse) {
@@ -1741,7 +1754,7 @@ class AFKService extends SubService {
1741
1754
  this.hideOverlay();
1742
1755
  this.store.dispatch(disconnectStream({
1743
1756
  reason: DisconnectReason.Afk,
1744
- message: `AFK timeout:${this.selectWarnTimeout()} seconds, popup timeout:${this.closeTimeout} seconds`,
1757
+ message: `AFK timeout:${this.warnTimeout()} seconds, popup timeout:${DEFAULT_AFK_TIMEOUT_PERIOD} seconds`,
1745
1758
  }));
1746
1759
  clearInterval(this.countdownTimer);
1747
1760
  }
@@ -3936,6 +3949,8 @@ class UnrealCommunicatorService {
3936
3949
  this.unrealConfig = inject(UNREAL_CONFIG, { optional: true });
3937
3950
  this.destroy$ = new Subject();
3938
3951
  this.cirrusConnected = this.store.selectSignal(unrealFeature.selectCirrusConnected);
3952
+ this.afkIgnoredCommands = new Set(['executeConsoleCommand']);
3953
+ this.skipAfkReset = false;
3939
3954
  this.init();
3940
3955
  }
3941
3956
  init() {
@@ -3962,7 +3977,11 @@ class UnrealCommunicatorService {
3962
3977
  const uniqId = data?.correlationId ?? generateUuid();
3963
3978
  const sendData = { ...data, correlationId: uniqId };
3964
3979
  Logger.colored(...COLOR_CODES.TO_UNREAL, sendData);
3980
+ if (this.afkIgnoredCommands.has(data.command)) {
3981
+ this.skipAfkReset = true;
3982
+ }
3965
3983
  this.emitUIInteraction(sendData);
3984
+ this.skipAfkReset = false;
3966
3985
  this.store.dispatch(commandStarted({ id: uniqId, command: data.command }));
3967
3986
  }
3968
3987
  }
@@ -3986,7 +4005,9 @@ class UnrealCommunicatorService {
3986
4005
  sendInputData(data) {
3987
4006
  this.isPlaywrightMode();
3988
4007
  try {
3989
- this.store.dispatch(resetAfk());
4008
+ if (!this.skipAfkReset) {
4009
+ this.store.dispatch(resetAfk());
4010
+ }
3990
4011
  this.webRtcPlayerService.send(data);
3991
4012
  }
3992
4013
  catch (error) {
@@ -4607,7 +4628,7 @@ class RegionsPingService {
4607
4628
  async fetchWithTimeout(url, options = {}, timeout) {
4608
4629
  const controller = new AbortController();
4609
4630
  options.signal = controller.signal;
4610
- const timeoutId = setTimeout(() => controller.abort(), timeout);
4631
+ const timeoutId = setTimeout(() => controller.abort(new DOMException('Request timed out', 'TimeoutError')), timeout);
4611
4632
  try {
4612
4633
  const response = await fetch(url, options);
4613
4634
  clearTimeout(timeoutId);
@@ -6068,12 +6089,15 @@ class ImageLoadingSrcComponent {
6068
6089
  this.imageLoadingSrc = this.#store.selectSignal(unrealFeature.selectImageLoadingSrc);
6069
6090
  }
6070
6091
  #store;
6092
+ imageError() {
6093
+ this.#store.dispatch(setLoadingImageSrc({ src: '' }));
6094
+ }
6071
6095
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6072
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ImageLoadingSrcComponent, isStandalone: true, selector: "app-image-loading-src", ngImport: i0, template: "@let image = imageLoadingSrc();\r\n@if (isLoaderScreenVisible()) {\r\n <div class=\"preload\">\r\n <div class=\"back\">\r\n @if (image) {\r\n <img [ngSrc]=\"image\" fill loading=\"lazy\" alt=\"image loading src\" />\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6096
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ImageLoadingSrcComponent, isStandalone: true, selector: "app-image-loading-src", ngImport: i0, template: "@let image = imageLoadingSrc();\r\n@if (isLoaderScreenVisible()) {\r\n <div class=\"preload\">\r\n <div class=\"back\">\r\n @if (image) {\r\n <img\r\n (error)=\"imageError()\"\r\n [ngSrc]=\"image\"\r\n fill\r\n loading=\"lazy\"\r\n alt=\"image loading src\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6073
6097
  }
6074
6098
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
6075
6099
  type: Component,
6076
- args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\r\n@if (isLoaderScreenVisible()) {\r\n <div class=\"preload\">\r\n <div class=\"back\">\r\n @if (image) {\r\n <img [ngSrc]=\"image\" fill loading=\"lazy\" alt=\"image loading src\" />\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"] }]
6100
+ args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\r\n@if (isLoaderScreenVisible()) {\r\n <div class=\"preload\">\r\n <div class=\"back\">\r\n @if (image) {\r\n <img\r\n (error)=\"imageError()\"\r\n [ngSrc]=\"image\"\r\n fill\r\n loading=\"lazy\"\r\n alt=\"image loading src\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"] }]
6077
6101
  }] });
6078
6102
 
6079
6103
  class StatGraphComponent {
@@ -6447,7 +6471,10 @@ class IntroSrcComponent {
6447
6471
  });
6448
6472
  }
6449
6473
  createVideo(src, videoContainer) {
6450
- const matchAbsolutePath = !!src.match(/.*\.(mp4|WebM)(\?.*)?$/i);
6474
+ if (typeof src !== 'string') {
6475
+ return;
6476
+ }
6477
+ const matchAbsolutePath = /.*\.(mp4|WebM)(\?.*)?$/i.test(src);
6451
6478
  videoContainer.innerHTML = ''; // This will remove all existing content within the layout
6452
6479
  const videoElement = document.createElement('video');
6453
6480
  videoElement.autoplay = true;
@@ -6469,15 +6496,19 @@ class IntroSrcComponent {
6469
6496
  }
6470
6497
  sourceElement.type = 'video/mp4';
6471
6498
  videoElement.appendChild(sourceElement);
6472
- videoElement.innerHTML += 'Browser not supported';
6499
+ videoElement.addEventListener('error', () => this.#store.dispatch(resetIntroSrc()));
6500
+ sourceElement.addEventListener('error', () => this.#store.dispatch(resetIntroSrc()));
6473
6501
  videoContainer.appendChild(videoElement);
6474
6502
  }
6503
+ imageError() {
6504
+ this.#store.dispatch(resetIntroSrc());
6505
+ }
6475
6506
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6476
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: IntroSrcComponent, isStandalone: true, selector: "app-intro-src", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true, isSignal: true }], ngImport: i0, template: "@let image = imageIntroSrc();\r\n@if (image) {\r\n <div class=\"backImage\">\r\n <img [ngSrc]=\"image\" fill alt=\"imageSrc\" />\r\n </div>\r\n} @else if (videoIntroSrc()) {\r\n <div #videoPlayerCover class=\"backVideo\"></div>\r\n}\r\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6507
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: IntroSrcComponent, isStandalone: true, selector: "app-intro-src", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true, isSignal: true }], ngImport: i0, template: "@let image = imageIntroSrc();\r\n@if (image) {\r\n <div class=\"backImage\">\r\n <img (error)=\"imageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\r\n </div>\r\n} @else if (videoIntroSrc()) {\r\n <div #videoPlayerCover class=\"backVideo\"></div>\r\n}\r\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6477
6508
  }
6478
6509
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IntroSrcComponent, decorators: [{
6479
6510
  type: Component,
6480
- args: [{ selector: 'app-intro-src', imports: [NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = imageIntroSrc();\r\n@if (image) {\r\n <div class=\"backImage\">\r\n <img [ngSrc]=\"image\" fill alt=\"imageSrc\" />\r\n </div>\r\n} @else if (videoIntroSrc()) {\r\n <div #videoPlayerCover class=\"backVideo\"></div>\r\n}\r\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
6511
+ args: [{ selector: 'app-intro-src', imports: [NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = imageIntroSrc();\r\n@if (image) {\r\n <div class=\"backImage\">\r\n <img (error)=\"imageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\r\n </div>\r\n} @else if (videoIntroSrc()) {\r\n <div #videoPlayerCover class=\"backVideo\"></div>\r\n}\r\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
6481
6512
  }], ctorParameters: () => [], propDecorators: { videoElement: [{ type: i0.ViewChild, args: ['videoPlayerCover', { isSignal: true }] }] } });
6482
6513
 
6483
6514
  class UnrealSceneComponent {