Skip to content

Commit

Permalink
Use separate ActionCable server to prevent disconnects (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillplatonov authored Dec 21, 2024
1 parent 00578f0 commit 8adfd0d
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 68 deletions.
101 changes: 47 additions & 54 deletions app/assets/javascripts/hotwire-livereload.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,18 +389,18 @@
this.subscriptions = subscriptions;
this.pendingSubscriptions = [];
}
guarantee(subscription2) {
if (this.pendingSubscriptions.indexOf(subscription2) == -1) {
logger.log(`SubscriptionGuarantor guaranteeing ${subscription2.identifier}`);
this.pendingSubscriptions.push(subscription2);
guarantee(subscription) {
if (this.pendingSubscriptions.indexOf(subscription) == -1) {
logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
this.pendingSubscriptions.push(subscription);
} else {
logger.log(`SubscriptionGuarantor already guaranteeing ${subscription2.identifier}`);
logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
}
this.startGuaranteeing();
}
forget(subscription2) {
logger.log(`SubscriptionGuarantor forgetting ${subscription2.identifier}`);
this.pendingSubscriptions = this.pendingSubscriptions.filter((s) => s !== subscription2);
forget(subscription) {
logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
this.pendingSubscriptions = this.pendingSubscriptions.filter((s) => s !== subscription);
}
startGuaranteeing() {
this.stopGuaranteeing();
Expand All @@ -412,9 +412,9 @@
retrySubscribing() {
this.retryTimeout = setTimeout(() => {
if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
this.pendingSubscriptions.map((subscription2) => {
logger.log(`SubscriptionGuarantor resubscribing ${subscription2.identifier}`);
this.subscriptions.subscribe(subscription2);
this.pendingSubscriptions.map((subscription) => {
logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
this.subscriptions.subscribe(subscription);
});
}
}, 500);
Expand All @@ -431,64 +431,64 @@
const params = typeof channel === "object" ? channel : {
channel
};
const subscription2 = new Subscription(this.consumer, params, mixin);
return this.add(subscription2);
const subscription = new Subscription(this.consumer, params, mixin);
return this.add(subscription);
}
add(subscription2) {
this.subscriptions.push(subscription2);
add(subscription) {
this.subscriptions.push(subscription);
this.consumer.ensureActiveConnection();
this.notify(subscription2, "initialized");
this.subscribe(subscription2);
return subscription2;
}
remove(subscription2) {
this.forget(subscription2);
if (!this.findAll(subscription2.identifier).length) {
this.sendCommand(subscription2, "unsubscribe");
this.notify(subscription, "initialized");
this.subscribe(subscription);
return subscription;
}
remove(subscription) {
this.forget(subscription);
if (!this.findAll(subscription.identifier).length) {
this.sendCommand(subscription, "unsubscribe");
}
return subscription2;
return subscription;
}
reject(identifier) {
return this.findAll(identifier).map((subscription2) => {
this.forget(subscription2);
this.notify(subscription2, "rejected");
return subscription2;
return this.findAll(identifier).map((subscription) => {
this.forget(subscription);
this.notify(subscription, "rejected");
return subscription;
});
}
forget(subscription2) {
this.guarantor.forget(subscription2);
this.subscriptions = this.subscriptions.filter((s) => s !== subscription2);
return subscription2;
forget(subscription) {
this.guarantor.forget(subscription);
this.subscriptions = this.subscriptions.filter((s) => s !== subscription);
return subscription;
}
findAll(identifier) {
return this.subscriptions.filter((s) => s.identifier === identifier);
}
reload() {
return this.subscriptions.map((subscription2) => this.subscribe(subscription2));
return this.subscriptions.map((subscription) => this.subscribe(subscription));
}
notifyAll(callbackName, ...args) {
return this.subscriptions.map((subscription2) => this.notify(subscription2, callbackName, ...args));
return this.subscriptions.map((subscription) => this.notify(subscription, callbackName, ...args));
}
notify(subscription2, callbackName, ...args) {
notify(subscription, callbackName, ...args) {
let subscriptions;
if (typeof subscription2 === "string") {
subscriptions = this.findAll(subscription2);
if (typeof subscription === "string") {
subscriptions = this.findAll(subscription);
} else {
subscriptions = [subscription2];
subscriptions = [subscription];
}
return subscriptions.map((subscription3) => typeof subscription3[callbackName] === "function" ? subscription3[callbackName](...args) : void 0);
return subscriptions.map((subscription2) => typeof subscription2[callbackName] === "function" ? subscription2[callbackName](...args) : void 0);
}
subscribe(subscription2) {
if (this.sendCommand(subscription2, "subscribe")) {
this.guarantor.guarantee(subscription2);
subscribe(subscription) {
if (this.sendCommand(subscription, "subscribe")) {
this.guarantor.guarantee(subscription);
}
}
confirmSubscription(identifier) {
logger.log(`Subscription confirmed ${identifier}`);
this.findAll(identifier).map((subscription2) => this.guarantor.forget(subscription2));
this.findAll(identifier).map((subscription) => this.guarantor.forget(subscription));
}
sendCommand(subscription2, command) {
const { identifier } = subscription2;
sendCommand(subscription, command) {
const { identifier } = subscription;
return this.consumer.send({
command,
identifier
Expand Down Expand Up @@ -587,9 +587,8 @@
}, 300);

// app/javascript/hotwire-livereload.js
var consumer = createConsumer();
var subscription = null;
var createSubscription = () => consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
var consumer = createConsumer("/hotwire-livereload");
consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
received: hotwire_livereload_received_default,
connected() {
console.log("[Hotwire::Livereload] Websocket connected");
Expand All @@ -598,14 +597,8 @@
console.log("[Hotwire::Livereload] Websocket disconnected");
}
});
subscription = createSubscription();
document.addEventListener("turbo:load", () => {
hotwire_livereload_scroll_position_default.restore();
hotwire_livereload_scroll_position_default.remove();
if (subscription) {
consumer.subscriptions.remove(subscription);
subscription = null;
}
subscription = createSubscription();
});
})();
15 changes: 2 additions & 13 deletions app/javascript/hotwire-livereload.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { createConsumer } from "@rails/actioncable"
import received from "./lib/hotwire-livereload-received"
import scrollPosition from "./lib/hotwire-livereload-scroll-position"

const consumer = createConsumer()
let subscription = null

const createSubscription = () => consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
const consumer = createConsumer("/hotwire-livereload")
consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
received,

connected() {
Expand All @@ -17,16 +15,7 @@ const createSubscription = () => consumer.subscriptions.create("Hotwire::Liverel
},
})

subscription = createSubscription()

document.addEventListener("turbo:load", () => {
scrollPosition.restore()
scrollPosition.remove()

if (subscription) {
consumer.subscriptions.remove(subscription)
subscription = null
}
subscription = createSubscription()
})

2 changes: 2 additions & 0 deletions lib/hotwire/livereload.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "action_cable/server/base"
require "hotwire/livereload/cable_server"
require "hotwire/livereload/engine"

module Hotwire
Expand Down
11 changes: 11 additions & 0 deletions lib/hotwire/livereload/cable_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Hotwire
module Livereload
class CableServer < ActionCable::Server::Base
def initialize
config = ::ActionCable::Server::Base.config.dup
config.connection_class = -> { ::ActionCable::Connection::Base }
super(config: config)
end
end
end
end
14 changes: 13 additions & 1 deletion lib/hotwire/livereload/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class Engine < ::Rails::Engine
config.hotwire_livereload.listen_options ||= {}
config.hotwire_livereload.debounce_delay_ms = 0

initializer "hotwire_livereload.routes" do
config.after_initialize do |app|
app.routes.prepend do
mount Hotwire::Livereload.cable_server => "/hotwire-livereload", :internal => true, :anchor => true
end
end
end

initializer "hotwire_livereload.assets" do
if Rails.application.config.respond_to?(:assets)
Rails.application.config.assets.precompile += %w[hotwire-livereload.js hotwire-livereload-turbo-stream.js]
Expand Down Expand Up @@ -109,8 +117,12 @@ def self.turbo_stream(locals)
)
end

def self.cable_server
@cable_server ||= Hotwire::Livereload::CableServer.new
end

def self.action_cable(opts)
ActionCable.server.broadcast("hotwire-reload", opts)
cable_server.broadcast("hotwire-reload", opts)
end

def self.server_process?
Expand Down

0 comments on commit 8adfd0d

Please sign in to comment.