Integration Steps

1

Integrate SuprSendProvider

Integrate SuprSendProvider as it is needed for creating SuprSend Client and authenticating user.

2

Accessing preferences methods using useSuprSendClient hook

Call useSuprSendClient hook in your react component code to get SuprSend client instance which has all preferences methods.

3

Integrating Preferences

Please refer these sections to understand and implement preferences methods in your react application, as integration steps are same for both the web-sdk and react-sdk.

Example

import { useState, useEffect } from "react";
import Switch from "react-switch";
import {
  SuprSendProvider,
  ChannelLevelPreferenceOptions,
  PreferenceOptions,
  useSuprSendClient,
  useAuthenticateUser,
} from "@suprsend/react";

// -------------- Category Level Preferences -------------- //

const handleCategoryPreferenceChange = async ({
  data,
  subcategory,
  setPreferenceData,
  suprSendClient,
}) => {
  const resp = await suprSendClient.user.preferences.updateCategoryPreference(
    subcategory.category,
    data ? PreferenceOptions.OPT_IN : PreferenceOptions.OPT_OUT
  );
  if (resp.status === "error") {
    console.log(resp.error.message);
  } else {
    setPreferenceData({ ...resp.body });
  }
};

const handleChannelPreferenceInCategoryChange = async ({
  channel,
  subcategory,
  setPreferenceData,
  suprSendClient,
}) => {
  if (!channel.is_editable) return;

  const resp =
    await suprSendClient.user.preferences.updateChannelPreferenceInCategory(
      channel.channel,
      channel.preference === PreferenceOptions.OPT_IN
        ? PreferenceOptions.OPT_OUT
        : PreferenceOptions.OPT_IN,
      subcategory.category
    );
  if (resp.status === "error") {
    console.log(resp.error.message);
  } else {
    setPreferenceData({ ...resp.body });
  }
};

