+ if (my $next = $node->next) {
+ my $nmid = $next->messageid;
+ $rv .= $pad . $mapping->{$nmid}->[1];
+ my $nnext = $nr_s - $idx;
+ if ($nnext > 2) {
+ my $s = ($nnext - 1).' subsequent siblings';
+ $rv .= pad_link($nmid, $level, $s);
+ } elsif (my $nn = $next->next) {
+ $rv .= $pad . $mapping->{$nn->messageid}->[1];
+ }
+ }
+ $rv .= "<a\nhref=#e$id\nid=m$id>_</a> ";
+ $rv .= "<a\nhref=#r$id>$s_s, $s_c; $ctx->{s_nr}</a>\n";
+}
+
+sub walk_thread {
+ my ($th, $ctx, $cb) = @_;
+ my @q = map { (0, $_) } $th->rootset;
+ while (@q) {
+ my $level = shift @q;
+ my $node = shift @q or next;
+ $cb->($ctx, $level, $node);
+ unshift @q, $level+1, $node->child, $level, $node->next;
+ }
+}
+
+sub pre_thread {
+ my ($ctx, $level, $node) = @_;
+ my $mapping = $ctx->{mapping};
+ my $idx = -1;
+ if (my $parent = $node->parent) {
+ my $m = $mapping->{$parent->messageid}->[0];
+ $idx = scalar @$m;
+ push @$m, $node;
+ }
+ $mapping->{$node->messageid} = [ [], '', $node, $idx, $level ];
+ skel_dump($ctx, $level, $node);
+}
+
+sub thread_index_entry {
+ my ($ctx, $level, $mime) = @_;
+ my ($beg, $end) = thread_adj_level($ctx, $level);
+ $beg . '<pre>' . index_entry($mime, $ctx, 0) . '</pre>' . $end;
+}
+
+sub stream_thread ($$) {
+ my ($th, $ctx) = @_;
+ my $inbox = $ctx->{-inbox};
+ my $mime;
+ my @q = map { (0, $_) } $th->rootset;
+ my $level;
+ while (@q) {
+ $level = shift @q;
+ my $node = shift @q or next;
+ unshift @q, $level+1, $node->child, $level, $node->next;
+ $mime = $inbox->msg_by_mid($node->messageid) and last;
+ }
+ return missing_thread($ctx) unless $mime;
+
+ $mime = Email::MIME->new($mime);
+ $ctx->{-title_html} = ascii_html($mime->header('Subject'));
+ $ctx->{-html_tip} = thread_index_entry($ctx, $level, $mime);
+ my $body = PublicInbox::WwwStream->new($ctx, sub {
+ return unless $ctx;
+ while (@q) {
+ $level = shift @q;
+ my $node = shift @q or next;
+ unshift @q, $level+1, $node->child, $level, $node->next;
+ my $mid = $node->messageid;
+ if ($mime = $inbox->msg_by_mid($mid)) {
+ $mime = Email::MIME->new($mime);
+ return thread_index_entry($ctx, $level, $mime);
+ } else {
+ return ghost_index_entry($ctx, $level, $mid);
+ }
+ }
+ my $ret = join('', thread_adj_level($ctx, 0));
+ $ret .= ${$ctx->{dst}}; # skel
+ $ctx = undef;
+ $ret;
+ });
+ [ 200, ['Content-Type', 'text/html; charset=UTF-8'], $body ];
+}
+
+sub thread_html {
+ my ($ctx) = @_;
+ my $mid = $ctx->{mid};
+ my $sres = $ctx->{srch}->get_thread($mid, { asc => 1 });
+ my $msgs = load_results($sres);
+ my $nr = $sres->{total};
+ return missing_thread($ctx) if $nr == 0;
+ my $skel = '<hr /><pre>';
+ $skel .= $nr == 1 ? 'only message in thread' : 'end of thread';
+ $skel .= ", back to <a\nhref=\"../../\">index</a>";
+ $skel .= "\n<a\nid=t>$nr+ messages in thread:</a> (download: ";
+ $skel .= "<a\nhref=\"../t.mbox.gz\">mbox.gz</a>";
+ $skel .= " / follow: <a\nhref=\"../t.atom\">Atom feed</a>)\n";
+ $ctx->{-upfx} = '../../';
+ $ctx->{cur_level} = 0;
+ $ctx->{dst} = \$skel;
+ $ctx->{prev_attr} = '';
+ $ctx->{prev_level} = 0;
+ $ctx->{root_anchor} = anchor_for($mid);
+ $ctx->{seen} = {};
+ $ctx->{mapping} = {};
+ $ctx->{s_nr} = "$nr+ messages in thread";
+
+ my $th = thread_results($msgs);
+ walk_thread($th, $ctx, *pre_thread);
+ $skel .= '</pre>';
+ return stream_thread($th, $ctx) unless $ctx->{flat};
+
+ # flat display: lazy load the full message from mini_mime:
+ my $inbox = $ctx->{-inbox};
+ my $mime;
+ while ($mime = shift @$msgs) {
+ $mime = $inbox->msg_by_mid(mid_clean(mid_mime($mime))) and last;
+ }
+ return missing_thread($ctx) unless $mime;
+ $mime = Email::MIME->new($mime);
+ $ctx->{-title_html} = ascii_html($mime->header('Subject'));
+ $ctx->{-html_tip} = '<pre>'.index_entry($mime, $ctx, scalar @$msgs);
+ $mime = undef;
+ my $body = PublicInbox::WwwStream->new($ctx, sub {
+ return unless $msgs;
+ while ($mime = shift @$msgs) {
+ $mid = mid_clean(mid_mime($mime));
+ $mime = $inbox->msg_by_mid($mid) and last;
+ }
+ if ($mime) {
+ $mime = Email::MIME->new($mime);
+ return index_entry($mime, $ctx, scalar @$msgs);
+ }
+ $msgs = undef;
+ '</pre>'.$skel;
+ });
+ [ 200, ['Content-Type', 'text/html; charset=UTF-8'], $body ];