From 5ff9620de286b62552766c7c41f2481c14806eef Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Fri, 23 Jul 2021 14:31:40 +0300 Subject: [PATCH] Migrate to AnyCable JS client --- .gitignore | 1 + dip.yml | 1 + docker-compose.yml | 2 + frontend/controllers/chat_controller.js | 27 +++++++----- frontend/controllers/connection_controller.js | 43 +++++-------------- frontend/controllers/list_controller.js | 20 ++++----- frontend/controllers/workspace_controller.js | 20 ++++----- frontend/utils/cable.js | 17 ++------ package.json | 6 ++- yarn.lock | 24 ++++++++--- 10 files changed, 77 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index 777a4ea..079330f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ /yarn-error.log yarn-debug.log* .yarn-integrity +dip.override.yml diff --git a/dip.yml b/dip.yml index 38532c3..546636b 100644 --- a/dip.yml +++ b/dip.yml @@ -2,6 +2,7 @@ version: '5.0' environment: RAILS_ENV: development + ANYCABLE_CLIENT_PATH: "./vendor/anycable-client" # use `dip.override.yml` to change the location compose: files: diff --git a/docker-compose.yml b/docker-compose.yml index d2211f5..8d31b9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ x-backend: &backend tty: true volumes: - .:/app:cached + - ${ANYCABLE_CLIENT_PATH}:/app/vendor/anycable-client - rails_cache:/app/tmp/cache - bundle:/usr/local/bundle - node_modules:/app/node_modules @@ -157,6 +158,7 @@ services: - 3036 volumes: - .:/app:cached + - ${ANYCABLE_CLIENT_PATH}:/app/vendor/anycable-client - bundle:/usr/local/bundle - node_modules:/app/node_modules - packs:/app/public/packs diff --git a/frontend/controllers/chat_controller.js b/frontend/controllers/chat_controller.js index 17e5845..3665553 100644 --- a/frontend/controllers/chat_controller.js +++ b/frontend/controllers/chat_controller.js @@ -1,24 +1,29 @@ import { Controller } from "stimulus"; -import { createChannel } from "../utils/cable"; +import cable from "../utils/cable"; +import { Channel } from "@anycable/web"; import { currentUser } from "../utils/current_user"; import { isPreview as isTurboPreview } from '../utils/turbo'; +class ChatChannel extends Channel { + static identifier = "ChatChannel"; + + speak(message) { + this.perform("speak", { message }); + } +} + export default class extends Controller { static targets = ["input", "messages", "placeholder"]; connect() { if (isTurboPreview()) return; - const channel = "ChatChannel"; const id = this.data.get("id"); - this.channel = createChannel( - {channel, id}, - { - received: (data) => { - this.handleMessage(data); - }, - } - ); + + this.channel = new ChatChannel({ id }); + this.channel.on("message", (data) => this.handleMessage(data)); + + cable.subscribe(this.channel); } disconnect() { @@ -61,6 +66,6 @@ export default class extends Controller { if (!message) return; - this.channel.perform("speak", {message}); + this.channel.speak(message); } } diff --git a/frontend/controllers/connection_controller.js b/frontend/controllers/connection_controller.js index 63ac863..18a0bc1 100644 --- a/frontend/controllers/connection_controller.js +++ b/frontend/controllers/connection_controller.js @@ -1,5 +1,5 @@ import { Controller } from "stimulus"; -import { createCable } from "../utils/cable"; +import cable from "../utils/cable"; import { isPreview as isTurboPreview } from '../utils/turbo'; export default class extends Controller { @@ -13,47 +13,25 @@ export default class extends Controller { connect() { if (isTurboPreview()) return; - this.connection = createCable().connection; + this.unbind = []; - if (!this.connection.webSocket) { - // Action Cable initializes a WebSocket connection lazily, - // let's "append" the open method to know when a socket becomes available - const origOpen = this.connection.open.bind(this.connection); - this.connection.open = () => { - origOpen.call(); - this.monitor(this.connection.webSocket); - } - } else if (this.connection.isActive()) { - return this.handleOpen(); - } + this.unbind.push(cable.on("connect", this.handleOpen)); + this.unbind.push(cable.on("disconnect", this.handleClose)); + this.unbind.push(cable.on("close", this.handleClose)); - this.handleClose(); + if (cable.state !== "connected") this.handleClose(); } connectCable() { - if (!this.connection.webSocket) return this.connection.open(); + if (cable.state !== "disconnected") return; - this.connection.monitor.reconnectAttempts = 2; - this.connection.monitor.start(); - this.connection.open(); + cable.connect(); } disconnectCable() { - if (!this.connection.isActive()) return; - - this.connection.monitor.stop(); - this.connection.close(); - } - - monitor(socket) { - if (this.socket) { - this.socket.removeEventListener("open", this.handleOpen); - this.socket.removeEventListener("close", this.handleClose); - } + if (cable.state !== "connected") return; - this.socket = socket; - this.socket.addEventListener("open", this.handleOpen); - this.socket.addEventListener("close", this.handleClose); + cable.close(); } toggleState(e) { @@ -76,5 +54,6 @@ export default class extends Controller { disconnect() { this.active = false; + if (this.unbind) this.unbind.forEach((clbk) => clbk()); } } diff --git a/frontend/controllers/list_controller.js b/frontend/controllers/list_controller.js index 3a912a1..fbdccea 100644 --- a/frontend/controllers/list_controller.js +++ b/frontend/controllers/list_controller.js @@ -1,26 +1,26 @@ import { Controller } from "stimulus"; -import { createChannel } from "../utils/cable"; +import cable from "../utils/cable"; +import { Channel } from "@anycable/web"; import { isPreview as isTurboPreview } from '../utils/turbo'; import { DELETE, PATCH } from "../utils/api"; +class ListChannel extends Channel { + static identifier = "ListChannel"; +} + export default class extends Controller { static targets = ["items"]; connect() { if (isTurboPreview()) return; - const channel = "ListChannel"; const id = this.data.get("id"); const workspace = this.data.get("workspace"); - this.channel = createChannel( - {channel, id, workspace}, - { - received: (data) => { - this.handleUpdate(data); - }, - } - ); + this.channel = new ListChannel({ id, workspace }); + this.channel.on("message", (data) => this.handleUpdate(data)); + + cable.subscribe(this.channel); } disconnect() { diff --git a/frontend/controllers/workspace_controller.js b/frontend/controllers/workspace_controller.js index 2d548f5..ffd921d 100644 --- a/frontend/controllers/workspace_controller.js +++ b/frontend/controllers/workspace_controller.js @@ -1,24 +1,24 @@ import { Controller } from "stimulus"; -import { createChannel } from "../utils/cable"; +import cable from "../utils/cable"; +import { Channel } from "@anycable/web"; import { isPreview as isTurboPreview } from '../utils/turbo'; +class WorkspaceChannel extends Channel { + static identifier = "WorkspaceChannel"; +} + export default class extends Controller { static targets = ["lists", "form"]; connect() { if (isTurboPreview()) return; - const channel = "WorkspaceChannel"; const id = this.data.get("id"); - this.channel = createChannel( - {channel, id}, - { - received: (data) => { - this.handleUpdate(data); - }, - } - ); + this.channel = new WorkspaceChannel({ id }); + this.channel.on("message", (data) => this.handleUpdate(data)); + + cable.subscribe(this.channel); } disconnect() { diff --git a/frontend/utils/cable.js b/frontend/utils/cable.js index 4c03011..1088be6 100644 --- a/frontend/utils/cable.js +++ b/frontend/utils/cable.js @@ -1,16 +1,5 @@ -import { createConsumer } from "@rails/actioncable"; +import { createCable } from "@anycable/web"; -let consumer; +let consumer = createCable({ logLevel: "debug" }); -export const createCable = () => { - if (!consumer) { - consumer = createConsumer(); - } - - return consumer; -} - -export const createChannel = (...args) => { - const consumer = createCable(); - return consumer.subscriptions.create(...args); -}; +export default consumer; diff --git a/package.json b/package.json index 6c72aa2..5d23251 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "dependencies": { + "@anycable/web": "^0.1.0", "@hotwired/turbo-rails": "^7.0.0-beta.5", - "@rails/actioncable": "6.0.2", "@rails/ujs": "^6.0.3-4", "@rails/webpacker": "^6.0.0-beta.5", "autoprefixer": "^10.2.5", @@ -29,6 +29,8 @@ ] }, "browserslist": [ - "defaults" + "defaults", + "not IE 11", + "not dead" ] } diff --git a/yarn.lock b/yarn.lock index f87bda1..44a4433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,20 @@ # yarn lockfile v1 +"@anycable/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@anycable/core/-/core-0.1.1.tgz#7228b368e0858d8f338cec60b237b6d442328dd6" + integrity sha512-LG7DYv9zZq53XM5Hgsxic5bkVbKtfIO5AZB86No5NJzpsW21DjGceyp+jLJaMjYOw9yzcqo5acG1cE7IRBirOg== + dependencies: + nanoevents "^6.0.0" + +"@anycable/web@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@anycable/web/-/web-0.1.0.tgz#80535f66e5aa77fdfa90e4726a8ace3500f55249" + integrity sha512-qm5BLWKv6u/eA4t/621hz8QawcD4jiVc149Fn6uQNhUm5AoW+QON+QXYMW0iDauhnlK+CoI5kbdLPpY55ARh5w== + dependencies: + "@anycable/core" "^0.1.0" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" @@ -890,11 +904,6 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@rails/actioncable@6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.2.tgz#bcba9bcd6ee09a47c6628336e07399a68ca87814" - integrity sha512-vN78gohsXPlC5jxBHlmiwkUI9bv6SulcZaE72kAIg/G4ou+wTdiMWPJK1bf2IxUNf+sfOjhl+tbRmY4AzJcgrw== - "@rails/actioncable@^6.1.0": version "6.1.3" resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.3.tgz#c8a67ec4d22ecd6931f7ebd98143fddbc815419a" @@ -3962,6 +3971,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nanoevents@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nanoevents/-/nanoevents-6.0.0.tgz#82454023f448a1eab0df103df59d5813ac16035b" + integrity sha512-0ASElaiZR21yBjhmBiRboCXeNirfIVuiKIxikn6a3H/9zUL+q2HBq+4B3Cb2UdCctoGx5YSwxTXypslD/olJKA== + nanoid@^3.1.20, nanoid@^3.1.22: version "3.1.22" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"