diff options
author | Yorhel <git@yorhel.nl> | 2022-06-19 12:10:57 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2022-06-19 12:10:59 +0200 |
commit | 43ef1a26d68f2b5dbc8b5ac3cc30e27b7bf89ca3 (patch) | |
tree | 1afcf240ee23a43511bb944ba57f50d26ad76781 | |
parent | 888c7d968f44e86ffe2808cef67ed7e0e6145ad3 (diff) |
Move & streamline VN release expanding settings
Problems with the old implementation were that the setting wasn't an
intentional user action, so there were a LOT of preference writes that
were reverted a minute or so later. Wasting both server and visitor
time. For regular visitors, the localstorage-based approach also caused
a reflow if their preferences didn't match the default.
Downside of the new implementation is that regular visitors can't change
their preference at all anymore, this now requires an account. :/
-rw-r--r-- | elm/Lib/Html.elm | 6 | ||||
-rw-r--r-- | elm/User/Edit.elm | 14 | ||||
-rw-r--r-- | elm/remember-details.js | 39 | ||||
-rw-r--r-- | lib/VNWeb/Misc/SavePref.pm | 24 | ||||
-rw-r--r-- | lib/VNWeb/User/Edit.pm | 13 | ||||
-rw-r--r-- | lib/VNWeb/VN/Page.pm | 14 | ||||
-rw-r--r-- | sql/schema.sql | 7 | ||||
-rw-r--r-- | util/updates/2022-06-19-user-prefs-vnrel.sql | 31 |
8 files changed, 73 insertions, 75 deletions
diff --git a/elm/Lib/Html.elm b/elm/Lib/Html.elm index 08648d55..fdb98823 100644 --- a/elm/Lib/Html.elm +++ b/elm/Lib/Html.elm @@ -29,6 +29,12 @@ onInputValidation msg = custom "input" <| onInvalid : msg -> Attribute msg onInvalid msg = on "invalid" (JD.succeed msg) +onInputMultiple : (List String -> msg) -> Attribute msg +onInputMultiple msg = + let dec = JD.at [ "target", "selectedOptions" ] <| JD.keyValuePairs <| JD.maybe (JD.field "value" JD.string) + f lst = msg (List.filterMap Tuple.second lst) + in on "input" (JD.map f dec) + -- Multi-<br> (ugly but oh, so, convenient) br_ : Int -> Html m br_ n = if n == 1 then br [] [] else span [] <| List.repeat n <| br [] [] diff --git a/elm/User/Edit.elm b/elm/User/Edit.elm index f9b9f38c..447691c6 100644 --- a/elm/User/Edit.elm +++ b/elm/User/Edit.elm @@ -106,6 +106,9 @@ type PrefMsg | TagsCont Bool | TagsEro Bool | TagsTech Bool + | VNRelLangs (List String) + | VNRelOLang Bool + | VNRelMTL Bool | ProdRel Bool | Skin String | Css String @@ -207,6 +210,9 @@ updatePrefs msg model = TagsCont b -> { model | tags_cont = b } TagsEro b -> { model | tags_ero = b } TagsTech b -> { model | tags_tech = b } + VNRelLangs l->{ model | vnrel_langs = l } + VNRelOLang b->{ model | vnrel_olang = b } + VNRelMTL b -> { model | vnrel_mtl = b } ProdRel b -> { model | prodrelexpand = b } Skin n -> { model | skin = n } Css n -> { model | customcss = n } @@ -411,6 +417,14 @@ view model = , label [] [ inputCheck "" m.tags_ero (Prefs << TagsEro ), text " Sexual content" ], br_ 1 , label [] [ inputCheck "" m.tags_tech (Prefs << TagsTech), text " Technical" ] ] + , formField "Releases" + [ text "Expand releases for the following languages by default", br_ 1 + , select [ tabindex 10, multiple True, onInputMultiple (Prefs << VNRelLangs), style "height" "200px" ] + <| List.map (\(k,v) -> option [ value k, selected (List.member k m.vnrel_langs) ] [ text v ]) GT.languages + , br_ 1 + , label [] [ inputCheck "" m.vnrel_olang (Prefs << VNRelOLang), text " Always expand original language" ], br_ 1 + , label [] [ inputCheck "" m.vnrel_mtl (Prefs << VNRelMTL ), text " Expand machine translations" ] + ] , tr [ class "newpart" ] [ td [ colspan 2 ] [ text "Theme" ] ] , formField "skin::Skin" [ inputSelect "skin" m.skin (Prefs << Skin) [ style "width" "300px" ] GT.skins ] , formField "css::Custom CSS" [ inputTextArea "css" m.customcss (Prefs << Css) ([ rows 5, cols 60 ] ++ GUE.valPrefsCustomcss) ] diff --git a/elm/remember-details.js b/elm/remember-details.js deleted file mode 100644 index 9d06bd55..00000000 --- a/elm/remember-details.js +++ /dev/null @@ -1,39 +0,0 @@ -/* Simple script to remember the open/closed state of <details> elements. - * Usage: - * - * <details data-remember-id=".."> .. </details> - * - * This does have the downside of causing a DOM reflow if the elements' default - * state differs from the one stored by the user, and the preference is stored - * in the users' browser rather than their account, so it doesn't transfer. - */ -document.querySelectorAll('details[data-remember-id]').forEach(function(el) { - var sid = 'remember-details-'+el.getAttribute('data-remember-id'); - el.addEventListener('toggle', function() { - window.localStorage.setItem(sid, el.open ? '1' : ''); - }); - var val = window.localStorage.getItem(sid); - if(val != null) - el.open = val == '1' ? true : false; -}); - - -/* Alternative to the above, for users who are logged in. - * Usage: - * - * <details data-save-id=".."> .. </details> - * - * State changes will be saved with an AJAX call to /js/save-pref. - * Preferences are already assumed to be loaded by server-side code, so this - * approach does not cause a DOM reflow. - */ -document.querySelectorAll('details[data-save-id]').forEach(function(el) { - el.addEventListener('toggle', function() { - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/js/save-pref'); - xhr.setRequestHeader('Content-Type', 'application/json'); - var obj = {}; - obj[el.getAttribute('data-save-id')] = el.open; - xhr.send(JSON.stringify(obj)); - }); -}); diff --git a/lib/VNWeb/Misc/SavePref.pm b/lib/VNWeb/Misc/SavePref.pm deleted file mode 100644 index 542cc2a3..00000000 --- a/lib/VNWeb/Misc/SavePref.pm +++ /dev/null @@ -1,24 +0,0 @@ -package VNWeb::Misc::SavePref; - -use VNWeb::Prelude; - -my @vnlang_keys = (map +($_,"$_-mtl"), keys %LANGUAGE); - -TUWF::post qr{/js/save-pref} => sub { - return tuwf->resDenied if !auth; - my $prefs = tuwf->validate(json => {type => 'hash', unknown => 'pass'})->data; - - my %vnlang = map exists($prefs->{"vnlang-$_"}) ? ($_, $prefs->{"vnlang-$_"}) : (), @vnlang_keys; - if(keys %vnlang) { - my $v = tuwf->dbVali('SELECT vnlang FROM users_prefs WHERE id =', \auth->uid); - $v = $v ? JSON::XS::decode_json($v) : {}; - for(keys %vnlang) { - delete $v->{$_} if !defined $vnlang{$_}; - $v->{$_} = $vnlang{$_}?\1:\0 if defined $vnlang{$_}; - } - $v = JSON::XS::encode_json($v); - tuwf->dbExeci('UPDATE users_prefs SET vnlang =', \$v, 'WHERE id =', \auth->uid); - } -}; - -1; diff --git a/lib/VNWeb/User/Edit.pm b/lib/VNWeb/User/Edit.pm index 98a09785..cf544179 100644 --- a/lib/VNWeb/User/Edit.pm +++ b/lib/VNWeb/User/Edit.pm @@ -41,6 +41,9 @@ my $FORM = { tags_tech => { anybool => 1 }, prodrelexpand => { anybool => 1 }, spoilers => { uint => 1, range => [ 0, 2 ] }, + vnrel_langs => { type => 'array', values => { enum => \%LANGUAGE }, sort => 'str', unique => 1 }, + vnrel_olang => { anybool => 1 }, + vnrel_mtl => { anybool => 1 }, skin => { enum => skins }, customcss => { required => 0, default => '', maxlength => 2000 }, @@ -89,13 +92,15 @@ TUWF::get qr{/$RE{uid}/edit}, sub { $u->{prefs} = $u->{id} eq auth->uid || auth->permUsermod ? tuwf->dbRowi( - 'SELECT max_sexual, max_violence, traits_sexual, tags_all, tags_cont, tags_ero, tags_tech, prodrelexpand, spoilers, skin, customcss - , nodistract_noads, nodistract_nofancy, support_enabled, uniname, pubskin_enabled, title_langs, alttitle_langs + 'SELECT max_sexual, max_violence, traits_sexual, tags_all, tags_cont, tags_ero, tags_tech, prodrelexpand + , spoilers, vnrel_langs::text[], vnrel_olang, vnrel_mtl, skin, customcss, title_langs, alttitle_langs + , nodistract_noads, nodistract_nofancy, support_enabled, uniname, pubskin_enabled FROM users u JOIN users_prefs up ON up.id = u.id WHERE u.id =', \$u->{id} ) : undef; if($u->{prefs}) { $u->{prefs}{email} = _getmail $u->{id}; $u->{prefs}{skin} ||= config->{skin_default}; + $u->{prefs}{vnrel_langs} ||= [ keys %LANGUAGE ]; $u->{prefs}{title_langs} = langpref_parse($u->{prefs}{title_langs}) // $DEFAULT_TITLE_LANGS; $u->{prefs}{alttitle_langs} = langpref_parse($u->{prefs}{alttitle_langs}) // $DEFAULT_ALTTITLE_LANGS; $u->{prefs}{traits} = tuwf->dbAlli('SELECT u.tid, t.name, g.name AS "group" FROM users_traits u JOIN traits t ON t.id = u.tid LEFT JOIN traits g ON g.id = t.group WHERE u.id =', \$u->{id}, 'ORDER BY g.order, t.name'); @@ -137,9 +142,11 @@ elm_api UserEdit => $FORM_OUT, $FORM_IN, sub { $p->{alttitle_langs} = langpref_fmt $p->{alttitle_langs}; $p->{title_langs} = undef if $p->{title_langs} && ($p->{title_langs} eq langpref_fmt($DEFAULT_TITLE_LANGS) || $p->{title_langs} eq '[]'); $p->{alttitle_langs} = undef if $p->{alttitle_langs} && $p->{alttitle_langs} eq langpref_fmt $DEFAULT_ALTTITLE_LANGS; + $p->{vnrel_langs} = $p->{vnrel_langs}->@* == keys %LANGUAGE ? undef : '{'.join(',',$p->{vnrel_langs}->@*).'}'; $set{$_} = $p->{$_} for qw/nodistract_noads nodistract_nofancy support_enabled uniname pubskin_enabled/; $setp{$_} = $p->{$_} for qw/ - max_sexual max_violence traits_sexual tags_all tags_cont tags_ero tags_tech prodrelexpand spoilers skin customcss title_langs alttitle_langs + max_sexual max_violence traits_sexual tags_all tags_cont tags_ero tags_tech prodrelexpand + vnrel_langs vnrel_olang vnrel_mtl spoilers skin customcss title_langs alttitle_langs /; tuwf->dbExeci('DELETE FROM users_traits WHERE id =', \$data->{id}); tuwf->dbExeci('INSERT INTO users_traits', { id => $data->{id}, tid => $_->{tid} }) for $p->{traits}->@*; diff --git a/lib/VNWeb/VN/Page.pm b/lib/VNWeb/VN/Page.pm index abe08476..4fb3a383 100644 --- a/lib/VNWeb/VN/Page.pm +++ b/lib/VNWeb/VN/Page.pm @@ -523,18 +523,18 @@ sub releases_ { $langrel{$_} = min map $_->{released}, $lang{$_}->@* for keys %lang; my @lang = sort { $langrel{$a} <=> $langrel{$b} || ($b eq $v->{olang}) cmp ($a eq $v->{olang}) || $a cmp $b } keys %lang; - my $pref = +(auth && do { - my $v = tuwf->dbVali('SELECT vnlang FROM users_prefs WHERE id =', \auth->uid); - $v && JSON::XS::decode_json($v) - }) || {}; + my $pref = auth ? do { + my $v = tuwf->dbRowi('SELECT vnrel_langs::text[] AS langs, vnrel_olang AS olang, vnrel_mtl AS mtl FROM users_prefs WHERE id =', \auth->uid); + $v->{langs} = $v->{langs} ? { map +($_,1), $v->{langs}->@* } : \%LANGUAGE; + $v + } : { langs => \%LANGUAGE, olang => 1, mtl => 0 }; my sub lang_ { my($lang) = @_; my $ropt = { id => $lang, lang => $lang }; my $mtl = $langmtl{$lang}; - my $prefid = $lang.($mtl?'-mtl':''); - my $open = $pref->{$prefid} // ($lang eq $v->{olang} || !$mtl); - tag_ 'details', $open ? (open => 'open') : (), auth ? 'data-save-id' : 'data-remember-id', "vnlang-$prefid", sub { + my $open = ($pref->{olang} && $lang eq $v->{olang} && !$mtl) || ($pref->{langs}{$lang} && (!$mtl || $pref->{mtl})); + details_ open => $open?'open':undef, sub { summary_ $mtl ? (class => 'mtl') : (), sub { abbr_ class => "icons lang $lang".($mtl?' mtl':''), title => $LANGUAGE{$lang}, ''; txt_ $LANGUAGE{$lang}; diff --git a/sql/schema.sql b/sql/schema.sql index d215546e..40b35603 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -1057,10 +1057,13 @@ CREATE TABLE users_prefs ( ulist_votes jsonb, ulist_vnlist jsonb, ulist_wish jsonb, - vnlang jsonb, -- '$lang(-mtl)?' => true/false, which languages to expand/collapse on VN pages + vnlang jsonb, -- Deprecated, replaced by vnrel_x. '$lang(-mtl)?' => true/false, which languages to expand/collapse on VN pages title_langs jsonb, alttitle_langs jsonb, - prodrelexpand boolean NOT NULL DEFAULT true + prodrelexpand boolean NOT NULL DEFAULT true, + vnrel_langs language[], -- NULL meaning "show all languages" + vnrel_olang boolean NOT NULL DEFAULT true, + vnrel_mtl boolean NOT NULL DEFAULT false ); -- Additional fields for the 'users' table, but with some protected columns. diff --git a/util/updates/2022-06-19-user-prefs-vnrel.sql b/util/updates/2022-06-19-user-prefs-vnrel.sql new file mode 100644 index 00000000..f9321b93 --- /dev/null +++ b/util/updates/2022-06-19-user-prefs-vnrel.sql @@ -0,0 +1,31 @@ +ALTER TABLE users_prefs ADD COLUMN vnrel_langs language[], + ADD COLUMN vnrel_olang boolean NOT NULL DEFAULT true, + ADD COLUMN vnrel_mtl boolean NOT NULL DEFAULT false; + +-- Attempt to infer vnrel_langs and vnrel_mtl from the old 'vnlang' column. +BEGIN; + +CREATE OR REPLACE FUNCTION vnlang_to_langs(vnlang jsonb) RETURNS language[] AS $$ +DECLARE + ret language[]; + del language; +BEGIN + ret := enum_range(null::language); + FOR del IN SELECT key::language FROM jsonb_each(vnlang) x WHERE key NOT LIKE '%-mtl' AND value = 'false' + LOOP + ret := array_remove(ret, del); + END LOOP; + RETURN CASE WHEN array_length(ret,1) = array_length(enum_range(null::language),1) THEN NULL ELSE RET END; +END$$ LANGUAGE plpgsql; + +WITH p(id,langs,mtl) AS ( + SELECT id, vnlang_to_langs(vnlang), vnlang->'en-mtl' is not distinct from 'true' + FROM users_prefs WHERE vnlang IS NOT NULL +) UPDATE users_prefs + SET vnrel_langs = langs, vnrel_mtl = mtl + FROM p + WHERE p.id = users_prefs.id AND (langs IS NOT NULL OR mtl); + +DROP FUNCTION vnlang_to_langs(jsonb); + +COMMIT; |