stream-chat 9.16.0 → 9.18.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 +430 -229
- package/dist/cjs/index.browser.cjs.map +4 -4
- package/dist/cjs/index.node.cjs +431 -229
- package/dist/cjs/index.node.cjs.map +4 -4
- package/dist/esm/index.js +469 -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/moderation.d.ts +29 -1
- 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 +142 -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/moderation.ts +62 -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 +170 -0
- package/src/utils.ts +75 -0
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
package/src/moderation.ts
CHANGED
|
@@ -7,11 +7,16 @@ import type {
|
|
|
7
7
|
ModerationConfig,
|
|
8
8
|
ModerationFlagOptions,
|
|
9
9
|
ModerationMuteOptions,
|
|
10
|
+
ModerationRule,
|
|
11
|
+
ModerationRuleRequest,
|
|
10
12
|
MuteUserResponse,
|
|
11
13
|
Pager,
|
|
12
14
|
QueryConfigsResponse,
|
|
13
15
|
QueryModerationConfigsFilters,
|
|
14
16
|
QueryModerationConfigsSort,
|
|
17
|
+
QueryModerationRulesFilters,
|
|
18
|
+
QueryModerationRulesResponse,
|
|
19
|
+
QueryModerationRulesSort,
|
|
15
20
|
RequireAtLeastOne,
|
|
16
21
|
ReviewQueueFilters,
|
|
17
22
|
ReviewQueueItem,
|
|
@@ -20,6 +25,7 @@ import type {
|
|
|
20
25
|
ReviewQueueSort,
|
|
21
26
|
SubmitActionOptions,
|
|
22
27
|
UpsertConfigResponse,
|
|
28
|
+
UpsertModerationRuleResponse,
|
|
23
29
|
} from './types';
|
|
24
30
|
import type { StreamChat } from './client';
|
|
25
31
|
import { normalizeQuerySort } from './utils';
|
|
@@ -388,4 +394,60 @@ export class Moderation {
|
|
|
388
394
|
flags,
|
|
389
395
|
);
|
|
390
396
|
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Create or update a moderation rule
|
|
400
|
+
* @param {ModerationRuleRequest} rule Rule configuration to be upserted
|
|
401
|
+
* @returns
|
|
402
|
+
*/
|
|
403
|
+
async upsertModerationRule(rule: ModerationRuleRequest) {
|
|
404
|
+
return await this.client.post<UpsertModerationRuleResponse>(
|
|
405
|
+
this.client.baseURL + '/api/v2/moderation/moderation_rule',
|
|
406
|
+
rule,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Query moderation rules
|
|
412
|
+
* @param {QueryModerationRulesFilters} filterConditions Filter conditions for querying moderation rules
|
|
413
|
+
* @param {QueryModerationRulesSort} sort Sort conditions for querying moderation rules
|
|
414
|
+
* @param {Pager} options Pagination options for querying moderation rules
|
|
415
|
+
* @returns
|
|
416
|
+
*/
|
|
417
|
+
async queryModerationRules(
|
|
418
|
+
filterConditions: QueryModerationRulesFilters = {},
|
|
419
|
+
sort: QueryModerationRulesSort = [],
|
|
420
|
+
options: Pager = {},
|
|
421
|
+
) {
|
|
422
|
+
return await this.client.post<QueryModerationRulesResponse>(
|
|
423
|
+
this.client.baseURL + '/api/v2/moderation/moderation_rules',
|
|
424
|
+
{
|
|
425
|
+
filter: filterConditions,
|
|
426
|
+
sort,
|
|
427
|
+
...options,
|
|
428
|
+
},
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get a specific moderation rule by ID
|
|
434
|
+
* @param {string} id ID of the moderation rule to fetch
|
|
435
|
+
* @returns
|
|
436
|
+
*/
|
|
437
|
+
async getModerationRule(id: string) {
|
|
438
|
+
return await this.client.get<{ rule: ModerationRule }>(
|
|
439
|
+
this.client.baseURL + '/api/v2/moderation/moderation_rule/' + id,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Delete a moderation rule by ID
|
|
445
|
+
* @param {string} id ID of the moderation rule to delete
|
|
446
|
+
* @returns
|
|
447
|
+
*/
|
|
448
|
+
async deleteModerationRule(id: string) {
|
|
449
|
+
return await this.client.delete(
|
|
450
|
+
this.client.baseURL + '/api/v2/moderation/moderation_rule/' + id,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
391
453
|
}
|
|
@@ -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) {
|
|
@@ -1,27 +1,66 @@
|
|
|
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
|
|
|
7
|
-
|
|
9
|
+
type CustomContext = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
export type ChannelSearchSourceFilterBuilderContext<
|
|
12
|
+
C extends CustomContext = CustomContext,
|
|
13
|
+
> = { searchQuery?: string } & C;
|
|
14
|
+
|
|
15
|
+
export class ChannelSearchSource<
|
|
16
|
+
TFilterContext extends CustomContext = CustomContext,
|
|
17
|
+
> extends BaseSearchSource<Channel> {
|
|
8
18
|
readonly type = 'channels';
|
|
9
|
-
|
|
19
|
+
client: StreamChat;
|
|
10
20
|
filters: ChannelFilters | undefined;
|
|
11
21
|
sort: ChannelSort | undefined;
|
|
12
22
|
searchOptions: Omit<ChannelOptions, 'limit' | 'offset'> | undefined;
|
|
23
|
+
filterBuilder: FilterBuilder<
|
|
24
|
+
ChannelFilters,
|
|
25
|
+
ChannelSearchSourceFilterBuilderContext<TFilterContext>
|
|
26
|
+
>;
|
|
13
27
|
|
|
14
|
-
constructor(
|
|
28
|
+
constructor(
|
|
29
|
+
client: StreamChat,
|
|
30
|
+
options?: SearchSourceOptions,
|
|
31
|
+
filterBuilderOptions: FilterBuilderOptions<
|
|
32
|
+
ChannelFilters,
|
|
33
|
+
ChannelSearchSourceFilterBuilderContext<TFilterContext>
|
|
34
|
+
> = {},
|
|
35
|
+
) {
|
|
15
36
|
super(options);
|
|
16
37
|
this.client = client;
|
|
38
|
+
this.filterBuilder = new FilterBuilder<
|
|
39
|
+
ChannelFilters,
|
|
40
|
+
ChannelSearchSourceFilterBuilderContext<TFilterContext>
|
|
41
|
+
>({
|
|
42
|
+
...filterBuilderOptions,
|
|
43
|
+
initialFilterConfig: {
|
|
44
|
+
name: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
generate: ({ searchQuery }) =>
|
|
47
|
+
searchQuery ? { name: { $autocomplete: searchQuery } } : null,
|
|
48
|
+
},
|
|
49
|
+
...filterBuilderOptions.initialFilterConfig,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
17
52
|
}
|
|
18
53
|
|
|
19
54
|
protected async query(searchQuery: string) {
|
|
20
|
-
const filters = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
55
|
+
const filters = this.filterBuilder.buildFilters({
|
|
56
|
+
baseFilters: {
|
|
57
|
+
...(this.client.userID ? { members: { $in: [this.client.userID] } } : {}),
|
|
58
|
+
...this.filters,
|
|
59
|
+
},
|
|
60
|
+
context: { searchQuery } as Partial<
|
|
61
|
+
ChannelSearchSourceFilterBuilderContext<TFilterContext>
|
|
62
|
+
>,
|
|
63
|
+
});
|
|
25
64
|
const sort = this.sort ?? {};
|
|
26
65
|
const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
|
|
27
66
|
const items = await this.client.queryChannels(filters, sort, options);
|
|
@@ -10,46 +10,161 @@ import type {
|
|
|
10
10
|
} from '../types';
|
|
11
11
|
import type { StreamChat } from '../client';
|
|
12
12
|
import type { SearchSourceOptions } from './types';
|
|
13
|
+
import { FilterBuilder, type FilterBuilderOptions } from '../pagination';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
type CustomContext = Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
// Built-in contexts per builder
|
|
18
|
+
type BuiltInContexts = {
|
|
19
|
+
messageSearchChannel: { searchQuery?: string };
|
|
20
|
+
messageSearch: { searchQuery?: string };
|
|
21
|
+
channelQuery: { cids?: string[] };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Merge Built-in with user-provided Custom context
|
|
25
|
+
type MergeContext<
|
|
26
|
+
B extends Record<string, unknown>,
|
|
27
|
+
C extends CustomContext | undefined,
|
|
28
|
+
> = B & (C extends object ? C : {});
|
|
29
|
+
|
|
30
|
+
// User can provide custom context for each builder
|
|
31
|
+
type MessageSearchSourceContexts = Partial<{
|
|
32
|
+
messageSearchChannelContext: Record<string, unknown>;
|
|
33
|
+
messageSearchContext: Record<string, unknown>;
|
|
34
|
+
channelQueryContext: Record<string, unknown>;
|
|
35
|
+
}>;
|
|
36
|
+
|
|
37
|
+
export type MessageSearchSourceFilterBuilderOptions<
|
|
38
|
+
TContexts extends MessageSearchSourceContexts = {},
|
|
39
|
+
> = Partial<{
|
|
40
|
+
messageSearchChannel: FilterBuilderOptions<
|
|
41
|
+
ChannelFilters,
|
|
42
|
+
MergeContext<
|
|
43
|
+
BuiltInContexts['messageSearchChannel'],
|
|
44
|
+
TContexts['messageSearchChannelContext']
|
|
45
|
+
>
|
|
46
|
+
>;
|
|
47
|
+
messageSearch: FilterBuilderOptions<
|
|
48
|
+
MessageFilters,
|
|
49
|
+
MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>
|
|
50
|
+
>;
|
|
51
|
+
channelQuery: FilterBuilderOptions<
|
|
52
|
+
ChannelFilters,
|
|
53
|
+
MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>
|
|
54
|
+
>;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
export class MessageSearchSource<
|
|
58
|
+
TContexts extends MessageSearchSourceContexts = {},
|
|
59
|
+
> extends BaseSearchSource<MessageResponse> {
|
|
15
60
|
readonly type = 'messages';
|
|
16
61
|
private client: StreamChat;
|
|
62
|
+
|
|
17
63
|
messageSearchChannelFilters: ChannelFilters | undefined;
|
|
18
64
|
messageSearchFilters: MessageFilters | undefined;
|
|
19
65
|
messageSearchSort: SearchMessageSort | undefined;
|
|
66
|
+
|
|
20
67
|
channelQueryFilters: ChannelFilters | undefined;
|
|
21
68
|
channelQuerySort: ChannelSort | undefined;
|
|
22
69
|
channelQueryOptions: Omit<ChannelOptions, 'limit' | 'offset'> | undefined;
|
|
23
70
|
|
|
24
|
-
|
|
71
|
+
messageSearchChannelFilterBuilder: FilterBuilder<
|
|
72
|
+
ChannelFilters,
|
|
73
|
+
MergeContext<
|
|
74
|
+
BuiltInContexts['messageSearchChannel'],
|
|
75
|
+
TContexts['messageSearchChannelContext']
|
|
76
|
+
>
|
|
77
|
+
>;
|
|
78
|
+
messageSearchFilterBuilder: FilterBuilder<
|
|
79
|
+
MessageFilters,
|
|
80
|
+
MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>
|
|
81
|
+
>;
|
|
82
|
+
channelQueryFilterBuilder: FilterBuilder<
|
|
83
|
+
ChannelFilters,
|
|
84
|
+
MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
constructor(
|
|
88
|
+
client: StreamChat,
|
|
89
|
+
options?: SearchSourceOptions,
|
|
90
|
+
filterBuilderOptions?: MessageSearchSourceFilterBuilderOptions<TContexts>,
|
|
91
|
+
) {
|
|
25
92
|
super(options);
|
|
26
93
|
this.client = client;
|
|
94
|
+
|
|
95
|
+
this.messageSearchChannelFilterBuilder = new FilterBuilder<
|
|
96
|
+
ChannelFilters,
|
|
97
|
+
MergeContext<
|
|
98
|
+
BuiltInContexts['messageSearchChannel'],
|
|
99
|
+
TContexts['messageSearchChannelContext']
|
|
100
|
+
>
|
|
101
|
+
>(filterBuilderOptions?.messageSearchChannel);
|
|
102
|
+
|
|
103
|
+
this.messageSearchFilterBuilder = new FilterBuilder<
|
|
104
|
+
MessageFilters,
|
|
105
|
+
MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>
|
|
106
|
+
>({
|
|
107
|
+
...filterBuilderOptions?.messageSearch,
|
|
108
|
+
initialFilterConfig: {
|
|
109
|
+
text: {
|
|
110
|
+
enabled: true,
|
|
111
|
+
generate: ({ searchQuery }) => (searchQuery ? { text: searchQuery } : null),
|
|
112
|
+
},
|
|
113
|
+
...filterBuilderOptions?.messageSearch?.initialFilterConfig,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.channelQueryFilterBuilder = new FilterBuilder<
|
|
118
|
+
ChannelFilters,
|
|
119
|
+
MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>
|
|
120
|
+
>({
|
|
121
|
+
...filterBuilderOptions?.channelQuery,
|
|
122
|
+
initialFilterConfig: {
|
|
123
|
+
cid: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
generate: ({ cids }) => (cids ? { cid: { $in: cids } } : null),
|
|
126
|
+
},
|
|
127
|
+
...filterBuilderOptions?.channelQuery?.initialFilterConfig,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
27
130
|
}
|
|
28
131
|
|
|
29
132
|
protected async query(searchQuery: string) {
|
|
30
|
-
if (!this.client.userID) return { items: [] };
|
|
133
|
+
if (!this.client.userID || !searchQuery || this.next === null) return { items: [] };
|
|
31
134
|
|
|
32
|
-
const channelFilters
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
135
|
+
const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
|
|
136
|
+
baseFilters: {
|
|
137
|
+
...(this.client.userID ? { members: { $in: [this.client.userID] } } : {}),
|
|
138
|
+
...this.messageSearchChannelFilters,
|
|
139
|
+
},
|
|
140
|
+
context: { searchQuery } as Partial<
|
|
141
|
+
MergeContext<
|
|
142
|
+
BuiltInContexts['messageSearchChannel'],
|
|
143
|
+
TContexts['messageSearchChannelContext']
|
|
144
|
+
>
|
|
145
|
+
>,
|
|
146
|
+
});
|
|
36
147
|
|
|
37
|
-
const messageFilters: MessageFilters = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
148
|
+
const messageFilters: MessageFilters = this.messageSearchFilterBuilder.buildFilters({
|
|
149
|
+
baseFilters: {
|
|
150
|
+
type: 'regular',
|
|
151
|
+
...this.messageSearchFilters,
|
|
152
|
+
},
|
|
153
|
+
context: { searchQuery } as Partial<
|
|
154
|
+
MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>
|
|
155
|
+
>,
|
|
156
|
+
});
|
|
42
157
|
|
|
43
158
|
const sort: SearchMessageSort = {
|
|
44
159
|
created_at: -1,
|
|
45
160
|
...this.messageSearchSort,
|
|
46
161
|
};
|
|
47
162
|
|
|
48
|
-
const options = {
|
|
163
|
+
const options: SearchOptions = {
|
|
49
164
|
limit: this.pageSize,
|
|
50
165
|
next: this.next,
|
|
51
166
|
sort,
|
|
52
|
-
}
|
|
167
|
+
};
|
|
53
168
|
|
|
54
169
|
const { next, results } = await this.client.search(
|
|
55
170
|
channelFilters,
|
|
@@ -62,15 +177,18 @@ export class MessageSearchSource extends BaseSearchSource<MessageResponse> {
|
|
|
62
177
|
items.reduce((acc, message) => {
|
|
63
178
|
if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
|
|
64
179
|
return acc;
|
|
65
|
-
}, new Set<string>()),
|
|
180
|
+
}, new Set<string>()),
|
|
66
181
|
);
|
|
67
|
-
|
|
68
|
-
if (
|
|
182
|
+
|
|
183
|
+
if (cids.length > 0) {
|
|
184
|
+
const channelQueryFilters = this.channelQueryFilterBuilder.buildFilters({
|
|
185
|
+
baseFilters: this.channelQueryFilters,
|
|
186
|
+
context: { cids } as Partial<
|
|
187
|
+
MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>
|
|
188
|
+
>,
|
|
189
|
+
});
|
|
69
190
|
await this.client.queryChannels(
|
|
70
|
-
|
|
71
|
-
cid: { $in: cids },
|
|
72
|
-
...this.channelQueryFilters,
|
|
73
|
-
} as ChannelFilters,
|
|
191
|
+
channelQueryFilters,
|
|
74
192
|
{
|
|
75
193
|
last_message_at: -1,
|
|
76
194
|
...this.channelQuerySort,
|
|
@@ -1,28 +1,65 @@
|
|
|
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
|
|
|
6
|
-
|
|
7
|
+
type CustomContext = Record<string, unknown>;
|
|
8
|
+
|
|
9
|
+
export type UserSearchSourceFilterBuilderContext<
|
|
10
|
+
C extends CustomContext = CustomContext,
|
|
11
|
+
> = { searchQuery?: string } & C;
|
|
12
|
+
|
|
13
|
+
export class UserSearchSource<
|
|
14
|
+
TFilterContext extends CustomContext = CustomContext,
|
|
15
|
+
> extends BaseSearchSource<UserResponse> {
|
|
7
16
|
readonly type = 'users';
|
|
8
|
-
|
|
17
|
+
client: StreamChat;
|
|
9
18
|
filters: UserFilters | undefined;
|
|
10
19
|
sort: UserSort | undefined;
|
|
11
20
|
searchOptions: Omit<UserOptions, 'limit' | 'offset'> | undefined;
|
|
21
|
+
filterBuilder: FilterBuilder<
|
|
22
|
+
UserFilters,
|
|
23
|
+
UserSearchSourceFilterBuilderContext<TFilterContext>
|
|
24
|
+
>;
|
|
12
25
|
|
|
13
|
-
constructor(
|
|
26
|
+
constructor(
|
|
27
|
+
client: StreamChat,
|
|
28
|
+
options?: SearchSourceOptions,
|
|
29
|
+
filterBuilderOptions: FilterBuilderOptions<
|
|
30
|
+
UserFilters,
|
|
31
|
+
UserSearchSourceFilterBuilderContext<TFilterContext>
|
|
32
|
+
> = {},
|
|
33
|
+
) {
|
|
14
34
|
super(options);
|
|
15
35
|
this.client = client;
|
|
36
|
+
this.filterBuilder = new FilterBuilder<
|
|
37
|
+
UserFilters,
|
|
38
|
+
UserSearchSourceFilterBuilderContext<TFilterContext>
|
|
39
|
+
>({
|
|
40
|
+
initialFilterConfig: {
|
|
41
|
+
$or: {
|
|
42
|
+
enabled: true,
|
|
43
|
+
generate: ({ searchQuery }) =>
|
|
44
|
+
searchQuery
|
|
45
|
+
? {
|
|
46
|
+
$or: [
|
|
47
|
+
{ id: { $autocomplete: searchQuery } },
|
|
48
|
+
{ name: { $autocomplete: searchQuery } },
|
|
49
|
+
],
|
|
50
|
+
}
|
|
51
|
+
: null,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
...filterBuilderOptions,
|
|
55
|
+
});
|
|
16
56
|
}
|
|
17
57
|
|
|
18
58
|
protected async query(searchQuery: string) {
|
|
19
|
-
const filters = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
],
|
|
24
|
-
...this.filters,
|
|
25
|
-
} as UserFilters;
|
|
59
|
+
const filters = this.filterBuilder.buildFilters({
|
|
60
|
+
baseFilters: this.filters,
|
|
61
|
+
context: { searchQuery } as UserSearchSourceFilterBuilderContext<TFilterContext>,
|
|
62
|
+
});
|
|
26
63
|
const sort = { id: 1, ...this.sort } as UserSort;
|
|
27
64
|
const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
|
|
28
65
|
const { users } = await this.client.queryUsers(filters, sort, options);
|
package/src/search/index.ts
CHANGED
|
@@ -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';
|