╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │859 return if (supports_conflicts) Data.resolveRoute(identity, selected, picks) else null; │ │860 } │ │861 │ │848 - // a list link keeps the filters the page carries: the label, and the │ │849 - // search query and url-encoded assignee when set │ │850 - fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, id: []const u8, search: []const u8, assignee: []const u8) ![]const u8 {│ │851 - var route = listRoute(identity, status, label, id) orelse return error.RouteTooLong; │ │862 + // a list link keeps the search the page carries │ │863 + fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, id: []const u8, search: []const u8) ![]const u8 { │ │864 + var route = listRoute(identity, status, id) orelse return error.RouteTooLong; │ │865 if (search.len != 0) route = route.withSearch(search) orelse return error.RouteTooLong; │ │853 - if (assignee.len != 0) route = route.withEncodedAssignee(assignee) orelse return error.RouteTooLong; │ │866 return page_arena.allocator().print("a:{s}", .{try route.toUrl(page_arena)}); │ │855 - } │ │856 - │ │857 - fn labelLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, search: []const u8, assignee: []const u8) ![]const u8 { │ │858 - const encoded = try ui.urlEncodeRef(page_arena.allocator(), label); │ │859 - return listLink(page_arena, identity, status, encoded, "", search, assignee); │ │867 } │ │868 │ │869 fn addToolButton(allocator: std.mem.Allocator, row: *wgt.Box(Widget), label: []const u8, bottom_label: []const u8, action: []const u8) !void { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │897 │ │898 if (supports_forks and self.session.data.current_page.parent() == .fork) { │ │899 // the fork page's own tabs show the diff and commits │ │893 - const route = ui.RoutablePage.repoPatchesRoute(self.data.identity, .open, "", entry.id) orelse return error.RouteTooLong; │ │900 + const route = ui.RoutablePage.repoPatchesRoute(self.data.identity, .open, entry.id) orelse return error.RouteTooLong; │ │901 try addToolButton(allocator, row, "view on target repo", "", try pa.print("a:{s}", .{try route.toUrl(self.session.page_arena)}));│ │902 } else if (supports_forks) { │ │903 if (try Data.diffRoute(self.data.identity, entry)) |route| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 999 var text_box = try wgt.TextBox.init(allocator, back_label, .{ .border = .single, .round_corners = true, .wrap_kind = .none }); │ │1000 errdefer text_box.deinit(allocator); │ │1001 text_box.getFocus().mode = .all; │ │ 995 - text_box.getFocus().kind = .{ .custom = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), "", entry.id, self.data.search orelse "", "") };│ │1002 + text_box.getFocus().kind = .{ .custom = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), entry.id, self.data.search orelse "") }; │ │1003 try inner_box.children.put(allocator, text_box.getFocus().id, .{ .widget = .{ .text_box = text_box }, .rect = null, .min_size = null }); │ │1004 self.title_id = text_box.getFocus().id; │ │1005 } else { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1030 var label_iter = Event.labelIterator(entry.record.event.labels); │ │1031 while (label_iter.next()) |label| { │ │1032 if (label.len == 0) continue; │ │1026 - try items.append(allocator, .{ .text = label, .link = try labelLink(self.session.page_arena, self.data.identity, entryStatus(entry), label, self.data.search orelse "", assigneeFilter(self.data)) });│ │1033 + const search = try srch_thrd.withFilter(self.session.page_arena.allocator(), self.data.search orelse "", srch_thrd.label_prefix, label); │ │1034 + try items.append(allocator, .{ .text = label, .link = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), "", search) }); │ │1035 } │ │1036 if (items.items.len > 0) { │ │1037 var labels = try WordFlow.init(allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1048 if (entry.can_modify) try appendAssigneeRow(allocator, inner_box, self.session, thread_url); │ │1049 // every viewer gets each assignee as a link to the issues assigned to it │ │1050 const links = try pa.alloc([]const u8, entry.assignees.len); │ │1043 - for (links, entry.assignees) |*link, assignee| link.* = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), self.data.label, "", self.data.search orelse "", try ui.urlEncodeRef(pa, assignee.display()));│ │1051 + for (links, entry.assignees) |*link, assignee| { │ │1052 + const search = try srch_thrd.withFilter(pa, self.data.search orelse "", srch_thrd.assignee_prefix, assignee.display()); │ │1053 + link.* = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), "", search); │ │1054 + } │ │1055 try appendAssignedRows(allocator, inner_box, self.session, thread_url, entry.assignees, links, entry.can_modify); │ │1056 } │ │1057 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │1739 }, │ │1740 } │ │1741 │ │1731 - const route = listRoute(self.data.identity, status, "", entry.id) orelse return;│ │1742 + const route = listRoute(self.data.identity, status, entry.id) orelse return; │ │1743 try self.session.navigate(route); │ │1744 } │ │1745 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1837 return if (supports_drafts) entry.draft else false; │ │1838 } │ │1839 │ │1829 - fn listRoute(identity: []const u8, status: Status, label: []const u8, selected: []const u8) ?ui.RoutablePage {│ │1830 - return Data.listRoute(identity, status, label, selected); │ │1840 + fn listRoute(identity: []const u8, status: Status, selected: []const u8) ?ui.RoutablePage { │ │1841 + return Data.listRoute(identity, status, selected); │ │1842 } │ │1843 │ │1844 fn draftsRoute(identity: []const u8) ?ui.RoutablePage { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1861 return if (supports_conflicts) Data.resolveRoute(identity, selected, picks) else null; │ │1862 } │ │1863 │ │1853 - // a list link keeps the filters the page carries: the label, and the │ │1854 - // search query and url-encoded assignee when set │ │1855 - fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, id: []const u8, search: []const u8, assignee: []const u8) ![]const u8 {│ │1856 - var route = listRoute(identity, status, label, id) orelse return error.RouteTooLong; │ │1864 + // a list link keeps the search the page carries │ │1865 + fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, id: []const u8, search: []const u8) ![]const u8 { │ │1866 + var route = listRoute(identity, status, id) orelse return error.RouteTooLong; │ │1867 if (search.len != 0) route = route.withSearch(search) orelse return error.RouteTooLong; │ │1858 - if (assignee.len != 0) route = route.withEncodedAssignee(assignee) orelse return error.RouteTooLong; │ │1868 return page_arena.allocator().print("a:{s}", .{try route.toUrl(page_arena)}); │ │1869 } │ │1870 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1891 const route = draftsRoute(data.identity) orelse return error.RouteTooLong; │ │1892 return page_arena.allocator().print("a:{s}", .{try route.toUrl(page_arena)}); │ │1893 } │ │1885 - return listLink(page_arena, data.identity, status_maybe orelse @fromBackingInt(@intCast(0)), data.label, id, data.search orelse "", assigneeFilter(data)); │ │1886 - } │ │1887 - │ │1888 - fn labelLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, search: []const u8, assignee: []const u8) ![]const u8 {│ │1889 - const encoded = try ui.urlEncodeRef(page_arena.allocator(), label); │ │1890 - return listLink(page_arena, identity, status, encoded, "", search, assignee); │ │1894 + return listLink(page_arena, data.identity, status_maybe orelse @fromBackingInt(@intCast(0)), id, data.search orelse ""); │ │1895 } │ │1896 │ │1897 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !This { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1930 errdefer tf.deinit(allocator); │ │1931 var items: std.ArrayList(WordFlow.Item) = .empty; │ │1932 defer items.deinit(allocator); │ │1929 - // when filtered, the first item clears the filter │ │1930 - if (data.label.len != 0) │ │1931 - try items.append(allocator, .{ .text = "✕", .link = try listLink(session.page_arena, data.identity, @fromBackingInt(@intCast(0)), "", "", data.search orelse "", assigneeFilter(data)) }); │ │1932 - for (data.labels) |label| │ │1933 - try items.append(allocator, .{ .text = label, .link = try labelLink(session.page_arena, data.identity, @fromBackingInt(@intCast(0)), label, data.search orelse "", assigneeFilter(data)) });│ │1933 + for (data.labels) |label| { │ │1934 + const search = try srch_thrd.withFilter(session.page_arena.allocator(), data.search orelse "", srch_thrd.label_prefix, label); │ │1935 + try items.append(allocator, .{ .text = label, .link = try listLink(session.page_arena, data.identity, @fromBackingInt(@intCast(0)), "", search) }); │ │1936 + } │ │1937 try tf.setItems(allocator, items.items); │ │1938 try stack.children.put(allocator, tf.getFocus().id, .{ .word_flow = tf }); │ │1939 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