Files
zitadel/console/src/app/services/overlay/overlay-workflow.service.ts
T
41e60b3d8a feat(console): v2 feature cleanup, fix styling issues, instance naming (#3530)
* new console

* move npm ci to angular build

* rel path for assets

* local grpc copy

* login policy, rm clear views, features rel path

* login_hint param

* edit default color values

* remove features, zitadel-tier, contact styles

* html formatter

* rm feature restrictions, fix theming issues

* instance naming

* statehandler

* rm class

* rm class

* update footer links

* statehandler

* stroked color

Co-authored-by: Livio Amstutz <[email protected]>
2022-04-29 10:25:12 +02:00

190 lines
5.2 KiB
TypeScript

import { MediaMatcher } from '@angular/cdk/layout';
import { Injectable, OnDestroy } from '@angular/core';
import { BehaviorSubject, Observable, of, Subject, switchMap, take, takeUntil, zip } from 'rxjs';
import { GrpcAuthService } from '../grpc-auth.service';
import { CnslOverlayRef } from './overlay-ref';
import { OverlayService } from './overlay.service';
export interface OverlayWorkflow {
currentIndex: number;
currentRef?: CnslOverlayRef;
overlays: CnslOverlay[];
}
export interface CnslOverlay {
id: string;
origin: string;
toHighlight: string[];
content: {
count?: number;
number?: number;
i18nText: string;
};
requirements?: {
media?: string;
permission?: string[];
};
}
@Injectable({
providedIn: 'root',
})
export class OverlayWorkflowService implements OnDestroy {
public readonly currentWorkflow$: BehaviorSubject<OverlayWorkflow | null> = new BehaviorSubject<OverlayWorkflow | null>(
null,
);
public destroy$: Subject<void> = new Subject();
public openRef: CnslOverlayRef | null = null;
public highlightedIds: { [id: string]: number } = {};
public callback: Function | null = null;
constructor(private mediaMatcher: MediaMatcher, overlayService: OverlayService, private authService: GrpcAuthService) {
this.currentWorkflow$.pipe(takeUntil(this.destroy$)).subscribe((workflow) => {
if (this.openRef) {
this.openRef.close();
this.openRef = null;
}
Object.keys(this.highlightedIds).forEach((id) => {
const element = document.getElementById(id);
if (element) {
element.style.removeProperty('z-index');
delete this.highlightedIds[id];
}
});
const overlay = workflow?.overlays[workflow.currentIndex];
if (overlay) {
this.meetsRequirements(overlay)
.pipe(take(1))
.subscribe((can) => {
if (can) {
this.openRef = overlayService.open(overlay);
overlay.toHighlight.forEach((id) => {
const element = document.getElementById(id);
if (element) {
const oldZ = element.style.zIndex;
this.highlightedIds[id] = Number(oldZ);
element.style.zIndex = '1001';
}
});
} else {
this.nextStep();
}
});
}
});
}
public meetsRequirements(overlay: CnslOverlay): Observable<boolean> {
return zip([
this.meetsMediaRequirements(overlay),
this.meetsPermissionRequirements(overlay),
] as Observable<boolean>[]).pipe(
switchMap(([media, permission]) => {
return of(media && permission);
}),
);
}
public meetsMediaRequirements(overlay: CnslOverlay): Observable<boolean> {
const media = overlay.requirements?.media;
if (media) {
const matches = this.mediaMatcher.matchMedia(media).matches;
if (!matches) {
return of(false);
} else {
return of(true);
}
} else {
return of(true);
}
}
public meetsPermissionRequirements(overlay: CnslOverlay): Observable<boolean> {
const regexArray = overlay.requirements?.permission;
if (regexArray && regexArray.length) {
return this.authService.isAllowed(regexArray);
} else {
return of(true);
}
}
public reset(): void {
this.currentWorkflow$.next(null);
if (this.callback) {
this.callback();
}
}
public ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
public startWorkflow(overlays: CnslOverlay[], cb: Function | null): void {
if (cb) {
this.callback = cb;
}
this.currentWorkflow$.next({ overlays, currentIndex: 0 });
}
public nextStep(): void {
const currentWorkflow = this.currentWorkflow$.value;
if (this.nextPossible && currentWorkflow) {
const nextIndex = currentWorkflow?.currentIndex + 1;
this.currentWorkflow$.next({ ...currentWorkflow, currentIndex: nextIndex });
} else {
this.currentWorkflow$.next(null);
if (this.callback) {
this.callback();
}
}
}
public previousStep(): void {
const currentWorkflow = this.currentWorkflow$.value;
if (this.previousPossible && currentWorkflow) {
const nextIndex = currentWorkflow?.currentIndex - 1;
this.currentWorkflow$.next({ ...currentWorkflow, currentIndex: nextIndex });
}
}
public get nextPossible(): boolean {
const currentWorkflow = this.currentWorkflow$.value;
if (currentWorkflow) {
const nextIndex = currentWorkflow?.currentIndex + 1;
if (nextIndex < currentWorkflow?.overlays.length) {
return true;
}
}
return false;
}
public get isLast(): boolean {
const currentWorkflow = this.currentWorkflow$.value;
if (currentWorkflow) {
const currentIndex = currentWorkflow?.currentIndex;
if (currentIndex == currentWorkflow?.overlays.length - 1) {
return true;
}
}
return false;
}
public get previousPossible(): boolean {
const currentWorkflow = this.currentWorkflow$.value;
if (currentWorkflow && currentWorkflow.currentIndex > 0) {
return true;
} else {
return false;
}
}
}