WIP: refactor/api #1
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
@ -90,7 +89,7 @@ func run() error {
|
||||
}
|
||||
})
|
||||
|
||||
ui, err := terminal.StartUI(ctx, terminal.StartParams{
|
||||
ui, err := terminal.NewUI(ctx, terminal.Params{
|
||||
EventBus: bus,
|
||||
Dispatcher: func(cmd event.Command) {
|
||||
logger.Info("Command dispatched", "cmd", cmd.Name())
|
||||
@ -112,18 +111,11 @@ func run() error {
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
errUIClosed := errors.New("UI closed")
|
||||
g.Go(func() error {
|
||||
ui.Wait()
|
||||
logger.Info("UI closed!")
|
||||
return errUIClosed
|
||||
})
|
||||
g.Go(func() error { return ui.Run(ctx) })
|
||||
|
||||
if err := g.Wait(); err == errUIClosed {
|
||||
logger.Info("UI closed, exiting")
|
||||
if err := g.Wait(); err == terminal.ErrUserClosed {
|
||||
return nil
|
||||
} else {
|
||||
logger.Error("UI closed with error", "err", err)
|
||||
return fmt.Errorf("errgroup.Wait: %w", err)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package terminal
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
@ -46,7 +47,7 @@ type UI struct {
|
||||
clipboardAvailable bool
|
||||
rtmpURL, rtmpsURL string
|
||||
buildInfo domain.BuildInfo
|
||||
doneC chan struct{}
|
||||
appExitC chan error
|
||||
logger *slog.Logger
|
||||
|
||||
// tview state
|
||||
@ -92,9 +93,9 @@ type ScreenCapture struct {
|
||||
Width, Height int
|
||||
}
|
||||
|
||||
// StartParams contains the parameters for starting a new terminal user
|
||||
// Params contains the parameters for starting a new terminal user
|
||||
// interface.
|
||||
type StartParams struct {
|
||||
type Params struct {
|
||||
EventBus *event.Bus
|
||||
Dispatcher func(event.Command)
|
||||
Logger *slog.Logger
|
||||
@ -103,8 +104,9 @@ type StartParams struct {
|
||||
Screen *Screen // Screen may be nil.
|
||||
}
|
||||
|
||||
// StartUI starts the terminal user interface.
|
||||
func StartUI(ctx context.Context, params StartParams) (*UI, error) {
|
||||
// NewUI creates the user interface. Call [Run] on the *UI instance to block
|
||||
// until it is completed.
|
||||
func NewUI(ctx context.Context, params Params) (*UI, error) {
|
||||
app := tview.NewApplication()
|
||||
|
||||
var screen tcell.Screen
|
||||
@ -210,7 +212,7 @@ func StartUI(ctx context.Context, params StartParams) (*UI, error) {
|
||||
eventBus: params.EventBus,
|
||||
dispatch: params.Dispatcher,
|
||||
clipboardAvailable: params.ClipboardAvailable,
|
||||
doneC: make(chan struct{}, 1),
|
||||
appExitC: make(chan error, 1),
|
||||
buildInfo: params.BuildInfo,
|
||||
logger: params.Logger,
|
||||
app: app,
|
||||
@ -236,8 +238,6 @@ func StartUI(ctx context.Context, params StartParams) (*UI, error) {
|
||||
app.SetInputCapture(ui.inputCaptureHandler)
|
||||
app.SetAfterDrawFunc(ui.afterDrawHandler)
|
||||
|
||||
go ui.run(ctx)
|
||||
|
||||
return ui, nil
|
||||
}
|
||||
|
||||
@ -264,23 +264,28 @@ func (ui *UI) renderAboutView() {
|
||||
ui.aboutView.AddItem(tview.NewTextView().SetDynamicColors(true).SetText("[grey]?[-] About"), 1, 0, false)
|
||||
}
|
||||
|
||||
func (ui *UI) run(ctx context.Context) {
|
||||
var ErrUserClosed = errors.New("user closed UI")
|
||||
|
||||
// Run runs the user interface. It always returns a non-nil error, which will
|
||||
// be [ErrUserClosed] if the user voluntarily closed the UI.
|
||||
func (ui *UI) Run(ctx context.Context) error {
|
||||
eventC := ui.eventBus.Register()
|
||||
defer ui.eventBus.Deregister(eventC)
|
||||
|
||||
go func() {
|
||||
defer close(ui.doneC)
|
||||
|
||||
if err := ui.app.Run(); err != nil {
|
||||
err := ui.app.Run()
|
||||
if err != nil {
|
||||
ui.logger.Error("Error in UI run loop, exiting", "err", err)
|
||||
}
|
||||
ui.appExitC <- err
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case evt, ok := <-eventC:
|
||||
if !ok {
|
||||
return
|
||||
// should never happen
|
||||
return errors.New("event channel closed")
|
||||
}
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
switch evt := evt.(type) {
|
||||
@ -307,12 +312,11 @@ func (ui *UI) run(ctx context.Context) {
|
||||
default:
|
||||
ui.logger.Warn("unhandled event", "event", evt)
|
||||
}
|
||||
|
||||
})
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ui.doneC:
|
||||
return
|
||||
return ctx.Err()
|
||||
case err := <-ui.appExitC:
|
||||
return cmp.Or(err, ErrUserClosed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,7 +823,7 @@ func (ui *UI) Close() {
|
||||
|
||||
// Wait waits for the terminal user interface to finish.
|
||||
func (ui *UI) Wait() {
|
||||
<-ui.doneC
|
||||
<-ui.appExitC
|
||||
}
|
||||
|
||||
func (ui *UI) addDestination() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user