Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Upcoming

✅ Added

- Added support for `MessageReminder` feature, which allows users to bookmark or set reminders
for specific messages in a channel.

## 9.11.0

✅ Added
Expand Down
107 changes: 107 additions & 0 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,44 @@ class Channel {
);
}

/// Create a reminder for the given [messageId].
///
/// Optionally, provide a [remindAt] date to set when the reminder should
/// be triggered. If not provided, the reminder will be created as a
/// bookmark type instead.
Future<CreateReminderResponse> createReminder(
String messageId, {
DateTime? remindAt,
}) {
_checkInitialized();
return _client.createReminder(
messageId,
remindAt: remindAt,
);
}
Comment thread
xsahil03x marked this conversation as resolved.

/// Update an existing reminder with the given [reminderId].
///
/// Optionally, provide a [remindAt] date to set when the reminder should
/// be triggered. If not provided, the reminder will be updated as a
/// bookmark type instead.
Future<UpdateReminderResponse> updateReminder(
String messageId, {
DateTime? remindAt,
}) {
_checkInitialized();
return _client.updateReminder(
messageId,
remindAt: remindAt,
);
}

/// Remove the reminder for the given [messageId].
Future<EmptyResponse> deleteReminder(String messageId) {
_checkInitialized();
return _client.deleteReminder(messageId);
}

/// Send a reaction to this channel.
///
/// Set [enforceUnique] to true to remove the existing user reaction.
Expand Down Expand Up @@ -2093,6 +2131,16 @@ class ChannelClientState {

_listenUserStopWatching();

/* Start of reminder events */

_listenReminderCreated();

_listenReminderUpdated();

_listenReminderDeleted();

/* End of reminder events */

_startCleaningStaleTypingEvents();

_startCleaningStalePinnedMessages();
Expand Down Expand Up @@ -2578,6 +2626,65 @@ class ChannelClientState {
);
}

void _listenReminderCreated() {
_subscriptions.add(
_channel.on(EventType.reminderCreated).listen((event) {
final reminder = event.reminder;
if (reminder == null) return;

updateReminder(reminder);
}),
);
}

void _listenReminderUpdated() {
_subscriptions.add(
_channel.on(EventType.reminderUpdated).listen((event) {
final reminder = event.reminder;
if (reminder == null) return;

updateReminder(reminder);
}),
);
}

void _listenReminderDeleted() {
_subscriptions.add(
_channel.on(EventType.reminderDeleted).listen((event) {
final reminder = event.reminder;
if (reminder == null) return;

deleteReminder(reminder);
}),
);
}

/// Updates the [reminder] of the message if it exists.
void updateReminder(MessageReminder reminder) {
final messageId = reminder.messageId;
// TODO: Improve once we have support for parentId in reminders.
for (final message in [...messages, ...threads.values.flattened]) {
if (message.id == messageId) {
return updateMessage(
message.copyWith(reminder: reminder),
);
}
}
}

/// Deletes the [reminder] of the message if it exists.
void deleteReminder(MessageReminder reminder) {
final messageId = reminder.messageId;
// TODO: Improve once we have support for parentId in reminders.
for (final message in [...messages, ...threads.values.flattened]) {
if (message.id == messageId) {
return updateMessage(
message.copyWith(reminder: null),
);
}
}
}
Comment thread
xsahil03x marked this conversation as resolved.

void _listenReactionDeleted() {
_subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) {
final oldMessage =
Expand Down
56 changes: 48 additions & 8 deletions packages/stream_chat/lib/src/client/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import 'package:stream_chat/src/core/models/filter.dart';
import 'package:stream_chat/src/core/models/member.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/core/models/message_reminder.dart';
import 'package:stream_chat/src/core/models/own_user.dart';
import 'package:stream_chat/src/core/models/poll.dart';
import 'package:stream_chat/src/core/models/poll_option.dart';
Expand Down Expand Up @@ -1914,14 +1915,6 @@
required String channelId,
required String channelType,
}) {
final currentUser = state.currentUser;
if (currentUser == null) {
throw const StreamChatError(
'User is not set on client, '
'use `connectUser` or `connectAnonymousUser` instead',
);
}

return partialMemberUpdate(
channelId: channelId,
channelType: channelType,
Expand Down Expand Up @@ -1962,6 +1955,53 @@
);
}

/// Queries reminders for the current user.
///
/// Optionally, pass [filter], [sort] and [pagination] to filter, sort and
/// paginate the reminders.
Future<QueryRemindersResponse> queryReminders({

Check warning on line 1962 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1962

Added line #L1962 was not covered by tests
Filter? filter,
SortOrder<MessageReminder>? sort,
PaginationParams pagination = const PaginationParams(),
}) {
return _chatApi.reminders.queryReminders(

Check warning on line 1967 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1967

Added line #L1967 was not covered by tests
filter: filter,
sort: sort,
pagination: pagination,
);
}

/// Creates a reminder for the given [messageId].
///
/// Optionally, pass [remindAt] to set the reminder time.
Future<CreateReminderResponse> createReminder(

Check warning on line 1977 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1977

Added line #L1977 was not covered by tests
String messageId, {
DateTime? remindAt,
}) {
return _chatApi.reminders.createReminder(

Check warning on line 1981 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1981

Added line #L1981 was not covered by tests
messageId,
remindAt: remindAt,
);
}

