import { AfterViewInit, ChangeDetectorRef, Component, Input, NgZone, OnInit, ViewChild } from '@angular/core'; import { NgxMoveableComponent, OnDragEnd, OnResize, OnResizeEnd } from 'ngx-moveable'; import { OnDrag } from 'ngx-moveable'; import { ConfigService } from '../_services/config.service'; import { Rect } from '../_models/generated/Rect'; import { ShortcutService } from '../_services/shortcut.service'; import { CarouselComponent } from '../carousel/carousel.component'; import { PlanService } from '../_services/plan.service'; import { Plan, PlanElement, PlanMetadata } from '../_models/plan'; import { WorldAreaService } from '../_services/world-area.service'; import { WorldArea } from '../_models/world-area'; import { Subscription, from } from 'rxjs'; import { open } from '@tauri-apps/api/dialog'; import { OverlayService, StateEvent } from '../_services/overlay.service'; import { appWindow } from '@tauri-apps/api/window'; import { EventsService } from '../_services/events.service'; import { Event } from '@tauri-apps/api/event'; @Component({ selector: 'plan-display', templateUrl: './plan-display.component.html', styleUrls: ['./plan-display.component.scss'] }) export class PlanDisplayComponent implements AfterViewInit, OnInit { @Input() backgroundColor?: String; draggable: boolean = true; rect?: Rect; bounds: any = { "left": 0, "top": 0, "right": 0, "bottom": 0, "position": "css" }; @ViewChild("moveable") moveable?: NgxMoveableComponent; slideIndex: number = 0; zoneSlides?: CarouselComponent; currentSlides?: CarouselComponent; worldAreaMap?: Map; settingsOpen: boolean = false; init: boolean = false; hasAttachedOnce: boolean = false; overlayStateChangeHandle?: Subscription; bindsAreSetup: boolean = false; nextBind?: Subscription; prevBind?: Subscription; currentPlan?: Plan; previousPlans: PlanMetadata[] = []; constructor(private events: EventsService, public configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, public planService: PlanService, public worldAreaService: WorldAreaService, public overlayService: OverlayService, private zone: NgZone) { window.addEventListener("resize", () => { this.zone.run(() => { this.windowInitHandler() }) }); this.planService.enumerateStoredPlans().subscribe(plans => { this.previousPlans = plans; }) this.planService.getCurrentPlan().subscribe(plan => { this.currentPlan = plan; setTimeout(() => this.setIndex(plan.current), 0); }) } registerOnZoneEnter() { appWindow.listen("entered", (entered) => { if (this.currentPlan) { const current = this.currentPlan.current; const length = this.currentPlan.plan.length; if (current + 1 < length) { if (entered.payload === this.currentPlan.plan[current + 1].area_key) { this.zone.run(() => this.next()); } } } }); } windowInitHandler() { if (window.innerWidth > 0) { this.ngAfterViewInit(); } } ngOnInit() { this.worldAreaService.getWorldAreas().subscribe(a => this.worldAreaMap = a); this.overlayStateChangeHandle = this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); } onOverlayStateChange(event: Event) { if (event.payload.Hidden) { this.destroyBinds(); } else { this.setupBinds(); } } destroyBinds() { if (this.bindsAreSetup) { this.nextBind?.unsubscribe(); this.prevBind?.unsubscribe(); this.bindsAreSetup = false; } } abs(v: number) { return Math.abs(v); } transform() { return `translate(${this.rect!.x}px, ${this.rect!.y}px)`; } width() { return `${this.rect!.width}px`; } height() { return `${this.rect!.height}px`; } hasWaypoint(key?: string): boolean { if (!key) { key = this.currentPlan!.plan[this.currentPlan!.current].area_key; } const world_area = this.worldAreaMap?.get(key); return world_area!.has_waypoint; } hasTrial(key?: string): boolean { if (!key) { key = this.currentPlan!.plan[this.currentPlan!.current].area_key; } return this.worldAreaService.hasTrial(key); } ngAfterViewInit(): void { if (window.innerWidth > 0) { const cfgRect = this.configService.config.initialPlanWindowPosition; this.rect = { x: cfgRect.x * window.innerWidth, y: cfgRect.y * window.innerHeight, width: cfgRect.width * window.innerWidth, height: cfgRect.height * window.innerHeight, } this.moveable?.updateRect(); setTimeout(() => this.cdr.detectChanges(), 0); this.init = true; } } onDrag(e: OnDrag) { this.rect!.x = e.translate[0]; this.rect!.y = e.translate[1]; } onDragEnd(e: OnDragEnd) { this.saveRect(); } onResize(e: OnResize) { this.rect!.width = e.width; this.rect!.height = e.height; this.onDrag(e.drag); } onResizeEnd(e: OnResizeEnd) { this.saveRect(); } saveRect() { const toCfgRect = this.rect!; this.configService.config.initialPlanWindowPosition = { x: toCfgRect.x / window.innerWidth, y: toCfgRect.y / window.innerHeight, width: toCfgRect.width / window.innerWidth, height: toCfgRect.height / window.innerHeight, } } registerZoneSlides(carousel: CarouselComponent) { this.zoneSlides = carousel; this.zoneSlides.setIndex(this.slideIndex); } setupBinds() { if (this.currentSlides && !this.bindsAreSetup) { this.nextBind = this.shortcut.register(this.configService.config.prev).subscribe((_shortcut) => this.prev()); this.prevBind = this.shortcut.register(this.configService.config.next).subscribe((_shortcut) => this.next()); this.bindsAreSetup = true; } } registerCurrentSlides(carousel: CarouselComponent) { this.currentSlides = carousel; this.currentSlides.setIndex(this.slideIndex); this.setupBinds(); } next() { if (this.overlayService.visible) { this.currentPlan!.next(); this.currentSlides?.next(); this.zoneSlides?.next(); } } prev() { if (this.overlayService.visible) { this.currentPlan!.prev(); this.currentSlides?.prev(); this.zoneSlides?.prev(); } } setIndex(index: number) { this.slideIndex = index; if (this.currentSlides) { this.currentSlides.setIndex(index); } if (this.zoneSlides) { this.zoneSlides.setIndex(index); } } loadPrevious(path: string) { this.planService.loadPlanFromPath(path, false).subscribe(plan => { this.planService.setCurrentPlan(plan); }); } settingsClick(event: any) { this.settingsOpen = !this.settingsOpen; event.stopPropagation(); } openDialog() { from(open({ multiple: false, filters: [ { name: "JSON (.json)", extensions: ['json'] } ] })).subscribe(file => { if (file) { this.planService.loadPlanFromPath(file as string).subscribe(plan => { if (plan) { this.planService.setCurrentPlan(plan); this.settingsOpen = false; } }); } }); } loadBasePlan() { this.planService.getBasePlan().subscribe(plan => { this.currentPlan = new Plan(plan); if (this.zoneSlides) { this.zoneSlides.setIndex(0); } if (this.currentSlides) { this.currentSlides.setIndex(0); } }) } onScroll(event: WheelEvent) { if (event.deltaY < 0) { this.prev(); } else { this.next(); } } specialClasses() { const waypoint = this.hasWaypoint() ? 'active' : ''; const trial = this.hasTrial() ? 'trial-active' : ''; return `${waypoint} ${trial}`; } clampedOffset(): number { return Math.min(this.configService.config.numVisible - 1, this.configService.config.offset); } zonesStyle() { return { 'min-height': `${this.configService.config.numVisible * 18}px`, 'max-height': `${this.configService.config.numVisible * 40}px` } } loadFromUrl() { this.planService.loadFromUrl().subscribe(plan => { console.log("plan", plan); if (plan) { this.planService.savePlanAtStore(plan.name!, plan).subscribe((path) => { console.log("path", path); if(path) { plan.setPath(path); } }); this.planService.setCurrentPlan(plan); } }); } }