╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │819 // a link to a different parent page always navigates; within a page, a │ │820 // files-directory / commits-page / list-window change navigates while tab │ │821 // links stay in-page (header-handled). │ │799 - if (route.parent() != current.parent() or RoutablePage.repoPageChanged(route, current) or RoutablePage.homePageChanged(route, current)) return route; │ │822 + if (route.parent() != current.parent() or RoutablePage.repoPageChanged(route, current) or RoutablePage.homePageChanged(route, current) or RoutablePage.userPageChanged(route, current)) return route;│ │823 return null; │ │824 } │ │825 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Auth/Login.zig│ ╰─────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │43 errdefer box.deinit(allocator); │ │44 // marks this subtree as an HTML form scope for the web overlay │ │45 box.getFocus().kind = .{ .custom = switch (session.data.current_page) { │ │46 - .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/login", .{name.slice()}),│ │46 + .user => |u| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/login", .{u.name.slice()}), │ │47 + .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/login", .{name.slice()}), │ │48 else => "form:/login", │ │49 } }; │ │50 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Auth/Logout.zig│ ╰──────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │31 errdefer box.deinit(allocator); │ │32 // marks this subtree as an HTML form scope for the web overlay │ │33 box.getFocus().kind = .{ .custom = switch (session.data.current_page) { │ │34 - .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/logout", .{name.slice()}),│ │34 + .user => |u| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/logout", .{u.name.slice()}), │ │35 + .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/logout", .{name.slice()}), │ │36 else => "form:/logout", │ │37 } }; │ │38 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Home/Repos.zig│ ╰─────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │33 const empty: Self = .{ .repos = &.{}, .owner_names = &.{}, .after = after, .next_after = null }; │ │34 │ │35 // the repos ordered by creation time (oldest first); absent until the first │ │36 - // repo exists. keyed by [timestamp][event-id], value = event-id. │ │37 - const created_ts_to_repo_id_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "created-ts->repo-id")) orelse return empty;│ │38 - const created_ts_to_repo_id = try DB.SortedMap(.read_only).init(created_ts_to_repo_id_cursor); │ │39 - const count = try created_ts_to_repo_id.count(); │ │36 + // repo exists. keyed by orderKey ([timestamp][event-id]); the trailing bytes │ │37 + // of each key are the repo event id. │ │38 + const repo_id_set_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "repo-id-set")) orelse return empty; │ │39 + const repo_id_set = try DB.SortedSet(.read_only).init(repo_id_set_cursor); │ │40 + const count = try repo_id_set.count(); │ │41 │ │42 const event_id_to_repo_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "event-id->repo")) orelse return empty; │ │43 const event_id_to_repo = try DB.HashMap(.read_only).init(event_id_to_repo_cursor); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │45 // read the window [after, after+page_size) with one seek to the start rank,│ │46 // then a sequential walk │ │47 const end = @min(after + page_size, count); │ │47 - var iter = try created_ts_to_repo_id.iteratorFromIndex(after); │ │48 + var iter = try repo_id_set.iteratorFromIndex(after); │ │49 var i = after; │ │50 while (i < end) : (i += 1) { │ │51 var id_cursor = (try iter.next()) orelse break; │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │52 const id_kv = try id_cursor.readKeyValuePair(); │ │52 - var event_id: [evt.event_id_size]u8 = undefined; │ │53 - _ = try id_kv.value_cursor.readBytes(&event_id); │ │54 - const repo_cursor = try event_id_to_repo.getCursor(hash.hashInt(hash_kind, &event_id)) orelse continue;│ │53 + var order_key: [@sizeOf(u64) + evt.event_id_size]u8 = undefined; │ │54 + _ = try id_kv.key_cursor.readBytes(&order_key); │ │55 + const event_id = order_key[@sizeOf(u64)..]; │ │56 + const repo_cursor = try event_id_to_repo.getCursor(hash.hashInt(hash_kind, event_id)) orelse continue; │ │57 const repo_map = try DB.HashMap(.read_only).init(repo_cursor); │ │58 const repo_event = try evt.read(evt.Repo, DB, hash_kind, arena, repo_map); │ │59 try repos.append(arena.allocator(), repo_event); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Home/Users.zig│ ╰─────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │29 var users: std.ArrayList(evt.User.Safe) = .empty; │ │30 │ │31 // the users ordered by creation time (oldest first); absent until the first │ │32 - // user exists. keyed by [timestamp][event-id], value = event-id. │ │33 - const created_ts_to_user_id_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "created-ts->user-id")) orelse│ │32 + // user exists. keyed by orderKey ([timestamp][event-id]); the trailing bytes │ │33 + // of each key are the user event id. │ │34 + const user_id_set_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "user-id-set")) orelse │ │35 return .{ .users = &.{}, .after = after, .next_after = null }; │ │35 - const created_ts_to_user_id = try DB.SortedMap(.read_only).init(created_ts_to_user_id_cursor); │ │36 - const count = try created_ts_to_user_id.count(); │ │36 + const user_id_set = try DB.SortedSet(.read_only).init(user_id_set_cursor); │ │37 + const count = try user_id_set.count(); │ │38 │ │39 const event_id_to_user_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "event-id->user")) orelse │ │40 return .{ .users = &.{}, .after = after, .next_after = null }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │43 // read the window [after, after+page_size) with one seek to the start rank,│ │44 // then a sequential walk │ │45 const end = @min(after + page_size, count); │ │45 - var iter = try created_ts_to_user_id.iteratorFromIndex(after); │ │46 + var iter = try user_id_set.iteratorFromIndex(after); │ │47 var i = after; │ │48 while (i < end) : (i += 1) { │ │49 var id_cursor = (try iter.next()) orelse break; │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │50 const id_kv = try id_cursor.readKeyValuePair(); │ │50 - var event_id: [evt.event_id_size]u8 = undefined; │ │51 - _ = try id_kv.value_cursor.readBytes(&event_id); │ │52 - const user_cursor = try event_id_to_user.getCursor(hash.hashInt(hash_kind, &event_id)) orelse continue;│ │51 + var order_key: [@sizeOf(u64) + evt.event_id_size]u8 = undefined; │ │52 + _ = try id_kv.key_cursor.readBytes(&order_key); │ │53 + const event_id = order_key[@sizeOf(u64)..]; │ │54 + const user_cursor = try event_id_to_user.getCursor(hash.hashInt(hash_kind, event_id)) orelse continue; │ │55 const user_map = try DB.HashMap(.read_only).init(user_cursor); │ │56 const user_event = try evt.read(evt.User, DB, hash_kind, arena, user_map); │ │57 try users.append(arena.allocator(), evt.User.Safe.init(user_event)); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │38 │ │39 // on the web each control posts to a page-scoped form │ │40 box.getFocus().kind = .{ .custom = switch (session.data.current_page) { │ │41 - .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{name.slice()}),│ │41 + .user => |u| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{u.name.slice()}), │ │42 + .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{name.slice()}), │ │43 else => "form:/ansi", │ │44 } }; │ │45 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