feat: unread indicator (*) for rooms with new messages

Rooms that receive a message while not active show a * prefix in the
sidebar. The marker clears when you tab to that room.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-06-26 22:30:10 +02:00
parent 340735f992
commit 95fd29ae8d

View File

@@ -107,6 +107,7 @@ type model struct {
rooms []string // "general" always first; DM rooms appended rooms []string // "general" always first; DM rooms appended
activeRoom int activeRoom int
messages map[string][]entry messages map[string][]entry
unread map[string]bool // rooms with messages since last viewed
peers map[proto.PeerID]string // connected peers: id → alias peers map[proto.PeerID]string // connected peers: id → alias
peerOrder []proto.PeerID peerOrder []proto.PeerID
@@ -131,6 +132,7 @@ func newModel(ipcPort int, network string) model {
networkName: network, networkName: network,
rooms: []string{"general"}, rooms: []string{"general"},
messages: make(map[string][]entry), messages: make(map[string][]entry),
unread: make(map[string]bool),
peers: make(map[proto.PeerID]string), peers: make(map[proto.PeerID]string),
input: ti, input: ti,
status: "connecting…", status: "connecting…",
@@ -215,9 +217,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m, cmds = m.doSend(cmds) m, cmds = m.doSend(cmds)
case msg.Type == tea.KeyTab: case msg.Type == tea.KeyTab:
m.activeRoom = (m.activeRoom + 1) % len(m.rooms) m.activeRoom = (m.activeRoom + 1) % len(m.rooms)
delete(m.unread, m.activeRoomName())
m = m.refreshViewport() m = m.refreshViewport()
case msg.Type == tea.KeyShiftTab: case msg.Type == tea.KeyShiftTab:
m.activeRoom = (m.activeRoom - 1 + len(m.rooms)) % len(m.rooms) m.activeRoom = (m.activeRoom - 1 + len(m.rooms)) % len(m.rooms)
delete(m.unread, m.activeRoomName())
m = m.refreshViewport() m = m.refreshViewport()
default: default:
var tiCmd tea.Cmd var tiCmd tea.Cmd
@@ -303,6 +307,9 @@ func (m model) applyEvent(evt proto.IpcMessage) model {
} }
m.messages[msg.Room] = append(m.messages[msg.Room], e) m.messages[msg.Room] = append(m.messages[msg.Room], e)
m = m.addRoom(msg.Room) m = m.addRoom(msg.Room)
if msg.Room != m.activeRoomName() {
m.unread[msg.Room] = true
}
m = m.refreshViewport() m = m.refreshViewport()
} }
} }
@@ -483,7 +490,11 @@ func (m model) renderRooms(boxH int) string {
if i == m.activeRoom { if i == m.activeRoom {
lines = append(lines, styleActive.Width(innerW).Render("▶ "+label)) lines = append(lines, styleActive.Width(innerW).Render("▶ "+label))
} else { } else {
lines = append(lines, styleRoom.Width(innerW).Render(" "+label)) prefix := " "
if m.unread[room] {
prefix = "* "
}
lines = append(lines, styleRoom.Width(innerW).Render(prefix+label))
} }
} }
for len(lines) < contentH { for len(lines) < contentH {