Skip to content

Commit fe3c100

Browse files
WIP: composer queue cache
1 parent e9f0175 commit fe3c100

File tree

2 files changed

+92
-26
lines changed

2 files changed

+92
-26
lines changed

src/components/MessageInput/hooks/messageComposer/useMessageComposer.ts

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,78 @@ import { useEffect, useMemo, useRef, useState } from 'react';
22
import { MessageComposer } from 'stream-chat';
33
import { useThreadContext } from '../../../Threads';
44
import { useChannelStateContext, useMessageInputContext } from '../../../../context';
5-
import type { LocalMessage, TextComposerMiddleware } from 'stream-chat';
5+
import type { LocalMessage } from 'stream-chat';
66
import { useLegacyThreadContext } from '../../../Thread';
77

8-
export type UseMessageComposerParams = {
9-
textComposerMiddleware?: TextComposerMiddleware[];
10-
};
8+
class FixedSizeQueueCache<T> {
9+
private elements: Array<T>;
10+
private size: number;
11+
constructor(size: number) {
12+
if (!size) throw new Error('Size must be greater than 0');
13+
this.elements = [];
14+
this.size = size;
15+
}
16+
17+
add(...values: T[]) {
18+
const pushableValues =
19+
values.length > this.size ? values.slice(values.length - this.size) : values;
20+
21+
if (pushableValues.length === this.size) {
22+
// this.elements.splice(0, this.size - 1);
23+
this.elements.length = 0;
24+
this.elements.push(...pushableValues);
25+
} else {
26+
for (const value of pushableValues) {
27+
if (this.elements.length >= this.size) {
28+
this.elements.shift();
29+
}
30+
31+
this.elements.push(value);
32+
}
33+
}
34+
}
35+
36+
// returns value without shifting it to the end of the array
37+
peek(predicate: (element: T) => boolean) {
38+
// start searching from the most recent (end of array)
39+
for (let index = 0; index < this.elements.length; index++) {
40+
const element = this.elements[this.elements.length - 1 - index];
41+
const predicateResult = predicate(element);
42+
43+
if (predicateResult) return element;
44+
}
45+
46+
return null;
47+
}
48+
49+
get(predicate: (element: T) => boolean) {
50+
const foundElement = this.peek(predicate);
51+
52+
if (foundElement && this.elements.indexOf(foundElement) !== this.size - 1) {
53+
this.elements.splice(this.elements.indexOf(foundElement), 1);
54+
this.elements.push(foundElement);
55+
}
56+
57+
return foundElement;
58+
}
59+
60+
toArray() {
61+
return [...this.elements];
62+
}
63+
}
64+
65+
export type UseMessageComposerParams = unknown;
1166

12-
export const useMessageComposer = ({
13-
textComposerMiddleware,
14-
}: UseMessageComposerParams = {}) => {
67+
const queueCache = new FixedSizeQueueCache<MessageComposer>(64);
68+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
69+
export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
1570
const { channel } = useChannelStateContext();
1671
const { message: editedMessage } = useMessageInputContext();
1772
// legacy thread will receive new composer
1873
const { legacyThread: parentMessage, messageDraft } = useLegacyThreadContext();
19-
const thread = useThreadContext();
74+
const threadInstance = useThreadContext();
2075
const detachedMessageComposerRef = useRef<MessageComposer>(
21-
new MessageComposer({ channel }),
76+
new MessageComposer({ channel, tag: 'detached' }),
2277
);
2378

2479
const [cachedEditedMessage, setCachedEditedMessage] = useState<
@@ -42,25 +97,37 @@ export const useMessageComposer = ({
4297

4398
const messageComposer = useMemo(() => {
4499
if (cachedEditedMessage) {
45-
const composer = new MessageComposer({ channel, composition: cachedEditedMessage });
46-
// todo: remove with factory functions introduction
47-
if (textComposerMiddleware) {
48-
composer.textComposer.use(textComposerMiddleware);
49-
}
50-
return composer;
51-
} else if (thread) {
52-
return thread.messageComposer;
100+
const tag = `edited-message-${cachedEditedMessage.id}`;
101+
102+
const element = queueCache.get((element) => element.tag === tag);
103+
if (element) return element;
104+
105+
const c = new MessageComposer({
106+
channel,
107+
composition: cachedEditedMessage,
108+
tag,
109+
});
110+
111+
// FIXME: don't like this side effect here
112+
queueCache.add(c);
113+
return c;
114+
} else if (threadInstance) {
115+
return threadInstance.messageComposer;
53116
} else if (cachedParentMessage) {
54-
const composer = new MessageComposer({
117+
const tag = `parent-message-${cachedParentMessage.id}`;
118+
119+
const element = queueCache.get((element) => element.tag === tag);
120+
if (element) return element;
121+
122+
const c = new MessageComposer({
55123
channel,
56124
composition: messageDraft,
125+
tag,
57126
threadId: cachedParentMessage.id,
58127
});
59-
// todo: remove with factory functions introduction
60-
if (textComposerMiddleware) {
61-
composer.textComposer.use(textComposerMiddleware);
62-
}
63-
return composer;
128+
129+
queueCache.add(c);
130+
return c;
64131
} else if (channel) {
65132
return channel.messageComposer;
66133
} else {
@@ -72,8 +139,7 @@ export const useMessageComposer = ({
72139
cachedParentMessage,
73140
channel,
74141
messageDraft, // TODO: set message draft after the fact
75-
textComposerMiddleware,
76-
thread,
142+
threadInstance,
77143
]);
78144

79145
useEffect(() => {

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12157,7 +12157,7 @@ [email protected]:
1215712157

1215812158
"stream-chat@https://github.com/GetStream/stream-chat-js.git#feat/message-composer":
1215912159
version "0.0.0-development"
12160-
resolved "https://github.com/GetStream/stream-chat-js.git#d621fc9f455a4274ae5f2d80b8e80cdade2d446a"
12160+
resolved "https://github.com/GetStream/stream-chat-js.git#4236389e4ddd2adf4eed23149a7f732a79d9ec1a"
1216112161
dependencies:
1216212162
"@types/jsonwebtoken" "^9.0.8"
1216312163
"@types/ws" "^8.5.14"

0 commit comments

Comments
 (0)