mirror of
https://github.com/owntracks/frontend.git
synced 2026-05-05 03:06:32 +00:00
219 lines
7.7 KiB
JavaScript
219 lines
7.7 KiB
JavaScript
(() => {
|
|
const { LMap, LTileLayer, LMarker, LCircleMarker, LCircle, LPolyline } = Vue2Leaflet;
|
|
const config = deepmerge(window.defaultConfig, window.config);
|
|
new Vue({
|
|
el: '#app',
|
|
components: { vuejsDatepicker, LMap, LTileLayer, LMarker, LCircleMarker, LPolyline, LCircle },
|
|
data: {
|
|
users: [],
|
|
devices: {},
|
|
lastLocations: [],
|
|
locationHistory: {},
|
|
showLastLocations: true,
|
|
showLocationHistoryPoints: false,
|
|
showLocationHistoryLine: false,
|
|
showLocationHeatmap: false,
|
|
selectedUser: '',
|
|
selectedDevice: '',
|
|
startDate: config.startDate,
|
|
endDate: config.endDate,
|
|
showDownloadModal: false,
|
|
showInformationModal: false,
|
|
map: {
|
|
center: config.map.center,
|
|
zoom: config.map.zoom,
|
|
maxNativeZoom: config.map.maxNativeZoom,
|
|
maxZoom: config.map.maxZoom,
|
|
url: config.map.url,
|
|
attribution: config.map.attribution,
|
|
polyline: {
|
|
color: config.accentColor,
|
|
fillColor: 'transparent',
|
|
},
|
|
circle: {
|
|
color: config.accentColor,
|
|
fillColor: config.accentColor,
|
|
fillOpacity: 0.2,
|
|
},
|
|
circleMarker: {
|
|
radius: 4,
|
|
color: config.accentColor,
|
|
fillColor: '#fff',
|
|
fillOpacity: 1,
|
|
},
|
|
heatmap: {
|
|
max: config.map.heatmap.max,
|
|
radius: config.map.heatmap.radius,
|
|
blur: config.map.heatmap.radius,
|
|
gradient: config.map.heatmap.gradient,
|
|
},
|
|
},
|
|
information: {
|
|
ownTracks: {
|
|
version: '',
|
|
documentationUrl: 'https://owntracks.org/booklet/',
|
|
sourceCodeUrl: 'https://github.com/owntracks/recorder',
|
|
twitterUrl: 'https://twitter.com/OwnTracks',
|
|
},
|
|
ownTracksUi: {
|
|
sourceCodeUrl: 'https://github.com/linusg/owntracks-ui',
|
|
},
|
|
}
|
|
},
|
|
watch: {
|
|
selectedUser: async function () {
|
|
this.selectedDevice = '';
|
|
this.lastLocations = await this.getLastLocations();
|
|
this.locationHistory = await this.getLocationHistory();
|
|
},
|
|
selectedDevice: async function () {
|
|
this.lastLocations = await this.getLastLocations();
|
|
this.locationHistory = await this.getLocationHistory();
|
|
},
|
|
startDate: async function () {
|
|
this.locationHistory = await this.getLocationHistory();
|
|
},
|
|
endDate: async function () {
|
|
this.locationHistory = await this.getLocationHistory();
|
|
},
|
|
},
|
|
computed: {
|
|
locationHistoryLatLngs() {
|
|
const latLngs = [];
|
|
Object.keys(this.locationHistory).forEach((user) => {
|
|
Object.keys(this.locationHistory[user]).forEach((device) => {
|
|
this.locationHistory[user][device].forEach((l) => {
|
|
latLngs.push(L.latLng(l.lat, l.lon));
|
|
});
|
|
});
|
|
});
|
|
return latLngs;
|
|
},
|
|
startDateDisabledDates() {
|
|
return {
|
|
customPredictor: (date) => (date > this.endDate) || (date > new Date())
|
|
};
|
|
},
|
|
endDateDisabledDates() {
|
|
return {
|
|
customPredictor: (date) => (date < this.startDate) || (date > new Date())
|
|
};
|
|
},
|
|
},
|
|
methods: {
|
|
init: async function () {
|
|
const root = document.documentElement;
|
|
root.style.setProperty('--color-accent', config.accentColor);
|
|
this.users = await this.getUsers();
|
|
this.devices = await this.getDevices();
|
|
this.lastLocations = await this.getLastLocations();
|
|
this.locationHistory = await this.getLocationHistory();
|
|
this.centerView();
|
|
await this.connectWebsocket();
|
|
this.information.ownTracks.version = await this.getVersion();
|
|
},
|
|
connectWebsocket: async function () {
|
|
const wsUrl = `${document.location.protocol.replace('http', 'ws')}//${document.location.host}/ws/last`;
|
|
const ws = new WebSocket(wsUrl);
|
|
console.log(`[WS] Connecting to ${wsUrl}...`);
|
|
ws.onopen = (e) => {
|
|
console.log('[WS] Connected');
|
|
ws.send('LAST');
|
|
};
|
|
ws.onclose = () => {
|
|
console.log('[WS] Disconnected. Reconnecting in one second...')
|
|
setTimeout(this.connectWebsocket, 1000);
|
|
};
|
|
ws.onmessage = async (msg) => {
|
|
if (msg.data) {
|
|
try {
|
|
const data = JSON.parse(msg.data);
|
|
if (data._type === 'location') {
|
|
console.log('[WS] Location update received');
|
|
this.lastLocations = await this.getLastLocations();
|
|
this.locationHistory = await this.getLocationHistory();
|
|
}
|
|
} catch (err) {}
|
|
} else {
|
|
console.log('[WS] Ping');
|
|
}
|
|
};
|
|
},
|
|
getVersion: async function () {
|
|
const response = await fetch('/api/0/version');
|
|
const json = await response.json();
|
|
const version = json.version;
|
|
return version;
|
|
},
|
|
getUsers: async function () {
|
|
const response = await fetch('/api/0/list');
|
|
const json = await response.json();
|
|
const users = json.results;
|
|
return users;
|
|
},
|
|
getDevices: async function () {
|
|
const devices = {};
|
|
await Promise.all(this.users.map(async (user) => {
|
|
const response = await fetch(`/api/0/list?user=${user}`);
|
|
const json = await response.json();
|
|
const userDevices = json.results;
|
|
devices[user] = userDevices;
|
|
}));
|
|
return devices;
|
|
},
|
|
getLastLocations: async function () {
|
|
let url = '/api/0/last';
|
|
if (this.selectedUser !== '') {
|
|
url += `?&user=${this.selectedUser}`;
|
|
if (this.selectedDevice !== '') {
|
|
url += `&device=${this.selectedDevice}`;
|
|
}
|
|
}
|
|
const response = await fetch(url);
|
|
const json = await response.json();
|
|
return json;
|
|
},
|
|
getLocationHistory: async function () {
|
|
let users;
|
|
let devices;
|
|
if (this.selectedUser === '') {
|
|
users = this.users;
|
|
devices = { ...this.devices };
|
|
} else {
|
|
users = [this.selectedUser];
|
|
if (this.selectedDevice === '') {
|
|
devices = { [this.selectedUser]: this.devices[this.selectedUser] };
|
|
} else {
|
|
devices = { [this.selectedUser]: [this.selectedDevice] };
|
|
}
|
|
}
|
|
const locations = {};
|
|
await Promise.all(users.map(async (user) => {
|
|
locations[user] = {};
|
|
await Promise.all(devices[user].map(async (device) => {
|
|
const startDateString = `${this.startDate.toISOString().split('T')[0]}T00:00:00`;
|
|
const endDateString = `${this.endDate.toISOString().split('T')[0]}T23:59:59`;
|
|
const url = `/api/0/locations?from=${startDateString}&to=${endDateString}&format=json&user=${user}&device=${device}`;
|
|
const response = await fetch(url);
|
|
const json = await response.json();
|
|
const userDeviceLocations = json.data;
|
|
locations[user][device] = userDeviceLocations;
|
|
}));
|
|
}));
|
|
return locations;
|
|
},
|
|
centerView() {
|
|
if ((this.showLocationHistoryPoints || this.showLocationHistoryLine || this.showLocationHeatmap) && this.locationHistoryLatLngs.length > 0) {
|
|
this.$refs.map.mapObject.fitBounds(new L.LatLngBounds(this.locationHistoryLatLngs));
|
|
} else if (this.showLastLocations && this.lastLocations.length > 0) {
|
|
const locations = this.lastLocations.map((l) => L.latLng(l.lat, l.lon));
|
|
this.$refs.map.mapObject.fitBounds(new L.LatLngBounds(locations), {maxZoom: this.map.maxNativeZoom});
|
|
}
|
|
},
|
|
},
|
|
mounted() {
|
|
this.init();
|
|
},
|
|
});
|
|
})();
|