Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: overlay events #139

Draft
wants to merge 2 commits into
base: refactor/modals
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
cobra.CheckErr(err)

// Construct the TUI Model from the State
m, err := ui.NewViewportViewModel(state, client)
m, err := ui.NewViewportViewModel(state)

Check warning on line 136 in cmd/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/root.go#L136

Added line #L136 was not covered by tests
cobra.CheckErr(err)

// Construct the TUI Application
Expand Down
8 changes: 6 additions & 2 deletions ui/app/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
)

// AccountSelected is a type alias for algod.Account, representing a selected account during application runtime.
type AccountSelected algod.Account
type AccountSelected *algod.Account

// EmitAccountSelected waits for and retrieves a new set of table rows from a given channel.
func EmitAccountSelected(account algod.Account) tea.Cmd {
func EmitAccountSelected(account *algod.Account) tea.Cmd {
// Do nothing when there is no account
if account == nil {
return nil
}

Check warning on line 16 in ui/app/accounts.go

View check run for this annotation

Codecov / codecov/patch

ui/app/accounts.go#L15-L16

Added lines #L15 - L16 were not covered by tests
return func() tea.Msg {
return AccountSelected(account)
}
Expand Down
12 changes: 3 additions & 9 deletions ui/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,17 @@ func Test_GenerateCmd(t *testing.T) {
client := test.GetClient(false)
fn := GenerateCmd("ABC", participation.TimeRange, int(time.Second*60), uitest.GetState(client))
res := fn()
evt, ok := res.(ModalEvent)
_, ok := res.(KeySelectedEvent)
if !ok {
t.Error("Expected ModalEvent")
}
if evt.Type != InfoModal {
t.Error("Expected InfoModal")
}

client = test.GetClient(true)
fn = GenerateCmd("ABC", participation.TimeRange, int(time.Second*60), uitest.GetState(client))
res = fn()
evt, ok = res.(ModalEvent)
_, ok = res.(error)
if !ok {
t.Error("Expected ModalEvent")
}
if evt.Type != ExceptionModal {
t.Error("Expected ExceptionModal")
t.Error("Expected error")
}

}
Expand Down
35 changes: 23 additions & 12 deletions ui/app/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,11 @@ func GenerateCmd(account string, rangeType participation.RangeType, duration int

key, err := participation.GenerateKeys(state.Context, state.Client, account, &params)
if err != nil {
return ModalEvent{
Key: nil,
Address: "",
Active: false,
Err: err,
Type: ExceptionModal,
}
return err
}

return ModalEvent{
Key: key,
Address: key.Address,
return KeySelectedEvent{
Key: key,
Prefix: lipgloss.JoinVertical(
lipgloss.Left,
"Participation keys generated.",
Expand All @@ -77,9 +70,27 @@ func GenerateCmd(account string, rangeType participation.RangeType, duration int
"",
),
Active: false,
Err: nil,
Type: InfoModal,
}
}

}

// KeySelectedEvent represents an event triggered in the modal system.
type KeySelectedEvent struct {

// Key represents a participation key associated with the modal event.
Key *api.ParticipationKey

// Active indicates whether key is Online or not.
Active bool

// Prefix adds prefix message to info modal
Prefix string
}

// EmitKeySelectedEvent creates a command that emits a ModalEvent as a message in the Tea framework.
func EmitKeySelectedEvent(event KeySelectedEvent) tea.Cmd {
return func() tea.Msg {
return event
}
}
36 changes: 0 additions & 36 deletions ui/app/modal.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package app

import (
"github.com/algorandfoundation/nodekit/api"
tea "github.com/charmbracelet/bubbletea"
)

Expand All @@ -10,12 +9,6 @@ type ModalType string

const (

// CloseModal represents an event or type used to close the currently active modal in the application.
CloseModal ModalType = ""

// CancelModal is a constant representing the type for modals used to indicate cancellation events in the application.
CancelModal ModalType = "cancel"

// InfoModal indicates a modal type used for displaying informational messages or content in the application.
InfoModal ModalType = "info"

Expand All @@ -38,32 +31,3 @@ func EmitShowModal(modal ModalType) tea.Cmd {
return modal
}
}

// ModalEvent represents an event triggered in the modal system.
type ModalEvent struct {

// Key represents a participation key associated with the modal event.
Key *api.ParticipationKey

// Active indicates whether key is Online or not.
Active bool

// Address represents the address associated with the modal event. It is used to identify the relevant account or key.
Address string

// Prefix adds prefix message to info modal
Prefix string

// Err is an error that represents an exceptional condition or failure state for the modal event.
Err error

// Type represents the specific category or variant of the modal event.
Type ModalType
}

// EmitModalEvent creates a command that emits a ModalEvent as a message in the Tea framework.
func EmitModalEvent(event ModalEvent) tea.Cmd {
return func() tea.Msg {
return event
}
}
22 changes: 22 additions & 0 deletions ui/app/overlay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app

import tea "github.com/charmbracelet/bubbletea"

type OverlayEventType string

const (
OverlayEventClose OverlayEventType = "close"
OverlayEventCancel OverlayEventType = "cancel"
)

func EmitCloseOverlay() tea.Cmd {
return func() tea.Msg {
return OverlayEventClose
}
}

func EmitCancelOverlay() tea.Cmd {
return func() tea.Msg {
return OverlayEventCancel
}
}
14 changes: 6 additions & 8 deletions ui/modals/confirm/confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,16 @@ func (m ViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// HandleMessage processes incoming messages, updates ViewModel state, and returns the updated model alongside a command.
func (m ViewModel) HandleMessage(msg tea.Msg) (ViewModel, tea.Cmd) {
switch msg := msg.(type) {
// Handle Confirmation Dialog Delete Finished
case app.DeleteFinished:
return m, app.EmitCloseOverlay()
case tea.KeyMsg:
switch msg.String() {
case "esc", "n":
return m, app.EmitModalEvent(app.ModalEvent{
Type: app.CancelModal,
})
return m, app.EmitCancelOverlay()
case "y":
var (
cmds []tea.Cmd
)
cmds = append(cmds, app.EmitDeleteKey(m.State.Context, m.State.Client, m.Participation.Id))
return m, tea.Batch(cmds...)
// Emit the delete request
return m, app.EmitDeleteKey(m.State.Context, m.State.Client, m.Participation.Id)
}
case tea.WindowSizeMsg:
m.Width = msg.Width
Expand Down
63 changes: 40 additions & 23 deletions ui/modals/exception/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,13 @@ type ViewModel struct {
Height int
Width int
Message string

Title string
BorderColor string
Controls string
Navigation string
}

func New(message string) *ViewModel {
return &ViewModel{
Height: 0,
Width: 0,
Message: message,
Title: "Error",
BorderColor: "1",
Controls: "( esc )",
Navigation: "",
func New(message string) ViewModel {
return ViewModel{
Height: 0,
Width: 0,
Message: message,
}
}

Expand All @@ -39,28 +30,54 @@ func (m ViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.HandleMessage(msg)
}

func (m ViewModel) HandleMessage(msg tea.Msg) (*ViewModel, tea.Cmd) {
func (m ViewModel) HandleMessage(msg tea.Msg) (ViewModel, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
// Handle errors make ensure the modal is visible
case error:
m.Message = msg.Error()
return m, app.EmitShowModal(app.ExceptionModal)
case tea.KeyMsg:
switch msg.String() {
case "esc":
return &m, app.EmitModalEvent(app.ModalEvent{
Type: app.CancelModal,
})
return m, app.EmitCloseOverlay()

}
case tea.WindowSizeMsg:
borderRender := style.Border.Render("")
m.Width = max(0, msg.Width-lipgloss.Width(borderRender))
m.Height = max(0, msg.Height-lipgloss.Height(borderRender))
m.Width = msg.Width
m.Height = msg.Height
}

return &m, cmd
return m, cmd
}

func (m ViewModel) View() string {
func (m ViewModel) Title() string {
return "Error"
}
func (m ViewModel) BorderColor() string {
return "1"
}
func (m ViewModel) Controls() string {
return "( esc )"
}
func (m ViewModel) Body() string {
return ansi.Hardwrap(style.Red.Render(m.Message), m.Width, false)
}

// View renders the ViewModel as a styled string, incorporating title, controls, and body content with dynamic borders.
func (m ViewModel) View() string {
body := m.Body()
width := lipgloss.Width(body)
height := lipgloss.Height(body)
return style.WithNavigation(
m.Controls(),
style.WithTitle(
m.Title(),
// Apply the Borders with the Padding
style.ApplyBorder(width+2, height+2, m.BorderColor()).
Padding(1).
Render(m.Body()),
),
)

}
6 changes: 5 additions & 1 deletion ui/modals/exception/testdata/Test_Snapshot/Visible.golden
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
Something went wrong
╭──Error───────────────╮
│ │
│ Something went wrong │
│ │
╰───────────( esc )────╯
9 changes: 1 addition & 8 deletions ui/modals/generate/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,14 @@ func (m ViewModel) HandleMessage(msg tea.Msg) (ViewModel, tea.Cmd) {
if msg.Address != m.Address {
m.Reset(msg.Address)
}
// Event triggered the Generate Modal
case app.ModalEvent:
if msg.Type == app.GenerateModal {
m.Reset(msg.Address)
}
case tea.WindowSizeMsg:
m.Width = msg.Width
m.Height = msg.Height
case tea.KeyMsg:
switch msg.String() {
case "esc":
if m.Step != WaitingStep {
return m, app.EmitModalEvent(app.ModalEvent{
Type: app.CancelModal,
})
return m, app.EmitCancelOverlay()
}
case "s":
if m.Step == DurationStep {
Expand Down
10 changes: 1 addition & 9 deletions ui/modals/info/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,10 @@ func (m ViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
func (m ViewModel) HandleMessage(msg tea.Msg) (ViewModel, tea.Cmd) {
switch msg := msg.(type) {
case app.ModalEvent:
if msg.Type == app.InfoModal {
m.Prefix = msg.Prefix
m.Participation = msg.Key
m.OfflineControls = msg.Active
}
case tea.KeyMsg:
switch msg.String() {
case "esc":
return m, app.EmitModalEvent(app.ModalEvent{
Type: app.CancelModal,
})
return m, app.EmitCloseOverlay()
case "d":
if !m.OfflineControls {
return m, app.EmitShowModal(app.ConfirmModal)
Expand Down
Loading
Loading