This contains the name, description, and subcategories. We have to loop through the sections list and for every section item if there is a name and description present, then show the heading, and if a subcategories list is present, loop through that subcategories list and show all subcategories under that section heading.
Subcategories can exist without sections as the section is an optional field. In that case, the section’s name will not be available. For sections where the name is not present, you can directly show its subcategories list without showing Heading for the section in UI.
This is the place where the user sets his category-level preferences. While looping through the subcategories list for every subcategory item, show the name and description in UI.
This contains a list of channels, channel preference status and whether it’s editable or not. While looping through the subcategory list for every subcategory item we have to loop through its channels list and for every channel to show channel level checkbox.
name of the channel to be shown on UI. The same key will be used as id of the channel while updating the preference.
is_restricted
This key indicates the restriction level of channel. If restricted, notification will only be sent in the category where this channel is added as mandatory in notification category settings. True means Required radio button is selected. False means All radio button is selected.
Use this method to get preferences data and create the preferences UI by following the above sections. This method should be called first before any update preference methods.
Calling this method will opt-in/opt-out users from that category-level channel. When the category’s channel checkbox is editable and the user clicks on the checkbox you can call this method.
All preferences update api’s are optimistic updates. Actual API call will happen in background with 1 second debounce. Since its a background task SDK provides event listeners to get updated preference data based on API call status. Listen to this event listeners and update the UI accordingly.
For understanding purpose we have added simple example of preferences implementation in React. You could refer this headless example and design the preferences in your Angular or Vue.js etc. If you want to implement in react please refer @suprsend/react.
Example.js
import{ useState, useEffect }from"react";importSwitchfrom"react-switch";import{SuprSend,ChannelLevelPreferenceOptions,PreferenceOptions,}from"@suprsend/web-sdk";// -------------- Category Level Preferences -------------- //const handleCategoryPreferenceChange =async({ data, subcategory, setPreferenceData,})=>{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,})=>{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});}};functionNotificationCategoryPreferences({ preferenceData, setPreferenceData,}){if(!preferenceData.sections){returnnull;}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,});}} 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,});}}/>);})}</div></div>);})}</div>);});}// -------------- Channel Level Preferences -------------- //const handleOverallChannelPreferenceChange =async({ channel, status, setPreferenceData,})=>{const resp =await suprSendClient.user.preferences.updateOverallChannelPreference( channel.channel, status);if(resp.status==="error"){console.log(resp.error.message);}else{setPreferenceData({...resp.body});}};functionChannelLevelPreferernceItem({ channel, setPreferenceData }){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,});}}/></div><label htmlFor={`all- ${channel.channel}`} style={{marginLeft:12}}>All</label></div><p style={{color:"#6C727F",fontSize:14,marginLeft:22}}>AllowAllNotifications, 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,});}}/></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>);}functionChannelLevelPreferences({ 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>NoData</p>)}</div></div>);}// -------------- Main component -------------- //const suprSendClient =newSuprSend(publicApiKey);// create suprsend clientexportdefaultfunctionPreferences(){const[preferenceData, setPreferenceData]=useState();constgetPreferencesData=async()=>{// get preferences data tree and set in local state 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);});};useEffect(()=>{// autheticate user to suprsend suprSendClient.identify(distinctId).then((resp)=>{// call suprsend.identify method before getting preferencesgetPreferencesData();});},[]);if(!preferenceData)return<p>Loading...</p>;return(<div style={{margin:24}}><h3 style={{marginBottom:24}}>NotificationPreferences</h3><NotificationCategoryPreferences preferenceData={preferenceData} setPreferenceData={setPreferenceData}/><ChannelLevelPreferences preferenceData={preferenceData} setPreferenceData={setPreferenceData}/></div>);}// -------------- Custom Checkbox Component -------------- //functionCheckbox({ 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>);}functionCircle({ 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,}}/>);}