# Reminder manager

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

`client.reminders` is the reminder manager, a `ReminderManager` from `stream-chat`. It keeps one `Reminder` per message, talks to the reminders API, and holds the user's reminder list. A reminder with a `remind_at` time is "remind me"; one without is "save for later". This page lists its API.

How reminders work end to end, with a "Saved for later" list and an alert when a reminder is due, is on [Reminders and save for later](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/reminders-and-save-for-later/).

## Getting the manager

The client creates the manager when it's constructed, so there is one per client: `client.reminders`. Don't construct a `ReminderManager` yourself.

The manager follows events only while its subscriptions run:

| Method                      | What it does                                                                                             |
| --------------------------- | -------------------------------------------------------------------------------------------------------- |
| `registerSubscriptions()`   | Starts applying the reminder and message events listed in [Events](#events). A second call does nothing. |
| `unregisterSubscriptions()` | Stops applying them.                                                                                     |
| `initTimers()`              | Starts the timer of every reminder in the state.                                                         |
| `clearTimers()`             | Stops the timer of every reminder in the state.                                                          |

The UI layer starts the subscriptions and the timers for you, and stops them when it's done. Code that uses the client without a UI layer calls these itself.

## State

### The manager's state

`client.reminders.state` is a store with one field:

| Field       | Type                    | What it holds                                                                     |
| ----------- | ----------------------- | --------------------------------------------------------------------------------- |
| `reminders` | `Map<string, Reminder>` | One `Reminder` per message, keyed by the message id. A new `Map` on every change. |

The map changes when a reminder is added or removed. A change to an existing reminder, such as a new `remind_at`, is published on that reminder's own `state`, not here. So following one message's reminder takes two subscriptions: one to find the `Reminder`, one to follow it.

```ts
const messageId = "message-1";

const unsubscribe = client.reminders.state.subscribeWithSelector(
  ({ reminders }) => ({ reminder: reminders.get(messageId) }),
  ({ reminder }) => {
    const remindAt = reminder?.remindAt;
    console.log(
      remindAt != null
        ? `Due ${nsToDate(remindAt).toLocaleString()}`
        : "No time set",
    );
  },
);
```

The getters of `client.reminders` return the value at the moment you read them, and don't tell you when it changes:

| Getter or method          | Returns                                   |
| ------------------------- | ----------------------------------------- |
| `reminders`               | The current `Map` of reminders.           |
| `getFromState(messageId)` | The message's `Reminder`, or `undefined`. |

### Each reminder's state

A `Reminder` has its own `state` store, of type `ReminderState`. The timestamps are unix nanoseconds, as the API sends them: convert them with `nsToDate()` or `nsToMs()`, as [Data conventions](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/data-conventions/) explains.

| Field         | Type                        | What it holds                                                                                                 |
| ------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `message_id`  | `string`                    | The message the reminder belongs to. It's also the reminder's id.                                             |
| `channel_cid` | `string`                    | The channel of the message.                                                                                   |
| `remind_at`   | `TimestampNS` or `null`     | When the reminder is due. `null` for a message saved for later.                                               |
| `timeLeftMs`  | `number` or `null`          | Milliseconds until `remind_at`, negative once it's due. `null` without a `remind_at`. The timer refreshes it. |
| `message`     | `MessageResponse` or `null` | The message, when the server sent it with the reminder.                                                       |
| `user_id`     | `string`                    | The user the reminder belongs to.                                                                             |
| `user`        | `UserResponse` or `null`    | That user, when the server sent it.                                                                           |
| `created_at`  | `TimestampNS`               | When the reminder was created.                                                                                |
| `updated_at`  | `TimestampNS`               | When it last changed.                                                                                         |

A `Reminder` also has:

| Member                                 | What it does                                                                                          |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `id`                                   | Getter: the message id.                                                                               |
| `remindAt`                             | Getter: `remind_at`.                                                                                  |
| `timeLeftMs`                           | Getter: `timeLeftMs`.                                                                                 |
| `timer`                                | The reminder's `ReminderTimer`. See [Timers](#timers).                                                |
| `setState(data: ReminderResponseData)` | Replaces the state with server data, then starts the timer, or stops it when there is no `remind_at`. |
| `refreshTimeLeft()`                    | Recomputes `timeLeftMs` from the clock.                                                               |
| `initTimer()`, `clearTimer()`          | Starts or stops the reminder's timer.                                                                 |

`timeLeftMs(remindAt)`, exported from `stream-chat`, computes the same value as the state field for any nanosecond timestamp.

### The reminder list

`client.reminders.paginator` is a `ReminderPaginator`: the current user's reminders across all channels, loaded page by page. It's one paginator per client. Its `state` has the fields every paginator in the state layer has (`items`, `isLoading`, `hasMoreHead`, `hasMoreTail`, `lastQueryError`), and [How pagination works](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/how-pagination-works/) explains them. The base paginator API is on [Channel manager and ChannelPaginator](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/#the-base-paginator).

| Member     | What it does                                                                                                                                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `items`    | Reminder records from the server (`ReminderResponseData`), each with its `message` and `channel`. They aren't `Reminder` instances.                                                                     |
| `filters`  | Getter and setter. Filters on `channel_cid`, `message_id`, `created_at`, `remind_at` and `expires_at`. `{ remind_at: { $exists: false } }` keeps the saved-for-later ones. Setting it empties the list. |
| `sort`     | Getter and setter, such as `[{ field: "remind_at", direction: 1 }]`. Setting it empties the list. Unset, the list is ordered by `created_at`.                                                           |
| `pageSize` | Reminders per page, 10 by default.                                                                                                                                                                      |
| `reload()` | Empties the list and loads the first page.                                                                                                                                                              |

While the manager's subscriptions run, every page the list loads also goes into the manager's state, so each loaded reminder gets its `Reminder`. The list itself holds what its queries returned: a reminder created or deleted later changes the manager's state, but not the list until it reloads.

## Methods

None of these is optimistic. The manager's state changes once the server answers.

| Method                                                         | Returns                          | What it does                                                                                                                                                                                  |
| -------------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createReminder({ message_id }, { remind_at?, expires_at? }?)` | `Promise<Reminder \| undefined>` | Creates the message's reminder and adds it to the state. Without `remind_at`, the message is saved for later.                                                                                 |
| `updateReminder({ message_id }, { remind_at?, expires_at? }?)` | `Promise<Reminder \| undefined>` | Changes the message's reminder and updates the state.                                                                                                                                         |
| `upsertReminder({ message_id }, { remind_at?, expires_at? }?)` | `Promise<Reminder \| undefined>` | Updates the reminder when the state has one for the message, and creates it otherwise. When the server answers that the reminder already exists, or that it doesn't, it makes the other call. |
| `deleteReminder(messageId: string)`                            | `Promise<void>`                  | Deletes the message's reminder, then removes it from the state and stops its timer.                                                                                                           |
| `queryNextReminders()`                                         | `Promise<void>`                  | Loads the next page of `paginator`, or the first page when nothing is loaded.                                                                                                                 |
| `queryPreviousReminders()`                                     | `Promise<void>`                  | Loads a page at the other end of `paginator`.                                                                                                                                                 |

`remind_at` and `expires_at` are `Date` objects in a request, and the methods take an optional third argument with request options. They resolve to the `Reminder` that was already in the state. For a message that had no reminder yet, they add a new `Reminder` and resolve to `undefined`: read it with `ReminderManager.getFromState(messageId)` afterwards.

```ts
// Remind me in an hour
await client.reminders.upsertReminder(
  { message_id: message.id },
  { remind_at: new Date(Date.now() + 60 * 60 * 1000) },
);

const reminder = client.reminders.getFromState(message.id);
```

These methods write the state without a request. The manager uses them itself, and they don't change anything on the server:

| Method                                | What it does                                                                                                 |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `upsertToState({ data, overwrite? })` | Adds a `Reminder` for `data.message_id`, or, with `overwrite` (`true` by default), updates the existing one. |
| `removeFromState(messageId)`          | Removes the message's reminder and stops its timer.                                                          |
| `hydrateState(messages)`              | Adds the `reminder` of each message that carries one.                                                        |

### Configuration

The manager has the `config`, `configState` and `ReminderManager.updateConfig()` that every configurable object has, and `ReminderManager.initializeConfig()`, which the client calls to rebuild the settings from `client.config`. In `client.config`, the settings are the `reminders` group of the `client` key:

| Setting                      | Default                                        | What it does                                                                  |
| ---------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------- |
| `scheduledOffsetsMs`         | 2 minutes, 30 minutes, 1, 2 and 8 hours, 1 day | The "remind me in…" choices a UI offers, as offsets from now in milliseconds. |
| `stopTimerRefreshBoundaryMs` | 2 weeks                                        | How long after its deadline a reminder's timer keeps refreshing `timeLeftMs`. |

The getters `scheduledOffsetsMs` and `stopTimerRefreshBoundaryMs` read the current values. A change to `stopTimerRefreshBoundaryMs` reaches the timers that are running. [Configuration tree](https://getstream.io/chat/docs/sdk/react/v15/reference/state/configuration-tree/#client) lists both, and [Configuration](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/configuration/) explains how to set them.

Whether reminders are offered in a channel is a channel setting: `channel.config.userMessageReminders.enabled`, which the channel type's `user_message_reminders` can turn off.

## Events

While its subscriptions run, the manager applies these events:

| Event                       | What changes                                                        |
| --------------------------- | ------------------------------------------------------------------- |
| `reminder.created`          | The reminder is added, or updated if it's in the state already.     |
| `reminder.updated`          | The reminder is updated, or added.                                  |
| `reminder.deleted`          | The reminder is removed.                                            |
| `message.deleted`           | The message's reminder is removed.                                  |
| `message.undeleted`         | The message's reminder is added back, when the message carries one. |
| `notification.reminder_due` | Nothing. The manager doesn't act on it.                             |

`ReminderManager.isReminderWsEventPayload(event)` tells whether an event is a `reminder.*` or `notification.reminder_due` event that carries a reminder.

Reminders also arrive with messages. A channel query and `client.queryChannels()` add the reminder of every message they load, with or without subscriptions.

## Timers

Each `Reminder` with a `remind_at` has a timer that refreshes `timeLeftMs` in its `state`. The timer starts when the reminder is created or gets a `remind_at`, and stops when the reminder is removed or loses it. It refreshes more often the closer the deadline is, before and after it:

| Time to or from the deadline | The timer fires                                                                   |
| ---------------------------- | --------------------------------------------------------------------------------- |
| More than a day              | Once a day.                                                                       |
| Between an hour and a day    | Once an hour.                                                                     |
| Between a minute and an hour | Once a minute.                                                                    |
| Less than a minute           | Once, when that time has passed. Before the deadline, that's the deadline itself. |

Once a reminder is overdue by `stopTimerRefreshBoundaryMs`, two weeks by default, its timer stops and `timeLeftMs` stays as it was last set.

A tick changes only that reminder's `timeLeftMs`. The manager's `reminders` map doesn't change, and nothing is published elsewhere. When the reminder comes due, the server sends `notification.reminder_due`, which the manager ignores. To alert the user, listen for that event, as [Alerting when a reminder is due](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/reminders-and-save-for-later/#alerting-when-a-reminder-is-due) shows.

`reminder.timer` is a `ReminderTimer`:

| Member                       | What it is                                                                            |
| ---------------------------- | ------------------------------------------------------------------------------------- |
| `stopRefreshBoundaryMs`      | The boundary this timer uses. The manager sets it from its configuration.             |
| `timeout`                    | The pending timeout, or `null` when the timer isn't running.                          |
| `getRefreshIntervalLength()` | The delay until the next tick, from the table above, or `null` without a `remind_at`. |
| `init()`                     | Schedules the next tick. Does nothing once the boundary has passed.                   |
| `clear()`                    | Cancels the pending tick.                                                             |

## See also

- [Reminders and save for later](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/reminders-and-save-for-later/): the feature end to end.
- [How pagination works](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/how-pagination-works/): the paginator state the reminder paginator shares.
- [Reactive state](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/reactive-state/): subscribing to stores.
- [Message reminders in the JavaScript client](https://getstream.io/chat/docs/javascript/message-reminders/): the reminders API and the server setting.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/reference/state/reminder-manager/](https://getstream.io/chat/docs/sdk/react/v15/reference/state/reminder-manager/).