stream-chat 9.12.0 → 9.14.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 +452 -184
- package/dist/cjs/index.browser.cjs.map +4 -4
- package/dist/cjs/index.node.cjs +1679 -233
- package/dist/cjs/index.node.cjs.map +4 -4
- package/dist/esm/index.js +452 -184
- package/dist/esm/index.js.map +4 -4
- package/dist/types/client.d.ts +8 -1
- package/dist/types/messageComposer/attachmentManager.d.ts +11 -3
- package/dist/types/messageComposer/configuration/types.d.ts +1 -1
- package/dist/types/messageComposer/middleware/attachmentManager/index.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.d.ts +5 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.d.ts +2 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/index.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/preUpload/AttachmentPreUploadMiddlewareExecutor.d.ts +5 -0
- package/dist/types/messageComposer/middleware/attachmentManager/preUpload/blockedUploadNotification.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/preUpload/index.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/preUpload/serverUploadConfigCheck.d.ts +3 -0
- package/dist/types/messageComposer/middleware/attachmentManager/types.d.ts +20 -0
- package/dist/types/messageComposer/types.d.ts +1 -0
- package/dist/types/middleware.d.ts +3 -2
- package/dist/types/types.d.ts +14 -0
- package/package.json +2 -2
- package/src/channel_manager.ts +18 -2
- package/src/client.ts +13 -0
- package/src/messageComposer/attachmentManager.ts +116 -25
- package/src/messageComposer/configuration/types.ts +3 -2
- package/src/messageComposer/messageComposer.ts +1 -1
- package/src/messageComposer/middleware/attachmentManager/index.ts +3 -0
- package/src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts +20 -0
- package/src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts +43 -0
- package/src/messageComposer/middleware/attachmentManager/postUpload/index.ts +3 -0
- package/src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts +39 -0
- package/src/messageComposer/middleware/attachmentManager/preUpload/AttachmentPreUploadMiddlewareExecutor.ts +20 -0
- package/src/messageComposer/middleware/attachmentManager/preUpload/blockedUploadNotification.ts +38 -0
- package/src/messageComposer/middleware/attachmentManager/preUpload/index.ts +3 -0
- package/src/messageComposer/middleware/attachmentManager/preUpload/serverUploadConfigCheck.ts +40 -0
- package/src/messageComposer/middleware/attachmentManager/types.ts +32 -0
- package/src/messageComposer/types.ts +6 -0
- package/src/middleware.ts +22 -10
- package/src/offline-support/offline_sync_manager.ts +16 -0
- package/src/types.ts +17 -0
- package/src/utils.ts +5 -1
package/dist/types/client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { TokenManager } from './token_manager';
|
|
|
7
7
|
import { WSConnectionFallback } from './connection_fallback';
|
|
8
8
|
import { Campaign } from './campaign';
|
|
9
9
|
import { Segment } from './segment';
|
|
10
|
-
import type { ActiveLiveLocationsAPIResponse, APIErrorResponse, APIResponse, AppSettings, AppSettingsAPIResponse, BannedUsersFilters, BannedUsersPaginationOptions, BannedUsersResponse, BannedUsersSort, BanUserOptions, BaseDeviceFields, BlockList, BlockListResponse, BlockUserAPIResponse, CampaignData, CampaignFilters, CampaignQueryOptions, CampaignResponse, CampaignSort, CastVoteAPIResponse, ChannelAPIResponse, ChannelData, ChannelFilters, ChannelMute, ChannelOptions, ChannelResponse, ChannelSort, ChannelStateOptions, CheckPushResponse, CheckSNSResponse, CheckSQSResponse, Configs, ConnectAPIResponse, CreateChannelOptions, CreateChannelResponse, CreateCommandOptions, CreateCommandResponse, CreateImportOptions, CreateImportResponse, CreateImportURLResponse, CreatePollAPIResponse, CreatePollData, CreatePollOptionAPIResponse, CreateReminderOptions, CustomPermissionOptions, DeactivateUsersOptions, DeleteCommandResponse, DeleteUserOptions, Device, DeviceIdentifier, DraftFilters, DraftSort, EndpointName, Event, EventHandler, ExportChannelOptions, ExportChannelRequest, ExportChannelResponse, ExportChannelStatusResponse, ExportUsersRequest, ExportUsersResponse, FlagMessageResponse, FlagReportsFilters, FlagReportsPaginationOptions, FlagReportsResponse, FlagsFilters, FlagsPaginationOptions, FlagsResponse, FlagUserResponse, GetBlockedUsersAPIResponse, GetCampaignOptions, GetChannelTypeResponse, GetCommandResponse, GetImportResponse, GetMessageOptions, GetPollAPIResponse, GetRateLimitsResponse, GetThreadAPIResponse, GetThreadOptions, GetUnreadCountAPIResponse, GetUnreadCountBatchAPIResponse, ListChannelResponse, ListCommandsResponse, ListImportsPaginationOptions, ListImportsResponse, LocalMessage, Logger, MarkChannelsReadOptions, MessageFilters, MessageFlagsFilters, MessageFlagsPaginationOptions, MessageFlagsResponse, MessageResponse, Mute, MuteUserOptions, MuteUserResponse, OGAttachment, OwnUserResponse, Pager, PartialMessageUpdate, PartialPollUpdate, PartialThreadUpdate, PartialUserUpdate, PermissionAPIResponse, PermissionsAPIResponse, PollAnswersAPIResponse, PollData, PollOptionData, PollSort, PollVote, PollVoteData, PollVotesAPIResponse, PushPreference, PushProvider, PushProviderConfig, PushProviderID, PushProviderListResponse, PushProviderUpsertResponse, QueryDraftsResponse, QueryMessageHistoryFilters, QueryMessageHistoryOptions, QueryMessageHistoryResponse, QueryMessageHistorySort, QueryPollsFilters, QueryPollsOptions, QueryPollsResponse, QueryReactionsAPIResponse, QueryReactionsOptions, QueryRemindersOptions, QueryRemindersResponse, QuerySegmentsOptions, QuerySegmentTargetsFilter, QueryThreadsOptions, QueryVotesFilters, QueryVotesOptions, ReactionFilters, ReactionResponse, ReactionSort, ReactivateUserOptions, ReactivateUsersOptions, ReminderAPIResponse, ReviewFlagReportOptions, ReviewFlagReportResponse, SdkIdentifier, SearchAPIResponse, SearchOptions, SegmentData, SegmentResponse, SegmentTargetsResponse, SegmentType, SendFileAPIResponse, SharedLocationResponse, SortParam, StreamChatOptions, SyncOptions, SyncResponse, TaskResponse, TaskStatus, TestPushDataInput, TestSNSDataInput, TestSQSDataInput, TokenOrProvider, TranslateResponse, UnBanUserOptions, UpdateChannelTypeRequest, UpdateChannelTypeResponse, UpdateCommandOptions, UpdateCommandResponse, UpdateLocationPayload, UpdateMessageAPIResponse, UpdateMessageOptions, UpdatePollAPIResponse, UpdateReminderOptions, UpdateSegmentData, UpsertPushPreferencesResponse, UserCustomEvent, UserFilters, UserOptions, UserResponse, UserSort, VoteSort } from './types';
|
|
10
|
+
import type { ActiveLiveLocationsAPIResponse, APIErrorResponse, APIResponse, AppSettings, AppSettingsAPIResponse, BannedUsersFilters, BannedUsersPaginationOptions, BannedUsersResponse, BannedUsersSort, BanUserOptions, BaseDeviceFields, BlockList, BlockListResponse, BlockUserAPIResponse, CampaignData, CampaignFilters, CampaignQueryOptions, CampaignResponse, CampaignSort, CastVoteAPIResponse, ChannelAPIResponse, ChannelData, ChannelFilters, ChannelMute, ChannelOptions, ChannelResponse, ChannelSort, ChannelStateOptions, CheckPushResponse, CheckSNSResponse, CheckSQSResponse, Configs, ConnectAPIResponse, CreateChannelOptions, CreateChannelResponse, CreateCommandOptions, CreateCommandResponse, CreateImportOptions, CreateImportResponse, CreateImportURLResponse, CreatePollAPIResponse, CreatePollData, CreatePollOptionAPIResponse, CreateReminderOptions, CustomPermissionOptions, DeactivateUsersOptions, DeleteCommandResponse, DeleteUserOptions, Device, DeviceIdentifier, DraftFilters, DraftSort, EndpointName, Event, EventHandler, ExportChannelOptions, ExportChannelRequest, ExportChannelResponse, ExportChannelStatusResponse, ExportUsersRequest, ExportUsersResponse, FlagMessageResponse, FlagReportsFilters, FlagReportsPaginationOptions, FlagReportsResponse, FlagsFilters, FlagsPaginationOptions, FlagsResponse, FlagUserResponse, GetBlockedUsersAPIResponse, GetCampaignOptions, GetChannelTypeResponse, GetCommandResponse, GetHookEventsResponse, GetImportResponse, GetMessageOptions, GetPollAPIResponse, GetRateLimitsResponse, GetThreadAPIResponse, GetThreadOptions, GetUnreadCountAPIResponse, GetUnreadCountBatchAPIResponse, ListChannelResponse, ListCommandsResponse, ListImportsPaginationOptions, ListImportsResponse, LocalMessage, Logger, MarkChannelsReadOptions, MessageFilters, MessageFlagsFilters, MessageFlagsPaginationOptions, MessageFlagsResponse, MessageResponse, Mute, MuteUserOptions, MuteUserResponse, OGAttachment, OwnUserResponse, Pager, PartialMessageUpdate, PartialPollUpdate, PartialThreadUpdate, PartialUserUpdate, PermissionAPIResponse, PermissionsAPIResponse, PollAnswersAPIResponse, PollData, PollOptionData, PollSort, PollVote, PollVoteData, PollVotesAPIResponse, Product, PushPreference, PushProvider, PushProviderConfig, PushProviderID, PushProviderListResponse, PushProviderUpsertResponse, QueryDraftsResponse, QueryMessageHistoryFilters, QueryMessageHistoryOptions, QueryMessageHistoryResponse, QueryMessageHistorySort, QueryPollsFilters, QueryPollsOptions, QueryPollsResponse, QueryReactionsAPIResponse, QueryReactionsOptions, QueryRemindersOptions, QueryRemindersResponse, QuerySegmentsOptions, QuerySegmentTargetsFilter, QueryThreadsOptions, QueryVotesFilters, QueryVotesOptions, ReactionFilters, ReactionResponse, ReactionSort, ReactivateUserOptions, ReactivateUsersOptions, ReminderAPIResponse, ReviewFlagReportOptions, ReviewFlagReportResponse, SdkIdentifier, SearchAPIResponse, SearchOptions, SegmentData, SegmentResponse, SegmentTargetsResponse, SegmentType, SendFileAPIResponse, SharedLocationResponse, SortParam, StreamChatOptions, SyncOptions, SyncResponse, TaskResponse, TaskStatus, TestPushDataInput, TestSNSDataInput, TestSQSDataInput, TokenOrProvider, TranslateResponse, UnBanUserOptions, UpdateChannelTypeRequest, UpdateChannelTypeResponse, UpdateCommandOptions, UpdateCommandResponse, UpdateLocationPayload, UpdateMessageAPIResponse, UpdateMessageOptions, UpdatePollAPIResponse, UpdateReminderOptions, UpdateSegmentData, UpsertPushPreferencesResponse, UserCustomEvent, UserFilters, UserOptions, UserResponse, UserSort, VoteSort } from './types';
|
|
11
11
|
import { ErrorFromResponse } from './types';
|
|
12
12
|
import { InsightMetrics } from './insights';
|
|
13
13
|
import { Thread } from './thread';
|
|
@@ -619,6 +619,13 @@ export declare class StreamChat {
|
|
|
619
619
|
serverSide?: boolean;
|
|
620
620
|
web?: boolean;
|
|
621
621
|
}): Promise<GetRateLimitsResponse>;
|
|
622
|
+
/**
|
|
623
|
+
* getHookEvents - Get available events for hooks (webhook, SQS, and SNS)
|
|
624
|
+
*
|
|
625
|
+
* @param {Product[]} [products] Optional array of products to filter events by (e.g., [Product.Chat, Product.Video])
|
|
626
|
+
* @returns {Promise<GetHookEventsResponse>} Response containing available hook events
|
|
627
|
+
*/
|
|
628
|
+
getHookEvents(products?: Product[]): Promise<GetHookEventsResponse>;
|
|
622
629
|
_addChannelConfig({ cid, config }: ChannelResponse): void;
|
|
623
630
|
/**
|
|
624
631
|
* channel - Returns a new channel with the given type, id and custom data
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AttachmentManagerConfig,
|
|
1
|
+
import type { AttachmentManagerConfig, UploadRequestFn } from './configuration';
|
|
2
|
+
import { AttachmentPostUploadMiddlewareExecutor, AttachmentPreUploadMiddlewareExecutor } from './middleware/attachmentManager';
|
|
2
3
|
import { StateStore } from '../store';
|
|
3
4
|
import type { AttachmentLoadingState, FileLike, FileReference, LocalAttachment, LocalUploadAttachment, UploadPermissionCheckResult } from './types';
|
|
4
5
|
import type { DraftMessage, LocalMessage } from '../types';
|
|
@@ -14,6 +15,8 @@ export type AttachmentManagerOptions = {
|
|
|
14
15
|
export declare class AttachmentManager {
|
|
15
16
|
readonly state: StateStore<AttachmentManagerState>;
|
|
16
17
|
readonly composer: MessageComposer;
|
|
18
|
+
readonly preUploadMiddlewareExecutor: AttachmentPreUploadMiddlewareExecutor;
|
|
19
|
+
readonly postUploadMiddlewareExecutor: AttachmentPostUploadMiddlewareExecutor;
|
|
17
20
|
private attachmentsByIdGetterCache;
|
|
18
21
|
constructor({ composer, message }: AttachmentManagerOptions);
|
|
19
22
|
get attachmentsById(): Record<string, LocalAttachment>;
|
|
@@ -47,6 +50,7 @@ export declare class AttachmentManager {
|
|
|
47
50
|
upsertAttachments: (attachmentsToUpsert: LocalAttachment[]) => void;
|
|
48
51
|
removeAttachments: (localAttachmentIds: string[]) => void;
|
|
49
52
|
getUploadConfigCheck: (fileLike: FileReference | FileLike) => Promise<UploadPermissionCheckResult>;
|
|
53
|
+
static toLocalUploadAttachment: (fileLike: FileReference | FileLike) => LocalUploadAttachment;
|
|
50
54
|
fileToLocalUploadAttachment: (fileLike: FileReference | FileLike) => Promise<LocalUploadAttachment>;
|
|
51
55
|
private ensureLocalUploadAttachment;
|
|
52
56
|
/**
|
|
@@ -60,7 +64,11 @@ export declare class AttachmentManager {
|
|
|
60
64
|
/**
|
|
61
65
|
* todo: docs how to customize the image and file upload by overriding do
|
|
62
66
|
*/
|
|
63
|
-
doUploadRequest: (fileLike: FileReference | FileLike) => Promise<
|
|
67
|
+
doUploadRequest: (fileLike: FileReference | FileLike) => Promise<{
|
|
68
|
+
file: string;
|
|
69
|
+
thumb_url?: string;
|
|
70
|
+
}>;
|
|
64
71
|
uploadAttachment: (attachment: LocalUploadAttachment) => Promise<LocalUploadAttachment | undefined>;
|
|
65
|
-
|
|
72
|
+
uploadFile: (file: FileReference | FileLike) => Promise<LocalUploadAttachment>;
|
|
73
|
+
uploadFiles: (files: FileReference[] | FileList | FileLike[]) => Promise<LocalUploadAttachment[] | undefined>;
|
|
66
74
|
}
|
|
@@ -4,7 +4,7 @@ import type { FileLike, FileReference } from '../types';
|
|
|
4
4
|
export type MinimumUploadRequestResult = {
|
|
5
5
|
file: string;
|
|
6
6
|
thumb_url?: string;
|
|
7
|
-
}
|
|
7
|
+
} & Partial<Record<string, unknown>>;
|
|
8
8
|
export type UploadRequestFn = (fileLike: FileReference | FileLike) => Promise<MinimumUploadRequestResult>;
|
|
9
9
|
export type DraftsConfiguration = {
|
|
10
10
|
enabled: boolean;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MiddlewareExecutor } from '../../../../middleware';
|
|
2
|
+
import type { AttachmentPostUploadMiddlewareExecutorOptions, AttachmentPostUploadMiddlewareState } from '../types';
|
|
3
|
+
export declare class AttachmentPostUploadMiddlewareExecutor extends MiddlewareExecutor<AttachmentPostUploadMiddlewareState, 'postProcess'> {
|
|
4
|
+
constructor({ composer }: AttachmentPostUploadMiddlewareExecutorOptions);
|
|
5
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MiddlewareExecutor } from '../../../../middleware';
|
|
2
|
+
import type { AttachmentPreUploadMiddlewareExecutorOptions, AttachmentPreUploadMiddlewareState } from '../types';
|
|
3
|
+
export declare class AttachmentPreUploadMiddlewareExecutor extends MiddlewareExecutor<AttachmentPreUploadMiddlewareState, 'prepare'> {
|
|
4
|
+
constructor({ composer }: AttachmentPreUploadMiddlewareExecutorOptions);
|
|
5
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LocalUploadAttachment } from '../../types';
|
|
2
|
+
import type { Middleware } from '../../../middleware';
|
|
3
|
+
import type { MessageComposer } from '../../messageComposer';
|
|
4
|
+
import type { MinimumUploadRequestResult } from '../../configuration';
|
|
5
|
+
export type AttachmentPreUploadMiddlewareState = {
|
|
6
|
+
attachment: LocalUploadAttachment;
|
|
7
|
+
};
|
|
8
|
+
export type AttachmentPostUploadMiddlewareState = {
|
|
9
|
+
attachment: LocalUploadAttachment;
|
|
10
|
+
error?: Error;
|
|
11
|
+
response?: MinimumUploadRequestResult;
|
|
12
|
+
};
|
|
13
|
+
export type AttachmentPreUploadMiddleware = Middleware<AttachmentPreUploadMiddlewareState, 'prepare'>;
|
|
14
|
+
export type AttachmentPostUploadMiddleware = Middleware<AttachmentPostUploadMiddlewareState, 'postProcess'>;
|
|
15
|
+
export type AttachmentPostUploadMiddlewareExecutorOptions = {
|
|
16
|
+
composer: MessageComposer;
|
|
17
|
+
};
|
|
18
|
+
export type AttachmentPreUploadMiddlewareExecutorOptions = {
|
|
19
|
+
composer: MessageComposer;
|
|
20
|
+
};
|
|
@@ -61,6 +61,7 @@ export type LocalAttachmentUploadMetadata = {
|
|
|
61
61
|
export type LocalImageAttachmentUploadMetadata = LocalAttachmentUploadMetadata & {
|
|
62
62
|
previewUri?: string;
|
|
63
63
|
};
|
|
64
|
+
export type LocalNotImageAttachment = LocalFileAttachment | LocalAudioAttachment | LocalVideoAttachment | LocalVoiceRecordingAttachment;
|
|
64
65
|
export type AttachmentLoadingState = 'uploading' | 'finished' | 'failed' | 'blocked' | 'pending';
|
|
65
66
|
export type UploadPermissionCheckResult = {
|
|
66
67
|
uploadBlocked: boolean;
|
|
@@ -13,6 +13,7 @@ export type MiddlewareExecutionResult<TValue> = {
|
|
|
13
13
|
export type ExecuteParams<TValue> = {
|
|
14
14
|
eventName: string;
|
|
15
15
|
initialValue: TValue;
|
|
16
|
+
mode?: 'concurrent' | 'cancelable';
|
|
16
17
|
};
|
|
17
18
|
export type MiddlewareHandlerParams<TValue> = {
|
|
18
19
|
state: TValue;
|
|
@@ -41,6 +42,6 @@ export declare class MiddlewareExecutor<TValue, THandlers extends string> {
|
|
|
41
42
|
unique?: boolean;
|
|
42
43
|
}): this;
|
|
43
44
|
setOrder(order: string[]): void;
|
|
44
|
-
protected executeMiddlewareChain({ eventName, initialValue, }: ExecuteParams<TValue>): Promise<MiddlewareExecutionResult<TValue>>;
|
|
45
|
-
execute({ eventName, initialValue: initialState, }: ExecuteParams<TValue>): Promise<MiddlewareExecutionResult<TValue>>;
|
|
45
|
+
protected executeMiddlewareChain({ eventName, initialValue, mode, }: ExecuteParams<TValue>): Promise<MiddlewareExecutionResult<TValue>>;
|
|
46
|
+
execute({ eventName, initialValue: initialState, mode, }: ExecuteParams<TValue>): Promise<MiddlewareExecutionResult<TValue>>;
|
|
46
47
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -478,6 +478,20 @@ export type GetRateLimitsResponse = APIResponse & {
|
|
|
478
478
|
server_side?: RateLimitsMap;
|
|
479
479
|
web?: RateLimitsMap;
|
|
480
480
|
};
|
|
481
|
+
export declare enum Product {
|
|
482
|
+
Chat = "chat",
|
|
483
|
+
Video = "video",
|
|
484
|
+
Moderation = "moderation",
|
|
485
|
+
Feeds = "feeds"
|
|
486
|
+
}
|
|
487
|
+
export type HookEvent = {
|
|
488
|
+
name: string;
|
|
489
|
+
description: string;
|
|
490
|
+
products: Product[];
|
|
491
|
+
};
|
|
492
|
+
export type GetHookEventsResponse = APIResponse & {
|
|
493
|
+
events: HookEvent[];
|
|
494
|
+
};
|
|
481
495
|
export type GetReactionsAPIResponse = APIResponse & {
|
|
482
496
|
reactions: ReactionResponse[];
|
|
483
497
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.14.0",
|
|
4
4
|
"description": "JS SDK for the Stream Chat API",
|
|
5
5
|
"homepage": "https://getstream.io/chat/",
|
|
6
6
|
"author": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@types/ws": "^8.5.14",
|
|
54
54
|
"axios": "^1.6.0",
|
|
55
55
|
"base64-js": "^1.5.1",
|
|
56
|
-
"form-data": "^4.0.
|
|
56
|
+
"form-data": "^4.0.4",
|
|
57
57
|
"isomorphic-ws": "^5.0.0",
|
|
58
58
|
"jsonwebtoken": "^9.0.2",
|
|
59
59
|
"linkifyjs": "^4.2.0",
|
package/src/channel_manager.ts
CHANGED
|
@@ -317,7 +317,19 @@ export class ChannelManager extends WithSubscriptions {
|
|
|
317
317
|
`Maximum number of retries reached in queryChannels. Last error message is: ${err}`,
|
|
318
318
|
);
|
|
319
319
|
|
|
320
|
-
this.state.
|
|
320
|
+
const state = this.state.getLatestValue();
|
|
321
|
+
// If the offline support is enabled, and there are channels in the DB, we should not error out.
|
|
322
|
+
const isOfflineSupportEnabledWithChannels =
|
|
323
|
+
this.client.offlineDb && state.channels.length > 0;
|
|
324
|
+
|
|
325
|
+
this.state.partialNext({
|
|
326
|
+
error: isOfflineSupportEnabledWithChannels ? undefined : wrappedError,
|
|
327
|
+
pagination: {
|
|
328
|
+
...state.pagination,
|
|
329
|
+
isLoading: false,
|
|
330
|
+
isLoadingNext: false,
|
|
331
|
+
},
|
|
332
|
+
});
|
|
321
333
|
return;
|
|
322
334
|
}
|
|
323
335
|
|
|
@@ -444,7 +456,11 @@ export class ChannelManager extends WithSubscriptions {
|
|
|
444
456
|
this.client.logger('error', (error as Error).message);
|
|
445
457
|
this.state.next((currentState) => ({
|
|
446
458
|
...currentState,
|
|
447
|
-
pagination: {
|
|
459
|
+
pagination: {
|
|
460
|
+
...currentState.pagination,
|
|
461
|
+
isLoadingNext: false,
|
|
462
|
+
isLoading: false,
|
|
463
|
+
},
|
|
448
464
|
}));
|
|
449
465
|
throw error;
|
|
450
466
|
}
|
package/src/client.ts
CHANGED
|
@@ -106,6 +106,7 @@ import type {
|
|
|
106
106
|
GetCampaignOptions,
|
|
107
107
|
GetChannelTypeResponse,
|
|
108
108
|
GetCommandResponse,
|
|
109
|
+
GetHookEventsResponse,
|
|
109
110
|
GetImportResponse,
|
|
110
111
|
GetMessageAPIResponse,
|
|
111
112
|
GetMessageOptions,
|
|
@@ -148,6 +149,7 @@ import type {
|
|
|
148
149
|
PollVote,
|
|
149
150
|
PollVoteData,
|
|
150
151
|
PollVotesAPIResponse,
|
|
152
|
+
Product,
|
|
151
153
|
PushPreference,
|
|
152
154
|
PushProvider,
|
|
153
155
|
PushProviderConfig,
|
|
@@ -2164,6 +2166,17 @@ export class StreamChat {
|
|
|
2164
2166
|
});
|
|
2165
2167
|
}
|
|
2166
2168
|
|
|
2169
|
+
/**
|
|
2170
|
+
* getHookEvents - Get available events for hooks (webhook, SQS, and SNS)
|
|
2171
|
+
*
|
|
2172
|
+
* @param {Product[]} [products] Optional array of products to filter events by (e.g., [Product.Chat, Product.Video])
|
|
2173
|
+
* @returns {Promise<GetHookEventsResponse>} Response containing available hook events
|
|
2174
|
+
*/
|
|
2175
|
+
async getHookEvents(products?: Product[]) {
|
|
2176
|
+
const params = products && products.length > 0 ? { product: products.join(',') } : {};
|
|
2177
|
+
return await this.get<GetHookEventsResponse>(this.baseURL + '/hook/events', params);
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2167
2180
|
_addChannelConfig({ cid, config }: ChannelResponse) {
|
|
2168
2181
|
if (this._cacheEnabled()) {
|
|
2169
2182
|
this.configs[cid] = config;
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
isFileReference,
|
|
15
15
|
isImageFile,
|
|
16
16
|
} from './fileUtils';
|
|
17
|
+
import {
|
|
18
|
+
AttachmentPostUploadMiddlewareExecutor,
|
|
19
|
+
AttachmentPreUploadMiddlewareExecutor,
|
|
20
|
+
} from './middleware/attachmentManager';
|
|
17
21
|
import { StateStore } from '../store';
|
|
18
22
|
import { generateUUIDv4 } from '../utils';
|
|
19
23
|
import { DEFAULT_UPLOAD_SIZE_LIMIT_BYTES } from '../constants';
|
|
@@ -22,23 +26,14 @@ import type {
|
|
|
22
26
|
FileLike,
|
|
23
27
|
FileReference,
|
|
24
28
|
LocalAttachment,
|
|
25
|
-
|
|
26
|
-
LocalFileAttachment,
|
|
29
|
+
LocalNotImageAttachment,
|
|
27
30
|
LocalUploadAttachment,
|
|
28
|
-
LocalVideoAttachment,
|
|
29
|
-
LocalVoiceRecordingAttachment,
|
|
30
31
|
UploadPermissionCheckResult,
|
|
31
32
|
} from './types';
|
|
32
33
|
import type { ChannelResponse, DraftMessage, LocalMessage } from '../types';
|
|
33
34
|
import type { MessageComposer } from './messageComposer';
|
|
34
35
|
import { mergeWithDiff } from '../utils/mergeWith';
|
|
35
36
|
|
|
36
|
-
type LocalNotImageAttachment =
|
|
37
|
-
| LocalFileAttachment
|
|
38
|
-
| LocalAudioAttachment
|
|
39
|
-
| LocalVideoAttachment
|
|
40
|
-
| LocalVoiceRecordingAttachment;
|
|
41
|
-
|
|
42
37
|
export type FileUploadFilter = (file: Partial<LocalUploadAttachment>) => boolean;
|
|
43
38
|
|
|
44
39
|
export type AttachmentManagerState = {
|
|
@@ -71,6 +66,8 @@ const initState = ({
|
|
|
71
66
|
export class AttachmentManager {
|
|
72
67
|
readonly state: StateStore<AttachmentManagerState>;
|
|
73
68
|
readonly composer: MessageComposer;
|
|
69
|
+
readonly preUploadMiddlewareExecutor: AttachmentPreUploadMiddlewareExecutor;
|
|
70
|
+
readonly postUploadMiddlewareExecutor: AttachmentPostUploadMiddlewareExecutor;
|
|
74
71
|
private attachmentsByIdGetterCache: {
|
|
75
72
|
attachmentsById: Record<string, LocalAttachment>;
|
|
76
73
|
attachments: LocalAttachment[];
|
|
@@ -80,6 +77,13 @@ export class AttachmentManager {
|
|
|
80
77
|
this.composer = composer;
|
|
81
78
|
this.state = new StateStore<AttachmentManagerState>(initState({ message }));
|
|
82
79
|
this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
|
|
80
|
+
|
|
81
|
+
this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
|
|
82
|
+
composer,
|
|
83
|
+
});
|
|
84
|
+
this.postUploadMiddlewareExecutor = new AttachmentPostUploadMiddlewareExecutor({
|
|
85
|
+
composer,
|
|
86
|
+
});
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
get attachmentsById() {
|
|
@@ -122,10 +126,16 @@ export class AttachmentManager {
|
|
|
122
126
|
this.composer.updateConfig({ attachments: { acceptedFiles } });
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
/*
|
|
130
|
+
@deprecated attachments can be filtered using injecting pre-upload middleware
|
|
131
|
+
*/
|
|
125
132
|
get fileUploadFilter() {
|
|
126
133
|
return this.config.fileUploadFilter;
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
/*
|
|
137
|
+
@deprecated attachments can be filtered using injecting pre-upload middleware
|
|
138
|
+
*/
|
|
129
139
|
set fileUploadFilter(fileUploadFilter: AttachmentManagerConfig['fileUploadFilter']) {
|
|
130
140
|
this.composer.updateConfig({ attachments: { fileUploadFilter } });
|
|
131
141
|
}
|
|
@@ -333,9 +343,9 @@ export class AttachmentManager {
|
|
|
333
343
|
return { uploadBlocked: false };
|
|
334
344
|
};
|
|
335
345
|
|
|
336
|
-
|
|
346
|
+
static toLocalUploadAttachment = (
|
|
337
347
|
fileLike: FileReference | FileLike,
|
|
338
|
-
):
|
|
348
|
+
): LocalUploadAttachment => {
|
|
339
349
|
const file =
|
|
340
350
|
isFileReference(fileLike) || isFile(fileLike)
|
|
341
351
|
? fileLike
|
|
@@ -345,16 +355,13 @@ export class AttachmentManager {
|
|
|
345
355
|
mimeType: fileLike.type,
|
|
346
356
|
});
|
|
347
357
|
|
|
348
|
-
const uploadPermissionCheck = await this.getUploadConfigCheck(file);
|
|
349
|
-
|
|
350
358
|
const localAttachment: LocalUploadAttachment = {
|
|
351
359
|
file_size: file.size,
|
|
352
360
|
mime_type: file.type,
|
|
353
361
|
localMetadata: {
|
|
354
362
|
file,
|
|
355
363
|
id: generateUUIDv4(),
|
|
356
|
-
|
|
357
|
-
uploadState: uploadPermissionCheck.uploadBlocked ? 'blocked' : 'pending',
|
|
364
|
+
uploadState: 'pending',
|
|
358
365
|
},
|
|
359
366
|
type: getAttachmentTypeFromMimeType(file.type),
|
|
360
367
|
};
|
|
@@ -383,10 +390,26 @@ export class AttachmentManager {
|
|
|
383
390
|
return localAttachment;
|
|
384
391
|
};
|
|
385
392
|
|
|
393
|
+
// @deprecated use AttachmentManager.toLocalUploadAttachment(file)
|
|
394
|
+
fileToLocalUploadAttachment = async (
|
|
395
|
+
fileLike: FileReference | FileLike,
|
|
396
|
+
): Promise<LocalUploadAttachment> => {
|
|
397
|
+
const localAttachment = AttachmentManager.toLocalUploadAttachment(fileLike);
|
|
398
|
+
const uploadPermissionCheck = await this.getUploadConfigCheck(
|
|
399
|
+
localAttachment.localMetadata.file,
|
|
400
|
+
);
|
|
401
|
+
localAttachment.localMetadata.uploadPermissionCheck = uploadPermissionCheck;
|
|
402
|
+
localAttachment.localMetadata.uploadState = uploadPermissionCheck.uploadBlocked
|
|
403
|
+
? 'blocked'
|
|
404
|
+
: 'pending';
|
|
405
|
+
|
|
406
|
+
return localAttachment;
|
|
407
|
+
};
|
|
408
|
+
|
|
386
409
|
private ensureLocalUploadAttachment = async (
|
|
387
410
|
attachment: Partial<LocalUploadAttachment>,
|
|
388
411
|
) => {
|
|
389
|
-
if (!attachment.localMetadata?.file
|
|
412
|
+
if (!attachment.localMetadata?.file) {
|
|
390
413
|
this.client.notifications.addError({
|
|
391
414
|
message: 'File is required for upload attachment',
|
|
392
415
|
origin: { emitter: 'AttachmentManager', context: { attachment } },
|
|
@@ -395,6 +418,15 @@ export class AttachmentManager {
|
|
|
395
418
|
return;
|
|
396
419
|
}
|
|
397
420
|
|
|
421
|
+
if (!attachment.localMetadata.id) {
|
|
422
|
+
this.client.notifications.addError({
|
|
423
|
+
message: 'Local upload attachment missing local id',
|
|
424
|
+
origin: { emitter: 'AttachmentManager', context: { attachment } },
|
|
425
|
+
options: { type: 'validation:attachment:id:missing' },
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
398
430
|
if (!this.fileUploadFilter(attachment)) return;
|
|
399
431
|
|
|
400
432
|
const newAttachment = await this.fileToLocalUploadAttachment(
|
|
@@ -446,6 +478,7 @@ export class AttachmentManager {
|
|
|
446
478
|
return this.doDefaultUploadRequest(fileLike);
|
|
447
479
|
};
|
|
448
480
|
|
|
481
|
+
// @deprecated use attachmentManager.uploadFile(file)
|
|
449
482
|
uploadAttachment = async (attachment: LocalUploadAttachment) => {
|
|
450
483
|
if (!this.isUploadEnabled) return;
|
|
451
484
|
|
|
@@ -546,20 +579,78 @@ export class AttachmentManager {
|
|
|
546
579
|
return uploadedAttachment;
|
|
547
580
|
};
|
|
548
581
|
|
|
582
|
+
uploadFile = async (file: FileReference | FileLike) => {
|
|
583
|
+
const preUpload = await this.preUploadMiddlewareExecutor.execute({
|
|
584
|
+
eventName: 'prepare',
|
|
585
|
+
initialValue: {
|
|
586
|
+
attachment: AttachmentManager.toLocalUploadAttachment(file),
|
|
587
|
+
},
|
|
588
|
+
mode: 'concurrent',
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
let attachment: LocalUploadAttachment = preUpload.state.attachment;
|
|
592
|
+
|
|
593
|
+
if (preUpload.status === 'discard') return attachment;
|
|
594
|
+
// todo: remove with the next major release as filtering can be done in middleware
|
|
595
|
+
// should we return the attachment object?
|
|
596
|
+
if (!this.fileUploadFilter(attachment)) return attachment;
|
|
597
|
+
|
|
598
|
+
if (attachment.localMetadata.uploadState === 'blocked') {
|
|
599
|
+
this.upsertAttachments([attachment]);
|
|
600
|
+
return preUpload.state.attachment;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
attachment = {
|
|
604
|
+
...attachment,
|
|
605
|
+
localMetadata: {
|
|
606
|
+
...attachment.localMetadata,
|
|
607
|
+
uploadState: 'uploading',
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
this.upsertAttachments([attachment]);
|
|
611
|
+
|
|
612
|
+
let response: MinimumUploadRequestResult | undefined;
|
|
613
|
+
let error: Error | undefined;
|
|
614
|
+
try {
|
|
615
|
+
response = await this.doUploadRequest(file);
|
|
616
|
+
} catch (err) {
|
|
617
|
+
error = err instanceof Error ? err : undefined;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const postUpload = await this.postUploadMiddlewareExecutor.execute({
|
|
621
|
+
eventName: 'postProcess',
|
|
622
|
+
initialValue: {
|
|
623
|
+
attachment: {
|
|
624
|
+
...attachment,
|
|
625
|
+
localMetadata: {
|
|
626
|
+
...attachment.localMetadata,
|
|
627
|
+
uploadState: error ? 'failed' : 'finished',
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
error,
|
|
631
|
+
response,
|
|
632
|
+
},
|
|
633
|
+
mode: 'concurrent',
|
|
634
|
+
});
|
|
635
|
+
attachment = postUpload.state.attachment;
|
|
636
|
+
|
|
637
|
+
if (postUpload.status === 'discard') {
|
|
638
|
+
this.removeAttachments([attachment.localMetadata.id]);
|
|
639
|
+
return attachment;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
this.updateAttachment(attachment);
|
|
643
|
+
return attachment;
|
|
644
|
+
};
|
|
645
|
+
|
|
549
646
|
uploadFiles = async (files: FileReference[] | FileList | FileLike[]) => {
|
|
550
647
|
if (!this.isUploadEnabled) return;
|
|
551
648
|
const iterableFiles: FileReference[] | FileLike[] = isFileList(files)
|
|
552
649
|
? Array.from(files)
|
|
553
650
|
: files;
|
|
554
|
-
const attachments = await Promise.all(
|
|
555
|
-
iterableFiles.map(this.fileToLocalUploadAttachment),
|
|
556
|
-
);
|
|
557
651
|
|
|
558
|
-
return Promise.all(
|
|
559
|
-
|
|
560
|
-
.filter(this.fileUploadFilter)
|
|
561
|
-
.slice(0, this.availableUploadSlots)
|
|
562
|
-
.map(this.uploadAttachment),
|
|
652
|
+
return await Promise.all(
|
|
653
|
+
iterableFiles.slice(0, this.availableUploadSlots).map(this.uploadFile),
|
|
563
654
|
);
|
|
564
655
|
};
|
|
565
656
|
}
|
|
@@ -2,7 +2,9 @@ import type { LinkPreview } from '../linkPreviewsManager';
|
|
|
2
2
|
import type { FileUploadFilter } from '../attachmentManager';
|
|
3
3
|
import type { FileLike, FileReference } from '../types';
|
|
4
4
|
|
|
5
|
-
export type MinimumUploadRequestResult = { file: string; thumb_url?: string }
|
|
5
|
+
export type MinimumUploadRequestResult = { file: string; thumb_url?: string } & Partial<
|
|
6
|
+
Record<string, unknown>
|
|
7
|
+
>;
|
|
6
8
|
|
|
7
9
|
export type UploadRequestFn = (
|
|
8
10
|
fileLike: FileReference | FileLike,
|
|
@@ -39,7 +41,6 @@ export type AttachmentManagerConfig = {
|
|
|
39
41
|
* describing which file types are allowed to be selected when uploading files.
|
|
40
42
|
*/
|
|
41
43
|
acceptedFiles: string[];
|
|
42
|
-
// todo: refactor this. We want a pipeline where it would be possible to customize the preparation, upload, and post-upload steps.
|
|
43
44
|
/** Function that allows to customize the upload request. */
|
|
44
45
|
doUploadRequest?: UploadRequestFn;
|
|
45
46
|
};
|
|
@@ -700,7 +700,7 @@ export class MessageComposer extends WithSubscriptions {
|
|
|
700
700
|
id: this.id,
|
|
701
701
|
mentioned_users: [],
|
|
702
702
|
parent_id: this.threadId ?? undefined,
|
|
703
|
-
pinned_at: null,
|
|
703
|
+
pinned_at: this.editedMessage?.pinned_at || null,
|
|
704
704
|
reaction_groups: null,
|
|
705
705
|
status: this.editedMessage ? this.editedMessage.status : 'sending',
|
|
706
706
|
text,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MiddlewareExecutor } from '../../../../middleware';
|
|
2
|
+
import type {
|
|
3
|
+
AttachmentPostUploadMiddlewareExecutorOptions,
|
|
4
|
+
AttachmentPostUploadMiddlewareState,
|
|
5
|
+
} from '../types';
|
|
6
|
+
import { createPostUploadAttachmentEnrichmentMiddleware } from './attachmentEnrichment';
|
|
7
|
+
import { createUploadErrorHandlerMiddleware } from './uploadErrorHandler';
|
|
8
|
+
|
|
9
|
+
export class AttachmentPostUploadMiddlewareExecutor extends MiddlewareExecutor<
|
|
10
|
+
AttachmentPostUploadMiddlewareState,
|
|
11
|
+
'postProcess'
|
|
12
|
+
> {
|
|
13
|
+
constructor({ composer }: AttachmentPostUploadMiddlewareExecutorOptions) {
|
|
14
|
+
super();
|
|
15
|
+
this.use([
|
|
16
|
+
createUploadErrorHandlerMiddleware(composer),
|
|
17
|
+
createPostUploadAttachmentEnrichmentMiddleware(),
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { MiddlewareHandlerParams } from '../../../../middleware';
|
|
2
|
+
import type {
|
|
3
|
+
AttachmentPostUploadMiddleware,
|
|
4
|
+
AttachmentPostUploadMiddlewareState,
|
|
5
|
+
} from '../types';
|
|
6
|
+
import { isLocalImageAttachment } from '../../../attachmentIdentity';
|
|
7
|
+
import type { LocalNotImageAttachment } from '../../../types';
|
|
8
|
+
|
|
9
|
+
export const createPostUploadAttachmentEnrichmentMiddleware =
|
|
10
|
+
(): AttachmentPostUploadMiddleware => ({
|
|
11
|
+
id: 'stream-io/attachment-manager-middleware/post-upload-enrichment',
|
|
12
|
+
handlers: {
|
|
13
|
+
postProcess: ({
|
|
14
|
+
state,
|
|
15
|
+
discard,
|
|
16
|
+
forward,
|
|
17
|
+
next,
|
|
18
|
+
}: MiddlewareHandlerParams<AttachmentPostUploadMiddlewareState>) => {
|
|
19
|
+
const { attachment, error, response } = state;
|
|
20
|
+
if (error) return forward();
|
|
21
|
+
if (!attachment || !response) return discard();
|
|
22
|
+
|
|
23
|
+
const enrichedAttachment = { ...attachment };
|
|
24
|
+
if (isLocalImageAttachment(attachment)) {
|
|
25
|
+
if (attachment.localMetadata.previewUri) {
|
|
26
|
+
URL.revokeObjectURL(attachment.localMetadata.previewUri);
|
|
27
|
+
delete enrichedAttachment.localMetadata.previewUri;
|
|
28
|
+
}
|
|
29
|
+
enrichedAttachment.image_url = response.file;
|
|
30
|
+
} else {
|
|
31
|
+
(enrichedAttachment as LocalNotImageAttachment).asset_url = response.file;
|
|
32
|
+
}
|
|
33
|
+
if (response.thumb_url) {
|
|
34
|
+
(enrichedAttachment as LocalNotImageAttachment).thumb_url = response.thumb_url;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return next({
|
|
38
|
+
...state,
|
|
39
|
+
attachment: enrichedAttachment,
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|