mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Implement live map updates with WebSockets
This commit is contained in:
parent
bec9db1198
commit
9c99a835de
9 changed files with 137 additions and 6 deletions
7
app/channels/points_channel.rb
Normal file
7
app/channels/points_channel.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PointsChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_for current_user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
// Import all the channels to be used by Action Cable
|
// Import all the channels to be used by Action Cable
|
||||||
import "notifications_channel"
|
import "notifications_channel"
|
||||||
|
import "points_channel"
|
||||||
|
|
|
||||||
15
app/javascript/channels/points_channel.js
Normal file
15
app/javascript/channels/points_channel.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import consumer from "./consumer"
|
||||||
|
|
||||||
|
consumer.subscriptions.create("PointsChannel", {
|
||||||
|
connected() {
|
||||||
|
// Called when the subscription is ready for use on the server
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnected() {
|
||||||
|
// Called when the subscription has been terminated by the server
|
||||||
|
},
|
||||||
|
|
||||||
|
received(data) {
|
||||||
|
// Called when there's incoming data on the websocket for this channel
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Controller } from "@hotwired/stimulus";
|
import { Controller } from "@hotwired/stimulus";
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
import "leaflet.heat";
|
import "leaflet.heat";
|
||||||
|
import consumer from "../channels/consumer"; // Add this import
|
||||||
|
|
||||||
import { createMarkersArray } from "../maps/markers";
|
import { createMarkersArray } from "../maps/markers";
|
||||||
|
|
||||||
|
|
@ -138,12 +139,92 @@ export default class extends Controller {
|
||||||
this.map.removeControl(this.drawControl);
|
this.map.removeControl(this.drawControl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setupSubscription(); // Add this line
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.map.remove();
|
this.map.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupSubscription() {
|
||||||
|
consumer.subscriptions.create("PointsChannel", {
|
||||||
|
received: (data) => {
|
||||||
|
this.appendPoint(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPoint(data) {
|
||||||
|
// Parse the received point data
|
||||||
|
console.log(data)
|
||||||
|
const newPoint = data;
|
||||||
|
|
||||||
|
// Add the new point to the markers array
|
||||||
|
this.markers.push(newPoint);
|
||||||
|
|
||||||
|
// Create a new marker for the point
|
||||||
|
const markerOptions = {
|
||||||
|
...this.userSettings, // Pass any relevant settings
|
||||||
|
id: newPoint[6], // Assuming index 6 contains the point ID
|
||||||
|
timestamp: newPoint[4] // Assuming index 2 contains the timestamp
|
||||||
|
};
|
||||||
|
|
||||||
|
const newMarker = this.createMarker(newPoint, markerOptions);
|
||||||
|
this.markersArray.push(newMarker);
|
||||||
|
|
||||||
|
// Update the markers layer
|
||||||
|
this.markersLayer.clearLayers();
|
||||||
|
this.markersLayer.addLayer(L.layerGroup(this.markersArray));
|
||||||
|
|
||||||
|
// Update heatmap
|
||||||
|
this.heatmapMarkers.push([newPoint[0], newPoint[1], 0.2]);
|
||||||
|
this.heatmapLayer.setLatLngs(this.heatmapMarkers);
|
||||||
|
|
||||||
|
// Update polylines
|
||||||
|
this.polylinesLayer.clearLayers();
|
||||||
|
this.polylinesLayer = createPolylinesLayer(
|
||||||
|
this.markers,
|
||||||
|
this.map,
|
||||||
|
this.timezone,
|
||||||
|
this.routeOpacity,
|
||||||
|
this.userSettings
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pan map to new location
|
||||||
|
this.map.setView([newPoint[0], newPoint[1]], 14);
|
||||||
|
|
||||||
|
// Update fog of war if enabled
|
||||||
|
if (this.map.hasLayer(this.fogOverlay)) {
|
||||||
|
this.updateFog(this.markers, this.clearFogRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the last marker
|
||||||
|
this.map.eachLayer((layer) => {
|
||||||
|
if (layer instanceof L.Marker && !layer._popup) {
|
||||||
|
this.map.removeLayer(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.addLastMarker(this.map, this.markers);
|
||||||
|
}
|
||||||
|
|
||||||
|
createMarker(point, options) {
|
||||||
|
const marker = L.marker([point[0], point[1]]);
|
||||||
|
|
||||||
|
// Add popup content based on point data
|
||||||
|
const popupContent = `
|
||||||
|
<div>
|
||||||
|
<p>Time: ${new Date(point[2]).toLocaleString()}</p>
|
||||||
|
${point[3] ? `<p>Address: ${point[3]}</p>` : ''}
|
||||||
|
${point[7] ? `<p>Country: ${point[7]}</p>` : ''}
|
||||||
|
<a href="#" class="delete-point" data-id="${point[6]}">Delete</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
marker.bindPopup(popupContent);
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
async setupScratchLayer(countryCodesMap) {
|
async setupScratchLayer(countryCodesMap) {
|
||||||
this.scratchLayer = L.geoJSON(null, {
|
this.scratchLayer = L.geoJSON(null, {
|
||||||
style: {
|
style: {
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@ class Notification < ApplicationRecord
|
||||||
NotificationsChannel.broadcast_to(
|
NotificationsChannel.broadcast_to(
|
||||||
user,
|
user,
|
||||||
{
|
{
|
||||||
id: id,
|
# id: id,
|
||||||
title: title,
|
# title: title,
|
||||||
content: content,
|
# content: content,
|
||||||
kind: kind,
|
# kind: kind,
|
||||||
timestamp: Time.current.to_i
|
# timestamp: Time.current.to_i
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class Point < ApplicationRecord
|
||||||
scope :not_visited, -> { where(visit_id: nil) }
|
scope :not_visited, -> { where(visit_id: nil) }
|
||||||
|
|
||||||
after_create :async_reverse_geocode
|
after_create :async_reverse_geocode
|
||||||
|
after_create_commit :broadcast_coordinates
|
||||||
|
|
||||||
def self.without_raw_data
|
def self.without_raw_data
|
||||||
select(column_names - ['raw_data'])
|
select(column_names - ['raw_data'])
|
||||||
|
|
@ -37,4 +38,22 @@ class Point < ApplicationRecord
|
||||||
|
|
||||||
ReverseGeocodingJob.perform_later(self.class.to_s, id)
|
ReverseGeocodingJob.perform_later(self.class.to_s, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def broadcast_coordinates
|
||||||
|
PointsChannel.broadcast_to(
|
||||||
|
user,
|
||||||
|
[
|
||||||
|
latitude.to_f,
|
||||||
|
longitude.to_f,
|
||||||
|
battery.to_s,
|
||||||
|
altitude.to_s,
|
||||||
|
timestamp.to_s,
|
||||||
|
velocity.to_s,
|
||||||
|
id.to_s,
|
||||||
|
country.to_s
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,10 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
id='map'
|
||||||
class="w-full"
|
class="w-full"
|
||||||
data-controller="maps"
|
data-controller="maps points"
|
||||||
|
data-points-target="map"
|
||||||
data-distance_unit="<%= DISTANCE_UNIT %>"
|
data-distance_unit="<%= DISTANCE_UNIT %>"
|
||||||
data-api_key="<%= current_user.api_key %>"
|
data-api_key="<%= current_user.api_key %>"
|
||||||
data-user_settings=<%= current_user.settings.to_json %>
|
data-user_settings=<%= current_user.settings.to_json %>
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,4 @@ pin 'leaflet-draw' # @1.0.4
|
||||||
pin '@rails/actioncable', to: 'actioncable.esm.js'
|
pin '@rails/actioncable', to: 'actioncable.esm.js'
|
||||||
pin_all_from 'app/javascript/channels', under: 'channels'
|
pin_all_from 'app/javascript/channels', under: 'channels'
|
||||||
pin 'notifications_channel', to: 'channels/notifications_channel.js'
|
pin 'notifications_channel', to: 'channels/notifications_channel.js'
|
||||||
|
pin 'points_channel', to: 'channels/points_channel.js'
|
||||||
|
|
|
||||||
5
spec/channels/points_channel_spec.rb
Normal file
5
spec/channels/points_channel_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PointsChannel, type: :channel do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue