unofficial mirror of meta@public-inbox.org
 help / color / mirror / Atom feed
From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH] view: display thread outline in single-message view
Date: Sun, 30 Aug 2015 00:29:58 +0000	[thread overview]
Message-ID: <1440894598-560-1-git-send-email-e@80x24.org> (raw)

If Xapian search is available, we can load most of the
entire thread and show a more meaningful navigation tree
than the References: and In-Reply-To: headers.  Searching
on those headers themselves is unreliable because it is
possible for clients to omit some references.
---
 lib/PublicInbox/View.pm | 193 +++++++++++++++++++++++++++---------------------
 1 file changed, 110 insertions(+), 83 deletions(-)

diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index bd0a27a..946df37 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -31,10 +31,9 @@ sub msg_html {
 	} else {
 		$footer = '';
 	}
-	my $srch = $ctx->{srch} if $ctx;
-	headers_to_html_header($mime, $full_pfx, $srch) .
+	headers_to_html_header($mime, $full_pfx, $ctx) .
 		multipart_text_as_html($mime, $full_pfx) .
-		'</pre><hr /><pre>' .
+		'</pre><hr />' . PRE_WRAP .
 		html_footer($mime, 1, $full_pfx, $ctx) .
 		$footer .
 		'</pre></body></html>';
