User: Notification settings (e.g. for Comments)
It would be nice to have notification settings such as for comments.
Ideally, it should support email unsubscribe links
Does anyone have any ideas on:
- What notification settings do we want to provide?
- How should we store these settings in the DB?
- What the UI will look like for configuring these settings
Is anyone interested in researching other apps to see how they handle this? Ideas:
- GitHub
- Substack
- Medium
- various banks
- Theses actually might not be a good case study candidate because from my experience, most of them have very confusing notification settings
(i'm assigning myself just to keep track of it, anyone's free to take on this ticket — although, just know that this is an important ticket that needs to be handled correctly!)
Before writing any code, please run your answers for the questions above by me first! ty
We should also consider settings for frequency of delivery:
- As it happens
- Daily digest
- Weekly digest
Notification Settings Spec
Delivery Frequency
For the MVP, we want to support the following delivery frequencies:
- Off (notification notifs, ever)
- As it happens (basically the same thing as "on")
- ~~Daily Digest~~
- Although this is nice, it's a heavy lift. We will tackle this later, but build out the MVP in a way that can be migrated into a future system that handles this.
Let's design the UI so that more frequency options can be easily added in the future.
Notification setting options
We only support configuring settings for a category of items (type of notification). We will not support notification settings for a specific item (e.g. Slack's "Get notified about new replies" in a thread).
A user will have settings for:
- each major model (ACH Transfer, Check, Disbursement, Check Deposit, Organizer Position, Reimbursements, etc.)
- My Comment Threads
- Comments on an object I created
- Comments in a thread I'm participating in
- You've been mentioned
- You've commented in that thread
- All Comment Threads
The settings above will act as a default for the user. These defaults can be overridden on an Organization-specific setting. For each organization, the user is able to configure all the settings mentioned above.
Configuring delivery methods
For the time being, we will not support configuring delivery methods for a specific notification type.
UI for settings
We'll use a table — similar to Canvas :)
Database & Low Level Design
This section should answer how we'll be storing these settings and what models we'll have
Database & Low Level Design
Probably use JSONB with the following schema:
{
model: {
subtype: 0
}
}
// Example
{
ach_transfer: {
created: 0 // where the number represents an enum value (on/off)
}
}
class Notification::Settings < ActiveRecord
belongs_to subject, polymorphic: true
validate :subject_is_user_or_organizer_position
def user # maybe this can be an association using `through`
return subject if subject.is_a? User
subject.user
end
# this model have the jsonb column
end
class User
def notify?(type, event: nil)
sql = <<~SQL # try to turn this into an activerecord query
SELECT config->>#{type}
FROM "notification_settings"
WHERE (subject_id = #{self.id} and subject_type = 'User') or (subject_id = 53 and subject_type = 'OrganizerPosition')
ORDER BY CASE
WHEN subject_type = 'OrganizerPosition' THEN 0
WHEN subject_type = 'User' THEN 1
END ASC
LIMIT 1
SQL
end
# USAGE for configuring
Notification::Settings.create(subject: User.first, config: {ach_transer: {create: :off}})
# USAGE for lookups
# for example, sending a email after creating a Check
User.first.notify?("ach_transfer.create")
- How do we handle default options?
- Is this the best way to be storing stuff in the DB?
- How can we create a nice API to work with in the model for setting and reading these configs (with consideration for the defaults)
Look into Active Storage stores https://courses.bigbinaryacademy.com/learn-rubyonrails/jsonb-columns-and-activerecord-store-module/
How do we handle default options?
I think we should hard code the default options in a hash. When I say default options, I mean that these are the default behavior when a user hasn't touched their settings.
When they do touch their settings, the JSONB should only contain keys for the settings they've touched. The rest should likely not be in the JSONB so that it can fallback to the default options defined in the hash.
Where do we define all the possible notification types?
Notification types are composed of a model and subtype. For example, AchTransfer and :create.
I think, we should store them with the model.
class AchTransfer
module Notification
SUBTYPES = %i[created rejected]
end
end
then, to get all types, we can do:
class Notification
def self.types
ActiveRecord.descendants.flat_map do |model|
subtypes = model.get_const("Notification::SUBTYPES")
subtypes.map do |subtype|
"#{model}.#{subtype}"
end
end
end
end
@garyhtou would we be able to create a NotificationSettings model?
I'm thinking one can be attached to each user and it'd be easier to reference in code because each field can just be called like "NotificationSettings.ach_transfer?" instead of doing the sql query thing
Also, we could create a Notification model and a Notifiable concern; a model that includes Notifiable will create a Notification on change, potentially referencing something like NotificationMailer where we have html mailers for each of the possible notification types (for example, ach_transfer.created.html.erb); the Notification has_many :users that are associated with the object creating it, and it checks the users' NotificationSettings to see if they want notifications for the specific model.
I'm frankly just spitballing here, and haven't done much deeper research on this to see if this would be a good architecture for notifications.
(Btw I would like to help with this if possible)
Activity tells us what happened, and then Notification is responsible for notifying people about that Activity.
- Notification belongs_to Activity
- Notification has_many recipients (Users)
- Notification model is responsible for delivery methods and the copy
@garyhtou would we be able to create a
NotificationSettingsmodel?I'm thinking one can be attached to each user and it'd be easier to reference in code because each field can just be called like "NotificationSettings.ach_transfer?" instead of doing the sql query thing
I haven't thought too much about the actually storing of notifications. It does need to be flexible to handle new notification types (e.g. models) and delivery methods. However, we still want to make it easy to query and use (keep it simple).
If we want to allow digests/summaries. We need to know whether a notification has already been delivered to a specific user.
To make this happen, Notification has_many recipients (Users) should have a join model that can store the delivered_at.
class Notification
has_many :notification_deliveries
has_many :recipients, through: :notification_deliveries, class: "User"
end
class NotificationDelivery # name is TBD
belongs_to :notification
belongs_to :recipient, class "User"
end
Consider researching https://github.com/excid3/noticed
FYI, Rails is also working on a native notification system, but don't waiting for this block us. https://github.com/rails/rails/issues/50454
Here's something to consider: https://github.com/hackclub/hcb/issues/10178#issue-2997181040