hcb icon indicating copy to clipboard operation
hcb copied to clipboard

User: Notification settings (e.g. for Comments)

Open garyhtou opened this issue 2 years ago • 17 comments

It would be nice to have notification settings such as for comments.

Ideally, it should support email unsubscribe links

garyhtou avatar Mar 01 '24 04:03 garyhtou

Does anyone have any ideas on:

  1. What notification settings do we want to provide?
  2. How should we store these settings in the DB?
  3. 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
  • Facebook

garyhtou avatar Mar 18 '24 01:03 garyhtou

(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!)

garyhtou avatar Mar 18 '24 01:03 garyhtou

Before writing any code, please run your answers for the questions above by me first! ty

garyhtou avatar Mar 20 '24 05:03 garyhtou

We should also consider settings for frequency of delivery:

  • As it happens
  • Daily digest
  • Weekly digest

garyhtou avatar Mar 24 '24 04:03 garyhtou

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 :)

image

Database & Low Level Design

This section should answer how we'll be storing these settings and what models we'll have

garyhtou avatar Mar 26 '24 00:03 garyhtou

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")

garyhtou avatar Mar 26 '24 01:03 garyhtou

  • 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)

garyhtou avatar Apr 01 '24 21:04 garyhtou

Look into Active Storage stores https://courses.bigbinaryacademy.com/learn-rubyonrails/jsonb-columns-and-activerecord-store-module/

garyhtou avatar Apr 01 '24 21:04 garyhtou

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.

garyhtou avatar Apr 01 '24 21:04 garyhtou

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 avatar Apr 01 '24 21:04 garyhtou

@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.

rluodev avatar Oct 08 '24 15:10 rluodev

(Btw I would like to help with this if possible)

rluodev avatar Oct 08 '24 16:10 rluodev

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 avatar Oct 08 '24 23:10 garyhtou

@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

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).

garyhtou avatar Oct 08 '24 23:10 garyhtou

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

garyhtou avatar Oct 08 '24 23:10 garyhtou

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

garyhtou avatar Oct 08 '24 23:10 garyhtou

Here's something to consider: https://github.com/hackclub/hcb/issues/10178#issue-2997181040

sampoder avatar Apr 15 '25 18:04 sampoder