@@ -343,8 +342,8 @@ sub add_text_body {
 }
 
 sub headers_to_html_header {
-	my ($mime, $full_pfx, $srch) = @_;
-
+	my ($mime, $full_pfx, $ctx) = @_;
+	my $srch = $ctx->{srch} if $ctx;
 	my $rv = "";
 	my @title;
 	my $header_obj = $mime->header_obj;
@@ -362,7 +361,8 @@ sub headers_to_html_header {
 		} elsif ($h eq 'Subject') {
 			$title[0] = $v->as_html;
 			if ($srch) {
-				$rv .= "$h: <a\nhref=\"../../t/$mid_href/\">";
+				$rv .= "$h: <a\nid=\"t\"\n" .
+					"href=\"../../t/$mid_href/\">";
 				$rv .= $v->as_html . "</a>\n";
 				next;
 			}
@@ -370,10 +370,47 @@ sub headers_to_html_header {
 		$rv .= "$h: " . $v->as_html . "\n";
 
 	}
-
 	$rv .= 'Message-ID: &lt;' . $mid->as_html . '&gt; ';
 	my $raw_ref = $full_pfx ? 'raw' : "../../m/$mid_href/raw";
 	$rv .= "(<a\nhref=\"$raw_ref\">raw</a>)\n";
+	if ($srch) {
+		$rv .= "<a\nhref=\"#r\">References: [see below]</a>\n";
+	} else {
+		$rv .= _parent_headers_nosrch($header_obj);
+	}
+	$rv .= "\n";
+
+	("<html><head><title>".  join(' - ', @title) .
+	 '</title></head><body>' . PRE_WRAP . $rv);
+}
+
+sub thread_inline {
+	my ($dst, $ctx, $cur) = @_;
+	my $srch = $ctx->{srch};
+	my $mid = mid_compress(mid_clean($cur->header('Message-ID')));
+	my $res = $srch->get_thread($mid);
+	my $nr = $res->{total};
+	if ($nr <= 1) {
+		$$dst .= "[only message in thread]\n";
+		return;
+	}
+
+	$$dst .= "roughly $nr messages in thread:\n";
+	my $subj = $srch->subject_path($cur->header('Subject'));
+	my $state = {
+		seen => { $subj => 1 },
+		srch => $srch,
+		cur => $mid,
+	};
+	for (thread_results(load_results($res))->rootset) {
+		inline_dump($dst, $state, $_, 0);
+	}
+	$state->{next_msg};
+}
+
+sub _parent_headers_nosrch {
+	my ($header_obj) = @_;
+	my $rv = '';
 
 	my $irt = $header_obj->header('In-Reply-To');
 	if (defined $irt) {
@@ -401,11 +438,7 @@ sub headers_to_html_header {
 			$rv .= 'References: '. join(' ', @refs) . "\n";
 		}
 	}
-
-	$rv .= "\n";
-
-	("<html><head><title>".  join(' - ', @title) .
-	 '</title></head><body>' . PRE_WRAP . $rv);
+	$rv;
 }
 
 sub html_footer {
@@ -440,25 +473,11 @@ sub html_footer {
 	my $srch = $ctx->{srch} if $ctx;
 	my $idx = $standalone ? " <a\nhref=\"../../\">index</a>" : '';
 	if ($idx && $srch) {
-		$mid = mid_compress(mid_clean($mid));
 		my $t_anchor = defined $irt ? T_ANCHOR : '';
 		$irt = $mime->header('In-Reply-To');
 		$idx = " <a\nhref=\"../../t/$mid/$t_anchor\">".
-		       "threadlink</a>$idx";
-		my $res = $srch->get_followups($mid);
-		if (my $c = $res->{total}) {
-			my $nr = scalar @{$res->{msgs}};
-			if ($nr < $c) {
-				$c = "$nr of $c followups";
-			} else {
-				$c = $c == 1 ? '1 followup' : "$c followups";
-			}
-			$idx .= "\n$c:\n";
-			$res->{srch} = $srch;
-			thread_followups(\$idx, $mime, $res);
-		} else {
-			$idx .= "\n(no followups, yet)\n";
-		}
+		       "threadlink</a>$idx\n\n";
+		my $next = thread_inline(\$idx, $ctx, $mime);
 		if (defined $irt) {
 			$irt = PublicInbox::Hval->new_msgid($irt);
 			$irt = $irt->as_href;
@@ -466,6 +485,11 @@ sub html_footer {
 		} else {
 			$irt = ' ' x length('parent ');
 		}
+		if ($next) {
+			$irt .= "<a\nhref=\"../$next/\">next</a> ";
+		} else {
+			$irt .= '     ';
+		}
 	} else {
 		$irt = '';
 	}
@@ -489,61 +513,6 @@ sub anchor_for {
 	'm' . $id;
 }
 
-sub simple_dump {
-	my ($dst, $root, $node, $level) = @_;
-	return unless $node;
-	# $root = [ undef, \%seen, $srch ];
-	if (my $x = $node->message) {
-		my $f = $x->header('X-PI-From');
-		my $d = $x->header('X-PI-Date');
-		if (defined $f && defined $d) {
-			my $mid = $x->header('Message-ID');
-			my $pfx = '  ' x $level;
-			$$dst .= $pfx;
-
-			# Subject is never undef, this mail was loaded from
-			# our Xapian which would've resulted in '' if it were
-			# really missing (and Filter rejects empty subjects)
-			my $s = $x->header('Subject');
-			my $h = $root->[2]->subject_path($s);
-			if ($root->[1]->{$h}) {
-				$s = undef;
-			} else {
-				$root->[1]->{$h} = 1;
-				$s = PublicInbox::Hval->new($s);
-				$s = $s->as_html;
-			}
-			my $m = PublicInbox::Hval->new_msgid($mid);
-			$f = PublicInbox::Hval->new($f);
-			$d = PublicInbox::Hval->new($d);
-			$m = '../' . $m->as_href . '/';
-			$f = $f->as_html;
-			$d = $d->as_html . ' UTC';
-			if (defined $s) {
-				$$dst .= "` <a\nhref=\"$m\">$s</a>\n" .
-				     "$pfx  by $f @ $d\n";
-			} else {
-				$$dst .= "` <a\nhref=\"$m\">$f @ $d</a>\n";
-			}
-		}
-	}
-	simple_dump($dst, $root, $node->child, $level+1);
-	simple_dump($dst, $root, $node->next, $level);
-}
-
-sub thread_followups {
-	my ($dst, $root, $res) = @_;
-	$root->header_set('X-PI-TS', '0');
-	my $msgs = load_results($res);
-	push @$msgs, $root;
-	my $th = thread_results($msgs);
-	my $srch = $res->{srch};
-	my $subj = $srch->subject_path($root->header('Subject'));
-	my %seen = ($subj => 1);
-	$root = [ undef, \%seen, $srch ];
-	simple_dump($dst, $root, $_, 0) for $th->rootset;
-}
-
 sub thread_html_head {
 	my ($mime) = @_;
 	my $s = PublicInbox::Hval->new_oneline($mime->header('Subject'));
@@ -601,4 +570,62 @@ sub missing_thread {
 EOF
 }
 
+sub _inline_header {
+	my ($dst, $state, $mime, $level) = @_;
+	my $pfx = '  ' x $level;
+
+	my $cur = $state->{cur};
+	my $mid = $mime->header('Message-ID');
+	my $f = $mime->header('X-PI-From');
+	my $d = $mime->header('X-PI-Date');
+	$f = PublicInbox::Hval->new($f);
+	$d = PublicInbox::Hval->new($d);
+	$f = $f->as_html;
+	$d = $d->as_html . ' UTC';
+	my $midc = mid_compress(mid_clean($mid));
+	if ($cur) {
+		if ($cur eq $midc) {
+			delete $state->{cur};
+			$$dst .= "$pfx` <b><a\nid=\"r\"\nhref=\"#t\">".
+				 "[this message]</a></b> by $f @ $d\n";
+
+			return;
+		}
+	} else {
+		$state->{next_msg} ||= $midc;
+	}
+
+	# Subject is never undef, this mail was loaded from
+	# our Xapian which would've resulted in '' if it were
+	# really missing (and Filter rejects empty subjects)
+	my $s = $mime->header('Subject');
+	my $h = $state->{srch}->subject_path($s);
+	if ($state->{seen}->{$h}) {
+		$s = undef;
+	} else {
+		$state->{seen}->{$h} = 1;
+		$s = PublicInbox::Hval->new($s);
+		$s = $s->as_html;
+	}
+	my $m = PublicInbox::Hval->new_msgid($mid);
+	$m = '../' . $m->as_href . '/';
+	if (defined $s) {
+		$$dst .= "$pfx` <a\nhref=\"$m\">$s</a>\n" .
+		         "$pfx  $f @ $d\n";
+	} else {
+		$$dst .= "$pfx` <a\nhref=\"$m\">$f @ $d</a>\n";
+	}
+}
+
+sub inline_dump {
+	my ($dst, $state, $node, $level) = @_;
+	return unless $node;
+	return if $state->{stopped};
+	if (my $mime = $node->message) {
+		_inline_header($dst, $state, $mime, $level);
+	}
+	inline_dump($dst, $state, $node->child, $level+1);
+	inline_dump($dst, $state, $node->next, $level);
+}
+
 1;
-- 
EW


                 reply	other threads:[~2015-08-30  0:29 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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

  List information: https://public-inbox.org/README

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

  git send-email \
    --in-reply-to=1440894598-560-1-git-send-email-e@80x24.org \
    --to=e@80x24.org \
    --cc=meta@public-inbox.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;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).