Skip to content

Commit

Permalink
Merge branch 'master' into refresh-token
Browse files Browse the repository at this point in the history
  • Loading branch information
guerinoni authored Feb 6, 2024
2 parents 70c1380 + 6013bb2 commit 41ca5f0
Show file tree
Hide file tree
Showing 13 changed files with 482 additions and 89 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [8.16.0](https://github.com/GetStream/stream-chat-js/compare/v8.15.0...v8.16.0) (2024-02-05)


### Features

* threads 2.0 ([#1204](https://github.com/GetStream/stream-chat-js/issues/1204)) ([7bb64a8](https://github.com/GetStream/stream-chat-js/commit/7bb64a8fd3e11c043343c79935dfe77b97fc3d6e))


### Bug Fixes

* allow user_id in keystroke and stopTyping function ([#1221](https://github.com/GetStream/stream-chat-js/issues/1221)) ([c81586b](https://github.com/GetStream/stream-chat-js/commit/c81586b2f82d60814588b442106ddd228a4a35b1))

## [8.15.0](https://github.com/GetStream/stream-chat-js/compare/v8.14.5...v8.15.0) (2024-02-01)


### Features

* add batch unread endpoint ([#1212](https://github.com/GetStream/stream-chat-js/issues/1212)) ([5ea11db](https://github.com/GetStream/stream-chat-js/commit/5ea11dbaef4683ad0b2e58716190973180965294))
* add moderation types ([#1213](https://github.com/GetStream/stream-chat-js/issues/1213)) ([2a13b05](https://github.com/GetStream/stream-chat-js/commit/2a13b053c7006fd64f7187160ca5ea22e486d3a5))
* handle notification.mark_unread ([#1208](https://github.com/GetStream/stream-chat-js/issues/1208)) ([4d73838](https://github.com/GetStream/stream-chat-js/commit/4d73838c7e9aa174b1367d334a011d579b67573f))
* segments API v2 ([#1205](https://github.com/GetStream/stream-chat-js/issues/1205)) ([d2bf603](https://github.com/GetStream/stream-chat-js/commit/d2bf603673400bdcb57b75989825eb3cb638eee9))


### Bug Fixes

* missing types for datadog_info and blocklist type ([33f09b9](https://github.com/GetStream/stream-chat-js/commit/33f09b9fdfb9388312a6e715eba6fff0e5c956f6))
* prevent channel unread count reset to 0 when sending message and on new own or thread messages ([#1210](https://github.com/GetStream/stream-chat-js/issues/1210)) ([9cedf6f](https://github.com/GetStream/stream-chat-js/commit/9cedf6ff14e9cc039722371ada3b7e0a5a2fab05))

### [8.14.5](https://github.com/GetStream/stream-chat-js/compare/v8.14.4...v8.14.5) (2024-01-09)


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stream-chat",
"version": "8.14.5",
"version": "8.16.0",
"description": "JS SDK for the Stream Chat API",
"author": "GetStream",
"homepage": "https://getstream.io/chat/",
Expand Down
38 changes: 19 additions & 19 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,10 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
* @return {Promise<SendMessageAPIResponse<StreamChatGenerics>>} The Server Response
*/
async sendMessage(message: Message<StreamChatGenerics>, options?: SendMessageOptions) {
const sendMessageResponse = await this.getClient().post<SendMessageAPIResponse<StreamChatGenerics>>(
this._channelURL() + '/message',
{
message,
...options,
},
);

// Reset unreadCount to 0.
this.state.unreadCount = 0;

return sendMessageResponse;
return await this.getClient().post<SendMessageAPIResponse<StreamChatGenerics>>(this._channelURL() + '/message', {
message,
...options,
});
}

sendFile(
Expand Down Expand Up @@ -649,7 +641,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
* @see {@link https://getstream.io/chat/docs/typing_indicators/?language=js|Docs}
* @param {string} [parent_id] set this field to `message.id` to indicate that typing event is happening in a thread
*/
async keystroke(parent_id?: string) {
async keystroke(parent_id?: string, options?: { user_id: string }) {
if (!this.getConfig()?.typing_events) {
return;
}
Expand All @@ -663,6 +655,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
await this.sendEvent({
type: 'typing.start',
parent_id,
...(options || {}),
} as Event<StreamChatGenerics>);
}
}
Expand All @@ -672,7 +665,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
* @see {@link https://getstream.io/chat/docs/typing_indicators/?language=js|Docs}
* @param {string} [parent_id] set this field to `message.id` to indicate that typing event is happening in a thread
*/
async stopTyping(parent_id?: string) {
async stopTyping(parent_id?: string, options?: { user_id: string }) {
if (!this.getConfig()?.typing_events) {
return;
}
Expand All @@ -681,6 +674,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
await this.sendEvent({
type: 'typing.stop',
parent_id,
...(options || {}),
} as Event<StreamChatGenerics>);
}

Expand Down Expand Up @@ -1289,6 +1283,12 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
channelState.addPinnedMessage(event.message);
}

// do not increase the unread count - the back-end does not increase the count neither in the following cases:
// 1. the message is mine
// 2. the message is a thread reply from any user
const preventUnreadCountUpdate = ownMessage || isThreadMessage;
if (preventUnreadCountUpdate) break;

if (event.user?.id) {
for (const userId in channelState.read) {
if (userId === event.user.id) {
Expand All @@ -1303,9 +1303,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
}
}

if (ownMessage) {
channelState.unreadCount = 0;
} else if (this._countMessageAsUnread(event.message)) {
if (this._countMessageAsUnread(event.message)) {
channelState.unreadCount = channelState.unreadCount + 1;
}
}
Expand Down Expand Up @@ -1363,15 +1361,17 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
const ownMessage = event.user?.id === this.getClient().user?.id;
if (!(ownMessage && event.user)) break;

const unreadCount = event.unread_messages ?? 0;

channelState.read[event.user.id] = {
first_unread_message_id: event.first_unread_message_id,
last_read: new Date(event.last_read_at as string),
last_read_message_id: event.last_read_message_id,
user: event.user,
unread_messages: event.unread_messages ?? 0,
unread_messages: unreadCount,
};

channelState.unreadCount = event.unread_messages ?? 0;
channelState.unreadCount = unreadCount;
break;
}
case 'channel.updated':
Expand Down
60 changes: 2 additions & 58 deletions src/channel_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
UserResponse,
PendingMessageResponse,
} from './types';
import { addToMessageList } from './utils';

type ChannelReadStatus<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = Record<
string,
Expand Down Expand Up @@ -441,64 +442,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
sortBy: 'pinned_at' | 'created_at' = 'created_at',
addIfDoesNotExist = true,
) {
const addMessageToList = addIfDoesNotExist || timestampChanged;
let messageArr = messages;

// if created_at has changed, message should be filtered and re-inserted in correct order
// slow op but usually this only happens for a message inserted to state before actual response with correct timestamp
if (timestampChanged) {
messageArr = messageArr.filter((msg) => !(msg.id && message.id === msg.id));
}

// Get array length after filtering
const messageArrayLength = messageArr.length;

// for empty list just concat and return unless it's an update or deletion
if (messageArrayLength === 0 && addMessageToList) {
return messageArr.concat(message);
} else if (messageArrayLength === 0) {
return [...messageArr];
}

const messageTime = (message[sortBy] as Date).getTime();
const messageIsNewest = (messageArr[messageArrayLength - 1][sortBy] as Date).getTime() < messageTime;

// if message is newer than last item in the list concat and return unless it's an update or deletion
if (messageIsNewest && addMessageToList) {
return messageArr.concat(message);
} else if (messageIsNewest) {
return [...messageArr];
}

// find the closest index to push the new message
let left = 0;
let middle = 0;
let right = messageArrayLength - 1;
while (left <= right) {
middle = Math.floor((right + left) / 2);
if ((messageArr[middle][sortBy] as Date).getTime() <= messageTime) left = middle + 1;
else right = middle - 1;
}

// message already exists and not filtered due to timestampChanged, update and return
if (!timestampChanged && message.id) {
if (messageArr[left] && message.id === messageArr[left].id) {
messageArr[left] = message;
return [...messageArr];
}

if (messageArr[left - 1] && message.id === messageArr[left - 1].id) {
messageArr[left - 1] = message;
return [...messageArr];
}
}

// Do not add updated or deleted messages to the list if they do not already exist
// or have a timestamp change.
if (addMessageToList) {
messageArr.splice(left, 0, message);
}
return [...messageArr];
return addToMessageList(messages, message, timestampChanged, sortBy, addIfDoesNotExist);
}

/**
Expand Down
103 changes: 103 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import {
GetImportResponse,
GetMessageAPIResponse,
GetRateLimitsResponse,
QueryThreadsAPIResponse,
GetUnreadCountAPIResponse,
GetUnreadCountBatchAPIResponse,
ListChannelResponse,
Expand Down Expand Up @@ -163,8 +164,13 @@ import {
UserOptions,
UserResponse,
UserSort,
GetThreadAPIResponse,
PartialThreadUpdate,
QueryThreadsOptions,
GetThreadOptions,
} from './types';
import { InsightMetrics, postInsights } from './insights';
import { Thread } from './thread';

function isString(x: unknown): x is string {
return typeof x === 'string' || x instanceof String;
Expand Down Expand Up @@ -2595,6 +2601,103 @@ export class StreamChat<StreamChatGenerics extends ExtendableGenerics = DefaultG
);
}

/**
* queryThreads - returns the list of threads of current user.
*
* @param {QueryThreadsOptions} options Options object for pagination and limiting the participants and replies.
* @param {number} options.limit Limits the number of threads to be returned.
* @param {boolean} options.watch Subscribes the user to the channels of the threads.
* @param {number} options.participant_limit Limits the number of participants returned per threads.
* @param {number} options.reply_limit Limits the number of replies returned per threads.
*
* @returns {{ threads: Thread<StreamChatGenerics>[], next: string }} Returns the list of threads and the next cursor.
*/
async queryThreads(options?: QueryThreadsOptions) {
const opts = {
limit: 10,
participant_limit: 10,
reply_limit: 3,
watch: true,
...options,
};

const res = await this.post<QueryThreadsAPIResponse<StreamChatGenerics>>(this.baseURL + `/threads`, opts);

return {
threads: res.threads.map((thread) => new Thread(this, thread)),
next: res.next,
};
}

/**
* getThread - returns the thread of a message by its id.
*
* @param {string} messageId The message id
* @param {GetThreadOptions} options Options object for pagination and limiting the participants and replies.
* @param {boolean} options.watch Subscribes the user to the channel of the thread.
* @param {number} options.participant_limit Limits the number of participants returned per threads.
* @param {number} options.reply_limit Limits the number of replies returned per threads.
*
* @returns {Thread<StreamChatGenerics>} Returns the thread.
*/
async getThread(messageId: string, options: GetThreadOptions = {}) {
if (!messageId) {
throw Error('Please specify the message id when calling partialUpdateThread');
}

const opts = {
participant_limit: 100,
reply_limit: 3,
watch: true,
...options,
};

const res = await this.get<GetThreadAPIResponse<StreamChatGenerics>>(this.baseURL + `/threads/${messageId}`, opts);

return new Thread<StreamChatGenerics>(this, res.thread);
}

/**
* partialUpdateThread - updates the given thread
*
* @param {string} messageId The id of the thread message which needs to be updated.
* @param {PartialThreadUpdate} partialThreadObject should contain "set" or "unset" params for any of the thread's non-reserved fields.
*
* @returns {GetThreadAPIResponse<StreamChatGenerics>} Returns the updated thread.
*/
async partialUpdateThread(messageId: string, partialThreadObject: PartialThreadUpdate) {
if (!messageId) {
throw Error('Please specify the message id when calling partialUpdateThread');
}

// check for reserved fields from ThreadResponse type within partialThreadObject's set and unset.
// Throw error if any of the reserved field is found.
const reservedThreadFields = [
'created_at',
'id',
'last_message_at',
'type',
'updated_at',
'user',
'reply_count',
'participants',
'channel',
];

for (const key in { ...partialThreadObject.set, ...partialThreadObject.unset }) {
if (reservedThreadFields.includes(key)) {
throw Error(
`You cannot set ${key} field on Thread object. ${key} is reserved for server-side use. Please omit ${key} from your set object.`,
);
}
}

return await this.patch<GetThreadAPIResponse<StreamChatGenerics>>(
this.baseURL + `/threads/${messageId}`,
partialThreadObject,
);
}

getUserAgent() {
return (
this.userAgent || `stream-chat-javascript-client-${this.node ? 'node' : 'browser'}-${process.env.PKG_VERSION}`
Expand Down
1 change: 1 addition & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const EVENT_MAP = {
'notification.message_new': true,
'notification.mutes_updated': true,
'notification.removed_from_channel': true,
'notification.thread_message_new': true,
'reaction.deleted': true,
'reaction.new': true,
'reaction.updated': true,
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ export * from './client';
export * from './client_state';
export * from './channel';
export * from './channel_state';
export * from './thread';
export * from './connection';
export * from './events';
export * from './permissions';
export * from './signing';
export * from './token_manager';
export * from './insights';
export * from './types';
export { isOwnUser, chatCodes, logChatPromiseExecution } from './utils';
export { isOwnUser, chatCodes, logChatPromiseExecution, formatMessage } from './utils';
Loading

0 comments on commit 41ca5f0

Please sign in to comment.