Skip to content

Commit 82056c2

Browse files
authored
feat: Add filter tabs
1 parent 7cdd04a commit 82056c2

25 files changed

+501
-61
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ customStyles | Object for custom styling | CustomStyle | {} |
6262
loadMoreLabel | Text shown on the load more component | string | "Load More" |
6363
hideBadge | Toggle to hide or show the badge | boolean | false |
6464
darkMode | Toggle to enable dark mode | boolean | false |
65+
hideTab | Toggle to enable all and unread tabs | false |
66+
tabProps | Props for customizing tab. <br>tabs - List of tab items. <br> activeTab - Active tab index. | tabProps| { tabs: [
67+
{ key: 'all', title: 'All' },
68+
{ key: 'unread', title: 'Unread' }
69+
], activeTab: 0} |
6570
itemsPerFetch | Number of notifications fetch per api request (have a max cap of 50) | number | 20 |
6671
windowViewOnly | Toggle to enable fit-to-screen window or modal view | boolean | false |
6772
notificationIcon | Option to use custom notification Icon | JSX Element | null |
@@ -125,6 +130,13 @@ type ThemeProps = {
125130
color?: string,
126131
background?: string,
127132
},
133+
tabs?: {
134+
containerBackgroundColor?: string,
135+
activeTabBackgroundColor?: string,
136+
activeTabTextColor?: string,
137+
inactiveTabTextColor?: string,
138+
indicatorColor?: string,
139+
};
128140
};
129141
```
130142

@@ -185,6 +197,15 @@ Please note that the badgeStyle, window shadow and border props are only applica
185197
clearAllIcon?:{
186198
size?: number
187199
},
200+
tabs?: {
201+
containerHeight?: number,
202+
tabPadding?: number,
203+
activeTabTextSize?: number,
204+
inactiveTabTextSize?: number,
205+
activeTabTextWeight?: TextStyle['fontWeight'],
206+
inactiveTabTextWeight?: TextStyle['fontWeight'],
207+
indicatorHeight?: number,
208+
};
188209
}
189210
```
190211

example/src/App.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ const App: React.FC = () => {
8383
<div>
8484
<div style={{ color: "#fff", fontWeight: "600" }}>Siren</div>
8585
</div>
86-
<div>
87-
<div style={{ color: "#fff", fontWeight: "600" }} onClick={() => markAsReadByDate(String(new Date().getTime()))}>Mark allAsRead</div>
86+
<div
87+
style={{ color: "#fff", fontWeight: "600" }}
88+
onClick={() => markAsReadByDate({ startDate: String(new Date().getTime()) })}
89+
>
90+
Mark allAsRead
8891
</div>
8992
</div>
9093
);

package-lock.json

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
"typescript": "^5.3.3"
8585
},
8686
"dependencies": {
87-
"@sirenapp/js-sdk": "^1.1.0",
88-
"pubsub-js": "^1.9.4"
87+
"pubsub-js": "^1.9.4",
88+
"test_notification": "^1.1.6"
8989
}
9090
}

src/components/EmptyList.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import darkModeIcon from "../assets/dark/emptyIconDark.svg";
55
import lightModeIcon from "../assets/light/emptyIconLight.svg";
66
import type { EmptyListProps } from "../types";
77
import { Constants } from "../utils";
8-
import { LIST_EMPTY_SUB_TEXT, LIST_EMPTY_TEXT } from "../utils/constants";
8+
import { LIST_EMPTY_SUB_TEXT } from "../utils/constants";
99
import "../styles/emptyList.css";
1010

