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(),
});
© Getstream.io, Inc. All Rights Reserved.