diff options
-rw-r--r-- | data/style.css | 3 | ||||
-rw-r--r-- | elm/UList/VNPage.elm | 59 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 3 | ||||
-rw-r--r-- | lib/VNWeb/User/Lists.pm | 3 | ||||
-rw-r--r-- | lib/VNWeb/VN/Page.pm | 3 |
5 files changed, 63 insertions, 8 deletions
diff --git a/data/style.css b/data/style.css index f01137bc..b668228c 100644 --- a/data/style.css +++ b/data/style.css @@ -219,7 +219,7 @@ fieldset.submit .textpreview { margin: 0 auto } /* .compact input elements are smaller and can be embedded in tables/inline text * .stealth input elements pretend to be just regular text, but turn into visibile input elements on mouse-over */ -.compact input.text, .compact select { margin: -2px -1px; padding: 1px 0 } +.compact input.text, .compact select, .compact textarea { margin: -2px -1px; padding: 1px 0 } .compact input.submit { margin: -2px -1px; padding: 1px 3px } .stealth input, .stealth select { font: inherit; background: none; border: 1px solid transparent; -moz-appearance: none; -webkit-appearance: none; appearance: none } .stealth input:hover, .stealth input:focus, @@ -440,6 +440,7 @@ div.vndetails td.anime b { font-size: 10px; font-weight: normal; padding-ri .ulistvn { padding: 5px 0 0 0 } .ulistvn > b { font-size: 14px } .ulistvn > span { float: right } +.ulistvn textarea { width: 100% } div#vntags { margin: 0 30px 0 30px; border-top: 1px solid $border$; padding: 3px 5% 0 5%; text-align: center; } #vntags span { white-space: nowrap; margin-left: 15px; } diff --git a/elm/UList/VNPage.elm b/elm/UList/VNPage.elm index 8e827224..64c5f99a 100644 --- a/elm/UList/VNPage.elm +++ b/elm/UList/VNPage.elm @@ -4,12 +4,16 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Browser +import Browser.Dom exposing (focus) +import Task +import Process import Set import Lib.Html exposing (..) import Lib.Util exposing (..) import Lib.Api as Api import Lib.DropDown as DD import Gen.Api as GApi +import Gen.UListVNNotes as GVN import Gen.UListDel as GDE import UList.LabelEdit as LE import UList.VoteEdit as VE @@ -29,6 +33,7 @@ type alias Recv = , vote : Maybe String , labels : List RecvLabels , selected : List Int + , notes : String } @@ -47,6 +52,10 @@ type alias Model = , state : Api.State -- For adding/deleting; Vote and label edit widgets have their own state , labels : LE.Model , vote : VE.Model + , notes : String + , notesRev : Int + , notesState : Api.State + , notesVis : Bool } init : Recv -> Model @@ -57,25 +66,55 @@ init f = , state = Api.Normal , labels = LE.init { uid = f.uid, vid = f.vid, labels = f.labels, selected = f.selected } , vote = VE.init { uid = f.uid, vid = f.vid, vote = f.vote } + , notes = f.notes + , notesRev = 0 + , notesState = Api.Normal + , notesVis = f.notes /= "" } type Msg - = Labels LE.Msg + = Noop + | Labels LE.Msg | Vote VE.Msg + | NotesToggle + | Notes String + | NotesSave Int + | NotesSaved Int GApi.Response | Del Bool | Delete | Deleted GApi.Response setOnList : Model -> Model -setOnList model = { model | onlist = model.onlist || model.vote.ovote /= Nothing || not (Set.isEmpty model.labels.sel) } +setOnList model = { model | onlist = model.onlist || model.vote.ovote /= Nothing || not (Set.isEmpty model.labels.sel) || model.notes /= "" } update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of + Noop -> (model, Cmd.none) Labels m -> let (nm, cmd) = LE.update m model.labels in (setOnList { model | labels = nm}, Cmd.map Labels cmd) Vote m -> let (nm, cmd) = VE.update m model.vote in (setOnList { model | vote = nm}, Cmd.map Vote cmd) + NotesToggle -> + ( { model | notesVis = not model.notesVis } + , if model.notesVis then Cmd.none else Task.attempt (always Noop) (focus "uvn_notes")) + Notes s -> + if s == model.notes then (model, Cmd.none) + else ( { model | notes = s, notesRev = model.notesRev + 1 } + , Task.perform (\_ -> NotesSave (model.notesRev+1)) <| Process.sleep 1000) + NotesSave rev -> + if rev /= model.notesRev || model.notes == model.flags.notes + then (model, Cmd.none) + else ( { model | notesState = Api.Loading } + , GVN.send { uid = model.flags.uid, vid = model.flags.vid, notes = model.notes } (NotesSaved rev)) + NotesSaved rev GApi.Success -> + let f = model.flags + nf = { f | notes = model.notes } + in if model.notesRev /= rev + then (model, Cmd.none) + else (setOnList {model | flags = nf, notesState = Api.Normal }, Cmd.none) + NotesSaved _ e -> ({ model | notesState = Api.Error e }, Cmd.none) + Del b -> ({ model | del = b }, Cmd.none) Delete -> ({ model | state = Api.Loading }, GDE.send { uid = model.flags.uid, vid = model.flags.vid } Deleted) Deleted GApi.Success -> @@ -83,6 +122,7 @@ update msg model = | state = Api.Normal, onlist = False, del = False , labels = LE.init { uid = model.flags.uid, vid = model.flags.vid, labels = model.flags.labels, selected = [] } , vote = VE.init { uid = model.flags.uid, vid = model.flags.vid, vote = Nothing } + , notes = "", notesVis = False } , Cmd.none) Deleted e -> ({ model | state = Api.Error e }, Cmd.none) @@ -122,7 +162,20 @@ view model = then tr [ class "nostripe compact" ] [ td [] [ text "My vote" ] , td [ style "width" "80px" ] [ Html.map Vote (VE.view model.vote "- vote -") ] - , td [] [] + , td [] + [ a [ href "#", onClickD NotesToggle ] [ text "💬" ] + , span [ class "spinner", classList [("hidden", model.notesState /= Api.Loading)] ] [] + , case model.notesState of + Api.Error e -> b [ class "standout" ] [ text <| Api.showResponse e ] + _ -> text "" + ] + ] + else text "" + , if model.notesVis + then tr [ class "nostripe compact" ] + [ td [] [ text "Notes" ] + , td [ colspan 2 ] + [ textarea ([ id "uvn_notes", placeholder "Notes", rows 2, cols 30, onInput Notes, onBlur (NotesSave model.notesRev)] ++ GVN.valNotes) [ text model.notes ] ] ] else text "" ] diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm index 3170fd30..30ddb43c 100644 --- a/lib/VNDB/Handler/VNPage.pm +++ b/lib/VNDB/Handler/VNPage.pm @@ -700,7 +700,7 @@ sub _useroptions { WHERE l.uid =', \$self->authInfo->{id}, ' ORDER BY CASE WHEN l.id < 10 THEN l.id ELSE 10 END, l.label' ); - my $lst = tuwf->dbRowi('SELECT vid, vote FROM ulist_vns WHERE uid =', \$self->authInfo->{id}, 'AND vid =', \$v->{id}); + my $lst = tuwf->dbRowi('SELECT vid, vote, notes FROM ulist_vns WHERE uid =', \$self->authInfo->{id}, 'AND vid =', \$v->{id}); Tr class => 'nostripe'; td colspan => 2; @@ -710,6 +710,7 @@ sub _useroptions { onlist => $lst->{vid}?\1:\0, canvote => $minreleased && $minreleased < strftime('%Y%m%d', gmtime) ? \1 : \0, vote => fmtvote($lst->{vote}).'', + notes => $lst->{notes}||'', labels => [ map +{ id => 1*$_->{id}, label => $_->{label}, private => $_->{private}?\1:\0 }, @$labels ], selected => [ map $_->{id}, grep $_->{assigned}, @$labels ], }); diff --git a/lib/VNWeb/User/Lists.pm b/lib/VNWeb/User/Lists.pm index 1d285618..3b9c0121 100644 --- a/lib/VNWeb/User/Lists.pm +++ b/lib/VNWeb/User/Lists.pm @@ -189,8 +189,7 @@ elm_api UListVNNotes => $VNOPT, { my($data) = @_; return elm_Unauth if !own $data->{uid}; tuwf->dbExeci( - 'UPDATE ulist_vns SET lastmod = NOW(), notes = ', \$data->{notes}, - 'WHERE uid =', \$data->{uid}, 'AND vid =', \$data->{vid} + 'INSERT INTO ulist_vns', \%$data, 'ON CONFLICT (uid, vid) DO UPDATE SET', { %$data, lastmod => sql('NOW()') } ); # Doesn't need `updcache()` elm_Success diff --git a/lib/VNWeb/VN/Page.pm b/lib/VNWeb/VN/Page.pm index 64603f00..9933ab3d 100644 --- a/lib/VNWeb/VN/Page.pm +++ b/lib/VNWeb/VN/Page.pm @@ -259,7 +259,7 @@ sub infobox_useroptions_ { WHERE l.uid =', \auth->uid, ' ORDER BY CASE WHEN l.id < 10 THEN l.id ELSE 10 END, l.label' ); - my $lst = tuwf->dbRowi('SELECT vid, vote FROM ulist_vns WHERE uid =', \auth->uid, 'AND vid =', \$v->{id}); + my $lst = tuwf->dbRowi('SELECT vid, vote, notes FROM ulist_vns WHERE uid =', \auth->uid, 'AND vid =', \$v->{id}); tr_ class => 'nostripe', sub { td_ colspan => 2, sub { @@ -269,6 +269,7 @@ sub infobox_useroptions_ { onlist => $lst->{vid}?\1:\0, canvote => $minreleased && $minreleased < strftime('%Y%m%d', gmtime) ? \1 : \0, vote => fmtvote($lst->{vote}).'', + notes => $lst->{notes}||'', labels => [ map +{ id => 1*$_->{id}, label => $_->{label}, private => $_->{private}?\1:\0 }, @$labels ], selected => [ map $_->{id}, grep $_->{assigned}, @$labels ], }; |