diff --git a/src/modules/common/utils/index.tsx b/src/modules/common/utils/index.tsx index ff94208d21d..96ed738ba52 100644 --- a/src/modules/common/utils/index.tsx +++ b/src/modules/common/utils/index.tsx @@ -230,3 +230,54 @@ export const generateRandomString = (len: number = 10) => { return randomString; }; + +/** + * Send desktop notification + */ +export const sendDesktopNotification = (doc: { + title: string; + content?: string; +}) => { + const notify = () => { + // Don't send notification to itself + if (!window.document.hidden) { + return; + } + + const notification = new Notification(doc.title, { + body: doc.content, + icon: '/favicon.png', + dir: 'ltr' + }); + + // notify by sound + const audio = new Audio('/sound/notify.mp3'); + audio.play(); + + notification.onclick = () => { + window.focus(); + notification.close(); + }; + }; + + // Browser doesn't support Notification api + if (!('Notification' in window)) { + return; + } + + if (Notification.permission === 'granted') { + return notify(); + } + + if (Notification.permission !== 'denied') { + Notification.requestPermission(permission => { + if (!('permission' in Notification)) { + (Notification as any).permission = permission; + } + + if (permission === 'granted') { + return notify(); + } + }); + } +}; diff --git a/src/modules/inbox/constants.ts b/src/modules/inbox/constants.ts index 611eb9c0585..2cc67fe6402 100644 --- a/src/modules/inbox/constants.ts +++ b/src/modules/inbox/constants.ts @@ -4,3 +4,10 @@ export const CONVERSATION_STATUSES = { CLOSED: 'closed', ALL_LIST: ['new', 'open', 'closed'] }; + +export const NOTIFICATION_TYPE = { + gmail: 'You have a new email', + facebook: 'You have a message from facebook', + lead: 'You have got a new lead', + messenger: 'You have a new message from messenger' +}; diff --git a/src/modules/inbox/containers/conversationDetail/WorkArea.tsx b/src/modules/inbox/containers/conversationDetail/WorkArea.tsx index 1fbf37c689a..8a39f785b74 100644 --- a/src/modules/inbox/containers/conversationDetail/WorkArea.tsx +++ b/src/modules/inbox/containers/conversationDetail/WorkArea.tsx @@ -1,11 +1,13 @@ import { AppConsumer } from 'appContext'; import gql from 'graphql-tag'; import DumbWorkArea from 'modules/inbox/components/conversationDetail/workarea/WorkArea'; +import { NOTIFICATION_TYPE } from 'modules/inbox/constants'; import { mutations, queries, subscriptions } from 'modules/inbox/graphql'; import React from 'react'; import { compose, graphql } from 'react-apollo'; +import strip from 'strip'; import { IUser } from '../../../auth/types'; -import { withProps } from '../../../common/utils'; +import { sendDesktopNotification, withProps } from '../../../common/utils'; import { AddMessageMutationResponse, AddMessageMutationVariables, @@ -73,12 +75,12 @@ class WorkArea extends React.Component { variables: { _id: currentId }, updateQuery: (prev, { subscriptionData }) => { const message = subscriptionData.data.conversationMessageInserted; + const kind = currentConversation.integration.kind; // current user's message is being showed after insert message // mutation. So to prevent from duplication we are ignoring current // user's messages from subscription - const isMessenger = - currentConversation.integration.kind === 'messenger'; + const isMessenger = kind === 'messenger'; if (isMessenger && message.userId === currentUser._id) { return; @@ -108,6 +110,12 @@ class WorkArea extends React.Component { conversationMessages: [...messages, message] }; + // send desktop notification + sendDesktopNotification({ + title: NOTIFICATION_TYPE[kind], + content: strip(message.content) || '' + }); + return next; } }); diff --git a/src/modules/inbox/graphql/subscriptions.ts b/src/modules/inbox/graphql/subscriptions.ts index 595814c98aa..29808ba4342 100755 --- a/src/modules/inbox/graphql/subscriptions.ts +++ b/src/modules/inbox/graphql/subscriptions.ts @@ -20,6 +20,7 @@ const conversationClientMessageInserted = ` subscription conversationClientMessageInserted($userId: String!) { conversationClientMessageInserted(userId: $userId) { _id + content } } `; diff --git a/src/modules/layout/containers/Navigation.tsx b/src/modules/layout/containers/Navigation.tsx index 7cc031e24f6..4132400bccc 100644 --- a/src/modules/layout/containers/Navigation.tsx +++ b/src/modules/layout/containers/Navigation.tsx @@ -4,7 +4,8 @@ import { queries, subscriptions } from 'modules/inbox/graphql'; import { UnreadConversationsTotalCountQueryResponse } from 'modules/inbox/types'; import React from 'react'; import { compose, graphql } from 'react-apollo'; -import { withProps } from '../../common/utils'; +import strip from 'strip'; +import { sendDesktopNotification, withProps } from '../../common/utils'; import Navigation from '../components/Navigation'; class NavigationContainer extends React.Component<{ @@ -18,12 +19,16 @@ class NavigationContainer extends React.Component<{ // listen for all conversation changes document: gql(subscriptions.conversationClientMessageInserted), variables: { userId: currentUser._id }, - updateQuery: () => { + updateQuery: (prev, { subscriptionData: { data } }) => { + const { conversationClientMessageInserted } = data; + const { content } = conversationClientMessageInserted; + this.props.unreadConversationsCountQuery.refetch(); - // notify by sound - const audio = new Audio('/sound/notify.mp3'); - audio.play(); + sendDesktopNotification({ + title: 'You have a new message', + content: strip(content || '') + }); } }); } diff --git a/src/modules/notifications/containers/NotificationsLatest.tsx b/src/modules/notifications/containers/NotificationsLatest.tsx index daf6049a010..c4ff657b00f 100644 --- a/src/modules/notifications/containers/NotificationsLatest.tsx +++ b/src/modules/notifications/containers/NotificationsLatest.tsx @@ -1,9 +1,14 @@ import gql from 'graphql-tag'; import { IUser } from 'modules/auth/types'; import Spinner from 'modules/common/components/Spinner'; -import { Alert, withProps } from 'modules/common/utils'; +import { + Alert, + sendDesktopNotification, + withProps +} from 'modules/common/utils'; import React from 'react'; import { compose, graphql } from 'react-apollo'; +import strip from 'strip'; import NotificationsLatest from '../components/NotificationsLatest'; import { mutations, queries, subscriptions } from '../graphql'; import { @@ -30,7 +35,12 @@ class NotificationsLatestContainer extends React.Component { notificationsQuery.subscribeToMore({ document: subscription, variables: { userId: currentUser ? currentUser._id : null }, - updateQuery: () => { + updateQuery: (prev, { subscriptionData: { data } }) => { + const { notificationInserted } = data; + const { title, content } = notificationInserted; + + sendDesktopNotification({ title, content: strip(content || '') }); + notificationsQuery.refetch(); } }); diff --git a/src/modules/notifications/graphql/subscriptions.ts b/src/modules/notifications/graphql/subscriptions.ts index bc04fe0d2e7..6bf7931b671 100644 --- a/src/modules/notifications/graphql/subscriptions.ts +++ b/src/modules/notifications/graphql/subscriptions.ts @@ -1,6 +1,9 @@ const notificationSubscription = ` subscription notificationInserted($userId: String) { - notificationInserted(userId: $userId) + notificationInserted(userId: $userId) { + title + content + } } `; diff --git a/src/modules/notifications/types.ts b/src/modules/notifications/types.ts index 9b8960a20c8..8667eab58af 100644 --- a/src/modules/notifications/types.ts +++ b/src/modules/notifications/types.ts @@ -15,13 +15,7 @@ export interface INotification { export type NotificationsQueryResponse = { notifications: INotification[]; - subscribeToMore: ( - params: { - document: string; - updateQuery: () => void; - variables: { userId: string | null }; - } - ) => void; + subscribeToMore: any; loading: boolean; refetch: () => void; };