//! Made-up mail for the prototypes: Gmail's system labels, user labels //! (one nested, as Gmail shows "Work/Hiring"), and threads spread across //! them, some unread, some starred, some in several labels at once. const std = @import("std"); const testing = std.testing; /// A Gmail label: the system ones, then the user's. Starred is a label in /// Gmail too; unread is not one here, since every view shows it anyway. pub const Label = enum { inbox, starred, sent, drafts, spam, trash, work, hiring, family, receipts, travel, newsletters, waiting, someday, pub const Set = std.EnumSet(Label); pub const Kind = enum { system, user }; pub const Info = struct { /// What Gmail calls it; a slash nests it under the label before it. name: []const u8, kind: Kind, /// The key that picks it where a prototype offers one. key: u8, color: Rgb, }; pub fn info(self: Label) Info { return infos.get(self); } /// The label's key as a string, for a keycap. pub fn keyCap(self: Label) []const u8 { return (&infos.getPtrConst(self).key)[0..1]; } /// The label whose key is `c`. pub fn fromKey(c: u8) ?Label { for (std.enums.values(Label)) |label| { if (label.info().key == c) return label; } return null; } }; pub const Rgb = struct { r: u8, g: u8, b: u8 }; const gray: Rgb = .{ .r = 0x6b, .g = 0x72, .b = 0x80 }; const infos: std.EnumArray(Label, Label.Info) = .init(.{ .inbox = .{ .name = "Inbox", .kind = .system, .key = 'i', .color = .{ .r = 0x3b, .g = 0x6e, .b = 0xd8 } }, .starred = .{ .name = "Starred", .kind = .system, .key = 's', .color = .{ .r = 0xd9, .g = 0xa4, .b = 0x06 } }, .sent = .{ .name = "Sent", .kind = .system, .key = 't', .color = gray }, .drafts = .{ .name = "Drafts", .kind = .system, .key = 'd', .color = gray }, .spam = .{ .name = "Spam", .kind = .system, .key = '!', .color = .{ .r = 0xb9, .g = 0x1c, .b = 0x1c } }, .trash = .{ .name = "Trash", .kind = .system, .key = '#', .color = gray }, .work = .{ .name = "Work", .kind = .user, .key = 'w', .color = .{ .r = 0x25, .g = 0x63, .b = 0xeb } }, .hiring = .{ .name = "Work/Hiring", .kind = .user, .key = 'h', .color = .{ .r = 0x7c, .g = 0x3a, .b = 0xed } }, .family = .{ .name = "Family", .kind = .user, .key = 'f', .color = .{ .r = 0x16, .g = 0xa3, .b = 0x4a } }, .receipts = .{ .name = "Receipts", .kind = .user, .key = 'r', .color = .{ .r = 0xea, .g = 0x58, .b = 0x0c } }, .travel = .{ .name = "Travel", .kind = .user, .key = 'v', .color = .{ .r = 0x08, .g = 0x91, .b = 0xb2 } }, .newsletters = .{ .name = "Newsletters", .kind = .user, .key = 'n', .color = .{ .r = 0xdb, .g = 0x27, .b = 0x77 } }, .waiting = .{ .name = "Waiting", .kind = .user, .key = 'a', .color = .{ .r = 0xca, .g = 0x8a, .b = 0x04 } }, .someday = .{ .name = "Someday", .kind = .user, .key = 'y', .color = .{ .r = 0x64, .g = 0x74, .b = 0x8b } }, }); pub const Thread = struct { from: []const u8, subject: []const u8, snippet: []const u8, /// How long ago the newest message arrived. minutes_ago: u32, /// How many messages the conversation holds. messages: u8, labels: Label.Set, unread: bool, }; const hour = 60; const day = 24 * hour; fn t( from: []const u8, subject: []const u8, snippet: []const u8, minutes_ago: u32, messages: u8, ids: []const Label, unread: bool, ) Thread { return .{ .from = from, .subject = subject, .snippet = snippet, .minutes_ago = minutes_ago, .messages = messages, .labels = .initMany(ids), .unread = unread, }; } /// Newest first. pub const threads = [_]Thread{ t( "Maya Okafor", "Offsite agenda, final", "Moved the roadmap session to Thursday morning so Ravi can join from Lisbon.", 7, 4, &.{ .inbox, .work, .starred }, true, ), t( "GitHub", "[siv] CI failed on main", "The test-render-vulkan job failed after 14m 02s: validation error in vkQueueSubmit2.", 22, 1, &.{ .inbox, .work }, true, ), t( "Mom", "Sunday dinner?", "Your dad is making the lamb again. Bring the kids, and the good bread if you pass the bakery.", 41, 3, &.{ .inbox, .family }, true, ), t( "Priya Raman", "Candidate packet: Jonas Weber", "Attached his take-home and the panel notes. Strong on systems, quiet in the design round.", 1 * hour + 10, 2, &.{ .inbox, .work, .hiring }, true, ), t( "Delta", "Your trip to Lisbon is confirmed", "Confirmation HX7K2P. Departs Tue 6:05 PM from MSP, arrives Wed 10:40 AM.", 2 * hour, 1, &.{ .inbox, .travel, .receipts }, false, ), t( "Ravi Menon", "Re: render thread priorities", "I tried SCHED_FIFO for the compositor thread and frame pacing got worse, not better.", 3 * hour, 6, &.{ .inbox, .work }, false, ), t( "Zig Weekly", "Issue 212: the 0.17 build system", "This week: addPassthruArgs, cache poisoning, and why @cImport had to go.", 5 * hour, 1, &.{ .inbox, .newsletters }, true, ), t( "Sam Torres", "Lunch tomorrow", "Taco place on Lake St at noon? I'll grab a table if I'm early.", 6 * hour, 2, &.{.inbox}, false, ), t( "Stripe", "Receipt from Fastmail", "Amount paid $60.00. Standard plan, 12 months. Card ending 4242.", 9 * hour, 1, &.{.receipts}, false, ), t( "Lena Fischer", "Re: offer letter", "Thank you! I've signed and sent it back. Looking forward to starting on the 1st.", 1 * day, 5, &.{ .inbox, .work, .hiring, .starred }, false, ), t( "Dad", "Photos from the lake", "Finally got these off the camera. The one of Ada with the fish is my favorite.", 1 * day + 2 * hour, 1, &.{ .family, .starred }, false, ), t( "Hotel Avenida", "Your reservation request", "We are pleased to confirm 3 nights, arriving 12 Nov. Breakfast is included.", 1 * day + 5 * hour, 2, &.{ .inbox, .travel }, false, ), t( "Jonas Weber", "Question about the take-home", "Should the parser stream, or is it fine to read the whole file first?", 1 * day + 8 * hour, 3, &.{ .work, .hiring, .waiting }, false, ), t( "Linear", "3 issues assigned to you", "PAR-12 Message list virtualization, PAR-14 Label picker, PAR-15 Undo archive.", 2 * day, 1, &.{ .inbox, .work }, true, ), t( "Maya Okafor", "Budget numbers for Q1", "Can you sanity check the infra line? It looks high but I might be double counting.", 2 * day + 3 * hour, 4, &.{ .work, .waiting }, false, ), t( "Amazon", "Your order has shipped", "Keychron Q1 Pro will arrive Friday. Track your package.", 2 * day + 6 * hour, 1, &.{.receipts}, false, ), t( "Ada's school", "Field trip permission slip", "Please sign and return by Friday. The trip to the science museum is on the 14th.", 3 * day, 1, &.{ .inbox, .family, .waiting }, true, ), t( "This Week in Rust", "Issue 571", "Rust 1.92 ships async closures in traits, plus a look at the new trait solver.", 3 * day + 4 * hour, 1, &.{.newsletters}, false, ), t( "Ravi Menon", "Lisbon dinner spots", "Cervejaria Ramiro is a must. Book ahead or come at 5 and wait with everyone else.", 4 * day, 2, &.{ .travel, .starred }, false, ), t( "Ana Lima", "Re: siv accessibility review", "Orca reads the sidebar fine, but the table headers announce twice. Notes inline.", 4 * day + 2 * hour, 7, &.{ .work, .starred }, false, ), t( "Fastmail", "Your storage is 80% full", "You're using 24 GB of 30 GB. Upgrade or clear out old messages.", 5 * day, 1, &.{.inbox}, false, ), t( "Grandma", "Recipe you asked for", "Two cups flour, one of sugar, a pinch of salt, and the secret is the browned butter.", 6 * day, 2, &.{ .family, .someday }, false, ), t( "Priya Raman", "Panel schedule next week", "Can you take the systems round on Tuesday at 2? Two candidates back to back.", 6 * day + 5 * hour, 3, &.{ .work, .hiring }, false, ), t( "Kickstarter", "A project you backed has an update", "Production is two weeks behind, but the first units ship in March.", 8 * day, 1, &.{ .newsletters, .someday }, false, ), t( "Sam Torres", "Climbing Saturday?", "The new gym in Northeast has a bouldering wall. Want to try it?", 9 * day, 4, &.{.someday}, false, ), t( "City of Minneapolis", "Water bill available", "Your bill of $84.12 is due on the 28th. Pay online or by phone.", 10 * day, 1, &.{ .receipts, .waiting }, false, ), t( "Mom", "Thanksgiving plans", "We're doing it at your aunt's this year. Can you bring pie? Two, maybe three.", 11 * day, 9, &.{.family}, false, ), t( "Lobste.rs Digest", "Top stories this week", "A tour of io_uring, Zig's new std.Io, and why your terminal is slow.", 12 * day, 1, &.{.newsletters}, false, ), t( "Maya Okafor", "Re: performance reviews", "Drafts are due on the 30th. I'll send the template this week.", 14 * day, 3, &.{.work}, false, ), t( "Unknown sender", "You have won a gift card", "Claim your $500 reward now. Click the link below before it expires.", 15 * day, 1, &.{.spam}, true, ), t( "Me", "Notes to self: parcel", "Keyboard first. Every action has a key and a click. Labels are first class.", 2 * hour, 1, &.{ .sent, .starred, .someday }, false, ), t("Me", "Re: Lunch tomorrow", "Sounds good, see you at noon.", 5 * hour, 2, &.{.sent}, false), t( "Me", "Draft: reply to Ana", "Thanks for the review. On the double announcement, I think the header cell", 30, 1, &.{.drafts}, false, ), }; test "every label's key is distinct" { var seen: std.bit_set.Integer(256) = .empty; for (std.enums.values(Label)) |label| { try testing.expect(!seen.isSet(label.info().key)); seen.set(label.info().key); } }