summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2020-04-13 12:31:16 +0200
committerYorhel <git@yorhel.nl>2020-04-13 12:31:16 +0200
commit162fe804937ccc5c45306043dc2da566d2782048 (patch)
tree7758e33b1a60f5a889e33de1a182f3aade724e42
parent3d44cf759f053aba7d36b056c96d33be375f8f7f (diff)
UList.VNPage: Allow setting notes from the VN page
-rw-r--r--data/style.css3
-rw-r--r--elm/UList/VNPage.elm59
-rw-r--r--lib/VNDB/Handler/VNPage.pm3
-rw-r--r--lib/VNWeb/User/Lists.pm3
-rw-r--r--lib/VNWeb/VN/Page.pm3
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 ],
};