diff options
author | Yorhel <git@yorhel.nl> | 2020-11-15 19:24:19 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2020-11-15 19:24:19 +0100 |
commit | c0e612e4401bf8188d0c409e0321e184f2835092 (patch) | |
tree | c13316a3af268d9651a805dbba5675078696bbf0 | |
parent | 86af5725b6c6dc749c88e6cbc8ffcadf3bd97af6 (diff) |
VN::Page: Experiment with a tag tree tab
-rw-r--r-- | data/style.css | 11 | ||||
-rw-r--r-- | lib/VNWeb/VN/Page.pm | 108 |
2 files changed, 115 insertions, 4 deletions
diff --git a/data/style.css b/data/style.css index 83aa4f29..71648eae 100644 --- a/data/style.css +++ b/data/style.css @@ -533,6 +533,17 @@ div#vntags { margin: 0 30px 0 30px; border-top: 1px solid $bo .reviewbox .myvote { font-weight: bold; text-decoration: underline } + +/***** VN tags tab (/v+/tags) *******/ + +.vntaglist { list-style-type: none; column-width: 400px } +.vntaglist li.tagvnlist-top:not(:first-child) { margin-top: 30px } +.vntaglist h3 a { color: $maintext$ } +.vntaglist li { list-style-type: none; padding-right: 30px } +.vntaglist li .tagscore { margin-right: 10px } + + + /***** Vote stats ****/ .votestats { width: 630px; margin: 0 auto; } diff --git a/lib/VNWeb/VN/Page.pm b/lib/VNWeb/VN/Page.pm index c99a2e0c..643d8b6c 100644 --- a/lib/VNWeb/VN/Page.pm +++ b/lib/VNWeb/VN/Page.pm @@ -247,7 +247,7 @@ sub infobox_tags_ { my($v) = @_; my $rating = 'avg(CASE WHEN tv.ignore OR (u.id IS NOT NULL AND NOT u.perm_tag) THEN NULL ELSE tv.vote END)'; my $tags = tuwf->dbAlli(" - SELECT t.id, t.name, t.cat, count(*) as cnt, $rating as rating + SELECT t.id, t.name, t.cat, $rating as rating , coalesce(avg(CASE WHEN tv.ignore OR (u.id IS NOT NULL AND NOT u.perm_tag) THEN NULL ELSE tv.spoiler END), t.defaultspoil) as spoiler FROM tags t JOIN tags_vn tv ON tv.tag = t.id @@ -333,7 +333,7 @@ sub infobox_useroptions_ { # Also used by Chars::VNTab & Reviews::VNTab sub infobox_ { - my($v) = @_; + my($v, $notags) = @_; div_ class => 'mainbox', sub { itemmsg_ v => $v; h1_ $v->{title}; @@ -384,7 +384,7 @@ sub infobox_ { } }; div_ class => 'clearfloat', style => 'height: 5px', ''; # otherwise the tabs below aren't positioned correctly - infobox_tags_ $v; + infobox_tags_ $v unless $notags; } } @@ -398,7 +398,8 @@ sub tabs_ { $tab ||= ''; div_ class => 'maintabs', sub { ul_ sub { - li_ class => ($tab eq '' ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}#main", name => 'main', 'main' } if $chars || $v->{reviews}{total}; + li_ class => ($tab eq '' ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}#main", name => 'main', 'main' }; + li_ class => ($tab eq 'tags' ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}/tags#tags", name => 'tags', 'tags' }; li_ class => ($tab eq 'chars' ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}/chars#chars", name => 'chars', "characters ($chars)" } if $chars; if($v->{reviews}{mini} > 4 || $tab eq 'minireviews' || $tab eq 'fullreviews') { li_ class => ($tab eq 'minireviews'?' tabselected' : ''), sub { a_ href => "/v$v->{id}/minireviews#review", name => 'review', "mini reviews ($v->{reviews}{mini})" } if $v->{reviews}{mini}; @@ -704,6 +705,90 @@ sub screenshots_ { } +sub tags_ { + my($v) = @_; + # TODO: Needs index on tags_vn_inherity.vid - or we could calculate the inherited scores here. + # (Could totally re-use the data from infobox_tags_() and do the calculation in Perl, but the exact algorithm is finicky) + my %tags = map +($_->{id},$_), tuwf->dbAlli(" + SELECT t.id, t.name, t.cat, tvi.rating, tvi.spoiler + FROM tags t + JOIN tags_vn_inherit tvi ON tvi.tag = t.id + WHERE t.state = 1+1 AND tvi.vid =", \$v->{id} + )->@*; + + if(!%tags) { + div_ class => 'mainbox', sub { + h1_ 'Tags'; + p_ 'This VN has no tags assigned to it (yet).'; + }; + return; + } + + my $parents = tuwf->dbAlli(" + WITH RECURSIVE parents (tag, child) AS ( + SELECT tag::int, NULL::int FROM (VALUES", sql_join(',', map sql('(',\$_,')'), keys %tags), ") AS x(tag) + UNION + SELECT tp.parent, tp.tag FROM tags_parents tp, parents a WHERE a.tag = tp.tag + ) SELECT * FROM parents WHERE child IS NOT NULL" + ); + + for(@$parents) { + $tags{$_->{tag}} ||= { id => $_->{tag} }; + push $tags{$_->{tag}}{childs}->@*, $_->{child}; + $tags{$_->{child}}{notroot} = 1; + } + enrich_merge id => 'SELECT id, name, cat FROM tags WHERE id IN', grep !$_->{name}, values %tags; + my @roots = sort { $a->{name} cmp $b->{name} } grep !$_->{notroot}, values %tags; + + # Have to do our own spoiler calculation for parent tags, as we also display parents that aren't in tags_vn_inherited. + my sub minspoil { + return if !$_[0]{childs}; + __SUB__->($tags{$_}) for $_[0]{childs}->@*; + $_[0]{minspoil} = min map +($tags{$_}{minspoil}//$tags{$_}{spoiler}), $_[0]{childs}->@*; + } + minspoil $_ for @roots; + + # TODO: Gray-out tags that do not have direct votes + my $view = viewget; + my sub rec { + my($lvl, $t) = @_; + return if max($t->{minspoil}||0, $t->{spoiler}||0) > $view->{spoilers}; + li_ class => "tagvnlist-top", sub { + h3_ sub { a_ href => "/g$t->{id}", $t->{name} } + } if !$lvl; + + li_ sub { + VNWeb::TT::Lib::tagscore_($t->{rating}) if $t->{rating}; + div_ class => 'tagscore', '' if !$t->{rating}; + b_ class => 'grayedout', '━━'x($lvl-1); + txt_ ' ' if $lvl > 1; + a_ href => "/g$t->{id}", $t->{rating} ? () : (class => 'parent'), $t->{name}; + spoil_ $t->{spoiler} if defined $t->{spoiler}; + } if $lvl; + + if($t->{childs}) { + __SUB__->($lvl+1, $_) for sort { $a->{name} cmp $b->{name} } map $tags{$_}, $t->{childs}->@*; + } + } + + div_ class => 'mainbox', sub { + my $max_spoil = max map $_->{spoiler}||0, values %tags; + p_ class => 'mainopts', sub { + if($max_spoil) { + a_ mkclass(checked => $view->{spoilers} == 0), href => '?view='.viewset(spoilers=>0).'#tags', 'Hide spoilers'; + a_ mkclass(checked => $view->{spoilers} == 1), href => '?view='.viewset(spoilers=>1).'#tags', 'Show minor spoilers'; + a_ mkclass(standout =>$view->{spoilers} == 2), href => '?view='.viewset(spoilers=>2).'#tags', 'Spoil me!' if $max_spoil == 2; + } + } if $max_spoil; + + h1_ 'Tags'; + ul_ class => 'vntaglist', sub { + rec 0, $_ for @roots; + }; + }; +} + + TUWF::get qr{/$RE{vrev}}, sub { my $v = db_entry v => tuwf->capture('id'), tuwf->capture('rev'); return tuwf->resNotFound if !$v; @@ -723,4 +808,19 @@ TUWF::get qr{/$RE{vrev}}, sub { }; }; + +TUWF::get qr{/$RE{vid}/tags}, sub { + my $v = db_entry v => tuwf->capture('id'); + return tuwf->resNotFound if !$v; + + enrich_vn $v; + + framework_ title => $v->{title}, index => 1, type => 'v', dbobj => $v, hiddenmsg => 1, + sub { + infobox_ $v, 1; + tabs_ $v, 'tags'; + tags_ $v; + }; +}; + 1; |