diff options
Diffstat (limited to 'lib/VN3/Char/Page.pm')
-rw-r--r-- | lib/VN3/Char/Page.pm | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/lib/VN3/Char/Page.pm b/lib/VN3/Char/Page.pm new file mode 100644 index 00000000..11939060 --- /dev/null +++ b/lib/VN3/Char/Page.pm @@ -0,0 +1,330 @@ +package VN3::Char::Page; + +use VN3::Prelude; +use List::Util 'all', 'min'; + +sub Top { + my $e = shift; + + my $img = $e->{image} && tuwf->imgurl(ch => $e->{image}); + + Div class => 'fixed-size-left-sidebar-md', sub { + Img class => 'page-header-img-mobile img img--rounded d-md-none', src => $img; + Div class => 'detail-header-image-container', sub { + Img class => 'img img--fit img--rounded elevation-1 d-none d-md-block detail-header-image', src => $img; + }; + } if $img; + + Div class => 'col-md', sub { + EntryEdit c => $e; + Div class => 'detail-page-title', sub { + Txt $e->{name}; + Txt ' '.gender_icon $e->{gender}; + Txt ' '.blood_type_display $e->{bloodt} if $e->{bloodt} ne 'unknown'; + Debug $e; + }; + Div class => 'detail-page-subtitle', $e->{original} if $e->{original}; + }; +} + + +sub Settings { + my $spoil = auth->pref('spoilers') || 0; + my $ero = auth->pref('traits_sexual'); + + Div class => 'page-inner-controls', id => 'charpage_settings', sub { + Div class => 'page-inner-controls__option dropdown', sub { + A href => 'javascript:;', class => 'link--subtle dropdown__toggle', sub { + Span class => 'page-inner-controls__option-spoil', spoil_display $spoil; + Lit ' '; + Span class => 'caret', ''; + }; + Div class => 'dropdown-menu', sub { + A class => 'dropdown-menu__item page-inner-controls__option-spoil-0', href => 'javascript:;', spoil_display 0; + A class => 'dropdown-menu__item page-inner-controls__option-spoil-1', href => 'javascript:;', spoil_display 1; + A class => 'dropdown-menu__item page-inner-controls__option-spoil-2', href => 'javascript:;', spoil_display 2; + }; + }; + Div class => 'page-inner-controls__option', sub { + Switch 'Sexual traits', $ero, 'page-inner-controls__option-ero' => 1; + }; + }; +} + + +sub Description { + my $e = shift; + + Div class => 'row', sub { + Div class => 'fixed-size-left-sidebar-md', sub { + if($e->{image}) { + # second copy of image to ensure there's enough space (uh, mkay) + Img class => 'img img--fit d-none d-md-block detail-header-image-push', src => tuwf->imgurl(ch => $e->{image}); + } else { + H3 class => 'detail-page-sidebar-section-header', 'Description'; + } + }; + Div class => 'col-md', sub { + Div class => 'description serif mb-5', sub { + P sub { Lit bb2html $e->{desc} }; + }; + }; + } if $e->{desc}; +} + + +sub DetailsTable { + my $e = shift; + + my(%groups, @groups); + for(@{$e->{traits}}) { + push @groups, $_->{gid} if !$groups{$_->{gid}}; + push @{$groups{$_->{gid}}}, $_; + } + + # TODO: This was copy-pasted from VN::Page, need to consolidate (...once we figure out how to actually display chars on the VN page) + my @list = ( + $e->{alias} ? sub { + Dt 'Aliases'; + Dd $e->{alias} =~ s/\n/, /gr; + } : (), + + defined $e->{weight} || $e->{height} || $e->{s_bust} || $e->{s_waist} || $e->{s_hip} ? sub { + Dt 'Measurements'; + Dd join ', ', + $e->{height} ? "Height: $e->{height}cm" : (), + defined $e->{weight} ? "Weight: $e->{weight}kg" : (), + $e->{s_bust} || $e->{s_waist} || $e->{s_hip} ? + sprintf 'Bust-Waist-Hips: %s-%s-%scm', $e->{s_bust}||'??', $e->{s_waist}||'??', $e->{s_hip}||'??' : (); + } : (), + + $e->{b_month} && $e->{b_day} ? sub { + Dt 'Birthday'; + Dd sprintf '%d %s', $e->{b_day}, [qw{January February March April May June July August September October November December}]->[$e->{b_month}-1]; + } : (), + + # XXX: Group visibility is determined by the same 'charpage--x' classes + # as the individual traits (group is considered 'ero' if all traits are + # ero, and the lowest trait spoiler determines group spoiler level). + # But this has an unfortunate special case that isn't handled: A trait + # with (ero && spoil>0) in a group that isn't itself (ero && spoil>0) + # will display an empty group if settings are (ero && spoil==0). + # XXX#2: I'd rather have the traits delimited by a comma, but that's a + # hard problem to solve in combination with the dynamic hiding of + # traits. + (map { my $g = $_; sub { + my @c = mkclass + 'charpage--ero' => (all { $_->{sexual} } @{$groups{$g}}), + sprintf('charpage--spoil-%d', min map $_->{spoil}, @{$groups{$g}}) => 1; + + Dt @c, sub { A href => "/i$g", $groups{$g}[0]{group} }; + Dd @c, sub { + Join ' ', sub { + A mkclass('trait-summary--trait' => 1, 'charpage--ero' => $_[0]{sexual}, sprintf('charpage--spoil-%d', $_[0]{spoil}), 1), + style => 'padding-right: 15px; white-space: nowrap', + href => "/i$_[0]{tid}", $_[0]{name} + }, @{$groups{$g}}; + }; + } } @groups), + ); + + Div class => 'row', sub { + Div class => 'fixed-size-left-sidebar-md', sub { + H2 class => 'detail-page-sidebar-section-header', 'Details'; + }; + Div class => 'col-md', sub { + Div class => 'card card--white mb-5', sub { + Div class => 'card__section fs-medium', sub { + Div class => 'row', sub { + Dl class => 'col-md dl--horizontal', sub { $_->() for @list[0..$#list/2] }; + Dl class => 'col-md dl--horizontal', sub { $_->() for @list[$#list/2+1..$#list] }; + } + } + } + } + } if @list; +} + + +sub VNs { + my $e = shift; + + # TODO: Maybe this table should be full-width? + # TODO: Improved styling of release rows + + my $rows = sub { + for my $vn (@{$e->{vns}}) { + Tr class => sprintf('charpage--spoil-%d', $vn->{spoil}), sub { + Td class => 'tabular-nums muted', sub { ReleaseDate $vn->{c_released} }; + Td sub { + A href => "/v$vn->{vid}", title => $vn->{original}||$vn->{title}, $vn->{title}; + }; + Td $vn->{releases}[0]{rid} ? '' : join ', ', map char_role_display($_->{role}), @{$vn->{releases}}; + Td sub { + Join ', ', sub { + A href => "/s$_[0]{sid}", title => $_[0]{original}||$_[0]{name}, $_[0]{name}; + Span class => 'muted', " ($_[0]{note})" if $_[0]{note}; + }, @{$vn->{seiyuu}}; + } + }; + for my $rel ($vn->{releases}[0]{rid} ? @{$vn->{releases}} : ()) { + Tr class => sprintf('charpage--spoil-%d', $rel->{spoil}), sub { + Td class => 'tabular-nums muted', $rel->{rid} ? sub { Lit ' '; ReleaseDate $rel->{released} } : ''; + Td sub { + Span class => 'muted', 'ยป '; + A href => "/r$rel->{rid}", title => $rel->{title}||$rel->{original}, $rel->{title} if $rel->{rid}; + Span class => 'muted', 'Other releases' if !$rel->{rid}; + }; + Td char_role_display $rel->{role}; + Td ''; + }; + } + } + }; + + Div class => 'row', sub { + Div class => 'fixed-size-left-sidebar-md', sub { + H2 class => 'detail-page-sidebar-section-header', 'Visual Novels'; + }; + Div class => 'col-md', sub { + Div class => 'card card--white mb-5', sub { + Table class => 'table table--responsive-single-sm fs-medium', sub { + Thead sub { + Tr sub { + Th width => '15%', 'Date'; + Th width => '40%', 'Title'; + Th width => '20%', 'Role'; + Th width => '25%', 'Voiced by'; + }; + }; + Tbody $rows; + }; + } + } + } +} + + +sub Instances { + my $e = shift; + + return if !@{$e->{instances}}; + + my $minspoil = min map $_->{spoiler}, @{$e->{instances}}; + + Div class => sprintf('row charpage--spoil-%d', $minspoil), sub { + Div class => 'fixed-size-left-sidebar-md', sub { + H2 class => 'detail-page-sidebar-section-header', 'Other instances'; + }; + Div class => 'col-md', sub { + for my $c (@{$e->{instances}}) { + A class => sprintf('card card--white character-card mb-3 charpage--spoil-%d', $c->{spoiler}), href => "/c$c->{id}", sub { + Div class => 'character-card__left', sub { + Div class => 'character-card__image-container', sub { + Img class => 'character-card__image', src => tuwf->imgurl(ch => $c->{image}) if $c->{image}; + }; + Div class => 'character-card__main', sub { + Div class => 'character-card__name', sub { + Txt $c->{name}; + Txt ' '.gender_icon $c->{gender}; + Txt ' '.blood_type_display $c->{bloodt} if $c->{bloodt} ne 'unknown'; + }; + Div class => 'character-card__sub-name', $c->{original} if $c->{original}; + Div class => 'character-card__vns muted single-line', join ', ', map $_->{title}, @{$c->{vns}} if @{$c->{vns}}; + }; + Div class => 'character-card__right serif semi-muted', sub { + Lit bb2text $c->{desc}; # TODO: maxlength? + }; + } + } + } + }; + }; +} + + +TUWF::get qr{/$CREV_RE}, sub { + my $e = entry c => tuwf->capture('id'), tuwf->capture('rev') or return tuwf->resNotFound; + return tuwf->resNotFound if !$e->{id} || $e->{hidden}; + + enrich tid => q{ + SELECT t.id AS tid, t.name, t.sexual, g.id AS gid, g.name AS group, g.order + FROM traits t + JOIN traits g ON g.id = t.group + WHERE t.id IN + }, $e->{traits}; + + $e->{traits} = [ sort { $a->{order} <=> $b->{order} || $a->{name} cmp $b->{name} } @{$e->{traits}} ]; + + $e->{vns} = tuwf->dbAlli(q{ + SELECT cv.vid, v.title, v.original, v.c_released, MIN(cv.spoil) AS spoil + FROM chars_vns_hist cv + JOIN vn v ON cv.vid = v.id + WHERE cv.chid =}, \$e->{chid}, q{ + GROUP BY v.c_released, cv.vid, v.title, v.original + ORDER BY v.c_released, cv.vid + }); + + enrich_list releases => vid => vid => sub {sql q{ + SELECT cv.rid, cv.vid, cv.role, cv.spoil, r.title, r.original, r.released + FROM chars_vns_hist cv + LEFT JOIN releases r ON r.id = cv.rid + WHERE cv.chid =}, \$e->{chid}, q{ + ORDER BY r.released, r.id + }}, $e->{vns}; + + enrich_list seiyuu => vid => vid => sub {sql q{ + SELECT vs.id AS vid, vs.note, sa.id AS sid, sa.aid, sa.name, sa.original + FROM vn_seiyuu vs + JOIN staff_alias sa ON vs.aid = sa.aid + WHERE vs.cid =}, \$e->{id}, q{ + ORDER BY sa.name, sa.aid + }}, $e->{vns}; + + $e->{instances} = tuwf->dbAlli(q{ + SELECT id, name, original, image, gender, bloodt, "desc", + (CASE WHEN id =}, \$e->{main}, THEN => \$e->{main_spoil}, q{ELSE main_spoil END) AS spoiler + FROM chars + WHERE NOT hidden + AND id <>}, \$e->{id}, q{ + AND ( main =}, \$e->{id}, q{ + OR main =}, \$e->{main}, q{ + OR id =}, \$e->{main}, q{ + ) + ORDER BY name, id + }); + enrich_list vns => id => cid => sub {sql q{ + SELECT cv.id AS cid, v.id, v.title + FROM chars_vns cv + JOIN vn v ON v.id = cv.vid + WHERE cv.id IN}, $_[0], q{ + AND cv.spoil = 0 + GROUP BY v.id, cv.id, v.title + ORDER BY MIN(cv.role), v.title, v.id + }}, $e->{instances}; + + my $spoil = auth->pref('spoilers') || 0; + my $ero = auth->pref('traits_sexual'); + + Framework + og => { + description => bb2text($e->{desc}), + $e->{image} ? (image => tuwf->imgurl(ch => $e->{image})) : () + }, + title => $e->{name}, + main_classes => { + 'charpage--hide-spoil-1' => $spoil < 1, + 'charpage--hide-spoil-2' => $spoil < 2, + 'charpage--hide-ero' => !$ero + }, + top => sub { Top $e }, + sub { + Settings $e; + Description $e; + DetailsTable $e; + VNs $e; + Instances $e; + }; +}; + +1; |