summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/global.pl14
-rw-r--r--lib/VNDB/DB/VNList.pm73
-rw-r--r--lib/VNDB/Handler/ULists.pm108
-rw-r--r--lib/VNDB/Util/CommonHTML.pm2
-rw-r--r--static/f/script.js32
-rw-r--r--static/f/style.css12
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>&#9656;</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>&#9656;</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' ? '&#9656;' : '&#9662;';
+ }
+ };
+ 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 ? '&#9656;' : '&#9662;';
+ }
+ this.getElementsByTagName('i')[0].innerHTML = allhid ? '&#9656;' : '&#9662;';
+ };
+ }
+
+
// 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 *****/