Linux maintainer tooling and workflows
 help / color / mirror / Atom feed
From: Christian Brauner <brauner@kernel.org>
To: "Kernel.org Tools" <tools@kernel.org>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>,
	 "Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH RFC v2 22/25] review-tui: extract the Msgs column renderer from TrackedSeriesItem
Date: Wed, 12 Aug 2026 23:47:03 +0200	[thread overview]
Message-ID: <20260812-work-b4-multiver-rows-v2-22-305d53cd723a@kernel.org> (raw)
In-Reply-To: <20260812-work-b4-multiver-rows-v2-0-305d53cd723a@kernel.org>

The tracker list computes the Msgs column, a thread total plus an unseen
badge, inline in TrackedSeriesItem.compose().  Per-version child rows
need the same column, so pull the computation out into _msgs_fields() and
the styled append into _append_msgs().

No functional change.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 src/b4/review_tui/_tracking_app.py | 107 +++++++++++++++++++++++--------------
 1 file changed, 66 insertions(+), 41 deletions(-)

diff --git a/src/b4/review_tui/_tracking_app.py b/src/b4/review_tui/_tracking_app.py
index 999513ca..53466498 100644
--- a/src/b4/review_tui/_tracking_app.py
+++ b/src/b4/review_tui/_tracking_app.py
@@ -726,6 +726,66 @@ def _format_attestation(att: str, app: Any = None) -> Optional[RichText]:
     return text
 
 
+def _unseen_delta(
+    message_count: Optional[int], seen_message_count: Optional[int]
+) -> int:
+    """Unseen messages in a thread, for every renderer of that number.
+
+    An absent seen count means nothing is known to have been read yet --
+    but the counts are written in pairs, so in practice that only happens
+    on rows migrated in from a database predating them.  Treat it as
+    "nothing unseen" rather than "everything unseen": two renderers
+    disagreeing about a missing value is worse than either answer.
+    """
+    if message_count is None or seen_message_count is None:
+        return 0
+    return max(0, message_count - seen_message_count)
+
+
+def _msgs_fields(
+    message_count: Optional[int], seen_message_count: Optional[int]
+) -> Tuple[str, str, bool]:
+    """Render the Msgs column for a (total, seen) message count pair.
+
+    Returns (base, badge, base_accent): "1" (all seen), "6" accented (all
+    new), "6" + "(3)" (mixed).  A never-fetched thread has no count and
+    renders as "-".  The badge is accented whenever it is non-empty.
+    """
+    if message_count is None:
+        return '-', '', False
+    if message_count == 0:
+        return '0', '', False
+    delta = _unseen_delta(message_count, seen_message_count)
+    if delta == message_count:
+        # All follow-ups are new
+        return str(message_count), '', True
+    if delta > 0:
+        # Mixed: total + (unseen)
+        return str(message_count), f'({delta})', False
+    # All seen
+    return str(message_count), '', False
+
+
+def _append_msgs(
+    label: RichText,
+    app: Any,
+    message_count: Optional[int],
+    seen_message_count: Optional[int],
+) -> None:
+    """Append the Msgs column (total + unseen badge) to *label*."""
+    base, badge, base_accent = _msgs_fields(message_count, seen_message_count)
+    base_style = ''
+    badge_style = ''
+    if base_accent or badge:
+        accent = f'bold {resolve_styles(app)["warning"]}'
+        if base_accent:
+            base_style = accent
+        if badge:
+            badge_style = accent
+    label.append(f'  {base.rjust(3)}', style=base_style)
+    label.append(f'{badge:<3s}', style=badge_style)
+
+
 class TrackedSeriesItem(ListItem):
     """A single tracked series entry in the listing."""
 
@@ -770,36 +830,6 @@ class TrackedSeriesItem(ListItem):
             art_str = f'{a}·{r}·{t}'
         else:
             art_str = '-'
-        fc = self.series.get('message_count')
-        sc = self.series.get('seen_message_count')
-        if fc is not None:
-            delta = (fc - sc) if (sc is not None and fc > sc) else 0
-        else:
-            delta = 0
-        # Msgs display: "1" (all seen), "6" accent (all new), "6(3)" mixed
-        if fc is None:
-            fu_base = '-'
-            fu_badge = ''
-            base_accent = False
-        elif fc == 0:
-            fu_base = '0'
-            fu_badge = ''
-            base_accent = False
-        elif delta == fc:
-            # All follow-ups are new
-            fu_base = str(fc)
-            fu_badge = ''
-            base_accent = True
-        elif delta > 0:
-            # Mixed: total + (unseen)
-            fu_base = str(fc)
-            fu_badge = f'({delta})'
-            base_accent = False
-        else:
-            # All seen
-            fu_base = str(fc)
-            fu_badge = ''
-            base_accent = False
         # Build compact prefix using LoreSubject to extract subsystem/modifier tokens
         ls = b4.LoreSubject(subject)
         extras = ls.get_extra_prefixes(exclude=['patch'])
@@ -822,17 +852,12 @@ class TrackedSeriesItem(ListItem):
             label.append(' ')
         label.append(' ')
         label.append(art_str.rjust(7))
-        base_style = ''
-        badge_style = ''
-        if base_accent or fu_badge:
-            ts = resolve_styles(self.app)
-            accent = f'bold {ts["warning"]}'
-            if base_accent:
-                base_style = accent
-            if fu_badge:
-                badge_style = accent
-        label.append(f'  {fu_base.rjust(3)}', style=base_style)
-        label.append(f'{fu_badge:<3s}', style=badge_style)
+        _append_msgs(
+            label,
+            self.app,
+            self.series.get('message_count'),
+            self.series.get('seen_message_count'),
+        )
         label.append(f'  {symbol}{flag}  {subject_display}')
         yield Label(label, markup=False)
 

-- 
2.53.0


  parent reply	other threads:[~2026-08-12 21:47 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12 21:46 [PATCH RFC v2 00/25] review: track and browse every version of a tracked series Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 01/25] review-tui: fix rethreaded series thread viewing Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 02/25] review: do not clear fields a re-adding caller does not know Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 03/25] review-tui: keep the rethread flag on an upgraded series row Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 04/25] review: guard the tracking-commit amend on the worktree, not the checkout Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 05/25] review-tui: recompute an evicted A·R·T cache entry Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 06/25] review: test the prerequisite fixes Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 07/25] review: serialize schema migrations against a concurrent opener Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 08/25] review: test the migration serialization Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 09/25] review: track message counts for all revisions of a series Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 10/25] review: give per-change_id state its own table Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 11/25] review: test per-revision message tracking Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 12/25] review-tui: poll every revision on u/U updates Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 13/25] review: test the per-revision poll sweep Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 14/25] review-tui: resolve the tracked revision in revision lists Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 15/25] review-tui: fall back when a cached thread blob has no series Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 16/25] review-tui: test revision resolution and the range-diff fallback Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 17/25] review: skip the catalog mirror when nothing moved Christian Brauner
2026-08-12 21:46 ` [PATCH RFC v2 18/25] review: match a stray posting by message-id Christian Brauner
2026-08-12 21:47 ` [PATCH RFC v2 19/25] review: add backward discovery of older series revisions Christian Brauner
2026-08-12 21:47 ` [PATCH RFC v2 20/25] review-tui: add a "Find older revisions" action Christian Brauner
2026-08-12 21:47 ` [PATCH RFC v2 21/25] review: test the catalog mirror, stray matching and backward discovery Christian Brauner
2026-08-12 21:47 ` Christian Brauner [this message]
2026-08-12 21:47 ` [PATCH RFC v2 23/25] review-tui: give the unseen badge a column of its own Christian Brauner
2026-08-12 21:47 ` [PATCH RFC v2 24/25] review-tui: expand tracked series into per-version rows Christian Brauner
2026-08-12 21:47 ` [PATCH RFC v2 25/25] review-tui: test per-version tracker rows Christian Brauner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260812-work-b4-multiver-rows-v2-22-305d53cd723a@kernel.org \
    --to=brauner@kernel.org \
    --cc=konstantin@linuxfoundation.org \
    --cc=tools@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox