UNPKG

@paperbits/core

Version:
213 lines (175 loc) 7.98 kB
import * as ko from "knockout"; import template from "./mapEditor.html"; import { Component, OnMounted, Param, Event } from "@paperbits/common/ko/decorators"; import { MapModel, MarkerModel } from "../mapModel"; import { SizeStylePluginConfig } from "@paperbits/styles/plugins"; import { ChangeRateLimit } from "@paperbits/common/ko/consts"; import { StyleHelper } from "@paperbits/styles"; import { ViewManager } from "@paperbits/common/ui"; import { Events, EventManager } from "@paperbits/common/events"; import { MediaContract } from "@paperbits/common/media"; import { BackgroundModel } from "@paperbits/common/widgets/background"; import { HyperlinkModel, IPermalinkResolver } from "@paperbits/common/permalinks"; import { ISettingsProvider } from "@paperbits/common/configuration"; import { GoogleMapsSettings } from "./googleMapsSettings"; import { ISiteService } from "@paperbits/common/sites"; import { GoogleGeocodingService } from "@paperbits/common/geocoding/googleGeocodingService"; interface MapTypeOption { label: string; value: string; } const googleMapsSettingsPath = "integration/googleMaps"; @Component({ selector: "paperbits-map-editor", template: template }) export class MapEditor { private geolocationService: GoogleGeocodingService; public readonly location: ko.Observable<string>; public readonly caption: ko.Observable<string>; public readonly zoom: ko.Observable<number>; public readonly layout: ko.Observable<string>; public readonly mapType: ko.Observable<string>; public readonly mapTypeOptions: ko.ObservableArray<MapTypeOption>; public readonly sizeConfig: ko.Observable<SizeStylePluginConfig>; public readonly background: ko.Observable<BackgroundModel>; public readonly hyperlink: ko.Observable<HyperlinkModel>; public readonly hyperlinkTitle: ko.Computed<string>; public readonly showApiKeyInput: ko.Observable<boolean>; public readonly apiKey: ko.Observable<string>; constructor( private readonly viewManager: ViewManager, private readonly eventManager: EventManager, private readonly mediaPermalinkResolver: IPermalinkResolver, private readonly popupPermalinkResolver: IPermalinkResolver, private readonly settingsProvider: ISettingsProvider, private readonly siteService: ISiteService ) { this.location = ko.observable<string>(); this.caption = ko.observable<string>(); this.zoom = ko.observable(17); this.layout = ko.observable<string>(); this.mapType = ko.observable<string>("terrain"); this.sizeConfig = ko.observable(); this.background = ko.observable(); this.hyperlink = ko.observable<HyperlinkModel>(); this.hyperlinkTitle = ko.computed<string>(() => this.hyperlink() ? this.hyperlink().title : "Attach popup..."); this.apiKey = ko.observable(); this.showApiKeyInput = ko.observable(false); this.mapTypeOptions = ko.observableArray<MapTypeOption>([ { label: "Terrain", value: "terrain" }, { label: "Satellite", value: "satellite" }, { label: "Hybrid", value: "hybrid" } ]); } @Param() public model: MapModel; @Event() public onChange: (model: MapModel) => void; @OnMounted() public async initialize(): Promise<void> { const location = typeof this.model.location === "string" ? this.model.location : this.model.location?.address; this.location(location); this.caption(this.model.caption); this.zoom(this.model.zoom); this.mapType(this.model.mapType); let googleMapsSettings = await this.settingsProvider.getSetting<GoogleMapsSettings>(googleMapsSettingsPath); if (googleMapsSettings?.apiKey) { this.showApiKeyInput(false); } else { googleMapsSettings = await this.siteService.getSetting<any>(googleMapsSettingsPath); this.apiKey(googleMapsSettings?.apiKey); this.showApiKeyInput(true); this.apiKey .extend(ChangeRateLimit) .subscribe(this.applyApiKey); } this.updateObservables(); this.eventManager.addEventListener(Events.ViewportChange, this.updateObservables); this.location .extend(ChangeRateLimit) .subscribe(this.geocodeLocation); this.caption .extend(ChangeRateLimit) .subscribe(this.applyChanges); this.zoom .extend(ChangeRateLimit) .subscribe(this.applyChanges); this.mapType .extend(ChangeRateLimit) .subscribe(this.applyChanges); } private async geocodeLocation(address: string): Promise<void> { const apiKey = this.apiKey(); if (!apiKey) { this.viewManager.addToast(`Map settings`, `Google Maps API key not specified.`); return; } try { const geolocationService = new GoogleGeocodingService(this.apiKey()); const geolocation = await geolocationService.addressToGeolocation(address); this.model.location = geolocation; this.onChange(this.model); } catch (error) { this.viewManager.notifyError(`Map widget`, error); } } private async updateObservables(): Promise<void> { const viewport = this.viewManager.getViewport(); const sizeStyles = StyleHelper.getPluginConfigForLocalStyles(this.model.styles, "size", viewport); this.sizeConfig(sizeStyles); if (this.model.marker?.sourceKey) { const background = new BackgroundModel(); background.sourceKey = this.model.marker.sourceKey; background.sourceUrl = await this.mediaPermalinkResolver.getUrlByTargetKey(this.model.marker.sourceKey); this.background(background); } if (this.model.marker?.popupKey) { const hyperlink = await this.popupPermalinkResolver.getHyperlinkByTargetKey(this.model.marker?.popupKey); this.hyperlink(hyperlink); } } private async applyApiKey(): Promise<void> { await this.siteService.setSetting(googleMapsSettingsPath, { apiKey: this.apiKey() }); this.onChange(this.model); } private applyChanges(): void { this.model.caption = this.caption(); this.model.zoom = this.zoom(); this.model.mapType = this.mapType(); this.onChange(this.model); } public onSizeChange(pluginConfig: SizeStylePluginConfig): void { const viewport = this.viewManager.getViewport(); StyleHelper.setPluginConfigForLocalStyles(this.model.styles, "size", pluginConfig, viewport); this.onChange(this.model); } public onMarkerIconChange(media: MediaContract): void { if (!media) { this.background(null); this.model.marker = null; } else { this.model.marker = new MarkerModel(media.key); const background = new BackgroundModel(); // TODO: Let's use proper model here background.sourceKey = media.key; background.sourceUrl = media.downloadUrl; background.size = "contain"; background.position = "center center"; this.background(background); } this.onChange(this.model); } public onHyperlinkChange(hyperlink: HyperlinkModel): void { this.hyperlink(hyperlink); if (!this.model.marker) { this.model.marker = new MarkerModel(); } this.model.marker.popupKey = hyperlink.targetKey; this.onChange(this.model); } }