Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const defaultUnsupportedObjectAttachment = {
uploadState: 'finished',
},
debug: true,
metadata: { randomNumber: 7, source: 'vite-preview' },
title: 'custom payload',
type: 'custom',
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ReactNode, Ref } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { Event } from 'stream-chat';
import {
Dropdown,
type DropdownTriggerProps,
Expand Down Expand Up @@ -808,7 +809,7 @@ export const WebSocketEventPromptDialog = ({
throw new Error('Payload must be a JSON object');
}

return parsedPayload as Record<string, unknown>;
return parsedPayload as Partial<Event>;
}, []);

const emitConfiguredEvent = useCallback(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ChannelMemberResponse, UserResponse } from 'stream-chat';
import type { SupportedWebsocketEventType } from './websocketEventTemplates';

export type PayloadMode = 'fixed' | 'fresh';
Expand Down Expand Up @@ -63,12 +64,12 @@ export type IntervalEmitter = {

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

export type SimulationUser = Record<string, unknown> & {
id: string;
export type SimulationUser = UserResponse & {
invisible?: boolean;
};

export type SimulationState = {
membersByCid: Record<string, Record<string, Record<string, unknown>>>;
membersByCid: Record<string, Record<string, ChannelMemberResponse>>;
messageIdsByCid: Record<string, string[]>;
nextReactionTypeIndex: number;
nextSequence: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import type { Channel, StreamChat } from 'stream-chat';
import type {
Channel,
ChannelMemberResponse,
Event,
MessageResponseBase,
ReactionResponse,
StreamChat,
UserResponse,
} from 'stream-chat';

import {
websocketEventTemplateDefinitions,
type SupportedWebsocketEventType,
type WebSocketEventTemplateContext,
websocketEventTemplateDefinitions,
} from './websocketEventTemplates';
import type { SimulationState, SimulationUser } from './types';

type JsonObject = Record<string, unknown>;
type HandleEventArgument = Parameters<StreamChat['handleEvent']>[0];
type UnknownRecord = Record<string, unknown>;
type EventPayload = Omit<
Partial<Event>,
'channel' | 'member' | 'message' | 'reaction' | 'user'
> & {
channel?: Partial<WebSocketEventTemplateContext['channel']>;
member?: ChannelMemberResponse;
message?: Partial<MessageResponseBase>;
reaction?: ReactionResponse;
user?: UserResponse;
};

const messageTextFragments = [
'debug event payload',
Expand All @@ -20,12 +37,12 @@ const messageTextFragments = [

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

const asJsonObject = (value: unknown): JsonObject | undefined => {
const asJsonObject = (value: unknown): UnknownRecord | undefined => {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return undefined;
}

return value as JsonObject;
return value as UnknownRecord;
};

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

const asUserResponse = (value: unknown): UserResponse | undefined => {
const user = asJsonObject(value);

return typeof user?.id === 'string' ? (user as unknown as UserResponse) : undefined;
};

const asChannelMemberResponse = (value: unknown): ChannelMemberResponse | undefined => {
const member = asJsonObject(value);

if (!member) return undefined;

const userId = getId(member.user_id) ?? getUserId(member.user);

return userId ? (member as unknown as ChannelMemberResponse) : undefined;
};

const buildRandomMessageText = (sequence: number) =>
`${messageTextFragments[sequence % messageTextFragments.length]} #${sequence}`;

const buildReactionState = ({ reaction }: { reaction: JsonObject }): JsonObject => {
const buildReactionState = ({
reaction,
}: {
reaction: ReactionResponse;
}): Pick<
MessageResponseBase,
'latest_reactions' | 'reaction_counts' | 'reaction_groups' | 'reaction_scores'
> => {
const reactionType = getId(reaction.type) ?? 'love';
const reactionScore =
typeof reaction.score === 'number' && Number.isFinite(reaction.score)
Expand Down Expand Up @@ -100,7 +140,7 @@ const getChannelMembersForCid = (
cid: string,
simulationState: SimulationState,
templateContext: WebSocketEventTemplateContext,
) => {
): ChannelMemberResponse[] => {
const knownMembers = Object.values(simulationState.membersByCid[cid] ?? {});

if (knownMembers.length > 0) {
Expand All @@ -122,7 +162,7 @@ const buildFreshContext = (
templateContext,
);
const memberCount = channelMembers.length || templateContext.memberCount;
const baseChannel = asJsonObject(templateContext.channel) ?? {};
const baseChannel = templateContext.channel;

return {
...templateContext,
Expand Down Expand Up @@ -212,9 +252,9 @@ const registerUserAndMember = ({
user,
}: {
cid: string;
member?: JsonObject;
member?: ChannelMemberResponse;
simulationState: SimulationState;
user?: JsonObject;
user?: UserResponse;
}) => {
if (user) {
const userId = getUserId(user);
Expand Down Expand Up @@ -266,9 +306,9 @@ export const createInitialSimulationState = ({

registerUserAndMember({
cid,
member: templateContext.actorMember,
member: templateContext.actorMember as ChannelMemberResponse,
simulationState: state,
user: templateContext.actor,
user: templateContext.actor as UserResponse,
});
registerUserAndMember({
cid,
Expand All @@ -284,9 +324,9 @@ export const createInitialSimulationState = ({

registerUserAndMember({
cid,
member: memberObject,
member: asChannelMemberResponse(memberObject),
simulationState: state,
user: userObject,
user: asUserResponse(userObject),
});
});

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

registerUserAndMember({
cid,
member: getMessageMember(messageObject),
member: asChannelMemberResponse(getMessageMember(messageObject)),
simulationState: state,
user: getMessageUser(messageObject),
user: asUserResponse(getMessageUser(messageObject)),
});
});

Expand All @@ -322,11 +362,11 @@ export const buildFreshWebSocketEventPayload = ({
eventType: SupportedWebsocketEventType;
simulationState: SimulationState;
templateContext: WebSocketEventTemplateContext;
}): JsonObject => {
}): EventPayload => {
const freshContext = buildFreshContext(templateContext, simulationState);
const basePayload = websocketEventTemplateDefinitions[eventType].buildDefault(
freshContext,
) as JsonObject;
) as EventPayload;

switch (eventType) {
case 'message.new':
Expand Down Expand Up @@ -454,7 +494,7 @@ export const trackSimulationStateFromPayload = ({
simulationState,
templateContext,
}: {
payload: JsonObject;
payload: Event;
simulationState: SimulationState;
templateContext: WebSocketEventTemplateContext;
}) => {
Expand All @@ -471,15 +511,15 @@ export const trackSimulationStateFromPayload = ({

registerUserAndMember({
cid,
member: asJsonObject(payload.member),
member: asChannelMemberResponse(payload.member),
simulationState,
user: asJsonObject(payload.user),
user: asUserResponse(payload.user),
});
registerUserAndMember({
cid,
member: getMessageMember(message),
member: asChannelMemberResponse(getMessageMember(message)),
simulationState,
user: getMessageUser(message),
user: asUserResponse(getMessageUser(message)),
});

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

registerUserAndMember({
cid,
member,
member: asChannelMemberResponse(member),
simulationState,
user: asJsonObject(member.user),
user: asUserResponse(member.user),
});
});
}
Expand All @@ -511,18 +551,16 @@ export const emitWebSocketEventPayload = ({
}: {
client: StreamChat;
eventType: SupportedWebsocketEventType;
payload: JsonObject;
payload: EventPayload;
simulationState: SimulationState;
templateContext: WebSocketEventTemplateContext;
}) => {
const emittedPayload = {
...payload,
type: eventType,
};
} as Event;

client.handleEvent({
data: JSON.stringify(emittedPayload),
} as HandleEventArgument);
client.dispatchEvent(emittedPayload);

trackSimulationStateFromPayload({
payload: emittedPayload,
Expand Down
Loading