Skip to content

Commit e7c8a3b

Browse files
Post-merge fixes
1 parent 99dfcfc commit e7c8a3b

16 files changed

+461
-359
lines changed

src/messageComposer/attachmentManager.ts

Lines changed: 120 additions & 94 deletions
Large diffs are not rendered by default.

src/messageComposer/fileUtils.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { LocalAttachment, LocalImageAttachment } from './types';
2-
import type { Attachment, DefaultGenerics, ExtendableGenerics, UR } from '../types';
2+
import type { Attachment, UR } from '../types';
33

4-
export const isBlobButNotFile = (obj: unknown): obj is Blob => obj instanceof Blob && !(obj instanceof File);
4+
export const isBlobButNotFile = (obj: unknown): obj is Blob =>
5+
obj instanceof Blob && !(obj instanceof File);
56

67
export const createFileFromBlobs = ({
78
blobsArray,
@@ -48,18 +49,21 @@ export const getAttachmentTypeFromMimeType = (mimeType: string) => {
4849
return 'file';
4950
};
5051

51-
export const isFile = (fileLike: File | Blob): fileLike is File => !!(fileLike as File).lastModified;
52+
export const isFile = (fileLike: File | Blob): fileLike is File =>
53+
!!(fileLike as File).lastModified;
5254

53-
export const isScrapedContent = <SCG extends ExtendableGenerics = DefaultGenerics>(attachment: Attachment<SCG>) =>
55+
export const isScrapedContent = (attachment: Attachment) =>
5456
attachment.og_scrape_url || attachment.title_link;
5557

56-
export const isUploadedImage = <SCG extends ExtendableGenerics = DefaultGenerics>(attachment: Attachment<SCG>) =>
58+
export const isUploadedImage = (attachment: Attachment) =>
5759
attachment.type === 'image' && !isScrapedContent(attachment);
5860

59-
export const isLocalAttachment = <SCG extends ExtendableGenerics = DefaultGenerics>(
60-
attachment: UR,
61-
): attachment is LocalAttachment<SCG> => !!(attachment as LocalAttachment<SCG>).localMetadata?.id;
61+
// @ts-expect-error tmp
62+
export const isLocalAttachment = (attachment: UR): attachment is LocalAttachment =>
63+
!!(attachment as LocalAttachment).localMetadata?.id;
6264

63-
export const isLocalImageAttachment = <SCG extends ExtendableGenerics = DefaultGenerics>(
64-
attachment: Attachment<SCG> | LocalAttachment<SCG>,
65-
): attachment is LocalImageAttachment<SCG> => isUploadedImage(attachment) && isLocalAttachment(attachment);
65+
export const isLocalImageAttachment = (
66+
attachment: Attachment | LocalAttachment,
67+
): attachment is LocalImageAttachment =>
68+
// @ts-expect-error tmp
69+
isUploadedImage(attachment) && isLocalAttachment(attachment);

src/messageComposer/linkPreviewsManager.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { find } from 'linkifyjs';
22
import { StateStore } from '../store';
3-
import { debounce, DebouncedFunc } from '../utils';
3+
import type { DebouncedFunc } from '../utils';
4+
import { debounce } from '../utils';
45
import type { StreamChat } from '../client';
56
import type {
6-
DefaultGenerics,
77
DraftMessage,
8-
ExtendableGenerics,
98
FormatMessageResponse,
109
MessageResponseBase,
1110
OGAttachment,
@@ -65,17 +64,17 @@ export type LinkPreviewsManagerState = {
6564
previews: LinkPreviewMap;
6665
};
6766

68-
export type LinkPreviewsManagerOptions<SCG extends ExtendableGenerics = DefaultGenerics> = {
69-
client: StreamChat<SCG>;
67+
export type LinkPreviewsManagerOptions = {
68+
client: StreamChat;
7069
/** Number of milliseconds to debounce firing the URL enrichment queries when typing. The default value is 1500(ms). */
7170
debounceURLEnrichmentMs?: number;
72-
message?: DraftMessage<SCG> | MessageResponseBase<SCG> | FormatMessageResponse<SCG>;
71+
message?: DraftMessage | MessageResponseBase | FormatMessageResponse;
7372
};
7473

75-
const initState = <SCG extends ExtendableGenerics = DefaultGenerics>(
76-
message?: DraftMessage<SCG> | MessageResponseBase<SCG> | FormatMessageResponse<SCG>,
77-
): LinkPreviewsManagerState => {
78-
return message
74+
const initState = (
75+
message?: DraftMessage | MessageResponseBase | FormatMessageResponse,
76+
): LinkPreviewsManagerState =>
77+
message
7978
? {
8079
previews:
8180
message.attachments?.reduce<LinkPreviewMap>((acc, attachment) => {
@@ -93,37 +92,45 @@ const initState = <SCG extends ExtendableGenerics = DefaultGenerics>(
9392
: {
9493
previews: new Map<LinkURL, LinkPreview>(),
9594
};
96-
};
9795

9896
/*
9997
docs:
10098
You can customize function to identify URLs in a string and request OG data by overriding findURLFn?: (text: string) => string[];
10199
*/
102100

103-
export class LinkPreviewsManager<SCG extends ExtendableGenerics = DefaultGenerics> implements ILinkPreviewsManager {
101+
export class LinkPreviewsManager implements ILinkPreviewsManager {
104102
state: StateStore<LinkPreviewsManagerState>;
105103
findAndEnrichUrls: DebouncedFunc<(text: string) => void>;
106-
private client: StreamChat<SCG>;
104+
private client: StreamChat;
107105
private shouldDiscardEnrichQueries = false;
108106

109-
constructor({ client, debounceURLEnrichmentMs = 1500, message }: LinkPreviewsManagerOptions<SCG>) {
107+
constructor({
108+
client,
109+
debounceURLEnrichmentMs = 1500,
110+
message,
111+
}: LinkPreviewsManagerOptions) {
110112
this.client = client;
111113
this.state = new StateStore<LinkPreviewsManagerState>(initState(message));
112-
this.findAndEnrichUrls = debounce(this._findAndEnrichUrls.bind(this), debounceURLEnrichmentMs);
114+
this.findAndEnrichUrls = debounce(
115+
this._findAndEnrichUrls.bind(this),
116+
debounceURLEnrichmentMs,
117+
);
113118
}
114119

115120
get previews() {
116121
return this.state.getLatestValue().previews;
117122
}
118123

119-
initState = ({ message }: { message?: DraftMessage<SCG> | MessageResponseBase<SCG> } = {}) => {
124+
initState = ({ message }: { message?: DraftMessage | MessageResponseBase } = {}) => {
120125
this.state.next(initState(message));
121126
};
122127

123128
private _findAndEnrichUrls = (text: string) => {
124129
const urls = this.findURLs(text).filter((url) => {
125130
const existingPreviewLink = this.previews.get(url);
126-
return !existingPreviewLink || existingPreviewLink.status !== LinkPreviewStatus.FAILED;
131+
return (
132+
!existingPreviewLink || existingPreviewLink.status !== LinkPreviewStatus.FAILED
133+
);
127134
});
128135

129136
this.shouldDiscardEnrichQueries = !urls.length;
@@ -132,7 +139,10 @@ export class LinkPreviewsManager<SCG extends ExtendableGenerics = DefaultGeneric
132139
}
133140

134141
const addedLinkPreviews = urls.map((url) => {
135-
const linkPreview = new LinkPreview({ data: { og_scrape_url: url }, status: LinkPreviewStatus.LOADING });
142+
const linkPreview = new LinkPreview({
143+
data: { og_scrape_url: url },
144+
status: LinkPreviewStatus.LOADING,
145+
});
136146
this.client
137147
.enrichURL(url)
138148
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -147,7 +157,9 @@ export class LinkPreviewsManager<SCG extends ExtendableGenerics = DefaultGeneric
147157
});
148158

149159
const newLinkPreviews = new Map(this.previews);
150-
addedLinkPreviews.forEach((linkPreview) => newLinkPreviews.set(linkPreview.og_scrape_url, linkPreview));
160+
addedLinkPreviews.forEach((linkPreview) =>
161+
newLinkPreviews.set(linkPreview.og_scrape_url, linkPreview),
162+
);
151163
this.state.partialNext({ previews: newLinkPreviews });
152164
};
153165

src/messageComposer/messageComposer.ts

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { LinkPreviewsManager } from './linkPreviewsManager';
22
import { AttachmentManager } from './attachmentManager';
33
import { TextComposer } from './textComposer';
4-
import { Channel } from '../channel';
54
import { StateStore } from '../store';
65
import { formatMessage, generateUUIDv4 } from '../utils';
6+
import type { Channel } from '../channel';
77
import type {
8-
DefaultGenerics,
98
DraftMessage,
109
DraftResponse,
1110
EventTypes,
12-
ExtendableGenerics,
1311
FormatMessageResponse,
1412
MessageResponse,
1513
MessageResponseBase,
@@ -21,10 +19,10 @@ import { mergeWith } from '../utils/mergeWith';
2119
1/ decide whether lastChange timestamp is necessary
2220
*/
2321

24-
export type MessageComposerState<SCG extends ExtendableGenerics = DefaultGenerics> = {
22+
export type MessageComposerState = {
2523
id: string;
2624
lastChange: Date | null;
27-
quotedMessage: FormatMessageResponse<SCG> | null;
25+
quotedMessage: FormatMessageResponse | null;
2826
};
2927

3028
export type MessageComposerConfig = {
@@ -34,26 +32,25 @@ export type MessageComposerConfig = {
3432
urlPreviewEnabled?: boolean;
3533
};
3634

37-
export type MessageComposerOptions<SCG extends ExtendableGenerics = DefaultGenerics> = {
38-
channel: Channel<SCG>;
39-
composition?: DraftResponse<SCG> | MessageResponse<SCG> | FormatMessageResponse<SCG>;
35+
export type MessageComposerOptions = {
36+
channel: Channel;
37+
composition?: DraftResponse | MessageResponse | FormatMessageResponse;
4038
config?: Partial<MessageComposerConfig>;
4139
threadId?: string;
4240
};
4341

44-
const isMessageDraft = <SCG extends ExtendableGenerics = DefaultGenerics>(
45-
composition: DraftResponse<SCG> | MessageResponse<SCG> | FormatMessageResponse<SCG>,
46-
): composition is DraftResponse<SCG> => !!composition.message;
42+
const isMessageDraft = (composition: unknown): composition is DraftResponse =>
43+
!!(composition as { message?: DraftMessage }).message;
4744

48-
const initState = <SCG extends ExtendableGenerics = DefaultGenerics>(
49-
composition?: DraftResponse<SCG> | MessageResponse<SCG> | FormatMessageResponse<SCG>,
50-
): MessageComposerState<SCG> =>
45+
const initState = (
46+
composition?: DraftResponse | MessageResponse | FormatMessageResponse,
47+
): MessageComposerState =>
5148
composition
5249
? {
5350
id: isMessageDraft(composition) ? composition.message.id : composition.id,
5451
lastChange: new Date(),
5552
quotedMessage: composition.quoted_message
56-
? formatMessage(composition.quoted_message as MessageResponseBase<SCG>)
53+
? formatMessage(composition.quoted_message as MessageResponseBase)
5754
: null,
5855
}
5956
: {
@@ -67,28 +64,31 @@ const DEFAULT_COMPOSER_CONFIG: MessageComposerConfig = {
6764
urlPreviewEnabled: false,
6865
};
6966

70-
export class MessageComposer<SCG extends ExtendableGenerics = DefaultGenerics> {
71-
channel: Channel<SCG>;
67+
export class MessageComposer {
68+
channel: Channel;
7269
config: MessageComposerConfig;
73-
state: StateStore<MessageComposerState<SCG>>;
74-
attachmentManager: AttachmentManager<SCG>;
75-
linkPreviewsManager: LinkPreviewsManager<SCG>;
76-
// todo: mediaRecorder: MediaRecorderController<SCG>;
77-
textComposer: TextComposer<SCG>;
70+
state: StateStore<MessageComposerState>;
71+
attachmentManager: AttachmentManager;
72+
linkPreviewsManager: LinkPreviewsManager;
73+
// todo: mediaRecorder: MediaRecorderController;
74+
textComposer: TextComposer;
7875
threadId: string | null;
7976
private unsubscribeFunctions: Set<() => void> = new Set();
8077

81-
constructor({ channel, composition, config = {}, threadId }: MessageComposerOptions<SCG>) {
78+
constructor({ channel, composition, config = {}, threadId }: MessageComposerOptions) {
8279
this.channel = channel;
8380
this.threadId = threadId ?? null;
8481
// todo: solve ts-ignore
85-
// @ts-ignore
86-
this.config = mergeWith(config, DEFAULT_COMPOSER_CONFIG);
87-
const message = composition && (isMessageDraft(composition) ? composition.message : composition);
88-
this.attachmentManager = new AttachmentManager<SCG>({ channel, message });
89-
this.linkPreviewsManager = new LinkPreviewsManager<SCG>({ client: channel.getClient(), message });
90-
this.textComposer = new TextComposer<SCG>({ composer: this, message });
91-
this.state = new StateStore<MessageComposerState<SCG>>(initState<SCG>(composition));
82+
this.config = mergeWith(DEFAULT_COMPOSER_CONFIG, config);
83+
const message =
84+
composition && (isMessageDraft(composition) ? composition.message : composition);
85+
this.attachmentManager = new AttachmentManager({ channel, message });
86+
this.linkPreviewsManager = new LinkPreviewsManager({
87+
client: channel.getClient(),
88+
message,
89+
});
90+
this.textComposer = new TextComposer({ composer: this, message });
91+
this.state = new StateStore<MessageComposerState>(initState(composition));
9292
}
9393

9494
get client() {
@@ -103,8 +103,12 @@ export class MessageComposer<SCG extends ExtendableGenerics = DefaultGenerics> {
103103
return this.state.getLatestValue().quotedMessage;
104104
}
105105

106-
initState = ({ composition }: { composition?: DraftResponse<SCG> | MessageResponse<SCG> } = {}) => {
107-
const message = composition && (composition.message as DraftMessage<SCG> | MessageResponseBase<SCG>);
106+
initState = ({
107+
composition,
108+
}: { composition?: DraftResponse | MessageResponse } = {}) => {
109+
const message = isMessageDraft(composition)
110+
? composition.message
111+
: (composition as MessageResponse);
108112
this.attachmentManager.initState({ message });
109113
this.linkPreviewsManager.initState({ message });
110114
this.textComposer.initState({ message });
@@ -133,7 +137,12 @@ export class MessageComposer<SCG extends ExtendableGenerics = DefaultGenerics> {
133137

134138
private subscribeMessageUpdated = () => {
135139
// todo: test the impact of 'reaction.new', 'reaction.deleted', 'reaction.updated'
136-
const eventTypes: EventTypes[] = ['message.updated', 'reaction.new', 'reaction.deleted', 'reaction.updated'];
140+
const eventTypes: EventTypes[] = [
141+
'message.updated',
142+
'reaction.new',
143+
'reaction.deleted',
144+
'reaction.updated',
145+
];
137146

138147
const unsubscribeFunctions = eventTypes.map(
139148
(eventType) =>
@@ -163,25 +172,32 @@ export class MessageComposer<SCG extends ExtendableGenerics = DefaultGenerics> {
163172

164173
private subscribeDraftUpdated = () =>
165174
this.client.on('draft.updated', (event) => {
166-
const draft = event.draft as DraftResponse<SCG>;
167-
if (!draft || draft.parent_id !== this.threadId || draft.message.id !== this.id) return;
175+
const draft = event.draft as DraftResponse;
176+
if (!draft || draft.parent_id !== this.threadId || draft.message.id !== this.id)
177+
return;
168178
this.initState({ composition: draft });
169179
}).unsubscribe;
170180

171181
private subscribeDraftDeleted = () =>
172182
this.client.on('draft.deleted', (event) => {
173-
const draft = event.draft as DraftResponse<SCG>;
174-
if (!draft || draft.parent_id !== this.threadId || draft.message.id !== this.id) return;
183+
const draft = event.draft as DraftResponse;
184+
if (!draft || draft.parent_id !== this.threadId || draft.message.id !== this.id)
185+
return;
175186
this.clear();
176187
}).unsubscribe;
177188

178189
private subscribeTextChanged = () =>
179190
this.textComposer.state.subscribe((nextValue, previousValue) => {
180-
if (!this.config.urlPreviewEnabled || !nextValue.text || nextValue.text === previousValue?.text) return;
191+
if (
192+
!this.config.urlPreviewEnabled ||
193+
!nextValue.text ||
194+
nextValue.text === previousValue?.text
195+
)
196+
return;
181197
this.linkPreviewsManager.findAndEnrichUrls(nextValue.text);
182198
});
183199

184-
setQuotedMessage = (quotedMessage: FormatMessageResponse<SCG> | null) => {
200+
setQuotedMessage = (quotedMessage: FormatMessageResponse | null) => {
185201
this.state.partialNext({ quotedMessage });
186202
};
187203

0 commit comments

Comments
 (0)