From 02cef019d30233603dd71cc837f382923cfd699a Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sat, 4 Jan 2020 15:06:22 +0100 Subject: ulist: Add ability to save views as default --- data/style.css | 9 ++-- elm/UList/ManageLabels.js | 1 + elm/UList/SaveDefault.elm | 76 +++++++++++++++++++++++++++ elm/UList/SaveDefault.js | 7 +++ lib/VNWeb/User/Lists.pm | 57 +++++++++++++++----- util/sql/perms.sql | 6 +-- util/sql/schema.sql | 5 +- util/updates/2020-01-04-ulist-saved-views.sql | 4 ++ 8 files changed, 146 insertions(+), 19 deletions(-) create mode 100644 elm/UList/SaveDefault.elm create mode 100644 elm/UList/SaveDefault.js create mode 100644 util/updates/2020-01-04-ulist-saved-views.sql diff --git a/data/style.css b/data/style.css index 108eccb1..b112ccb1 100644 --- a/data/style.css +++ b/data/style.css @@ -82,8 +82,8 @@ div.warning h2, div.notice h2 { font-size: 13px; font-weight: bold; margin: 0; } #ds_box table { width: 100%; } /* Elm dropdowns */ -.elm_dd > a { color: $maintext$; display: block; border: none!important; padding-right: 10px; position: relative } -.elm_dd > a > span { position: absolute; right: 0; top: 0; width: 16px; text-align: right; display: block } +.elm_dd > a { color: $maintext$; display: block; border: none!important; padding-right: 15px; position: relative } +.elm_dd > a > span { position: absolute; right: 5px; top: 0; width: 16px; text-align: right; display: block } .elm_dd > a > span i { visibility: hidden; font-style: normal } .elm_dd > a:hover > span > i, .elm_dd > a:focus > span > i { visibility: visible } @@ -92,9 +92,10 @@ div.warning h2, div.notice h2 { font-size: 13px; font-weight: bold; margin: 0; } .elm_dd.search > div { float: left } .elm_dd.search > div > ul { right: auto; left: 0; top: 23px } .elm_dd > div > ul li { white-space: nowrap } -.elm_dd > div > ul li a { display: block; padding-left: 10px; border: 0; padding: 3px 5px 3px 3px } +.elm_dd > div > ul li a { display: block; border: 0; padding: 3px 5px 3px 3px } .elm_dd > div > ul li a.active, .elm_dd > div > ul li a:hover { background: $boxbg$ } +.elm_dd > div > ul li p { white-space: normal; padding: 3px 5px 3px 3px } @@ -822,6 +823,8 @@ div.votelist td.tc2 { width: 50px; text-align: right; padding-right: 10px } .managelabels select { width: 100% } .managelabels tfoot div { float: right; text-align: right } +.savedefault { width: 600px; margin: 10px auto } + .ulist .tc1 { white-space: nowrap; width: 70px } .ulist .tc1 label { cursor: pointer } .ulist .tc1 input { display: none } diff --git a/elm/UList/ManageLabels.js b/elm/UList/ManageLabels.js index 41ea1c12..6f762bd8 100644 --- a/elm/UList/ManageLabels.js +++ b/elm/UList/ManageLabels.js @@ -1,6 +1,7 @@ document.querySelectorAll('#managelabels').forEach(function(b) { b.onclick = function() { document.querySelectorAll('.managelabels').forEach(function(e) { e.classList.toggle('hidden') }) + document.querySelectorAll('.savedefault').forEach(function(e) { e.classList.add('hidden') }) }; return false; }); diff --git a/elm/UList/SaveDefault.elm b/elm/UList/SaveDefault.elm new file mode 100644 index 00000000..42fdef84 --- /dev/null +++ b/elm/UList/SaveDefault.elm @@ -0,0 +1,76 @@ +module UList.SaveDefault exposing (main) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Browser +import Lib.Html exposing (..) +import Lib.Util exposing (..) +import Lib.Api as Api +import Gen.Api as GApi +import Gen.UListSaveDefault as GUSD + + +main : Program GUSD.Recv Model Msg +main = Browser.element + { init = \e -> (init e, Cmd.none) + , view = view + , update = update + , subscriptions = always Sub.none + } + +type alias Model = + { state : Api.State + , uid : Int + , opts : GUSD.SendOpts + , field : String -- Ewwww stringly typed enum + , hid : Bool + } + +init : GUSD.Recv -> Model +init d = + { state = Api.Normal + , uid = d.uid + , opts = d.opts + , field = "vnlist" + , hid = True + } + +type Msg + = SetField String + | Submit + | Submitted GApi.Response + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + SetField s -> ({ model | field = s, hid = False }, Cmd.none) + + Submit -> + ( { model | state = Api.Loading, hid = False } + , Api.post "/u/ulist/savedefault.json" (GUSD.encode { uid = model.uid, opts = model.opts, field = model.field }) Submitted) + Submitted GApi.Success -> ({ model | state = Api.Normal, hid = True }, Cmd.none) + Submitted r -> ({ model | state = Api.Error r }, Cmd.none) + + +view : Model -> Html Msg +view model = + form_ Submit (model.state == Api.Loading) + [ div [ classList [("savedefault", True), ("hidden", model.hid)] ] + [ b [] [ text "Save as default" ] + , br [] [] + , text "This will change the default label selection, visible columns and table sorting options for the selected page to the currently applied settings." + , text " The saved view will also apply to users visiting your lists." + , br [] [] + , text "(If you just changed the label filters, make sure to hit \"Update filters\" before saving)" + , br [] [] + , label [] [ inputRadio "savedefault_page" (model.field == "votes") (always (SetField "votes") ), text " My Votes" ] + , br [] [] + , label [] [ inputRadio "savedefault_page" (model.field == "vnlist") (always (SetField "vnlist")), text " My Visual Novel List" ] + , br [] [] + , label [] [ inputRadio "savedefault_page" (model.field == "wish") (always (SetField "wish") ), text " My Wishlist" ] + , br [] [] + , submitButton "Save" model.state True + ] + ] diff --git a/elm/UList/SaveDefault.js b/elm/UList/SaveDefault.js new file mode 100644 index 00000000..a253680f --- /dev/null +++ b/elm/UList/SaveDefault.js @@ -0,0 +1,7 @@ +document.querySelectorAll('#savedefault').forEach(function(b) { + b.onclick = function() { + document.querySelectorAll('.savedefault').forEach(function(e) { e.classList.toggle('hidden') }) + document.querySelectorAll('.managelabels').forEach(function(e) { e.classList.add('hidden') }) + }; + return false; +}); diff --git a/lib/VNWeb/User/Lists.pm b/lib/VNWeb/User/Lists.pm index bc806c2c..9620eb80 100644 --- a/lib/VNWeb/User/Lists.pm +++ b/lib/VNWeb/User/Lists.pm @@ -236,7 +236,7 @@ my $VNADD = form_compile any => { elm_form 'UListAdd', undef, $VNADD; -json_api qr{/u/ulist/add\.json}, $VNDEL, sub { +json_api qr{/u/ulist/add\.json}, $VNADD, sub { my($data) = @_; return elm_Unauth if !own $data->{uid}; tuwf->dbExeci('INSERT INTO ulist_vns', $data, 'ON CONFLICT (uid, vid) DO NOTHING'); @@ -270,24 +270,55 @@ json_api qr{/u/ulist/rstatus\.json}, $RSTATUS, sub { + +my %SAVED_OPTS = ( + # Labels + l => { onerror => [], type => 'array', scalar => 1, values => { int => 1 } }, + mul => { anybool => 1 }, + # Sort column & order + s => { onerror => 'title', enum => [qw[ title label vote voted added modified started finished rel rating ]] }, + o => { onerror => 'a', enum => ['a', 'd'] }, + # Visible columns + c => { onerror => [], type => 'array', scalar => 1, values => { enum => [qw[ label vote voted added modified started finished rel rating ]] } }, +); + +my $SAVED_OPTS = { + uid => { id => 1 }, + opts => { type => 'hash', keys => \%SAVED_OPTS }, + field => { _when => 'in', enum => [qw/ vnlist votes wish /] }, +}; + +my $SAVED_OPTS_IN = form_compile in => $SAVED_OPTS; +my $SAVED_OPTS_OUT = form_compile out => $SAVED_OPTS; + +elm_form UListSaveDefault => $SAVED_OPTS_OUT, $SAVED_OPTS_IN; + +json_api qr{/u/ulist/savedefault\.json}, $SAVED_OPTS_IN, sub { + my($data) = @_; + return elm_Unauth if !own $data->{uid}; + tuwf->dbExeci('UPDATE users SET ulist_'.$data->{field}, '=', \JSON::XS->new->encode($data->{opts}), 'WHERE id =', \$data->{uid}); + elm_Success +}; + + + + sub opt { - my($filtlabels) = @_; + my($u, $filtlabels) = @_; + + my sub load { my $o = $u->{"ulist_$_[0]"}; ($o && eval { JSON::XS->new->decode($o) } or {})->%* }; my $opt = # Presets - tuwf->reqGet('vnlist') ? { mul => 0, p => 1, l => [1,2,3,4,7,-1,0], s => 'title', o => 'a', c => [qw/label vote added started finished/] } : - tuwf->reqGet('votes') ? { mul => 0, p => 1, l => [7], s => 'voted', o => 'd', c => [qw/vote voted/] } : - tuwf->reqGet('wishlist') ? { mul => 0, p => 1, l => [5], s => 'title', o => 'a', c => [qw/label added/] } : + tuwf->reqGet('vnlist') ? { mul => 0, p => 1, l => [1,2,3,4,7,-1,0], s => 'title', o => 'a', c => [qw/label vote added started finished/], load 'vnlist' } : + tuwf->reqGet('votes') ? { mul => 0, p => 1, l => [7], s => 'voted', o => 'd', c => [qw/vote voted/], load 'votes' } : + tuwf->reqGet('wishlist') ? { mul => 0, p => 1, l => [5], s => 'title', o => 'a', c => [qw/label added/], load 'wishlist' } : # Full options tuwf->validate(get => p => { upage => 1 }, - l => { onerror => [], type => 'array', scalar => 1, values => { int => 1 } }, - s => { onerror => 'title', enum => [qw[ title label vote voted added modified started finished rel rating ]] }, - o => { onerror => 'a', enum => ['a', 'd'] }, - c => { onerror => [], type => 'array', scalar => 1, values => { enum => [qw[ label vote voted added modified started finished rel rating ]] } }, ch=> { onerror => undef, enum => [ 'a'..'z', 0 ] }, q => { required => 0 }, - mul => { anybool => 1 }, + %SAVED_OPTS )->data; # $labels only includes labels we are allowed to see, getting rid of any labels in 'l' that aren't in $labels ensures we only filter on visible labels @@ -343,6 +374,7 @@ sub filters_ { br_; input_ type => 'submit', class => 'submit', tabindex => 10, value => 'Update filters'; input_ type => 'button', class => 'submit', tabindex => 10, id => 'managelabels', value => 'Manage labels' if $own; + input_ type => 'button', class => 'submit', tabindex => 10, id => 'savedefault', value => 'Save as default' if $own; }; }; } @@ -524,7 +556,7 @@ sub listing_ { # TODO: Ability to add VNs from this page TUWF::get qr{/$RE{uid}/ulist}, sub { - my $u = tuwf->dbRowi('SELECT id,', sql_user(), 'FROM users u WHERE id =', \tuwf->capture('id')); + my $u = tuwf->dbRowi('SELECT id,', sql_user(), ', ulist_votes, ulist_vnlist, ulist_wish FROM users u WHERE id =', \tuwf->capture('id')); return tuwf->resNotFound if !$u->{id}; my $own = own $u->{id}; @@ -551,7 +583,7 @@ TUWF::get qr{/$RE{uid}/ulist}, sub { } : (), ]; - my($opt, $opt_labels) = opt $filtlabels; + my($opt, $opt_labels) = opt $u, $filtlabels; my sub url { '?'.query_encode %$opt, @_ } # This page has 3 user tabs: list, wish and votes; Select the appropriate active tab based on label filters. @@ -577,6 +609,7 @@ TUWF::get qr{/$RE{uid}/ulist}, sub { } else { filters_ $own, $filtlabels, $opt, $opt_labels, \&url; elm_ 'UList.ManageLabels' if $own; + elm_ 'UList.SaveDefault', $SAVED_OPTS_OUT, { uid => $u->{id}, opts => $opt } if $own; } }; listing_ $u->{id}, $own, $opt, $labels, \&url if !$empty; diff --git a/util/sql/perms.sql b/util/sql/perms.sql index e649526f..b4833a60 100644 --- a/util/sql/perms.sql +++ b/util/sql/perms.sql @@ -67,9 +67,9 @@ GRANT SELECT, INSERT, UPDATE, DELETE ON ulist_vns_labels TO vndb_site; -- users table is special; The 'perm', 'passwd' and 'mail' columns are -- protected and can only be accessed through the user_* functions. -GRANT SELECT (id, username, registered, perm, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish), - INSERT (id, username, mail, registered, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish), - UPDATE ( username, registered, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish) ON users TO vndb_site; +GRANT SELECT (id, username, registered, perm, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish, ulist_votes, ulist_vnlist, ulist_wish), + INSERT (id, username, mail, registered, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish, ulist_votes, ulist_vnlist, ulist_wish), + UPDATE ( username, registered, c_votes, c_changes, ip, c_tags, ign_votes, email_confirmed, skin, customcss, filter_vn, filter_release, show_nsfw, hide_list, notify_dbedit, notify_announce, vn_list_own, vn_list_wish, tags_all, tags_cont, tags_ero, tags_tech, spoilers, traits_sexual, nodistract_can, nodistract_noads, nodistract_nofancy, support_can, support_enabled, uniname_can, uniname, pubskin_can, pubskin_enabled, c_vns, c_wish, ulist_votes, ulist_vnlist, ulist_wish) ON users TO vndb_site; GRANT SELECT, INSERT, UPDATE ON vn TO vndb_site; GRANT SELECT, INSERT, DELETE ON vn_anime TO vndb_site; diff --git a/util/sql/schema.sql b/util/sql/schema.sql index 3bc27c77..f2cc611c 100644 --- a/util/sql/schema.sql +++ b/util/sql/schema.sql @@ -782,7 +782,10 @@ CREATE TABLE users ( pubskin_can boolean NOT NULL DEFAULT FALSE, pubskin_enabled boolean NOT NULL DEFAULT FALSE, c_vns integer NOT NULL DEFAULT 0, - c_wish integer NOT NULL DEFAULT 0 + c_wish integer NOT NULL DEFAULT 0, + ulist_votes jsonb, + ulist_vnlist jsonb, + ulist_wish jsonb ); -- vn diff --git a/util/updates/2020-01-04-ulist-saved-views.sql b/util/updates/2020-01-04-ulist-saved-views.sql new file mode 100644 index 00000000..14926ba6 --- /dev/null +++ b/util/updates/2020-01-04-ulist-saved-views.sql @@ -0,0 +1,4 @@ +ALTER TABLE users ADD COLUMN ulist_votes jsonb; +ALTER TABLE users ADD COLUMN ulist_vnlist jsonb; +ALTER TABLE users ADD COLUMN ulist_wish jsonb; +\i util/sql/perms.sql -- cgit v1.2.3