Skip to content

Commit

Permalink
feat(js): Com 82 implement filters on sdk (#6060)
Browse files Browse the repository at this point in the history
Co-authored-by: Paweł <[email protected]>
  • Loading branch information
BiswaViraj and LetItRock authored Jul 16, 2024
1 parent c58f063 commit 41f40a5
Show file tree
Hide file tree
Showing 16 changed files with 672 additions and 124 deletions.
1 change: 1 addition & 0 deletions packages/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
index.directcss
2 changes: 1 addition & 1 deletion packages/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"build": "pnpm run clean && pnpm run pre:build && tsup && pnpm run post:build",
"build:declarations": "tsc -p tsconfig.declarations.json",
"build:umd": "webpack --config webpack.config.cjs",
"build:watch": "tsup --watch",
"build:watch": "pnpm run pre:build && tsup --watch && pnpm run post:build",
"post:build": "rm ./src/ui/index.directcss && node scripts/size-limit.mjs",
"lint": "eslint --ext .ts,.tsx src",
"test": "jest"
Expand Down
139 changes: 134 additions & 5 deletions packages/js/src/api/inbox-service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { HttpClient, ApiOptions } from '@novu/client';

import { ApiOptions, HttpClient } from '@novu/client';
import type { Session } from '../types';
import type { ButtonTypeEnum, InboxNotification, NotificationFilter } from './types';

export type InboxServiceOptions = ApiOptions;

const INBOX_ROUTE = '/inbox';

export class InboxService {
#token: string | undefined;
#httpClient: HttpClient;

constructor(options: InboxServiceOptions = {}) {
Expand All @@ -21,13 +22,141 @@ export class InboxService {
subscriberId: string;
subscriberHash?: string;
}): Promise<Session> {
const response = (await this.#httpClient.post(`/inbox/session`, {
const response = (await this.#httpClient.post(`${INBOX_ROUTE}/session`, {
applicationIdentifier,
subscriberId,
subscriberHash,
})) as Session;
this.#httpClient.setAuthorizationToken(response.token);

return response;
}

async fetchNotifications({
after,
archived,
limit = 10,
offset,
read,
tags,
}: {
tags?: InboxNotification['tags'];
read?: boolean;
archived?: boolean;
limit?: number;
after?: string;
offset?: number;
}): Promise<{ data: InboxNotification[]; hasMore: boolean; filter: NotificationFilter }> {
const queryParams = new URLSearchParams(`limit=${limit}`);
if (after) {
queryParams.append('after', after);
}
if (offset) {
queryParams.append('offset', `${offset}`);
}
if (tags) {
tags.forEach((tag) => queryParams.append('tags[]', tag));
}
if (read !== undefined) {
queryParams.append('read', `${read}`);
}
if (archived !== undefined) {
queryParams.append('archived', `${archived}`);
}

const data = await this.#httpClient.get(`${INBOX_ROUTE}/notifications?${queryParams.toString()}`);

// TODO: fix this
return {
data,
hasMore: false,
filter: {},
};
}

async count({
tags,
read,
archived,
}: {
tags?: InboxNotification['tags'];
read?: boolean;
archived?: boolean;
}): Promise<{
data: {
count: number;
};
filter: NotificationFilter;
}> {
const queryParams = new URLSearchParams();

if (tags) {
tags.forEach((tag) => queryParams.append('tags[]', tag));
}
if (read !== undefined) {
queryParams.append('read', `${read}`);
}
if (archived !== undefined) {
queryParams.append('archived', `${archived}`);
}

return await this.#httpClient.get(`${INBOX_ROUTE}/notifications/count?${queryParams.toString()}`);
}

async read(notificationId: string): Promise<InboxNotification> {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/read`);

return response;
}

async unread(notificationId: string): Promise<InboxNotification> {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/unread`);

return response;
}

async archived(notificationId: string): Promise<InboxNotification> {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/archive`);

return response;
}

async unarchived(notificationId: string): Promise<InboxNotification> {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/unarchive`);

return response;
}

async readAll({ tags }: { tags?: InboxNotification['tags'] }): Promise<void> {
const response = await this.#httpClient.post(`${INBOX_ROUTE}/notifications/read`, { tags });

return response;
}

async archivedAll({ tags }: { tags?: InboxNotification['tags'] }): Promise<void> {
const response = await this.#httpClient.post(`${INBOX_ROUTE}/notifications/archive`, { tags });

return response;
}

async readArchivedAll({ tags }: { tags?: InboxNotification['tags'] }): Promise<void> {
const response = await this.#httpClient.post(`${INBOX_ROUTE}/notifications/read-archive`, { tags });

return response;
}

