diff --git a/app/channels/imports_channel.rb b/app/channels/imports_channel.rb new file mode 100644 index 00000000..fed59af9 --- /dev/null +++ b/app/channels/imports_channel.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ImportsChannel < ApplicationCable::Channel + def subscribed + stream_for current_user + end +end diff --git a/app/javascript/channels/imports_channel.js b/app/javascript/channels/imports_channel.js new file mode 100644 index 00000000..45749452 --- /dev/null +++ b/app/javascript/channels/imports_channel.js @@ -0,0 +1,15 @@ +import consumer from "./consumer" + +consumer.subscriptions.create("ImportsChannel", { + connected() { + console.log("Connected to the imports channel!"); + }, + + 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 + } +}); diff --git a/app/javascript/channels/index.js b/app/javascript/channels/index.js index ed929ca4..0c2237ee 100644 --- a/app/javascript/channels/index.js +++ b/app/javascript/channels/index.js @@ -1,3 +1,4 @@ // Import all the channels to be used by Action Cable import "notifications_channel" import "points_channel" +import "imports_channel" diff --git a/app/javascript/controllers/imports_controller.js b/app/javascript/controllers/imports_controller.js new file mode 100644 index 00000000..fe302ac7 --- /dev/null +++ b/app/javascript/controllers/imports_controller.js @@ -0,0 +1,51 @@ +import { Controller } from "@hotwired/stimulus"; +import consumer from "../channels/consumer"; + +export default class extends Controller { + static targets = ["index"]; + + connect() { + console.log("Imports controller connected", { + hasIndexTarget: this.hasIndexTarget, + element: this.element, + userId: this.element.dataset.userId + }); + this.setupSubscription(); + } + + setupSubscription() { + const userId = this.element.dataset.userId; + console.log("Setting up subscription with userId:", userId); + + this.channel = consumer.subscriptions.create( + { channel: "ImportsChannel" }, + { + connected: () => { + console.log("Successfully connected to ImportsChannel"); + // Test that we can receive messages + console.log("Subscription object:", this.channel); + }, + disconnected: () => { + console.log("Disconnected from ImportsChannel"); + }, + received: (data) => { + console.log("Received data:", data); + const row = this.element.querySelector(`tr[data-import-id="${data.import.id}"]`); + + if (row) { + const pointsCell = row.querySelector('[data-points-count]'); + if (pointsCell) { + pointsCell.textContent = new Intl.NumberFormat().format(data.import.points_count); + } + } + } + } + ); + } + + disconnect() { + if (this.channel) { + this.channel.unsubscribe(); + } + } +} diff --git a/app/services/geojson/import_parser.rb b/app/services/geojson/import_parser.rb index ba3d333f..ff78e6f6 100644 --- a/app/services/geojson/import_parser.rb +++ b/app/services/geojson/import_parser.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Geojson::ImportParser + include Imports::Broadcaster + attr_reader :import, :json, :user_id def initialize(import, user_id) @@ -12,10 +14,12 @@ class Geojson::ImportParser def call data = Geojson::Params.new(json).call - data.each do |point| + data.each.with_index(1) do |point, index| next if point_exists?(point, user_id) Point.create!(point.merge(user_id:, import_id: import.id)) + + broadcast_import_progress(import, index) end end diff --git a/app/services/gpx/track_parser.rb b/app/services/gpx/track_parser.rb index 3fde5a4b..27cf1ae2 100644 --- a/app/services/gpx/track_parser.rb +++ b/app/services/gpx/track_parser.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Gpx::TrackParser + include Imports::Broadcaster + attr_reader :import, :json, :user_id def initialize(import, user_id) @@ -13,7 +15,9 @@ class Gpx::TrackParser tracks = json['gpx']['trk'] tracks_arr = tracks.is_a?(Array) ? tracks : [tracks] - tracks_arr.map { parse_track(_1) }.flatten + tracks_arr.map { parse_track(_1) }.flatten.each.with_index(1) do |point, index| + create_point(point, index) + end end private @@ -22,12 +26,10 @@ class Gpx::TrackParser segments = track['trkseg'] segments_array = segments.is_a?(Array) ? segments : [segments] - segments_array.map do |segment| - segment['trkpt'].each { create_point(_1) } - end + segments_array.map { |segment| segment['trkpt'] } end - def create_point(point) + def create_point(point, index) return if point['lat'].blank? || point['lon'].blank? || point['time'].blank? return if point_exists?(point) @@ -40,6 +42,8 @@ class Gpx::TrackParser raw_data: point, user_id: ) + + broadcast_import_progress(import, index) end def point_exists?(point) diff --git a/app/services/imports/broadcaster.rb b/app/services/imports/broadcaster.rb new file mode 100644 index 00000000..1c7f54bb --- /dev/null +++ b/app/services/imports/broadcaster.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Imports::Broadcaster + def broadcast_import_progress(import, index) + ImportsChannel.broadcast_to( + import.user, + { + action: 'update', + import: { + id: import.id, + points_count: index + } + } + ) + end +end diff --git a/app/views/imports/index.html.erb b/app/views/imports/index.html.erb index d74c6182..134a8f26 100644 --- a/app/views/imports/index.html.erb +++ b/app/views/imports/index.html.erb @@ -39,18 +39,23 @@