Skip to content

Commit 8fa096a

Browse files
authored
feat: introduce selective command enablement based on message editing or quoting in message composer (#3171)
1 parent 6735fa1 commit 8fa096a

32 files changed

Lines changed: 572 additions & 82 deletions

examples/vite/src/AppSettings/ActionsMenu/AttachmentPromptDialog.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const defaultUnsupportedObjectAttachment = {
3030
uploadState: 'finished',
3131
},
3232
debug: true,
33-
metadata: { randomNumber: 7, source: 'vite-preview' },
3433
title: 'custom payload',
3534
type: 'custom',
3635
};

examples/vite/src/AppSettings/ActionsMenu/WebSocketEventPromptDialog/WebSocketEventPromptDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactNode, Ref } from 'react';
22
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3+
import type { Event } from 'stream-chat';
34
import {
45
Dropdown,
56
type DropdownTriggerProps,
@@ -808,7 +809,7 @@ export const WebSocketEventPromptDialog = ({
808809
throw new Error('Payload must be a JSON object');
809810
}
810811

811-
return parsedPayload as Record<string, unknown>;
812+
return parsedPayload as Partial<Event>;
812813
}, []);
813814

814815
const emitConfiguredEvent = useCallback(

examples/vite/src/AppSettings/ActionsMenu/WebSocketEventPromptDialog/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ChannelMemberResponse, UserResponse } from 'stream-chat';
12
import type { SupportedWebsocketEventType } from './websocketEventTemplates';
23

