stream-chat 9.15.0 → 9.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.browser.cjs +382 -229
- package/dist/cjs/index.browser.cjs.map +4 -4
- package/dist/cjs/index.node.cjs +383 -229
- package/dist/cjs/index.node.cjs.map +4 -4
- package/dist/esm/index.js +421 -274
- package/dist/esm/index.js.map +4 -4
- package/dist/types/channel_state.d.ts +1 -1
- package/dist/types/client.d.ts +1 -1
- package/dist/types/events.d.ts +1 -0
- package/dist/types/pagination/FilterBuilder.d.ts +41 -0
- package/dist/types/pagination/index.d.ts +1 -0
- package/dist/types/search/BaseSearchSource.d.ts +1 -1
- package/dist/types/search/ChannelSearchSource.d.ts +11 -3
- package/dist/types/search/MessageSearchSource.d.ts +30 -2
- package/dist/types/search/UserSearchSource.d.ts +10 -3
- package/dist/types/search/index.d.ts +3 -3
- package/dist/types/types.d.ts +4 -0
- package/dist/types/utils.d.ts +86 -1
- package/package.json +2 -2
- package/src/channel.ts +9 -0
- package/src/channel_state.ts +24 -48
- package/src/client.ts +19 -3
- package/src/events.ts +1 -0
- package/src/pagination/FilterBuilder.ts +104 -0
- package/src/pagination/index.ts +1 -0
- package/src/search/BaseSearchSource.ts +1 -1
- package/src/search/ChannelSearchSource.ts +47 -8
- package/src/search/MessageSearchSource.ts +139 -21
- package/src/search/UserSearchSource.ts +47 -10
- package/src/search/index.ts +3 -3
- package/src/types.ts +4 -0
- package/src/utils.ts +75 -0
|
@@ -175,7 +175,7 @@ export declare class ChannelState {
|
|
|
175
175
|
* @param {UserResponse} user
|
|
176
176
|
* @param {boolean} hardDelete
|
|
177
177
|
*/
|
|
178
|
-
deleteUserMessages: (user: UserResponse, hardDelete?: boolean) => void;
|
|
178
|
+
deleteUserMessages: (user: UserResponse, hardDelete?: boolean, deletedAt?: LocalMessage["deleted_at"]) => void;
|
|
179
179
|
/**
|
|
180
180
|
* filterErrorMessages - Removes error messages from the channel state.
|
|
181
181
|
*
|
package/dist/types/client.d.ts
CHANGED
|
@@ -432,7 +432,7 @@ export declare class StreamChat {
|
|
|
432
432
|
* @param {UserResponse} user
|
|
433
433
|
* @param {boolean} hardDelete
|
|
434
434
|
*/
|
|
435
|
-
_deleteUserMessageReference: (user: UserResponse, hardDelete?: boolean) => void;
|
|
435
|
+
_deleteUserMessageReference: (user: UserResponse, hardDelete?: boolean, deletedAt?: LocalMessage["deleted_at"]) => void;
|
|
436
436
|
/**
|
|
437
437
|
* @private
|
|
438
438
|
*
|
package/dist/types/events.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare const EVENT_MAP: {
|
|
|
45
45
|
'typing.stop': boolean;
|
|
46
46
|
'user.banned': boolean;
|
|
47
47
|
'user.deleted': boolean;
|
|
48
|
+
'user.messages.deleted': boolean;
|
|
48
49
|
'user.presence.changed': boolean;
|
|
49
50
|
'user.unbanned': boolean;
|
|
50
51
|
'user.unread_message_reminder': boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { StateStore } from '../store';
|
|
2
|
+
import type { ArrayOneOrMore, ArrayTwoOrMore, QueryFilter } from '../types';
|
|
3
|
+
type ElementType<T> = T extends (infer U)[] ? U : T;
|
|
4
|
+
export type ExtendedQueryFilter<T = string> = QueryFilter<T> & {
|
|
5
|
+
$autocomplete?: T extends string ? string : never;
|
|
6
|
+
$contains?: ElementType<T>;
|
|
7
|
+
$in?: ElementType<T>[];
|
|
8
|
+
$q?: T extends string ? string : never;
|
|
9
|
+
};
|
|
10
|
+
export type ExtendedQueryLogicalOperators<T> = {
|
|
11
|
+
$and?: ArrayOneOrMore<ExtendedQueryFilters<T>>;
|
|
12
|
+
$nor?: ArrayOneOrMore<ExtendedQueryFilters<T>>;
|
|
13
|
+
$or?: ArrayTwoOrMore<ExtendedQueryFilters<T>>;
|
|
14
|
+
};
|
|
15
|
+
export type ExtendedQueryFilters<T> = {
|
|
16
|
+
[K in keyof T]?: ExtendedQueryFilter<T[K]>;
|
|
17
|
+
} & ExtendedQueryLogicalOperators<T>;
|
|
18
|
+
export type FilterBuilderGenerators<TFilters, TContext extends Record<string, unknown> = {}> = {
|
|
19
|
+
[K in string]: {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
generate: (context: TContext) => Partial<TFilters> | null;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export type FilterBuilderOptions<TFilters, TContext extends Record<string, unknown>> = {
|
|
25
|
+
initialFilterConfig?: FilterBuilderGenerators<TFilters, TContext>;
|
|
26
|
+
initialContext?: TContext;
|
|
27
|
+
};
|
|
28
|
+
export declare class FilterBuilder<TFilters, TContext extends Record<string, unknown> = {}> {
|
|
29
|
+
filterConfig: StateStore<FilterBuilderGenerators<TFilters, TContext>>;
|
|
30
|
+
context: StateStore<TContext>;
|
|
31
|
+
constructor(params?: FilterBuilderOptions<TFilters, TContext>);
|
|
32
|
+
updateFilterConfig(config: Partial<FilterBuilderGenerators<TFilters, TContext>>): void;
|
|
33
|
+
enableFilter(filterKey: keyof FilterBuilderGenerators<TFilters, TContext>): void;
|
|
34
|
+
disableFilter(filterKey: keyof FilterBuilderGenerators<TFilters, TContext>): void;
|
|
35
|
+
updateContext(newContext: Partial<TContext>): void;
|
|
36
|
+
buildFilters(params?: {
|
|
37
|
+
context?: Partial<TContext>;
|
|
38
|
+
baseFilters?: Partial<TFilters>;
|
|
39
|
+
}): Partial<TFilters>;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -35,7 +35,7 @@ export interface SearchSourceSync<T = any> extends ISearchSource<T> {
|
|
|
35
35
|
}
|
|
36
36
|
declare abstract class BaseSearchSourceBase<T> implements ISearchSource<T> {
|
|
37
37
|
state: StateStore<SearchSourceState<T>>;
|
|
38
|
-
|
|
38
|
+
pageSize: number;
|
|
39
39
|
abstract readonly type: SearchSourceType;
|
|
40
40
|
protected constructor(options?: SearchSourceOptions);
|
|
41
41
|
get lastQueryError(): Error | undefined;
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { BaseSearchSource } from './BaseSearchSource';
|
|
2
|
+
import type { FilterBuilderOptions } from '../pagination';
|
|
3
|
+
import { FilterBuilder } from '../pagination';
|
|
2
4
|
import type { Channel } from '../channel';
|
|
3
5
|
import type { StreamChat } from '../client';
|
|
4
6
|
import type { ChannelFilters, ChannelOptions, ChannelSort } from '../types';
|
|
5
7
|
import type { SearchSourceOptions } from './types';
|
|
6
|
-
|
|
8
|
+
type CustomContext = Record<string, unknown>;
|
|
9
|
+
export type ChannelSearchSourceFilterBuilderContext<C extends CustomContext = CustomContext> = {
|
|
10
|
+
searchQuery?: string;
|
|
11
|
+
} & C;
|
|
12
|
+
export declare class ChannelSearchSource<TFilterContext extends CustomContext = CustomContext> extends BaseSearchSource<Channel> {
|
|
7
13
|
readonly type = "channels";
|
|
8
|
-
|
|
14
|
+
client: StreamChat;
|
|
9
15
|
filters: ChannelFilters | undefined;
|
|
10
16
|
sort: ChannelSort | undefined;
|
|
11
17
|
searchOptions: Omit<ChannelOptions, 'limit' | 'offset'> | undefined;
|
|
12
|
-
|
|
18
|
+
filterBuilder: FilterBuilder<ChannelFilters, ChannelSearchSourceFilterBuilderContext<TFilterContext>>;
|
|
19
|
+
constructor(client: StreamChat, options?: SearchSourceOptions, filterBuilderOptions?: FilterBuilderOptions<ChannelFilters, ChannelSearchSourceFilterBuilderContext<TFilterContext>>);
|
|
13
20
|
protected query(searchQuery: string): Promise<{
|
|
14
21
|
items: Channel[];
|
|
15
22
|
}>;
|
|
16
23
|
protected filterQueryResults(items: Channel[]): Channel[];
|
|
17
24
|
}
|
|
25
|
+
export {};
|
|
@@ -2,7 +2,31 @@ import { BaseSearchSource } from './BaseSearchSource';
|
|
|
2
2
|
import type { ChannelFilters, ChannelOptions, ChannelSort, MessageFilters, MessageResponse, SearchMessageSort } from '../types';
|
|
3
3
|
import type { StreamChat } from '../client';
|
|
4
4
|
import type { SearchSourceOptions } from './types';
|
|
5
|
-
|
|
5
|
+
import { FilterBuilder, type FilterBuilderOptions } from '../pagination';
|
|
6
|
+
type CustomContext = Record<string, unknown>;
|
|
7
|
+
type BuiltInContexts = {
|
|
8
|
+
messageSearchChannel: {
|
|
9
|
+
searchQuery?: string;
|
|
10
|
+
};
|
|
11
|
+
messageSearch: {
|
|
12
|
+
searchQuery?: string;
|
|
13
|
+
};
|
|
14
|
+
channelQuery: {
|
|
15
|
+
cids?: string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
type MergeContext<B extends Record<string, unknown>, C extends CustomContext | undefined> = B & (C extends object ? C : {});
|
|
19
|
+
type MessageSearchSourceContexts = Partial<{
|
|
20
|
+
messageSearchChannelContext: Record<string, unknown>;
|
|
21
|
+
messageSearchContext: Record<string, unknown>;
|
|
22
|
+
channelQueryContext: Record<string, unknown>;
|
|
23
|
+
}>;
|
|
24
|
+
export type MessageSearchSourceFilterBuilderOptions<TContexts extends MessageSearchSourceContexts = {}> = Partial<{
|
|
25
|
+
messageSearchChannel: FilterBuilderOptions<ChannelFilters, MergeContext<BuiltInContexts['messageSearchChannel'], TContexts['messageSearchChannelContext']>>;
|
|
26
|
+
messageSearch: FilterBuilderOptions<MessageFilters, MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>>;
|
|
27
|
+
channelQuery: FilterBuilderOptions<ChannelFilters, MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>>;
|
|
28
|
+
}>;
|
|
29
|
+
export declare class MessageSearchSource<TContexts extends MessageSearchSourceContexts = {}> extends BaseSearchSource<MessageResponse> {
|
|
6
30
|
readonly type = "messages";
|
|
7
31
|
private client;
|
|
8
32
|
messageSearchChannelFilters: ChannelFilters | undefined;
|
|
@@ -11,7 +35,10 @@ export declare class MessageSearchSource extends BaseSearchSource<MessageRespons
|
|
|
11
35
|
channelQueryFilters: ChannelFilters | undefined;
|
|
12
36
|
channelQuerySort: ChannelSort | undefined;
|
|
13
37
|
channelQueryOptions: Omit<ChannelOptions, 'limit' | 'offset'> | undefined;
|
|
14
|
-
|
|
38
|
+
messageSearchChannelFilterBuilder: FilterBuilder<ChannelFilters, MergeContext<BuiltInContexts['messageSearchChannel'], TContexts['messageSearchChannelContext']>>;
|
|
39
|
+
messageSearchFilterBuilder: FilterBuilder<MessageFilters, MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>>;
|
|
40
|
+
channelQueryFilterBuilder: FilterBuilder<ChannelFilters, MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>>;
|
|
41
|
+
constructor(client: StreamChat, options?: SearchSourceOptions, filterBuilderOptions?: MessageSearchSourceFilterBuilderOptions<TContexts>);
|
|
15
42
|
protected query(searchQuery: string): Promise<{
|
|
16
43
|
items: never[];
|
|
17
44
|
next?: undefined;
|
|
@@ -21,3 +48,4 @@ export declare class MessageSearchSource extends BaseSearchSource<MessageRespons
|
|
|
21
48
|
}>;
|
|
22
49
|
protected filterQueryResults(items: MessageResponse[]): MessageResponse[];
|
|
23
50
|
}
|
|
51
|
+
export {};
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { BaseSearchSource } from './BaseSearchSource';
|
|
2
|
+
import { FilterBuilder, type FilterBuilderOptions } from '../pagination';
|
|
2
3
|
import type { StreamChat } from '../client';
|
|
3
4
|
import type { UserFilters, UserOptions, UserResponse, UserSort } from '../types';
|
|
4
5
|
import type { SearchSourceOptions } from './types';
|
|
5
|
-
|
|
6
|
+
type CustomContext = Record<string, unknown>;
|
|
7
|
+
export type UserSearchSourceFilterBuilderContext<C extends CustomContext = CustomContext> = {
|
|
8
|
+
searchQuery?: string;
|
|
9
|
+
} & C;
|
|
10
|
+
export declare class UserSearchSource<TFilterContext extends CustomContext = CustomContext> extends BaseSearchSource<UserResponse> {
|
|
6
11
|
readonly type = "users";
|
|
7
|
-
|
|
12
|
+
client: StreamChat;
|
|
8
13
|
filters: UserFilters | undefined;
|
|
9
14
|
sort: UserSort | undefined;
|
|
10
15
|
searchOptions: Omit<UserOptions, 'limit' | 'offset'> | undefined;
|
|
11
|
-
|
|
16
|
+
filterBuilder: FilterBuilder<UserFilters, UserSearchSourceFilterBuilderContext<TFilterContext>>;
|
|
17
|
+
constructor(client: StreamChat, options?: SearchSourceOptions, filterBuilderOptions?: FilterBuilderOptions<UserFilters, UserSearchSourceFilterBuilderContext<TFilterContext>>);
|
|
12
18
|
protected query(searchQuery: string): Promise<{
|
|
13
19
|
items: UserResponse[];
|
|
14
20
|
}>;
|
|
15
21
|
protected filterQueryResults(items: UserResponse[]): UserResponse[];
|
|
16
22
|
}
|
|
23
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './BaseSearchSource';
|
|
2
2
|
export * from './SearchController';
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
3
|
+
export * from './UserSearchSource';
|
|
4
|
+
export * from './ChannelSearchSource';
|
|
5
|
+
export * from './MessageSearchSource';
|
|
6
6
|
export * from './types';
|
package/dist/types/types.d.ts
CHANGED
|
@@ -236,6 +236,7 @@ export type ChannelResponse = CustomChannelData & {
|
|
|
236
236
|
last_message_at?: string;
|
|
237
237
|
member_count?: number;
|
|
238
238
|
members?: ChannelMemberResponse[];
|
|
239
|
+
message_count?: number;
|
|
239
240
|
muted?: boolean;
|
|
240
241
|
mute_expires_at?: string;
|
|
241
242
|
own_capabilities?: string[];
|
|
@@ -1250,6 +1251,8 @@ export type Event = CustomEventData & {
|
|
|
1250
1251
|
ai_message?: string;
|
|
1251
1252
|
ai_state?: AIState;
|
|
1252
1253
|
channel?: ChannelResponse;
|
|
1254
|
+
channel_custom?: CustomChannelData;
|
|
1255
|
+
channel_member_count?: number;
|
|
1253
1256
|
channel_id?: string;
|
|
1254
1257
|
channel_type?: string;
|
|
1255
1258
|
cid?: string;
|
|
@@ -1837,6 +1840,7 @@ export type ChannelConfigFields = {
|
|
|
1837
1840
|
replies?: boolean;
|
|
1838
1841
|
search?: boolean;
|
|
1839
1842
|
shared_locations?: boolean;
|
|
1843
|
+
count_messages?: boolean;
|
|
1840
1844
|
typing_events?: boolean;
|
|
1841
1845
|
uploads?: boolean;
|
|
1842
1846
|
url_enrichment?: boolean;
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import FormData from 'form-data';
|
|
2
|
-
import type { AscDesc, ChannelFilters, ChannelQueryOptions, ChannelSort, ChannelSortBase, LocalMessage, Logger, Message, MessagePaginationOptions, MessageResponse, MessageResponseBase, MessageSet, OwnUserResponse, PromoteChannelParams, UpdatedMessage, UserResponse } from './types';
|
|
2
|
+
import type { AscDesc, ChannelFilters, ChannelQueryOptions, ChannelSort, ChannelSortBase, LocalMessage, LocalMessageBase, Logger, Message, MessagePaginationOptions, MessageResponse, MessageResponseBase, MessageSet, OwnUserResponse, PromoteChannelParams, ReactionGroupResponse, UpdatedMessage, UserResponse } from './types';
|
|
3
3
|
import type { StreamChat } from './client';
|
|
4
4
|
import type { Channel } from './channel';
|
|
5
5
|
import type { AxiosRequestConfig } from 'axios';
|
|
@@ -63,6 +63,91 @@ export declare function formatMessage(message: MessageResponse | MessageResponse
|
|
|
63
63
|
export declare function unformatMessage(message: LocalMessage): MessageResponse;
|
|
64
64
|
export declare const localMessageToNewMessagePayload: (localMessage: LocalMessage) => Message;
|
|
65
65
|
export declare const toUpdatedMessagePayload: (message: LocalMessage | Partial<MessageResponse>) => UpdatedMessage;
|
|
66
|
+
export declare const toDeletedMessage: ({ message, deletedAt, hardDelete, }: {
|
|
67
|
+
message: LocalMessage | LocalMessageBase;
|
|
68
|
+
deletedAt: LocalMessage["deleted_at"];
|
|
69
|
+
hardDelete: boolean;
|
|
70
|
+
}) => {
|
|
71
|
+
attachments: never[];
|
|
72
|
+
cid: string | undefined;
|
|
73
|
+
created_at: Date;
|
|
74
|
+
deleted_at: Date | null;
|
|
75
|
+
id: string;
|
|
76
|
+
latest_reactions: never[];
|
|
77
|
+
mentioned_users: never[];
|
|
78
|
+
own_reactions: never[];
|
|
79
|
+
parent_id: string | undefined;
|
|
80
|
+
reply_count: number | undefined;
|
|
81
|
+
status: string;
|
|
82
|
+
thread_participants: UserResponse[] | undefined;
|
|
83
|
+
type: "deleted";
|
|
84
|
+
updated_at: Date;
|
|
85
|
+
user: UserResponse | null | undefined;
|
|
86
|
+
} | {
|
|
87
|
+
attachments: never[];
|
|
88
|
+
type: string;
|
|
89
|
+
deleted_at: Date | null;
|
|
90
|
+
id: string;
|
|
91
|
+
html?: string | undefined;
|
|
92
|
+
mml?: string | undefined;
|
|
93
|
+
parent_id?: string | undefined;
|
|
94
|
+
pin_expires?: string | null | undefined;
|
|
95
|
+
pinned?: boolean | undefined;
|
|
96
|
+
poll_id?: string | undefined;
|
|
97
|
+
quoted_message_id?: string | undefined;
|
|
98
|
+
restricted_visibility?: string[] | undefined;
|
|
99
|
+
show_in_channel?: boolean | undefined;
|
|
100
|
+
silent?: boolean | undefined;
|
|
101
|
+
text?: string | undefined;
|
|
102
|
+
user?: (UserResponse | null) | undefined;
|
|
103
|
+
user_id?: string | undefined;
|
|
104
|
+
args?: string | undefined;
|
|
105
|
+
before_message_send_failed?: boolean | undefined;
|
|
106
|
+
channel?: import("./types").ChannelResponse | undefined;
|
|
107
|
+
cid?: string | undefined;
|
|
108
|
+
command?: string | undefined;
|
|
109
|
+
command_info?: {
|
|
110
|
+
name?: string;
|
|
111
|
+
} | undefined;
|
|
112
|
+
deleted_reply_count?: number | undefined;
|
|
113
|
+
i18n?: (import("./types").RequireAtLeastOne<Record<`${import("./types").TranslationLanguages}_text`, string>> & {
|
|
114
|
+
language: import("./types").TranslationLanguages;
|
|
115
|
+
}) | undefined;
|
|
116
|
+
latest_reactions?: import("./types").ReactionResponse[] | undefined;
|
|
117
|
+
mentioned_users?: UserResponse[] | undefined;
|
|
118
|
+
message_text_updated_at?: string | undefined;
|
|
119
|
+
moderation?: import("./types").ModerationResponse | undefined;
|
|
120
|
+
moderation_details?: import("./types").ModerationDetailsResponse | undefined;
|
|
121
|
+
own_reactions?: import("./types").ReactionResponse[] | null | undefined;
|
|
122
|
+
pinned_by?: (UserResponse | null) | undefined;
|
|
123
|
+
poll?: import("./types").PollResponse | undefined;
|
|
124
|
+
reaction_counts?: {
|
|
125
|
+
[key: string]: number;
|
|
126
|
+
} | null | undefined;
|
|
127
|
+
reaction_groups?: {
|
|
128
|
+
[key: string]: ReactionGroupResponse;
|
|
129
|
+
} | null | undefined;
|
|
130
|
+
reaction_scores?: {
|
|
131
|
+
[key: string]: number;
|
|
132
|
+
} | null | undefined;
|
|
133
|
+
reminder?: import("./types").ReminderResponseBase | undefined;
|
|
134
|
+
reply_count?: number | undefined;
|
|
135
|
+
shadowed?: boolean | undefined;
|
|
136
|
+
shared_location?: import("./types").SharedLocationResponse | undefined;
|
|
137
|
+
thread_participants?: UserResponse[] | undefined;
|
|
138
|
+
created_at: Date;
|
|
139
|
+
pinned_at: Date | null;
|
|
140
|
+
status: string;
|
|
141
|
+
updated_at: Date;
|
|
142
|
+
error?: import("./types").ErrorFromResponse<import("./types").APIErrorResponse>;
|
|
143
|
+
quoted_message?: LocalMessageBase;
|
|
144
|
+
};
|
|
145
|
+
export declare const deleteUserMessages: ({ messages, user, hardDelete, deletedAt, }: {
|
|
146
|
+
messages: Array<LocalMessage>;
|
|
147
|
+
user: UserResponse;
|
|
148
|
+
hardDelete: boolean;
|
|
149
|
+
deletedAt: LocalMessage["deleted_at"];
|
|
150
|
+
}) => void;
|
|
66
151
|
export declare const findIndexInSortedArray: <T, L>({ needle, sortedArray, selectKey, selectValueToCompare, sortDirection, }: {
|
|
67
152
|
needle: T;
|
|
68
153
|
sortedArray: readonly T[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.17.0",
|
|
4
4
|
"description": "JS SDK for the Stream Chat API",
|
|
5
5
|
"homepage": "https://getstream.io/chat/",
|
|
6
6
|
"author": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"form-data": "^4.0.4",
|
|
57
57
|
"isomorphic-ws": "^5.0.0",
|
|
58
58
|
"jsonwebtoken": "^9.0.2",
|
|
59
|
-
"linkifyjs": "^4.2
|
|
59
|
+
"linkifyjs": "^4.3.2",
|
|
60
60
|
"ws": "^8.18.1"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
package/src/channel.ts
CHANGED
|
@@ -1903,6 +1903,15 @@ export class Channel {
|
|
|
1903
1903
|
}
|
|
1904
1904
|
}
|
|
1905
1905
|
break;
|
|
1906
|
+
case 'user.messages.deleted':
|
|
1907
|
+
if (event.user) {
|
|
1908
|
+
this.state.deleteUserMessages(
|
|
1909
|
+
event.user,
|
|
1910
|
+
!!event.hard_delete,
|
|
1911
|
+
new Date(event.created_at ?? Date.now()),
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1914
|
+
break;
|
|
1906
1915
|
case 'message.new':
|
|
1907
1916
|
if (event.message) {
|
|
1908
1917
|
/* if message belongs to current user, always assume timestamp is changed to filter it out and add again to avoid duplication */
|
package/src/channel_state.ts
CHANGED
|
@@ -11,7 +11,11 @@ import type {
|
|
|
11
11
|
ReactionResponse,
|
|
12
12
|
UserResponse,
|
|
13
13
|
} from './types';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
deleteUserMessages as _deleteUserMessages,
|
|
16
|
+
addToMessageList,
|
|
17
|
+
formatMessage,
|
|
18
|
+
} from './utils';
|
|
15
19
|
import { DEFAULT_MESSAGE_SET_PAGINATION } from './constants';
|
|
16
20
|
|
|
17
21
|
type ChannelReadStatus = Record<
|
|
@@ -712,58 +716,30 @@ export class ChannelState {
|
|
|
712
716
|
* @param {UserResponse} user
|
|
713
717
|
* @param {boolean} hardDelete
|
|
714
718
|
*/
|
|
715
|
-
deleteUserMessages = (
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
) =>
|
|
721
|
-
|
|
722
|
-
const m = messages[i];
|
|
723
|
-
if (m.user?.id !== user.id) {
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
if (hardDelete) {
|
|
728
|
-
/**
|
|
729
|
-
* In case of hard delete, we need to strip down all text, html,
|
|
730
|
-
* attachments and all the custom properties on message
|
|
731
|
-
*/
|
|
732
|
-
messages[i] = {
|
|
733
|
-
cid: m.cid,
|
|
734
|
-
created_at: m.created_at,
|
|
735
|
-
deleted_at: user.deleted_at,
|
|
736
|
-
id: m.id,
|
|
737
|
-
latest_reactions: [],
|
|
738
|
-
mentioned_users: [],
|
|
739
|
-
own_reactions: [],
|
|
740
|
-
parent_id: m.parent_id,
|
|
741
|
-
reply_count: m.reply_count,
|
|
742
|
-
status: m.status,
|
|
743
|
-
thread_participants: m.thread_participants,
|
|
744
|
-
type: 'deleted',
|
|
745
|
-
updated_at: m.updated_at,
|
|
746
|
-
user: m.user,
|
|
747
|
-
} as unknown as ReturnType<ChannelState['formatMessage']>;
|
|
748
|
-
} else {
|
|
749
|
-
messages[i] = {
|
|
750
|
-
...m,
|
|
751
|
-
type: 'deleted',
|
|
752
|
-
deleted_at: user.deleted_at ? new Date(user.deleted_at) : null,
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
this.messageSets.forEach((set) =>
|
|
759
|
-
_deleteUserMessages(set.messages, user, hardDelete),
|
|
719
|
+
deleteUserMessages = (
|
|
720
|
+
user: UserResponse,
|
|
721
|
+
hardDelete = false,
|
|
722
|
+
deletedAt?: LocalMessage['deleted_at'],
|
|
723
|
+
) => {
|
|
724
|
+
this.messageSets.forEach(({ messages }) =>
|
|
725
|
+
_deleteUserMessages({ messages, user, hardDelete, deletedAt: deletedAt ?? null }),
|
|
760
726
|
);
|
|
761
727
|
|
|
762
728
|
for (const parentId in this.threads) {
|
|
763
|
-
_deleteUserMessages(
|
|
729
|
+
_deleteUserMessages({
|
|
730
|
+
messages: this.threads[parentId],
|
|
731
|
+
user,
|
|
732
|
+
hardDelete,
|
|
733
|
+
deletedAt: deletedAt ?? null,
|
|
734
|
+
});
|
|
764
735
|
}
|
|
765
736
|
|
|
766
|
-
_deleteUserMessages(
|
|
737
|
+
_deleteUserMessages({
|
|
738
|
+
messages: this.pinnedMessages,
|
|
739
|
+
user,
|
|
740
|
+
hardDelete,
|
|
741
|
+
deletedAt: deletedAt ?? null,
|
|
742
|
+
});
|
|
767
743
|
};
|
|
768
744
|
|
|
769
745
|
/**
|
package/src/client.ts
CHANGED
|
@@ -1404,7 +1404,11 @@ export class StreamChat {
|
|
|
1404
1404
|
* @param {UserResponse} user
|
|
1405
1405
|
* @param {boolean} hardDelete
|
|
1406
1406
|
*/
|
|
1407
|
-
_deleteUserMessageReference = (
|
|
1407
|
+
_deleteUserMessageReference = (
|
|
1408
|
+
user: UserResponse,
|
|
1409
|
+
hardDelete = false,
|
|
1410
|
+
deletedAt?: LocalMessage['deleted_at'],
|
|
1411
|
+
) => {
|
|
1408
1412
|
const refMap = this.state.userChannelReferences[user.id] || {};
|
|
1409
1413
|
|
|
1410
1414
|
for (const channelID in refMap) {
|
|
@@ -1413,7 +1417,7 @@ export class StreamChat {
|
|
|
1413
1417
|
const state = channel.state;
|
|
1414
1418
|
|
|
1415
1419
|
/** deleted the messages from this user. */
|
|
1416
|
-
state?.deleteUserMessages(user, hardDelete);
|
|
1420
|
+
state?.deleteUserMessages(user, hardDelete, deletedAt);
|
|
1417
1421
|
}
|
|
1418
1422
|
}
|
|
1419
1423
|
};
|
|
@@ -1478,7 +1482,11 @@ export class StreamChat {
|
|
|
1478
1482
|
event.user.deleted_at &&
|
|
1479
1483
|
(event.mark_messages_deleted || event.hard_delete)
|
|
1480
1484
|
) {
|
|
1481
|
-
this._deleteUserMessageReference(
|
|
1485
|
+
this._deleteUserMessageReference(
|
|
1486
|
+
event.user,
|
|
1487
|
+
event.hard_delete,
|
|
1488
|
+
event.user.deleted_at ? new Date(event.user.deleted_at) : null,
|
|
1489
|
+
);
|
|
1482
1490
|
}
|
|
1483
1491
|
};
|
|
1484
1492
|
|
|
@@ -1503,6 +1511,14 @@ export class StreamChat {
|
|
|
1503
1511
|
this._handleUserEvent(event);
|
|
1504
1512
|
}
|
|
1505
1513
|
|
|
1514
|
+
if (event.type === 'user.messages.deleted' && !event.cid && event.user) {
|
|
1515
|
+
this._deleteUserMessageReference(
|
|
1516
|
+
event.user,
|
|
1517
|
+
event.hard_delete,
|
|
1518
|
+
event.created_at ? new Date(event.created_at) : null,
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1506
1522
|
if (event.type === 'health.check' && event.me) {
|
|
1507
1523
|
client.user = event.me;
|
|
1508
1524
|
client.state.updateUser(event.me);
|
package/src/events.ts
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { StateStore } from '../store';
|
|
2
|
+
import type { ArrayOneOrMore, ArrayTwoOrMore, QueryFilter } from '../types';
|
|
3
|
+
|
|
4
|
+
type ElementType<T> = T extends (infer U)[] ? U : T;
|
|
5
|
+
|
|
6
|
+
// redeclared because QueryFilter does not account for the additional operators
|
|
7
|
+
export type ExtendedQueryFilter<T = string> = QueryFilter<T> & {
|
|
8
|
+
$autocomplete?: T extends string ? string : never;
|
|
9
|
+
$contains?: ElementType<T>;
|
|
10
|
+
$in?: ElementType<T>[];
|
|
11
|
+
$q?: T extends string ? string : never;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ExtendedQueryLogicalOperators<T> = {
|
|
15
|
+
$and?: ArrayOneOrMore<ExtendedQueryFilters<T>>;
|
|
16
|
+
$nor?: ArrayOneOrMore<ExtendedQueryFilters<T>>;
|
|
17
|
+
$or?: ArrayTwoOrMore<ExtendedQueryFilters<T>>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ExtendedQueryFilters<T> = {
|
|
21
|
+
[K in keyof T]?: ExtendedQueryFilter<T[K]>;
|
|
22
|
+
} & ExtendedQueryLogicalOperators<T>;
|
|
23
|
+
|
|
24
|
+
export type FilterBuilderGenerators<
|
|
25
|
+
TFilters,
|
|
26
|
+
TContext extends Record<string, unknown> = {},
|
|
27
|
+
> = {
|
|
28
|
+
[K in string]: {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
generate: (context: TContext) => Partial<TFilters> | null;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type FilterBuilderOptions<TFilters, TContext extends Record<string, unknown>> = {
|
|
35
|
+
initialFilterConfig?: FilterBuilderGenerators<TFilters, TContext>;
|
|
36
|
+
initialContext?: TContext;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class FilterBuilder<TFilters, TContext extends Record<string, unknown> = {}> {
|
|
40
|
+
filterConfig: StateStore<FilterBuilderGenerators<TFilters, TContext>>;
|
|
41
|
+
context: StateStore<TContext>;
|
|
42
|
+
|
|
43
|
+
constructor(params?: FilterBuilderOptions<TFilters, TContext>) {
|
|
44
|
+
this.context = new StateStore(params?.initialContext ?? ({} as TContext));
|
|
45
|
+
this.filterConfig = new StateStore(
|
|
46
|
+
params?.initialFilterConfig ?? ({} as FilterBuilderGenerators<TFilters, TContext>),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
updateFilterConfig(config: Partial<FilterBuilderGenerators<TFilters, TContext>>) {
|
|
51
|
+
this.filterConfig.partialNext(config);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
enableFilter(filterKey: keyof FilterBuilderGenerators<TFilters, TContext>) {
|
|
55
|
+
const config = this.filterConfig.getLatestValue();
|
|
56
|
+
if (config[filterKey]) {
|
|
57
|
+
this.filterConfig.partialNext({
|
|
58
|
+
[filterKey]: {
|
|
59
|
+
...config[filterKey],
|
|
60
|
+
enabled: true,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
disableFilter(filterKey: keyof FilterBuilderGenerators<TFilters, TContext>) {
|
|
67
|
+
const config = this.filterConfig.getLatestValue();
|
|
68
|
+
if (config[filterKey]) {
|
|
69
|
+
this.filterConfig.partialNext({
|
|
70
|
+
[filterKey]: {
|
|
71
|
+
...config[filterKey],
|
|
72
|
+
enabled: false,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
updateContext(newContext: Partial<TContext>) {
|
|
79
|
+
this.context.partialNext(newContext);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
buildFilters(params?: {
|
|
83
|
+
context?: Partial<TContext>;
|
|
84
|
+
baseFilters?: Partial<TFilters>;
|
|
85
|
+
}): Partial<TFilters> {
|
|
86
|
+
const filters: Partial<TFilters> = {
|
|
87
|
+
...(params?.baseFilters ?? {}),
|
|
88
|
+
} as Partial<TFilters>;
|
|
89
|
+
|
|
90
|
+
const filterConfig = this.filterConfig.getLatestValue();
|
|
91
|
+
for (const key in filterConfig) {
|
|
92
|
+
const configItem = filterConfig[key];
|
|
93
|
+
if (!configItem?.enabled) continue;
|
|
94
|
+
|
|
95
|
+
const generated = configItem.generate({
|
|
96
|
+
...this.context.getLatestValue(),
|
|
97
|
+
...(params?.context ?? {}),
|
|
98
|
+
});
|
|
99
|
+
if (generated) Object.assign(filters, generated);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return filters;
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/pagination/index.ts
CHANGED
|
@@ -59,7 +59,7 @@ const DEFAULT_SEARCH_SOURCE_OPTIONS: Required<SearchSourceOptions> = {
|
|
|
59
59
|
|
|
60
60
|
abstract class BaseSearchSourceBase<T> implements ISearchSource<T> {
|
|
61
61
|
state: StateStore<SearchSourceState<T>>;
|
|
62
|
-
|
|
62
|
+
pageSize: number;
|
|
63
63
|
abstract readonly type: SearchSourceType;
|
|
64
64
|
|
|
65
65
|
protected constructor(options?: SearchSourceOptions) {
|