Przeglądaj źródła

refactor(ui,picker)!: upgrade tview to latest and use newer api (#771)

ayn2op 1 miesiąc temu
rodzic
commit
6167d7d3d8

+ 1 - 1
go.mod

@@ -11,7 +11,7 @@ require (
 	github.com/alecthomas/chroma/v2 v2.23.1
 	github.com/andybalholm/brotli v1.2.0
 	github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97
-	github.com/ayn2op/tview v0.0.0-20260315074416-50a7d9c11304
+	github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2
 	github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
 	github.com/diamondburned/arikawa/v3 v3.6.1-0.20260311205148-176ad9b9440f
 	github.com/diamondburned/ningen/v3 v3.0.1-0.20260306213430-5a08d3a709b4

+ 2 - 2
go.sum

@@ -16,8 +16,8 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwTo
 github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
 github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97 h1:WujETUV+v0DEJyZgjeLzQvihWyL80c0Tg4qf0dDo+Io=
 github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97/go.mod h1:3kFnpNCa3dF6WryzOMCDao7PfZ7DTCh+pievlfuwV80=
-github.com/ayn2op/tview v0.0.0-20260315074416-50a7d9c11304 h1:IbClNDuGoPrJtP4WkKr/iLXIxsR1WKSSJH46wLM0azM=
-github.com/ayn2op/tview v0.0.0-20260315074416-50a7d9c11304/go.mod h1:IXZrCv1VAZyWPdKDUJYs9ZYFiJ1GmCQUo9tLjQWoSnU=
+github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2 h1:AMOGrvQ1+TnDRuKC2Jnr+H1WL4bq8GSNd2GDxvy3dig=
+github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2/go.mod h1:IXZrCv1VAZyWPdKDUJYs9ZYFiJ1GmCQUo9tLjQWoSnU=
 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=

+ 1 - 2
internal/ui/chat/attachments_picker.go

@@ -7,7 +7,6 @@ import (
 	"github.com/ayn2op/tview"
 	"github.com/ayn2op/tview/help"
 	"github.com/ayn2op/tview/keybind"
-	"github.com/gdamore/tcell/v3"
 )
 
 type attachmentItem struct {
@@ -74,7 +73,7 @@ func (ap *attachmentsPicker) close() {
 	ap.chatView.app.SetFocus(ap.chatView.messagesList)
 }
 
-func (ap *attachmentsPicker) HandleEvent(event tcell.Event) tview.Command {
+func (ap *attachmentsPicker) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *picker.SelectedEvent:
 		index, ok := event.Reference.(int)

+ 1 - 2
internal/ui/chat/channels_picker.go

@@ -11,7 +11,6 @@ import (
 	"github.com/ayn2op/tview/help"
 	"github.com/ayn2op/tview/keybind"
 	"github.com/diamondburned/arikawa/v3/discord"
-	"github.com/gdamore/tcell/v3"
 )
 
 type channelsPicker struct {
@@ -52,7 +51,7 @@ func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker {
 	return cp
 }
 
-func (cp *channelsPicker) HandleEvent(event tcell.Event) tview.Command {
+func (cp *channelsPicker) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *picker.SelectedEvent:
 		channelID, ok := event.Reference.(discord.ChannelID)

+ 3 - 3
internal/ui/chat/events.go

@@ -10,7 +10,7 @@ import (
 type LogoutEvent struct{ tcell.EventTime }
 
 func (m *Model) logout() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		return &LogoutEvent{}
 	}
 }
@@ -18,7 +18,7 @@ func (m *Model) logout() tview.Command {
 type QuitEvent struct{ tcell.EventTime }
 
 func (m *Model) closeState() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if m.state != nil {
 			if err := m.state.Close(); err != nil {
 				slog.Error("failed to close the session", "err", err)
@@ -35,7 +35,7 @@ type closeLayerEvent struct {
 }
 
 func closeLayer(name string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		return &closeLayerEvent{name: name}
 	}
 }

+ 2 - 2
internal/ui/chat/guilds_tree.go

@@ -393,7 +393,7 @@ func (gt *guildsTree) collapseParentNode(node *tview.TreeNode) {
 		})
 }
 
-func (gt *guildsTree) HandleEvent(event tcell.Event) tview.Command {
+func (gt *guildsTree) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.TreeViewSelectedEvent:
 		gt.onSelected(event.Node)
@@ -437,7 +437,7 @@ func (gt *guildsTree) yankID() tview.Command {
 	// Reference of a tree node in the guilds tree is its ID.
 	// discord.Snowflake (discord.GuildID and discord.ChannelID) have the String method.
 	if id, ok := node.GetReference().(fmt.Stringer); ok {
-		return func() tcell.Event {
+		return func() tview.Event {
 			if err := clipboard.Write(clipboard.FmtText, []byte(id.String())); err != nil {
 				slog.Error("failed to copy node id", "err", err)
 			}

+ 1 - 1
internal/ui/chat/message_input.go

@@ -103,7 +103,7 @@ func (mi *messageInput) stopTypingTimer() {
 	}
 }
 
-func (mi *messageInput) HandleEvent(event tcell.Event) tview.Command {
+func (mi *messageInput) HandleEvent(event tview.Event) tview.Command {
 	handler := mi.TextArea.HandleEvent
 	switch event := event.(type) {
 	case *tview.KeyEvent:

+ 5 - 5
internal/ui/chat/messages_list.go

@@ -838,7 +838,7 @@ func (ml *messagesList) selectedMessage() (*discord.Message, error) {
 	return &ml.messages[cursor], nil
 }
 
-func (ml *messagesList) HandleEvent(event tcell.Event) tview.Command {
+func (ml *messagesList) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.KeyEvent:
 		switch {
@@ -1018,7 +1018,7 @@ func (ml *messagesList) yankMessageID() tview.Command {
 		return nil
 	}
 
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := clipboard.Write(clipboard.FmtText, []byte(msg.ID.String())); err != nil {
 			slog.Error("failed to copy message id", "err", err)
 		}
@@ -1033,7 +1033,7 @@ func (ml *messagesList) yankContent() tview.Command {
 		return nil
 	}
 
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := clipboard.Write(clipboard.FmtText, []byte(msg.Content)); err != nil {
 			slog.Error("failed to copy message content", "err", err)
 		}
@@ -1048,7 +1048,7 @@ func (ml *messagesList) yankURL() tview.Command {
 		return nil
 	}
 
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := clipboard.Write(clipboard.FmtText, []byte(msg.URL())); err != nil {
 			slog.Error("failed to copy message url", "err", err)
 		}
@@ -1289,7 +1289,7 @@ func (ml *messagesList) deleteSelectedMessage() tview.Command {
 		return nil
 	}
 
-	return func() tcell.Event {
+	return func() tview.Event {
 		if selectedMessage.GuildID.IsValid() {
 			me, _ := ml.chatView.state.Cabinet.Me()
 			if selectedMessage.Author.ID != me.ID && !ml.chatView.state.HasPermissions(selectedMessage.ChannelID, discord.PermissionManageMessages) {

+ 3 - 3
internal/ui/chat/model.go

@@ -48,7 +48,7 @@ type Model struct {
 	typers   map[discord.UserID]*time.Timer
 
 	confirmModalDone          func(label string)
-	confirmModalPreviousFocus tview.Primitive
+	confirmModalPreviousFocus tview.Model
 
 	app   *tview.Application
 	cfg   *config.Config
@@ -216,10 +216,10 @@ func (m *Model) focusNext() tview.Command {
 	return nil
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.InitEvent:
-		return func() tcell.Event {
+		return func() tview.Event {
 			if err := m.OpenState(m.token); err != nil {
 				slog.Error("failed to open chat state", "err", err)
 				return tcell.NewEventError(err)

+ 1 - 1
internal/ui/login/events.go

@@ -9,7 +9,7 @@ import (
 )
 
 func setClipboard(content string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := clipboard.Write(clipboard.FmtText, []byte(content)); err != nil {
 			slog.Error("failed to copy error message", "err", err)
 			return tcell.NewEventError(err)

+ 1 - 1
internal/ui/login/model.go

@@ -41,7 +41,7 @@ func NewModel(cfg *config.Config) *Model {
 	}
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tcell.EventError:
 		if m.HasLayer(errorLayerName) {

+ 11 - 11
internal/ui/login/qr/events.go

@@ -34,7 +34,7 @@ type connCreateEvent struct {
 type connCloseEvent struct{ tcell.EventTime }
 
 func (m *Model) connect() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		headers := http.Headers()
 		headers.Set("User-Agent", http.BrowserUserAgent)
 		conn, _, err := websocket.DefaultDialer.Dial(remoteAuthGatewayURL, headers)
@@ -46,7 +46,7 @@ func (m *Model) connect() tview.Command {
 }
 
 func (m *Model) close() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if m.conn != nil {
 			if err := m.conn.Close(); err != nil {
 				return tcell.NewEventError(err)
@@ -85,7 +85,7 @@ type pendingLoginEvent struct {
 type cancelEvent struct{ tcell.EventTime }
 
 func (m *Model) listen() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if m.conn == nil {
 			return nil
 		}
@@ -155,14 +155,14 @@ func (m *Model) listen() tview.Command {
 type heartbeatTickEvent struct{ tcell.EventTime }
 
 func (m *Model) heartbeat() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		time.Sleep(m.heartbeatInterval)
 		return &heartbeatTickEvent{}
 	}
 }
 
 func (m *Model) sendHeartbeat() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if m.conn == nil {
 			return nil
 		}
@@ -182,7 +182,7 @@ type privateKeyEvent struct {
 }
 
 func (m *Model) generatePrivateKey() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
 		if err != nil {
 			return tcell.NewEventError(err)
@@ -192,7 +192,7 @@ func (m *Model) generatePrivateKey() tview.Command {
 }
 
 func (m *Model) sendInit() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if m.privateKey == nil {
 			return tcell.NewEventError(errors.New("missing private key"))
 		}
@@ -213,7 +213,7 @@ func (m *Model) sendInit() tview.Command {
 }
 
 func (m *Model) sendNonceProof(encryptedNonce string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		decodedNonce, err := base64.StdEncoding.DecodeString(encryptedNonce)
 		if err != nil {
 			return tcell.NewEventError(err)
@@ -242,7 +242,7 @@ type qrCodeEvent struct {
 }
 
 func (m *Model) generateQRCode(fingerprint string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		content := "https://discord.com/ra/" + fingerprint
 		qrCode, err := qrcode.New(content, qrcode.Low)
 		if err != nil {
@@ -260,7 +260,7 @@ type userEvent struct {
 }
 
 func (m *Model) decryptUserPayload(encryptedPayload string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		decodedPayload, err := base64.StdEncoding.DecodeString(encryptedPayload)
 		if err != nil {
 			return tcell.NewEventError(err)
@@ -281,7 +281,7 @@ func (m *Model) decryptUserPayload(encryptedPayload string) tview.Command {
 }
 
 func (m *Model) exchangeTicket(ticket string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		headers := http.Headers()
 		headers.Set("Referer", "https://discord.com/login")
 		if m.fingerprint != "" {

+ 2 - 2
internal/ui/login/qr/model.go

@@ -44,7 +44,7 @@ func (m *Model) Label() string {
 	return "QR"
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.InitEvent:
 		m.msg = "Connecting to Remote Auth Gateway..."
@@ -103,7 +103,7 @@ func (m *Model) HandleEvent(event tcell.Event) tview.Command {
 
 	case *tcell.EventError:
 		m.msg = event.Error()
-		return tview.Batch(m.close(), tview.Command(func() tcell.Event { return event }))
+		return tview.Batch(m.close(), tview.Command(func() tview.Event { return event }))
 	}
 
 	return nil

+ 1 - 1
internal/ui/login/token/events.go

@@ -11,7 +11,7 @@ type TokenEvent struct {
 }
 
 func tokenCommand(token string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		return &TokenEvent{Token: token}
 	}
 }

+ 1 - 2
internal/ui/login/token/model.go

@@ -3,7 +3,6 @@ package token
 import (
 	"github.com/ayn2op/tview"
 	"github.com/ayn2op/tview/tabs"
-	"github.com/gdamore/tcell/v3"
 )
 
 type Model struct {
@@ -23,7 +22,7 @@ func (m *Model) Label() string {
 	return "Token"
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event.(type) {
 	case *tview.FormSubmitEvent:
 		token := m.GetFormItem(0).(*tview.InputField).GetText()

+ 5 - 5
internal/ui/root/events.go

@@ -15,7 +15,7 @@ type tokenEvent struct {
 }
 
 func tokenCommand(token string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		return &tokenEvent{token: token}
 	}
 }
@@ -23,7 +23,7 @@ func tokenCommand(token string) tview.Command {
 type loginEvent struct{ tcell.EventTime }
 
 func getToken() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		token, err := keyring.GetToken()
 		if err != nil {
 			slog.Info("failed to retrieve token from keyring", "err", err)
@@ -34,7 +34,7 @@ func getToken() tview.Command {
 }
 
 func setToken(token string) tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := keyring.SetToken(token); err != nil {
 			slog.Error("failed to set token to keyring", "err", err)
 			return tcell.NewEventError(err)
@@ -44,7 +44,7 @@ func setToken(token string) tview.Command {
 }
 
 func deleteToken() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := keyring.DeleteToken(); err != nil {
 			slog.Error("failed to delete token from keyring", "err", err)
 			return tcell.NewEventError(err)
@@ -54,7 +54,7 @@ func deleteToken() tview.Command {
 }
 
 func initClipboard() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		if err := clipboard.Init(); err != nil {
 			slog.Error("failed to init clipboard", "err", err)
 			return tcell.NewEventError(err)

+ 4 - 4
internal/ui/root/model.go

@@ -21,7 +21,7 @@ const tokenEnvVarKey = "DISCORDO_TOKEN"
 type Model struct {
 	app      *tview.Application
 	rootFlex *flex.Model // inner + help
-	inner    tview.Primitive
+	inner    tview.Model
 	help     *help.Help
 
 	cfg *config.Config
@@ -74,13 +74,13 @@ func (m *Model) buildLayout() {
 	m.updateHelpHeight()
 }
 
-var _ tview.Primitive = (*Model)(nil)
+var _ tview.Model = (*Model)(nil)
 
 func (m *Model) Draw(screen tcell.Screen) {
 	m.rootFlex.Draw(screen)
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.InitEvent:
 		var cmd tview.Command
@@ -151,7 +151,7 @@ func (m *Model) SetRect(x int, y int, width int, height int) {
 	m.rootFlex.SetRect(x, y, width, height)
 }
 
-func (m *Model) Focus(delegate func(p tview.Primitive)) {
+func (m *Model) Focus(delegate func(tview.Model)) {
 	if m.inner != nil {
 		delegate(m.inner)
 	}

+ 2 - 2
internal/ui/util.go

@@ -55,11 +55,11 @@ func ConfigureBox(box *tview.Box, cfg *config.Theme) *tview.Box {
 }
 
 // Centered creates a new grid with provided primitive aligned in the center.
-func Centered(p tview.Primitive, width, height int) tview.Primitive {
+func Centered(m tview.Model, width, height int) tview.Model {
 	return tview.NewGrid().
 		SetColumns(0, width, 0).
 		SetRows(0, height, 0).
-		AddItem(p, 1, 1, 1, 1, 0, 0, true)
+		AddItem(m, 1, 1, 1, 1, 0, 0, true)
 }
 
 func ChannelToString(channel discord.Channel, icons config.Icons, state *ningen.State) string {

+ 2 - 2
pkg/picker/events.go

@@ -18,7 +18,7 @@ func (m *Model) _select() tview.Command {
 	index := m.list.Cursor()
 	if index >= 0 && index < len(m.filtered) {
 		item := m.filtered[index]
-		return func() tcell.Event {
+		return func() tview.Event {
 			return newSelectedEvent(item)
 		}
 	}
@@ -28,7 +28,7 @@ func (m *Model) _select() tview.Command {
 type CancelEvent struct{ tcell.EventTime }
 
 func cancel() tview.Command {
-	return func() tcell.Event {
+	return func() tview.Event {
 		return &CancelEvent{}
 	}
 }

+ 1 - 1
pkg/picker/model.go

@@ -128,7 +128,7 @@ func (m *Model) onInputChanged(text string) {
 	m.setFilteredItems(fuzzied)
 }
 
-func (m *Model) HandleEvent(event tcell.Event) tview.Command {
+func (m *Model) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.KeyEvent:
 		if m.keyMap != nil {