Skip to content

Commit cefce0e

Browse files
committed
Route logout through standard TUI event flow
1 parent d659e0c commit cefce0e

File tree

3 files changed

+26
-179
lines changed

3 files changed

+26
-179
lines changed

cmd/logout.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ var logoutCmd = &cobra.Command{
2323
}
2424

2525
if ui.IsInteractive() {
26-
a := auth.New(nil, platformClient, tokenStorage, false)
27-
return ui.RunLogout(cmd.Context(), version, a)
26+
return ui.RunLogout(cmd.Context(), version, platformClient, tokenStorage)
2827
}
2928

3029
// Non-interactive mode: auth emits events through the sink

internal/ui/components/spinner.go

Lines changed: 0 additions & 57 deletions
This file was deleted.

internal/ui/run_logout.go

Lines changed: 25 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -3,147 +3,52 @@ package ui
33
import (
44
"context"
55
"errors"
6-
"time"
6+
"os"
77

88
tea "github.com/charmbracelet/bubbletea"
9+
"github.com/localstack/lstk/internal/api"
910
"github.com/localstack/lstk/internal/auth"
10-
"github.com/localstack/lstk/internal/ui/components"
11-
"github.com/localstack/lstk/internal/ui/styles"
11+
"github.com/localstack/lstk/internal/output"
1212
)
1313

14-
const minSpinnerDuration = 400 * time.Millisecond
14+
func RunLogout(parentCtx context.Context, version string, platformClient api.PlatformAPI, tokenStorage auth.AuthTokenStorage) error {
15+
_, cancel := context.WithCancel(parentCtx)
16+
defer cancel()
1517

16-
type logoutSuccessMsg struct{}
17-
18-
type logoutNotLoggedInMsg struct{}
19-
20-
type logoutErrMsg struct {
21-
err error
22-
}
23-
24-
type logoutState int
25-
26-
const (
27-
logoutStateLoading logoutState = iota
28-
logoutStateSuccess
29-
logoutStateNotLoggedIn
30-
)
31-
32-
type LogoutApp struct {
33-
header components.Header
34-
spinner components.Spinner
35-
state logoutState
36-
err error
37-
}
38-
39-
func NewLogoutApp(version string) LogoutApp {
40-
return LogoutApp{
41-
header: components.NewHeader(version),
42-
spinner: components.NewSpinner().Show("Logging out"),
43-
state: logoutStateLoading,
44-
}
45-
}
46-
47-
func (a LogoutApp) Init() tea.Cmd {
48-
return a.spinner.Tick()
49-
}
50-
51-
func (a LogoutApp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
52-
switch msg := msg.(type) {
53-
case tea.KeyMsg:
54-
if msg.String() == "ctrl+c" || msg.String() == "q" {
55-
return a, tea.Quit
56-
}
57-
58-
case logoutSuccessMsg:
59-
a.spinner = a.spinner.Hide()
60-
a.state = logoutStateSuccess
61-
return a, tea.Quit
62-
63-
case logoutNotLoggedInMsg:
64-
a.spinner = a.spinner.Hide()
65-
a.state = logoutStateNotLoggedIn
66-
return a, tea.Quit
67-
68-
case logoutErrMsg:
69-
a.spinner = a.spinner.Hide()
70-
a.err = msg.err
71-
return a, tea.Quit
72-
73-
default:
74-
var cmd tea.Cmd
75-
a.spinner, cmd = a.spinner.Update(msg)
76-
return a, cmd
77-
}
78-
79-
return a, nil
80-
}
81-
82-
func (a LogoutApp) View() string {
83-
var s string
84-
s += a.header.View()
85-
s += "\n"
86-
87-
if a.spinner.Visible() {
88-
s += a.spinner.View() + "\n"
89-
}
90-
91-
switch a.state {
92-
case logoutStateSuccess:
93-
s += renderLogoutSuccess() + "\n"
94-
case logoutStateNotLoggedIn:
95-
s += renderLogoutNotLoggedIn() + "\n"
96-
}
97-
98-
return s
99-
}
100-
101-
func renderLogoutSuccess() string {
102-
prefix := styles.Secondary.Render("> ")
103-
return prefix + styles.Success.Render("Success:") + " " + styles.Message.Render("Logged out successfully.")
104-
}
105-
106-
func renderLogoutNotLoggedIn() string {
107-
prefix := styles.Secondary.Render("> ")
108-
return prefix + styles.Note.Render("Note:") + " " + styles.Message.Render("Not currently logged in.")
109-
}
110-
111-
func (a LogoutApp) Err() error {
112-
return a.err
113-
}
114-
115-
func RunLogout(ctx context.Context, version string, a *auth.Auth) error {
116-
app := NewLogoutApp(version)
117-
p := tea.NewProgram(app)
18+
app := NewApp(version, cancel)
19+
p := tea.NewProgram(app, tea.WithInput(os.Stdin), tea.WithOutput(os.Stdout))
20+
runErrCh := make(chan error, 1)
11821

11922
go func() {
120-
start := time.Now()
23+
a := auth.New(output.NewTUISink(programSender{p: p}), platformClient, tokenStorage, false)
12124
err := a.Logout()
122-
123-
// Ensure spinner is visible for minimum duration
124-
elapsed := time.Since(start)
125-
if elapsed < minSpinnerDuration {
126-
time.Sleep(minSpinnerDuration - elapsed)
127-
}
128-
12925
if errors.Is(err, auth.ErrNotLoggedIn) {
130-
p.Send(logoutNotLoggedInMsg{})
131-
return
26+
err = nil
13227
}
28+
runErrCh <- err
29+
13330
if err != nil {
134-
p.Send(logoutErrMsg{err: err})
31+
if errors.Is(err, context.Canceled) {
32+
return
33+
}
34+
p.Send(runErrMsg{err: err})
13535
return
13636
}
137-
p.Send(logoutSuccessMsg{})
37+
p.Send(runDoneMsg{})
13838
}()
13939

14040
model, err := p.Run()
14141
if err != nil {
14242
return err
14343
}
14444

145-
if logoutApp, ok := model.(LogoutApp); ok && logoutApp.Err() != nil {
146-
return logoutApp.Err()
45+
if app, ok := model.(App); ok && app.Err() != nil {
46+
return app.Err()
47+
}
48+
49+
runErr := <-runErrCh
50+
if runErr != nil && !errors.Is(runErr, context.Canceled) {
51+
return runErr
14752
}
14853

14954
return nil

0 commit comments

Comments
 (0)