╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────╮ │src/event/Repo.zig│ ╰──────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │67 │ │68 const name_to_repo_id_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "name->repo-id")); │ │69 const name_to_repo_id = try DB.HashMap(.read_write).init(name_to_repo_id_cursor); │ │70 + │ │71 + const conflicts_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, conflicts_key)); │ │72 + const conflicts = try DB.SortedMap(.read_write).init(conflicts_cursor); │ │73 + │ │74 + const repo_id_to_field_to_oid_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, id_to_field_to_oid_key));│ │75 + const repo_id_to_field_to_oid = try DB.HashMap(.read_write).init(repo_id_to_field_to_oid_cursor); │ │76 │ │77 if (event_maybe) |event| { │ │78 try validateName(event.name); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │79 │ │80 var event_to_write = event; │ │81 │ │60 - // the index is keyed by "user-id/repo-name": a repo is unique per │ │61 - // owner, and the read side resolves the url's username to its user id. │ │62 - const repo_path = try std.fmt.allocPrint(arena.allocator(), "{s}/{s}", .{ event.user_id, event.name }); │ │82 + const repo_path = try event.indexKey(arena.allocator()); │ │83 │ │84 const user_id_to_repo_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "user-id->repo-id-set"));│ │85 const user_id_to_repo_id_set = try DB.HashMap(.read_write).init(user_id_to_repo_id_set_cursor); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │86 │ │87 // if this event_id already maps to a repo under a different key (its │ │88 // name or owner changed), drop the stale index entries first │ │89 + var existing_event_maybe: ?@This() = null; │ │90 const existing_cursor_maybe = try event_id_to_repo.getCursor(repo_key); │ │91 if (existing_cursor_maybe) |existing_cursor| { │ │92 const existing_repo = try DB.HashMap(.read_only).init(existing_cursor);│ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 93 const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_repo); │ │ 94 + existing_event_maybe = existing_event; │ │ 95 // updates preserve the original creation timestamp │ │ 96 event_to_write.created_ts = existing_event.created_ts; │ │ 75 - const existing_path = try std.fmt.allocPrint(arena.allocator(), "{s}/{s}", .{ existing_event.user_id, existing_event.name });│ │ 97 + │ │ 98 + // any event settles the conflict, since resolving in the ui may │ │ 99 + // keep our own values and so change nothing to detect │ │100 + if (event_oid != null) { │ │101 + _ = try conflicts.remove(&evt.orderKeyDesc(existing_event.created_ts, event_id)); │ │102 + } │ │103 + const existing_path = try existing_event.indexKey(arena.allocator()); │ │104 if (!std.mem.eql(u8, existing_path, repo_path)) { │ │105 _ = try name_to_repo_id.remove(hash.hashInt(hash_kind, existing_path)); │ │106 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │107 if (!std.mem.eql(u8, existing_event.user_id, event.user_id)) { │ │108 const old_user_repos_cursor = try user_id_to_repo_id_set.putCursor(hash.hashInt(hash_kind, existing_event.user_id));│ │109 const old_user_repos = try DB.SortedSet(.read_write).init(old_user_repos_cursor); │ │ 82 - const order_key = evt.orderKey(existing_event.created_ts, event_id); │ │110 + const order_key = evt.orderKeyDesc(existing_event.created_ts, event_id); │ │111 _ = try old_user_repos.remove(&order_key); │ │112 } │ │113 } else { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │115 event_to_write.created_ts = created_ts; │ │116 } │ │117 │ │118 + try evt.writeOid(@This(), DB, hash_kind, repo_id_to_field_to_oid, repo_key, existing_event_maybe, event, event_oid);│ │119 + │ │120 const repo_cursor = try event_id_to_repo.putCursor(repo_key); │ │121 const repo = try DB.HashMap(.read_write).init(repo_cursor); │ │122 try evt.upsert(@This(), DB, hash_kind, repo, event_to_write); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │124 try name_to_repo_id.put(hash.hashInt(hash_kind, repo_path), .{ .bytes = event_id }); │ │125 │ │126 // first time we've seen this repo: add it to the ordered set the repos │ │ 97 - // view paginates through. the key (orderKey) embeds the event id, so the │ │ 98 - // set needs no value. │ │127 + // view paginates through. the key embeds the event id, so the set needs │ │128 + // no value. │ │129 if (existing_cursor_maybe == null) { │ │130 const repo_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "repo-id-set"));│ │131 const repo_id_set = try DB.SortedSet(.read_write).init(repo_id_set_cursor); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │102 - const order_key = evt.orderKey(event_to_write.created_ts, event_id); │ │132 + const order_key = evt.orderKeyDesc(event_to_write.created_ts, event_id);│ │133 try repo_id_set.put(&order_key); │ │134 } │ │135 │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │106 - // each user's repos are a set ordered by creation time (oldest first), so │ │136 + // each user's repos are a set ordered by creation time (newest first), so │ │137 // the user page paginates them the same way the home repos list does. │ │138 const user_repos_cursor = try user_id_to_repo_id_set.putCursor(hash.hashInt(hash_kind, event.user_id));│ │139 const user_repos = try DB.SortedSet(.read_write).init(user_repos_cursor); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │110 - const user_repo_order_key = evt.orderKey(event_to_write.created_ts, event_id); │ │140 + const user_repo_order_key = evt.orderKeyDesc(event_to_write.created_ts, event_id);│ │141 try user_repos.put(&user_repo_order_key); │ │142 } else { │ │143 if (try event_id_to_repo.getCursor(repo_key)) |existing_repo_cursor| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