this repo has no description
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

ui: scroll to unread message on select

+47 -21
+1 -1
src/app.zig
··· 579 579 if (channel == target) { 580 580 self.buffer_list.cursor = i; 581 581 self.buffer_list.ensureScroll(); 582 - if (target.messageViewIsAtBottom()) target.has_unread = false; 582 + channel.doSelect(); 583 583 if (self.ctx) |ctx| { 584 584 ctx.requestFocus(channel.text_field.widget()) catch {}; 585 585 }
+46 -20
src/irc.zig
··· 126 126 // The location of the last read indicator. This doesn't necessarily match the state of 127 127 // last_read 128 128 last_read_indicator: u32 = 0, 129 + scroll_to_last_read: bool = true, 129 130 has_unread: bool = false, 130 131 has_unread_highlight: bool = false, 131 132 ··· 306 307 pub fn insertMessage(self: *Channel, msg: Message) !void { 307 308 try self.messages.append(msg); 308 309 std.sort.insertion(Message, self.messages.items, {}, Message.compareTime); 310 + if (self.scroll.msg_offset) |offset| { 311 + self.scroll.msg_offset = offset + 1; 312 + } 309 313 } 310 314 311 315 fn onChange(ptr: ?*anyopaque, _: *vxfw.EventContext, input: []const u8) anyerror!void { ··· 375 379 }; 376 380 } 377 381 382 + pub fn doSelect(self: *Channel) void { 383 + // Set the state of the last_read_indicator 384 + self.last_read_indicator = self.last_read; 385 + } 386 + 378 387 fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { 379 388 const self: *Channel = @ptrCast(@alignCast(ptr)); 380 389 switch (event) { ··· 415 424 var style: vaxis.Style = .{}; 416 425 if (selected) style.bg = .{ .index = 8 }; 417 426 if (self.has_mouse) style.bg = .{ .index = 8 }; 418 - if (self.client.app.selectedBuffer()) |buffer| { 419 - switch (buffer) { 420 - .client => {}, 421 - .channel => |channel| { 422 - if (channel == self and self.messageViewIsAtBottom()) { 423 - self.has_unread = false; 424 - self.has_unread_highlight = false; 425 - } 426 - }, 427 - } 428 - } 429 427 if (self.has_unread) { 430 428 style.fg = .{ .index = 4 }; 431 429 style.bold = true; ··· 507 505 508 506 /// issue a MARKREAD command for this channel. The most recent message in the channel will be used as 509 507 /// the last read time 510 - pub fn markRead(self: *Channel) !void { 508 + pub fn markRead(self: *Channel) Allocator.Error!void { 511 509 self.has_unread = false; 512 510 self.has_unread_highlight = false; 513 511 const last_msg = self.messages.getLastOrNull() orelse return; ··· 911 909 var sender = last_msg.senderNick() orelse ""; 912 910 var this_instant = last_msg.localTime(&self.client.app.tz); 913 911 912 + // True when we *don't* need to scroll to last message. False if we do. We will turn this 913 + // true when we have it the last message 914 + var did_scroll_to_last_read = !self.scroll_to_last_read; 914 915 while (iter.next()) |msg| { 915 916 // Break if we have gone past the top of the screen 916 - if (row < 0) break; 917 + if (row < 0 and did_scroll_to_last_read) break; 917 918 918 919 // Get the sender nickname of the *next* message. Next meaning next message in the 919 920 // iterator, which is chronologically the previous message since we are printing in ··· 1109 1110 const this = this_instant.unixTimestamp(); 1110 1111 const next = next_instant.unixTimestamp(); 1111 1112 1113 + // If this message is before last_read, we did any scroll_to_last_read. Set the flag to 1114 + // true 1115 + if (this <= self.last_read) did_scroll_to_last_read = true; 1116 + 1112 1117 if (this > self.last_read_indicator and next <= self.last_read_indicator) { 1113 1118 const bot = "─"; 1114 1119 var writer = try std.ArrayList(u8).initCapacity(ctx.arena, bot.len * max.width); ··· 1148 1153 try self.client.requestHistory(.before, self); 1149 1154 } 1150 1155 1156 + // If we scroll_to_last_read, we probably need to reposition all of our children. We also 1157 + // check that we have messages, and if we do that the top message is outside the viewport. 1158 + // If we don't have messages, or the top message is within the viewport, we don't have to 1159 + // reposition 1160 + if (self.scroll_to_last_read and 1161 + children.items.len > 0 and 1162 + children.getLast().origin.row < 0) 1163 + { 1164 + // We will adjust the origin of each item so that the last item we added has an origin 1165 + // of 0 1166 + const adjustment: u16 = @intCast(@abs(children.getLast().origin.row)); 1167 + for (children.items) |*item| { 1168 + item.origin.row += adjustment; 1169 + } 1170 + // Our scroll offset gets adjusted as well 1171 + self.scroll.offset += adjustment; 1172 + } 1173 + 1174 + if (did_scroll_to_last_read) { 1175 + self.scroll_to_last_read = false; 1176 + } 1177 + 1178 + if (self.has_unread and self.messageViewIsAtBottom()) { 1179 + try self.markRead(); 1180 + } 1181 + 1151 1182 return .{ 1152 1183 .size = max, 1153 1184 .widget = self.messageViewWidget(), ··· 2101 2132 var channel = try client.getOrCreateChannel(target); 2102 2133 channel.last_read = @intCast(last_read.unixTimestamp()); 2103 2134 const last_msg = channel.messages.getLastOrNull() orelse return; 2104 - if (last_msg.timestamp_s > channel.last_read) 2105 - channel.has_unread = true 2106 - else 2107 - channel.has_unread = false; 2135 + channel.has_unread = last_msg.timestamp_s > channel.last_read; 2136 + channel.has_unread_highlight = channel.has_unread; 2108 2137 }, 2109 2138 .PART => { 2110 2139 // get the user ··· 2154 2183 const entry = client.batches.getEntry(tag) orelse @panic("TODO"); 2155 2184 var channel = entry.value_ptr.*; 2156 2185 try channel.insertMessage(msg2); 2157 - if (channel.scroll.msg_offset) |offset| { 2158 - channel.scroll.msg_offset = offset + 1; 2159 - } 2160 2186 channel.at_oldest = false; 2161 2187 if (msg2.timestamp_s > channel.last_read) { 2162 2188 channel.has_unread = true;