Skip to content

Commit

Permalink
Merge pull request #488 from GetStream/before-message-hook
Browse files Browse the repository at this point in the history
feat: add before send/update message hooks
  • Loading branch information
szuperaz authored Oct 3, 2023
2 parents d9de1cc + 3ed514c commit 3c8b106
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 5 deletions.
70 changes: 70 additions & 0 deletions projects/stream-chat-angular/src/lib/channel.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { NotificationService } from './notification.service';
import {
AttachmentUpload,
DefaultStreamChatGenerics,
MessageInput,
StreamMessage,
} from './types';

Expand Down Expand Up @@ -1181,6 +1182,59 @@ describe('ChannelService', () => {
expect(messageCount).toEqual(prevMessageCount + 1);
});

it('should send message - #beforeSendMessage hook is provided', async () => {
await init();
let channel!: Channel<DefaultStreamChatGenerics>;
service.activeChannel$.pipe(first()).subscribe((c) => (channel = c!));
spyOn(channel, 'sendMessage').and.callThrough();
spyOn(channel.state, 'addMessageSorted').and.callThrough();
const text = 'Hi';
const attachments = [{ fallback: 'image.png', url: 'http://url/to/image' }];
const mentionedUsers = [{ id: 'sara', name: 'Sara' }];
const spy = jasmine.createSpy();
service.beforeSendMessage = spy;
spy.and.callFake((i: MessageInput) => {
i.customData = { custom: 'red' };
return i;
});
await service.sendMessage(text, attachments, mentionedUsers);

expect(channel.sendMessage).toHaveBeenCalledWith({
text,
attachments,
mentioned_users: ['sara'],
id: jasmine.any(String),
parent_id: undefined,
quoted_message_id: undefined,
custom: 'red',
});

expect(channel.state.addMessageSorted).toHaveBeenCalledWith(
jasmine.objectContaining({ custom: 'red' }),
true
);

spy.and.callFake((i: MessageInput) => {
i.text = 'censored';
return Promise.resolve(i);
});
await service.sendMessage(text, attachments, mentionedUsers);

expect(channel.sendMessage).toHaveBeenCalledWith({
text: 'censored',
attachments,
mentioned_users: ['sara'],
id: jasmine.any(String),
parent_id: undefined,
quoted_message_id: undefined,
});

expect(channel.state.addMessageSorted).toHaveBeenCalledWith(
jasmine.objectContaining({ text: 'censored' }),
true
);
});

it('should send action', async () => {
await init();
let channel!: Channel<DefaultStreamChatGenerics>;
Expand Down Expand Up @@ -1230,6 +1284,22 @@ describe('ChannelService', () => {
expect(mockChatClient.updateMessage).toHaveBeenCalledWith(message);
});

it('should update message - #beforeUpdateMessage is provided', async () => {
const message = mockMessage();
mockChatClient.updateMessage.and.resolveTo({ message });
const spy = jasmine.createSpy();
service.beforeUpdateMessage = spy;
spy.and.callFake((m: StreamMessage) => {
m.text = 'Testing beforeUpdateMessage hook';
return Promise.resolve(m);
});
await service.updateMessage(message);

expect(mockChatClient.updateMessage).toHaveBeenCalledWith(
jasmine.objectContaining({ text: 'Testing beforeUpdateMessage hook' })
);
});

it('should remove translation object before updating message', () => {
const message = mockMessage();
void service.updateMessage({
Expand Down
37 changes: 32 additions & 5 deletions projects/stream-chat-angular/src/lib/channel.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
AttachmentUpload,
ChannelQueryState,
DefaultStreamChatGenerics,
MessageInput,
MessageReactionType,
StreamMessage,
} from './types';
Expand Down Expand Up @@ -277,6 +278,18 @@ export class ChannelService<
messageDeleteConfirmationHandler?: (
message: StreamMessage<T>
) => Promise<boolean>;
/**
* The provided method will be called before a new message is sent to Stream's API. You can use this hook to tranfrom or enrich the message being sent.
*/
beforeSendMessage?: (
input: MessageInput<T>
) => MessageInput<T> | Promise<MessageInput<T>>;
/**
* The provided method will be called before a message is sent to Stream's API for update. You can use this hook to tranfrom or enrich the message being updated.
*/
beforeUpdateMessage?: (
message: StreamMessage<T>
) => StreamMessage<T> | Promise<StreamMessage<T>>;
private channelsSubject = new BehaviorSubject<Channel<T>[] | undefined>(
undefined
);
Expand Down Expand Up @@ -723,19 +736,30 @@ export class ChannelService<
quotedMessageId: string | undefined = undefined,
customData: undefined | Partial<T['messageType']> = undefined
) {
const preview = createMessagePreview(
this.chatClientService.chatClient.user!,
let input: MessageInput<T> = {
text,
attachments,
mentionedUsers,
parentId,
quotedMessageId,
customData
customData,
};
if (this.beforeSendMessage) {
input = await this.beforeSendMessage(input);
}
const preview = createMessagePreview(
this.chatClientService.chatClient.user!,
input.text,
input.attachments,
input.mentionedUsers,
input.parentId,
input.quotedMessageId,
input.customData
);
const channel = this.activeChannelSubject.getValue()!;
preview.readBy = [];
channel.state.addMessageSorted(preview, true);
const response = await this.sendMessageRequest(preview, customData);
const response = await this.sendMessageRequest(preview, input.customData);
return response;
}

Expand All @@ -761,8 +785,11 @@ export class ChannelService<
* @param message Mesage to be updated
*/
async updateMessage(message: StreamMessage<T>) {
const messageToUpdate = { ...message };
let messageToUpdate = { ...message };
delete messageToUpdate.i18n;
if (this.beforeUpdateMessage) {
messageToUpdate = await this.beforeUpdateMessage(messageToUpdate);
}
const response = await this.chatClientService.chatClient.updateMessage(
messageToUpdate as any as UpdatedMessage<T>
);
Expand Down
11 changes: 11 additions & 0 deletions projects/stream-chat-angular/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,14 @@ export type ChannelQueryState = {
// No type def from stream-chat
error?: unknown;
};

export type MessageInput<
T extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
> = {
text: string;
attachments: Attachment<T>[];
mentionedUsers: UserResponse<T>[];
parentId: string | undefined;
quotedMessageId: string | undefined;
customData: undefined | Partial<T['messageType']>;
};

0 comments on commit 3c8b106

Please sign in to comment.