summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/style.css20
-rw-r--r--elm/ReleaseExtLinks.elm29
-rw-r--r--elm/UList/Opt.elm4
-rw-r--r--elm/UList/ReleaseEdit.elm6
-rw-r--r--lib/VNDB/ExtLinks.pm8
-rw-r--r--lib/VNDB/Types.pm20
-rw-r--r--lib/VNWeb/Releases/Lib.pm24
-rw-r--r--lib/VNWeb/Releases/Page.pm2
-rw-r--r--lib/VNWeb/User/Lists.pm2
-rw-r--r--lib/VNWeb/VN/Page.pm84
10 files changed, 168 insertions, 31 deletions
diff --git a/data/style.css b/data/style.css
index f0056bfb..9c1e824a 100644
--- a/data/style.css
+++ b/data/style.css
@@ -100,6 +100,10 @@ div.warning h2, div.notice h2 { font-size: 13px; font-weight: bold; margin: 0; }
.maintabs .elm_dd > a { box-sizing: border-box; height: 21px; padding: 1px 15px 0 7px; border: 1px solid $border$; border-bottom: none; background-color: $tabbg$; color: $maintext$ }
.elm_votedd .elm_dd > div > ul li { text-align: left }
.elm_dd_input .elm_dd > a { background-color: $secbg$; color: $maintext$; border: 1px solid $secborder$; font: 14px "Tahoma", "Arial", sans-serif; padding: 1px 15px 1px 2px; margin: -1px }
+.elm_dd_noarrow .elm_dd > a { padding-right: 0 }
+.elm_dd_noarrow .elm_dd > a > span:last-child { display: none }
+.elm_dd_left .elm_dd > div { float: left }
+.elm_dd_left .elm_dd > div > ul { right: 4px; top: -20px }
@@ -478,8 +482,7 @@ div#vntags { margin: 0 30px 0 30px; border-top: 1px solid $bo
.releases td.tc2 { text-align: center; width: 50px; white-space: nowrap }
.releases td.tc3 { text-align: right; padding: 0; width: 90px; }
.releases td.tc_icons { padding: 0 4px }
-.releases td.tc5 { width: 70px; }
-.releases td.tc5 a { color: $maintext$; border: 0; }
+.releases td.tc5 { width: 70px; text-align: right }
.releases td.tc6 { text-align: right; width: 25px; padding: 0; white-space: nowrap }
.rllinks_dd a { text-align: right }
@@ -1131,15 +1134,10 @@ a .icons { cursor: pointer }
$iconcss$
-.release_icons_container { width: 16px; height: 16px; float: right; margin-left: 4px; }
-.release_icons { width: 16px; height: 16px; }
-.release_icon_not_voiced, .release_icon_story_not_animated, .release_icon_ero_not_animated { }
-.release_icon_ero_voiced, .release_icon_story_simple_animated, .release_icon_ero_simple_animated { filter: hue-rotate(30deg); }
-.release_icon_partially_voiced, .release_icon_story_some_fully_animated, .release_icon_ero_some_fully_animated { filter: invert(100%) hue-rotate(240deg); }
-.release_icon_fully_voiced, .release_icon_story_all_fully_animated, .release_icon_ero_all_fully_animated { filter: hue-rotate(80deg); }
-.release_icon_notes, .release_icon_unknown, .release_icon_freeware, .release_icon_nonfree,
- .release_icon_commercial, .release_icon_doujin, .release_icon_res16-9, .release_icon_res4-3,
- .release_icon_disk, .release_icon_cartridge, .release_icon_download { }
+.release_icons { width: 16px; height: 16px; float: right; margin-left: 4px; }
+.release_icon_voiced2, .release_icon_anim2 { filter: hue-rotate(30deg); }
+.release_icon_voiced3, .release_icon_anim3 { filter: invert(100%) hue-rotate(240deg); }
+.release_icon_voiced4, .release_icon_anim4 { filter: hue-rotate(80deg); }
/* Relation graph colors */
svg .border { fill: none; stroke: $border$ }
diff --git a/elm/ReleaseExtLinks.elm b/elm/ReleaseExtLinks.elm
new file mode 100644
index 00000000..1d64e944
--- /dev/null
+++ b/elm/ReleaseExtLinks.elm
@@ -0,0 +1,29 @@
+-- Helper for VNWeb::Releases::Lib::release_extlinks_()
+module ReleaseExtLinks exposing (main)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Browser
+import Lib.Api as Api
+import Lib.DropDown as DD
+
+type alias Links = List (String, String, Maybe String)
+type alias Model = { lnk : Links, dd : DD.Config Bool }
+
+main : Program (String,Links) Model Bool
+main = Browser.element
+ { init = \(id,l) -> ({ lnk = l, dd = DD.init ("relextlink_"++id) identity }, Cmd.none)
+ , view = view
+ , update = \b m -> ({ m | dd = DD.toggle m.dd b }, Cmd.none)
+ , subscriptions = \model -> DD.sub model.dd
+ }
+
+view : Model -> Html Bool
+view model =
+ div [ class "elm_dd_noarrow", class "elm_dd_left" ]
+ [ DD.view model.dd Api.Normal
+ (span [ class "fake_link" ] [ text <| String.fromInt (List.length model.lnk), abbr [ class "icons external", title "External link" ] [] ])
+ (\_ -> [ ul [ class "rllinks_dd" ] <| List.map (\(lbl,url,price) ->
+ li [] [ a [ href url ] [ Maybe.withDefault (text "") (Maybe.map (\p -> span [] [ text p ]) price), text lbl ] ]
+ ) model.lnk ])
+ ]
diff --git a/elm/UList/Opt.elm b/elm/UList/Opt.elm
index cf176981..87b123fa 100644
--- a/elm/UList/Opt.elm
+++ b/elm/UList/Opt.elm
@@ -55,7 +55,7 @@ init f =
, notes = f.notes
, notesRev = 0
, notesState = Api.Normal
- , rels = List.map2 (\st nfo -> RE.init f.vid { uid = f.uid, rid = nfo.id, status = Just st }) f.relstatus f.rels
+ , rels = List.map2 (\st nfo -> RE.init f.vid { uid = f.uid, rid = nfo.id, status = Just st, empty = "" }) f.relstatus f.rels
, relNfo = Dict.fromList <| List.map (\r -> (r.id, r)) f.rels
, relOptions = Nothing
, relState = Api.Normal
@@ -132,7 +132,7 @@ update msg model =
}, Cmd.none)
RelLoaded e -> ({ model | relState = Api.Error e }, Cmd.none)
RelAdd rid ->
- ( { model | rels = model.rels ++ (if rid == 0 then [] else [RE.init model.flags.vid { rid = rid, uid = model.flags.uid, status = Just 2 }]) }
+ ( { model | rels = model.rels ++ (if rid == 0 then [] else [RE.init model.flags.vid { rid = rid, uid = model.flags.uid, status = Just 2, empty = "" }]) }
, Task.perform (always <| Rel rid <| RE.Set (Just 2) True) <| Task.succeed True)
diff --git a/elm/UList/ReleaseEdit.elm b/elm/UList/ReleaseEdit.elm
index dbd9ec29..5373e316 100644
--- a/elm/UList/ReleaseEdit.elm
+++ b/elm/UList/ReleaseEdit.elm
@@ -25,6 +25,7 @@ type alias Model =
{ uid : Int
, rid : Int
, status : Maybe Int
+ , empty : String
, state : Api.State
, dd : DD.Config Msg
}
@@ -34,6 +35,7 @@ init vid f =
{ uid = f.uid
, rid = f.rid
, status = f.status
+ , empty = f.empty
, state = Api.Normal
, dd = DD.init ("ulist_reldd" ++ String.fromInt vid ++ "_" ++ String.fromInt f.rid) Open
}
@@ -50,7 +52,7 @@ update msg model =
Open b -> ({ model | dd = DD.toggle model.dd b }, Cmd.none)
Set st _ ->
( { model | dd = DD.toggle model.dd False, status = st, state = Api.Loading }
- , GRS.send { uid = model.uid, rid = model.rid, status = st } Saved )
+ , GRS.send { uid = model.uid, rid = model.rid, status = st, empty = "" } Saved )
Saved GApi.Success -> ({ model | state = Api.Normal }, Cmd.none)
Saved e -> ({ model | state = Api.Error e }, Cmd.none)
@@ -59,7 +61,7 @@ update msg model =
view : Model -> Html Msg
view model =
DD.view model.dd model.state
- (text <| Maybe.withDefault "not on your list" <| Maybe.andThen (\s -> lookup s rlistStatus) model.status)
+ (text <| Maybe.withDefault model.empty <| Maybe.andThen (\s -> lookup s rlistStatus) model.status)
<| \_ ->
[ ul [] <| List.map (\(n, status) ->
li [ ] [ linkRadio (Just n == model.status) (Set (Just n)) [ text status ] ]
diff --git a/lib/VNDB/ExtLinks.pm b/lib/VNDB/ExtLinks.pm
index 2d057847..00472abd 100644
--- a/lib/VNDB/ExtLinks.pm
+++ b/lib/VNDB/ExtLinks.pm
@@ -218,7 +218,7 @@ sub enrich_extlinks {
my sub w {
return if !$obj->{l_wikidata};
my($v, $fmt, $label) = ($w->{$obj->{l_wikidata}}{$_[0]}, @{$WIKIDATA{$_[0]}}{'fmt', 'label'});
- push @links, map [ $label, ref $fmt ? $fmt->($_) : sprintf $fmt, $_ ], ref $v ? @$v : $v ? $v : ()
+ push @links, map [ $label, ref $fmt ? $fmt->($_) : sprintf($fmt, $_), undef ], ref $v ? @$v : $v ? $v : ()
}
my sub l {
my($f, $price) = @_;
@@ -242,7 +242,7 @@ sub enrich_extlinks {
w 'howlongtobeat';
w 'igdb_game';
l 'l_renai';
- push @links, [ 'VNStat', sprintf 'https://vnstat.net/novel/%d', $obj->{id} ] if $obj->{c_votecount}>=20;
+ push @links, [ 'VNStat', sprintf('https://vnstat.net/novel/%d', $obj->{id}), undef ] if $obj->{c_votecount}>=20;
}
# Release links
@@ -250,7 +250,7 @@ sub enrich_extlinks {
l 'l_egs';
l 'l_erotrail';
l 'l_steam';
- push @links, [ 'SteamDB', sprintf 'https://steamdb.info/app/%d/info', $obj->{l_steam} ] if $obj->{l_steam};
+ push @links, [ 'SteamDB', sprintf('https://steamdb.info/app/%d/info', $obj->{l_steam}), undef ] if $obj->{l_steam};
l 'l_dlsite', $obj->{l_dlsite_price};
l 'l_dlsiteen', $obj->{l_dlsiteen_price};
l 'l_gog';
@@ -289,7 +289,7 @@ sub enrich_extlinks {
w 'mobygames_company';
w 'gamefaqs_company';
w 'doujinshi_author';
- push @links, [ 'VNStat', sprintf 'https://vnstat.net/developer/%d', $obj->{id} ];
+ push @links, [ 'VNStat', sprintf('https://vnstat.net/developer/%d', $obj->{id}), undef ];
}
$obj->{extlinks} = \@links
diff --git a/lib/VNDB/Types.pm b/lib/VNDB/Types.pm
index dfb187f4..573562a2 100644
--- a/lib/VNDB/Types.pm
+++ b/lib/VNDB/Types.pm
@@ -182,20 +182,20 @@ hash TAG_CATEGORY =>
hash ANIMATED =>
- 0 => { txt => 'Unknown', story_icon => 'unknown', ero_icon => 'unknown' },
- 1 => { txt => 'No animations', story_icon => 'story_not_animated', ero_icon => 'ero_not_animated' },
- 2 => { txt => 'Simple animations', story_icon => 'story_simple_animated', ero_icon => 'ero_simple_animated' },
- 3 => { txt => 'Some fully animated scenes', story_icon => 'story_some_fully_animated', ero_icon => 'ero_some_fully_animated' },
- 4 => { txt => 'All scenes fully animated', story_icon => 'story_all_fully_animated', ero_icon => 'ero_all_fully_animated' };
+ 0 => { txt => 'Unknown' },
+ 1 => { txt => 'No animations' },
+ 2 => { txt => 'Simple animations' },
+ 3 => { txt => 'Some fully animated scenes' },
+ 4 => { txt => 'All scenes fully animated' };
hash VOICED =>
- 0 => { txt => 'Unknown', icon => 'unknown' },
- 1 => { txt => 'Not voiced', icon => 'not_voiced' },
- 2 => { txt => 'Only ero scenes voiced', icon => 'ero_voiced' },
- 3 => { txt => 'Partially voiced', icon => 'partially_voiced' },
- 4 => { txt => 'Fully voiced', icon => 'fully_voiced' };
+ 0 => { txt => 'Unknown' },
+ 1 => { txt => 'Not voiced' },
+ 2 => { txt => 'Only ero scenes voiced' },
+ 3 => { txt => 'Partially voiced' },
+ 4 => { txt => 'Fully voiced' };
diff --git a/lib/VNWeb/Releases/Lib.pm b/lib/VNWeb/Releases/Lib.pm
new file mode 100644
index 00000000..a5853504
--- /dev/null
+++ b/lib/VNWeb/Releases/Lib.pm
@@ -0,0 +1,24 @@
+package VNWeb::Releases::Lib;
+
+use VNWeb::Prelude;
+use Exporter 'import';
+
+our @EXPORT = qw/release_extlinks_/;
+
+
+# Generate the html for an 'external links' dropdown, assumes enrich_extlinks() has already been called on this object.
+sub release_extlinks_ {
+ my($r, $id) = @_;
+ return if !$r->{extlinks}->@*;
+ my $has_dd = $r->{extlinks}->@* > ($r->{website} ? 1 : 0);
+ my sub icon_ {
+ a_ href => $r->{website}||'#', sub {
+ txt_ scalar $r->{extlinks}->@* if $has_dd;
+ abbr_ class => 'icons external', title => 'External link', '';
+ }
+ }
+ elm_ ReleaseExtLinks => undef, [ ''.($id||$r->{id}), $r->{extlinks} ], \&icon_ if $has_dd;
+ icon_ if !$has_dd;
+}
+
+1;
diff --git a/lib/VNWeb/Releases/Page.pm b/lib/VNWeb/Releases/Page.pm
index d0c6d620..77a50f27 100644
--- a/lib/VNWeb/Releases/Page.pm
+++ b/lib/VNWeb/Releases/Page.pm
@@ -193,7 +193,7 @@ sub _infotable_ {
td_ sub {
div_ class => 'elm_dd_input', style => 'width: 150px', sub {
my $d = tuwf->dbVali('SELECT status FROM rlists WHERE', { rid => $r->{id}, uid => auth->uid });
- elm_ 'UList.ReleaseEdit', $VNWeb::User::Lists::RLIST_STATUS, { rid => $r->{id}, uid => auth->uid, status => $d };
+ elm_ 'UList.ReleaseEdit', $VNWeb::User::Lists::RLIST_STATUS, { rid => $r->{id}, uid => auth->uid, status => $d, empty => 'not on your list' };
}
};
} if auth;
diff --git a/lib/VNWeb/User/Lists.pm b/lib/VNWeb/User/Lists.pm
index d4a5b800..dbfd0aea 100644
--- a/lib/VNWeb/User/Lists.pm
+++ b/lib/VNWeb/User/Lists.pm
@@ -218,9 +218,11 @@ our $RLIST_STATUS = form_compile any => {
uid => { id => 1 },
rid => { id => 1 },
status => { required => 0, uint => 1, enum => \%RLIST_STATUS }, # undef meaning delete
+ empty => { required => 0, default => '' }, # An 'out' field
};
elm_api UListRStatus => undef, $RLIST_STATUS, sub {
my($data) = @_;
+ delete $data->{empty};
return elm_Unauth if !own $data->{uid};
if(!defined $data->{status}) {
tuwf->dbExeci('DELETE FROM rlists WHERE uid =', \$data->{uid}, 'AND rid =', \$data->{rid})
diff --git a/lib/VNWeb/VN/Page.pm b/lib/VNWeb/VN/Page.pm
index bdd7ff5a..849a97ce 100644
--- a/lib/VNWeb/VN/Page.pm
+++ b/lib/VNWeb/VN/Page.pm
@@ -1,6 +1,7 @@
package VNWeb::VN::Page;
use VNWeb::Prelude;
+use VNWeb::Releases::Lib 'release_extlinks_';
use VNDB::Func 'fmtrating';
use POSIX 'strftime';
@@ -410,6 +411,87 @@ sub tabs_ {
}
+sub releases_ {
+ my($v) = @_;
+
+ # TODO: Organize a long list of releases a bit better somehow? Collapsable language sections?
+
+ enrich_merge id => '
+ SELECT id, title, original, notes, minage, freeware, doujin, resolution, voiced, ani_story, ani_ero, uncensored
+ FROM releases WHERE id IN', $v->{releases};
+ enrich_merge id => sql('SELECT rid as id, status as rlist_status FROM rlists WHERE uid =', \auth->uid, 'AND rid IN'), $v->{releases} if auth;
+ enrich_flatten lang => id => id => 'SELECT id, lang FROM releases_lang WHERE id IN', $v->{releases};
+ enrich_flatten platforms => id => id => 'SELECT id, platform FROM releases_platforms WHERE id IN', $v->{releases};
+ enrich media => id => id => 'SELECT id, medium, qty FROM releases_media WHERE id IN', $v->{releases};
+
+ $v->{releases} = [ sort { $a->{released} <=> $b->{released} || $a->{id} <=> $b->{id} } $v->{releases}->@* ];
+ my %lang;
+ my @lang = grep !$lang{$_}++, map $_->{lang}->@*, $v->{releases}->@*;
+
+ my sub icon_ {
+ my($img, $label, $class) = @_;
+ $class = $class ? " release_icon_$class" : '';
+ img_ src => config->{url_static}."/f/$img.svg", class => "release_icons$class", title => $label;
+ }
+
+ my sub icons_ {
+ my($r) = @_;
+ icon_ 'voiced', $VOICED{$r->{voiced}}{txt}, "voiced$r->{voiced}" if $r->{voiced};
+ icon_ 'story_animated', "Story: $ANIMATED{$r->{ani_story}}{txt}", "anim$r->{ani_story}" if $r->{ani_story};
+ icon_ 'ero_animated', "Ero: $ANIMATED{$r->{ani_ero}}{txt}", "anim$r->{ani_ero}" if $r->{ani_ero};
+ icon_ 'free', 'Freeware' if $r->{freeware};
+ icon_ 'nonfree', 'Non-free' if !$r->{freeware};
+ icon_ 'doujin', 'Doujin' if !$r->{patch} && $r->{doujin};
+ icon_ 'commercial', 'Commercial' if !$r->{patch} && !$r->{doujin};
+ if($r->{resolution} ne 'unknown') {
+ my $type = $r->{resolution} eq 'nonstandard' ? 'custom' : $RESOLUTION{$r->{resolution}}{cat} eq 'widescreen' ? '16-9' : '4-3';
+ # Ugly workaround: PC-98 has non-square pixels, thus not widescreen
+ $type = '4-3' if $type eq '16-9' && grep $_ eq 'p98', $r->{platforms}->@*;
+ icon_ "resolution_$type", $RESOLUTION{$r->{resolution}}{txt};
+ }
+ icon_ $MEDIUM{ $r->{media}[0]{medium} }{icon}, join ', ', map fmtmedia($_->{medium}, $_->{qty}), $r->{media}->@* if $r->{media}->@*;
+ icon_ 'uncensor', 'Uncensored' if $r->{uncensored};
+ icon_ 'notes', bb2text $r->{notes} if $r->{notes};
+ }
+
+ my sub lang_ {
+ my($lang) = @_;
+ tr_ class => 'lang', sub {
+ td_ colspan => 7, sub {
+ abbr_ class => "icons lang $lang", title => $LANGUAGE{$lang}, '';
+ txt_ $LANGUAGE{$lang};
+ }
+ };
+ tr_ sub {
+ td_ class => 'tc1', sub { rdate_ $_->{released} };
+ td_ class => 'tc2', $_->{minage} < 0 ? '' : minage $_->{minage};
+ td_ class => 'tc3', sub {
+ abbr_ class => "icons $_", title => $PLATFORM{$_}, '' for grep $_ ne 'oth', $_->{platforms}->@*;
+ abbr_ class => "icons rt$_->{type}", title => $_->{type}, '';
+ };
+ td_ class => 'tc4', sub {
+ a_ href => "/r$_->{id}", title => $_->{original}||$_->{title}, $_->{title};
+ b_ class => 'grayedout', ' (patch)' if $_->{patch};
+ };
+ td_ class => 'tc_icons', sub { icons_ $_ };
+ td_ class => 'tc5 elm_dd_left', sub {
+ elm_ 'UList.ReleaseEdit', $VNWeb::User::Lists::RLIST_STATUS, { rid => $_->{id}, uid => auth->uid, status => $_->{rlist_status}, empty => '--' };
+ };
+ td_ class => 'tc6', sub { release_extlinks_ $_, "$lang$_->{id}" };
+ } for grep grep($_ eq $lang, $_->{lang}->@*), $v->{releases}->@*;
+ }
+
+ div_ class => 'mainbox releases', sub {
+ h1_ 'Releases';
+ if(!$v->{releases}->@*) {
+ p_ 'We don\'t have any information about releases of this visual novel yet...';
+ } else {
+ table_ sub { lang_ $_ for @lang };
+ }
+ }
+}
+
+
sub stats_ {
my($v) = @_;
@@ -548,7 +630,7 @@ TUWF::get qr{/$RE{vrev}}, sub {
rev_ $v if tuwf->capture('rev');
infobox_ $v;
tabs_ $v, 0;
- # TODO: Releases
+ releases_ $v;
# TODO: Staff
# TODO: Character summary
stats_ $v;