refactor(ui): dialog: unify session and model switching dialogs

Ayman Bagabas created

Change summary

internal/ui/dialog/commands.go |  4 +-
internal/ui/dialog/messages.go |  7 +++-
internal/ui/model/ui.go        | 60 +++++++++++++++++------------------
3 files changed, 36 insertions(+), 35 deletions(-)

Detailed changes

internal/ui/dialog/commands.go 🔗

@@ -324,7 +324,7 @@ func (c *Commands) defaultCommands() []uicmd.Command {
 			Description: "Switch to a different session",
 			Shortcut:    "ctrl+s",
 			Handler: func(cmd uicmd.Command) tea.Cmd {
-				return uiutil.CmdHandler(SwitchSessionsMsg{})
+				return uiutil.CmdHandler(OpenDialogMsg{SessionsID})
 			},
 		},
 		{
@@ -333,7 +333,7 @@ func (c *Commands) defaultCommands() []uicmd.Command {
 			Description: "Switch to a different model",
 			Shortcut:    "ctrl+l",
 			Handler: func(cmd uicmd.Command) tea.Cmd {
-				return uiutil.CmdHandler(SwitchModelMsg{})
+				return uiutil.CmdHandler(OpenDialogMsg{ModelsID})
 			},
 		},
 	}

internal/ui/dialog/messages.go 🔗

@@ -12,6 +12,11 @@ type CloseMsg struct{}
 // QuitMsg is a message to quit the application.
 type QuitMsg = tea.QuitMsg
 
+// OpenDialogMsg is a message to open a dialog.
+type OpenDialogMsg struct {
+	DialogID string
+}
+
 // SessionSelectedMsg is a message indicating a session has been selected.
 type SessionSelectedMsg struct {
 	Session session.Session
@@ -25,9 +30,7 @@ type ModelSelectedMsg struct {
 
 // Messages for commands
 type (
-	SwitchSessionsMsg      struct{}
 	NewSessionsMsg         struct{}
-	SwitchModelMsg         struct{}
 	OpenFilePickerMsg      struct{}
 	ToggleHelpMsg          struct{}
 	ToggleCompactModeMsg   struct{}

internal/ui/model/ui.go 🔗

@@ -64,11 +64,6 @@ type openEditorMsg struct {
 	Text string
 }
 
-// listSessionsMsg is a message to list available sessions.
-type listSessionsMsg struct {
-	sessions []session.Session
-}
-
 // UI represents the main user interface model.
 type UI struct {
 	com          *common.Common
@@ -190,10 +185,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if !m.sendProgressBar {
 			m.sendProgressBar = slices.Contains(msg, "WT_SESSION")
 		}
-	case listSessionsMsg:
-		if cmd := m.openSessionsDialog(msg.sessions); cmd != nil {
-			cmds = append(cmds, cmd)
-		}
 	case loadSessionMsg:
 		m.state = uiChat
 		m.session = msg.session
@@ -499,11 +490,8 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
 			}
 			return true
 		case key.Matches(msg, m.keyMap.Sessions):
-			if m.dialog.ContainsDialog(dialog.SessionsID) {
-				// Bring to front
-				m.dialog.BringToFront(dialog.SessionsID)
-			} else {
-				cmds = append(cmds, m.listSessions)
+			if cmd := m.openSessionsDialog(); cmd != nil {
+				cmds = append(cmds, cmd)
 			}
 			return true
 		}
@@ -536,15 +524,30 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
 			m.dialog.CloseDialog(dialog.SessionsID)
 			cmds = append(cmds, m.loadSession(msg.Session.ID))
 
+		// Open dialog message
+		case dialog.OpenDialogMsg:
+			switch msg.DialogID {
+			case dialog.SessionsID:
+				if cmd := m.openSessionsDialog(); cmd != nil {
+					cmds = append(cmds, cmd)
+				}
+			case dialog.ModelsID:
+				if cmd := m.openModelsDialog(); cmd != nil {
+					cmds = append(cmds, cmd)
+				}
+			default:
+				// Unknown dialog
+				break
+			}
+
+			m.dialog.CloseDialog(msg.DialogID)
+
 		// Command dialog messages
 		case dialog.ToggleYoloModeMsg:
 			yolo := !m.com.App.Permissions.SkipRequests()
 			m.com.App.Permissions.SetSkipRequests(yolo)
 			m.setEditorPrompt(yolo)
 			m.dialog.CloseDialog(dialog.CommandsID)
-		case dialog.SwitchSessionsMsg:
-			cmds = append(cmds, m.listSessions)
-			m.dialog.CloseDialog(dialog.CommandsID)
 		case dialog.NewSessionsMsg:
 			if m.com.App.AgentCoordinator != nil && m.com.App.AgentCoordinator.IsBusy() {
 				cmds = append(cmds, uiutil.ReportWarn("Agent is busy, please wait before starting a new session..."))
@@ -562,11 +565,6 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
 			m.dialog.CloseDialog(dialog.CommandsID)
 		case dialog.QuitMsg:
 			cmds = append(cmds, tea.Quit)
-		case dialog.SwitchModelMsg:
-			m.dialog.CloseDialog(dialog.CommandsID)
-			if cmd := m.openModelsDialog(); cmd != nil {
-				cmds = append(cmds, cmd)
-			}
 		case dialog.ModelSelectedMsg:
 			// TODO: Handle model switching
 		}
@@ -1428,14 +1426,21 @@ func (m *UI) openCommandsDialog() tea.Cmd {
 	return nil
 }
 
-// openSessionsDialog opens the sessions dialog with the given sessions.
-func (m *UI) openSessionsDialog(sessions []session.Session) tea.Cmd {
+// openSessionsDialog opens the sessions dialog. If the dialog is already open,
+// it brings it to the front. Otherwise, it will list all the sessions and open
+// the dialog.
+func (m *UI) openSessionsDialog() tea.Cmd {
 	if m.dialog.ContainsDialog(dialog.SessionsID) {
 		// Bring to front
 		m.dialog.BringToFront(dialog.SessionsID)
 		return nil
 	}
 
+	sessions, err := m.com.App.Sessions.List(context.TODO())
+	if err != nil {
+		return uiutil.ReportError(err)
+	}
+
 	dialog := dialog.NewSessions(m.com, sessions...)
 	// TODO: Get. Rid. Of. Magic numbers!
 	dialog.SetSize(min(120, m.width-8), 30)
@@ -1444,13 +1449,6 @@ func (m *UI) openSessionsDialog(sessions []session.Session) tea.Cmd {
 	return nil
 }
 
-// listSessions is a [tea.Cmd] that lists all sessions and returns them in a
-// [listSessionsMsg].
-func (m *UI) listSessions() tea.Msg {
-	allSessions, _ := m.com.App.Sessions.List(context.TODO())
-	return listSessionsMsg{sessions: allSessions}
-}
-
 // newSession clears the current session state and prepares for a new session.
 // The actual session creation happens when the user sends their first message.
 func (m *UI) newSession() {