commit b813210
delthas
·
2021-11-24 15:59:00 +0000 UTC
parent d41d7c5
Add support for draft/event-playback - Refactor formatting lines into a function - Store event times in the event - Refactor merging lines into a function - Always merge lines from the history, now that we will add mergeable lines with CHATHISTORY Successfully tested locally.
4 files changed,
+229,
-104
M
app.go
+110,
-76
1@@ -642,21 +642,9 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
2 Highlight: true,
3 })
4 case irc.UserNickEvent:
5- var body ui.StyledStringBuilder
6- body.WriteString(fmt.Sprintf("%s\u2192%s", ev.FormerNick, ev.User))
7- textStyle := tcell.StyleDefault.Foreground(tcell.ColorGray)
8- arrowStyle := tcell.StyleDefault
9- body.AddStyle(0, textStyle)
10- body.AddStyle(len(ev.FormerNick), arrowStyle)
11- body.AddStyle(body.Len()-len(ev.User), textStyle)
12+ line := app.formatEvent(ev)
13 for _, c := range s.ChannelsSharedWith(ev.User) {
14- app.win.AddLine(netID, c, ui.NotifyNone, ui.Line{
15- At: msg.TimeOrNow(),
16- Head: "--",
17- HeadColor: tcell.ColorGray,
18- Body: body.StyledString(),
19- Mergeable: true,
20- })
21+ app.win.AddLine(netID, c, ui.NotifyNone, line)
22 }
23 case irc.SelfJoinEvent:
24 i, added := app.win.AddBuffer(netID, "", ev.Channel)
25@@ -685,70 +673,27 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
26 app.lastBuffer = ""
27 }
28 case irc.UserJoinEvent:
29- var body ui.StyledStringBuilder
30- body.Grow(len(ev.User) + 1)
31- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGreen))
32- body.WriteByte('+')
33- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
34- body.WriteString(ev.User)
35- app.win.AddLine(netID, ev.Channel, ui.NotifyNone, ui.Line{
36- At: msg.TimeOrNow(),
37- Head: "--",
38- HeadColor: tcell.ColorGray,
39- Body: body.StyledString(),
40- Mergeable: true,
41- })
42+ line := app.formatEvent(ev)
43+ app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
44 case irc.SelfPartEvent:
45 app.win.RemoveBuffer(netID, ev.Channel)
46 delete(app.messageBounds, boundKey{netID, ev.Channel})
47 case irc.UserPartEvent:
48- var body ui.StyledStringBuilder
49- body.Grow(len(ev.User) + 1)
50- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
51- body.WriteByte('-')
52- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
53- body.WriteString(ev.User)
54- app.win.AddLine(netID, ev.Channel, ui.NotifyNone, ui.Line{
55- At: msg.TimeOrNow(),
56- Head: "--",
57- HeadColor: tcell.ColorGray,
58- Body: body.StyledString(),
59- Mergeable: true,
60- })
61+ line := app.formatEvent(ev)
62+ app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
63 case irc.UserQuitEvent:
64- var body ui.StyledStringBuilder
65- body.Grow(len(ev.User) + 1)
66- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
67- body.WriteByte('-')
68- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
69- body.WriteString(ev.User)
70+ line := app.formatEvent(ev)
71 for _, c := range ev.Channels {
72- app.win.AddLine(netID, c, ui.NotifyNone, ui.Line{
73- At: msg.TimeOrNow(),
74- Head: "--",
75- HeadColor: tcell.ColorGray,
76- Body: body.StyledString(),
77- Mergeable: true,
78- })
79+ app.win.AddLine(netID, c, ui.NotifyNone, line)
80 }
81 case irc.TopicChangeEvent:
82+ line := app.formatEvent(ev)
83+ app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, line)
84 topic := ui.IRCString(ev.Topic).String()
85- body := fmt.Sprintf("Topic changed to: %s", topic)
86 app.win.SetTopic(netID, ev.Channel, topic)
87- app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, ui.Line{
88- At: msg.TimeOrNow(),
89- Head: "--",
90- HeadColor: tcell.ColorGray,
91- Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
92- })
93 case irc.ModeChangeEvent:
94- body := fmt.Sprintf("Mode change: %s", ev.Mode)
95- app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, ui.Line{
96- At: msg.TimeOrNow(),
97- Head: "--",
98- HeadColor: tcell.ColorGray,
99- Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
100- })
101+ line := app.formatEvent(ev)
102+ app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, line)
103 case irc.InviteEvent:
104 var buffer string
105 var notify ui.NotifyType
106@@ -807,19 +752,25 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
107 var linesAfter []ui.Line
108 bounds, hasBounds := app.messageBounds[boundKey{netID, ev.Target}]
109 for _, m := range ev.Messages {
110+ var line ui.Line
111 switch ev := m.(type) {
112 case irc.MessageEvent:
113- _, line, _ := app.formatMessage(s, ev)
114- if hasBounds {
115- c := bounds.Compare(&line)
116- if c < 0 {
117- linesBefore = append(linesBefore, line)
118- } else if c > 0 {
119- linesAfter = append(linesAfter, line)
120- }
121- } else {
122+ _, line, _ = app.formatMessage(s, ev)
123+ default:
124+ line = app.formatEvent(ev)
125+ }
126+ if line.IsZero() {
127+ continue
128+ }
129+ if hasBounds {
130+ c := bounds.Compare(&line)
131+ if c < 0 {
132 linesBefore = append(linesBefore, line)
133+ } else if c > 0 {
134+ linesAfter = append(linesAfter, line)
135 }
136+ } else {
137+ linesBefore = append(linesBefore, line)
138 }
139 }
140 app.win.AddLines(netID, ev.Target, linesBefore, linesAfter)
141@@ -987,6 +938,89 @@ func (app *App) completions(cursorIdx int, text []rune) []ui.Completion {
142 return cs
143 }
144
145+// formatEvent returns a formatted ui.Line for an irc.Event.
146+func (app *App) formatEvent(ev irc.Event) ui.Line {
147+ switch ev := ev.(type) {
148+ case irc.UserNickEvent:
149+ var body ui.StyledStringBuilder
150+ body.WriteString(fmt.Sprintf("%s\u2192%s", ev.FormerNick, ev.User))
151+ textStyle := tcell.StyleDefault.Foreground(tcell.ColorGray)
152+ arrowStyle := tcell.StyleDefault
153+ body.AddStyle(0, textStyle)
154+ body.AddStyle(len(ev.FormerNick), arrowStyle)
155+ body.AddStyle(body.Len()-len(ev.User), textStyle)
156+
157+ return ui.Line{
158+ At: ev.Time,
159+ Head: "--",
160+ HeadColor: tcell.ColorGray,
161+ Body: body.StyledString(),
162+ Mergeable: true,
163+ }
164+ case irc.UserJoinEvent:
165+ var body ui.StyledStringBuilder
166+ body.Grow(len(ev.User) + 1)
167+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGreen))
168+ body.WriteByte('+')
169+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
170+ body.WriteString(ev.User)
171+ return ui.Line{
172+ At: ev.Time,
173+ Head: "--",
174+ HeadColor: tcell.ColorGray,
175+ Body: body.StyledString(),
176+ Mergeable: true,
177+ }
178+ case irc.UserPartEvent:
179+ var body ui.StyledStringBuilder
180+ body.Grow(len(ev.User) + 1)
181+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
182+ body.WriteByte('-')
183+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
184+ body.WriteString(ev.User)
185+ return ui.Line{
186+ At: ev.Time,
187+ Head: "--",
188+ HeadColor: tcell.ColorGray,
189+ Body: body.StyledString(),
190+ Mergeable: true,
191+ }
192+ case irc.UserQuitEvent:
193+ var body ui.StyledStringBuilder
194+ body.Grow(len(ev.User) + 1)
195+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
196+ body.WriteByte('-')
197+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
198+ body.WriteString(ev.User)
199+ return ui.Line{
200+ At: ev.Time,
201+ Head: "--",
202+ HeadColor: tcell.ColorGray,
203+ Body: body.StyledString(),
204+ Mergeable: true,
205+ }
206+ case irc.TopicChangeEvent:
207+ topic := ui.IRCString(ev.Topic).String()
208+ body := fmt.Sprintf("Topic changed to: %s", topic)
209+ return ui.Line{
210+ At: ev.Time,
211+ Head: "--",
212+ HeadColor: tcell.ColorGray,
213+ Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
214+ }
215+ case irc.ModeChangeEvent:
216+ body := fmt.Sprintf("Mode change: %s", ev.Mode)
217+ return ui.Line{
218+ At: ev.Time,
219+ Head: "--",
220+ HeadColor: tcell.ColorGray,
221+ Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
222+ }
223+ default:
224+ return ui.Line{}
225+ }
226+}
227+
228 // formatMessage sets how a given message must be formatted.
229 //
230 // It computes three things:
+6,
-0
1@@ -19,6 +19,7 @@ type SelfNickEvent struct {
2 type UserNickEvent struct {
3 User string
4 FormerNick string
5+ Time time.Time
6 }
7
8 type SelfJoinEvent struct {
9@@ -30,6 +31,7 @@ type SelfJoinEvent struct {
10 type UserJoinEvent struct {
11 User string
12 Channel string
13+ Time time.Time
14 }
15
16 type SelfPartEvent struct {
17@@ -39,21 +41,25 @@ type SelfPartEvent struct {
18 type UserPartEvent struct {
19 User string
20 Channel string
21+ Time time.Time
22 }
23
24 type UserQuitEvent struct {
25 User string
26 Channels []string
27+ Time time.Time
28 }
29
30 type TopicChangeEvent struct {
31 Channel string
32 Topic string
33+ Time time.Time
34 }
35
36 type ModeChangeEvent struct {
37 Channel string
38 Mode string
39+ Time time.Time
40 }
41
42 type InviteEvent struct {
+82,
-6
1@@ -58,6 +58,7 @@ var SupportedCapabilities = map[string]struct{}{
2 "setname": {},
3
4 "draft/chathistory": {},
5+ "draft/event-playback": {},
6 "soju.im/bouncer-networks": {},
7 }
8
9@@ -527,18 +528,23 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
10 }
11 s.targetsBatch.Targets[target] = t
12 } else if b, ok := s.chBatches[id]; ok {
13- ev, err := s.newMessageEvent(msg)
14+ ev, err := s.handleMessageRegistered(msg, true)
15 if err != nil {
16 return nil, err
17 }
18- s.chBatches[id] = HistoryEvent{
19- Target: b.Target,
20- Messages: append(b.Messages, ev),
21+ if ev != nil {
22+ s.chBatches[id] = HistoryEvent{
23+ Target: b.Target,
24+ Messages: append(b.Messages, ev),
25+ }
26+ return nil, nil
27 }
28- return nil, nil
29 }
30 }
31+ return s.handleMessageRegistered(msg, false)
32+}
33
34+func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, error) {
35 switch msg.Command {
36 case "AUTHENTICATE":
37 if s.auth == nil {
38@@ -663,6 +669,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
39 return nil, err
40 }
41
42+ if playback {
43+ return UserJoinEvent{
44+ User: msg.Prefix.Name,
45+ Channel: channel,
46+ Time: msg.TimeOrNow(),
47+ }, nil
48+ }
49+
50 nickCf := s.Casemap(msg.Prefix.Name)
51 channelCf := s.Casemap(channel)
52
53@@ -685,6 +699,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
54 return UserJoinEvent{
55 User: msg.Prefix.Name,
56 Channel: c.Name,
57+ Time: msg.TimeOrNow(),
58 }, nil
59 }
60 case "PART":
61@@ -697,6 +712,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
62 return nil, err
63 }
64
65+ if playback {
66+ return UserPartEvent{
67+ User: msg.Prefix.Name,
68+ Channel: channel,
69+ Time: msg.TimeOrNow(),
70+ }, nil
71+ }
72+
73 nickCf := s.Casemap(msg.Prefix.Name)
74 channelCf := s.Casemap(channel)
75
76@@ -718,6 +741,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
77 return UserPartEvent{
78 User: u.Name.Name,
79 Channel: c.Name,
80+ Time: msg.TimeOrNow(),
81 }, nil
82 }
83 }
84@@ -727,6 +751,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
85 return nil, err
86 }
87
88+ if playback {
89+ return UserPartEvent{
90+ User: nick,
91+ Channel: channel,
92+ Time: msg.TimeOrNow(),
93+ }, nil
94+ }
95+
96 nickCf := s.Casemap(nick)
97 channelCf := s.Casemap(channel)
98
99@@ -748,6 +780,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
100 return UserPartEvent{
101 User: nick,
102 Channel: c.Name,
103+ Time: msg.TimeOrNow(),
104 }, nil
105 }
106 }
107@@ -756,6 +789,13 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
108 return nil, errMissingPrefix
109 }
110
111+ if playback {
112+ return UserQuitEvent{
113+ User: msg.Prefix.Name,
114+ Time: msg.TimeOrNow(),
115+ }, nil
116+ }
117+
118 nickCf := s.Casemap(msg.Prefix.Name)
119
120 if u, ok := s.users[nickCf]; ok {
121@@ -771,6 +811,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
122 return UserQuitEvent{
123 User: u.Name.Name,
124 Channels: channels,
125+ Time: msg.TimeOrNow(),
126 }, nil
127 }
128 case rplNamreply:
129@@ -864,6 +905,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
130 return nil, err
131 }
132
133+ if playback {
134+ return TopicChangeEvent{
135+ Channel: channel,
136+ Topic: topic,
137+ Time: msg.TimeOrNow(),
138+ }, nil
139+ }
140+
141 channelCf := s.Casemap(channel)
142
143 if c, ok := s.channels[channelCf]; ok {
144@@ -874,6 +923,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
145 return TopicChangeEvent{
146 Channel: c.Name,
147 Topic: c.Topic,
148+ Time: msg.TimeOrNow(),
149 }, nil
150 }
151 case "MODE":
152@@ -882,6 +932,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
153 return nil, err
154 }
155
156+ if playback {
157+ return ModeChangeEvent{
158+ Channel: channel,
159+ Mode: mode,
160+ Time: msg.TimeOrNow(),
161+ }, nil
162+ }
163+
164 channelCf := s.Casemap(channel)
165
166 if c, ok := s.channels[channelCf]; ok {
167@@ -917,7 +975,8 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
168 s.channels[channelCf] = c
169 return ModeChangeEvent{
170 Channel: c.Name,
171- Mode: strings.Join(msg.Params[1:], " "),
172+ Mode: mode,
173+ Time: msg.TimeOrNow(),
174 }, nil
175 }
176 case "INVITE":
177@@ -966,12 +1025,20 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
178 return nil, err
179 }
180
181+ if playback {
182+ return s.newMessageEvent(msg)
183+ }
184+
185 targetCf := s.casemap(target)
186 nickCf := s.casemap(msg.Prefix.Name)
187 s.typings.Done(targetCf, nickCf)
188
189 return s.newMessageEvent(msg)
190 case "TAGMSG":
191+ if playback {
192+ return nil, nil
193+ }
194+
195 if msg.Prefix == nil {
196 return nil, errMissingPrefix
197 }
198@@ -1048,6 +1115,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
199 return nil, err
200 }
201
202+ if playback {
203+ return UserNickEvent{
204+ User: nick,
205+ FormerNick: msg.Prefix.Name,
206+ Time: msg.TimeOrNow(),
207+ }, nil
208+ }
209+
210 nickCf := s.Casemap(msg.Prefix.Name)
211 newNick := nick
212 newNickCf := s.Casemap(newNick)
213@@ -1070,6 +1145,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
214 return UserNickEvent{
215 User: nick,
216 FormerNick: msg.Prefix.Name,
217+ Time: msg.TimeOrNow(),
218 }, nil
219 }
220 case "BOUNCER":
+31,
-22
1@@ -39,6 +39,21 @@ type Line struct {
2 newLines []int
3 }
4
5+func (l *Line) IsZero() bool {
6+ return l.Body.string == ""
7+}
8+
9+func (l *Line) Merge(line Line) {
10+ newBody := new(StyledStringBuilder)
11+ newBody.Grow(len(l.Body.string) + 2 + len(line.Body.string))
12+ newBody.WriteStyledString(l.Body)
13+ newBody.WriteString(" ")
14+ newBody.WriteStyledString(line.Body)
15+ l.Body = newBody.StyledString()
16+ l.computeSplitPoints()
17+ l.width = 0
18+}
19+
20 func (l *Line) computeSplitPoints() {
21 if l.splitPoints == nil {
22 l.splitPoints = []point{}
23@@ -307,14 +322,7 @@ func (bs *BufferList) AddLine(netID, title string, notify NotifyType, line Line)
24
25 if line.Mergeable && n != 0 && b.lines[n-1].Mergeable {
26 l := &b.lines[n-1]
27- newBody := new(StyledStringBuilder)
28- newBody.Grow(len(l.Body.string) + 2 + len(line.Body.string))
29- newBody.WriteStyledString(l.Body)
30- newBody.WriteString(" ")
31- newBody.WriteStyledString(line.Body)
32- l.Body = newBody.StyledString()
33- l.computeSplitPoints()
34- l.width = 0
35+ l.Merge(line)
36 // TODO change b.scrollAmt if it's not 0 and bs.current is idx.
37 } else {
38 line.computeSplitPoints()
39@@ -340,21 +348,22 @@ func (bs *BufferList) AddLines(netID, title string, before, after []Line) {
40
41 b := &bs.list[idx]
42
43- for i := 0; i < len(before); i++ {
44- before[i].Body = before[i].Body.ParseURLs()
45- before[i].computeSplitPoints()
46- }
47- for i := 0; i < len(after); i++ {
48- after[i].Body = after[i].Body.ParseURLs()
49- after[i].computeSplitPoints()
50- }
51-
52- if len(before) != 0 {
53- b.lines = append(before, b.lines...)
54- }
55- if len(after) != 0 {
56- b.lines = append(b.lines, after...)
57+ lines := make([]Line, 0, len(before)+len(b.lines)+len(after))
58+ for _, buf := range []*[]Line{&before, &b.lines, &after} {
59+ for _, line := range *buf {
60+ if line.Mergeable && len(lines) > 0 && lines[len(lines)-1].Mergeable {
61+ l := &lines[len(lines)-1]
62+ l.Merge(line)
63+ } else {
64+ if buf != &b.lines {
65+ line.Body = line.Body.ParseURLs()
66+ line.computeSplitPoints()
67+ }
68+ lines = append(lines, line)
69+ }
70+ }
71 }
72+ b.lines = lines
73 }
74
75 func (bs *BufferList) SetTopic(netID, title string, topic string) {