164 lines
5.5 KiB
TypeScript
164 lines
5.5 KiB
TypeScript
import {Injectable} from "@angular/core";
|
|
import {AngularFirestore, AngularFirestoreDocument} from "@angular/fire/firestore";
|
|
import {BehaviorSubject, combineLatest, Subscription} from "rxjs";
|
|
import {Circle, MapData, MapSymbol, Marker, Measurement, Polygon, Polyline, Rectangle} from "../models/mapSymbol";
|
|
import * as _ from 'lodash';
|
|
import {map} from "rxjs/operators";
|
|
|
|
export const LOCATION_COLLECTION = 'Users';
|
|
export const MAP_COLLECTION = 'Maps';
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class SyncService {
|
|
private
|
|
|
|
private location;
|
|
private locationChanged = false;
|
|
private locationDoc: AngularFirestoreDocument;
|
|
private mapDoc: AngularFirestoreDocument;
|
|
private mapChanged = false;
|
|
private mapSub: Subscription;
|
|
private saveInterval: number;
|
|
|
|
mapData = new BehaviorSubject<MapData>({});
|
|
|
|
constructor(private db: AngularFirestore) { }
|
|
|
|
private addMapSymbol(s: MapSymbol, key: string) {
|
|
s.new = true;
|
|
let map = this.mapData.value;
|
|
if(!map[key]) map[key] = [];
|
|
map[key].push(s);
|
|
this.mapData.next(map);
|
|
this.mapChanged = true;
|
|
}
|
|
|
|
async exists(mapCode: string) {
|
|
return (await this.db.collection(MAP_COLLECTION).doc(mapCode).ref.get()).exists;
|
|
}
|
|
|
|
addCircle(circle: Circle) {
|
|
this.addMapSymbol(circle, 'circles');
|
|
}
|
|
|
|
addMarker(marker: Marker) {
|
|
this.addMapSymbol(marker, 'markers');
|
|
}
|
|
|
|
addMeasurement(measurement: Measurement) {
|
|
this.addMapSymbol(measurement, 'measurements');
|
|
}
|
|
|
|
addMyLocation(location: Marker) {
|
|
let markForSave = this.location == null;
|
|
if(!this.locationChanged) this.locationChanged = !_.isEqual(this.location, location);
|
|
if(this.locationChanged) this.location = location;
|
|
if(markForSave) this.save(true);
|
|
}
|
|
|
|
addPolygon(polygon: Polygon) {
|
|
this.addMapSymbol(polygon, 'polygons');
|
|
}
|
|
|
|
addPolyline(polyline: Polyline) {
|
|
this.addMapSymbol(polyline, 'polylines')
|
|
}
|
|
|
|
addRectangle(rect: Rectangle) {
|
|
this.addMapSymbol(rect, 'rectangles')
|
|
}
|
|
|
|
delete(...symbols) {
|
|
let map = this.mapData.value;
|
|
symbols.forEach(s => {
|
|
if(map.circles) map.circles = map.circles.filter(r => !_.isEqual(s, r));
|
|
if(map.markers) map.markers = map.markers.filter(r => !_.isEqual(s, r));
|
|
if(map.measurements) map.measurements = map.measurements.filter(r => !_.isEqual(s, r));
|
|
if(map.polygons) map.polygons = map.polygons.filter(r => !_.isEqual(s, r));
|
|
if(map.polylines) map.polylines = map.polylines.filter(r => !_.isEqual(s, r));
|
|
if(map.rectangles) map.rectangles = map.rectangles.filter(r => !_.isEqual(s, r));
|
|
});
|
|
this.mapData.next(map);
|
|
this.mapChanged = true;
|
|
}
|
|
|
|
load(mapCode: string, username: string) {
|
|
this.unload();
|
|
this.mapDoc = this.db.collection(MAP_COLLECTION).doc(mapCode);
|
|
this.locationDoc = this.mapDoc.collection(LOCATION_COLLECTION).doc(username);
|
|
|
|
this.mapSub = combineLatest(this.mapDoc.valueChanges(), this.mapDoc.collection(LOCATION_COLLECTION).snapshotChanges())
|
|
.pipe(map(data => {
|
|
let newMap = data[0];
|
|
let oldMap = this.mapData.value;
|
|
let mergedMap = this.mergeMaps(newMap, oldMap);
|
|
|
|
let locations = data[1].map(doc => ({id: doc.payload.doc.id, data: <Marker>doc.payload.doc.data()}));
|
|
locations.filter(l => l.id != username).forEach(l => {
|
|
if(!mergedMap.locations) mergedMap.locations = {};
|
|
mergedMap.locations[l.id] = l.data;
|
|
});
|
|
|
|
return mergedMap;
|
|
})).subscribe((mapData: MapData) => {
|
|
this.mapData.next(mapData);
|
|
if(this.saveInterval) clearInterval(this.saveInterval);
|
|
this.saveInterval = setInterval(() => this.save(), (mapData.locations && Object.keys(mapData.locations).length > 0) ? 5_000 : 30_000)
|
|
});
|
|
}
|
|
|
|
mergeMaps(newMap: MapData, oldMap: MapData) {
|
|
let map = Object.assign({}, newMap);
|
|
Object.keys(oldMap).forEach(key => {
|
|
if(Array.isArray(map[key])) {
|
|
if(!map[key]) map[key] = [];
|
|
oldMap[key].filter(s => !_.find(map[key], s) && s.new).forEach(s => map[key].push(s));
|
|
}
|
|
});
|
|
if(!map.locations) map.locations = {};
|
|
return map;
|
|
}
|
|
|
|
removeMyLocation() {
|
|
this.location = null;
|
|
return this.locationDoc.delete();
|
|
}
|
|
|
|
save(locationOnly?) {
|
|
if(this.locationDoc && this.locationChanged) {
|
|
let ignore = this.locationDoc.set(this.location);
|
|
}
|
|
|
|
if(!locationOnly && this.mapDoc && this.mapChanged) {
|
|
let map = this.mapData.value;
|
|
Object.values(map).filter(val => Array.isArray(val)).forEach(val => val.filter(s => s.new).forEach(s => delete s.new));
|
|
delete map.locations;
|
|
let ignore = this.mapDoc.set(map);
|
|
this.mapChanged = false;
|
|
}
|
|
}
|
|
|
|
async unload() {
|
|
if(this.saveInterval) clearInterval(this.saveInterval);
|
|
this.mapData.next({});
|
|
|
|
if(this.mapSub) {
|
|
this.mapSub.unsubscribe();
|
|
this.mapSub = null;
|
|
}
|
|
|
|
if(this.mapDoc) {
|
|
this.mapDoc = null;
|
|
this.mapChanged = false;
|
|
}
|
|
|
|
if(this.locationDoc) {
|
|
this.locationChanged = false;
|
|
await this.removeMyLocation();
|
|
this.locationDoc = null;
|
|
}
|
|
}
|
|
}
|