import debounce from 'lodash/debounce'; import { wantsState, userState, offersState, acpOffersState } from './store' import * as toast from './toast'; const SOCKET_URL = `${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`; export default function createSocket(dispatch) { let ws; // ------------- // Outgoing // ------------- function send(msg) { if (msg[0] !== 'Ping') console.log('outgoing msg', msg); if (ws.readyState !== 1) return false; try { ws.send(JSON.stringify(msg)); } catch (e) { console.warn(e); } } // let ping; function sendPing() { // ping = Date.now(); send(['Ping', {}]); // setTimeout(sendPing, 50); } function sendOfferStatusSet(id, status) { send(['OfferStatusSet', { id, status }]); } function sendWantRm(id) { send(['WantRm', { id }]); } function sendWantAdd(releaseId) { send(['WantAdd', { releaseId }]); } function sendOffers() { send(['Offers', {}]); } function sendWants() { send(['Wants', {}]); } function sendAcpOffers() { send(['AcpOffers', {}]); } function sendAcpOfferStatusSet(id, status) { send(['AcpOfferStatusSet', { id, status }]); } function sendAcpListingsSync() { send(['AcpListingsSync', {}]); } let pongTimeout; function onPong() { pongTimeout = setTimeout(sendPing, 5000); } // ------------- // Incoming // ------------- function onUser(user) { dispatch(userState.actions.set(user)); } function onWants(wants) { dispatch(wantsState.actions.set(wants)); } function onOffers(offers) { dispatch(offersState.actions.set(offers)); } function onAcpOffers(offers) { dispatch(acpOffersState.actions.set(offers)); } // ------------- // Setup // ------------- // when the server sends a reply it will have one of these message types // this object wraps the reply types to a function const handlers = { Wants: onWants, Offers: onOffers, User: onUser, AcpOffers: onAcpOffers, Pong: onPong, Error: errHandler, }; function errHandler(error) { // store.dispatch(http.LIBRARY_EVENT, { level: 'error', log: error }); console.error(error); } function onMessage(event) { const res = event.data; const [msgType, params] = JSON.parse(res); if (msgType !== 'Pong') { console.log(msgType, params); } // check for error and split into response type and data if (!handlers[msgType]) return console.error(`${msgType} handler missing`); return handlers[msgType](params); } // Connection opened function onOpen() { console.log('websocket connected', SOCKET_URL) toast.info('connected', 'connected to server'); sendPing(); } function onError(event) { console.error('WebSocket error', event); } function onClose(event) { console.error('WebSocket closed', event); return setTimeout(connect, 2000); } function connect() { if (ws) { clearTimeout(pongTimeout); ws.removeEventListener('open', onOpen); ws.removeEventListener('message', onMessage); ws.removeEventListener('error', onError); ws.removeEventListener('close', onClose); ws = null; } console.log('connecting to websocket', SOCKET_URL); ws = new WebSocket(SOCKET_URL); // Listen for messages ws.addEventListener('open', onOpen); ws.addEventListener('message', onMessage); ws.addEventListener('error', onError); ws.addEventListener('close', onClose); return ws; } return { connect, sendOfferStatusSet, sendWantRm, sendWantAdd, sendOffers, sendWants, sendAcpOffers, sendAcpOfferStatusSet, sendAcpListingsSync, }; }