Hide Channel History For Newly Added Members

In this tutorial we will demonstrate how to build a simple dialog that allows to specify the length of channel conversation history to be shared with a newly added member.

We will build the following simple dialog:

Dialog for the past conversation history access restriction

Minimal dialog to control history visibility when adding members

The UI below lets you choose how much of the conversation history a newly added member will see. On Add, it calls channel.addMembers() with hide_history_before mapped from the selected option:

  • don’t include: hide all history before now → hide_history_before = new Date().toISOString()
  • from today: show only today → hide_history_before = startOfToday.toISOString()
  • from the beginning: show everything → omit hide_history_before
  • custom: show from a specific date/time → hide_history_before = new Date(input).toISOString()

Minimal CSS was added to make it pleasant and centered.

import React, { useMemo, useState } from "react";
import { useChannelStateContext } from "stream-chat-react";
import "./add-members-dialog.css";

type HistoryMode = "none" | "today" | "all" | "custom";

type AddMembersDialogProps = {
  memberIds: string[];
};

export function AddMembersDialog({ memberIds }: AddMembersDialogProps) {
  const { channel } = useChannelStateContext();
  const [open, setOpen] = useState<boolean>(true);
  const [mode, setMode] = useState<HistoryMode>("none");
  const [customInput, setCustomInput] = useState<string>("");

  const isCustomDateValid = useMemo(() => {
    if (mode !== "custom") return true;
    const parsed = new Date(customInput);
    return !Number.isNaN(parsed.getTime());
  }, [mode, customInput]);

  const handleAdd = async () => {
    if (!channel) return;

    const options: { hide_history_before?: string } = {};

    if (mode !== "all") {
      let cutoff = new Date(); // do not show any history

      if (mode === "today") {
        const startOfToday = new Date();
        startOfToday.setHours(0, 0, 0, 0);
        cutoff = startOfToday;
      } else if (mode === "custom") {
        const parsed = new Date(customInput);
        if (Number.isNaN(parsed.getTime())) return;
        cutoff = parsed;
      }

      options.hide_history_before = cutoff.toISOString();
    }

    await channel.addMembers(memberIds, undefined, options);
    setOpen(false);
  };

  if (!open) return null;

  return (
    <div className="sch-dialog-overlay">
      <div className="sch-dialog">
        <h3 className="sch-title">Include conversation history?</h3>

        <div className="sch-options">
          <label className="sch-option">
            <input
              type="radio"
              name="history"
              value="none"
              checked={mode === "none"}
              onChange={() => setMode("none")}
            />
            <span>don't include</span>
          </label>

          <label className="sch-option">
            <input
              type="radio"
              name="history"
              value="today"
              checked={mode === "today"}
              onChange={() => setMode("today")}
            />
            <span>from today</span>
          </label>

          <label className="sch-option">
            <input
              type="radio"
              name="history"
              value="all"
              checked={mode === "all"}
              onChange={() => setMode("all")}
            />
            <span>from the beginning</span>
          </label>

          <label className="sch-option">
            <input
              type="radio"
              name="history"
              value="custom"
              checked={mode === "custom"}
              onChange={() => setMode("custom")}
            />
            <span>custom</span>
          </label>
        </div>

        {mode === "custom" ? (
          <div className="sch-custom">
            <input
              className="sch-input"
              type="datetime-local"
              value={customInput}
              onChange={(e) => setCustomInput(e.target.value)}
              placeholder="YYYY-MM-DDTHH:mm"
            />
            {!isCustomDateValid && (
              <div className="sch-error">Enter a valid date/time</div>
            )}
          </div>
        ) : null}

        <div className="sch-actions">
          <button
            className="sch-btn sch-secondary"
            onClick={() => setOpen(false)}
          >
            Cancel
          </button>
          <button
            className="sch-btn sch-primary"
            onClick={handleAdd}
            disabled={mode === "custom" && !isCustomDateValid}
          >
            Add
          </button>
        </div>
      </div>
    </div>
  );
}

Mapping to the raw API

Here’s a standalone snippet showing the parameter shape. This mirrors the button behavior above:

// Example: include only the last 7 days of history for the new member
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - 7);
await channel.addMembers(["thierry"], undefined, {
  hide_history_before: cutoff.toISOString(),
});