refactor(ui): merge each model's update into Update

This commit is contained in:
ayn2op 2026-10-06 15:42:47 -04:00
parent c8b024eb0e
commit c3cb114e22
7 changed files with 142 additions and 181 deletions

View file

@ -221,86 +221,81 @@ func (c *Model) reset() {
c.editState.SetValue("")
}
// Update returns c changed in response to msg and a command to run, or nil.
func (c Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := c.update(msg)
return c, cmd
}
// update changes c in response to msg and returns a command to run, or nil.
func (c *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case TabSuggestMsg:
return c.tabSuggest()
return c, c.tabSuggest()
case imagePastedMsg:
if len(msg) == 0 {
return nil
return c, nil
}
c.attach(imageAttachmentName, bytes.NewReader(msg))
return nil
return c, nil
case filesPickedMsg:
selectedChannel := c.channel
if selectedChannel == nil || selectedChannel.ID != msg.channelID {
return closeFiles(msg.files)
return c, closeFiles(msg.files)
}
for _, file := range msg.files {
c.attach(file.Name, file.Reader)
}
return nil
return c, nil
case editorMsg:
c.editState.SetValue(string(msg))
return nil
return c, nil
case editMsg:
c.editState.Apply(textarea.Change(msg))
typingCmd := c.sendTyping()
if c.cfg.AutocompleteLimit > 0 {
return tview.Batch(typingCmd, c.tabSuggest())
return c, tview.Batch(typingCmd, c.tabSuggest())
}
return typingCmd
return c, typingCmd
case tview.KeyMsg:
switch {
case keybind.Matches(msg, c.cfg.Keybinds.Composer.EditLast.Keybind) && c.canEditLastMessage():
return c.editLastMessage()
return c, c.editLastMessage()
case keybind.Matches(msg, c.cfg.Keybinds.Composer.Paste.Keybind):
c.pasteText()
return pasteImage()
return c, pasteImage()
case keybind.Matches(msg, c.cfg.Keybinds.Composer.Send.Keybind):
if c.mentionsVisible {
return c.tabComplete()
return c, c.tabComplete()
}
return c.send()
return c, c.send()
case keybind.Matches(msg, c.cfg.Keybinds.Composer.OpenEditor.Keybind):
c.stopTabCompletion()
return c.openEditor()
return c, c.openEditor()
case keybind.Matches(msg, c.cfg.Keybinds.Composer.OpenFilePicker.Keybind):
c.stopTabCompletion()
return c.pickFiles()
return c, c.pickFiles()
case keybind.Matches(msg, c.cfg.Keybinds.Composer.Cancel.Keybind):
if c.mentionsVisible {
c.stopTabCompletion()
return nil
return c, nil
}
c.reset()
return nil
return c, nil
case keybind.Matches(msg, c.cfg.Keybinds.Composer.TabComplete.Keybind):
if c.mentionsVisible {
return c.tabComplete()
return c, c.tabComplete()
}
c.insert("\t")
return nil
return c, nil
case keybind.Matches(msg, c.cfg.Keybinds.Composer.ToggleReplyMention.Keybind):
c.toggleReplyMention()
return nil
return c, nil
case keybind.Matches(msg, c.cfg.Keybinds.Composer.Undo.Keybind):
c.editState.Undo()
return nil
return c, nil
}
case mentionslist.Msg:
var cmd tview.Cmd
c.mentionsList, cmd = c.mentionsList.Update(msg)
return cmd
return c, cmd
}
return nil
return c, nil
}
func (c *Model) canEditLastMessage() bool {

View file

@ -300,47 +300,42 @@ func (m *Model) collapseParentNode(node *tree.Node) {
m.selectionState.SetCurrentNode(parent)
}
// Update returns m changed in response to msg and a command to run, or nil.
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := m.update(msg)
return m, cmd
}
// update changes m in response to msg and returns a command to run, or nil.
func (m *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case *gateway.ReadyEvent:
m.rebuild(msg)
return nil
return m, nil
case *gateway.MessageCreateEvent:
if !msg.GuildID.IsValid() {
m.moveDMToFront(msg.ChannelID)
}
return nil
return m, nil
case *gateway.PresenceUpdateEvent:
m.updateDMNodeStyle(msg.User.ID)
return nil
return m, nil
case *read.UpdateEvent:
m.refreshReadStyles(msg)
return nil
return m, nil
case NavigateMsg:
return m.navigate(msg.ChannelID)
return m, m.navigate(msg.ChannelID)
case Msg:
m.selectionState.Apply(tree.Change(msg))
return nil
return m, nil
case SelectedMsg:
m.selectionState.SetCurrentNode(msg.Node)
return m.selectNode(msg.Node)
return m, m.selectNode(msg.Node)
case tview.KeyMsg:
switch {
case keybind.Matches(msg, m.cfg.Keybinds.GuildsTree.CollapseAll.Keybind):
for _, node := range m.root.Children() {
node.CollapseAll()
}
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.GuildsTree.CollapseParentNode.Keybind):
m.collapseParentNode(m.selectionState.CurrentNode())
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.GuildsTree.SelectPreviousUnread.Keybind, m.cfg.Keybinds.GuildsTree.SelectNextUnread.Keybind):
previous := keybind.Matches(msg, m.cfg.Keybinds.GuildsTree.SelectPreviousUnread.Keybind)
if node := adjacentNode(m.root, m.selectionState.CurrentNode(), m.isUnread, previous); node != nil {
@ -354,12 +349,12 @@ func (m *Model) update(msg tview.Msg) tview.Cmd {
m.expandPathToNode(node)
m.selectionState.SetCurrentNode(node)
}
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.GuildsTree.YankID.Keybind):
return uitree.YankID(m.selectionState.CurrentNode())
return m, uitree.YankID(m.selectionState.CurrentNode())
}
}
return nil
return m, nil
}
// isUnread reports whether node is an unread channel, or a guild or the direct messages whose channels are not shown yet and has one.

View file

@ -88,36 +88,31 @@ func (m Model) View(focused bool) tview.Widget {
return uitree.New(m.root, m.selectionState, m.cfg, m.cfg.Theme.MembersTree, m.cfg.Keybinds.MembersTree, focused, onChange, onSelect).Title("Members")
}
// Update returns m changed in response to msg and a command to run, or nil.
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := m.update(msg)
return m, cmd
}
// update changes m in response to msg and returns a command to run, or nil.
func (m *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case *gateway.ReadyEvent:
// ningen drops its member lists on READY, so request the list again.
m.rebuild()
return m.requestMembers(0)
return m, m.requestMembers(0)
case *gateway.GuildMemberListUpdateEvent:
if channel := m.channel; channel != nil && channel.GuildID == msg.GuildID && m.state.MemberState.ListID(channel) == msg.ID {
m.rebuild()
}
return nil
return m, nil
case *gateway.PresenceUpdateEvent:
m.refreshStatus(msg.User.ID)
return nil
return m, nil
case Msg:
m.selectionState.Apply(tree.Change(msg))
return m.requestSelectedMembers()
return m, m.requestSelectedMembers()
case SelectedMsg:
m.selectionState.SetCurrentNode(msg.Node)
if _, ok := msg.Node.Reference().(groupRef); ok {
msg.Node.SetExpanded(!msg.Node.Expanded())
}
return m.requestSelectedMembers()
return m, m.requestSelectedMembers()
case tview.KeyMsg:
kbs := m.cfg.Keybinds.MembersTree
switch {
@ -125,18 +120,18 @@ func (m *Model) update(msg tview.Msg) tview.Cmd {
for _, node := range m.root.Children() {
node.Collapse()
}
return nil
return m, nil
case keybind.Matches(msg, kbs.CollapseParentNode.Keybind):
if parent := m.parent(m.selectionState.CurrentNode()); parent != nil {
parent.Collapse()
m.selectionState.SetCurrentNode(parent)
}
return nil
return m, nil
case keybind.Matches(msg, kbs.YankID.Keybind):
return uitree.YankID(m.selectionState.CurrentNode())
return m, uitree.YankID(m.selectionState.CurrentNode())
}
}
return nil
return m, nil
}
// rebuild recreates the tree from the member list of the selected channel.

View file

@ -837,64 +837,50 @@ func (ml *Model) selectedMessage() (*discord.Message, bool) {
return &ml.items[cursor].message, true
}
// Update returns ml changed in response to msg and a command to run, or nil.
func (ml Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := ml.update(msg)
return ml, cmd
}
// atTop reports whether the oldest message shown is selected.
func (ml *Model) atTop() bool {
return ml.cursor() >= 0 && ml.cursor() == ml.messageIndex(-1, 1)
}
// update changes ml in response to msg and returns a command to run, or nil.
func (ml *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case tview.KeyMsg:
switch {
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Cancel.Keybind):
ml.clearSelection()
return nil
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.SelectUp.Keybind):
return ml.selectUp()
return ml, ml.selectUp()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.SelectDown.Keybind):
ml.selectDown()
return nil
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.SelectTop.Keybind):
return ml.selectTop()
return ml, ml.selectTop()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.SelectBottom.Keybind):
ml.selectBottom()
return nil
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.SelectReply.Keybind):
ml.selectReply()
return nil
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.YankID.Keybind):
return ml.yankMessageID()
return ml, ml.yankMessageID()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.YankContent.Keybind):
return ml.yankContent()
return ml, ml.yankContent()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.YankURL.Keybind):
return ml.yankURL()
return ml, ml.yankURL()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Open.Keybind):
return ml.open()
return ml, ml.open()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.OpenInBrowser.Keybind):
return ml.openInBrowser()
return ml, ml.openInBrowser()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Download.Keybind):
return ml.download()
return ml, ml.download()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Reply.Keybind):
return ml.reply(false)
return ml, ml.reply(false)
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.ReplyMention.Keybind):
return ml.reply(true)
return ml, ml.reply(true)
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Edit.Keybind):
return ml.editSelectedMessage()
return ml, ml.editSelectedMessage()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Delete.Keybind):
return ml.deleteSelectedMessage()
return ml, ml.deleteSelectedMessage()
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.DeleteConfirm.Keybind):
return ml.confirmDelete()
return ml, ml.confirmDelete()
}
case olderMessagesLoadedMsg:
selectedChannel := ml.channel
if selectedChannel == nil || selectedChannel.ID != msg.ChannelID {
return nil
return ml, nil
}
prevCursor := ml.cursor()
@ -908,13 +894,13 @@ func (ml *Model) update(msg tview.Msg) tview.Cmd {
}
ml.rebuildItems()
if selectedChannel.GuildID.IsValid() {
return tview.Batch(ml.requestGuildMembers(selectedChannel.GuildID, msg.Older), ml.loadPreviews())
return ml, tview.Batch(ml.requestGuildMembers(selectedChannel.GuildID, msg.Older), ml.loadPreviews())
}
return ml.loadPreviews()
return ml, ml.loadPreviews()
case previewLoadedMsg:
index := slices.IndexFunc(ml.items, func(item messageItem) bool { return !item.separator && item.message.ID == msg.messageID })
if index < 0 || ml.items[index].previews == nil {
return nil
return ml, nil
}
preview, cmd := msg.preview, tview.Cmd(nil)
if ml.kitty {
@ -925,7 +911,7 @@ func (ml *Model) update(msg tview.Msg) tview.Cmd {
ml.items[index].previews[msg.proxy] = preview
// Invalidate the view so it is laid out with the preview.
ml.items[index].view = nil
return cmd
return ml, cmd
case tview.ResizeMsg:
cols, rows := msg.Size()
width, height := msg.PixelSize()
@ -947,10 +933,15 @@ func (ml *Model) update(msg tview.Msg) tview.Cmd {
ml.selectionState.Apply(list.Change(msg))
ml.onRowCursorChanged(ml.cursor())
if !top && ml.atTop() {
return ml.fetchOlderMessages()
return ml, ml.fetchOlderMessages()
}
}
return nil
return ml, nil
}
// atTop reports whether the oldest message shown is selected.
func (ml *Model) atTop() bool {
return ml.cursor() >= 0 && ml.cursor() == ml.messageIndex(-1, 1)
}
func (ml *Model) selectDown() {

View file

@ -193,27 +193,22 @@ func (m Model) Init() tview.Cmd {
return tview.Batch(openState(m.state), listen(m.events), tview.RequestTerminalInfo())
}
// Update returns m changed in response to msg and a command to run, or nil.
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := m.update(msg)
return m, cmd
}
// update changes m in response to msg and returns a command to run, or nil.
func (m *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case gateway.Event:
return tview.Batch(m.applyEvent(msg), listen(m.events))
return m, tview.Batch(m.applyEvent(msg), listen(m.events))
case tview.FocusMsg:
m.windowUnfocused = !msg.Focused
return nil
return m, nil
case guildstree.ChannelLoadedMsg:
node := m.guildsTree.CurrentNode()
if node == nil {
return nil
return m, nil
}
channelID, ok := node.Reference().(discord.ChannelID)
if !ok || channelID != msg.Channel.ID {
return nil
return m, nil
}
membersCmd := m.setSelectedChannel(&msg.Channel)
@ -223,97 +218,97 @@ func (m *Model) update(msg tview.Msg) tview.Cmd {
m.setFocus(paneComposer)
}
title := ui.ChannelToString(msg.Channel, m.cfg.Icons, m.state) + " - " + consts.Name
return tview.Batch(tview.SetTitle(title), m.messagesList.SetChannel(&msg.Channel, msg.Messages), membersCmd)
return m, tview.Batch(tview.SetTitle(title), m.messagesList.SetChannel(&msg.Channel, msg.Messages), membersCmd)
case messageslist.Msg, tview.TerminalInfoMsg, tview.ResizeMsg:
return m.updatePane(paneMessagesList, msg)
return m, m.updatePane(paneMessagesList, msg)
case typingExpiredMsg:
if until, ok := m.typers[msg.userID]; ok && !time.Now().Before(until) {
delete(m.typers, msg.userID)
}
return nil
return m, nil
case channelspicker.SelectedMsg:
return m.navigateToChannel(msg.ChannelID)
return m, m.navigateToChannel(msg.ChannelID)
case channelspicker.CancelMsg:
m.closePicker()
return nil
return m, nil
case attachmentspicker.SelectedMsg:
m.overlay = overlayNone
return msg.Action
return m, msg.Action
case attachmentspicker.CancelMsg:
m.overlay = overlayNone
return nil
return m, nil
case messageslist.ShowAttachmentsMsg:
m.attachmentsPicker.SetItems(picker.Items(msg))
m.overlay = overlayAttachmentsPicker
return nil
return m, nil
case messageslist.ReplyMsg:
m.composer.StartReply(msg.Message, msg.Name, msg.Mention)
m.setFocus(paneComposer)
return nil
return m, nil
case messageslist.EditMsg:
m.composer.StartEdit(discord.Message(msg))
m.setFocus(paneComposer)
return nil
return m, nil
case composer.EditLastMsg:
if message, ok := m.messagesList.SelectLastOwn(); ok {
m.composer.StartEdit(message)
}
return nil
return m, nil
case composer.SentMsg:
m.messagesList.ShowNewest()
return nil
return m, nil
case QuitMsg:
return closeState(m.state)
return m, closeState(m.state)
case tview.KeyMsg:
switch {
case keybind.Matches(msg, m.cfg.Keybinds.FocusGuildsTree.Keybind):
m.composer.CloseMentions()
m.setFocus(paneGuildsTree)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.FocusMembersTree.Keybind):
m.composer.CloseMentions()
m.setFocus(paneMembersTree)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.FocusMessagesList.Keybind):
m.composer.CloseMentions()
m.setFocus(paneMessagesList)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.FocusComposer.Keybind):
m.setFocus(paneComposer)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.FocusPrevious.Keybind):
m.cycleFocus(-1)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.FocusNext.Keybind):
m.cycleFocus(1)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.ToggleGuildsTree.Keybind):
m.toggle(&m.guildsTreeVisible, paneGuildsTree)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.ToggleMembersTree.Keybind):
m.toggle(&m.membersTreeVisible, paneMembersTree)
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.ToggleChannelsPicker.Keybind):
m.togglePicker()
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.Logout.Keybind):
return tview.Sequence(closeState(m.state), logout())
return m, tview.Sequence(closeState(m.state), logout())
}
case composer.TabSuggestMsg, mentionslist.Msg:
return m.updatePane(paneComposer, msg)
return m, m.updatePane(paneComposer, msg)
case paneMsg:
if msg.focus {
m.setFocus(msg.pane)
}
if msg.msg == nil {
return nil
return m, nil
}
return m.updatePane(msg.pane, msg.msg)
return m, m.updatePane(msg.pane, msg.msg)
}
return m.route(msg)
return m, m.route(msg)
}
// route sends msg to the open picker, which takes all input, and otherwise to the focused pane.

View file

@ -42,80 +42,75 @@ func (Model) Init() tview.Cmd {
return func() tview.Msg { return startMsg{} }
}
// Update returns m changed in response to msg and a command to run, or nil.
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := m.update(msg)
return m, cmd
}
// update changes m in response to msg and returns a command to run, or nil.
func (m *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case startMsg:
m.status = "Connecting to Remote Auth Gateway..."
return connect()
return m, connect()
case tview.KeyMsg:
if msg.Key() == tcell.KeyEsc {
m.status = "Canceled"
return closeConn(m.conn)
return m, closeConn(m.conn)
}
return nil
return m, nil
case scrollMsg:
m.scrollState.Apply(textview.Change(msg))
return nil
return m, nil
case connCreateMsg:
m.conn = msg.conn
m.status = "Connected. Handshaking..."
return listen(m.conn)
return m, listen(m.conn)
case connCloseMsg:
m.conn = nil
return nil
return m, nil
case helloMsg:
m.heartbeatInterval = time.Duration(msg.heartbeatInterval) * time.Millisecond
return tview.Batch(listen(m.conn), scheduleHeartbeat(m.heartbeatInterval), generatePrivateKey())
return m, tview.Batch(listen(m.conn), scheduleHeartbeat(m.heartbeatInterval), generatePrivateKey())
case privateKeyMsg:
m.privateKey = msg.privateKey
return sendInit(m.conn, m.privateKey)
return m, sendInit(m.conn, m.privateKey)
case nonceProofMsg:
return tview.Batch(listen(m.conn), sendNonceProof(m.conn, m.privateKey, msg.encryptedNonce))
return m, tview.Batch(listen(m.conn), sendNonceProof(m.conn, m.privateKey, msg.encryptedNonce))
case pendingRemoteInitMsg:
m.fingerprint = msg.fingerprint
return tview.Batch(listen(m.conn), generateQRCode(msg.fingerprint))
return m, tview.Batch(listen(m.conn), generateQRCode(msg.fingerprint))
case qrCodeMsg:
m.qrCode = msg.qrCode
m.status = "Scan this with the Discord mobile app to log in instantly."
return nil
return m, nil
case pendingTicketMsg:
return tview.Batch(listen(m.conn), decryptUserPayload(m.privateKey, msg.encryptedUserPayload))
return m, tview.Batch(listen(m.conn), decryptUserPayload(m.privateKey, msg.encryptedUserPayload))
case userMsg:
name := msg.username
if msg.discriminator != "0" {
name += "#" + msg.discriminator
}
m.status = "Check your phone! Logging in as " + name
return nil
return m, nil
case pendingLoginMsg:
m.status = "Authenticating..."
return tview.Batch(closeConn(m.conn), exchangeTicket(m.fingerprint, m.privateKey, msg.ticket))
return m, tview.Batch(closeConn(m.conn), exchangeTicket(m.fingerprint, m.privateKey, msg.ticket))
case ignoredMsg:
return listen(m.conn)
return m, listen(m.conn)
case cancelMsg:
m.status = "Login canceled on mobile"
return closeConn(m.conn)
return m, closeConn(m.conn)
case heartbeatTickMsg:
if m.conn == nil {
return nil
return m, nil
}
return tview.Batch(scheduleHeartbeat(m.heartbeatInterval), sendHeartbeat(m.conn))
return m, tview.Batch(scheduleHeartbeat(m.heartbeatInterval), sendHeartbeat(m.conn))
case error:
m.status = msg.Error()
return closeConn(m.conn)
return m, closeConn(m.conn)
}
return nil
return m, nil
}
// halfBlock packs a vertical pair of QR pixels into one glyph, fitting two bitmap rows into a single terminal row.

