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>
);
}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:

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.
.sch-dialog-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: grid;
place-items: center;
padding: 16px;
z-index: 1000;
}
.sch-dialog {
width: 100%;
max-width: 420px;
background: #fff;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
padding: 16px 18px;
font-family:
ui-sans-serif,
system-ui,
-apple-system,
Segoe UI,
Roboto,
Ubuntu,
Cantarell,
Noto Sans,
sans-serif,
Apple Color Emoji,
Segoe UI Emoji;
}
.sch-title {
margin: 4px 0 12px;
font-size: 16px;
font-weight: 600;
}
.sch-options {
display: grid;
gap: 8px;
margin-bottom: 12px;
}
.sch-option {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.sch-custom {
margin: 8px 0 12px;
}
.sch-input {
width: 100%;
box-sizing: border-box;
padding: 8px 10px;
border: 1px solid #e5e7eb;
border-radius: 8px;
font-size: 14px;
}
.sch-error {
color: #b91c1c;
font-size: 12px;
margin-top: 6px;
}
.sch-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 4px;
}
.sch-btn {
border: 0;
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
cursor: pointer;
}
.sch-primary {
background: #005fff;
color: #fff;
}
.sch-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.sch-secondary {
background: #f3f4f6;
color: #111827;
}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(),
});