this repo has no description
3
fork

Configure Feed

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

ui: print sender

+150 -13
+150 -13
src/irc.zig
··· 526 526 527 527 fn drawMessageView(self: *Channel, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { 528 528 const max = ctx.max.size(); 529 - if (max.width == 0 or max.height == 0) { 529 + if (max.width == 0 or 530 + max.height == 0 or 531 + self.messages.items.len == 0) 532 + { 530 533 return .{ 531 534 .size = max, 532 535 .widget = self.messageViewWidget(), ··· 539 542 540 543 // Row is the row we are printing on. We add the offset to achieve our scroll location 541 544 var row: i17 = max.height + self.scroll.offset; 545 + // Message offset 546 + const offset = self.scroll.msg_offset orelse self.messages.items.len; 547 + // Timezone ref 548 + // Gutter (left side where time is printed) width 549 + const gutter_width = 6; 542 550 543 - const offset = self.scroll.msg_offset orelse self.messages.items.len; 551 + const messages = self.messages.items[0..offset]; 552 + var iter = std.mem.reverseIterator(messages); 553 + 554 + var sender: []const u8 = ""; 555 + var maybe_instant: ?zeit.Instant = null; 556 + 557 + { 558 + assert(messages.len > 0); 559 + // Initialize sender and maybe_instant to the last message values 560 + const last_msg = iter.next() orelse unreachable; 561 + // Reset iter index 562 + iter.index += 1; 563 + sender = last_msg.senderNick() orelse ""; 564 + maybe_instant = last_msg.localTime(&self.client.app.tz); 565 + } 544 566 545 - var iter = std.mem.reverseIterator(self.messages.items[0..offset]); 546 - const gutter_width = 6; 547 567 while (iter.next()) |msg| { 548 568 // Break if we have gone past the top of the screen 549 569 if (row < 0) break; 550 570 571 + // Get the sender nickname of the *next* message. Next meaning next message in the 572 + // iterator, which is chronologically the previous message since we are printing in 573 + // reverse 574 + const next_sender: []const u8 = blk: { 575 + const next_msg = iter.next() orelse break :blk ""; 576 + // Fix the index of the iterator 577 + iter.index += 1; 578 + break :blk next_msg.senderNick() orelse ""; 579 + }; 580 + 581 + // Get the server time for the *next* message. We'll use this to decide printing of 582 + // username and time 583 + const maybe_next_instant: ?zeit.Instant = blk: { 584 + const next_msg = iter.next() orelse break :blk null; 585 + // Fix the index of the iterator 586 + iter.index += 1; 587 + break :blk next_msg.localTime(&self.client.app.tz); 588 + }; 589 + 590 + defer { 591 + // After this loop, we want to save these values for the next iteration 592 + maybe_instant = maybe_next_instant; 593 + sender = next_sender; 594 + } 595 + 596 + // Message content 597 + const content: []const u8 = blk: { 598 + var param_iter = msg.paramIterator(); 599 + // First param is the target, we don't need it 600 + _ = param_iter.next() orelse unreachable; 601 + break :blk param_iter.next() orelse ""; 602 + }; 603 + 551 604 // Draw the message so we have it's wrapped height 552 - const text: vxfw.Text = .{ .text = msg.bytes }; 605 + const text: vxfw.Text = .{ .text = content }; 553 606 const child_ctx = ctx.withConstraints( 554 607 .{ .height = 0, .width = 0 }, 555 608 .{ .width = max.width -| gutter_width, .height = null }, ··· 564 617 }); 565 618 566 619 // If we have a time, print it in the gutter 567 - if (msg.localTime(&self.client.app.tz)) |instant| { 568 - const time = instant.time(); 569 - const buf = try std.fmt.allocPrint( 570 - ctx.arena, 571 - "{d:0>2}:{d:0>2}", 572 - .{ time.hour, time.minute }, 573 - ); 620 + if (maybe_instant) |instant| { 621 + var style: vaxis.Style = .{ .dim = true }; 622 + 623 + // The time text we will print 624 + const buf: []const u8 = blk: { 625 + const time = instant.time(); 626 + // Check our next time. If *this* message occurs on a different day, we want to 627 + // print the date 628 + if (maybe_next_instant) |next_instant| { 629 + const next_time = next_instant.time(); 630 + if (time.day != next_time.day) { 631 + style = .{}; 632 + break :blk try std.fmt.allocPrint( 633 + ctx.arena, 634 + "{d:0>2}/{d:0>2}", 635 + .{ @intFromEnum(time.month), time.day }, 636 + ); 637 + } 638 + } 639 + 640 + // if it is the first message, we also want to print the date 641 + if (iter.index == 0) { 642 + style = .{}; 643 + break :blk try std.fmt.allocPrint( 644 + ctx.arena, 645 + "{d:0>2}/{d:0>2}", 646 + .{ @intFromEnum(time.month), time.day }, 647 + ); 648 + } 649 + 650 + // Otherwise, we print clock time 651 + break :blk try std.fmt.allocPrint( 652 + ctx.arena, 653 + "{d:0>2}:{d:0>2}", 654 + .{ time.hour, time.minute }, 655 + ); 656 + }; 657 + 574 658 const time_text: vxfw.Text = .{ 575 659 .text = buf, 576 - .style = .{ .dim = true }, 660 + .style = style, 577 661 .softwrap = false, 578 662 }; 579 663 try children.append(.{ 580 664 .origin = .{ .row = row, .col = 0 }, 581 665 .surface = try time_text.draw(child_ctx), 582 666 }); 667 + 668 + // Check if we need to print the sender of this message. We do this when the timegap 669 + // between this message and next message is > 5 minutes, or if the sender is 670 + // different 671 + if (sender.len > 0 and 672 + printSender(sender, next_sender, maybe_instant, maybe_next_instant)) 673 + { 674 + // Back up one row to print 675 + row -= 1; 676 + // If we need to print the sender, it will be *this* messages sender 677 + const user = try self.client.getOrCreateUser(sender); 678 + const sender_text: vxfw.Text = .{ 679 + .text = user.nick, 680 + .style = .{ .fg = user.color, .bold = true }, 681 + }; 682 + try children.append(.{ 683 + .origin = .{ .row = row, .col = gutter_width }, 684 + .surface = try sender_text.draw(child_ctx), 685 + }); 686 + 687 + // Back up 1 more row for spacing 688 + row -= 1; 689 + } 583 690 } 584 691 } 585 692 ··· 597 704 return self.members.items[idx].widget(); 598 705 } 599 706 return null; 707 + } 708 + 709 + // Helper function which tells us if we should print the sender of a message, based on he 710 + // current message sender and time, and the (chronologically) previous message sent 711 + fn printSender( 712 + a_sender: []const u8, 713 + b_sender: []const u8, 714 + a_instant: ?zeit.Instant, 715 + b_instant: ?zeit.Instant, 716 + ) bool { 717 + // If sender is different, we always print the sender 718 + if (!std.mem.eql(u8, a_sender, b_sender)) return true; 719 + 720 + if (a_instant != null and b_instant != null) { 721 + const a_ts = a_instant.?.timestamp_ns; 722 + const b_ts = b_instant.?.timestamp_ns; 723 + const delta: i64 = @intCast(a_ts - b_ts); 724 + return @abs(delta) > (5 * std.time.ns_per_min); 725 + } 726 + 727 + // In any other case, we 728 + return false; 600 729 } 601 730 }; 602 731 ··· 815 944 const rhs_time = rhs.time() orelse return false; 816 945 817 946 return lhs_time.timestamp_ns < rhs_time.timestamp_ns; 947 + } 948 + 949 + /// Returns the NICK of the sender of the message 950 + pub fn senderNick(self: Message) ?[]const u8 { 951 + const src = self.source() orelse return null; 952 + if (std.mem.indexOfScalar(u8, src, '!')) |idx| return src[0..idx]; 953 + if (std.mem.indexOfScalar(u8, src, '@')) |idx| return src[0..idx]; 954 + return src; 818 955 } 819 956 }; 820 957