diff options
author | Yorhel <git@yorhel.nl> | 2019-11-04 11:52:45 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2019-11-10 12:44:55 +0100 |
commit | de6df4ab815a4f4447122d7f19888afd1b3e50ec (patch) | |
tree | 2eb3ce6ba9ebaf0d6c5fa588b7d2e4c4f87bc684 | |
parent | 6821b9ab3f3b24e4d635a0763122d0ed2b1c0b72 (diff) |
ulist: Add (nonfunctional) releases + options UI; More consistent naming
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | data/style.css | 12 | ||||
-rw-r--r-- | elm/Lib/Html.elm | 10 | ||||
-rw-r--r-- | elm/Lib/Util.elm | 5 | ||||
-rw-r--r-- | elm/UList/DateEdit.elm (renamed from elm/ULists/DateEdit.elm) | 2 | ||||
-rw-r--r-- | elm/UList/LabelEdit.elm (renamed from elm/ULists/LabelEdit.elm) | 6 | ||||
-rw-r--r-- | elm/UList/LabelEdit.js (renamed from elm/ULists/LabelEdit.js) | 6 | ||||
-rw-r--r-- | elm/UList/ManageLabels.elm (renamed from elm/ULists/ManageLabels.elm) | 2 | ||||
-rw-r--r-- | elm/UList/ManageLabels.js (renamed from elm/ULists/ManageLabels.js) | 4 | ||||
-rw-r--r-- | elm/UList/Opt.elm | 79 | ||||
-rw-r--r-- | elm/UList/VoteEdit.elm (renamed from elm/ULists/VoteEdit.elm) | 6 | ||||
-rw-r--r-- | elm/UList/VoteEdit.js (renamed from elm/ULists/VoteEdit.js) | 6 | ||||
-rw-r--r-- | elm/checkall.js | 10 | ||||
-rw-r--r-- | elm/checkhidden.js | 4 | ||||
-rw-r--r-- | lib/VNWeb/Elm.pm | 2 | ||||
-rw-r--r-- | lib/VNWeb/User/Lists.pm | 48 |
16 files changed, 167 insertions, 41 deletions
@@ -120,9 +120,9 @@ static/f/vndb.min.js: static/f/vndb.js JS_FILES=\ elm/polyfills.js \ elm/pagevars.js \ - elm/ULists/ManageLabels.js \ - elm/ULists/LabelEdit.js \ - elm/ULists/VoteEdit.js \ + elm/UList/ManageLabels.js \ + elm/UList/LabelEdit.js \ + elm/UList/VoteEdit.js \ elm/Lib/Ffi.js \ elm/elm-init.js \ elm/checkall.js \ diff --git a/data/style.css b/data/style.css index 092f853b..54d9bec8 100644 --- a/data/style.css +++ b/data/style.css @@ -159,8 +159,8 @@ p.linkradio { padding: 2px } .linkradio input:checked + label:before { content: '✓' } .linkradio em { font-weight: normal; font-style: normal; color: $grayedout$ } -.spinner { content: ''; border: 3px solid #9eaebd; border-bottom-color: transparent; border-radius: 100%; animation: spin 1s infinite linear; width: 14px; height: 14px; display: inline-block; margin: auto } -span.spinner { width: 1ex; height: 1ex } +.spinner { content: ''; box-sizing: border-box; border: 3px solid #9eaebd; border-bottom-color: transparent; border-radius: 100%; animation: spin 1s infinite linear; width: 16px; height: 16px; display: inline-block; margin: auto } +span.spinner { width: 1em; height: 1em } @keyframes spin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } } .textpreview .head { width: 100%; text-align: right } @@ -817,6 +817,14 @@ div.votelist td.tc2 { width: 50px; text-align: right; padding-right: 10px } .labeledit > div > ul li label { display: block; padding-left: 10px; border: 0; padding: 3px 5px 3px 3px } .labeledit > div > ul li label:hover { background: $boxbg$ } +.ulist .tc_opt { padding: 0 0 5px 70px } +.ulist .tc_opt textarea { width: 500px; height: 50px } +.ulist .tc_opt textarea + div { display: inline-block; padding-left: 10px } +.ulist .tc_opt .tco1 { white-space: nowrap; width: 70px } +.ulist .tc_opt .tco2 { white-space: nowrap; width: 70px } +.ulist .tc_opt .tco3 { white-space: nowrap; width: 100px } +.ulist .tc_opt .tco4 { white-space: nowrap; width: 60px; text-align: right; padding-bottom: 0 } + /***** User VN list browser ******/ diff --git a/elm/Lib/Html.elm b/elm/Lib/Html.elm index 770e9142..6e67cefb 100644 --- a/elm/Lib/Html.elm +++ b/elm/Lib/Html.elm @@ -6,6 +6,8 @@ import Html.Events exposing (..) import Json.Decode as JD import List import Lib.Api as Api +import Lib.Util exposing (..) +import Gen.Types as T -- onClick with stopPropagation & preventDefault @@ -165,3 +167,11 @@ formField lbl cont = txt -> genlbl (String.concat txt) , td (class "field" :: if lbl == "none" then [ colspan 2 ] else []) cont ] + + + +langIcon : String -> Html m +langIcon l = abbr [ class "icons lang", class l, title (Maybe.withDefault "" <| lookup l T.languages) ] [ text " " ] + +releaseTypeIcon : String -> Html m +releaseTypeIcon t = abbr [ class ("icons rt"++t), title (Maybe.withDefault "" <| lookup t T.releaseTypes) ] [ text " " ] diff --git a/elm/Lib/Util.elm b/elm/Lib/Util.elm index 186cf365..b86fcbad 100644 --- a/elm/Lib/Util.elm +++ b/elm/Lib/Util.elm @@ -30,3 +30,8 @@ hasDuplicates l = case List.foldr step (Just Dict.empty) l of Nothing -> True Just _ -> False + + +-- Haskell's 'lookup' - find an entry in an association list +lookup : a -> List (a,b) -> Maybe b +lookup n l = List.filter (\(a,_) -> a == n) l |> List.head |> Maybe.map Tuple.second diff --git a/elm/ULists/DateEdit.elm b/elm/UList/DateEdit.elm index 98ccf5d0..dc6b18e8 100644 --- a/elm/ULists/DateEdit.elm +++ b/elm/UList/DateEdit.elm @@ -1,4 +1,4 @@ -module ULists.DateEdit exposing (main) +module UList.DateEdit exposing (main) import Html exposing (..) import Html.Attributes exposing (..) diff --git a/elm/ULists/LabelEdit.elm b/elm/UList/LabelEdit.elm index b6a03de2..ce65e414 100644 --- a/elm/ULists/LabelEdit.elm +++ b/elm/UList/LabelEdit.elm @@ -1,4 +1,4 @@ -port module ULists.LabelEdit exposing (main) +port module UList.LabelEdit exposing (main) import Html exposing (..) import Html.Attributes exposing (..) @@ -24,7 +24,7 @@ main = Browser.element , update = update } -port ulistsLabelChanged : Bool -> Cmd msg +port ulistLabelChanged : Bool -> Cmd msg type alias Model = { uid : Int @@ -79,7 +79,7 @@ update msg model = Saved l b (GApi.Success) -> let nmodel = { model | sel = if b then Set.insert l model.sel else Set.remove l model.sel, state = Dict.remove l model.state } public = List.any (\lb -> lb.id /= 7 && not lb.private && Set.member lb.id nmodel.sel) nmodel.labels - in (nmodel, ulistsLabelChanged public) + in (nmodel, ulistLabelChanged public) Saved l b e -> ({ model | state = Dict.insert l (Api.Error e) model.state }, Cmd.none) diff --git a/elm/ULists/LabelEdit.js b/elm/UList/LabelEdit.js index f1c0c278..6617bf11 100644 --- a/elm/ULists/LabelEdit.js +++ b/elm/UList/LabelEdit.js @@ -1,9 +1,9 @@ -var init = Elm.ULists.LabelEdit.init; -Elm.ULists.LabelEdit.init = function(opt) { +var init = Elm.UList.LabelEdit.init; +Elm.UList.LabelEdit.init = function(opt) { opt.flags.uid = pageVars.uid; opt.flags.labels = pageVars.labels; var app = init(opt); - app.ports.ulistsLabelChanged.subscribe(function(pub) { + app.ports.ulistLabelChanged.subscribe(function(pub) { var l = document.getElementById('ulist_public_'+opt.flags.vid); l.setAttribute('data-publabel', pub?1:''); l.classList.toggle('invisible', !((l.getAttribute('data-voted') && !pageVars.voteprivate) || l.getAttribute('data-publabel'))) diff --git a/elm/ULists/ManageLabels.elm b/elm/UList/ManageLabels.elm index 1e953b0a..755c4e08 100644 --- a/elm/ULists/ManageLabels.elm +++ b/elm/UList/ManageLabels.elm @@ -1,4 +1,4 @@ -module ULists.ManageLabels exposing (main) +module UList.ManageLabels exposing (main) import Html exposing (..) import Html.Attributes exposing (..) diff --git a/elm/ULists/ManageLabels.js b/elm/UList/ManageLabels.js index 1061a1e3..41ea1c12 100644 --- a/elm/ULists/ManageLabels.js +++ b/elm/UList/ManageLabels.js @@ -5,8 +5,8 @@ document.querySelectorAll('#managelabels').forEach(function(b) { return false; }); -var init = Elm.ULists.ManageLabels.init; -Elm.ULists.ManageLabels.init = function(opt) { +var init = Elm.UList.ManageLabels.init; +Elm.UList.ManageLabels.init = function(opt) { opt.flags = { uid: pageVars.uid, labels: pageVars.labels }; init(opt); }; diff --git a/elm/UList/Opt.elm b/elm/UList/Opt.elm new file mode 100644 index 00000000..22350b7c --- /dev/null +++ b/elm/UList/Opt.elm @@ -0,0 +1,79 @@ +module UList.Opt exposing (main) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Task +import Process +import Browser +import Lib.Util exposing (..) +import Lib.Html exposing (..) +import Lib.Api as Api +import Gen.Types as T +import Gen.Api as GApi +import Gen.UListVNOpt as GVO + + +main : Program GVO.Send Model Msg +main = Browser.element + { init = \f -> (init f, Cmd.none) + , subscriptions = always Sub.none + , view = view + , update = update + } + +type alias Model = + { state : Api.State + , flags : GVO.Send + , del : Bool + } + +init : GVO.Send -> Model +init f = + { state = Api.Normal + , flags = f + , del = False + } + +type Msg + = Del Bool + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + Del b -> ({ model | del = b }, Cmd.none) + + +view : Model -> Html Msg +view model = + let + opt = + [ tr [] + [ td [ colspan 5 ] + [ textarea ([ placeholder "Notes", rows 2, cols 100 ] ++ GVO.valNotes) [ text model.flags.notes ] + , div [ ] + [ div [ class "spinner" ] [] + , br_ 2 + , a [ href "#", onClickD (Del True) ] [ text "Remove VN" ] + ] + ] + ] + , tfoot [] + [ tr [] + [ td [ colspan 5 ] [ a [ href "#" ] [ text "Add release" ] ] ] + ] + ] + + rel i r = + tr [] + [ if model.flags.own + then td [ class "tco1" ] [ a [ href "#" ] [ text "remove" ] ] + else text "" + , td [ class "tco2" ] [ text <| Maybe.withDefault "status" <| lookup r.status T.rlistStatus ] + , td [ class "tco3" ] [ text "2018-11-10" ] + , td [ class "tco4" ] <| List.map langIcon r.lang ++ [ releaseTypeIcon r.rtype ] + , td [ class "tco5" ] [ a [ href ("/r"++String.fromInt r.id), title r.original ] [ text r.title ] ] + ] + + in table [] <| (if model.flags.own then opt else []) ++ List.indexedMap rel model.flags.rels diff --git a/elm/ULists/VoteEdit.elm b/elm/UList/VoteEdit.elm index 60814e5d..8007d648 100644 --- a/elm/ULists/VoteEdit.elm +++ b/elm/UList/VoteEdit.elm @@ -1,4 +1,4 @@ -port module ULists.VoteEdit exposing (main) +port module UList.VoteEdit exposing (main) import Html exposing (..) import Html.Attributes exposing (..) @@ -21,7 +21,7 @@ main = Browser.element , update = update } -port ulistsVoteChanged : Bool -> Cmd msg +port ulistVoteChanged : Bool -> Cmd msg type alias Model = { state : Api.State @@ -66,7 +66,7 @@ update msg model = Saved GApi.Success -> let flags = model.flags nflags = { flags | vote = Just model.text } - in ({ model | flags = nflags, state = Api.Normal }, ulistsVoteChanged (model.text /= "" && model.text /= "-")) + in ({ model | flags = nflags, state = Api.Normal }, ulistVoteChanged (model.text /= "" && model.text /= "-")) Saved e -> ({ model | state = Api.Error e }, Cmd.none) diff --git a/elm/ULists/VoteEdit.js b/elm/UList/VoteEdit.js index 4a3e304c..99d5b31b 100644 --- a/elm/ULists/VoteEdit.js +++ b/elm/UList/VoteEdit.js @@ -1,7 +1,7 @@ -var init = Elm.ULists.VoteEdit.init; -Elm.ULists.VoteEdit.init = function(opt) { +var init = Elm.UList.VoteEdit.init; +Elm.UList.VoteEdit.init = function(opt) { var app = init(opt); - app.ports.ulistsVoteChanged.subscribe(function(voted) { + app.ports.ulistVoteChanged.subscribe(function(voted) { var l = document.getElementById('ulist_public_'+opt.flags.vid); l.setAttribute('data-voted', voted?1:''); l.classList.toggle('invisible', !((l.getAttribute('data-voted') && !pageVars.voteprivate) || l.getAttribute('data-publabel'))) diff --git a/elm/checkall.js b/elm/checkall.js index 4343ffc8..5b7dc44e 100644 --- a/elm/checkall.js +++ b/elm/checkall.js @@ -5,12 +5,10 @@ * Checking that will synchronize all other checkboxes with name="$somename". */ document.querySelectorAll('input[type=checkbox].checkall').forEach(function(el) { - el.onclick = function() { + el.addEventListener('click', function() { document.querySelectorAll('input[type=checkbox][name="'+el.name+'"]').forEach(function(el2) { - if(!el2.classList.contains('hidden')) { - if(el2.checked != el.checked) - el2.click(); - } + if(el2.checked != el.checked) + el2.click(); }); - }; + }); }); diff --git a/elm/checkhidden.js b/elm/checkhidden.js index 0f76d646..cfe2f8a2 100644 --- a/elm/checkhidden.js +++ b/elm/checkhidden.js @@ -5,9 +5,11 @@ * Checking that will toggle the 'hidden' class of all elements with the "$somename" class. */ document.querySelectorAll('input[type=checkbox].checkhidden').forEach(function(el) { - el.onclick = function() { + var f = function() { document.querySelectorAll('.'+el.value).forEach(function(el2) { el2.classList.toggle('hidden', !el.checked); }); }; + f(); + el.addEventListener('click', f); }); diff --git a/lib/VNWeb/Elm.pm b/lib/VNWeb/Elm.pm index 1cdfcb88..80487839 100644 --- a/lib/VNWeb/Elm.pm +++ b/lib/VNWeb/Elm.pm @@ -237,6 +237,8 @@ sub write_types { $data .= def languages => 'List (String, String)' => list map tuple(string $_, string $LANGUAGE{$_}), sort { $LANGUAGE{$a} cmp $LANGUAGE{$b} } keys %LANGUAGE; + $data .= def releaseTypes => 'List (String, String)' => list map tuple(string $_, string $RELEASE_TYPE{$_}), keys %RELEASE_TYPE; + $data .= def rlistStatus => 'List (Int, String)' => list map tuple($_, string $RLIST_STATUS{$_}), keys %RLIST_STATUS; write_module Types => $data; } diff --git a/lib/VNWeb/User/Lists.pm b/lib/VNWeb/User/Lists.pm index e745db71..0aa10630 100644 --- a/lib/VNWeb/User/Lists.pm +++ b/lib/VNWeb/User/Lists.pm @@ -13,10 +13,10 @@ my $LABELS = form_compile any => { } } }; - elm_form 'ManageLabels', undef, $LABELS; + my $VNVOTE = form_compile any => { uid => { id => 1 }, vid => { id => 1 }, @@ -26,6 +26,7 @@ my $VNVOTE = form_compile any => { elm_form 'VoteEdit', undef, $VNVOTE; + my $VNLABELS = { uid => { id => 1 }, vid => { id => 1 }, @@ -41,6 +42,7 @@ my $VNLABELS_IN = form_compile in => $VNLABELS; elm_form 'LabelEdit', $VNLABELS_OUT, $VNLABELS_IN; + my $VNDATE = form_compile any => { uid => { id => 1 }, vid => { id => 1 }, @@ -51,7 +53,28 @@ my $VNDATE = form_compile any => { elm_form 'DateEdit', undef, $VNDATE; -# TODO: Filters to find unlabeled VNs or VNs with notes? + +my $VNOPT = form_compile any => { + own => { anybool => 1 }, + uid => { id => 1 }, + vid => { id => 1 }, + notes => { required => 0, default => '', maxlength => 2000 }, + rels => { aoh => { + id => { id => 1 }, + title => {}, + original => {}, + released => { uint => 1 }, + rtype => {}, + status => { uint => 1 }, + lang => { type => 'array', values => {} }, + } }, +}; + +elm_form 'UListVNOpt', undef, $VNOPT; + + + +# TODO: Filters to find unlabeled VNs or VNs with/without notes? sub filters_ { my($own, $labels) = @_; @@ -135,28 +158,30 @@ sub vn_ { my @l = grep $labels{$_->{id}} && $_->{id} != 7, @$labels; my $txt = @l ? join ', ', map $_->{label}, @l : '-'; if($own) { - elm_ 'ULists.LabelEdit' => $VNLABELS_OUT, { vid => $v->{id}, selected => [ grep $_ != 7, $v->{labels}->@* ] }, $txt; + elm_ 'UList.LabelEdit' => $VNLABELS_OUT, { vid => $v->{id}, selected => [ grep $_ != 7, $v->{labels}->@* ] }, $txt; } else { txt_ $txt; } }; td_ mkclass(tc4 => 1, compact => $own, stealth => $own), sub { txt_ fmtvote $v->{vote} if !$own; - elm_ 'ULists.VoteEdit' => $VNVOTE, { uid => $uid, vid => $v->{id}, vote => fmtvote($v->{vote}) }, fmtvote $v->{vote} if $own; + elm_ 'UList.VoteEdit' => $VNVOTE, { uid => $uid, vid => $v->{id}, vote => fmtvote($v->{vote}) }, fmtvote $v->{vote} if $own; }; td_ class => 'tc5', fmtdate $v->{added}, 'compact'; td_ class => 'tc6', sub { txt_ $v->{started}||'' if !$own; - elm_ 'ULists.DateEdit' => $VNDATE, { uid => $uid, vid => $v->{id}, date => $v->{started}||'', start => 1 }, $v->{started}||'' if $own; + elm_ 'UList.DateEdit' => $VNDATE, { uid => $uid, vid => $v->{id}, date => $v->{started}||'', start => 1 }, $v->{started}||'' if $own; }; td_ class => 'tc7', sub { txt_ $v->{finished}||'' if !$own; - elm_ 'ULists.DateEdit' => $VNDATE, { uid => $uid, vid => $v->{id}, date => $v->{finished}||'', start => 0 }, $v->{finished}||'' if $own; + elm_ 'UList.DateEdit' => $VNDATE, { uid => $uid, vid => $v->{id}, date => $v->{finished}||'', start => 0 }, $v->{finished}||'' if $own; }; }; tr_ mkclass(hidden => 1, 'collapsed_vid'.$v->{id} => 1, odd => $n % 2 == 0), sub { - td_ colspan => 7, 'Options, releases and note stuff here (likely Elm)'; + td_ colspan => 7, class => 'tc_opt', sub { + elm_ 'UList.Opt' => $VNOPT, { own => $own, uid => $uid, vid => $v->{id}, notes => $v->{notes}, rels => $v->{rels} }; + }; }; } @@ -191,7 +216,7 @@ sub listing_ { enrich_flatten labels => id => vid => sql('SELECT vid, lbl FROM ulist_vns_labels WHERE uid =', \$uid, 'AND vid IN'), $lst; enrich rels => id => vid => sub { sql ' - SELECT rv.vid, r.id, r.title, r.original, r.released, r.type, rl.status + SELECT rv.vid, r.id, r.title, r.original, r.released, r.type as rtype, rl.status FROM rlists rl JOIN releases r ON rl.rid = r.id JOIN releases_vn rv ON rv.id = r.id @@ -204,9 +229,7 @@ sub listing_ { my sub url { '?'.query_encode %$opt, @_ } - # TODO: In-line editable notes, remove-from-list - # TODO: Releases - # TODO: Thumbnail view + # TODO: Thumbnail view? paginate_ \&url, $opt->{p}, [ $count, 50 ], 't'; div_ class => 'mainbox browse ulist', sub { table_ sub { @@ -229,7 +252,6 @@ sub listing_ { } -# TODO: Keep this URL? Steal /u+/list when that one's gone? # TODO: Display something useful when all labels are private? # TODO: Ability to add VNs from this page TUWF::get qr{/$RE{uid}/ulist}, sub { @@ -257,7 +279,7 @@ TUWF::get qr{/$RE{uid}/ulist}, sub { div_ class => 'mainbox', sub { h1_ $title; $opt = filters_ $own, $labels; - elm_ 'ULists.ManageLabels' if $own; + elm_ 'UList.ManageLabels' if $own; }; listing_ $u->{id}, $own, $opt, $labels; }; |