Chime is notifications for Phoenix: something happens in the app, and Chime tells the people it concerns, in the app's feed (the bell), by email and by the app's own channels, as each person wants.
Add Chime to the app's supervision tree, after the repo, the PubSub and
Oban (Chime.Config has the options):
{Chime, repo: MyApp.Repo, pubsub: MyApp.PubSub, resolver: MyApp.ChimeResolver}Write a Chime.Notifier for each kind of notification, and send one:
Chime.notify(MyApp.Notifications.NewComment, %{comment: comment})The functions of this module read and change a person's feed, by the
person's id: feed/2, counts/1, mark_seen/1, mark_read/2,
mark_unread/2, archive/2, unarchive/2; their preferences:
preferences/1, put_preference/4; and the log of what was decided:
explain/1.
Start with the Quickstart.
Live
subscribe/1 subscribes the caller to a person's topic, on the app's
PubSub. It receives:
{Chime, :notified, counts}- a notification came, or an event joined one of their groups;{Chime, :updated, counts}- they saw, read or archived notifications, in another tab or on another device.
counts is %{unseen: n, unread: n, workspaces: %{id => %{unseen: n, unread: n}}}: the whole feed's, and each workspace's. Chime.Live and the JSON API's
stream are subscribers.
Summary
Functions
Takes an action of a notification for its person: one of the
:actions of the notifier's feed/2 (%{key: "accept", label: "Accept"}), which calls the notifier's act/3 with the key, the data
and the notification. When it says :ok or {:ok, url}, the
notification keeps the action and is read, and the person's pages are
told. Returns {:ok, %{action: key, url: url | nil}}, or
{:error, :not_found | :unknown_action | :already | reason}.
Who did a notification's events, in words: "Ana", "Ana and Rui", "Ana
and 4 others", in the app's languages (Chime.Messages). An app can
write its own from notification.actors, the latest first, and
notification.actor_count.
Archives notifications of a person: out of the inbox. Returns the counts.
Cancels an event, by its idempotency key or the event
Returns a specification to start this module under a supervisor.
A person's counts: %{unseen: n, unread: n}, of the notifications of
their feed not archived, or of a workspace's (workspace: id). One row,
kept as notifications come and go.
A person's devices, the latest first.
The log of a notification: when it came, what each channel decided
and why. See Chime.Explain.
A person's feed, the latest first, each notification with its event:
Chime.Feed.render/1 gives what it shows.
A link to a notification, for an email or a channel, that reads it in
the feed when it is opened, then goes to url (a path goes on the
config's :url). See Chime.Links. Without the config's :mail_url
and :secret_key_base, the address itself.
Marks notifications of a person as read (and seen): a list of ids, or
:all. Option :workspace. Returns the counts.
Marks a person's feed as seen: the bell's dot goes. Option :workspace,
only a workspace's. Returns the counts, of the workspace with one.
Marks notifications of a person as unread. Returns the counts.
Notifies the people of an event, by the notifier's channels.
A person's preferences: for each category of the config, its settings
by channel (Chime.Preferences). Option :workspace: the settings in a
workspace, its own where the person set them.
Changes a person's setting of a channel for a category: "on",
"off", or for email "daily" or "weekly". Option :workspace: for
that workspace only, over the person's setting for all. See
Chime.Preferences.
Registers a device of a person, for the :push channel: %{kind: "web", token: endpoint, keys: %{"p256dh" => ..., "auth" => ...}} for
a browser's subscription, %{kind: "apns" | "fcm", token: token} for a
phone's; and :name. A token registered before is the same device, now
of this person. Errors: :unknown_kind, :invalid_token,
:invalid_subscription.
Removes a person's device.
Sends a delivery again: one that failed or was skipped is scheduled now,
its attempts from nought, with "sent again" in the log; what is decided
at its time (the preferences, the conditions, the rules) is decided
again. {:error, :not_found} for an unknown one, {:error, :waiting}
for one that waits already.
Starts Chime with the options of Chime.Config.
Subscribes the caller to a person's live news (see Live, above).
Brings archived notifications of a person back. Returns the counts.
The address to unsubscribe a person from a category's emails, for the
footer of a notifier's email; nil without :mail_url and
:secret_key_base in the config.
Turns a person's device on or off (enabled:), or names it (name:).
Types
@type counts() :: %{unseen: non_neg_integer(), unread: non_neg_integer()}
A person's counts: what they have not seen, and not read.
Functions
@spec act(String.t(), Ecto.UUID.t(), String.t()) :: {:ok, %{action: String.t(), url: String.t() | nil}} | {:error, term()}
Takes an action of a notification for its person: one of the
:actions of the notifier's feed/2 (%{key: "accept", label: "Accept"}), which calls the notifier's act/3 with the key, the data
and the notification. When it says :ok or {:ok, url}, the
notification keeps the action and is read, and the person's pages are
told. Returns {:ok, %{action: key, url: url | nil}}, or
{:error, :not_found | :unknown_action | :already | reason}.
@spec actors(Chime.Notification.t()) :: String.t()
Who did a notification's events, in words: "Ana", "Ana and Rui", "Ana
and 4 others", in the app's languages (Chime.Messages). An app can
write its own from notification.actors, the latest first, and
notification.actor_count.
Archives notifications of a person: out of the inbox. Returns the counts.
@spec cancel(String.t() | Chime.Event.t(), keyword()) :: {:ok, %{deliveries: non_neg_integer(), removed: non_neg_integer()}} | {:error, :not_found}
Cancels an event, by its idempotency key or the event:
Chime.cancel("comment:#{comment.id}")A scheduled event (:at) is not sent. One sent has its deliveries that
wait (an email in ten minutes, a digest) skipped, with "the
notification was cancelled" in the log. With feed: :remove, its
notifications are taken out of the feeds too, but those of a group that
holds other events. Returns {:ok, %{deliveries: n, removed: n}}, or
{:error, :not_found}.
Returns a specification to start this module under a supervisor.
See Supervisor.
A person's counts: %{unseen: n, unread: n}, of the notifications of
their feed not archived, or of a workspace's (workspace: id). One row,
kept as notifications come and go.
@spec devices(String.t()) :: [Chime.Device.t()]
A person's devices, the latest first.
@spec explain(Ecto.UUID.t() | Chime.Notification.t()) :: Chime.Explain.t() | nil
The log of a notification: when it came, what each channel decided
and why. See Chime.Explain.
@spec feed(String.t(), keyword()) :: [Chime.Notification.t()]
A person's feed, the latest first, each notification with its event:
Chime.Feed.render/1 gives what it shows.
Options:
:limit- how many (20, up to 100).:before- the id of a notification: the page after it.:filter-:inbox, those not archived (the default),:unread,:archivedor:all.:workspace- only the notifications of a workspace.
@spec link(Chime.Notification.t(), String.t()) :: String.t()
A link to a notification, for an email or a channel, that reads it in
the feed when it is opened, then goes to url (a path goes on the
config's :url). See Chime.Links. Without the config's :mail_url
and :secret_key_base, the address itself.
~s(<a href="#{Chime.link(notification, "/stories/42")}">Read it</a>)
Marks notifications of a person as read (and seen): a list of ids, or
:all. Option :workspace. Returns the counts.
Marks a person's feed as seen: the bell's dot goes. Option :workspace,
only a workspace's. Returns the counts, of the workspace with one.
Marks notifications of a person as unread. Returns the counts.
@spec notify(module(), term(), keyword()) :: {:ok, Chime.Event.t()} | {:error, :duplicate, Chime.Event.t()}
Notifies the people of an event, by the notifier's channels.
The notifier's recipients/1 says who, unless :to does. Each person
gets a notification in their feed at once (unless they turned the feed
off for the category), or the event joins their open notification of
its group; each other channel is a delivery, which goes at its time as
an Oban job, in the same transaction.
Options:
:to- the people, instead of the notifier'srecipients/1: the app's users,Chime.Recipients or ids.:workspace- the workspace it happens in, instead of the notifier'sworkspace/1(see the Workspaces guide).:at- a time to notify at, instead of now: the event is"scheduled", and a job notifies its people then. The guard (notify?/2) and the people are decided now; the feed, the preferences and the channels at its time.cancel/2stops it.:key- an idempotency key: a second call with the same key does nothing, and returns{:error, :duplicate, event}. Give one when the call may run twice (a retried job):"comment:#{comment.id}".
Returns {:ok, %Chime.Event{}}. For many people, it works a batch of
1000 at a time, each its own transaction: call it from a job of the
app for an event to everyone.
@spec preferences(String.t(), keyword()) :: [Chime.Preferences.t()]
A person's preferences: for each category of the config, its settings
by channel (Chime.Preferences). Option :workspace: the settings in a
workspace, its own where the person set them.
@spec put_preference( String.t(), String.t() | atom(), String.t() | atom(), String.t() | atom(), keyword() ) :: :ok | {:error, atom()}
Changes a person's setting of a channel for a category: "on",
"off", or for email "daily" or "weekly". Option :workspace: for
that workspace only, over the person's setting for all. See
Chime.Preferences.
@spec register_device(String.t(), map()) :: {:ok, Chime.Device.t()} | {:error, atom()}
Registers a device of a person, for the :push channel: %{kind: "web", token: endpoint, keys: %{"p256dh" => ..., "auth" => ...}} for
a browser's subscription, %{kind: "apns" | "fcm", token: token} for a
phone's; and :name. A token registered before is the same device, now
of this person. Errors: :unknown_kind, :invalid_token,
:invalid_subscription.
@spec remove_device(String.t(), Ecto.UUID.t()) :: :ok | {:error, :not_found}
Removes a person's device.
@spec resend(Ecto.UUID.t()) :: :ok | {:error, :not_found | :waiting}
Sends a delivery again: one that failed or was skipped is scheduled now,
its attempts from nought, with "sent again" in the log; what is decided
at its time (the preferences, the conditions, the rules) is decided
again. {:error, :not_found} for an unknown one, {:error, :waiting}
for one that waits already.
@spec start_link(keyword()) :: Supervisor.on_start()
Starts Chime with the options of Chime.Config.
Subscribes the caller to a person's live news (see Live, above).
Brings archived notifications of a person back. Returns the counts.
@spec unsubscribe_url( Chime.Recipient.t() | String.t(), Chime.Notification.t() | String.t() ) :: String.t() | nil
The address to unsubscribe a person from a category's emails, for the
footer of a notifier's email; nil without :mail_url and
:secret_key_base in the config.
@spec update_device(String.t(), Ecto.UUID.t(), keyword() | map()) :: {:ok, Chime.Device.t()} | {:error, :not_found}
Turns a person's device on or off (enabled:), or names it (name:).