1111
/**
@@ -25,7 +25,8 @@ import "../styles/emptyList.css";
2525
*/
2626
const EmptyList: FC<EmptyListProps> = ({
2727
styles,
28-
darkMode
28+
darkMode,
29+
emptyText,
2930
}) => {
3031

3132
const containerStyle = { backgroundColor: darkMode ? Constants.COLORS.dark.iconColor : Constants.COLORS.light.iconColor };
@@ -38,7 +39,7 @@ const EmptyList: FC<EmptyListProps> = ({
3839
<img src={lightModeIcon} alt="empty-icon" className="siren-sdk-empty-icon" />}
3940
</div>
4041
<div style={styles.emptyText} className="siren-sdk-empty-text">
41-
{LIST_EMPTY_TEXT}
42+
{emptyText}
4243
</div>
4344
<div style={styles.emptyText} className="siren-sdk-empty-sub-text">
4445
{LIST_EMPTY_SUB_TEXT}

src/components/SirenInbox.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ const SirenInbox: FC<SirenProps> = ({
6060
onCardClick,
6161
onError,
6262
itemsPerFetch = 20,
63+
hideTab,
64+
tabProps,
6365
}) => {
6466

6567
const { siren } = useSirenContext();
@@ -205,6 +207,8 @@ const SirenInbox: FC<SirenProps> = ({
205207
darkMode={darkMode}
206208
customErrorWindow={customErrorWindow}
207209
modalWidth={updatedModalWidth}
210+
hideTab={hideTab}
211+
tabProps={tabProps}
208212
/>
209213
</div>
210214
)}

src/components/SirenPanel.tsx

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
NotificationDataType,
77
NotificationsApiResponse,
88
SirenErrorType,
9-
} from "@sirenapp/js-sdk/dist/esm/types";
9+
} from "test_notification/dist/esm/types";
1010

1111
import "../styles/sirenPanel.css";
1212
import NotificationCard from "./Card";
@@ -16,6 +16,7 @@ import Header from "./Header";
1616
import AnimatedLoader from "./Loader";
1717
import ShowMoreButton from "./ShowMore";
1818
import { useSirenContext } from "./SirenProvider";
19+
import Tab from "./Tab";
1920
import type { EventListenerDataType, SirenPanelProps } from "../types";
2021
import {
2122
filterDataProperty,
@@ -27,7 +28,7 @@ import {
2728
mergeStyles,
2829
updateNotifications,
2930
} from "../utils/commonUtils";
30-
import { DEFAULT_WINDOW_TITLE, ERROR_TEXT, errorMap, events, EventType, eventTypes, VerificationStatus } from "../utils/constants";
31+
import { DEFAULT_WINDOW_TITLE, ERROR_TEXT, errorMap, events, EventType, eventTypes, LIST_EMPTY_TEXT, Tabs, UNREAD_LIST_EMPTY_TEXT, VerificationStatus } from "../utils/constants";
3132
import useSiren from "../utils/sirenHook";
3233

3334
/**
@@ -87,6 +88,14 @@ const SirenPanel: FC<SirenPanelProps> = ({
8788
onCardClick,
8889
onError,
8990
modalWidth,
91+
hideTab = false,
92+
tabProps = {
93+
tabs: [
94+
{ key: Tabs.ALL, title: 'All' },
95+
{ key: Tabs.UNREAD, title: 'Unread' }
96+
],
97+
activeTab: 0
98+
}
9099
}) => {
91100
const {
92101
markAllAsViewed,
@@ -98,7 +107,8 @@ const SirenPanel: FC<SirenPanelProps> = ({
98107
const [notifications, setNotifications] = useState<NotificationDataType[]>(
99108
[]
100109
);
101-
110+
const [activeTabIndex, setActiveTabIndex] = useState(0);
111+
const [filterType, setFilterType] = useState<string>(tabProps.tabs[tabProps.activeTab].key);
102112
const [error, setError] = useState<string>("");
103113
const [isLoading, setIsLoading] = useState(true);
104114
const [endReached, setEndReached] = useState(false);
@@ -154,7 +164,7 @@ const SirenPanel: FC<SirenPanelProps> = ({
154164
else if(verificationStatus === VerificationStatus.FAILED) {
155165
handleVerificationFailure();
156166
}
157-
}, [siren, verificationStatus, hideBadge]);
167+
}, [siren, verificationStatus, hideBadge, activeTabIndex]);
158168

159169
const restartNotificationCountFetch = () => {
160170
try {
@@ -183,17 +193,27 @@ const SirenPanel: FC<SirenPanelProps> = ({
183193
setError("");
184194
};
185195

186-
const handleClearAllNotification = async (): Promise<void> => {
196+
const handleClearAllNotification = async (): Promise<void> => {
187197
try {
188198
if (!isEmptyArray(notifications)) {
199+
let params;
200+
const startDate = notifications[0].createdAt;
201+
202+
if (filterType === Tabs.UNREAD)
203+
params = { startDate, isRead: false };
204+
else
205+
params = { startDate };
206+
207+
189208
const response = await deleteByDate(
190-
notifications[0].createdAt
209+
params
191210
);
192211

193212
response && triggerOnError(response);
194213

195214
if (response && isValidResponse(response)) {
196215
resetValues();
216+
setEndReached(true);
197217
setIsLoading(false);
198218
}
199219
}
@@ -227,7 +247,8 @@ const SirenPanel: FC<SirenPanelProps> = ({
227247
generateFilterParams(
228248
isRefresh ? [] : notifications,
229249
false,
230-
noOfNotificationsPerFetch
250+
noOfNotificationsPerFetch,
251+
filterType
231252
)
232253
);
233254

@@ -270,7 +291,7 @@ const SirenPanel: FC<SirenPanelProps> = ({
270291

271292
try {
272293
siren?.startRealTimeFetch(
273-
{eventType: EventType.NOTIFICATION, params: generateFilterParams(newList ?? [], true, noOfNotificationsPerFetch)}
294+
{eventType: EventType.NOTIFICATION, params: generateFilterParams(newList ?? [], true, noOfNotificationsPerFetch, filterType)}
274295
);
275296
} catch (er) {
276297
// handle error if needed
@@ -326,6 +347,14 @@ const SirenPanel: FC<SirenPanelProps> = ({
326347
onEndReached();
327348
};
328349

350+
351+
const handleTabChange = (index: number) => {
352+
resetValues();
353+
cleanUp();
354+
setActiveTabIndex(index);
355+
setFilterType(tabProps.tabs[index].key);
356+
};
357+
329358
const renderFooter = () => {
330359
if (isEmptyArray(notifications) && isLoading) return <div />;
331360

@@ -350,7 +379,7 @@ const SirenPanel: FC<SirenPanelProps> = ({
350379
/>
351380
));
352381
}, [notifications, cardProps, onCardClick, deleteNotificationById, styles, darkMode]);
353-
382+
354383

355384
const renderList = () => {
356385
if (isLoading && isEmptyArray(notifications)) {
@@ -387,14 +416,15 @@ const SirenPanel: FC<SirenPanelProps> = ({
387416
data-testid="empty-list"
388417
styles={styles}
389418
darkMode={darkMode}
419+
emptyText={filterType === Tabs.UNREAD ? UNREAD_LIST_EMPTY_TEXT : LIST_EMPTY_TEXT}
390420
/>)
391421
}
392422
</div>
393423
);
394424

395-
if (customCard)
396-
return notifications.map((item) => customCard(item));
397-
425+
if (customCard)
426+
return notifications.map((item) => customCard(item));
427+
398428
return renderedListItems;
399429
};
400430

@@ -457,6 +487,13 @@ const SirenPanel: FC<SirenPanelProps> = ({
457487
return getModalContentHeightInFullScreen(styles?.headerContainer?.height);
458488
}, [styles?.headerContainer?.height]);
459489

490+
const defaultTabProps = {
491+
tabs: tabProps.tabs,
492+
activeTab: tabProps.activeTab,
493+
onTabChange: handleTabChange,
494+
styles: styles,
495+
};
496+
460497
return (
461498
<div
462499
className={
@@ -484,6 +521,7 @@ const SirenPanel: FC<SirenPanelProps> = ({
484521
...styles.contentContainer,
485522
}}
486523
>
524+
{!hideTab && (<Tab {...defaultTabProps} />)}
487525
<div
488526
id="contentContainer"
489527
style={{

src/components/SirenProvider.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
22

3-
import { Siren } from "@sirenapp/js-sdk";
3+
import PubSub from "pubsub-js";
4+
import { Siren } from "test_notification";
45
import type {
56
InitConfigType,
67
NotificationsApiResponse,
78
SirenErrorType,
89
UnviewedCountApiResponse,
9-
} from "@sirenapp/js-sdk/dist/esm/types";
10-
import PubSub from "pubsub-js";
10+
} from "test_notification/dist/esm/types";
1111

1212
import type { SirenProviderConfigProps } from "../types";
1313
import { generateUniqueId, logger } from "../utils/commonUtils";

0 commit comments

Comments
 (0)