Skip to content

Instantly share code, notes, and snippets.

@ccapndave
Created December 12, 2018 18:30
Show Gist options
  • Save ccapndave/72381b45f01e6d798049e6375b475297 to your computer and use it in GitHub Desktop.
Save ccapndave/72381b45f01e6d798049e6375b475297 to your computer and use it in GitHub Desktop.
port module WebSocket exposing (listen, send, open)
open : String -> Cmd msg
open url =
wsOpen { url = url }
send : String -> String -> Cmd msg
send url message =
wsSend { url = url, message = message }
listen : String -> (String -> msg) -> msg -> msg -> Sub msg
listen urlToListenTo onMessageTagger disconnected noop =
Sub.batch
[ wsOnMessage <|
\{ url, message } ->
if url == urlToListenTo then
onMessageTagger message
else
noop
, wsOnClose <|
\{ url } ->
if url == urlToListenTo then
disconnected
else
noop
]
port wsOpen : { url : String } -> Cmd msg
port wsSend : { url : String, message : String } -> Cmd msg
port wsOnMessage : ({ url : String, message : String } -> msg) -> Sub msg
port wsOnClose : ({ url : String } -> msg) -> Sub msg
interface IncomingPort<T> {
subscribe: (callback: (value: T) => void) => void;
unsubscribe: (callback: (value: T) => void) => void;
}
interface OutgoingPort<T> {
send: (value: T) => void;
}
interface Ports {
wsOpen: IncomingPort<{ url: string }>
wsSend: IncomingPort<{ url: string, message: string }>
wsOnMessage: OutgoingPort<{ url: string, message: string }>
wsOnClose: OutgoingPort<{ url: string }>
}
export function configureWebsocketClient(ports: Ports) {
let webSockets = new Map();
if (ports.wsOpen) {
ports.wsOpen.subscribe(({ url }) => {
console.log("open ws - " + url);
open(url);
});
}
if (ports.wsSend) {
ports.wsSend.subscribe(({ url, message }) => {
console.log("send - " + url + ": " + message);
open(url)
.then(webSocket => webSocket.send(message))
.catch(err => {
console.error(err.toString());
})
});
}
function open(url: string): Promise<WebSocket> {
if (!webSockets.has(url)) {
return new Promise((resolve, reject) => {
try {
const webSocket = new WebSocket(url);
webSocket.addEventListener("open", _ => resolve(webSocket));
webSocket.addEventListener("message", ({ data }) => onMessage(url, data));
webSocket.addEventListener("close", _ => onClose(url));
} catch (err) {
console.error(err.toString());
reject(err);
}
});
} else {
return Promise.resolve(webSockets.get(url))
}
}
function onMessage(url: string, data: string) {
ports.wsOnMessage.send({ url, message: data });
}
function onClose(url: string) {
ports.wsOnClose.send({ url });
attemptReconnect(url);
}
function attemptReconnect(url: string, attempt: number = 0) {
let delay = 10 * Math.pow(2, attempt) * 1000;
setTimeout(() => {
open(url).catch(_ => attemptReconnect(url, attempt + 1));
}, delay)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment