commit e4a2fe2

delthas  ·  2022-10-18 15:27:20 +0000 UTC
parent faefde5
Clear highlights from MARKREAD when only NotifyNone lines are left

Previously, we did not clear buffers highlight statuses on MARKREAD if
there was any unread line after it.

This meant that if we received a plain message, than a join message, and
some other device sent us a read marker for the plain message, we would
still highlight the buffer. But we should not: a join message should not
highlight the buffer.

This is a recurrent use case because some clients do not display join
mesasges and therefore do not send read markers for it.

This updates the logic to actually store the notify level (in the line)
and uses it to reset the highlight status when only NotifyNone messages
(or no messages) are left.
5 files changed,  +58, -47
M app.go
M app.go
+24, -19
  1@@ -626,10 +626,11 @@ func (app *App) handleKeyEvent(ev *tcell.EventKey) {
  2 		input := app.win.InputEnter()
  3 		err := app.handleInput(buffer, input)
  4 		if err != nil {
  5-			app.win.AddLine(netID, buffer, ui.NotifyUnread, ui.Line{
  6+			app.win.AddLine(netID, buffer, ui.Line{
  7 				At:        time.Now(),
  8 				Head:      "!!",
  9 				HeadColor: tcell.ColorRed,
 10+				Notify:    ui.NotifyUnread,
 11 				Body:      ui.PlainSprintf("%q: %s", input, err),
 12 			})
 13 		}
 14@@ -715,9 +716,10 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 15 	// Mutate IRC state
 16 	ev, err := s.HandleMessage(msg)
 17 	if err != nil {
 18-		app.win.AddLine(netID, "", ui.NotifyUnread, ui.Line{
 19+		app.win.AddLine(netID, "", ui.Line{
 20 			Head:      "!!",
 21 			HeadColor: tcell.ColorRed,
 22+			Notify:    ui.NotifyUnread,
 23 			Body:      ui.PlainSprintf("Received corrupt message %q: %s", msg.String(), err),
 24 		})
 25 		return
 26@@ -742,7 +744,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 27 		if s.Nick() != app.cfg.Nick {
 28 			body = fmt.Sprintf("Connected to the server as %s", s.Nick())
 29 		}
 30-		app.win.AddLine(netID, "", ui.NotifyNone, ui.Line{
 31+		app.win.AddLine(netID, "", ui.Line{
 32 			At:   msg.TimeOrNow(),
 33 			Head: "--",
 34 			Body: ui.PlainString(body),
 35@@ -770,7 +772,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 36 	case irc.UserNickEvent:
 37 		line := app.formatEvent(ev)
 38 		for _, c := range s.ChannelsSharedWith(ev.User) {
 39-			app.win.AddLine(netID, c, ui.NotifyNone, line)
 40+			app.win.AddLine(netID, c, line)
 41 		}
 42 	case irc.SelfJoinEvent:
 43 		i, added := app.win.AddBuffer(netID, "", ev.Channel)
 44@@ -801,26 +803,26 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 45 		}
 46 	case irc.UserJoinEvent:
 47 		line := app.formatEvent(ev)
 48-		app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
 49+		app.win.AddLine(netID, ev.Channel, line)
 50 	case irc.SelfPartEvent:
 51 		app.win.RemoveBuffer(netID, ev.Channel)
 52 		delete(app.messageBounds, boundKey{netID, ev.Channel})
 53 	case irc.UserPartEvent:
 54 		line := app.formatEvent(ev)
 55-		app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
 56+		app.win.AddLine(netID, ev.Channel, line)
 57 	case irc.UserQuitEvent:
 58 		line := app.formatEvent(ev)
 59 		for _, c := range ev.Channels {
 60-			app.win.AddLine(netID, c, ui.NotifyNone, line)
 61+			app.win.AddLine(netID, c, line)
 62 		}
 63 	case irc.TopicChangeEvent:
 64 		line := app.formatEvent(ev)
 65-		app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, line)
 66+		app.win.AddLine(netID, ev.Channel, line)
 67 		topic := ui.IRCString(ev.Topic).String()
 68 		app.win.SetTopic(netID, ev.Channel, topic)
 69 	case irc.ModeChangeEvent:
 70 		line := app.formatEvent(ev)
 71-		app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
 72+		app.win.AddLine(netID, ev.Channel, line)
 73 	case irc.InviteEvent:
 74 		var buffer string
 75 		var notify ui.NotifyType
 76@@ -838,16 +840,17 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 77 			notify = ui.NotifyUnread
 78 			body = fmt.Sprintf("%s invited %s to join this channel", ev.Inviter, ev.Invitee)
 79 		}
 80-		app.win.AddLine(netID, buffer, notify, ui.Line{
 81+		app.win.AddLine(netID, buffer, ui.Line{
 82 			At:        msg.TimeOrNow(),
 83 			Head:      "--",
 84 			HeadColor: tcell.ColorGray,
 85+			Notify:    notify,
 86 			Body:      ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
 87 			Highlight: notify == ui.NotifyHighlight,
 88 			Readable:  true,
 89 		})
 90 	case irc.MessageEvent:
 91-		buffer, line, notification := app.formatMessage(s, ev)
 92+		buffer, line := app.formatMessage(s, ev)
 93 		if line.IsZero() {
 94 			break
 95 		}
 96@@ -861,8 +864,8 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
 97 					Before(msg.TimeOrNow())
 98 			}
 99 		}
100-		app.win.AddLine(netID, buffer, notification, line)
101-		if notification == ui.NotifyHighlight {
102+		app.win.AddLine(netID, buffer, line)
103+		if line.Notify == ui.NotifyHighlight {
104 			app.notifyHighlight(buffer, ev.User, line.Body.String())
105 		}
106 		if !s.IsChannel(msg.Params[0]) && !s.IsMe(ev.User) {
107@@ -910,7 +913,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
108 			var line ui.Line
109 			switch ev := m.(type) {
110 			case irc.MessageEvent:
111-				_, line, _ = app.formatMessage(s, ev)
112+				_, line = app.formatMessage(s, ev)
113 			default:
114 				line = app.formatEvent(ev)
115 			}
116@@ -944,7 +947,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
117 		app.win.OpenOverlay()
118 		lines := make([]ui.Line, 0, len(ev.Messages))
119 		for _, m := range ev.Messages {
120-			_, line, _ := app.formatMessage(s, m)
121+			_, line := app.formatMessage(s, m)
122 			if line.IsZero() {
123 				continue
124 			}
125@@ -1229,6 +1232,7 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
126 			At:        ev.Time,
127 			Head:      "--",
128 			HeadColor: tcell.ColorGray,
129+			Notify:    ui.NotifyUnread,
130 			Body:      ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
131 			Readable:  true,
132 		}
133@@ -1254,9 +1258,8 @@ func (app *App) formatEvent(ev irc.Event) ui.Line {
134 //
135 // It computes three things:
136 // - which buffer the message must be added to,
137-// - the UI line,
138-// - what kind of notification senpai should send.
139-func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer string, line ui.Line, notification ui.NotifyType) {
140+// - the UI line.
141+func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer string, line ui.Line) {
142 	isFromSelf := s.IsMe(ev.User)
143 	isToSelf := s.IsMe(ev.Target)
144 	isHighlight := ev.TargetIsChannel && app.isHighlight(s, ev.Content)
145@@ -1292,6 +1295,7 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
146 		buffer = ev.Target
147 	}
148 
149+	var notification ui.NotifyType
150 	hlLine := ev.TargetIsChannel && isHighlight && !isFromSelf
151 	if isFromSelf {
152 		notification = ui.NotifyNone
153@@ -1332,6 +1336,7 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
154 		At:        ev.Time,
155 		Head:      head,
156 		HeadColor: headColor,
157+		Notify:    notification,
158 		Body:      body.StyledString(),
159 		Highlight: hlLine,
160 		Readable:  true,
161@@ -1477,7 +1482,7 @@ func (app *App) printTopic(netID, buffer string) (ok bool) {
162 	} else {
163 		body = fmt.Sprintf("Topic (by %s, %s): %s", who, at.Local().Format("Mon Jan 2 15:04:05"), topic)
164 	}
165-	app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
166+	app.win.AddLine(netID, buffer, ui.Line{
167 		At:        time.Now(),
168 		Head:      "--",
169 		HeadColor: tcell.ColorGray,
+15, -15
  1@@ -198,7 +198,7 @@ func noCommand(app *App, content string) error {
  2 
  3 	s.PrivMsg(buffer, content)
  4 	if !s.HasCapability("echo-message") {
  5-		buffer, line, _ := app.formatMessage(s, irc.MessageEvent{
  6+		buffer, line := app.formatMessage(s, irc.MessageEvent{
  7 			User:            s.Nick(),
  8 			Target:          buffer,
  9 			TargetIsChannel: s.IsChannel(buffer),
 10@@ -206,7 +206,7 @@ func noCommand(app *App, content string) error {
 11 			Content:         content,
 12 			Time:            time.Now(),
 13 		})
 14-		app.win.AddLine(netID, buffer, ui.NotifyNone, line)
 15+		app.win.AddLine(netID, buffer, line)
 16 	}
 17 
 18 	return nil
 19@@ -239,15 +239,15 @@ func commandDoHelp(app *App, args []string) (err error) {
 20 		sb.SetStyle(tcell.StyleDefault)
 21 		sb.WriteByte(' ')
 22 		sb.WriteString(cmd.Usage)
 23-		app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 24+		app.win.AddLine(netID, buffer, ui.Line{
 25 			At:   t,
 26 			Body: sb.StyledString(),
 27 		})
 28-		app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 29+		app.win.AddLine(netID, buffer, ui.Line{
 30 			At:   t,
 31 			Body: ui.PlainSprintf("  %s", cmd.Desc),
 32 		})
 33-		app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 34+		app.win.AddLine(netID, buffer, ui.Line{
 35 			At: t,
 36 		})
 37 	}
 38@@ -261,7 +261,7 @@ func commandDoHelp(app *App, args []string) (err error) {
 39 	}
 40 
 41 	if len(args) == 0 {
 42-		app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 43+		app.win.AddLine(netID, buffer, ui.Line{
 44 			At:   t,
 45 			Head: "--",
 46 			Body: ui.PlainString("Available commands:"),
 47@@ -274,7 +274,7 @@ func commandDoHelp(app *App, args []string) (err error) {
 48 		addLineCommands(cmdNames)
 49 	} else {
 50 		search := strings.ToUpper(args[0])
 51-		app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 52+		app.win.AddLine(netID, buffer, ui.Line{
 53 			At:   t,
 54 			Head: "--",
 55 			Body: ui.PlainSprintf("Commands that match \"%s\":", search),
 56@@ -288,7 +288,7 @@ func commandDoHelp(app *App, args []string) (err error) {
 57 			cmdNames = append(cmdNames, cmdName)
 58 		}
 59 		if len(cmdNames) == 0 {
 60-			app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 61+			app.win.AddLine(netID, buffer, ui.Line{
 62 				At:   t,
 63 				Body: ui.PlainSprintf("  no command matches %q", args[0]),
 64 			})
 65@@ -326,7 +326,7 @@ func commandDoMe(app *App, args []string) (err error) {
 66 	content := fmt.Sprintf("\x01ACTION %s\x01", args[0])
 67 	s.PrivMsg(buffer, content)
 68 	if !s.HasCapability("echo-message") {
 69-		buffer, line, _ := app.formatMessage(s, irc.MessageEvent{
 70+		buffer, line := app.formatMessage(s, irc.MessageEvent{
 71 			User:            s.Nick(),
 72 			Target:          buffer,
 73 			TargetIsChannel: s.IsChannel(buffer),
 74@@ -334,7 +334,7 @@ func commandDoMe(app *App, args []string) (err error) {
 75 			Content:         content,
 76 			Time:            time.Now(),
 77 		})
 78-		app.win.AddLine(netID, buffer, ui.NotifyNone, line)
 79+		app.win.AddLine(netID, buffer, line)
 80 	}
 81 	return nil
 82 }
 83@@ -368,7 +368,7 @@ func commandDoNames(app *App, args []string) (err error) {
 84 	}
 85 	body := sb.StyledString()
 86 	// TODO remove last space
 87-	app.win.AddLine(netID, buffer, ui.NotifyNone, ui.Line{
 88+	app.win.AddLine(netID, buffer, ui.Line{
 89 		At:        time.Now(),
 90 		Head:      "--",
 91 		HeadColor: tcell.ColorGray,
 92@@ -491,7 +491,7 @@ func commandDoR(app *App, args []string) (err error) {
 93 	}
 94 	s.PrivMsg(app.lastQuery, args[0])
 95 	if !s.HasCapability("echo-message") {
 96-		buffer, line, _ := app.formatMessage(s, irc.MessageEvent{
 97+		buffer, line := app.formatMessage(s, irc.MessageEvent{
 98 			User:            s.Nick(),
 99 			Target:          app.lastQuery,
100 			TargetIsChannel: s.IsChannel(app.lastQuery),
101@@ -499,7 +499,7 @@ func commandDoR(app *App, args []string) (err error) {
102 			Content:         args[0],
103 			Time:            time.Now(),
104 		})
105-		app.win.AddLine(app.lastQueryNet, buffer, ui.NotifyNone, line)
106+		app.win.AddLine(app.lastQueryNet, buffer, line)
107 	}
108 	return nil
109 }
110@@ -696,7 +696,7 @@ func commandSendMessage(app *App, target string, content string) error {
111 	}
112 	s.PrivMsg(target, content)
113 	if !s.HasCapability("echo-message") {
114-		buffer, line, _ := app.formatMessage(s, irc.MessageEvent{
115+		buffer, line := app.formatMessage(s, irc.MessageEvent{
116 			User:            s.Nick(),
117 			Target:          target,
118 			TargetIsChannel: s.IsChannel(target),
119@@ -711,7 +711,7 @@ func commandSendMessage(app *App, target string, content string) error {
120 			app.win.AddBuffer(netID, "", buffer)
121 		}
122 
123-		app.win.AddLine(netID, buffer, ui.NotifyNone, line)
124+		app.win.AddLine(netID, buffer, line)
125 	}
126 	return nil
127 }
+14, -8
 1@@ -33,6 +33,7 @@ type Line struct {
 2 	Head      string
 3 	Body      StyledString
 4 	HeadColor tcell.Color
 5+	Notify    NotifyType
 6 	Highlight bool
 7 	Readable  bool
 8 	Mergeable bool
 9@@ -366,7 +367,7 @@ func (bs *BufferList) mergeLine(former *Line, addition Line) (keepLine bool) {
10 	return true
11 }
12 
13-func (bs *BufferList) AddLine(netID, title string, notify NotifyType, line Line) {
14+func (bs *BufferList) AddLine(netID, title string, line Line) {
15 	_, b := bs.at(netID, title)
16 	if b == nil {
17 		return
18@@ -394,10 +395,10 @@ func (bs *BufferList) AddLine(netID, title string, notify NotifyType, line Line)
19 		}
20 	}
21 
22-	if notify != NotifyNone && b != current {
23+	if line.Notify != NotifyNone && b != current {
24 		b.unread = true
25 	}
26-	if notify == NotifyHighlight && b != current {
27+	if line.Notify == NotifyHighlight && b != current {
28 		b.highlights++
29 	}
30 }
31@@ -443,16 +444,21 @@ func (bs *BufferList) SetRead(netID, title string, timestamp time.Time) {
32 	if b == nil {
33 		return
34 	}
35+	clearRead := true
36 	for i := len(b.lines) - 1; i >= 0; i-- {
37 		line := &b.lines[i]
38-		if line.Readable {
39-			if !line.At.After(timestamp) {
40-				b.highlights = 0
41-				b.unread = false
42-			}
43+		if !line.At.After(timestamp) {
44+			break
45+		}
46+		if line.Readable && line.Notify != NotifyNone {
47+			clearRead = false
48 			break
49 		}
50 	}
51+	if clearRead {
52+		b.highlights = 0
53+		b.unread = false
54+	}
55 	if b.read.Before(timestamp) {
56 		b.read = timestamp
57 	}
+2, -2
 1@@ -270,8 +270,8 @@ func (ui *UI) RemoveNetworkBuffers(netID string) {
 2 	ui.memberOffset = 0
 3 }
 4 
 5-func (ui *UI) AddLine(netID, buffer string, notify NotifyType, line Line) {
 6-	ui.bs.AddLine(netID, buffer, notify, line)
 7+func (ui *UI) AddLine(netID, buffer string, line Line) {
 8+	ui.bs.AddLine(netID, buffer, line)
 9 }
10 
11 func (ui *UI) AddLines(netID, buffer string, before, after []Line) {
+3, -3
 1@@ -11,7 +11,7 @@ const welcomeMessage = "senpai dev build. Enter /help for a list of commands."
 2 
 3 func (app *App) initWindow() {
 4 	app.win.AddBuffer("", "(home)", "")
 5-	app.win.AddLine("", "", ui.NotifyNone, ui.Line{
 6+	app.win.AddLine("", "", ui.Line{
 7 		Head: "--",
 8 		Body: ui.PlainString(welcomeMessage),
 9 		At:   time.Now(),
10@@ -39,9 +39,9 @@ func (app *App) queueStatusLine(netID string, line ui.Line) {
11 func (app *App) addStatusLine(netID string, line ui.Line) {
12 	currentNetID, buffer := app.win.CurrentBuffer()
13 	if currentNetID == netID && buffer != "" {
14-		app.win.AddLine(netID, buffer, ui.NotifyNone, line)
15+		app.win.AddLine(netID, buffer, line)
16 	}
17-	app.win.AddLine(netID, "", ui.NotifyNone, line)
18+	app.win.AddLine(netID, "", line)
19 }
20 
21 func (app *App) setStatus() {