···126126 // The location of the last read indicator. This doesn't necessarily match the state of
127127 // last_read
128128 last_read_indicator: u32 = 0,
129129+ scroll_to_last_read: bool = true,
129130 has_unread: bool = false,
130131 has_unread_highlight: bool = false,
131132···306307 pub fn insertMessage(self: *Channel, msg: Message) !void {
307308 try self.messages.append(msg);
308309 std.sort.insertion(Message, self.messages.items, {}, Message.compareTime);
310310+ if (self.scroll.msg_offset) |offset| {
311311+ self.scroll.msg_offset = offset + 1;
312312+ }
309313 }
310314311315 fn onChange(ptr: ?*anyopaque, _: *vxfw.EventContext, input: []const u8) anyerror!void {
···375379 };
376380 }
377381382382+ pub fn doSelect(self: *Channel) void {
383383+ // Set the state of the last_read_indicator
384384+ self.last_read_indicator = self.last_read;
385385+ }
386386+378387 fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
379388 const self: *Channel = @ptrCast(@alignCast(ptr));
380389 switch (event) {
···415424 var style: vaxis.Style = .{};
416425 if (selected) style.bg = .{ .index = 8 };
417426 if (self.has_mouse) style.bg = .{ .index = 8 };
418418- if (self.client.app.selectedBuffer()) |buffer| {
419419- switch (buffer) {
420420- .client => {},
421421- .channel => |channel| {
422422- if (channel == self and self.messageViewIsAtBottom()) {
423423- self.has_unread = false;
424424- self.has_unread_highlight = false;
425425- }
426426- },
427427- }
428428- }
429427 if (self.has_unread) {
430428 style.fg = .{ .index = 4 };
431429 style.bold = true;
···507505508506 /// issue a MARKREAD command for this channel. The most recent message in the channel will be used as
509507 /// the last read time
510510- pub fn markRead(self: *Channel) !void {
508508+ pub fn markRead(self: *Channel) Allocator.Error!void {
511509 self.has_unread = false;
512510 self.has_unread_highlight = false;
513511 const last_msg = self.messages.getLastOrNull() orelse return;
···911909 var sender = last_msg.senderNick() orelse "";
912910 var this_instant = last_msg.localTime(&self.client.app.tz);
913911912912+ // True when we *don't* need to scroll to last message. False if we do. We will turn this
913913+ // true when we have it the last message
914914+ var did_scroll_to_last_read = !self.scroll_to_last_read;
914915 while (iter.next()) |msg| {
915916 // Break if we have gone past the top of the screen
916916- if (row < 0) break;
917917+ if (row < 0 and did_scroll_to_last_read) break;
917918918919 // Get the sender nickname of the *next* message. Next meaning next message in the
919920 // iterator, which is chronologically the previous message since we are printing in
···11091110 const this = this_instant.unixTimestamp();
11101111 const next = next_instant.unixTimestamp();
1111111211131113+ // If this message is before last_read, we did any scroll_to_last_read. Set the flag to
11141114+ // true
11151115+ if (this <= self.last_read) did_scroll_to_last_read = true;
11161116+11121117 if (this > self.last_read_indicator and next <= self.last_read_indicator) {
11131118 const bot = "─";
11141119 var writer = try std.ArrayList(u8).initCapacity(ctx.arena, bot.len * max.width);
···11481153 try self.client.requestHistory(.before, self);
11491154 }
1150115511561156+ // If we scroll_to_last_read, we probably need to reposition all of our children. We also
11571157+ // check that we have messages, and if we do that the top message is outside the viewport.
11581158+ // If we don't have messages, or the top message is within the viewport, we don't have to
11591159+ // reposition
11601160+ if (self.scroll_to_last_read and
11611161+ children.items.len > 0 and
11621162+ children.getLast().origin.row < 0)
11631163+ {
11641164+ // We will adjust the origin of each item so that the last item we added has an origin
11651165+ // of 0
11661166+ const adjustment: u16 = @intCast(@abs(children.getLast().origin.row));
11671167+ for (children.items) |*item| {
11681168+ item.origin.row += adjustment;
11691169+ }
11701170+ // Our scroll offset gets adjusted as well
11711171+ self.scroll.offset += adjustment;
11721172+ }
11731173+11741174+ if (did_scroll_to_last_read) {
11751175+ self.scroll_to_last_read = false;
11761176+ }
11771177+11781178+ if (self.has_unread and self.messageViewIsAtBottom()) {
11791179+ try self.markRead();
11801180+ }
11811181+11511182 return .{
11521183 .size = max,
11531184 .widget = self.messageViewWidget(),
···21012132 var channel = try client.getOrCreateChannel(target);
21022133 channel.last_read = @intCast(last_read.unixTimestamp());
21032134 const last_msg = channel.messages.getLastOrNull() orelse return;
21042104- if (last_msg.timestamp_s > channel.last_read)
21052105- channel.has_unread = true
21062106- else
21072107- channel.has_unread = false;
21352135+ channel.has_unread = last_msg.timestamp_s > channel.last_read;
21362136+ channel.has_unread_highlight = channel.has_unread;
21082137 },
21092138 .PART => {
21102139 // get the user
···21542183 const entry = client.batches.getEntry(tag) orelse @panic("TODO");
21552184 var channel = entry.value_ptr.*;
21562185 try channel.insertMessage(msg2);
21572157- if (channel.scroll.msg_offset) |offset| {
21582158- channel.scroll.msg_offset = offset + 1;
21592159- }
21602186 channel.at_oldest = false;
21612187 if (msg2.timestamp_s > channel.last_read) {
21622188 channel.has_unread = true;