Settings to opt-out of certain notification types
This commit is contained in:
parent
64d2749299
commit
c0bff8ee18
|
@ -63,9 +63,12 @@ var (
|
||||||
"interests",
|
"interests",
|
||||||
"music_movies",
|
"music_movies",
|
||||||
"hide_age",
|
"hide_age",
|
||||||
|
}
|
||||||
|
|
||||||
// Site prefs
|
// Site preference names (stored in ProfileField table)
|
||||||
// "dm_privacy", "blur_explicit",
|
SitePreferenceFields = []string{
|
||||||
|
"dm_privacy",
|
||||||
|
"blur_explicit",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choices for the Contact Us subject
|
// Choices for the Contact Us subject
|
||||||
|
@ -112,3 +115,27 @@ type Option struct {
|
||||||
Value string
|
Value string
|
||||||
Label string
|
Label string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationOptout field values (stored in user ProfileField table)
|
||||||
|
const (
|
||||||
|
NotificationOptOutFriendPhotos = "notif_optout_friends_photos"
|
||||||
|
NotificationOptOutPrivatePhotos = "notif_optout_private_photos"
|
||||||
|
NotificationOptOutExplicitPhotos = "notif_optout_explicit_photos"
|
||||||
|
NotificationOptOutLikes = "notif_optout_likes"
|
||||||
|
NotificationOptOutComments = "notif_optout_comments"
|
||||||
|
NotificationOptOutSubscriptions = "notif_optout_subscriptions"
|
||||||
|
NotificationOptOutFriendRequestAccepted = "notif_optout_friend_request_accepted"
|
||||||
|
NotificationOptOutPrivateGrant = "notif_optout_private_grant"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notification opt-outs (stored in ProfileField table)
|
||||||
|
var NotificationOptOutFields = []string{
|
||||||
|
NotificationOptOutFriendPhotos,
|
||||||
|
NotificationOptOutPrivatePhotos,
|
||||||
|
NotificationOptOutExplicitPhotos,
|
||||||
|
NotificationOptOutLikes,
|
||||||
|
NotificationOptOutComments,
|
||||||
|
NotificationOptOutSubscriptions,
|
||||||
|
NotificationOptOutFriendRequestAccepted,
|
||||||
|
NotificationOptOutPrivateGrant,
|
||||||
|
}
|
||||||
|
|
|
@ -164,6 +164,44 @@ func Settings() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
session.Flash(w, r, "Privacy settings updated!")
|
session.Flash(w, r, "Privacy settings updated!")
|
||||||
|
case "notifications":
|
||||||
|
hashtag = "#notifications"
|
||||||
|
|
||||||
|
// Store their notification opt-outs.
|
||||||
|
for _, key := range config.NotificationOptOutFields {
|
||||||
|
var value = r.PostFormValue(key)
|
||||||
|
|
||||||
|
// Boolean flip for DB storage:
|
||||||
|
// - Pre-existing users before these options are added have no pref stored in the DB
|
||||||
|
// - The default pref is opt-IN (receive all notifications)
|
||||||
|
// - The checkboxes on front-end are on by default, uncheck them to opt-out, checkbox value="true"
|
||||||
|
// - So when they post as "true" (default), we keep the notifications sending
|
||||||
|
// - If they uncheck the box, no value is sent and that's an opt-out.
|
||||||
|
if value == "" {
|
||||||
|
value = "true" // opt-out, store opt-out=true in the DB
|
||||||
|
} else if value == "true" {
|
||||||
|
value = "false" // the box remained checked, they don't opt-out, store opt-out=false in the DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save it. TODO: fires off inserts/updates for each one,
|
||||||
|
// probably not performant to do.
|
||||||
|
user.SetProfileField(key, value)
|
||||||
|
}
|
||||||
|
session.Flash(w, r, "Notification preferences updated!")
|
||||||
|
|
||||||
|
// Save the user for new fields to be committed to DB.
|
||||||
|
if err := user.Save(); err != nil {
|
||||||
|
session.FlashError(w, r, "Failed to save user to database: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are they unsubscribing from all threads?
|
||||||
|
if r.PostFormValue("unsubscribe_all_threads") == "true" {
|
||||||
|
if err := models.UnsubscribeAllThreads(user); err != nil {
|
||||||
|
session.FlashError(w, r, "Couldn't unsubscribe from threads: %s", err)
|
||||||
|
} else {
|
||||||
|
session.Flash(w, r, "Unsubscribed from all comment threads!")
|
||||||
|
}
|
||||||
|
}
|
||||||
case "location":
|
case "location":
|
||||||
hashtag = "#location"
|
hashtag = "#location"
|
||||||
var (
|
var (
|
||||||
|
@ -293,6 +331,9 @@ func Settings() http.HandlerFunc {
|
||||||
// Show enabled status for 2FA.
|
// Show enabled status for 2FA.
|
||||||
vars["TwoFactorEnabled"] = models.Get2FA(user.ID).Enabled
|
vars["TwoFactorEnabled"] = models.Get2FA(user.ID).Enabled
|
||||||
|
|
||||||
|
// Count of subscribed comment threads.
|
||||||
|
vars["SubscriptionCount"] = models.CountSubscriptions(user)
|
||||||
|
|
||||||
if err := tmpl.Execute(w, r, vars); err != nil {
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -181,7 +181,7 @@ func Likes() http.HandlerFunc {
|
||||||
|
|
||||||
// Notify the recipient of the like.
|
// Notify the recipient of the like.
|
||||||
log.Info("Added like on %s:%d, notifying owner %+v", req.TableName, tableID, targetUser)
|
log.Info("Added like on %s:%d, notifying owner %+v", req.TableName, tableID, targetUser)
|
||||||
if targetUser != nil {
|
if targetUser != nil && !targetUser.NotificationOptOut(config.NotificationOptOutLikes) {
|
||||||
notif := &models.Notification{
|
notif := &models.Notification{
|
||||||
UserID: targetUser.ID,
|
UserID: targetUser.ID,
|
||||||
AboutUser: *currentUser,
|
AboutUser: *currentUser,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
"code.nonshy.com/nonshy/website/pkg/log"
|
||||||
"code.nonshy.com/nonshy/website/pkg/models"
|
"code.nonshy.com/nonshy/website/pkg/models"
|
||||||
"code.nonshy.com/nonshy/website/pkg/session"
|
"code.nonshy.com/nonshy/website/pkg/session"
|
||||||
|
@ -109,6 +110,9 @@ func PostComment() http.HandlerFunc {
|
||||||
|
|
||||||
// Are we DELETING this comment?
|
// Are we DELETING this comment?
|
||||||
if isDelete {
|
if isDelete {
|
||||||
|
// Revoke notifications.
|
||||||
|
models.RemoveNotification("comments", comment.ID)
|
||||||
|
|
||||||
if err := comment.Delete(); err != nil {
|
if err := comment.Delete(); err != nil {
|
||||||
session.FlashError(w, r, "Error deleting your commenting: %s", err)
|
session.FlashError(w, r, "Error deleting your commenting: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,7 +169,7 @@ func PostComment() http.HandlerFunc {
|
||||||
templates.Redirect(w, fromURL)
|
templates.Redirect(w, fromURL)
|
||||||
|
|
||||||
// Notify the recipient of the comment.
|
// Notify the recipient of the comment.
|
||||||
if notifyUser != nil && notifyUser.ID != currentUser.ID {
|
if notifyUser != nil && notifyUser.ID != currentUser.ID && !notifyUser.NotificationOptOut(config.NotificationOptOutComments) {
|
||||||
notif := &models.Notification{
|
notif := &models.Notification{
|
||||||
UserID: notifyUser.ID,
|
UserID: notifyUser.ID,
|
||||||
AboutUser: *currentUser,
|
AboutUser: *currentUser,
|
||||||
|
@ -206,11 +210,13 @@ func PostComment() http.HandlerFunc {
|
||||||
|
|
||||||
// Subscribe the current user to this comment thread, so they are
|
// Subscribe the current user to this comment thread, so they are
|
||||||
// notified if other users add followup comments.
|
// notified if other users add followup comments.
|
||||||
|
if !currentUser.NotificationOptOut(config.NotificationOptOutSubscriptions) {
|
||||||
if _, err := models.SubscribeTo(currentUser, comment.TableName, comment.TableID); err != nil {
|
if _, err := models.SubscribeTo(currentUser, comment.TableName, comment.TableID); err != nil {
|
||||||
log.Error("Couldn't subscribe user %d to comment thread %s/%d: %s",
|
log.Error("Couldn't subscribe user %d to comment thread %s/%d: %s",
|
||||||
currentUser.ID, comment.TableName, comment.TableID, err,
|
currentUser.ID, comment.TableName, comment.TableID, err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,9 +366,11 @@ func NewPost() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe the current user to further responses on this thread.
|
// Subscribe the current user to further responses on this thread.
|
||||||
|
if !currentUser.NotificationOptOut(config.NotificationOptOutSubscriptions) {
|
||||||
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
||||||
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect the poster to the correct page number too.
|
// Redirect the poster to the correct page number too.
|
||||||
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d%s", thread.ID, queryString))
|
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d%s", thread.ID, queryString))
|
||||||
|
@ -420,9 +422,11 @@ func NewPost() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe the current user to responses on this thread.
|
// Subscribe the current user to responses on this thread.
|
||||||
|
if !currentUser.NotificationOptOut(config.NotificationOptOutSubscriptions) {
|
||||||
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
if _, err := models.SubscribeTo(currentUser, "threads", thread.ID); err != nil {
|
||||||
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
log.Error("Couldn't subscribe user %d to forum thread %d: %s", currentUser.ID, thread.ID, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
|
||||||
return
|
return
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
"code.nonshy.com/nonshy/website/pkg/log"
|
||||||
"code.nonshy.com/nonshy/website/pkg/models"
|
"code.nonshy.com/nonshy/website/pkg/models"
|
||||||
"code.nonshy.com/nonshy/website/pkg/session"
|
"code.nonshy.com/nonshy/website/pkg/session"
|
||||||
|
@ -87,6 +88,7 @@ func AddFriend() http.HandlerFunc {
|
||||||
} else {
|
} else {
|
||||||
if verdict == "approve" {
|
if verdict == "approve" {
|
||||||
// Notify the requestor they'd been approved.
|
// Notify the requestor they'd been approved.
|
||||||
|
if !user.NotificationOptOut(config.NotificationOptOutFriendRequestAccepted) {
|
||||||
notif := &models.Notification{
|
notif := &models.Notification{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
AboutUser: *currentUser,
|
AboutUser: *currentUser,
|
||||||
|
@ -95,6 +97,7 @@ func AddFriend() http.HandlerFunc {
|
||||||
if err := models.CreateNotification(notif); err != nil {
|
if err := models.CreateNotification(notif); err != nil {
|
||||||
log.Error("Couldn't create approved notification: %s", err)
|
log.Error("Couldn't create approved notification: %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
session.Flash(w, r, "You accepted the friend request from %s!", username)
|
session.Flash(w, r, "You accepted the friend request from %s!", username)
|
||||||
templates.Redirect(w, "/friends?view=requests")
|
templates.Redirect(w, "/friends?view=requests")
|
||||||
|
|
|
@ -213,6 +213,9 @@ func Delete() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take back notifications on it.
|
||||||
|
models.RemoveNotification("photos", photo.ID)
|
||||||
|
|
||||||
if err := photo.Delete(); err != nil {
|
if err := photo.Delete(); err != nil {
|
||||||
session.FlashError(w, r, "Couldn't delete photo: %s", err)
|
session.FlashError(w, r, "Couldn't delete photo: %s", err)
|
||||||
templates.Redirect(w, redirect)
|
templates.Redirect(w, redirect)
|
||||||
|
|
|
@ -138,6 +138,7 @@ func Share() http.HandlerFunc {
|
||||||
templates.Redirect(w, "/photo/private")
|
templates.Redirect(w, "/photo/private")
|
||||||
|
|
||||||
// Create a notification for this.
|
// Create a notification for this.
|
||||||
|
if !user.NotificationOptOut(config.NotificationOptOutPrivateGrant) {
|
||||||
notif := &models.Notification{
|
notif := &models.Notification{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
AboutUser: *currentUser,
|
AboutUser: *currentUser,
|
||||||
|
@ -149,6 +150,7 @@ func Share() http.HandlerFunc {
|
||||||
if err := models.CreateNotification(notif); err != nil {
|
if err := models.CreateNotification(notif); err != nil {
|
||||||
log.Error("Couldn't create PrivatePhoto notification: %s", err)
|
log.Error("Couldn't create PrivatePhoto notification: %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if intent == "revoke" {
|
} else if intent == "revoke" {
|
||||||
|
|
|
@ -208,6 +208,9 @@ func notifyFriendsNewPhoto(photo *models.Photo, currentUser *models.User) {
|
||||||
// You should not get notified about their new private photos.
|
// You should not get notified about their new private photos.
|
||||||
notifyUserIDs = models.FilterFriendIDs(notifyUserIDs, friendIDs)
|
notifyUserIDs = models.FilterFriendIDs(notifyUserIDs, friendIDs)
|
||||||
|
|
||||||
|
// Filter down the notifyUserIDs further to respect their notification opt-out preferences.
|
||||||
|
notifyUserIDs = models.FilterPhotoUploadNotificationUserIDs(photo, notifyUserIDs)
|
||||||
|
|
||||||
for _, fid := range notifyUserIDs {
|
for _, fid := range notifyUserIDs {
|
||||||
notif := &models.Notification{
|
notif := &models.Notification{
|
||||||
UserID: fid,
|
UserID: fid,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
"code.nonshy.com/nonshy/website/pkg/log"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
@ -75,7 +76,6 @@ func CreateNotification(n *Notification) error {
|
||||||
time.Now(),
|
time.Now(),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
).Error
|
).Error
|
||||||
// return DB.Create(n).Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNotification by ID.
|
// GetNotification by ID.
|
||||||
|
@ -85,6 +85,11 @@ func GetNotification(id uint64) (*Notification, error) {
|
||||||
return n, result.Error
|
return n, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationOptOut checks whether the user opts-out of a class of notification.
|
||||||
|
func (u *User) NotificationOptOut(name string) bool {
|
||||||
|
return u.GetProfileField(name) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveNotification about a table ID, e.g. when removing a like.
|
// RemoveNotification about a table ID, e.g. when removing a like.
|
||||||
func RemoveNotification(tableName string, tableID uint64) error {
|
func RemoveNotification(tableName string, tableID uint64) error {
|
||||||
result := DB.Where(
|
result := DB.Where(
|
||||||
|
@ -218,6 +223,67 @@ func PaginateNotifications(user *User, pager *Pagination) ([]*Notification, erro
|
||||||
return ns, result.Error
|
return ns, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterPhotoUploadNotificationUserIDs will narrow a set of UserIDs who would be notified about
|
||||||
|
// a new photo upload to respect each user's preference for notification opt-outs.
|
||||||
|
//
|
||||||
|
// It is assumed that userIDs are already narrowed down to Friends of the current user.
|
||||||
|
func FilterPhotoUploadNotificationUserIDs(photo *Photo, userIDs []uint64) []uint64 {
|
||||||
|
var (
|
||||||
|
result = []uint64{}
|
||||||
|
|
||||||
|
// Collect notification opt-out profile fields and map them by user ID for easy lookup.
|
||||||
|
prefs = []*ProfileField{}
|
||||||
|
mapPrefs = map[uint64]map[string]bool{}
|
||||||
|
)
|
||||||
|
if len(userIDs) == 0 {
|
||||||
|
return userIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect opt-out preferences for these users.
|
||||||
|
r := DB.Model(&ProfileField{}).Where(
|
||||||
|
"user_id IN ? AND name IN ?",
|
||||||
|
userIDs, []string{
|
||||||
|
config.NotificationOptOutFriendPhotos, // all friends' photos
|
||||||
|
config.NotificationOptOutPrivatePhotos, // private photos from friends
|
||||||
|
config.NotificationOptOutExplicitPhotos, // explicit photos
|
||||||
|
},
|
||||||
|
).Find(&prefs)
|
||||||
|
if r.Error != nil {
|
||||||
|
log.Error("FilterPhotoUploadNotificationUserIDs: couldn't collect user preferences: %s", r.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the preferences by user ID.
|
||||||
|
for _, row := range prefs {
|
||||||
|
if _, ok := mapPrefs[row.UserID]; !ok {
|
||||||
|
mapPrefs[row.UserID] = map[string]bool{}
|
||||||
|
}
|
||||||
|
mapPrefs[row.UserID][row.Name] = row.Value == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Narrow the notification recipients based on photo property and their preferences.
|
||||||
|
for _, userID := range userIDs {
|
||||||
|
// Skip explicit photo notification?
|
||||||
|
if photo.Explicit && mapPrefs[userID][config.NotificationOptOutExplicitPhotos] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip private photo notification?
|
||||||
|
if photo.Visibility == PhotoPrivate && mapPrefs[userID][config.NotificationOptOutPrivatePhotos] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip friend photo notifications?
|
||||||
|
if mapPrefs[userID][config.NotificationOptOutFriendPhotos] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// They get the notification.
|
||||||
|
result = append(result, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Save a notification.
|
// Save a notification.
|
||||||
func (n *Notification) Save() error {
|
func (n *Notification) Save() error {
|
||||||
return DB.Save(n).Error
|
return DB.Save(n).Error
|
||||||
|
|
|
@ -28,6 +28,29 @@ func GetSubscription(user *User, tableName string, tableID uint64) (*Subscriptio
|
||||||
return s, result.Error
|
return s, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountSubscriptions counts how many comment threads the user is subscribed to.
|
||||||
|
func CountSubscriptions(user *User) int64 {
|
||||||
|
var (
|
||||||
|
count int64
|
||||||
|
result = DB.Model(&Subscription{}).Where(
|
||||||
|
"user_id = ? AND subscribed IS TRUE",
|
||||||
|
user.ID,
|
||||||
|
).Count(&count)
|
||||||
|
)
|
||||||
|
if result.Error != nil {
|
||||||
|
log.Error("Error in CountSubscriptions(%s): %s", user.Username, result.Error)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsubscribeAllThreads removes subscription preferences for all comment threads.
|
||||||
|
func UnsubscribeAllThreads(user *User) error {
|
||||||
|
return DB.Where(
|
||||||
|
"user_id = ?",
|
||||||
|
user.ID,
|
||||||
|
).Delete(&Subscription{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubscribers returns all of the UserIDs that are subscribed to a thread.
|
// GetSubscribers returns all of the UserIDs that are subscribed to a thread.
|
||||||
func GetSubscribers(tableName string, tableID uint64) []uint64 {
|
func GetSubscribers(tableName string, tableID uint64) []uint64 {
|
||||||
var userIDs = []uint64{}
|
var userIDs = []uint64{}
|
||||||
|
|
|
@ -315,6 +315,13 @@
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
<a href="/settings#notifications">
|
||||||
|
<i class="fa fa-gear mr-1"></i>
|
||||||
|
Manage notification settings
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<table class="table is-striped is-fullwidth is-hoverable">
|
<table class="table is-striped is-fullwidth is-hoverable">
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range .Notifications}}
|
{{range .Notifications}}
|
||||||
|
|
|
@ -61,6 +61,15 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/settings#notifications" class="nonshy-tab-button">
|
||||||
|
<strong><i class="fa fa-bell mr-1"></i> Notifications</strong>
|
||||||
|
<p class="help">
|
||||||
|
Control your (on-site) notification preferences.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/settings#account" class="nonshy-tab-button">
|
<a href="/settings#account" class="nonshy-tab-button">
|
||||||
<strong><i class="fa fa-user mr-1"></i> Account Settings</strong>
|
<strong><i class="fa fa-user mr-1"></i> Account Settings</strong>
|
||||||
|
@ -655,6 +664,192 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Notification Settings -->
|
||||||
|
<div class="card mb-5" id="notifications">
|
||||||
|
<header class="card-header has-background-link">
|
||||||
|
<p class="card-header-title has-text-light">
|
||||||
|
<i class="fa fa-bell pr-2"></i>
|
||||||
|
Notification Settings
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="block">
|
||||||
|
On this page you may opt-out of certain kinds of (on-site) notification messages.
|
||||||
|
{{PrettyTitle}} does not send you any e-mails or push notification -- these on-site
|
||||||
|
notifications only appear while you are visiting the website (on your
|
||||||
|
<a href="/me">home/user dashboard page</a>).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="/settings">
|
||||||
|
{{InputCSRF}}
|
||||||
|
<input type="hidden" name="intent" value="notifications">
|
||||||
|
|
||||||
|
<h2 class="subtitle">New Photo Uploads</h2>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
By default you will be notified when your friends upload a new picture to the site.
|
||||||
|
Below, you may opt-out of new photo upload notifications.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Notify me when...</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_friends_photos"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_friends_photos") "true"}}checked{{end}}>
|
||||||
|
My friends upload a new photo
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
If unchecked, the following two notifications will not be sent either.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_private_photos"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_private_photos") "true"}}checked{{end}}>
|
||||||
|
A friend who shared their private photos with me uploads a new private photo
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_explicit_photos"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_explicit_photos") "true"}}checked{{end}}>
|
||||||
|
Allow notifications for 'explicit' photo uploads by my friends
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
This will also depend on your <a href="/settings#prefs" target="_blank">opt-in to see explicit content</a> -- otherwise
|
||||||
|
notifications about explicit photo uploads will not be sent to you.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="subtitle mt-5">Likes & Comments</h2>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
By default you will be notified when somebody 'likes' or comments on your profile page
|
||||||
|
or photos. You may turn off those notifications with the options below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Notify me when...</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_likes"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_likes") "true"}}checked{{end}}>
|
||||||
|
Somebody 'likes' my profile page, photos, or comments
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_comments"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_comments") "true"}}checked{{end}}>
|
||||||
|
Somebody leaves a comment on one of my photos
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="subtitle mt-5">Comment Thread Subscriptions</h2>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
Comment threads and forum posts may be 'subscribed' to so that you can be notified about
|
||||||
|
comments left by other people after you. By default, you will subscribe to comment threads
|
||||||
|
after you leave your first comment.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
<strong>Note:</strong> you may unsubscribe from comment threads by using the link at the
|
||||||
|
top of its page (for example: at the top of a forum thread page or the top of the list of
|
||||||
|
comments on a photo page). You may also opt <em>in</em> to get notifications on a thread
|
||||||
|
that you didn't comment on by using the same link at the top of their pages.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
The options below can control the automatic opt-in for subscriptions when you leave a
|
||||||
|
comment on a new comment thread.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_subscriptions"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_subscriptions") "true"}}checked{{end}}>
|
||||||
|
Subscribe to notifications for future comments when I leave a comment on something
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Unsubscribe from Comment Threads</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="unsubscribe_all_threads"
|
||||||
|
value="true">
|
||||||
|
Unsubscribe NOW from <strong>all ({{.SubscriptionCount}}) comment threads</strong> that I am currently following.
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
You are currently subscribed to <strong>{{.SubscriptionCount}}</strong> comment thread{{Pluralize64 .SubscriptionCount}}.
|
||||||
|
You may immediately unsubscribe from all of these threads by checking this box and clicking "Save" below.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="subtitle mt-5">Miscellaneous</h2>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Notify me when...</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_friend_request_accepted"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_friend_request_accepted") "true"}}checked{{end}}>
|
||||||
|
Somebody approves my friendship request
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="notif_optout_private_grant"
|
||||||
|
value="true"
|
||||||
|
{{if ne (.CurrentUser.GetProfileField "notif_optout_private_grant") "true"}}checked{{end}}>
|
||||||
|
Somebody unlocks their private photos for me to see
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Read-only box for certification photo response -->
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
value="true"
|
||||||
|
checked
|
||||||
|
disabled>
|
||||||
|
My certification photo is approved or rejected
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
This notification is important for your account status, is rarely sent out, and can
|
||||||
|
not be opted-out from.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<button type="submit" class="button is-primary">
|
||||||
|
<i class="fa fa-save mr-2"></i> Save Privacy Settings
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Account Settings -->
|
<!-- Account Settings -->
|
||||||
<div id="account">
|
<div id="account">
|
||||||
<div class="card mb-5">
|
<div class="card mb-5">
|
||||||
|
@ -819,6 +1014,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
$prefs = document.querySelector("#prefs"),
|
$prefs = document.querySelector("#prefs"),
|
||||||
$location = document.querySelector("#location"),
|
$location = document.querySelector("#location"),
|
||||||
$privacy = document.querySelector("#privacy"),
|
$privacy = document.querySelector("#privacy"),
|
||||||
|
$notifications = document.querySelector("#notifications"),
|
||||||
$account = document.querySelector("#account")
|
$account = document.querySelector("#account")
|
||||||
$deactivate = document.querySelector("#deactivate"),
|
$deactivate = document.querySelector("#deactivate"),
|
||||||
buttons = Array.from(document.getElementsByClassName("nonshy-tab-button"));
|
buttons = Array.from(document.getElementsByClassName("nonshy-tab-button"));
|
||||||
|
@ -828,6 +1024,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
$prefs.style.display = 'none';
|
$prefs.style.display = 'none';
|
||||||
$location.style.display = 'none';
|
$location.style.display = 'none';
|
||||||
$privacy.style.display = 'none';
|
$privacy.style.display = 'none';
|
||||||
|
$notifications.style.display = 'none';
|
||||||
$account.style.display = 'none';
|
$account.style.display = 'none';
|
||||||
$deactivate.style.display = 'none';
|
$deactivate.style.display = 'none';
|
||||||
|
|
||||||
|
@ -849,6 +1046,9 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
case "privacy":
|
case "privacy":
|
||||||
$activeTab = $privacy;
|
$activeTab = $privacy;
|
||||||
break;
|
break;
|
||||||
|
case "notifications":
|
||||||
|
$activeTab = $notifications;
|
||||||
|
break;
|
||||||
case "account":
|
case "account":
|
||||||
$activeTab = $account;
|
$activeTab = $account;
|
||||||
break;
|
break;
|
||||||
|
@ -887,11 +1087,17 @@ window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
showTab(name);
|
showTab(name);
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
// Show the requested tab on first page load.
|
// Show the requested tab on first page load.
|
||||||
showTab(window.location.hash.replace(/^#/, ''));
|
showTab(window.location.hash.replace(/^#/, ''));
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Location tab scripts.
|
// Location tab scripts.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user