refactor(app): add AppStateChangedEvent
This commit is contained in:
parent
c02a66202f
commit
b8550f050b
@ -89,7 +89,9 @@ func (a *App) Run(ctx context.Context) error {
|
||||
// It is only needed for integration tests when rendering modals before the
|
||||
// main loop starts. It would be nice to remove this but the risk/impact on
|
||||
// non-test code is pretty low.
|
||||
emptyUI := func() { ui.SetState(domain.AppState{}) }
|
||||
emptyUI := func() {
|
||||
a.eventBus.Send(event.AppStateChangedEvent{State: domain.AppState{}})
|
||||
}
|
||||
|
||||
containerClient, err := container.NewClient(ctx, a.dockerClient, a.logger.With("component", "container_client"))
|
||||
if err != nil {
|
||||
@ -109,7 +111,11 @@ func (a *App) Run(ctx context.Context) error {
|
||||
}
|
||||
defer containerClient.Close()
|
||||
|
||||
updateUI := func() { ui.SetState(*state) }
|
||||
updateUI := func() {
|
||||
// The state is mutable so can't be passed into another goroutine
|
||||
// without cloning it first.
|
||||
a.eventBus.Send(event.AppStateChangedEvent{State: state.Clone()})
|
||||
}
|
||||
updateUI()
|
||||
|
||||
var tlsCertPath, tlsKeyPath string
|
||||
@ -219,7 +225,7 @@ func (a *App) handleCommand(
|
||||
break
|
||||
}
|
||||
a.cfg = newCfg
|
||||
handleConfigUpdate(a.cfg, state, ui)
|
||||
a.handleConfigUpdate(state)
|
||||
a.eventBus.Send(event.DestinationAddedEvent{URL: c.URL})
|
||||
case domain.CommandRemoveDestination:
|
||||
repl.StopDestination(c.URL) // no-op if not live
|
||||
@ -233,7 +239,7 @@ func (a *App) handleCommand(
|
||||
break
|
||||
}
|
||||
a.cfg = newCfg
|
||||
handleConfigUpdate(a.cfg, state, ui)
|
||||
a.handleConfigUpdate(state)
|
||||
a.eventBus.Send(event.DestinationRemovedEvent{URL: c.URL})
|
||||
case domain.CommandStartDestination:
|
||||
if !state.Source.Live {
|
||||
@ -251,10 +257,10 @@ func (a *App) handleCommand(
|
||||
return true
|
||||
}
|
||||
|
||||
// handleConfigUpdate applies the config to the app state, and updates the UI.
|
||||
func handleConfigUpdate(cfg config.Config, appState *domain.AppState, ui *terminal.UI) {
|
||||
applyConfig(cfg, appState)
|
||||
ui.SetState(*appState)
|
||||
// handleConfigUpdate applies the config to the app state, and sends an AppStateChangedEvent.
|
||||
func (a *App) handleConfigUpdate(appState *domain.AppState) {
|
||||
applyConfig(a.cfg, appState)
|
||||
a.eventBus.Send(event.AppStateChangedEvent{State: appState.Clone()})
|
||||
}
|
||||
|
||||
// applyServerState applies the current server state to the app state.
|
||||
|
@ -1,18 +1,31 @@
|
||||
package event
|
||||
|
||||
import "git.netflux.io/rob/octoplex/internal/domain"
|
||||
|
||||
type Name string
|
||||
|
||||
const (
|
||||
EventNameAppStateChanged Name = "app_state_changed"
|
||||
EventNameDestinationAdded Name = "destination_added"
|
||||
EventNameDestinationRemoved Name = "destination_removed"
|
||||
EventNameMediaServerStarted Name = "media_server_started"
|
||||
EventNameFatalErrorOccurred Name = "fatal_error_occurred"
|
||||
)
|
||||
|
||||
// Event represents something which happened in the appllication.
|
||||
type Event interface {
|
||||
name() Name
|
||||
}
|
||||
|
||||
// AppStateChangedEvent is emitted when the application state changes.
|
||||
type AppStateChangedEvent struct {
|
||||
State domain.AppState
|
||||
}
|
||||
|
||||
func (e AppStateChangedEvent) name() Name {
|
||||
return EventNameAppStateChanged
|
||||
}
|
||||
|
||||
// DestinationAddedEvent is emitted when a destination is successfully added.
|
||||
type DestinationAddedEvent struct {
|
||||
URL string
|
||||
|
@ -279,6 +279,7 @@ func (ui *UI) C() <-chan domain.Command {
|
||||
func (ui *UI) run(ctx context.Context) {
|
||||
defer close(ui.commandC)
|
||||
|
||||
appStateChangedC := ui.eventBus.Register(event.EventNameAppStateChanged)
|
||||
destinationAddedC := ui.eventBus.Register(event.EventNameDestinationAdded)
|
||||
destinationRemovedC := ui.eventBus.Register(event.EventNameDestinationRemoved)
|
||||
mediaServerStartedC := ui.eventBus.Register(event.EventNameMediaServerStarted)
|
||||
@ -297,14 +298,26 @@ func (ui *UI) run(ctx context.Context) {
|
||||
|
||||
for {
|
||||
select {
|
||||
case evt := <-appStateChangedC:
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.handleAppStateChanged(evt.(event.AppStateChangedEvent))
|
||||
})
|
||||
case evt := <-destinationAddedC:
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.handleDestinationAdded(evt.(event.DestinationAddedEvent))
|
||||
})
|
||||
case evt := <-destinationRemovedC:
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.handleDestinationRemoved(evt.(event.DestinationRemovedEvent))
|
||||
})
|
||||
case evt := <-mediaServerStartedC:
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.handleMediaServerStarted(evt.(event.MediaServerStartedEvent))
|
||||
})
|
||||
case evt := <-fatalErrorOccurredC:
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.handleFatalErrorOccurred(evt.(event.FatalErrorOccurredEvent))
|
||||
})
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-uiDone:
|
||||
@ -319,7 +332,7 @@ func (ui *UI) handleMediaServerStarted(evt event.MediaServerStartedEvent) {
|
||||
ui.rtmpsURL = evt.RTMPSURL
|
||||
ui.mu.Unlock()
|
||||
|
||||
ui.app.QueueUpdateDraw(ui.renderAboutView)
|
||||
ui.renderAboutView()
|
||||
}
|
||||
|
||||
func (ui *UI) inputCaptureHandler(event *tcell.EventKey) *tcell.EventKey {
|
||||
@ -447,7 +460,6 @@ func (ui *UI) ShowDestinationErrorModal(name string, err error) {
|
||||
}
|
||||
|
||||
func (ui *UI) handleFatalErrorOccurred(evt event.FatalErrorOccurredEvent) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.showModal(
|
||||
pageNameModalFatalError,
|
||||
fmt.Sprintf(
|
||||
@ -460,7 +472,6 @@ func (ui *UI) handleFatalErrorOccurred(evt event.FatalErrorOccurredEvent) {
|
||||
ui.commandC <- domain.CommandQuit{}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func (ui *UI) afterDrawHandler(screen tcell.Screen) {
|
||||
@ -491,8 +502,9 @@ func (ui *UI) captureScreen(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the state of the terminal user interface.
|
||||
func (ui *UI) SetState(state domain.AppState) {
|
||||
func (ui *UI) handleAppStateChanged(evt event.AppStateChangedEvent) {
|
||||
state := evt.State
|
||||
|
||||
if state.Source.ExitReason != "" {
|
||||
ui.handleMediaServerClosed(state.Source.ExitReason)
|
||||
}
|
||||
@ -507,10 +519,7 @@ func (ui *UI) SetState(state domain.AppState) {
|
||||
ui.hasDestinations = len(state.Destinations) > 0
|
||||
ui.mu.Unlock()
|
||||
|
||||
// The state is mutable so can't be passed into QueueUpdateDraw, which
|
||||
// passes it to another goroutine, without cloning it first.
|
||||
stateClone := state.Clone()
|
||||
ui.app.QueueUpdateDraw(func() { ui.redrawFromState(stateClone) })
|
||||
ui.redrawFromState(state)
|
||||
}
|
||||
|
||||
func (ui *UI) updatePullProgress(state domain.AppState) {
|
||||
@ -531,9 +540,7 @@ func (ui *UI) updatePullProgress(state domain.AppState) {
|
||||
}
|
||||
|
||||
if len(pullingContainers) == 0 {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.hideModal(pageNameModalPullProgress)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@ -547,7 +554,6 @@ func (ui *UI) updatePullProgress(state domain.AppState) {
|
||||
}
|
||||
|
||||
func (ui *UI) updateProgressModal(container domain.Container) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
modalName := string(pageNameModalPullProgress)
|
||||
|
||||
var status string
|
||||
@ -569,7 +575,6 @@ func (ui *UI) updateProgressModal(container domain.Container) {
|
||||
} else {
|
||||
ui.pages.AddPage(modalName, ui.pullProgressModal, true, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// page names represent a specific page in the terminal user interface.
|
||||
@ -699,7 +704,6 @@ func (ui *UI) hideModal(pageName string) {
|
||||
}
|
||||
|
||||
func (ui *UI) handleMediaServerClosed(exitReason string) {
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
if ui.pages.HasPage(pageNameModalSourceError) {
|
||||
return
|
||||
}
|
||||
@ -715,7 +719,6 @@ func (ui *UI) handleMediaServerClosed(exitReason string) {
|
||||
modal.SetBorderStyle(tcell.StyleDefault.Background(tcell.ColorBlack).Foreground(tcell.ColorWhite))
|
||||
|
||||
ui.pages.AddPage(pageNameModalSourceError, modal, true, true)
|
||||
})
|
||||
}
|
||||
|
||||
const dash = "—"
|
||||
@ -969,15 +972,13 @@ func (ui *UI) handleDestinationAdded(event.DestinationAddedEvent) {
|
||||
ui.hasDestinations = true
|
||||
ui.mu.Unlock()
|
||||
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.pages.HidePage(pageNameNoDestinations)
|
||||
ui.closeAddDestinationForm()
|
||||
ui.selectLastDestination()
|
||||
})
|
||||
}
|
||||
|
||||
func (ui *UI) handleDestinationRemoved(event.DestinationRemovedEvent) {
|
||||
ui.app.QueueUpdateDraw(ui.selectPreviousDestination)
|
||||
ui.selectPreviousDestination()
|
||||
}
|
||||
|
||||
func (ui *UI) closeAddDestinationForm() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user