Skip to content

call.update can set call members to empty array when updating unrelated field #2146

@Jorundur

Description

@Jorundur

Which package/packages do you use?

  • @stream-io/video-react-sdk
  • @stream-io/video-react-native-sdk
  • @stream-io/video-client

Describe the bug
I'm calling call.update shortly after call.getOrCreate. It is setting members to 0. This causes this bug: caller A calls callees B and C, members is set to 0 by the call.update call behind-the-scenes (bug), callee B rejects the call, the call ends (endedBy is caller A, probably because of logic checking for people still in the call). What should be happening is that the call should still be ringing for callee C, they should get a chance to answer.

This is my code for call creation:

export const createCallForChannel = async ({
  videoClient,
  chatClient,
  channelId,
  channelType,
  tenantId,
  translationMap,
  onSuccess,
}: {
  videoClient: StreamVideoClient;
  chatClient: StreamChat;
  channelId: string;
  channelType: ChannelType;
  tenantId: string;
  translationMap: {
    isCalling: (value: number) => string;
  };
  onSuccess: ({ channelMembersCount }: { channelMembersCount: number }) => void;
}) => {
  const channel = chatClient.channel(channelType, channelId);
  await channel.watch();
  const memberIds = Object.keys(channel.state.members);

  // Create a unique call ID using channel ID and timestamp
  const timestamp = Date.now().toString();
  const maxChannelIdLength = 64 - timestamp.length - 1; // -1 for the dash separator
  const channelIdSubstring = channelId.slice(-maxChannelIdLength);
  const callId = `${channelIdSubstring}-${timestamp}`;

  const call = videoClient.call("default", callId);
  const currentUser = call.currentUserId
    ? channel.state.members[call.currentUserId]
    : null;
  const { firstName } = getFirstAndLastName(currentUser?.user?.name);

  await call
    .getOrCreate({
      ring: true,
      data: {
        settings_override: {
          ring: {
            auto_cancel_timeout_ms: 30_000, // 30 seconds
            incoming_call_timeout_ms: 30_000, // 30 seconds
          },
        },
        team: tenantId,
        members: memberIds.map((userId) => ({
          user_id: userId,
        })),
        channel_cid: channel.cid,
        custom: {
          channelId,
          channelType,
          tenantId,
        },
      },
    })
    .then((callResponse) => {
      onSuccess({ channelMembersCount: memberIds.length });
    })
    .catch((error) => {
      console.error("Failed to getOrCreate call", error);
    });

  // Send a message to the chat channel about the call
  channel
    .sendMessage({
      text: `${firstName} ${translationMap.isCalling(memberIds.length - 1)}`,
      attachments: [
        {
          type: "call",
          call_id: callId,
        },
      ],
    })
    .then(async (response) => {
      // Needed for patching bug, commented out to show state when there's an issue
      // const currentMembers = call.state.members;
      call
        .update({
          custom: {
            messageId: response.message.id,
          },
        })
        .then(() => {
          // Needed for patching bug - uncommenting makes the flow work properly
          // if (call.state.members.length === 0 && currentMembers.length > 0) {
          //   call.state.setMembers(currentMembers);
          // }
        })
        .catch((error) => {
          console.error("Failed to update call with message ID:", error);
        });
    })
    .catch((error) => {
      console.error("Failed to send call message to chat:", error);
    });

  return call;
};

To Reproduce

Expected behavior
I should be able to update a call for some totally unrelated field without having the members go to 0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions