Skip to content

Commit 6a56b12

Browse files
committed
Merge branch 'master' into feat/offline-support-rework
# Conflicts: # src/channel_manager.ts
2 parents c5d6fc6 + e4db506 commit 6a56b12

File tree

11 files changed

+287
-133
lines changed

11 files changed

+287
-133
lines changed

src/channel_manager.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
DEFAULT_QUERY_CHANNELS_RETRY_COUNT,
2727
DEFAULT_QUERY_CHANNELS_SECONDS_BETWEEN_RETRIES,
2828
} from './constants';
29+
import { WithSubscriptions } from './utils/WithSubscriptions';
2930

3031
export type ChannelManagerPagination = {
3132
filters: ChannelFilters;
@@ -154,10 +155,9 @@ export const DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS = {
154155
*
155156
* @internal
156157
*/
157-
export class ChannelManager {
158+
export class ChannelManager extends WithSubscriptions {
158159
public readonly state: StateStore<ChannelManagerState>;
159160
private client: StreamChat;
160-
private unsubscribeFunctions: Set<() => void> = new Set();
161161
private eventHandlers: Map<string, EventHandlerType> = new Map();
162162
private eventHandlerOverrides: Map<string, EventHandlerOverrideType> = new Map();
163163
private options: ChannelManagerOptions = {};
@@ -173,6 +173,8 @@ export class ChannelManager {
173173
eventHandlerOverrides?: ChannelManagerEventHandlerOverrides;
174174
options?: ChannelManagerOptions;
175175
}) {
176+
super();
177+
176178
this.id = `channel-manager-${generateUUIDv4()}`;
177179
this.client = client;
178180
this.state = new StateStore<ChannelManagerState>({
@@ -706,20 +708,15 @@ export class ChannelManager {
706708
};
707709

708710
public registerSubscriptions = () => {
709-
if (this.unsubscribeFunctions.size) {
711+
if (this.hasSubscriptions) {
710712
// Already listening for events and changes
711713
return;
712714
}
713715

714716
for (const eventType of Object.keys(channelManagerEventToHandlerMapping)) {
715-
this.unsubscribeFunctions.add(
717+
this.addUnsubscribeFunction(
716718
this.client.on(eventType, this.subscriptionOrOverride).unsubscribe,
717719
);
718720
}
719721
};
720-
721-
public unregisterSubscriptions = () => {
722-
this.unsubscribeFunctions.forEach((cleanupFunction) => cleanupFunction());
723-
this.unsubscribeFunctions.clear();
724-
};
725722
}

src/messageComposer/attachmentManager.ts

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,35 @@ const initState = ({
7171
export class AttachmentManager {
7272
readonly state: StateStore<AttachmentManagerState>;
7373
readonly composer: MessageComposer;
74+
private attachmentsByIdGetterCache: {
75+
attachmentsById: Record<string, LocalAttachment>;
76+
attachments: LocalAttachment[];
77+
};
7478

7579
constructor({ composer, message }: AttachmentManagerOptions) {
7680
this.composer = composer;
7781
this.state = new StateStore<AttachmentManagerState>(initState({ message }));
82+
this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
83+
}
84+
85+
get attachmentsById() {
86+
const { attachments } = this.state.getLatestValue();
87+
88+
if (attachments !== this.attachmentsByIdGetterCache.attachments) {
89+
this.attachmentsByIdGetterCache.attachments = attachments;
90+
this.attachmentsByIdGetterCache.attachmentsById = attachments.reduce<
91+
Record<string, LocalAttachment>
92+
>((newAttachmentsById, attachment) => {
93+
// should never happen but does not hurt to check
94+
if (!attachment.localMetadata.id) return newAttachmentsById;
95+
96+
newAttachmentsById[attachment.localMetadata.id] ??= attachment;
97+
98+
return newAttachmentsById;
99+
}, {});
100+
}
101+
102+
return this.attachmentsByIdGetterCache.attachmentsById;
78103
}
79104

80105
get client() {
@@ -176,44 +201,47 @@ export class AttachmentManager {
176201
this.state.next(initState({ message }));
177202
};
178203

179-
getAttachmentIndex = (localId: string) =>
180-
this.attachments.findIndex(
181-
(attachment) =>
182-
attachment.localMetadata.id && localId === attachment.localMetadata?.id,
183-
);
204+
getAttachmentIndex = (localId: string) => {
205+
const attachmentsById = this.attachmentsById;
206+
207+
return this.attachments.indexOf(attachmentsById[localId]);
208+
};
184209

185210
upsertAttachments = (attachmentsToUpsert: LocalAttachment[]) => {
186211
if (!attachmentsToUpsert.length) return;
187-
const stateAttachments = this.attachments;
188-
const attachments = [...this.attachments];
189-
attachmentsToUpsert.forEach((upsertedAttachment) => {
190-
const attachmentIndex = this.getAttachmentIndex(
191-
upsertedAttachment.localMetadata.id,
192-
);
193212

194-
if (attachmentIndex === -1) {
195-
const localAttachment = ensureIsLocalAttachment(upsertedAttachment);
196-
if (localAttachment) attachments.push(localAttachment);
213+
const currentAttachments = this.attachments;
214+
const newAttachments = [...currentAttachments];
215+
216+
attachmentsToUpsert.forEach((attachment) => {
217+
const targetAttachmentIndex = this.getAttachmentIndex(attachment.localMetadata?.id);
218+
219+
if (targetAttachmentIndex < 0) {
220+
const localAttachment = ensureIsLocalAttachment(attachment);
221+
if (localAttachment) newAttachments.push(localAttachment);
197222
} else {
223+
// do not re-organize newAttachments array otherwise indexing would no longer work
224+
// replace in place only with the attachments with the same id's
198225
const merged = mergeWithDiff<LocalAttachment>(
199-
stateAttachments[attachmentIndex] ?? {},
200-
upsertedAttachment,
226+
currentAttachments[targetAttachmentIndex],
227+
attachment,
201228
);
202229
const updatesOnMerge = merged.diff && Object.keys(merged.diff.children).length;
203230
if (updatesOnMerge) {
204231
const localAttachment = ensureIsLocalAttachment(merged.result);
205-
if (localAttachment) attachments.splice(attachmentIndex, 1, localAttachment);
232+
if (localAttachment)
233+
newAttachments.splice(targetAttachmentIndex, 1, localAttachment);
206234
}
207235
}
208236
});
209237

210-
this.state.partialNext({ attachments });
238+
this.state.partialNext({ attachments: newAttachments });
211239
};
212240

213241
removeAttachments = (localAttachmentIds: string[]) => {
214242
this.state.partialNext({
215243
attachments: this.attachments.filter(
216-
(att) => !localAttachmentIds.includes(att.localMetadata?.id),
244+
(attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id),
217245
),
218246
});
219247
};

src/messageComposer/configuration/types.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,16 @@ export type AttachmentManagerConfig = {
4141
/** Function that allows to customize the upload request. */
4242
doUploadRequest?: UploadRequestFn;
4343
};
44-
export type LinkPreviewConfig = {
45-
/** Custom function to react to link preview dismissal */
46-
onLinkPreviewDismissed?: (linkPreview: LinkPreview) => void;
47-
};
48-
export type LinkPreviewsManagerConfig = LinkPreviewConfig & {
44+
45+
export type LinkPreviewsManagerConfig = {
4946
/** Number of milliseconds to debounce firing the URL enrichment queries when typing. The default value is 1500(ms). */
5047
debounceURLEnrichmentMs: number;
5148
/** Allows for toggling the URL enrichment and link previews in `MessageInput`. By default, the feature is disabled. */
5249
enabled: boolean;
5350
/** Custom function to identify URLs in a string and request OG data */
5451
findURLFn: (text: string) => string[];
52+
/** Custom function to react to link preview dismissal */
53+
onLinkPreviewDismissed?: (linkPreview: LinkPreview) => void;
5554
};
5655

5756
export type MessageComposerConfig = {

0 commit comments

Comments
 (0)