View file

@ -71,28 +71,23 @@ func (m Model) Init() tview.Cmd {
)
}
// Update returns m changed in response to msg and a command to run, or nil.
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {
cmd := m.update(msg)
return m, cmd
}
// update changes m in response to msg and returns a command to run, or nil.
func (m *Model) update(msg tview.Msg) tview.Cmd {
switch msg := msg.(type) {
case loginMsg:
return m.showLogin()
return m, m.showLogin()
case tokenMsg:
return m.showChat(string(msg))
return m, m.showChat(string(msg))
case password.TokenMsg:
return tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
return m, tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
case qr.TokenMsg:
return tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
return m, tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
case token.TokenMsg:
return tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
return m, tview.Batch(m.showChat(string(msg)), setToken(string(msg)))
case chat.LogoutMsg:
return tview.Batch(
return m, tview.Batch(
m.showLogin(),
deleteToken(),
)
@ -100,41 +95,41 @@ func (m *Model) update(msg tview.Msg) tview.Cmd {
if m.modal == nil {
m.modal, m.dialogFocus = &msg, 0
}
return nil
return m, nil
case dialogFocusMsg:
m.dialogFocus = int(msg)
return nil
return m, nil
case dialogDoneMsg:
return m.finishModal(int(msg))
return m, m.finishModal(int(msg))
case tview.KeyMsg:
// The dialog takes all input while it is open, so nothing behind it reacts.
if m.modal != nil {
return nil
return m, nil
}
switch {
case keybind.Matches(msg, m.cfg.Keybinds.ToggleHelp.Keybind):
m.toggleHelp()
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.ToggleFullHelp.Keybind):
if m.cfg.Help.Enabled {
m.helpShowAll = !m.helpShowAll
}
return nil
return m, nil
case keybind.Matches(msg, m.cfg.Keybinds.Suspend.Keybind):
return suspend()
return m, suspend()
case keybind.Matches(msg, m.cfg.Keybinds.Quit.Keybind):
return tview.Batch(m.updateScreen(chat.QuitMsg{}), tview.Quit())
return m, tview.Batch(m.updateScreen(chat.QuitMsg{}), tview.Quit())
}
case tview.MouseMsg, tview.PasteMsg:
if m.modal != nil {
return nil
return m, nil
}
case tview.ResizeMsg:
m.resize = msg
}
return m.updateScreen(msg)
return m, m.updateScreen(msg)
}
// updateScreen updates the model shown above help with msg.