╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Discussions.zig ──────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
176 null 177 else 178 try Comment.init(repo_opts.hash, arena, admin_moment, haxy_moment, empty.selected_id, empty.comment_id, comments_start, viewer); 179 180 var label_names: std.ArrayList([]const u8) = .empty; 181 if (try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.Discussion.label_to_id_set_key))) |labels_cursor| { 182 const label_sets = try DB.SortedMap(.read_only).init(labels_cursor); 183 var label_iter = try label_sets.iterator(); 184 while (try label_iter.next()) |entry_cursor| { 185 if (label_names.items.len == max_labels) break; 186 var entry = entry_cursor; 187 const pair = try entry.readKeyValuePair(); 188 try label_names.append(aa, try pair.key_cursor.readBytesAlloc(aa, null)); 189 } 190 } 191 192 return .{ 193 .identity = empty.identity, 194 .label = empty.label, 195 .search = empty.search, 196 .selected_id = empty.selected_id, 197 .comment_id = empty.comment_id, 198 .comments_start = comments_start, 199 .comment_page = comment_page, 200 .viewer = viewer, 201 .recent = loaded_window, 202 .view = empty.view, 203 .description_page = empty.description_page, 204 .labels = label_names.items, 205 }; 206 } 207 208 pub const View = thread.View(.discuss, Self); 209 pub const Detail = thread.Detail(.discuss, Self); 210 pub const detail_widget_name = "repo_discussion_detail"; 211 212 pub const Header = thread.Header; 213 214 const recent_tab_label = "recent"; 215 const labels_tab_label = "labels"; 216 const edit_tab_label = "edit"; 217 const reply_tab_label = "reply"; 218 const remove_tab_label = "remove"; 219 const new_tab_label = "new"; 220 221 // tabs switching between the discussions page's views 222 pub fn initHeader(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self) !Header { 223 var header = try Header.init(allocator, session, data.search); 224 errdefer header.deinit(allocator); 225 const aa = session.page_arena.allocator(); 226 const selected_index = View.viewIndex(data.view); 227 const page_selected = std.meta.activeTag(session.data.current_page) == .repo_discussions; 228 229 // recent discussions 230 { 231 const route = try thread.searchRoute(ui.RoutablePage.repoDiscussionsRoute(data.identity, data.label, ""), data.search); 232 const link = try ui.inPageTabLink(session, route, page_selected and selected_index == 0); 233 var label_buf: [64]u8 = undefined; 234 const label = try thread.countLabel(&label_buf, recent_tab_label, data.recent); 235 try header.addTab(allocator, label, link, 0); 236 } 237 238 // labels tab, showing the active label filter 239 { 240 const labels_route = try thread.searchRoute(ui.RoutablePage.repoThreadLabelsRoute(.discuss, data.identity, data.label), data.search); 241 const labels_link = try ui.inPageTabLink(session, labels_route, page_selected and selected_index == View.viewIndex(.labels)); 242 const label = if (data.label.len == 0) labels_tab_label else blk: { 243 const decoded = std.Uri.percentDecodeInPlace(try aa.dupe(u8, data.label)); 244 break :blk try std.fmt.allocPrint(aa, labels_tab_label ++ " ({s})", .{decoded}); 245 }; 246 try header.addTab(allocator, label, labels_link, View.viewIndex(.labels)); 247 } 248 249 // new-discussion tab; an edit or comment url shows its tab in this place 250 { 251 const route = switch (data.view) { 252 .edit => ui.RoutablePage.repoThreadEditRoute(.discuss, data.identity, data.selected_id) orelse return error.RouteTooLong, 253 .new_comment => ui.RoutablePage.repoThreadCommentNewRoute(.discuss, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong, 254 .edit_comment => ui.RoutablePage.repoThreadCommentEditRoute(.discuss, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong, 255 .remove => ui.RoutablePage.repoThreadRemoveRoute(.discuss, data.identity, data.selected_id, data.comment_id) orelse return error.RouteTooLong, 256 else => ui.RoutablePage.repoThreadNewRoute(.discuss, data.identity) orelse return error.RouteTooLong, 257 }; 258 const link = try ui.inPageTabLink(session, route, page_selected and selected_index == View.viewIndex(.new)); 259 const label: []const u8 = switch (data.view) { 260 .edit => edit_tab_label, 261 .new_comment => reply_tab_label, 262 .edit_comment => edit_tab_label, 263 .remove => remove_tab_label, 264 else => new_tab_label, 265 }; 266 try header.addTab(allocator, label, link, View.viewIndex(.new)); 267 } 268 269 header.select(View.viewIndex(data.view)); 270 return header; 271 } 272