diff options
-rw-r--r-- | frontend/flow-typed/npm/webstomp-client_vx.x.x.js | 219 | ||||
-rw-r--r-- | frontend/src/api/sevenWondersApi.js | 66 | ||||
-rw-r--r-- | frontend/src/api/websocket.js | 32 | ||||
-rw-r--r-- | frontend/src/models/games.js | 10 | ||||
-rw-r--r-- | frontend/src/routes.js | 9 | ||||
-rw-r--r-- | frontend/src/sagas.js | 13 | ||||
-rw-r--r-- | frontend/src/sagas/errors.js | 17 | ||||
-rw-r--r-- | frontend/src/sagas/gameBrowser.js | 29 | ||||
-rw-r--r-- | frontend/src/sagas/home.js | 17 | ||||
-rw-r--r-- | frontend/src/sagas/lobby.js | 28 | ||||
-rw-r--r-- | frontend/src/utils/websocket.js | 50 |
11 files changed, 311 insertions, 179 deletions
diff --git a/frontend/flow-typed/npm/webstomp-client_vx.x.x.js b/frontend/flow-typed/npm/webstomp-client_vx.x.x.js index 68f2e35e..01ea5ba6 100644 --- a/frontend/flow-typed/npm/webstomp-client_vx.x.x.js +++ b/frontend/flow-typed/npm/webstomp-client_vx.x.x.js @@ -1,67 +1,160 @@ // flow-typed signature: d776c429ba6acfd6ff5187bd78efb074 // flow-typed version: <<STUB>>/webstomp-client_v^1.0.6/flow_v0.46.0 -/** - * This is an autogenerated libdef stub for: - * - * 'webstomp-client' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ +// declare module 'webstomp-client' { +// +// declare module .exports: { +// client(url: string): Client; +// client(url: string, options: Object): Client; +// over(ws: any): Client; +// over(ws: any, options: Object): Client; +// } +// } +// +// declare type Frame = { +// body: string, +// command: string, +// header: { +// 'heart-beat': number, +// 'user-name': string, +// version: string +// } +// }; +// +// declare type FrameConsumer = (frame: Frame) => void; +// +// declare type Runnable = () => void; +// +// declare type Subscription = { +// id: string, +// unsubscribe: Runnable +// }; +// +// declare class Client { +// constructor(ws: any): Client; +// constructor(ws: any, options: Object): Client; +// debug(...args: string[]): void; +// connect(headers: Object, connectCallback: FrameConsumer): void; +// connect(headers: Object, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer, host: string): void; +// disconnect(disconnectCallback: Runnable): void; +// disconnect(disconnectCallback: Runnable, headers: Object): void; +// send(destination: string): void; +// send(destination: string, body: string): void; +// send(destination: string, body: string, headers: Object): void; +// begin(): void; +// begin(transaction: string): void; +// commit(transaction: string): void; +// abort(transaction: string): void; +// ack(messageID: string, subscription: string): void; +// ack(messageID: string, subscription: string, headers: Object): void; +// nack(messageID: string, subscription: string): void; +// nack(messageID: string, subscription: string, headers: Object): void; +// subscribe(destination: string, callback: FrameConsumer): Subscription; +// subscribe(destination: string, callback: FrameConsumer, headers: Object): Subscription; +// unsubscribe(id: string): void; +// unsubscribe(id: string, headers: Object): void; +// } +// } -declare module 'webstomp-client' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'webstomp-client/dist/webstomp' { - declare module.exports: any; -} - -declare module 'webstomp-client/dist/webstomp.min' { - declare module.exports: any; -} - -declare module 'webstomp-client/src/client' { - declare module.exports: any; -} - -declare module 'webstomp-client/src/frame' { - declare module.exports: any; -} - -declare module 'webstomp-client/src/utils' { - declare module.exports: any; -} - -declare module 'webstomp-client/src/webstomp' { - declare module.exports: any; -} - -// Filename aliases -declare module 'webstomp-client/dist/webstomp.js' { - declare module.exports: $Exports<'webstomp-client/dist/webstomp'>; -} -declare module 'webstomp-client/dist/webstomp.min.js' { - declare module.exports: $Exports<'webstomp-client/dist/webstomp.min'>; -} -declare module 'webstomp-client/src/client.js' { - declare module.exports: $Exports<'webstomp-client/src/client'>; -} -declare module 'webstomp-client/src/frame.js' { - declare module.exports: $Exports<'webstomp-client/src/frame'>; -} -declare module 'webstomp-client/src/utils.js' { - declare module.exports: $Exports<'webstomp-client/src/utils'>; -} -declare module 'webstomp-client/src/webstomp.js' { - declare module.exports: $Exports<'webstomp-client/src/webstomp'>; -} +// declare module 'webstomp-client/dist/webstomp' { +// +// declare export type Frame = { +// body: string, +// command: string, +// header: { +// 'heart-beat': number, +// 'user-name': string, +// version: string +// } +// }; +// +// declare export type FrameConsumer = (frame: Frame) => void; +// +// declare export type Runnable = () => void; +// +// declare export type Subscription = { +// id: string, +// unsubscribe: Runnable +// }; +// +// declare export class Client { +// constructor(ws: any): Client; +// constructor(ws: any, options: Object): Client; +// debug(...args: string[]): void; +// connect(headers: Object, connectCallback: FrameConsumer): void; +// connect(headers: Object, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer, host: string): void; +// disconnect(disconnectCallback: Runnable): void; +// disconnect(disconnectCallback: Runnable, headers: Object): void; +// send(destination: string): void; +// send(destination: string, body: string): void; +// send(destination: string, body: string, headers: Object): void; +// begin(): void; +// begin(transaction: string): void; +// commit(transaction: string): void; +// abort(transaction: string): void; +// ack(messageID: string, subscription: string): void; +// ack(messageID: string, subscription: string, headers: Object): void; +// nack(messageID: string, subscription: string): void; +// nack(messageID: string, subscription: string, headers: Object): void; +// subscribe(destination: string, callback: FrameConsumer): Subscription; +// subscribe(destination: string, callback: FrameConsumer, headers: Object): Subscription; +// unsubscribe(id: string): void; +// unsubscribe(id: string, headers: Object): void; +// } +// } +// +// declare module 'webstomp-client/src/client' { +// +// declare type Frame = { +// body: string, +// command: string, +// header: { +// 'heart-beat': number, +// 'user-name': string, +// version: string +// } +// }; +// +// declare type FrameConsumer = (frame: Frame) => void; +// +// declare type Runnable = () => void; +// +// declare type Subscription = { +// id: string, +// unsubscribe: Runnable +// }; +// +// declare module.exports: { +// constructor(ws: any): Client; +// constructor(ws: any, options: Object): Client; +// debug(...args: string[]): void; +// connect(headers: Object, connectCallback: FrameConsumer): void; +// connect(headers: Object, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer): void; +// connect(login: string, passcode: string, connectCallback: FrameConsumer, errorCallback: FrameConsumer, host: string): void; +// disconnect(disconnectCallback: Runnable): void; +// disconnect(disconnectCallback: Runnable, headers: Object): void; +// send(destination: string): void; +// send(destination: string, body: string): void; +// send(destination: string, body: string, headers: Object): void; +// begin(): void; +// begin(transaction: string): void; +// commit(transaction: string): void; +// abort(transaction: string): void; +// ack(messageID: string, subscription: string): void; +// ack(messageID: string, subscription: string, headers: Object): void; +// nack(messageID: string, subscription: string): void; +// nack(messageID: string, subscription: string, headers: Object): void; +// subscribe(destination: string, callback: FrameConsumer): Subscription; +// subscribe(destination: string, callback: FrameConsumer, headers: Object): Subscription; +// unsubscribe(id: string): void; +// unsubscribe(id: string, headers: Object): void; +// }; +// } diff --git a/frontend/src/api/sevenWondersApi.js b/frontend/src/api/sevenWondersApi.js new file mode 100644 index 00000000..4ba0fff3 --- /dev/null +++ b/frontend/src/api/sevenWondersApi.js @@ -0,0 +1,66 @@ +import { createJsonSubscriptionChannel, createStompSession } from './websocket'; +import type { Client } from 'webstomp-client'; +import type { Channel } from 'redux-saga'; + +const wsURL = '/seven-wonders-websocket'; + +export class SevenWondersSession { + client: Client; + + constructor(client: Client) { + this.client = client; + } + + watchErrors(): Channel<ApiError> { + return createJsonSubscriptionChannel(this.client, '/user/queue/errors'); + } + + chooseName(displayName: string): void { + this.client.send('/app/chooseName', JSON.stringify({ playerName: displayName })); + } + + watchNameChoice(): Channel<Object> { + return createJsonSubscriptionChannel(this.client, '/user/queue/nameChoice'); + } + + watchGames(): Channel<Object> { + return createJsonSubscriptionChannel(this.client, '/topic/games'); + } + + watchLobbyJoined(): Channel<Object> { + return createJsonSubscriptionChannel(this.client, '/user/queue/lobby/joined'); + } + + watchLobbyUpdated(currentGameId: number): Channel<Object> { + return createJsonSubscriptionChannel(this.client, `/topic/lobby/${currentGameId}/updated`); + } + + watchGameStarted(currentGameId: number): Channel<Object> { + return createJsonSubscriptionChannel(this.client, `/topic/lobby/${currentGameId}/started`); + } + + createGame(gameName: string): void { + this.client.send('/app/lobby/create', JSON.stringify({ gameName })); + } + + joinGame(gameId: number): void { + this.client.send('/app/lobby/join', JSON.stringify({ gameId })); + } + + startGame(): void { + this.client.send('/app/lobby/startGame', {}); + } +} + +export function createSession(): Promise<SevenWondersSession> { + return createStompSession(wsURL).then(client => new SevenWondersSession(client)); +} + +export class ApiError { + message: string; + details: ApiErrorDetail[]; +} + +export class ApiErrorDetail { + message: string; +} diff --git a/frontend/src/api/websocket.js b/frontend/src/api/websocket.js new file mode 100644 index 00000000..6dc6e1a0 --- /dev/null +++ b/frontend/src/api/websocket.js @@ -0,0 +1,32 @@ +// @flow +import SockJS from 'sockjs-client'; +import Stomp from 'webstomp-client'; +import type { Client, Frame, Subscription } from 'webstomp-client'; + +import { eventChannel } from 'redux-saga'; +import type { Channel } from 'redux-saga'; + +function createStompClient(url: string): Client { + return Stomp.over(new SockJS(url), { + debug: process.env.NODE_ENV !== 'production', + }); +} + +export function createStompSession(url: string, headers: Object = {}): Promise<Client> { + return new Promise((resolve, reject) => { + const client: Client = createStompClient(url); + const onSuccess = (frame: Frame) => resolve(client); + client.connect(headers, onSuccess, reject); + }); +} + +export function createJsonSubscriptionChannel(client: Client, path: string): Channel { + return eventChannel((emitter: (data: any) => void) => { + const socketSubscription: Subscription = client.subscribe(path, (frame: Frame) => { + // not all frames have a JSON body + const value = frame && frame.body && JSON.parse(frame.body); + emitter(value); + }); + return () => socketSubscription.unsubscribe(); + }); +} diff --git a/frontend/src/models/games.js b/frontend/src/models/games.js index aab37aea..36619760 100644 --- a/frontend/src/models/games.js +++ b/frontend/src/models/games.js @@ -8,11 +8,11 @@ export type SettingsShape = { discardedCardGold: number, defaultTradingCost: number, wonPointsPerVictoryPerAge: { - '1': number, - '2': number, - '3': number + "1": number, + "2": number, + "3": number }, - wonderSidePickMethod: 'EACH_RANDOM' | 'TODO', + wonderSidePickMethod: "EACH_RANDOM" | "TODO", pointsPer3Gold: number }; export type SettingsType = Record<SettingsShape>; @@ -39,7 +39,7 @@ export type GameShape = { name: string | void, players: List<string>, settings: SettingsType, - state: 'LOBBY' | 'TODO' + state: "LOBBY" | "TODO" }; export type GameType = Record<GameShape>; export type GameMapType = Map<string, GameShape>; diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 40906b93..52990728 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -9,16 +9,17 @@ import HomePage from './containers/home'; import GameBrowser from './containers/gameBrowser'; import Lobby from './containers/lobby'; import Error404 from './components/errors/Error404'; +import { SevenWondersSession } from './api/sevenWondersApi'; -export const makeSagaRoutes = wsConnection => ({ +export const makeSagaRoutes = (sevenWondersSession: SevenWondersSession) => ({ *'/'() { - yield fork(homeSaga, wsConnection); + yield fork(homeSaga, sevenWondersSession); }, *'/games'() { - yield fork(gameBrowserSaga, wsConnection); + yield fork(gameBrowserSaga, sevenWondersSession); }, *'/lobby/*'() { - yield fork(lobbySaga, wsConnection); + yield fork(lobbySaga, sevenWondersSession); }, }); diff --git a/frontend/src/sagas.js b/frontend/src/sagas.js index 037aa9ed..2b690a02 100644 --- a/frontend/src/sagas.js +++ b/frontend/src/sagas.js @@ -3,20 +3,19 @@ import { router } from 'redux-saga-router'; import { call, fork } from 'redux-saga/effects'; import { makeSagaRoutes } from './routes'; -import { createWsConnection } from './utils/websocket'; import errorHandlingSaga from './sagas/errors'; -import type { SocketObjectType } from './utils/websocket'; import type { History } from 'react-router'; +import { SevenWondersSession, createSession } from './api/sevenWondersApi'; export default function* rootSaga(history: History): * { - let wsConnection: SocketObjectType | void; + let sevenWondersSession: SevenWondersSession | void; try { - wsConnection = yield call(createWsConnection); + sevenWondersSession = yield call(createSession); } catch (error) { - console.error('Could not connect to socket'); + console.error('Could not connect to socket', error); return; } - yield fork(errorHandlingSaga, wsConnection); - yield* router(history, makeSagaRoutes(wsConnection)); + yield fork(errorHandlingSaga, sevenWondersSession); + yield* router(history, makeSagaRoutes(sevenWondersSession)); } diff --git a/frontend/src/sagas/errors.js b/frontend/src/sagas/errors.js index e43875ae..86fa0124 100644 --- a/frontend/src/sagas/errors.js +++ b/frontend/src/sagas/errors.js @@ -1,12 +1,13 @@ -import { apply, call, cancelled, take } from 'redux-saga/effects'; -import { createSubscriptionChannel } from '../utils/websocket'; -import { toastr } from 'react-redux-toastr'; +import {apply, cancelled, take} from 'redux-saga/effects'; +import {toastr} from 'react-redux-toastr'; +import {ApiError, SevenWondersSession} from '../api/sevenWondersApi'; +import type {Channel} from 'redux-saga'; -export default function* errorHandlingSaga({ socket }) { - const errorChannel = yield call(createSubscriptionChannel, socket, '/user/queue/errors'); +export default function* errorHandlingSaga(session: SevenWondersSession) { + const errorChannel: Channel<ApiError> = yield apply(session, session.watchErrors, []); try { while (true) { - const error = yield take(errorChannel); + const error: ApiError = yield take(errorChannel); yield* handleOneError(error); } } finally { @@ -17,13 +18,13 @@ export default function* errorHandlingSaga({ socket }) { } } -function* handleOneError(err) { +function* handleOneError(err: ApiError) { console.error('Error received on web socket channel', err); const msg = buildMsg(err); yield apply(toastr, toastr.error, [msg, { icon: 'error' }]); } -function buildMsg(err) { +function buildMsg(err: ApiError): string { if (err.details.length > 0) { return err.details.map(d => d.message).join('\n'); } else { diff --git a/frontend/src/sagas/gameBrowser.js b/frontend/src/sagas/gameBrowser.js index 55927bf2..17eb9287 100644 --- a/frontend/src/sagas/gameBrowser.js +++ b/frontend/src/sagas/gameBrowser.js @@ -1,6 +1,5 @@ // @flow import { call, put, take, apply } from 'redux-saga/effects'; -import { createSubscriptionChannel } from '../utils/websocket'; import { push } from 'react-router-redux'; import { normalize } from 'normalizr'; @@ -8,11 +7,10 @@ import { game as gameSchema, gameList as gameListSchema } from '../schemas/games import { actions as gameActions, types } from '../redux/games'; import { actions as playerActions } from '../redux/players'; +import type { SevenWondersSession } from '../api/sevenWondersApi'; -import type { SocketObjectType, SocketType } from '../utils/websocket'; - -function* watchGames({ socket }: { socket: SocketType }): * { - const gamesChannel = yield call(createSubscriptionChannel, socket, '/topic/games'); +function* watchGames(session: SevenWondersSession): * { + const gamesChannel = yield apply(session, session.watchGames, []); try { while (true) { const gameList = yield take(gamesChannel); @@ -26,8 +24,8 @@ function* watchGames({ socket }: { socket: SocketType }): * { } } -function* watchLobbyJoined({ socket }: { socket: SocketType }): * { - const joinedLobbyChannel = yield call(createSubscriptionChannel, socket, '/user/queue/lobby/joined'); +function* watchLobbyJoined(session: SevenWondersSession): * { + const joinedLobbyChannel = yield apply(session, session.watchLobbyJoined, []); try { const joinedLobby = yield take(joinedLobbyChannel); const normalized = normalize(joinedLobby, gameSchema); @@ -41,27 +39,22 @@ function* watchLobbyJoined({ socket }: { socket: SocketType }): * { } } -function* createGame({ socket }: { socket: SocketType }): * { +function* createGame(session: SevenWondersSession): * { while (true) { const { gameName } = yield take(types.REQUEST_CREATE_GAME); - yield apply(socket, socket.send, ['/app/lobby/create', JSON.stringify({ gameName }), {}]); + yield apply(session, session.createGame, [gameName]); } } -function* joinGame({ socket }: { socket: SocketType }): * { +function* joinGame(session: SevenWondersSession): * { while (true) { const { gameId } = yield take(types.REQUEST_JOIN_GAME); - yield apply(socket, socket.send, ['/app/lobby/join', JSON.stringify({ gameId }), {}]); + yield apply(session, session.joinGame, [gameId]); } } -function* gameBrowserSaga(socketConnection: SocketObjectType): * { - yield [ - call(watchGames, socketConnection), - call(watchLobbyJoined, socketConnection), - call(createGame, socketConnection), - call(joinGame, socketConnection), - ]; +function* gameBrowserSaga(session: SevenWondersSession): * { + yield [call(watchGames, session), call(watchLobbyJoined, session), call(createGame, session), call(joinGame, session)]; } export default gameBrowserSaga; diff --git a/frontend/src/sagas/home.js b/frontend/src/sagas/home.js index 579c7fd6..eb65097b 100644 --- a/frontend/src/sagas/home.js +++ b/frontend/src/sagas/home.js @@ -1,18 +1,18 @@ import { apply, call, put, take } from 'redux-saga/effects'; -import { createSubscriptionChannel } from '../utils/websocket'; import { push } from 'react-router-redux'; import { actions, types } from '../redux/players'; +import type { SevenWondersSession } from '../api/sevenWondersApi'; -function* sendUsername({ socket }) { +function* sendUsername(session: SevenWondersSession) { while (true) { const { username } = yield take(types.REQUEST_CHOOSE_USERNAME); - yield apply(socket, socket.send, ['/app/chooseName', JSON.stringify({ playerName: username })]); + yield apply(session, session.chooseName, [username]); } } -function* validateUsername({ socket }) { - const usernameChannel = yield call(createSubscriptionChannel, socket, '/user/queue/nameChoice'); +function* validateUsername(session: SevenWondersSession) { + const usernameChannel = yield apply(session, session.watchNameChoice, []); while (true) { const user = yield take(usernameChannel); yield put(actions.setCurrentPlayer(user)); @@ -21,9 +21,8 @@ function* validateUsername({ socket }) { } } -function* usernameChoiceSaga(wsConnection) { - // TODO: Run sendUsername in loop when we have the ability to cancel saga on route change - yield [call(sendUsername, wsConnection), call(validateUsername, wsConnection)]; +function* homeSaga(session: SevenWondersSession) { + yield [call(sendUsername, session), call(validateUsername, session)]; } -export default usernameChoiceSaga; +export default homeSaga; diff --git a/frontend/src/sagas/lobby.js b/frontend/src/sagas/lobby.js index d11511e8..0c264dde 100644 --- a/frontend/src/sagas/lobby.js +++ b/frontend/src/sagas/lobby.js @@ -1,5 +1,6 @@ import { call, put, take, apply } from 'redux-saga/effects'; -import { createSubscriptionChannel } from '../utils/websocket'; +import type { Channel } from 'redux-saga'; + import { push } from 'react-router-redux'; import { normalize } from 'normalizr'; @@ -7,15 +8,16 @@ import { game as gameSchema } from '../schemas/games'; import { actions as gameActions, types } from '../redux/games'; import { actions as playerActions } from '../redux/players'; +import { SevenWondersSession } from '../api/sevenWondersApi'; -function getCurrentGameId() { +function getCurrentGameId(): number { const path = window.location.pathname; return path.split('lobby/')[1]; } -function* watchLobbyUpdates({ socket }) { - const currentGameId = getCurrentGameId(); - const lobbyUpdatesChannel = yield call(createSubscriptionChannel, socket, `/topic/lobby/${currentGameId}/updated`); +function* watchLobbyUpdates(session: SevenWondersSession) { + const currentGameId: number = getCurrentGameId(); + const lobbyUpdatesChannel: Channel = yield apply(session, session.watchLobbyUpdated, [currentGameId]); try { while (true) { const lobby = yield take(lobbyUpdatesChannel); @@ -28,9 +30,9 @@ function* watchLobbyUpdates({ socket }) { } } -function* watchGameStart({ socket }) { +function* watchGameStart(session: SevenWondersSession) { const currentGameId = getCurrentGameId(); - const gameStartedChannel = yield call(createSubscriptionChannel, socket, `/topic/lobby/${currentGameId}/started`); + const gameStartedChannel = yield apply(session, session.watchGameStarted, [currentGameId]); try { yield take(gameStartedChannel); yield put(gameActions.enterGame()); @@ -40,19 +42,15 @@ function* watchGameStart({ socket }) { } } -function* startGame({ socket }) { +function* startGame(session: SevenWondersSession) { while (true) { yield take(types.REQUEST_START_GAME); - yield apply(socket, socket.send, ['/app/lobby/startGame', {}]); + yield apply(session, session.startGame, []); } } -function* lobbySaga(socketConnection) { - yield [ - call(watchLobbyUpdates, socketConnection), - call(watchGameStart, socketConnection), - call(startGame, socketConnection), - ]; +function* lobbySaga(session: SevenWondersSession) { + yield [call(watchLobbyUpdates, session), call(watchGameStart, session), call(startGame, session)]; } export default lobbySaga; diff --git a/frontend/src/utils/websocket.js b/frontend/src/utils/websocket.js deleted file mode 100644 index 6acd0806..00000000 --- a/frontend/src/utils/websocket.js +++ /dev/null @@ -1,50 +0,0 @@ -// @flow -import SockJS from 'sockjs-client'; -import Stomp from 'webstomp-client'; -import { eventChannel } from 'redux-saga'; - -const wsURL = '/seven-wonders-websocket'; - -export type FrameType = { - body: string, - command: string, - header: { - 'heart-beat': number, - 'user-name': string, - version: string - } -}; -export type SocketType = { - connect: (headers: Object, onSucces: (frame: FrameType) => void, onReject: (error: any) => void) => void, - subscribe: (path: string, callback: (event: any) => void) => Object -}; -export type SocketSubscriptionType = { - id: string, - unsubscribe: () => void -}; -export type SocketEventType = { - body: string -}; -export type SocketObjectType = { - frame: FrameType, - socket: SocketType -}; - -export const createWsConnection = (headers: Object = {}): Promise<SocketObjectType> => - new Promise((resolve, reject) => { - let socket: SocketType = Stomp.over(new SockJS(wsURL), { - debug: process.env.NODE_ENV !== 'production', - }); - socket.connect(headers, (frame: FrameType) => resolve({ frame, socket }), reject); - }); - -export const createSubscriptionChannel = (socket: SocketType, path: string) => { - return eventChannel((emitter: (data: any) => void) => { - const socketSubscription: SocketSubscriptionType = socket.subscribe(path, (event: SocketEventType) => { - // not all events have a body - const value = event.body && JSON.parse(event.body); - emitter(value); - }); - return () => socketSubscription.unsubscribe(); - }); -}; |