╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────╮ │1 const std = @import("std"); │ │2 const xit = @import("xit"); │ │3 const rp = xit.repo; │ │4 +const hash = xit.hash; │ │5 const xitui = xit.xitui; │ │6 const term = xitui.terminal;│ │7 const wgt = xitui.widget; │ ╰───────────────────────────────╯ ╭──────────────────────────────────────────────────────╮ │26 home_repos, │ │27 home_auth, │ │28 │ │29 + pub const default: RoutablePage = .home_users;│ │30 + │ │31 pub fn url(self: RoutablePage) []const u8 { │ │32 return switch (self) { │ │33 .home_users => "/users", │ ╰──────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │37 } │ │38 │ │39 pub fn fromUrl(path: []const u8) ?RoutablePage { │ │37 - if (std.mem.eql(u8, path, "/") or std.mem.eql(u8, path, "/users")) return .home_users;│ │40 + if (std.mem.eql(u8, path, "/")) return default; │ │41 + if (std.mem.eql(u8, path, "/users")) return .home_users; │ │42 if (std.mem.eql(u8, path, "/repos")) return .home_repos; │ │43 if (std.mem.eql(u8, path, "/auth")) return .home_auth; │ │44 return null; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 }; │ │47 │ │48 // per-connection mutable state. each SSH session / web session / local TUI │ │45 -// run gets its own. `data` is the subset that round-trips between server │ │46 -// and wasm; the other fields are runtime context only and stay local. │ │49 +// run gets its own │ │50 pub const Session = struct { │ │48 - data: SessionData = .{}, │ │49 - arena: ?*std.heap.ArenaAllocator = null, │ │50 - haxy_moment: ?DB.HashMap(.read_only) = null, │ │51 -}; │ │51 + data: Data = .{}, // serializable data sent down to web client │ │52 + arena: *std.heap.ArenaAllocator, │ │53 + haxy_moment: ?DB.HashMap(.read_only) = null, // db cursor (null on the wasm side) │ │54 + │ │55 + pub const Data = struct { │ │56 + user_id: ?[]const u8 = null, │ │57 + // a transient outcome to surface from the last /login POST attempt │ │58 + login_failure: ?Home.Auth.Login.Failure = null, │ │59 + current_page: RoutablePage = .default, │ │60 + }; │ │61 + │ │62 + pub fn init( │ │63 + comptime repo_opts: rp.RepoOpts(.xit), │ │64 + arena: *std.heap.ArenaAllocator, │ │65 + repo: *rp.Repo(.xit, repo_opts), │ │66 + data: Data, │ │67 + ) !Session { │ │68 + const RepoDB = rp.Repo(.xit, repo_opts).DB; │ │69 + │ │70 + const history = try RepoDB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │71 + │ │72 + const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │73 + const moment = try RepoDB.HashMap(.read_only).init(moment_cursor); │ │74 + │ │75 + const last_object_id_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy-last-object-id")) orelse return error.NotFound;│ │76 + var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │77 + _ = try last_object_id_cursor.readBytes(&last_object_id); │ │78 │ │53 -// the serializable part of session │ │54 -pub const SessionData = struct { │ │55 - user_id: ?[]const u8 = null, │ │56 - // a transient outcome to surface from the last /login POST attempt │ │57 - login_failure: ?Home.Auth.Login.Failure = null, │ │58 - current_page: RoutablePage = .home_users, │ │79 + const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │80 + const haxy = try RepoDB.ArrayList(.read_only).init(haxy_cursor); │ │81 + │ │82 + const haxy_moments_cursor = try haxy.getCursor(-1) orelse return error.NotFound; │ │83 + const haxy_moments = try RepoDB.HashMap(.read_only).init(haxy_moments_cursor); │ │84 + │ │85 + const haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound;│ │86 + const haxy_moment = try RepoDB.HashMap(.read_only).init(haxy_moment_cursor); │ │87 + │ │88 + return .{ │ │89 + .data = data, │ │90 + .arena = arena, │ │91 + .haxy_moment = haxy_moment, │ │92 + }; │ │93 + } │ │94 }; │ │95 │ │96 // what the server hands to the client (and what main_wasm parses on _start). │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 97 // keeps Page free of any per-request session state. │ │ 98 pub const Snapshot = struct { │ │ 99 page: Page, │ │ 65 - session: SessionData = .{}, │ │100 + session: Session.Data = .{}, │ │101 }; │ │102 │ │103 pub fn run(io: std.Io, allocator: std.mem.Allocator, page: *const Page, session: *Session) !void {│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │169 │ │170 pub fn initRoot(allocator: std.mem.Allocator, page: *const Page, session: *Session) !Widget {│ │171 var root: Widget = switch (page.*) { │ │137 - .home => |*p| .{ .home = try Home.View.init(allocator, p, session) }, │ │172 + .home => |*p| .{ .home = try .init(allocator, p, session) }, │ │173 }; │ │174 errdefer root.deinit(allocator); │ │175 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