diff --git a/src/node/StreamWindow.js b/src/node/StreamWindow.js index 0b7374b..b31eae5 100644 --- a/src/node/StreamWindow.js +++ b/src/node/StreamWindow.js @@ -175,6 +175,17 @@ export default class StreamWindow extends EventEmitter { } } + reloadView(viewIdx) { + const view = this.views.find( + (v) => + v.state.matches('displaying') && + v.state.context.pos.spaces.includes(viewIdx), + ) + if (view) { + view.send('RELOAD') + } + } + send(...args) { this.overlayView.webContents.send(...args) } diff --git a/src/node/index.js b/src/node/index.js index 38917ea..360a4c7 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -60,6 +60,8 @@ async function main() { streamWindow.setViews(new Map(msg.views)) } else if (msg.type === 'set-listening-view') { streamWindow.setListeningView(msg.viewIdx) + } else if (msg.type === 'reload-view') { + streamWindow.reloadView(msg.viewIdx) } } diff --git a/src/node/viewStateMachine.js b/src/node/viewStateMachine.js index cc36251..2cc7376 100644 --- a/src/node/viewStateMachine.js +++ b/src/node/viewStateMachine.js @@ -16,14 +16,11 @@ const viewStateMachine = Machine( }, states: { empty: { - entry: [ - assign({ - pos: {}, - info: {}, - url: null, - }), - 'hideView', - ], + entry: assign({ + pos: {}, + info: {}, + url: null, + }), invoke: { src: 'clearView', onError: { @@ -45,6 +42,7 @@ const viewStateMachine = Machine( }), cond: 'urlUnchanged', }, + RELOAD: '.loading', }, states: { loading: { @@ -80,6 +78,7 @@ const viewStateMachine = Machine( running: { initial: 'muted', entry: 'positionView', + exit: 'hideView', on: { DISPLAY: { actions: [ diff --git a/src/static/redo-alt-solid.svg b/src/static/redo-alt-solid.svg new file mode 100644 index 0000000..b8e9455 --- /dev/null +++ b/src/static/redo-alt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/control.js b/src/web/control.js index 2041b40..57802c9 100644 --- a/src/web/control.js +++ b/src/web/control.js @@ -2,11 +2,13 @@ import range from 'lodash/range' import ReconnectingWebSocket from 'reconnecting-websocket' import { h, render } from 'preact' import { useEffect, useState, useCallback, useRef } from 'preact/hooks' -import styled from 'styled-components' +import { State } from 'xstate' +import styled, { css } from 'styled-components' import '../index.css' import { GRID_COUNT } from '../constants' import SoundIcon from '../static/volume-up-solid.svg' +import ReloadIcon from '../static/redo-alt-solid.svg' function emptyStateIdxMap() { return new Map( @@ -15,7 +17,7 @@ function emptyStateIdxMap() { { streamId: null, url: null, - state: {}, + state: State.from({}), isListening: false, }, ]), @@ -47,13 +49,13 @@ function App({ wsEndpoint }) { continue } const streamId = streams.find((d) => d.Link === url)?._id - const isListening = - viewState.state.displaying?.running === 'listening' + const state = State.from(viewState.state) + const isListening = state.matches('displaying.running.listening') for (const space of pos.spaces) { Object.assign(newStateIdxMap.get(space), { streamId, url, - state: viewState.state, + state, isListening, }) } @@ -102,6 +104,15 @@ function App({ wsEndpoint }) { ) }, []) + const handleReloadView = useCallback((idx) => { + wsRef.current.send( + JSON.stringify({ + type: 'reload-view', + viewIdx: idx, + }), + ) + }, []) + return (