34
export type PayloadMode = 'fixed' | 'fresh';
@@ -63,12 +64,12 @@ export type IntervalEmitter = {
6364

6465
export type DialogMode = 'single' | 'pipeline' | 'intervals';
6566

66-
export type SimulationUser = Record<string, unknown> & {
67-
id: string;
67+
export type SimulationUser = UserResponse & {
68+
invisible?: boolean;
6869
};
6970

7071
export type SimulationState = {
71-
membersByCid: Record<string, Record<string, Record<string, unknown>>>;
72+
membersByCid: Record<string, Record<string, ChannelMemberResponse>>;
7273
messageIdsByCid: Record<string, string[]>;
7374
nextReactionTypeIndex: number;
7475
nextSequence: number;

examples/vite/src/AppSettings/ActionsMenu/WebSocketEventPromptDialog/websocketEventAutomation.ts

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
1-
import type { Channel, StreamChat } from 'stream-chat';
1+
import type {
2+
Channel,
3+
ChannelMemberResponse,
4+
Event,
5+
MessageResponseBase,
6+
ReactionResponse,
7+
StreamChat,
8+
UserResponse,
9+
} from 'stream-chat';
210

311
import {
4-
websocketEventTemplateDefinitions,
512
type SupportedWebsocketEventType,
613
type WebSocketEventTemplateContext,
14+
websocketEventTemplateDefinitions,
715
} from './websocketEventTemplates';
816
import type { SimulationState, SimulationUser } from './types';
917

10-
type JsonObject = Record<string, unknown>;
11-
type HandleEventArgument = Parameters<StreamChat['handleEvent']>[0];
18+
type UnknownRecord = Record<string, unknown>;
19+
type EventPayload = Omit<
20+
Partial<Event>,
21+
'channel' | 'member' | 'message' | 'reaction' | 'user'
22+
> & {
23+
channel?: Partial<WebSocketEventTemplateContext['channel']>;
24+
member?: ChannelMemberResponse;
25+
message?: Partial<MessageResponseBase>;
26+
reaction?: ReactionResponse;
27+
user?: UserResponse;
28+
};
1229

1330
const messageTextFragments = [
1431
'debug event payload',
@@ -20,12 +37,12 @@ const messageTextFragments = [
2037

2138
const reactionTypes = ['love', 'haha', 'wow', 'like', 'sad'] as const;
2239

23-
const asJsonObject = (value: unknown): JsonObject | undefined => {
40+
const asJsonObject = (value: unknown): UnknownRecord | undefined => {
2441
if (!value || typeof value !== 'object' || Array.isArray(value)) {
2542
return undefined;
2643
}
2744

28-
return value as JsonObject;
45+
return value as UnknownRecord;
2946
};
3047

3148
const getId = (value: unknown) => {
@@ -45,10 +62,33 @@ const getMessageUser = (message: unknown) => asJsonObject(asJsonObject(message)?
4562
const getMessageMember = (message: unknown) =>
4663
asJsonObject(asJsonObject(message)?.member);
4764

65+
const asUserResponse = (value: unknown): UserResponse | undefined => {
66+
const user = asJsonObject(value);
67+
68+
return typeof user?.id === 'string' ? (user as unknown as UserResponse) : undefined;
69+
};
70+
71+
const asChannelMemberResponse = (value: unknown): ChannelMemberResponse | undefined => {
72+
const member = asJsonObject(value);
73+
74+
if (!member) return undefined;
75+
76+
const userId = getId(member.user_id) ?? getUserId(member.user);
77+
78+
return userId ? (member as unknown as ChannelMemberResponse) : undefined;
79+
};
80+
4881
const buildRandomMessageText = (sequence: number) =>
4982
`${messageTextFragments[sequence % messageTextFragments.length]} #${sequence}`;
5083

51-
const buildReactionState = ({ reaction }: { reaction: JsonObject }): JsonObject => {
84+
const buildReactionState = ({
85+
reaction,
86+
}: {
87+
reaction: ReactionResponse;
88+
}): Pick<
89+
MessageResponseBase,
90+
'latest_reactions' | 'reaction_counts' | 'reaction_groups' | 'reaction_scores'
91+
> => {
5292
const reactionType = getId(reaction.type) ?? 'love';
5393
const reactionScore =
5494
typeof reaction.score === 'number' && Number.isFinite(reaction.score)
@@ -100,7 +140,7 @@ const getChannelMembersForCid = (
100140
cid: string,
101141
simulationState: SimulationState,
102142
templateContext: WebSocketEventTemplateContext,
103-
) => {
143+
): ChannelMemberResponse[] => {
104144
const knownMembers = Object.values(simulationState.membersByCid[cid] ?? {});
105145

106146
if (knownMembers.length > 0) {
@@ -122,7 +162,7 @@ const buildFreshContext = (
122162
templateContext,
123163
);
124164
const memberCount = channelMembers.length || templateContext.memberCount;
125-
const baseChannel = asJsonObject(templateContext.channel) ?? {};
165+
const baseChannel = templateContext.channel;
126166

127167
return {
128168
...templateContext,
@@ -212,9 +252,9 @@ const registerUserAndMember = ({
212252
user,
213253
}: {
214254
cid: string;
215-
member?: JsonObject;
255+
member?: ChannelMemberResponse;
216256
simulationState: SimulationState;
217-
user?: JsonObject;
257+
user?: UserResponse;
218258
}) => {
219259
if (user) {
220260
const userId = getUserId(user);
@@ -266,9 +306,9 @@ export const createInitialSimulationState = ({
266306

267307
registerUserAndMember({
268308
cid,
269-
member: templateContext.actorMember,
309+
member: templateContext.actorMember as ChannelMemberResponse,
270310
simulationState: state,
271-
user: templateContext.actor,
311+
user: templateContext.actor as UserResponse,
272312
});
273313
registerUserAndMember({
274314
cid,
@@ -284,9 +324,9 @@ export const createInitialSimulationState = ({
284324

285325
registerUserAndMember({
286326
cid,
287-
member: memberObject,
327+
member: asChannelMemberResponse(memberObject),
288328
simulationState: state,
289-
user: userObject,
329+
user: asUserResponse(userObject),
290330
});
291331
});
292332

@@ -305,9 +345,9 @@ export const createInitialSimulationState = ({
305345

306346
registerUserAndMember({
307347
cid,
308-
member: getMessageMember(messageObject),
348+
member: asChannelMemberResponse(getMessageMember(messageObject)),
309349
simulationState: state,
310-
user: getMessageUser(messageObject),
350+
user: asUserResponse(getMessageUser(messageObject)),
311351
});
312352
});
313353

@@ -322,11 +362,11 @@ export const buildFreshWebSocketEventPayload = ({
322362
eventType: SupportedWebsocketEventType;
323363
simulationState: SimulationState;
324364
templateContext: WebSocketEventTemplateContext;
325-
}): JsonObject => {
365+
}): EventPayload => {
326366
const freshContext = buildFreshContext(templateContext, simulationState);
327367
const basePayload = websocketEventTemplateDefinitions[eventType].buildDefault(
328368
freshContext,
329-
) as JsonObject;
369+
) as EventPayload;
330370

331371
switch (eventType) {
332372
case 'message.new':
@@ -454,7 +494,7 @@ export const trackSimulationStateFromPayload = ({
454494
simulationState,
455495
templateContext,
456496
}: {
457-
payload: JsonObject;
497+
payload: Event;
458498
simulationState: SimulationState;
459499
templateContext: WebSocketEventTemplateContext;
460500
}) => {
@@ -471,15 +511,15 @@ export const trackSimulationStateFromPayload = ({
471511

472512
registerUserAndMember({
473513
cid,
474-
member: asJsonObject(payload.member),
514+
member: asChannelMemberResponse(payload.member),
475515
simulationState,
476-
user: asJsonObject(payload.user),
516+
user: asUserResponse(payload.user),
477517
});
478518
registerUserAndMember({
479519
cid,
480-
member: getMessageMember(message),
520+
member: asChannelMemberResponse(getMessageMember(message)),
481521
simulationState,
482-
user: getMessageUser(message),
522+
user: asUserResponse(getMessageUser(message)),
483523
});
484524

485525
const channelObject = asJsonObject(payload.channel);
@@ -494,9 +534,9 @@ export const trackSimulationStateFromPayload = ({
494534

495535
registerUserAndMember({
496536
cid,
497-
member,
537+
member: asChannelMemberResponse(member),
498538
simulationState,
499-
user: asJsonObject(member.user),
539+
user: asUserResponse(member.user),
500540
});
501541
});
502542
}
@@ -511,18 +551,16 @@ export const emitWebSocketEventPayload = ({
511551
}: {
512552
client: StreamChat;
513553
eventType: SupportedWebsocketEventType;
514-
payload: JsonObject;
554+
payload: EventPayload;
515555
simulationState: SimulationState;
516556
templateContext: WebSocketEventTemplateContext;
517557
}) => {
518558
const emittedPayload = {
519559
...payload,
520560
type: eventType,
521-
};
561+
} as Event;
522562

523-
client.handleEvent({
524-
data: JSON.stringify(emittedPayload),
525-
} as HandleEventArgument);
563+
client.dispatchEvent(emittedPayload);
526564

527565
trackSimulationStateFromPayload({
528566
payload: emittedPayload,

0 commit comments

Comments
 (0)