Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@
/yarn-error.log
yarn-debug.log*
.yarn-integrity
dip.override.yml
1 change: 1 addition & 0 deletions dip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
27 changes: 16 additions & 11 deletions frontend/controllers/chat_controller.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -61,6 +66,6 @@ export default class extends Controller {

if (!message) return;

this.channel.perform("speak", {message});
this.channel.speak(message);
}
}
43 changes: 11 additions & 32 deletions frontend/controllers/connection_controller.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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) {
Expand All @@ -76,5 +54,6 @@ export default class extends Controller {

disconnect() {
this.active = false;
if (this.unbind) this.unbind.forEach((clbk) => clbk());
}
}
20 changes: 10 additions & 10 deletions frontend/controllers/list_controller.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
20 changes: 10 additions & 10 deletions frontend/controllers/workspace_controller.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
17 changes: 3 additions & 14 deletions frontend/utils/cable.js
Original file line number Diff line number Diff line change
@@ -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;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -29,6 +29,8 @@
]
},
"browserslist": [
"defaults"
"defaults",
"not IE 11",
"not dead"
]
}
24 changes: 19 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down