╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │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 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │2501 return &self.viewStack().children.values()[labels_view_index].word_flow;│ │2502 } │ │2503 │ │2501 - // no labels and no filter to clear, which shows the empty message │ │2504 + // no labels, which shows the empty message │ │2505 fn labelsEmpty(data: *const Self) bool { │ │2503 - return data.label.len == 0 and data.labels.len == 0; │ │2506 + return data.labels.len == 0; │ │2507 } │ │2508 │ │2509 // the non-confirmation form inside the stack │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2738 false; │ │2739 if (enterable) root_focus.setFocus(self.box.children.keys()[stack_index]);│ │2740 } else if (key == .enter) { │ │2738 - if (try self.header().submittedText(allocator)) |submission| { │ │2739 - defer allocator.free(submission.text); │ │2740 - switch (submission.kind) { │ │2741 - .search => try self.submitSearch(submission.text), │ │2742 - .assignee => try self.submitAssignee(submission.text), │ │2743 - } │ │2741 + if (try self.header().submittedText(allocator)) |text| { │ │2742 + defer allocator.free(text); │ │2743 + try self.submitSearch(text); │ │2744 } │ │2745 } else { │ │2746 try self.header().input(allocator, key, root_focus); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │2783 } │ │2784 } │ │2785 │ │2786 - // navigate to the typed query's results, keeping the label filter and the│ │2787 - // status list the page shows. an empty box clears a filtered page and │ │2788 - // does nothing on any other one. │ │2786 + // navigate to the typed query's results, keeping the status list the │ │2787 + // page shows. an empty box clears a filtered page and does nothing on │ │2788 + // any other one. │ │2789 fn submitSearch(self: *This, text: []const u8) !void { │ │2790 if (text.len == 0 and self.data.search == null) return; │ │2791 const selected = self.header().getSelectedIndex() orelse 0; │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2792 const status: Status = if (selected < status_count) splitStatus(selected) else @fromBackingInt(@intCast(0));│ │2793 - var route = listRoute(self.data.identity, status, self.data.label, "") orelse return; │ │2794 - const assignee = assigneeFilter(self.data); │ │2795 - if (assignee.len != 0) route = route.withEncodedAssignee(assignee) orelse return; │ │2793 + const route = listRoute(self.data.identity, status, "") orelse return; │ │2794 try self.session.navigate(route.withSearch(text) orelse return); │ │2795 } │ │2796 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