mapillary-js
Version:
A WebGL interactive street imagery library
252 lines (221 loc) • 9.43 kB
text/typescript
import {
distinctUntilChanged,
first,
switchMap,
} from "rxjs/operators";
import {
Observable,
Subscription,
} from "rxjs";
import { Container } from "./Container";
import { Navigator } from "./Navigator";
import { Observer } from "./Observer";
import { ComponentOptions } from "./options/ComponentOptions";
import { Component } from "../component/Component";
import { ComponentService } from "../component/ComponentService";
import { CoverComponent } from "../component/cover/CoverComponent";
import { ComponentConfiguration }
from "../component/interfaces/ComponentConfiguration";
import { CoverConfiguration }
from "../component/interfaces/CoverConfiguration";
import { Image } from "../graph/Image";
import { CoverState } from "../component/cover/CoverState";
import { FallbackComponentName } from "../component/fallback/FallbackComponentName";
import { ComponentName } from "../component/ComponentName";
export class ComponentController {
private _container: Container;
private _coverComponent: CoverComponent;
private _observer: Observer;
private _navigator: Navigator;
private _componentService: ComponentService;
private _options: ComponentOptions;
private _key: string;
private _navigable: boolean;
private _configurationSubscription: Subscription;
constructor(
container: Container,
navigator: Navigator,
observer: Observer,
key: string,
options: ComponentOptions,
componentService?: ComponentService) {
this._container = container;
this._observer = observer;
this._navigator = navigator;
this._options = options != null ? options : {};
this._key = key;
this._navigable = key == null;
this._componentService = !!componentService ?
componentService :
new ComponentService(this._container, this._navigator);
this._coverComponent = this._componentService.getCover();
this._initializeComponents();
if (key) {
this._initilizeCoverComponent();
this._subscribeCoverComponent();
} else {
this._navigator.movedToId$.pipe(
first(
(k: string): boolean => {
return k != null;
}))
.subscribe(
(k: string): void => {
this._key = k;
this._componentService.deactivateCover();
this._coverComponent.configure({
id: this._key,
state: CoverState.Hidden,
});
this._subscribeCoverComponent();
this._navigator.stateService.start();
this._navigator.cacheService.start();
this._navigator.panService.start();
this._observer.startEmit();
});
}
}
public get navigable(): boolean {
return this._navigable;
}
public get<TComponent extends Component<ComponentConfiguration>>(name: string): TComponent {
return this._componentService.get<TComponent>(name);
}
public activate(name: ComponentName | FallbackComponentName): void {
this._componentService.activate(name);
}
public activateCover(): void {
this._coverComponent.configure({ state: CoverState.Visible });
}
public deactivate(name: ComponentName | FallbackComponentName): void {
this._componentService.deactivate(name);
}
public deactivateCover(): void {
this._coverComponent.configure({ state: CoverState.Loading });
}
public remove(): void {
this._componentService.remove();
if (this._configurationSubscription != null) {
this._configurationSubscription.unsubscribe();
}
}
private _initializeComponents(): void {
const options = this._options;
this._uFalse(options.fallback?.image, "imagefallback");
this._uFalse(options.fallback?.navigation, "navigationfallback");
this._uFalse(options.marker, "marker");
this._uFalse(options.popup, "popup");
this._uFalse(options.slider, "slider");
this._uFalse(options.spatial, "spatial");
this._uFalse(options.tag, "tag");
this._uTrue(options.attribution, "attribution");
this._uTrue(options.bearing, "bearing");
this._uTrue(options.cache, "cache");
this._uTrue(options.direction, "direction");
this._uTrue(options.image, "image");
this._uTrue(options.keyboard, "keyboard");
this._uTrue(options.pointer, "pointer");
this._uTrue(options.sequence, "sequence");
this._uTrue(options.zoom, "zoom");
}
private _initilizeCoverComponent(): void {
let options: ComponentOptions = this._options;
this._coverComponent.configure({ id: this._key });
if (options.cover === undefined || options.cover) {
this.activateCover();
} else {
this.deactivateCover();
}
}
private _setNavigable(navigable: boolean): void {
if (this._navigable === navigable) {
return;
}
this._navigable = navigable;
this._observer.navigable$.next(navigable);
}
private _subscribeCoverComponent(): void {
this._configurationSubscription =
this._coverComponent.configuration$.pipe(
distinctUntilChanged(
undefined,
(c: CoverConfiguration): CoverState => {
return c.state;
}))
.subscribe((conf: CoverConfiguration) => {
if (conf.state === CoverState.Loading) {
this._navigator.stateService.currentId$.pipe(
first(),
switchMap(
(key: string): Observable<Image> => {
const keyChanged: boolean = key == null || key !== conf.id;
if (keyChanged) {
this._setNavigable(false);
}
return keyChanged ?
this._navigator.moveTo$(conf.id) :
this._navigator.stateService.currentImage$.pipe(
first());
}))
.subscribe(
(): void => {
this._navigator.stateService.start();
this._navigator.cacheService.start();
this._navigator.panService.start();
this._observer.startEmit();
this._coverComponent.configure({ state: CoverState.Hidden });
this._componentService.deactivateCover();
this._setNavigable(true);
},
(error: Error): void => {
console.error("Failed to deactivate cover.", error);
this._coverComponent.configure({ state: CoverState.Visible });
});
} else if (conf.state === CoverState.Visible) {
this._observer.stopEmit();
this._navigator.stateService.stop();
this._navigator.cacheService.stop();
this._navigator.playService.stop();
this._navigator.panService.stop();
this._componentService.activateCover();
this._setNavigable(conf.id == null);
}
});
}
private _uFalse<TConfiguration extends ComponentConfiguration>(
option: boolean | TConfiguration,
name: ComponentName | FallbackComponentName): void {
if (option === undefined) {
this._componentService.deactivate(name);
return;
}
if (typeof option === "boolean") {
if (option) {
this._componentService.activate(name);
} else {
this._componentService.deactivate(name);
}
return;
}
this._componentService.configure(name, <TConfiguration>option);
this._componentService.activate(name);
}
private _uTrue<TConfiguration extends ComponentConfiguration>(
option: boolean | TConfiguration,
name: ComponentName | FallbackComponentName): void {
if (option === undefined) {
this._componentService.activate(name);
return;
}
if (typeof option === "boolean") {
if (option) {
this._componentService.activate(name);
} else {
this._componentService.deactivate(name);
}
return;
}
this._componentService.configure(name, <TConfiguration>option);
this._componentService.activate(name);
}
}