diff options
-rw-r--r-- | data/style.css | 20 | ||||
-rw-r--r-- | elm/ReleaseExtLinks.elm | 29 | ||||
-rw-r--r-- | elm/UList/Opt.elm | 4 | ||||
-rw-r--r-- | elm/UList/ReleaseEdit.elm | 6 | ||||
-rw-r--r-- | lib/VNDB/ExtLinks.pm | 8 | ||||
-rw-r--r-- | lib/VNDB/Types.pm | 20 | ||||
-rw-r--r-- | lib/VNWeb/Releases/Lib.pm | 24 | ||||
-rw-r--r-- | lib/VNWeb/Releases/Page.pm | 2 | ||||
-rw-r--r-- | lib/VNWeb/User/Lists.pm | 2 | ||||
-rw-r--r-- | lib/VNWeb/VN/Page.pm | 84 |
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; |