diff options
-rw-r--r-- | data/global.pl | 14 | ||||
-rw-r--r-- | lib/VNDB/DB/VNList.pm | 73 | ||||
-rw-r--r-- | lib/VNDB/Handler/ULists.pm | 108 | ||||
-rw-r--r-- | lib/VNDB/Util/CommonHTML.pm | 2 | ||||
-rw-r--r-- | static/f/script.js | 32 | ||||
-rw-r--r-- | static/f/style.css | 12 |
6 files changed, 240 insertions, 1 deletions
diff --git a/data/global.pl b/data/global.pl index b6c83063..3eb7a623 100644 --- a/data/global.pl +++ b/data/global.pl @@ -203,6 +203,20 @@ our %S = ( 'low', 'blacklist', ], + vn_rstat => [ + 'Unknown', + 'Pending', + 'Obtained', # hardcoded + 'On loan', + 'Deleted', + ], + vn_vstat => [ + 'Unknown', + 'Playing', + 'Finished', # hardcoded + 'Stalled', + 'Dropped', + ], ); diff --git a/lib/VNDB/DB/VNList.pm b/lib/VNDB/DB/VNList.pm new file mode 100644 index 00000000..40ff20c7 --- /dev/null +++ b/lib/VNDB/DB/VNList.pm @@ -0,0 +1,73 @@ + +package VNDB::DB::VNList; + +use strict; +use warnings; +use Exporter 'import'; + + +our @EXPORT = qw|dbVNListGet|; + + +# %options->{ uid order char voted page results } +# NOTE: this function is mostly copied from 1.x, may need some rewriting... +sub dbVNListGet { + my($self, %o) = @_; + + $o{results} ||= 50; + $o{page} ||= 1; + $o{order} ||= 'vr.title ASC'; + $o{voted} ||= 0; # -1: only non-voted, 0: all, 1: only voted + + # construct the global WHERE clause + my $where = $o{voted} != -1 ? 'vo.vote IS NOT NULL' : ''; + $where .= ($where?' OR ':'').q|v.id IN( + SELECT irv.vid + FROM rlists irl + JOIN releases ir ON ir.id = irl.rid + JOIN releases_vn irv ON irv.rid = ir.latest + WHERE uid = ? + )| if $o{voted} != 1; + $where = '('.$where.') AND LOWER(SUBSTR(vr.title, 1, 1)) = \''.$o{char}.'\'' if $o{char}; + $where = '('.$where.') AND (ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)' if defined $o{char} && !$o{char}; + $where = '('.$where.') AND vo.vote IS NULL' if $o{voted} == -1; + + # execute query + my($r, $np) = $self->dbPage(\%o, qq| + SELECT vr.vid, vr.title, vr.original, v.c_released, v.c_languages, v.c_platforms, COALESCE(vo.vote, 0) AS vote + FROM vn v + JOIN vn_rev vr ON vr.id = v.latest + !s JOIN votes vo ON vo.vid = v.id AND vo.uid = ? + WHERE $where + ORDER BY !s|, + $o{voted} == 1 ? '' : 'LEFT', $o{uid}, # JOIN if we only want votes, LEFT JOIN if we also want rlist items + $o{voted} != 1 ? $o{uid} : (), $o{order}, + ); + + # fetch releases and link to VNs + if(@$r) { + my %vns = map { + $_->{rels}=[]; + $_->{vid}, $_->{rels} + } @$r; + + push @{$vns{$_->{vid}}}, $_ for (@{$self->dbAll(q| + SELECT rv.vid, rr.rid, rr.title, rr.original, rr.released, rr.type, rr.language, rr.minage, rl.rstat, rl.vstat + FROM rlists rl + JOIN releases r ON rl.rid = r.id + JOIN releases_rev rr ON rr.id = r.latest + JOIN releases_vn rv ON rv.rid = r.latest + WHERE rl.uid = ? + AND rv.vid IN(!l) + ORDER BY rr.released ASC|, + $o{uid}, [ keys %vns ] + )}); + } + + return wantarray ? ($r, $np) : $r; +} + + + +1; + diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm index f8603014..fcb8d6c0 100644 --- a/lib/VNDB/Handler/ULists.pm +++ b/lib/VNDB/Handler/ULists.pm @@ -11,6 +11,7 @@ YAWF::register( qr{v([1-9]\d*)/vote}, \&vnvote, qr{v([1-9]\d*)/wish}, \&vnwish, qr{u([1-9]\d*)/wish}, \&wishlist, + qr{u([1-9]\d*)/list}, \&vnlist, ); @@ -150,5 +151,112 @@ sub wishlist { } +sub vnlist { + my($self, $uid) = @_; + + my $own = $self->authInfo->{id} && $self->authInfo->{id} == $uid; + my $u = $self->dbUserGet(uid => $uid)->[0]; + return 404 if !$u || !$own && !$u->{show_list}; + + my $f = $self->formValidate( + { name => 'p', required => 0, default => 1, template => 'int' }, + { name => 'o', required => 0, default => 'a', enum => [ 'a', 'd' ] }, + { name => 's', required => 0, default => 'title', enum => [ 'title', 'vote' ] }, + { name => 'c', required => 0, default => 'all', enum => [ 'all', 'a'..'z', 0 ] }, + { name => 'v', required => 0, default => 0, enum => [ -1..1 ] }, + ); + return 404 if $f->{_err}; + + my($list, $np) = $self->dbVNListGet( + uid => $uid, + results => 50, + page => $f->{p}, + order => $f->{s}.' '.($f->{o} eq 'd' ? 'DESC' : 'ASC'), + voted => $f->{v}, + $f->{c} ne 'all' ? (char => $f->{c}) : (), + ); + + my $title = $own ? 'My visual novel list' : "\u$u->{username}'s visual novel list"; + $self->htmlHeader(title => $title); + $self->htmlMainTabs('u', $u, 'list'); + + # url generator + my $url = sub { + my($n, $v) = @_; + $n ||= ''; + local $_ = "/u$uid/list"; + $_ .= '?c='.($n eq 'c' ? $v : $f->{c}); + $_ .= ';v='.($n eq 'v' ? $v : $f->{v}); + if($n eq 'page') { + $_ .= ';o='.($n eq 'o' ? $v : $f->{o}); + $_ .= ';s='.($n eq 's' ? $v : $f->{s}); + } + return $_; + }; + + div class => 'mainbox'; + h1 $title; + p class => 'browseopts'; + for ('all', 'a'..'z', 0) { + a href => $url->(c => $_), $_ eq $f->{c} ? (class => 'optselected') : (), $_ ? uc $_ : '#'; + } + end; + p class => 'browseopts'; + a href => $url->(v => 0), 0 == $f->{v} ? (class => 'optselected') : (), 'All'; + a href => $url->(v => 1), 1 == $f->{v} ? (class => 'optselected') : (), 'Only voted'; + a href => $url->(v => -1), -1 == $f->{v} ? (class => 'optselected') : (), 'Hide voted'; + end; + end; + + $self->htmlBrowse( + class => 'rlist', + items => $list, + nextpage => $np, + options => $f, + sorturl => $url->(), + pageurl => $url->('page'), + header => [ + [ Title => 'title', 3 ], + sub { td class => 'tc2', id => 'relhidall'; lit '<i>▸</i>Releases'; end; }, + [ Vote => 'vote' ], + ], + row => sub { + my($s, $n, $i) = @_; + Tr $n % 2 == 0 ? (class => 'odd') : (); + td class => 'tc1', colspan => 3; + a href => "/v$i->{vid}", title => $i->{original}||$i->{title}, shorten $i->{title}, 70; + end; + td class => 'tc2'.(@{$i->{rels}} ? ' relhid_but' : ''), id => 'vid'.$i->{vid}; + lit '<i>▸</i>'; + my $obtained = grep $_->{rstat}==2, @{$i->{rels}}; + my $finished = grep $_->{vstat}==2, @{$i->{rels}}; + my $txt = sprintf '%d/%d/%d', $obtained, $finished, scalar @{$i->{rels}}; + $txt = qq|<b class="done">$txt</b>| if $finished > $obtained || $finished && $finished == $obtained; + $txt = qq|<b class="todo">$txt</b>| if $obtained > $finished; + lit $txt; + end; + td class => 'tc3', $i->{vote} || '-'; + end; + for (@{$i->{rels}}) { + Tr class => "relhid vid$i->{vid} "; + td class => 'tc1'; + lit datestr $_->{released}; + end; + td class => 'tc2'; + acronym class => "icons lang $_->{language}", title => $self->{languages}{$_->{language}}, ' '; + acronym class => 'icons '.substr(lc $self->{release_types}[$_->{type}], 0, 3), title => $self->{release_types}[$_->{type}].' release', ' '; + txt substr($self->{vn_rstat}[$_->{rstat}],0,2).'/'.substr($self->{vn_vstat}[$_->{vstat}],0,2); + end; + td class => 'tc3', colspan => 1; + a href => "/r$_->{rid}", title => $_->{original}||$_->{title}, shorten $_->{title}, 50; + end; + end; + } + }, + ); + $self->htmlFooter; +} + + 1; diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm index 68e12411..77897a78 100644 --- a/lib/VNDB/Util/CommonHTML.pm +++ b/lib/VNDB/Util/CommonHTML.pm @@ -162,7 +162,7 @@ sub htmlBrowse { if(ref $opt{header}[$_] eq 'CODE') { $opt{header}[$_]->($self, $_+1); } else { - td class => 'tc'.($_+1); + td class => 'tc'.($_+1), $opt{header}[$_][2] ? (colspan => $opt{header}[$_][2]) : (); lit $opt{header}[$_][0]; if($opt{header}[$_][1]) { lit ' '; diff --git a/static/f/script.js b/static/f/script.js index c7755e54..6f775bba 100644 --- a/static/f/script.js +++ b/static/f/script.js @@ -290,6 +290,38 @@ DOMLoad(function() { }; + // User VN list + i = x('relhidall'); + if(i) { + var l = document.getElementsByTagName('tr'); + for(var i=0;i<l.length;i++) + if(l[i].className.indexOf('relhid') >= 0) + l[i].style.display = 'none'; + var l = document.getElementsByTagName('td'); + for(var i=0;i<l.length;i++) + if(l[i].className.indexOf('relhid_but') >= 0) + l[i].onclick = function() { + var l = document.getElementsByTagName('tr'); + for(var i=0;i<l.length;i++) + if(l[i].className.substr(7) == this.id) { + l[i].style.display = l[i].style.display == 'none' ? '' : 'none'; + this.getElementsByTagName('i')[0].innerHTML = l[i].style.display == 'none' ? '▸' : '▾'; + } + }; + var allhid = 1; + x('relhidall').onclick = function() { + allhid = !allhid; + var l = document.getElementsByTagName('tr'); + for(var i=0;i<l.length;i++) + if(l[i].className.indexOf('relhid') >= 0) { + l[i].style.display = allhid ? 'none' : ''; + x(l[i].className.substr(7)).getElementsByTagName('i')[0].innerHTML = allhid ? '▸' : '▾'; + } + this.getElementsByTagName('i')[0].innerHTML = allhid ? '▸' : '▾'; + }; + } + + // Advanced VN search if(x('advselect')) searchInit(); diff --git a/static/f/style.css b/static/f/style.css index b277dca5..4a7a08b9 100644 --- a/static/f/style.css +++ b/static/f/style.css @@ -826,6 +826,18 @@ ul#catselect li li.exc { background-position: 0px -33px; color: #c00; } +/***** User VN list browser ******/ + +.rlist .relhid { } +.rlist .relhid_but, #relhidall { cursor: pointer } +.browse.rlist .tc2 { width: 100px; } +.browse.rlist .tc3 { width: 70px; } +.browse.rlist .relhid .tc1 { padding-left: 40px; width: 70px; } +.browse.rlist .relhid .tc2 { padding: 0; width: 60px; } +.browse.rlist .relhid .tc3 { width: auto } +.rlist .done { font-weight: normal; color: #0c0 } +.rlist .todo { font-weight: normal; color: #c00 } + /***** Warning/Notice Box *****/ |