function NotificationCategoryPreferences({
  preferenceData,
  setPreferenceData,
}) {
  const suprSendClient = useSuprSendClient();

  if (!preferenceData.sections) {
    return null;
  }
  return preferenceData.sections?.map((section, index) => {
    return (
      <div style={{ marginBottom: 24 }} key={index}>
        {section?.name && (
          <div
            style={{
              backgroundColor: "#FAFBFB",
              paddingTop: 12,
              paddingBottom: 12,
              marginBottom: 18,
            }}
          >
            <p
              style={{
                fontSize: 18,
                fontWeight: 500,
                color: "#3D3D3D",
              }}
            >
              {section.name}
            </p>
            <p style={{ color: "#6C727F" }}>{section.description}</p>
          </div>
        )}

        {section?.subcategories?.map((subcategory, index) => {
          return (
            <div
              key={index}
              style={{
                borderBottom: "1px solid #D9D9D9",
                paddingBottom: 12,
                marginTop: 18,
              }}
            >
              <div
                style={{
                  display: "flex",
                  justifyContent: "space-between",
                  alignItems: "center",
                }}
              >
                <div>
                  <p
                    style={{
                      fontSize: 16,
                      fontWeight: 600,
                      color: "#3D3D3D",
                    }}
                  >
                    {subcategory.name}
                  </p>
                  <p style={{ color: "#6C727F", fontSize: 14 }}>
                    {subcategory.description}
                  </p>
                </div>
                <Switch
                  disabled={!subcategory.is_editable}
                  onChange={(data) => {
                    handleCategoryPreferenceChange({
                      data,
                      subcategory,
                      setPreferenceData,
                      suprSendClient,
                    });
                  }}
                  uncheckedIcon={false}
                  checkedIcon={false}
                  height={20}
                  width={40}
                  onColor="#2563EB"
                  checked={subcategory.preference === PreferenceOptions.OPT_IN}
                />
              </div>

              <div style={{ display: "flex", gap: 10, marginTop: 12 }}>
                {subcategory?.channels.map((channel, index) => {
                  return (
                    <Checkbox
                      key={index}
                      value={channel.preference}
                      title={channel.channel}
                      disabled={!channel.is_editable}
                      onClick={() => {
                        handleChannelPreferenceInCategoryChange({
                          channel,
                          subcategory,
                          setPreferenceData,
                          suprSendClient,
                        });
                      }}
                    />
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
    );
  });
}

// -------------- Channel Level Preferences -------------- //

const handleOverallChannelPreferenceChange = async ({
  channel,
  status,
  setPreferenceData,
  suprSendClient,
}) => {
  const resp =
    await suprSendClient.user.preferences.updateOverallChannelPreference(
      channel.channel,
      status
    );
  if (resp.status === "error") {
    console.log(resp.error.message);
  } else {
    setPreferenceData({ ...resp.body });
  }
};

function ChannelLevelPreferernceItem({ channel, setPreferenceData }) {
  const suprSendClient = useSuprSendClient();
  const [isActive, setIsActive] = useState(false);

  return (
    <div
      style={{
        border: "1px solid #D9D9D9",
        borderRadius: 5,
        padding: "12px 24px",
        marginBottom: 24,
      }}
    >
      <div
        style={{
          cursor: "pointer",
        }}
        onClick={() => setIsActive(!isActive)}
      >
        <p
          style={{
            fontSize: 18,
            fontWeight: 500,
            color: "#3D3D3D",
          }}
        >
          {channel.channel}
        </p>
        <p style={{ color: "#6C727F", fontSize: 14 }}>
          {channel.is_restricted
            ? "Allow required notifications only"
            : "Allow all notifications"}
        </p>
      </div>
      {isActive && (
        <div style={{ marginTop: 12, marginLeft: 24 }}>
          <p
            style={{
              color: "#3D3D3D",
              fontSize: 16,
              fontWeight: 500,
              marginTop: 12,
              borderBottom: "1px solid #E8E8E8",
            }}
          >
            {channel.channel} Preferences
          </p>
          <div style={{ marginTop: 12 }}>
            <div style={{ marginBottom: 8 }}>
              <div
                style={{
                  display: "flex",
                  alignItems: "center",
                }}
              >
                <div>
                  <input
                    type="radio"
                    name={`all- ${channel.channel}`}
                    value={true}
                    id={`all- ${channel.channel}`}
                    checked={!channel.is_restricted}
                    onChange={() => {
                      handleOverallChannelPreferenceChange({
                        channel,
                        status: ChannelLevelPreferenceOptions.ALL,
                        setPreferenceData,
                        suprSendClient,
                      });
                    }}
                  />
                </div>
                <label
                  htmlFor={`all- ${channel.channel}`}
                  style={{ marginLeft: 12 }}
                >
                  All
                </label>
              </div>
              <p style={{ color: "#6C727F", fontSize: 14, marginLeft: 22 }}>
                Allow All Notifications, except the ones that I have turned off
              </p>
            </div>
            <div>
              <div style={{ display: "flex", alignItems: "center" }}>
                <div>
                  <input
                    type="radio"
                    name={`required- ${channel.channel}`}
                    value={true}
                    id={`required- ${channel.channel}`}
                    checked={channel.is_restricted}
                    onChange={() => {
                      handleOverallChannelPreferenceChange({
                        channel,
                        status: ChannelLevelPreferenceOptions.REQUIRED,
                        setPreferenceData,
                        suprSendClient,
                      });
                    }}
                  />
                </div>
                <label
                  htmlFor={`required- ${channel.channel}`}
                  style={{ marginLeft: 12 }}
                >
                  Required
                </label>
              </div>
              <p style={{ color: "#6C727F", fontSize: 14, marginLeft: 22 }}>
                Allow only important notifications related to account and
                security settings
              </p>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function ChannelLevelPreferences({ preferenceData, setPreferenceData }) {
  return (
    <div>
      <div
        style={{
          backgroundColor: "#FAFBFB",
          paddingTop: 12,
          paddingBottom: 12,
          marginBottom: 18,
        }}
      >
        <p
          style={{
            fontSize: 18,
            fontWeight: 500,
            color: "#3D3D3D",
          }}
        >
          What notifications to allow for channel?
        </p>
      </div>
      <div>
        {preferenceData.channel_preferences ? (
          <div>
            {preferenceData.channel_preferences?.map((channel, index) => {
              return (
                <ChannelLevelPreferernceItem
                  key={index}
                  channel={channel}
                  setPreferenceData={setPreferenceData}
                />
              );
            })}
          </div>
        ) : (
          <p>No Data</p>
        )}
      </div>
    </div>
  );
}

// -------------- Main component -------------- //

export default function App() {
  return (
    <SuprSendProvider
      publicApiKey="YOUR_PUBLIC_API_KEY"
      distinctId={"YOUR_DISTINCT_ID"}
    >
      <Preferences />
    </SuprSendProvider>
  );
}

function Preferences() {
  const suprSendClient = useSuprSendClient();
  const { authenticatedUser } = useAuthenticateUser();
  const [preferenceData, setPreferenceData] = useState();

  useEffect(() => {
    if (!authenticatedUser) return;
    
    suprSendClient.user.preferences.getPreferences().then((resp) => {
      if (resp.status === "error") {
        console.log(resp.error.message);
      } else {
        setPreferenceData({ ...resp.body });
      }
    });

    // listen for update in preferences data
    suprSendClient.emitter.on("preferences_updated", (preferenceData) => {
      setPreferenceData({ ...preferenceData.body });
    });

    // listen for errors
    suprSendClient.emitter.on("preferences_error", (response) => {
      console.log("ERROR:", response?.error?.message);
    });
  }, [authenticatedUser]);

  if (!preferenceData) return <p>Loading...</p>;
  return (
    <div style={{ margin: 24 }}>
      <h3 style={{ marginBottom: 24 }}>Notification Preferences</h3>
      <NotificationCategoryPreferences
        preferenceData={preferenceData}
        setPreferenceData={setPreferenceData}
      />
      <ChannelLevelPreferences
        preferenceData={preferenceData}
        setPreferenceData={setPreferenceData}
      />
    </div>
  );
}

// -------------- Custom Checkbox Component -------------- //

function Checkbox({ title, value, onClick, disabled }) {
  const selected = value === PreferenceOptions.OPT_IN;

  return (
    <div
      style={{
        border: "0.5px solid #B5B5B5",
        display: "inline-flex",
        padding: "0px 20px 0px 4px",
        borderRadius: 30,
        cursor: disabled ? "not-allowed" : "pointer",
      }}
      onClick={onClick}
    >
      <Circle selected={selected} disabled={disabled} />
      <p
        style={{
          marginLeft: 8,
          color: "#6C727F",
          marginTop: 1,
          fontWeight: 500,
          paddingBottom: 4,
        }}
      >
        {title}
      </p>
    </div>
  );
}

function Circle({ selected, disabled }) {
  const bgColor = selected
    ? disabled
      ? "#BDCFF8"
      : "#2463EB"
    : disabled
    ? "#D0CFCF"
    : "#FFF";

  return (
    <div
      style={{
        height: 20,
        width: 20,
        borderRadius: 100,
        border: "0.5px solid #A09F9F",
        backgroundColor: bgColor,
        marginTop: 3.6,
      }}
    />
  );
}