ソースを参照

refactor(ui): use event-driven startup and logout

ayn2op 1 ヶ月 前
コミット
41a6833b81
7 ファイル変更87 行追加39 行削除
  1. 1 0
      cmd/root.go
  2. 1 1
      go.mod
  3. 2 2
      go.sum
  4. 11 0
      internal/ui/chat/events.go
  5. 6 13
      internal/ui/chat/view.go
  6. 49 0
      internal/ui/root/events.go
  7. 17 23
      internal/ui/root/view.go

+ 1 - 0
cmd/root.go

@@ -62,6 +62,7 @@ func Run() error {
 	screen.EnablePaste()
 	screen.EnableFocus()
 
+	tview.Styles = tview.Theme{}
 	app := tview.NewApplication()
 	app.SetRoot(root.NewView(cfg, app))
 	app.SetScreen(screen)

+ 1 - 1
go.mod

@@ -10,7 +10,7 @@ require (
 	github.com/BurntSushi/toml v1.6.0
 	github.com/alecthomas/chroma/v2 v2.23.1
 	github.com/andybalholm/brotli v1.2.0
-	github.com/ayn2op/tview v0.0.0-20260304032402-f69fad13517e
+	github.com/ayn2op/tview v0.0.0-20260304052427-7549865d186e
 	github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
 	github.com/diamondburned/arikawa/v3 v3.6.1-0.20260226015332-783a3e8e8e86
 	github.com/diamondburned/ningen/v3 v3.0.1-0.20260226220604-93f1e60c3cdb

+ 2 - 2
go.sum

@@ -14,8 +14,8 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs
 github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
 github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
-github.com/ayn2op/tview v0.0.0-20260304032402-f69fad13517e h1:xbfoet3RFg7bRY3MhRi5WfxhbLNoOlM+E+f83ABCfoM=
-github.com/ayn2op/tview v0.0.0-20260304032402-f69fad13517e/go.mod h1:lZ8RdOegQWBQafTOasGE7Ps1/Ymy4jmXoPt5vz2QsS0=
+github.com/ayn2op/tview v0.0.0-20260304052427-7549865d186e h1:fc5qHUJV+XYlYabsJtWW/AwbGLFu6XMFTiHXDViUBeM=
+github.com/ayn2op/tview v0.0.0-20260304052427-7549865d186e/go.mod h1:lZ8RdOegQWBQafTOasGE7Ps1/Ymy4jmXoPt5vz2QsS0=
 github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
 github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

+ 11 - 0
internal/ui/chat/events.go

@@ -0,0 +1,11 @@
+package chat
+
+import "github.com/gdamore/tcell/v3"
+
+type LogoutEvent struct{ tcell.EventTime }
+
+func NewLogoutEvent() *LogoutEvent {
+	event := &LogoutEvent{}
+	event.SetEventNow()
+	return event
+}

+ 6 - 13
internal/ui/chat/view.go

@@ -7,7 +7,6 @@ import (
 	"time"
 
 	"github.com/ayn2op/discordo/internal/config"
-	"github.com/ayn2op/discordo/internal/keyring"
 	"github.com/ayn2op/discordo/internal/ui"
 	"github.com/ayn2op/tview"
 	"github.com/ayn2op/tview/help"
@@ -51,11 +50,9 @@ type View struct {
 	app   *tview.Application
 	cfg   *config.Config
 	state *ningen.State
-
-	onLogout func()
 }
 
-func NewView(app *tview.Application, cfg *config.Config, onLogout func()) *View {
+func NewView(app *tview.Application, cfg *config.Config) *View {
 	v := &View{
 		Layers: layers.New(),
 
@@ -65,9 +62,8 @@ func NewView(app *tview.Application, cfg *config.Config, onLogout func()) *View
 
 		typers: make(map[discord.UserID]*time.Timer),
 
-		app:      app,
-		cfg:      cfg,
-		onLogout: onLogout,
+		app: app,
+		cfg: cfg,
 	}
 
 	v.guildsTree = newGuildsTree(cfg, v)
@@ -249,13 +245,10 @@ func (v *View) HandleEvent(event tcell.Event) tview.Command {
 			v.focusNext()
 			return redraw
 		case keybind.Matches(event, v.cfg.Keybinds.Logout.Keybind):
-			if v.onLogout != nil {
-				v.onLogout()
-			}
-			if err := keyring.DeleteToken(); err != nil {
-				slog.Error("failed to delete token from keyring", "err", err)
+			if err := v.CloseState(); err != nil {
+				slog.Error("failed to close the session", "err", err)
 			}
-			return redraw
+			return tview.EventCommand(func() tcell.Event { return NewLogoutEvent() })
 		case keybind.Matches(event, v.cfg.Keybinds.ToggleGuildsTree.Keybind):
 			v.toggleGuildsTree()
 			return redraw

+ 49 - 0
internal/ui/root/events.go

@@ -0,0 +1,49 @@
+package root
+
+import (
+	"log/slog"
+	"os"
+
+	"github.com/ayn2op/discordo/internal/clipboard"
+	"github.com/ayn2op/discordo/internal/keyring"
+	"github.com/gdamore/tcell/v3"
+)
+
+type tokenEvent struct {
+	tcell.EventTime
+	token string
+}
+
+func newTokenEvent(token string) *tokenEvent {
+	event := &tokenEvent{token: token}
+	event.SetEventNow()
+	return event
+}
+
+func getToken() tcell.Event {
+	token := os.Getenv(tokenEnvVarKey)
+	if token == "" {
+		tok, err := keyring.GetToken()
+		if err != nil {
+			slog.Info("failed to retrieve token from keyring", "err", err)
+		}
+		token = tok
+	}
+	return newTokenEvent(token)
+}
+
+func deleteToken() tcell.Event {
+	if err := keyring.DeleteToken(); err != nil {
+		slog.Error("failed to delete token from keyring", "err", err)
+		return tcell.NewEventError(err)
+	}
+	return nil
+}
+
+func initClipboard() tcell.Event {
+	if err := clipboard.Init(); err != nil {
+		slog.Error("failed to init clipboard", "err", err)
+		return tcell.NewEventError(err)
+	}
+	return nil
+}

+ 17 - 23
internal/ui/root/view.go

@@ -2,12 +2,9 @@ package root
 
 import (
 	"log/slog"
-	"os"
 
-	"github.com/ayn2op/discordo/internal/clipboard"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/consts"
-	"github.com/ayn2op/discordo/internal/keyring"
 	"github.com/ayn2op/discordo/internal/ui/chat"
 	"github.com/ayn2op/discordo/internal/ui/login"
 	"github.com/ayn2op/tview"
@@ -26,17 +23,10 @@ type View struct {
 }
 
 func NewView(cfg *config.Config, app *tview.Application) *View {
-	tview.Styles = tview.Theme{}
-	v := &View{
+	return &View{
 		app: app,
 		cfg: cfg,
 	}
-
-	if err := clipboard.Init(); err != nil {
-		slog.Error("failed to init clipboard", "err", err)
-	}
-
-	return v
 }
 
 func (v *View) showLoginView() {
@@ -45,11 +35,12 @@ func (v *View) showLoginView() {
 			slog.Error("failed to show chat view", "err", err)
 		}
 	})
+	v.chat = nil
 	v.inner = loginForm
 }
 
 func (v *View) showChatView(token string) error {
-	v.chat = chat.NewView(v.app, v.cfg, v.showLoginView)
+	v.chat = chat.NewView(v.app, v.cfg)
 	if err := v.chat.OpenState(token); err != nil {
 		return err
 	}
@@ -77,24 +68,27 @@ func (v *View) Draw(screen tcell.Screen) {
 func (v *View) HandleEvent(event tcell.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.InitEvent:
-		token := os.Getenv(tokenEnvVarKey)
-		if token == "" {
-			tok, err := keyring.GetToken()
-			if err != nil {
-				slog.Info("failed to retrieve token from keyring", "err", err)
-			}
-			token = tok
+		return tview.BatchCommand{
+			tview.SetTitleCommand(consts.Name),
+			tview.EventCommand(initClipboard),
+			tview.EventCommand(getToken),
 		}
-
-		if token == "" {
+	case *tokenEvent:
+		if event.token == "" {
 			v.showLoginView()
 		} else {
-			if err := v.showChatView(token); err != nil {
+			if err := v.showChatView(event.token); err != nil {
 				slog.Error("failed to show chat view", "err", err)
 				return tview.QuitCommand{}
 			}
 		}
-		return tview.BatchCommand{tview.SetTitleCommand(consts.Name), tview.SetFocusCommand{Target: v.inner}}
+		return tview.BatchCommand{tview.SetFocusCommand{Target: v.inner}}
+	case *chat.LogoutEvent:
+		v.showLoginView()
+		return tview.BatchCommand{
+			tview.EventCommand(deleteToken),
+			tview.SetFocusCommand{Target: v.inner},
+		}
 
 	case *tview.KeyEvent:
 		switch {