diff options
author | Yorhel <git@yorhel.nl> | 2020-02-13 15:28:05 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2020-02-13 15:28:07 +0100 |
commit | 23fb02e36defa7660ee871dd9e650906b0d2d616 (patch) | |
tree | ef96a9aad699df8a42da33930af373d4ea69e177 /lib | |
parent | f95eab4d6547d402d79e443c5488d12b74650f74 (diff) |
v2rw: WIP: Convert character pages
This uses an alternative (non-JS) approach to spoiler hiding, which is
both much simpler to implement and properly handles all cases. Downside
is that it requires a page reload. It also allows direct linking to a
character page with a particular view, but I'm not entirely sure if this
is a welcome feature.
This is a WIP because it doesn't display instances yet.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/VNDB/BBCode.pm | 15 | ||||
-rw-r--r-- | lib/VNDB/Handler/Chars.pm | 2 | ||||
-rw-r--r-- | lib/VNWeb/Chars/Page.pm | 216 | ||||
-rw-r--r-- | lib/VNWeb/Validation.pm | 28 |
4 files changed, 256 insertions, 5 deletions
diff --git a/lib/VNDB/BBCode.pm b/lib/VNDB/BBCode.pm index 3c8964dc..d11171c5 100644 --- a/lib/VNDB/BBCode.pm +++ b/lib/VNDB/BBCode.pm @@ -147,6 +147,11 @@ FINAL: } +# charspoil: +# 0/undef/missing: Output <b class="spoiler">.. +# 1: Output 'charspoil_*' classes +# 2: Just output 'hidden by spoiler setting' message +# 3: Just output the spoilers, unmarked sub bb2html { my($input, $maxlength, $charspoil) = @_; @@ -186,11 +191,13 @@ sub bb2html { $ret .= $e->($raw); } elsif($tag eq 'spoiler_start') { - $ret .= !$charspoil - ? '<b class="spoiler">' - : '<b class="grayedout charspoil charspoil_-1"><hidden by spoiler settings></b><span class="charspoil charspoil_2">'; + $ret .= !$charspoil ? '<b class="spoiler">' : + $charspoil == 1 ? '<b class="grayedout charspoil charspoil_-1"><hidden by spoiler settings></b><span class="charspoil charspoil_2">' : + $charspoil == 2 ? '<b class="grayedout charspoil charspoil_-1"><hidden by spoiler settings></b><!--' : ''; } elsif($tag eq 'spoiler_end') { - $ret .= !$charspoil ? '</b>' : '</span>'; + $ret .= !$charspoil ? '</b>' : + $charspoil == 1 ? '</span>' : + $charspoil == 2 ? '-->' : ''; } elsif($tag eq 'quote_start') { $ret .= '<div class="quote">' if !$maxlength; diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm index cff84607..059c7b3f 100644 --- a/lib/VNDB/Handler/Chars.pm +++ b/lib/VNDB/Handler/Chars.pm @@ -12,7 +12,7 @@ use List::Util 'min'; our @EXPORT = ('charOps', 'charTable', 'charBrowseTable'); TUWF::register( - qr{c([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, + qr{oc([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, qr{c(?:([1-9]\d*)(?:\.([1-9]\d*))?/(edit|copy)|/new)} => \&edit, qr{c/([a-z0]|all)} => \&list, diff --git a/lib/VNWeb/Chars/Page.pm b/lib/VNWeb/Chars/Page.pm new file mode 100644 index 00000000..06493de6 --- /dev/null +++ b/lib/VNWeb/Chars/Page.pm @@ -0,0 +1,216 @@ +package VNWeb::Chars::Page; + +use VNWeb::Prelude; + + +sub enrich_seiyuu { + my($vid, @chars) = @_; + enrich seiyuu => id => cid => sub { sql ' + SELECT DISTINCT vs.cid, sa.id, sa.name, sa.original, vs.note + FROM vn_seiyuu vs + JOIN staff_alias sa ON sa.aid = vs.aid + WHERE vs.cid IN', $_, $vid ? ('AND vs.id =', \$vid) : (), ' + ORDER BY sa.name' + }, @chars; +} + + +sub enrich_item { + my($c) = @_; + + enrich_merge vid => 'SELECT id AS vid, title, original FROM vn WHERE id IN', $c->{vns}; + enrich_merge rid => 'SELECT id AS rid, title AS rtitle, original AS roriginal FROM releases WHERE id IN', grep $_->{rid}, $c->{vns}->@*; + enrich_merge tid => + 'SELECT t.id AS tid, t.name, t.sexual, coalesce(g.id, t.id) AS group, coalesce(g.name, t.name) AS groupname, coalesce(g.order,0) AS order + FROM traits t LEFT JOIN traits g ON t.group = g.id WHERE t.id IN', $c->{traits}; + + $c->{vns} = [ sort { $a->{title} cmp $b->{title} || $a->{vid} <=> $b->{vid} || ($a->{rid}||999999) <=> ($b->{rid}||999999) } $c->{vns}->@* ]; + $c->{traits} = [ sort { $a->{order} <=> $b->{order} || $a->{groupname} cmp $b->{groupname} || $a->{name} cmp $b->{name} } $c->{traits}->@* ]; +} + + +sub _rev_ { + my($c) = @_; + revision_ c => $c, \&enrich_item, + [ name => 'Name' ], + [ original => 'Original name' ], + [ alias => 'Aliases' ], + [ desc => 'Description' ], + [ gender => 'Sex', fmt => \%GENDER ], + [ b_month => 'Birthday/month',empty => 0 ], + [ b_day => 'Birthday/day', empty => 0 ], + [ s_bust => 'Bust', empty => 0 ], + [ s_waist => 'Waist', empty => 0 ], + [ s_hip => 'Hip', empty => 0 ], + [ height => 'Height', empty => 0 ], + [ weight => 'Weight', ], + [ bloodt => 'Blood type', fmt => \%BLOOD_TYPE ], + [ cup_size => 'Cup size', fmt => \%CUP_SIZE ], + [ age => 'Age', empty => 0 ], + [ main => 'Main character',empty => 0, fmt => sub { + my $c = tuwf->dbRowi('SELECT id, name, original FROM chars WHERE id =', \$_); + a_ href => "/c$c->{id}", title => $c->{name}, "c$c->{id}" + } ], + [ main_spoil => 'Spoiler', fmt => sub { txt_ fmtspoil $_ } ], + [ image => 'Image', empty => 0, fmt => sub { img_ src => tuwf->imgurl(ch => $_) } ], + [ vns => 'Visual novels', fmt => sub { + a_ href => "/v$_->{vid}", title => $_->{original}||$_->{title}, "v$_->{vid}"; + if($_->{rid}) { + txt_ ' ['; a_ href => "/r$_->{rid}", "r$_->{rid}"; txt_ ']'; + } + txt_ " $CHAR_ROLE{$_->{role}}{txt} (".fmtspoil($_->{spoil}).')'; + } ], + [ traits => 'Traits', fmt => sub { + b_ class => 'grayedout', "$_->{groupname} / " if $_->{group} != $_->{tid}; + a_ href => "/i$_->{tid}", $_->{name}; + txt_ ' ('.fmtspoil($_->{spoil}).')'; + } ], +} + + +# TODO: Also to be used by the character listing on VN pages; But it's not +# currently compatible with VNDB::Handler::VNPage because that uses a different +# spoiler hiding mechanism. +sub chartable_ { + my($c, $link, $sep, $vn) = @_; + my $view = viewget; + + div_ mkclass(chardetails => 1, charsep => $sep), sub { + div_ class => 'charimg', sub { + p_ 'No image uploaded yet' if !$c->{image}; + img_ src => tuwf->imgurl(ch => $c->{image}), alt => $c->{name} if $c->{image}; + }; + table_ class => 'stripe', sub { + thead_ sub { tr_ sub { td_ colspan => 2, sub { + $link + ? a_ href => "/c$c->{id}", style => 'margin-right: 10px; font-weight: bold', $c->{name} + : b_ style => 'margin-right: 10px', $c->{name}; + b_ class => 'grayedout', style => 'margin-right: 10px', $c->{original} if $c->{original}; + abbr_ class => "icons gen $c->{gender}", title => $GENDER{$c->{gender}}, '' if $c->{gender} ne 'unknown'; + span_ $BLOOD_TYPE{$c->{bloodt}} if $c->{bloodt} ne 'unknown'; + }}}; + + tr_ sub { + td_ class => 'key', 'Aliases'; + td_ $c->{alias} =~ s/\n/, /rg; + } if $c->{alias}; + + tr_ sub { + td_ class => 'key', 'Measurements'; + td_ join ', ', + $c->{height} ? "Height: $c->{height}cm" : (), + defined($c->{weight}) ? "Weight: $c->{weight}kg" : (), + $c->{s_bust} || $c->{s_waist} || $c->{s_hip} ? + sprintf 'Bust-Waist-Hips: %s-%s-%scm', $c->{s_bust}||'??', $c->{s_waist}||'??', $c->{s_hip}||'??' : (), + $c->{cup_size} ? "$CUP_SIZE{$c->{cup_size}} cup" : (); + } if defined($c->{weight}) || $c->{height} || $c->{s_bust} || $c->{s_waist} || $c->{s_hip} || $c->{cup_size}; + + tr_ sub { + td_ class => 'key', 'Birthday'; + td_ $c->{b_day}.' '.[qw{January February March April May June July August September October November December}]->[$c->{b_month}-1]; + } if $c->{b_day} && $c->{b_month}; + + tr_ sub { + td_ class => 'key', 'Age'; + td_ $c->{age}; + } if defined $c->{age}; + + my @groups; + for(grep $_->{spoil} <= $view->{spoilers} && (!$_->{sexual} || $view->{traits_sexual}), $c->{traits}->@*) { + push @groups, $_ if !@groups || $groups[$#groups]{group} != $_->{group}; + push $groups[$#groups]{traits}->@*, $_; + } + tr_ sub { + td_ class => 'key', sub { a_ href => "/i$_->{group}", $_->{groupname} }; + td_ sub { join_ ', ', sub { a_ href => "/i$_->{tid}", $_->{name} }, $_->{traits}->@* }; + } for @groups; + + my @visvns = grep $_->{spoil} <= $view->{spoilers}, $c->{vns}->@*; + tr_ sub { + td_ $vn ? 'Releases' : 'Visual novels'; + td_ sub { + my @vns; + for(@visvns) { + push @vns, $_ if !@vns || $vns[$#vns]{vid} != $_->{vid}; + push $vns[$#vns]{rels}->@*, $_; + } + join_ \&br_, sub { + my $v = $_; + # Just a VN link, no releases + if(!$vn && $v->{rels}->@* == 1 && !$v->{rels}[0]{rid}) { + txt_ $CHAR_ROLE{$v->{role}}{txt}.' - '; + a_ href => "/v$v->{vid}", title => $v->{original}||$v->{title}, $v->{title}; + # With releases + } else { + a_ href => "/v$v->{vid}", title => $v->{original}||$v->{title}, $v->{title} if !$vn; + br_ if !$vn; + join_ \&br_, sub { + b_ class => 'grayedout', '> '; + txt_ $CHAR_ROLE{$v->{role}}{txt}.' - '; + if($_->{rid}) { + b_ class => 'grayedout', "r$_->{rid}:"; + a_ href => "/r$_->{rid}", title => $_->{roriginal}||$_->{rtitle}, $_->{rtitle}; + } else { + txt_ 'All other releases'; + } + }, $v->{rels}->@*; + } + }, @vns; + }; + } if @visvns && (!$vn || $vn && (@visvns > 1 || $visvns[0]{rid})); + + tr_ sub { + td_ 'Voiced by'; + td_ sub { + join_ \&br_, sub { + a_ href => "/s$_->{id}", title => $_->{original}||$_->{name}, $_->{name}; + txt_ " ($_->{note})" if $_->{note}; + }, $c->{seiyuu}->@*; + }; + } if $c->{seiyuu}->@*; + + tr_ class => 'nostripe', sub { + td_ colspan => 2, class => 'chardesc', sub { + h2_ 'Description'; + p_ sub { lit_ bb2html $c->{desc}, 0, $view->{spoilers} == 2 ? 3 : 2 }; + }; + } if $c->{desc}; + }; + }; + clearfloat_; +} + + +TUWF::get qr{/$RE{crev}} => sub { + my $c = db_entry c => tuwf->capture('id'), tuwf->capture('rev'); + return tuwf->resNotFound if !$c; + + enrich_item $c; + enrich_seiyuu undef, $c; + + framework_ title => $c->{name}, index => !tuwf->capture('rev'), type => 'c', dbobj => $c, hiddenmsg => 1, + og => { + description => bb2text $c->{desc} + }, + sub { + _rev_ $c if tuwf->capture('rev'); + div_ class => 'mainbox', sub { + itemmsg_ c => $c; + p_ class => 'mainopts', sub { + my $view = viewget; + a_ mkclass(checked => $view->{spoilers} == 0), href => '?view='.viewset(spoilers=>0), 'Hide spoilers'; + a_ mkclass(checked => $view->{spoilers} == 1), href => '?view='.viewset(spoilers=>1), 'Show minor spoilers'; + a_ mkclass(standout =>$view->{spoilers} == 2), href => '?view='.viewset(spoilers=>2), 'Spoil me!'; + b_ class => 'grayedout', ' | '; + a_ mkclass(checked => $view->{traits_sexual}), href => '?view='.viewset(traits_sexual=>!$view->{traits_sexual}), 'Show sexual traits'; + }; + h1_ sub { txt_ $c->{name}; debug_ $c }; + h2_ class => 'alttitle', $c->{original} if length $c->{original}; + chartable_ $c; + }; + + # TODO: Other instances + }; +}; + +1; diff --git a/lib/VNWeb/Validation.pm b/lib/VNWeb/Validation.pm index c506903a..b8f1bcdc 100644 --- a/lib/VNWeb/Validation.pm +++ b/lib/VNWeb/Validation.pm @@ -16,6 +16,7 @@ our @EXPORT = qw/ form_changed validate_dbid can_edit + viewget viewset /; @@ -174,4 +175,31 @@ sub can_edit { auth->permDbmod || (auth->permEdit && !($entry->{entry_hidden} || $entry->{entry_locked})); } + +# Returns { spoilers => 0-2, traits_sexual => 0/1 } +# Based on the view= query parameter or the user's preferences. +# The query parameter has the following format: +# view=1 -> spoilers=1, traits_sexual=<default> +# view=2s -> spoilers=2, traits_sexual=1 +# view=2S -> spoilers=2, traits_sexual=0 +# view=S -> spoilers=<default>, traits_sexual=0 +# i.e. a list of single-character flags: +# 0-2 -> spoilers +# s/S -> 1/0 traits_sexual +# Missing flags will use default. +sub viewget { + (tuwf->reqGet('view')) =~ /^([0-2]?)([sS]?)$/; + { + spoilers => $1 // auth->pref('spoilers') || 0, + traits_sexual => !$2 ? auth->pref('traits_sexual') : $2 eq 's', + } +} + +# Modifies the current view settings and serializes that into a view= value. +# XXX: This may include more flags than the current page will use. +sub viewset { + my %s = (viewget->%*, @_); + $s{spoilers}.($s{traits_sexual}?'s':'S') +} + 1; |