╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │1377 const inner = self.detailInner(index); │ │1378 const cid = inner.getFocus().child_id orelse return false; │ │1379 return inner.children.getIndex(cid) == title_child_index; │ │1380 + } │ │1381 + │ │1382 + fn authorPresent(self: *View, index: usize) bool { │ │1383 + return self.author_id[index] != null; │ │1384 } │ │1385 │ │1386 + fn authorFocused(self: *View, index: usize) bool { │ │1387 + const inner = self.detailInner(index); │ │1388 + const cid = inner.getFocus().child_id orelse return false; │ │1389 + return cid == self.author_id[index]; │ │1390 + } │ │1391 + │ │1392 + // the author sits right under the title at the top of the pane, so │ │1393 + // focusing it scrolls there. │ │1394 + fn focusAuthor(self: *View, index: usize, root_focus: *Focus) void { │ │1395 + const id = self.author_id[index] orelse return; │ │1396 + root_focus.setFocus(id); │ │1397 + self.detailScroll(index).y = 0; │ │1398 + } │ │1399 + │ │1400 // the title sits at the top of the pane, so focusing it scrolls there.│ │1401 fn focusTitle(self: *View, index: usize, root_focus: *Focus) void { │ │1402 const inner = self.detailInner(index); │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │346 }); │ │347 } │ │348 │ │349 -// the email the request's session authors event commits as │ │350 -fn authorEmail( │ │349 +// the logged-in user the request's session cookie names, or null for local│ │350 +// mode / no session / an unknown token │ │351 +fn sessionUser( │ │352 io: std.Io, │ │353 allocator: std.mem.Allocator, │ │354 arena: *std.heap.ArenaAllocator, │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │355 request: *std.http.Server.Request, │ │356 host: Host, │ │356 -) ![]const u8 { │ │357 +) !?evt.User { │ │358 const remote = switch (host) { │ │359 .remote => |remote| remote, │ │359 - .local => return "user@haxy", │ │360 + .local => return null, │ │361 }; │ │361 - const token = getCookieValue(request, cookie_name) orelse return "user@haxy"; │ │362 + const token = getCookieValue(request, cookie_name) orelse return null; │ │363 var user_id: [evt.event_id_size]u8 = undefined; │ │363 - if (!remote.session_store.lookup(token, &user_id)) return "user@haxy"; │ │364 + if (!remote.session_store.lookup(token, &user_id)) return null; │ │365 │ │366 var repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = remote.admin_repo_path });│ │367 defer repo.deinit(io, allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │368 const moment = try evt.currentMoment(evt.admin_repo_opts, &repo); │ │368 - const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, arena, &user_id)) orelse unreachable;│ │369 - return user.email; │ │369 + return (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, arena, &user_id)) orelse unreachable; │ │370 } │ │371 │ │372 // create an issue in the repo the form's page names (base is │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │414 const event = evt.EventWithId{ │ │415 .id = event_id_hex, │ │416 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │417 - .author_email = try authorEmail(io, allocator, &author_arena, request, host), │ │417 + .author_email = if (try sessionUser(io, allocator, &author_arena, request, host)) |author| author.email else "user@haxy",│ │418 .event = .{ .issue = .{ │ │419 .title = title, │ │420 .description = description, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │562 │ │563 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │564 defer author_arena.deinit(); │ │565 - const author_email = try authorEmail(io, allocator, &author_arena, request, host); │ │565 + const author_email = if (try sessionUser(io, allocator, &author_arena, request, host)) |author| author.email else "user@haxy";│ │566 │ │567 updateIssue(io, allocator, host, parts, .{ .status = status }, author_email) catch |err| switch (err) { │ │568 error.NotFound => { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │633 │ │634 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │635 defer author_arena.deinit(); │ │636 - const author_email = try authorEmail(io, allocator, &author_arena, request, host); │ │636 + const author_email = if (try sessionUser(io, allocator, &author_arena, request, host)) |author| author.email else "user@haxy";│ │637 │ │638 updateIssue(io, allocator, host, parts, .{ .fields = .{ │ │639 .title = title, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