/// Updates a reminder for the given [messageId].
///
/// Optionally, pass [remindAt] to set the new reminder time.
Future<UpdateReminderResponse> updateReminder(

Check warning on line 1990 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1990

Added line #L1990 was not covered by tests
String messageId, {
DateTime? remindAt,
}) {
return _chatApi.reminders.updateReminder(

Check warning on line 1994 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L1994

Added line #L1994 was not covered by tests
messageId,
remindAt: remindAt,
);
}

/// Deletes a reminder for the given [messageId].
Future<EmptyResponse> deleteReminder(String messageId) {
return _chatApi.reminders.deleteReminder(messageId);

Check warning on line 2002 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L2001-L2002

Added lines #L2001 - L2002 were not covered by tests
}

/// Closes the [_ws] connection and resets the [state]
/// If [flushChatPersistence] is true the client deletes all offline
/// user's data.
Expand Down
91 changes: 91 additions & 0 deletions packages/stream_chat/lib/src/core/api/reminders_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'dart:convert';

import 'package:stream_chat/src/core/api/requests.dart';
import 'package:stream_chat/src/core/api/responses.dart';
import 'package:stream_chat/src/core/api/sort_order.dart';
import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/src/core/models/filter.dart';
import 'package:stream_chat/src/core/models/message_reminder.dart';

/// Defines the api dedicated to message reminders operations
class RemindersApi {
/// Initialize a new reminders api
const RemindersApi(this._client);

final StreamHttpClient _client;

/// Retrieves the list of reminders for the current user.
///
/// Optionally, you can filter and sort the reminders using the [filter] and
/// [sort] parameters respectively. You can also paginate the results using
/// [pagination].
///
/// Returns a [QueryRemindersResponse] containing the list of reminders.
Future<QueryRemindersResponse> queryReminders({
Filter? filter,
SortOrder<MessageReminder>? sort,
PaginationParams? pagination,
}) async {
final response = await _client.post(
'/reminders/query',
data: jsonEncode({
if (filter != null) 'filter': filter,
if (sort != null) 'sort': sort,
if (pagination != null) ...pagination.toJson(),
}),
);

return QueryRemindersResponse.fromJson(response.data);
}

/// Creates a new reminder for the specified [messageId].
///
/// You can specify the time to remind using the [remindAt] parameter.
///
/// Returns a [CreateReminderResponse] containing the created reminder.
Future<CreateReminderResponse> createReminder(
String messageId, {
DateTime? remindAt,
}) async {
final response = await _client.post(
'/messages/$messageId/reminders',
data: jsonEncode({
if (remindAt != null) 'remind_at': remindAt.toUtc().toIso8601String(),
}),
);

return CreateReminderResponse.fromJson(response.data);
}

/// Updates an existing reminder for the specified [messageId].
///
/// You can change the reminder time using the [remindAt] parameter.
///
/// Returns an [UpdateReminderResponse] containing the updated reminder.
Future<UpdateReminderResponse> updateReminder(
String messageId, {
DateTime? remindAt,
}) async {
final response = await _client.patch(
'/messages/$messageId/reminders',
data: jsonEncode({
if (remindAt != null) 'remind_at': remindAt.toUtc().toIso8601String(),
}),
);

return UpdateReminderResponse.fromJson(response.data);
}

/// Deletes a reminder for the specified [messageId].
///
/// Returns an [EmptyResponse] indicating the deletion was successful.
Future<EmptyResponse> deleteReminder(
String messageId,
) async {
final response = await _client.delete(
'/messages/$messageId/reminders',
);

return EmptyResponse.fromJson(response.data);
}
}
38 changes: 38 additions & 0 deletions packages/stream_chat/lib/src/core/api/responses.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:stream_chat/src/core/models/draft.dart';
import 'package:stream_chat/src/core/models/event.dart';
import 'package:stream_chat/src/core/models/member.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/core/models/message_reminder.dart';
import 'package:stream_chat/src/core/models/poll.dart';
import 'package:stream_chat/src/core/models/poll_option.dart';
import 'package:stream_chat/src/core/models/poll_vote.dart';
Expand Down Expand Up @@ -764,3 +765,40 @@ class QueryDraftsResponse extends _BaseResponse {
static QueryDraftsResponse fromJson(Map<String, dynamic> json) =>
_$QueryDraftsResponseFromJson(json);
}

/// Base Model response for draft based api calls.
class MessageReminderResponse extends _BaseResponse {
/// Draft returned by the api call
late MessageReminder reminder;
}

/// Model response for [StreamChatClient.createReminder] api call
@JsonSerializable(createToJson: false)
class CreateReminderResponse extends MessageReminderResponse {
/// Create a new instance from a json
static CreateReminderResponse fromJson(Map<String, dynamic> json) =>
_$CreateReminderResponseFromJson(json);
}

/// Model response for [StreamChatClient.updateReminder] api call
@JsonSerializable(createToJson: false)
class UpdateReminderResponse extends MessageReminderResponse {
/// Create a new instance from a json
static UpdateReminderResponse fromJson(Map<String, dynamic> json) =>
_$UpdateReminderResponseFromJson(json);
}

/// Model response for [StreamChatClient.queryReminders] api call
@JsonSerializable(createToJson: false)
class QueryRemindersResponse extends _BaseResponse {
/// List of reminders returned by the query
@JsonKey(defaultValue: [])
late List<MessageReminder> reminders;

/// The next page token
late String? next;

/// Create a new instance from a json
static QueryRemindersResponse fromJson(Map<String, dynamic> json) =>
_$QueryRemindersResponseFromJson(json);
}
24 changes: 24 additions & 0 deletions packages/stream_chat/lib/src/core/api/responses.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading