╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Attachment.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
45 allocator: std.mem.Allocator, 46 box: *wgt.Box(ui.Widget), 47 session: *ui.Session, 48 identity: []const u8, 49 target_url: []const u8, 50 attachments: []const WithId, 51 ) !void { 52 const pa = session.page_arena.allocator(); 53 for (attachments) |entry| { 54 var row = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .direction = .horiz }); 55 errdefer row.deinit(allocator); 56 57 var tb = try wgt.TextBox.init(allocator, entry.name, .{ 58 .border = .single, 59 .round_corners = true, 60 .wrap_kind = .none, 61 .top_label = .{ .text = " attachment " }, 62 }); 63 errdefer tb.deinit(allocator); 64 tb.getFocus().mode = .all; 65 tb.getFocus().kind = .{ .custom = try pa.print("{s}{s}", .{ ui.raw_link_prefix, try url(session.page_arena, identity, &entry.id) }) }; 66 try row.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); 67 68 row.getFocus().kind = .{ .custom = try pa.print("form:{s}/attachment:{s}/remove", .{ target_url, &entry.id }) }; 69 var remove = try wgt.TextBox.init(allocator, "✕", .{ .border = .single, .round_corners = true, .wrap_kind = .none }); 70 errdefer remove.deinit(allocator); 71 remove.getFocus().mode = .all; 72 remove.getFocus().kind = .{ .custom = "submit" }; 73 try row.children.put(allocator, remove.getFocus().id, .{ .widget = .{ .text_box = remove }, .rect = null, .min_size = .{ .width = 3, .height = null } }); 74 75 row.getFocus().child_id = row.children.keys()[0]; 76 try box.children.put(allocator, row.getFocus().id, .{ .widget = .{ .box = row }, .rect = null, .min_size = null }); 77 } 78 } 79 80 // the attachment id carried by a removable row 81 pub fn removeId(row: *wgt.Box(ui.Widget)) ?[evt.event_id_size]u8 { 82 const prefix = "form:"; 83 const infix = "/attachment:"; 84 const suffix = "/remove"; 85 const action = switch (row.getFocus().kind) { 86 .custom => |custom| custom, 87 else => return null, 88 }; 89 if (!std.mem.startsWith(u8, action, prefix) or !std.mem.endsWith(u8, action, suffix)) return null; 90 const at = std.mem.lastIndexOf(u8, action, infix) orelse return null; 91 const id = action[at + infix.len .. action.len - suffix.len]; 92 if (id.len != evt.event_id_size * 2) return null; 93 var bytes: [evt.event_id_size]u8 = undefined; 94 _ = std.fmt.hexToBytes(&bytes, id) catch return null; 95 return bytes; 96 } 97 98 // the url an attachment's bytes are served from 99 pub fn url(page_arena: *std.heap.ArenaAllocator, identity: []const u8, id: []const u8) ![]const u8 { 100 return if (identity.len == 0) 101 try page_arena.allocator().print("/attachment:{s}", .{id}) 102 else 103 try page_arena.allocator().print("/{s}/attachment:{s}", .{ identity, id }); 104 } 105