summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2020-10-08 15:09:09 +0200
committerYorhel <git@yorhel.nl>2020-10-08 15:09:14 +0200
commit6d8c7104e6f01d3f1b8c048f2641940b4838a85e (patch)
treed42c9270e8a6746a85a728989a68935d754fe9c6
parent7eb7a3e073107bb5ad6ec20ae18f296bde33b4da (diff)
notifications: Add subscription management UI + finalize DB migration
Seems to be working so far. I'll find the actual bugs in production.
-rw-r--r--data/style.css23
-rw-r--r--elm/Lib/DropDown.elm2
-rw-r--r--elm/Subscribe.elm93
-rw-r--r--lib/VNWeb/Discussions/Thread.pm2
-rw-r--r--lib/VNWeb/HTML.pm37
-rw-r--r--lib/VNWeb/User/Notifications.pm31
-rw-r--r--util/updates/2020-10-08-extra-notifications.sql (renamed from util/updates/wip-notifications.sql)0
7 files changed, 179 insertions, 9 deletions
diff --git a/data/style.css b/data/style.css
index 74012057..240981e3 100644
--- a/data/style.css
+++ b/data/style.css
@@ -306,11 +306,14 @@ div.maintabs { display: flex; justify-content: space-between; position:
div.maintabs.right { justify-content: flex-end }
div.maintabs.left { justify-content: flex-start }
div.maintabs > ul { margin: 0; padding: 0; list-style-type: none }
-div.maintabs > ul li { display: inline-block; margin: 0 0 0 10px }
-div.maintabs > ul li:nth-child(1) { margin-left: 0!important }
-div.maintabs > ul li a { display: inline-block; box-sizing: border-box; height: 21px; padding: 1px 7px 0 7px; border: 1px solid $border$; border-bottom: none; background-color: $tabbg$; color: $grayedout$; }
-div.maintabs > ul li.tabselected a,
-div.maintabs > ul li a:hover { background: $_blendbg$; color: $maintext$; height: 22px }
+div.maintabs > ul > li { display: inline-block; margin: 0 0 0 10px }
+div.maintabs > ul > li:nth-child(1) { margin-left: 0!important }
+div.maintabs > ul > li > a,
+div.maintabs > ul > li > div > a { display: inline-block; box-sizing: border-box; height: 21px; padding: 1px 7px 0 7px; border: 1px solid $border$; border-bottom: none; background-color: $tabbg$; color: $grayedout$; }
+div.maintabs > ul > li.tabselected > a,
+div.maintabs > ul > li.tabselected > div > a,
+div.maintabs > ul > li > div > a:hover,
+div.maintabs > ul > li > a:hover { background: $_blendbg$; color: $maintext$; height: 22px }
div.maintabs.browsetabs > ul li a { color: $maintext$ }
div.maintabs.browsetabs > ul li { margin-left: 5px }
div.maintabs.bottom { margin-top: 10px; /* WHY!? */ margin-bottom: -10px }
@@ -858,6 +861,16 @@ div.votelist td.tc2 { width: 50px; text-align: right; padding-right: 10px }
.browse.notifies .unread td { font-weight: bold }
.browse.notifies tfoot td { padding: 0 0 0 25px }
+/***** Subscription tab thiny (HTML::_maintabs_subscribe_() + elm/Subscribe) ****/
+
+#subscribe .inactive { color: transparent; text-shadow: 0 0 $grayedout$ }
+#subscribe .active { color: transparent; text-shadow: 0 0 $maintext$ }
+
+#subscribe > div > a { height: 21px!important /* override :hover change */ }
+#subscribe > div > div { position: absolute; width: 1px }
+#subscribe > div > div > div { box-sizing: border-box; padding: 10px; width: 500px; border: 1px solid $border$; background: $secbg$; position: relative; bottom: 0; left: -470px; z-index: 100 }
+#subscribe p, #subscribe h4, #subscribe label { display: block; margin-bottom: 3px }
+
/***** User list *****/
.browse.userlist .tc3,
diff --git a/elm/Lib/DropDown.elm b/elm/Lib/DropDown.elm
index 286a61cb..1e6204ac 100644
--- a/elm/Lib/DropDown.elm
+++ b/elm/Lib/DropDown.elm
@@ -1,4 +1,4 @@
-module Lib.DropDown exposing (Config, init, sub, toggle, view)
+module Lib.DropDown exposing (Config, init, sub, toggle, view, onClickOutside)
import Browser.Events as E
import Json.Decode as JD
diff --git a/elm/Subscribe.elm b/elm/Subscribe.elm
new file mode 100644
index 00000000..8631daac
--- /dev/null
+++ b/elm/Subscribe.elm
@@ -0,0 +1,93 @@
+module Subscribe exposing (main)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Browser
+import Lib.Html exposing (..)
+import Lib.Api as Api
+import Lib.DropDown exposing (onClickOutside)
+import Gen.Api as GApi
+import Gen.Subscribe as GS
+
+
+main : Program GS.Send Model Msg
+main = Browser.element
+ { init = \e -> ({ state = Api.Normal, opened = False, data = e}, Cmd.none)
+ , view = view
+ , update = update
+ , subscriptions = \m -> if m.opened then onClickOutside "subscribe" (Opened False) else Sub.none
+ }
+
+type alias Model =
+ { state : Api.State
+ , opened : Bool
+ , data : GS.Send
+ }
+
+type Msg
+ = Opened Bool
+ | SubNum Bool Bool
+ | SubReview Bool
+ | Submitted GApi.Response
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ let dat = model.data
+ save nd = ({ model | data = nd, state = Api.Loading }, GS.send nd Submitted)
+ in
+ case msg of
+ Opened b -> ({ model | opened = b }, Cmd.none)
+ SubNum v b -> save { dat | subnum = if b then Just v else Nothing }
+ SubReview b -> save { dat | subreview = b }
+ Submitted e -> ({ model | state = if e == GApi.Success then Api.Normal else Api.Error e }, Cmd.none)
+
+
+view : Model -> Html Msg
+view model =
+ let
+ dat = model.data
+ t = String.left 1 dat.id
+ msg txt = p [] [ text txt, text " These can be disabled globally in your ", a [ href "/u/notifies" ] [ text "notification settings" ], text "." ]
+ in
+ div []
+ [ a [ href "#", onClickD (Opened (not model.opened)), class (if (dat.noti > 0 && dat.subnum /= Just False) || dat.subnum == Just True || dat.subreview then "active" else "inactive") ] [ text "🔔" ]
+ , if not model.opened then text ""
+ else div [] [ div []
+ [ h4 []
+ [ if model.state == Api.Loading then span [ class "spinner", style "float" "right" ] [] else text ""
+ , text "Manage Notifications"
+ ]
+ , case (t, dat.noti) of
+ ("t", 1) -> msg "You receive notifications for replies because you have posted in this thread."
+ ("t", 2) -> msg "You receive notifications for replies because this thread is linked to your personal board."
+ ("t", 3) -> msg "You receive notifications for replies because you have posted in this thread and it is linked to your personal board."
+ ("w", 1) -> msg "You receive notifications for new comments because you have commented on this review."
+ ("w", 2) -> msg "You receive notifications for new comments because this is your review."
+ ("w", 3) -> msg "You receive notifications for new comments because this is your review and you have commented it."
+ (_, 1) -> msg "You receive edit notifications for this entry because you have contributed to it."
+ _ -> text ""
+ , if dat.noti == 0 then text "" else
+ label []
+ [ inputCheck "" (dat.subnum == Just False) (SubNum False)
+ , case t of
+ "t" -> text " Disable notifications only for this thread."
+ "w" -> text " Disable notifications only for this review."
+ _ -> text " Disable edit notifications only for this entry."
+ ]
+ , label []
+ [ inputCheck "" (dat.subnum == Just True) (SubNum True)
+ , case t of
+ "t" -> text " Enable notifications for new replies"
+ "w" -> text " Enable notifications for new comments"
+ _ -> text " Enable notifications for new edits"
+ , if dat.noti == 0 then text "." else text ", regardless of the global setting."
+ ]
+ , if t /= "v" then text "" else
+ label [] [ inputCheck "" dat.subreview SubReview, text " Enable notifications for new reviews." ]
+ , case model.state of
+ Api.Error e -> b [ class "standout" ] [ br [] [], text (Api.showResponse e) ]
+ _ -> text ""
+ ] ]
+ ]
diff --git a/lib/VNWeb/Discussions/Thread.pm b/lib/VNWeb/Discussions/Thread.pm
index 39de4b96..dd443d93 100644
--- a/lib/VNWeb/Discussions/Thread.pm
+++ b/lib/VNWeb/Discussions/Thread.pm
@@ -194,7 +194,7 @@ TUWF::get qr{/$RE{tid}(?:(?<sep>[\./])$RE{num})?}, sub {
auth->notiRead($id, [ map $_->{num}, $posts->@* ]) if @$posts;
- framework_ title => $t->{title}, $num ? (js => 1, pagevars => {sethash=>$num}) : (), sub {
+ framework_ title => $t->{title}, type => 't', dbobj => $t, $num ? (js => 1, pagevars => {sethash=>$num}) : (), sub {
metabox_ $t;
elm_ 'Discussions.Poll' => $POLL_OUT, {
question => $t->{poll_question},
diff --git a/lib/VNWeb/HTML.pm b/lib/VNWeb/HTML.pm
index 772f3ebc..d9098ffe 100644
--- a/lib/VNWeb/HTML.pm
+++ b/lib/VNWeb/HTML.pm
@@ -336,6 +336,38 @@ sub _footer_ {
}
+sub _maintabs_subscribe_ {
+ my($o, $id) = @_;
+ return if !auth || $id !~ /^[twvrpcsd]/;
+
+ my $noti =
+ $id =~ /^t/ ? tuwf->dbVali('SELECT SUM(x) FROM (
+ SELECT 1 FROM threads_posts tp, users u WHERE u.id =', \auth->uid, 'AND tp.uid =', \auth->uid, 'AND tp.tid =', \$id, ' AND u.notify_post
+ UNION SELECT 1+1 FROM threads_boards tb WHERE tb.tid =', \$id, 'AND tb.type = \'u\' AND tb.iid =', \auth->uid, '
+ ) x(x)')
+
+ : $id =~ /^w/ ? (auth->pref('notify_post') || auth->pref('notify_comment')) && tuwf->dbVali('SELECT SUM(x) FROM (
+ SELECT 1 FROM reviews_posts wp, users u WHERE u.id =', \auth->uid, 'AND wp.uid =', \auth->uid, 'AND wp.id =', \$id, 'AND u.notify_post
+ UNION SELECT 1+1 FROM reviews w, users u WHERE u.id =', \auth->uid, 'AND w.uid =', \auth->uid, 'AND w.id =', \$id, 'AND u.notify_comment
+ ) x(x)')
+
+ : auth->pref('notify_dbedit') && tuwf->dbVali('SELECT 1 FROM changes WHERE type = vndbid_type(', \$id, ')::dbentry_type AND itemid = vndbid_num(', \$id, ') AND requester =', \auth->uid);
+
+ my $sub = tuwf->dbRowi('SELECT subnum, subreview FROM notification_subs WHERE uid =', \auth->uid, 'AND iid =', \$id);
+
+ li_ id => 'subscribe', sub {
+ elm_ Subscribe => $VNWeb::User::Notifications::SUB, {
+ id => $id,
+ noti => $noti||0,
+ subnum => $sub->{subnum},
+ subreview => $sub->{subreview}||0,
+ }, sub {
+ a_ href => '#', class => ($noti && (!defined $sub->{subnum} || $sub->{subnum})) || $sub->{subnum} || $sub->{subreview} ? 'active' : 'inactive', '🔔';
+ };
+ };
+}
+
+
sub _maintabs_ {
my $opt = shift;
my($t, $o, $sel) = @{$opt}{qw/type dbobj tab/};
@@ -353,13 +385,13 @@ sub _maintabs_ {
div_ class => 'maintabs right', sub {
ul_ sub {
- t '' => "/$id", $id;
+ t '' => "/$id", $id if $t ne 't';
t rg => "/$id/rg", 'relations'
if $t =~ /[vp]/ && tuwf->dbVali('SELECT 1 FROM', $t eq 'v' ? 'vn_relations' : 'producers_relations', 'WHERE id =', \$o->{id}, 'LIMIT 1');
t releases => "/$id/releases", 'releases' if $t eq 'v';
- t edit => "/$id/edit", 'edit' if can_edit $t, $o;
+ t edit => "/$id/edit", 'edit' if $t ne 't' && can_edit $t, $o;
t copy => "/$id/copy", 'copy' if $t =~ /[rc]/ && can_edit $t, $o;
t tagmod => "/$id/tagmod", 'modify tags' if $t eq 'v' && auth->permTag && !$o->{entry_hidden};
@@ -381,6 +413,7 @@ sub _maintabs_ {
};
t hist => "/$id/hist", 'history' if $t =~ /[uvrpcsd]/;
+ _maintabs_subscribe_ $o, $id;
}
}
}
diff --git a/lib/VNWeb/User/Notifications.pm b/lib/VNWeb/User/Notifications.pm
index 3831402b..18ddb9f5 100644
--- a/lib/VNWeb/User/Notifications.pm
+++ b/lib/VNWeb/User/Notifications.pm
@@ -109,6 +109,10 @@ sub listing_ {
}
+# Redirect so that elm/Subscribe.elm can link to this page without knowing our uid.
+TUWF::get qr{/u/notifies}, sub { auth ? tuwf->resRedirect('/u'.auth->uid.'/notifies') : tuwf->resNotFound };
+
+
TUWF::get qr{/$RE{uid}/notifies}, sub {
my $id = tuwf->capture('id');
return tuwf->resNotFound if !auth || $id != auth->uid;
@@ -200,9 +204,36 @@ TUWF::get qr{/$RE{uid}/notify/$RE{num}/(?<lid>[a-z0-9\.]+)}, sub {
};
+
# It's a bit annoying to add auth->notiRead() to each revision page, so do that in bulk with a simple hook.
TUWF::hook before => sub {
auth->notiRead($+{vndbid}, $+{rev}) if auth && tuwf->reqPath() =~ qr{^/(?<vndbid>[vrpcsd]$RE{num})\.(?<rev>$RE{num})$};
};
+
+
+
+our $SUB = form_compile any => {
+ id => { vndbid => [qw|t w v r p c s d|] },
+ subnum => { required => 0, jsonbool => 1 },
+ subreview => { anybool => 1 },
+ noti => { uint => 1 }, # Whether the user already gets 'subnum' notifications for this entry (see HTML.pm for possible values)
+};
+
+elm_api Subscribe => undef, $SUB, sub {
+ my($data) = @_;
+
+ delete $data->{noti};
+ $data->{subnum} = $data->{subnum}?1:0 if defined $data->{subnum}; # 'jsonbool' isn't understood by SQL
+ $data->{subreview} = 0 if $data->{id} !~ /^v/;
+
+ my %where = (iid => delete $data->{id}, uid => auth->uid);
+ if(!defined $data->{subnum} && !$data->{subreview}) {
+ tuwf->dbExeci('DELETE FROM notification_subs WHERE', \%where);
+ } else {
+ tuwf->dbExeci('INSERT INTO notification_subs', {%where, %$data}, 'ON CONFLICT (iid,uid) DO UPDATE SET', $data);
+ }
+ elm_Success
+};
+
1;
diff --git a/util/updates/wip-notifications.sql b/util/updates/2020-10-08-extra-notifications.sql
index ef0f574b..ef0f574b 100644
--- a/util/updates/wip-notifications.sql
+++ b/util/updates/2020-10-08-extra-notifications.sql