async completeAction({ actionType, notificationId }: { notificationId: string; actionType: ButtonTypeEnum }) {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/complete`, {
actionType,
});

return response;
}

this.#token = response.token;
async revertAction({ actionType, notificationId }: { notificationId: string; actionType: ButtonTypeEnum }) {
const response = await this.#httpClient.patch(`${INBOX_ROUTE}/notifications/${notificationId}/revert`, {
actionType,
});

return response;
}
Expand Down
63 changes: 63 additions & 0 deletions packages/js/src/api/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export enum ActorTypeEnum {
NONE = 'none',
USER = 'user',
SYSTEM_ICON = 'system_icon',
SYSTEM_CUSTOM = 'system_custom',
}

export enum ButtonTypeEnum {
PRIMARY = 'primary',
SECONDARY = 'secondary',
}

export enum ChannelTypeEnum {
IN_APP = 'in_app',
EMAIL = 'email',
SMS = 'sms',
CHAT = 'chat',
PUSH = 'push',
}

type Avatar = {
type: ActorTypeEnum;
data: string | null;
};

export type Subscriber = {
id: string;
firstName?: string;
lastName?: string;
avatar?: string;
subscriberId: string;
};

type Action = {
type: ButtonTypeEnum;
label: string;
url?: string;
isCompleted: boolean;
};

export type InboxNotification = {
id: string;
subject?: string;
body: string;
to: Subscriber;
read?: boolean;
archived?: boolean;
createdAt: string;
readAt?: string | null;
archivedAt?: string | null;
actor?: Subscriber;
avatar?: Avatar;
primaryAction?: Action;
secondaryAction?: Action;
channelType: ChannelTypeEnum;
tags?: string[];
};

export type NotificationFilter = {
tags?: string[];
read?: boolean;
archived?: boolean;
};
4 changes: 4 additions & 0 deletions packages/js/src/base-module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { ApiService } from '@novu/client';
import { InboxService } from './api';

import { NovuEventEmitter } from './event-emitter';
import { Session } from './types';
import { ApiServiceSingleton } from './utils/api-service-singleton';
import { InboxServiceSingleton } from './utils/inbox-service-singleton';

interface CallQueueItem {
fn: () => Promise<unknown>;
Expand All @@ -13,13 +15,15 @@ interface CallQueueItem {

export class BaseModule {
_apiService: ApiService;
_inboxService: InboxService;
_emitter: NovuEventEmitter;
#callsQueue: CallQueueItem[] = [];
#sessionError: unknown;

constructor() {
this._emitter = NovuEventEmitter.getInstance();
this._apiService = ApiServiceSingleton.getInstance();
this._inboxService = InboxServiceSingleton.getInstance();
this._emitter.on('session.initialize.success', ({ result }) => {
this.onSessionSuccess(result);
this.#callsQueue.forEach(async ({ fn, resolve }) => {
Expand Down
41 changes: 37 additions & 4 deletions packages/js/src/event-emitter/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { InboxNotification } from '../api/types';
import type {
FetchCountArgs,
FetchFeedArgs,
Expand All @@ -9,11 +10,17 @@ import type {
RemoveNotificationArgs,
RemoveAllNotificationsArgs,
RemoveNotificationsArgs,
FetchFeedResponse,
FetchCountResponse,
ReadArgs,
UnreadArgs,
ArchivedArgs,
UnarchivedArgs,
} from '../feeds';
import { Preference } from '../preferences/preference';
import { FetchPreferencesArgs, UpdatePreferencesArgs } from '../preferences/types';
import type { InitializeSessionArgs } from '../session';
import { PaginatedResponse, Session, WebSocketEvent } from '../types';
import { Session, WebSocketEvent } from '../types';

type NovuPendingEvent<A, O = undefined> = {
args: A;
Expand Down Expand Up @@ -49,8 +56,27 @@ type BaseEvents<T extends string, ARGS, RESULT, OPTIMISTIC = undefined, FALLBACK
};

type SessionInitializeEvents = BaseEvents<'session.initialize', InitializeSessionArgs, Session>;
type FeedFetchEvents = BaseEvents<'feeds.fetch', FetchFeedArgs, PaginatedResponse<Notification>>;
type FeedFetchCountEvents = BaseEvents<'feeds.fetch_count', FetchCountArgs, number>;
type FeedFetchEvents = BaseEvents<'feeds.fetch', FetchFeedArgs, FetchFeedResponse>;
type FeedFetchCountEvents = BaseEvents<'feeds.fetch_count', FetchCountArgs, FetchCountResponse>;
type FeedReadEvents = BaseEvents<'feeds.read', ReadArgs, InboxNotification, InboxNotification, InboxNotification>;
type FeedReadAllEvents = BaseEvents<'feeds.read_all', { tags?: InboxNotification['tags'] }, void>;
type FeedUnreadEvents = BaseEvents<'feeds.unread', UnreadArgs, InboxNotification, InboxNotification, InboxNotification>;
type FeedArchivedEvents = BaseEvents<
'feeds.archived',
ArchivedArgs,
InboxNotification,
InboxNotification,
InboxNotification
>;
type FeedUnarchivedEvents = BaseEvents<
'feeds.unarchived',
UnarchivedArgs,
InboxNotification,
InboxNotification,
InboxNotification
>;
type FeedArchivedAllEvents = BaseEvents<'feeds.archived_all', { tags?: InboxNotification['tags'] }, void>;
type FeedReadArchivedAllEvents = BaseEvents<'feeds.read_archived_all', { tags?: InboxNotification['tags'] }, void>;
type FeedMarkNotificationsAsEvents = BaseEvents<
'feeds.mark_notifications_as',
MarkNotificationsAsArgs,
Expand Down Expand Up @@ -132,7 +158,14 @@ export type Events = SessionInitializeEvents &
PreferencesFetchEvents &
PreferencesUpdateEvents &
SocketConnectEvents &
SocketEvents;
SocketEvents &
FeedReadEvents &
FeedReadAllEvents &
FeedArchivedEvents &
FeedArchivedAllEvents &
FeedReadArchivedAllEvents &
FeedUnreadEvents &
FeedUnarchivedEvents;

export type EventNames = keyof Events;
export type SocketEventNames = keyof SocketEvents;
Expand Down
Loading

0 comments on commit 41f40a5

Please sign in to comment.