Hide Channel History For Newly Added Members

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

We will build the following simple modal:

Modal for the past conversation history access restriction

Minimal modal 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(customDate).toISOString()

Minimal styles are used for the modal.

AddMemberWithHistoryModal.tsx
import { useState } from "react";
import { Channel, ChannelUpdateOptions, UserResponse } from "stream-chat";
import { Modal, Pressable, StyleSheet, Text, View } from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
import { useTheme } from "stream-chat-react-native";
import DateTimePicker, {
  DateTimePickerEvent,
} from "@react-native-community/datetimepicker";

export type AddMemberWithHistoryModalProps = {
  channel?: Channel;
  visible: boolean;
  onClose: () => void;
  user: UserResponse | null;
  onMemberAdded: () => void;
};

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

export const AddMemberWithHistoryModal = (
  props: AddMemberWithHistoryModalProps,
) => {
  const [selectedOption, setSelectedOption] = useState<SelectedOption>("none");
  const [customDate, setCustomDate] = useState<Date>(new Date());
  const {
    theme: {
      colors: { accent_blue },
    },
  } = useTheme();
  const { channel, visible, onClose, user, onMemberAdded } = props;

  const options: { name: string; value: SelectedOption }[] = [
    { name: "Don't Include History", value: "none" },
    {
      name: "From Today",
      value: "today",
    },
    { name: "From the beginning", value: "all" },
    { name: "Custom", value: "custom" },
  ];

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

    const updateOptions: ChannelUpdateOptions = {};

    if (selectedOption !== "all") {
      let cutoffDate = new Date();
      if (selectedOption === "today") {
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        cutoffDate = today;
      } else if (selectedOption === "custom") {
        if (Number.isNaN(customDate.getTime())) {
          return;
        }
        cutoffDate = customDate;
      }
      updateOptions.hide_history_before = cutoffDate.toISOString();
    }

    try {
      await channel.addMembers([user.id], undefined, updateOptions);
    } catch (error) {
      console.error("Error adding member with history: ", error);
    } finally {
      onMemberAdded();
    }
  };

  const setDate = (event: DateTimePickerEvent, date?: Date) => {
    const { type } = event;
    if (type === "set") {
      if (date) {
        setCustomDate(date);
      }
    }
  };

  return (
    <SafeAreaProvider>
      <SafeAreaView style={styles.container}>
        <Modal
          animationType="slide"
          transparent={true}
          visible={visible}
          onRequestClose={onClose}
        >
          <View style={styles.container}>
            <View style={styles.modal}>
              <Text style={styles.title}>Include Conversation History?</Text>
              {options.map((option) => (
                <View style={[styles.option]} key={option.value}>
                  <Pressable
                    style={[styles.radioButton, { borderColor: accent_blue }]}
                    onPress={() => setSelectedOption(option.value)}
                  >
                    {selectedOption === option.value && (
                      <View
                        style={[
                          styles.radioButtonInner,
                          { backgroundColor: accent_blue },
                        ]}
                      />
                    )}
                  </Pressable>
                  <Text style={styles.optionText}>{option.name}</Text>
                </View>
              ))}
              {selectedOption === "custom" && (
                <View style={styles.datePickerContainer}>
                  <DateTimePicker
                    mode="datetime"
                    onChange={setDate}
                    value={customDate}
                    display="default"
                  />
                </View>
              )}
              <View style={styles.buttonContainer}>
                <Pressable style={styles.button} onPress={onClose}>
                  <Text style={[styles.buttonText, { color: accent_blue }]}>
                    Cancel
                  </Text>
                </Pressable>
                <Pressable style={styles.button} onPress={handleAdd}>
                  <Text style={[styles.buttonText, { color: accent_blue }]}>
                    Add
                  </Text>
                </Pressable>
              </View>
            </View>
          </View>
        </Modal>
      </SafeAreaView>
    </SafeAreaProvider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  modal: {
    backgroundColor: "white",
    margin: 20,
    borderRadius: 16,
    padding: 20,
    shadowColor: "black",
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
  },
  title: {
    fontSize: 18,
    fontWeight: "bold",
    textAlign: "center",
    marginBottom: 16,
  },
  option: {
    flexDirection: "row",
    alignItems: "center",
    paddingVertical: 8,
  },
  optionText: {
    fontSize: 16,
    marginLeft: 8,
  },
  radioButton: {
    width: 20,
    height: 20,
    borderRadius: 10,
    borderWidth: 2,
    alignItems: "center",
    justifyContent: "center",
  },
  radioButtonInner: {
    width: 12,
    height: 12,
    borderRadius: 6,
  },
  buttonContainer: {
    flexDirection: "row",
    justifyContent: "flex-end",
    marginTop: 8,
  },
  button: {
    padding: 8,
    marginLeft: 8,
  },
  buttonText: {
    fontSize: 16,
    fontWeight: "bold",
    textAlign: "center",
  },
  datePickerContainer: {
    alignItems: "center",
    justifyContent: "center",
  },
});

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.