- Email::Address->purge_cache;
-
- # there could be a race due to a message being deleted in git
- # but still being in the Xapian index:
- my $fh = delete $state->{fh} or return missing_thread($res, $ctx);
-
- my $final_anchor = $state->{anchor_idx};
- my $next = "<a\nid=s$final_anchor>";
- $next .= $final_anchor == 1 ? 'only message in' : 'end of';
- $next .= " thread</a>, back to <a\nhref=\"../../\">index</a>";
- $next .= "\ndownload thread: ";
- $next .= "<a\nhref=\"../t.mbox.gz\">mbox.gz</a>";
- $next .= " / follow: <a\nhref=\"../t.atom\">Atom feed</a>";
- $fh->write('<hr /><pre>' . $next . "\n\n".
- $foot . '</pre></body></html>');
- $fh->close;
-}
-
-sub index_walk {
- my ($fh, $part, $enc, $part_nr, $fhref) = @_;
- my $s = add_text_body($enc, $part, $part_nr, $fhref, 1);
-
- return if $s eq '';
-
- $s .= "\n"; # ensure there's a trailing newline
-
- $fh->write($s);
+ my $s_s = nr_to_s($nr_s, 'sibling', 'siblings');
+ my $s_c = nr_to_s($nr_c, 'reply', 'replies');
+ my $this = $map->[1];
+ $this =~ s!\n\z!</b>\n!s;
+ $this =~ s!<a\nhref.*</a> !!s; # no point in duplicating subject
+ $rv .= "<b>@ $this";
+ my $node = $map->[2];
+ if (my $child = $node->child) {
+ my $cmid = $child->messageid;
+ $rv .= $pad . $mapping->{$cmid}->[1];
+ if ($nr_c > 2) {
+ my $s = ($nr_c - 1). ' more replies';
+ $rv .= pad_link($cmid, $level + 1, $s);
+ } elsif (my $cn = $child->next) {
+ $rv .= $pad . $mapping->{$cn->messageid}->[1];
+ }
+ }
+ 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 ];