map-alliance/src/app/services/sync.service.ts
2019-09-01 15:46:20 -04:00

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;
}
}
}