From 8f1f90c3e7d836a23f1cd09617c2a0fcfac3f3f4 Mon Sep 17 00:00:00 2001 From: Melonai Date: Mon, 31 May 2021 01:47:38 +0200 Subject: Socket connection with managed state --- assets/src/network/channel/connection.ts | 69 ++++++++++++++++++++++++++++++++ assets/src/network/channel/request.ts | 14 +++++++ assets/src/network/channel/share.ts | 17 ++++++++ assets/src/network/channel/socket.ts | 59 +++++++++++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 assets/src/network/channel/connection.ts create mode 100644 assets/src/network/channel/request.ts create mode 100644 assets/src/network/channel/share.ts create mode 100644 assets/src/network/channel/socket.ts (limited to 'assets/src/network/channel') diff --git a/assets/src/network/channel/connection.ts b/assets/src/network/channel/connection.ts new file mode 100644 index 0000000..52d85d1 --- /dev/null +++ b/assets/src/network/channel/connection.ts @@ -0,0 +1,69 @@ +import { Channel, Push, Socket } from "phoenix"; +import { startRequest } from "./request"; +import { startShare } from "./share"; +import { connectSocket, fetchToken } from "./socket"; + +export enum Type { + NONE, + REQUEST, + SHARE, +} + +enum ConnectionState { + CONNECTING_SOCKET, + FETCHING_TOKEN, + CONNECTING_CHANNEL, + + CONNECTED, +} + +export type Connection = { + socket: Socket; + channel: Channel | null; + token: string | null; + state: ConnectionState; + type: Type; +}; + +const connection: Connection = { + socket: new Socket("/socket", {}), + channel: null, + token: null, + state: ConnectionState.CONNECTING_SOCKET, + type: Type.NONE, +}; + +export async function start(type: Type.REQUEST | Type.SHARE) { + connection.type = type; + + await connectSocket(connection.socket); + + updateState(ConnectionState.FETCHING_TOKEN); + connection.token = await fetchToken(connection.socket); + + updateState(ConnectionState.CONNECTING_CHANNEL); + type === Type.SHARE + ? await startShare(connection) + : await startRequest(connection); +} + +export function send(event: string, data: any): Push { + if (connection.state !== ConnectionState.CONNECTED) { + throw new Error("There is no connection yet."); + } + + return connection.channel.push(event, data); +} + +export function getOwnToken(): string { + if (connection.state <= ConnectionState.FETCHING_TOKEN) { + throw new Error("There is no token yet."); + } + + return connection.token; +} + +function updateState(state: ConnectionState) { + // TODO: Notify state listeners + connection.state = state; +} diff --git a/assets/src/network/channel/request.ts b/assets/src/network/channel/request.ts new file mode 100644 index 0000000..801eee1 --- /dev/null +++ b/assets/src/network/channel/request.ts @@ -0,0 +1,14 @@ +import getShareToken from "../../utils/getShareToken"; +import type { Connection } from "./connection"; +import { joinRequestChannel } from "./socket"; + +export async function startRequest(connection: Connection) { + const requestChannel = await joinRequestChannel( + connection.socket, + connection.token, + getShareToken() + ); + connection.channel = requestChannel; + + // TODO: Handle incoming messages from the request channel +} diff --git a/assets/src/network/channel/share.ts b/assets/src/network/channel/share.ts new file mode 100644 index 0000000..10a34fa --- /dev/null +++ b/assets/src/network/channel/share.ts @@ -0,0 +1,17 @@ +import requests from "../../stores/requests"; +import type { Connection } from "./connection"; +import { joinShareChannel } from "./socket"; + +export async function startShare(connection: Connection) { + const shareChannel = await joinShareChannel( + connection.socket, + connection.token + ); + connection.channel = shareChannel; + + shareChannel.on("new_request", onNewRequest); +} + +function onNewRequest(data) { + requests.addRequest(data); +} diff --git a/assets/src/network/channel/socket.ts b/assets/src/network/channel/socket.ts new file mode 100644 index 0000000..7d14842 --- /dev/null +++ b/assets/src/network/channel/socket.ts @@ -0,0 +1,59 @@ +import type { Channel, Socket } from "phoenix"; + +export function connectSocket(socket: Socket): Promise { + return new Promise((resolve, _reject) => { + socket.connect(); + socket.onOpen(() => { + resolve(); + }); + }); +} + +export function fetchToken(socket: Socket): Promise { + let tokenChannel = socket.channel("token", {}); + return new Promise((resolve, reject) => { + tokenChannel + .join() + .receive("ok", () => { + tokenChannel + .push("get_token", {}, 5000) + .receive("ok", ({ token }) => resolve(token)) + .receive("error", err => reject(err)) + .receive("timeout", err => reject(err)); + }) + .receive("error", err => reject(err)); + }); +} + +export function joinShareChannel( + socket: Socket, + token: string +): Promise { + return joinChannel(socket, `share:${token}`); +} + +export function joinRequestChannel( + socket: Socket, + request_token: string, + share_token: string +): Promise { + return joinChannel(socket, `request:${request_token}`, { + share: share_token, + }); +} + +function joinChannel( + socket: Socket, + topic: string, + opts?: object +): Promise { + let channel = socket.channel(topic, opts); + + return new Promise((resolve, reject) => { + channel + .join() + .receive("ok", () => resolve(channel)) + .receive("error", err => reject(err)) + .receive("timeout", err => reject(err)); + }); +} -- cgit 1.4.1