Skip to content

Commit

Permalink
feat(kudos): send kudos by text in standups (#9259)
Browse files Browse the repository at this point in the history
* feat(kudos): send kudos by text in standups

* Remove console logs

* Fix test

* Store unicode emoji too

* Link teamPromptResponseId

* Update slack notification

* Update email notification

* Mention notification analytics

* response mentioned toast analytics

* isValid

* Add types
  • Loading branch information
igorlesnenko committed Jan 9, 2024
1 parent 126a1a0 commit 371d5f6
Show file tree
Hide file tree
Showing 23 changed files with 492 additions and 41 deletions.
9 changes: 3 additions & 6 deletions packages/client/components/KudosReceivedNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {KudosReceivedNotification_notification$key} from '~/__generated__/KudosR
import NotificationTemplate from './NotificationTemplate'
import useAtmosphere from '../hooks/useAtmosphere'
import SendClientSideEvent from '../utils/SendClientSideEvent'
import getReactji from '~/utils/getReactji'

interface Props {
notification: KudosReceivedNotification_notification$key
Expand All @@ -25,15 +24,13 @@ const KudosReceivedNotification = (props: Props) => {
picture
meetingName
meetingId
emoji
emojiUnicode
status
}
`,
notificationRef
)
const {type, name, picture, meetingName, emoji, meetingId, status} = notification

const {unicode} = getReactji(emoji)
const {type, name, picture, meetingName, emojiUnicode, meetingId, status} = notification

useEffect(() => {
SendClientSideEvent(atmosphere, 'Notification Viewed', {
Expand All @@ -46,7 +43,7 @@ const KudosReceivedNotification = (props: Props) => {
<NotificationTemplate
message={
<>
{unicode} {name} gave you kudos in{' '}
{emojiUnicode} {name} gave you kudos in{' '}
<Link to={`/meet/${meetingId}`} className='font-semibold text-sky-500 underline'>
{meetingName}
</Link>
Expand Down
24 changes: 21 additions & 3 deletions packages/client/components/ResponseMentioned.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import React, {useEffect} from 'react'
import {useFragment} from 'react-relay'
import NotificationAction from '~/components/NotificationAction'
import useRouter from '../hooks/useRouter'
import {ResponseMentioned_notification$key} from '../__generated__/ResponseMentioned_notification.graphql'
import NotificationTemplate from './NotificationTemplate'
import SendClientSideEvent from '../utils/SendClientSideEvent'
import useAtmosphere from '~/hooks/useAtmosphere'

interface Props {
notification: ResponseMentioned_notification$key
Expand All @@ -27,24 +29,40 @@ const ResponseMentioned = (props: Props) => {
id
name
}
type
status
kudosEmojiUnicode
}
`,
notificationRef
)
const {history} = useRouter()
const {meeting, response} = notification
const atmosphere = useAtmosphere()
const {meeting, response, kudosEmojiUnicode, type, status} = notification
const {picture: authorPicture, preferredName: authorName} = response.user

useEffect(() => {
SendClientSideEvent(atmosphere, 'Notification Viewed', {
notificationType: type,
notificationStatus: status,
kudosEmojiUnicode
})
}, [])

const {id: meetingId, name: meetingName} = meeting
const goThere = () => {
history.push(`/meet/${meetingId}/responses?responseId=${encodeURIComponent(response.id)}`)
}

const message = kudosEmojiUnicode
? `${kudosEmojiUnicode} ${authorName} mentioned you and gave kudos in their response in ${meetingName}.`
: `${authorName} mentioned you in their response in ${meetingName}.`

// :TODO: (jmtaber129): Show mention preview.
return (
<NotificationTemplate
avatar={authorPicture}
message={`${authorName} mentioned you in their response in ${meetingName}.`}
message={message}
notification={notification}
action={<NotificationAction label={'See their response'} onClick={goThere} />}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ const EmailResponseMentioned = (props: Props) => {
id
name
}
kudosEmojiUnicode
}
`,
notificationRef
)
const {meeting, response} = notification
const {meeting, response, kudosEmojiUnicode} = notification
const {rasterPicture: authorPicture, preferredName: authorName} = response.user

const {id: meetingId, name: meetingName} = meeting
Expand All @@ -46,11 +47,15 @@ const EmailResponseMentioned = (props: Props) => {
}
})

const message = kudosEmojiUnicode
? `${kudosEmojiUnicode} ${authorName} mentioned you and gave kudos in their response in ${meetingName}.`
: `${authorName} mentioned you in their response in ${meetingName}.`

// :TODO: (jmtaber129): Show mention preview.
return (
<EmailNotificationTemplate
avatar={authorPicture}
message={`${authorName} mentioned you in their response in ${meetingName}.`}
message={message}
notificationRef={notification}
linkLabel={'See their response'}
linkUrl={linkUrl}
Expand Down
7 changes: 3 additions & 4 deletions packages/client/mutations/AddReactjiToReactableMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {commitMutation} from 'react-relay'
import createProxyRecord from '~/utils/relay/createProxyRecord'
import {StandardMutation} from '../types/relayMutations'
import {AddReactjiToReactableMutation as TAddReactjiToReactableMutation} from '../__generated__/AddReactjiToReactableMutation.graphql'
import getReactji from '~/utils/getReactji'
import SendClientSideEvent from '../utils/SendClientSideEvent'

graphql`
Expand Down Expand Up @@ -41,7 +40,7 @@ const mutation = graphql`
}
... on AddReactjiToReactableSuccess {
addedKudos {
emoji
emojiUnicode
receiverUser {
preferredName
}
Expand Down Expand Up @@ -118,10 +117,10 @@ const AddReactjiToReactableMutation: StandardMutation<TAddReactjiToReactableMuta
const {isRemove} = variables
const addedKudos = res.addReactjiToReactable.addedKudos
if (!isRemove && addedKudos) {
const {unicode} = getReactji(addedKudos.emoji)
const {emojiUnicode} = addedKudos
atmosphere.eventEmitter.emit('addSnackbar', {
key: 'youGaveKudos',
message: `You gave kudos to ${addedKudos.receiverUser.preferredName} ${unicode}`,
message: `You gave kudos to ${addedKudos.receiverUser.preferredName} ${emojiUnicode}`,
autoDismiss: 5,
onShow: () => {
SendClientSideEvent(atmosphere, 'Snackbar Viewed', {
Expand Down
32 changes: 31 additions & 1 deletion packages/client/mutations/UpsertTeamPromptResponseMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import clientTempId from '~/utils/relay/clientTempId'
import {UpsertTeamPromptResponseMutation_meeting$data} from '~/__generated__/UpsertTeamPromptResponseMutation_meeting.graphql'
import {LocalHandlers, SharedUpdater, StandardMutation} from '../types/relayMutations'
import {UpsertTeamPromptResponseMutation as TUpsertTeamPromptResponseMutation} from '../__generated__/UpsertTeamPromptResponseMutation.graphql'
import SendClientSideEvent from '../utils/SendClientSideEvent'

graphql`
fragment UpsertTeamPromptResponseMutation_meeting on UpsertTeamPromptResponseSuccess {
Expand All @@ -17,6 +18,12 @@ graphql`
createdAt
...TeamPromptResponseEmojis_response
}
addedKudoses {
receiverUser {
preferredName
}
emojiUnicode
}
}
`

Expand Down Expand Up @@ -97,7 +104,30 @@ const UpsertTeamPromptResponseMutation: StandardMutation<
const payload = store.getRootField('upsertTeamPromptResponse')
upsertTeamPromptResponseUpdater(payload as any, {atmosphere, store})
},
onCompleted,
onCompleted: (res, errors) => {
const addedKudoses = res.upsertTeamPromptResponse.addedKudoses
if (addedKudoses?.length && addedKudoses[0]) {
const {emojiUnicode} = addedKudoses[0]
atmosphere.eventEmitter.emit('addSnackbar', {
key: 'youGaveKudos',
message: `You gave kudos to ${addedKudoses
.map((kudos) => kudos.receiverUser.preferredName)
.join(', ')} ${emojiUnicode}`,
autoDismiss: 5,
onShow: () => {
SendClientSideEvent(atmosphere, 'Snackbar Viewed', {
snackbarType: 'kudosSent'
})
},
onManualDismiss: () => {
SendClientSideEvent(atmosphere, 'Snackbar Clicked', {
snackbarType: 'kudosSent'
})
}
})
}
onCompleted?.(res, errors)
},
onError
})
}
Expand Down
8 changes: 3 additions & 5 deletions packages/client/mutations/toasts/mapKudosReceivedToToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,27 @@ import {mapKudosReceivedToToast_notification$data} from '../../__generated__/map
import makeNotificationToastKey from './makeNotificationToastKey'
import {OnNextHistoryContext} from '../../types/relayMutations'
import SendClientSideEvent from '../../utils/SendClientSideEvent'
import getReactji from '~/utils/getReactji'

graphql`
fragment mapKudosReceivedToToast_notification on NotifyKudosReceived {
id
name
meetingName
meetingId
emoji
emojiUnicode
}
`

const mapKudosReceivedToToast = (
notification: mapKudosReceivedToToast_notification$data,
{atmosphere, history}: OnNextHistoryContext
): Snack => {
const {id: notificationId, meetingName, name, emoji, meetingId} = notification
const {unicode} = getReactji(emoji)
const {id: notificationId, meetingName, name, emojiUnicode, meetingId} = notification
return {
autoDismiss: 5,
showDismissButton: true,
key: makeNotificationToastKey(notificationId),
message: `${unicode} ${name} gave you kudos in`,
message: `${emojiUnicode} ${name} gave you kudos in`,
action: {
label: meetingName,
callback: () => {
Expand Down
24 changes: 21 additions & 3 deletions packages/client/mutations/toasts/mapResponseMentionedToToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Snack} from '../../components/Snackbar'
import {OnNextHistoryContext} from '../../types/relayMutations'
import {mapResponseMentionedToToast_notification$data} from '../../__generated__/mapResponseMentionedToToast_notification.graphql'
import makeNotificationToastKey from './makeNotificationToastKey'
import SendClientSideEvent from '../../utils/SendClientSideEvent'

graphql`
fragment mapResponseMentionedToToast_notification on NotifyResponseMentioned {
Expand All @@ -17,30 +18,47 @@ graphql`
id
name
}
kudosEmojiUnicode
}
`

const mapResponseMentionedToToast = (
notification: mapResponseMentionedToToast_notification$data,
{history}: OnNextHistoryContext
{atmosphere, history}: OnNextHistoryContext
): Snack | null => {
if (!notification) return null
const {id: notificationId, meeting, response} = notification
const {id: notificationId, meeting, response, kudosEmojiUnicode} = notification
const {preferredName: authorName} = response.user
const {id: meetingId, name: meetingName} = meeting

const message = kudosEmojiUnicode
? `${kudosEmojiUnicode} ${authorName} mentioned you and gave kudos in their response in ${meetingName}.`
: `${authorName} mentioned you in their response in ${meetingName}.`

// :TODO: (jmtaber129): Check if we're already open to the relevant standup response discussion
// thread, and do nothing if we are.

return {
key: makeNotificationToastKey(notificationId),
autoDismiss: 10,
message: `${authorName} mentioned you in their response in ${meetingName}.`,
message,
action: {
label: 'See their response',
callback: () => {
history.push(`/meet/${meetingId}/responses?responseId=${encodeURIComponent(response.id)}`)
}
},
onShow: () => {
SendClientSideEvent(atmosphere, 'Snackbar Viewed', {
snackbarType: 'responseMentioned',
kudosEmojiUnicode
})
},
onManualDismiss: () => {
SendClientSideEvent(atmosphere, 'Snackbar Clicked', {
snackbarType: 'responseMentioned',
kudosEmojiUnicode
})
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/server/database/types/NotificationKudosReceived.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Input {
meetingName: string
meetingId: string
emoji: string
emojiUnicode: string
}

export default class NotificationKudosReceived extends Notification {
Expand All @@ -18,15 +19,17 @@ export default class NotificationKudosReceived extends Notification {
meetingName: string
meetingId: string
emoji: string
emojiUnicode: string

constructor(input: Input) {
const {userId, name, picture, senderUserId, meetingName, meetingId, emoji} = input
const {userId, name, picture, senderUserId, meetingName, meetingId, emoji, emojiUnicode} = input
super({userId, type: 'KUDOS_RECEIVED'})
this.name = name
this.picture = picture
this.senderUserId = senderUserId
this.meetingName = meetingName
this.meetingId = meetingId
this.emoji = emoji
this.emojiUnicode = emojiUnicode
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@ interface Input {
responseId: string
meetingId: string
userId: string
kudosEmoji?: string | null
kudosEmojiUnicode?: string | null
}

export default class NotificationResponseMentioned extends Notification {
readonly type = 'RESPONSE_MENTIONED'
responseId: string
meetingId: string
kudosEmoji?: string | null
kudosEmojiUnicode?: string | null

constructor(input: Input) {
const {responseId, meetingId, userId} = input
const {responseId, meetingId, userId, kudosEmoji, kudosEmojiUnicode} = input
super({userId, type: 'RESPONSE_MENTIONED'})
this.responseId = responseId
this.meetingId = meetingId
this.kudosEmoji = kudosEmoji
this.kudosEmojiUnicode = kudosEmojiUnicode
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,12 @@ const getSlackMessageForNotification = async (
const responseId = notification.responseId
const response = await dataLoader.get('teamPromptResponses').loadNonNull(responseId)
const author = await dataLoader.get('users').loadNonNull(response.userId)
const title = notification.kudosEmojiUnicode
? `${notification.kudosEmojiUnicode} *${author.preferredName}* mentioned you and gave kudos in their response in *${meeting.name}*`
: `*${author.preferredName}* mentioned you in their response in *${meeting.name}*`
return {
responseId,
title: `*${author.preferredName}* mentioned you in their response in *${meeting.name}*`,
title,
buttonText: 'See their response'
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ const addReactjiToReactable: MutationResolvers['addReactjiToReactable'] = async
reactableType: reactableType,
reactableId: reactableId,
teamId,
emoji: team.kudosEmoji
emoji: team.kudosEmoji,
emojiUnicode: team.kudosEmojiUnicode
})
.returning('id')
.executeTakeFirst())!.id
Expand All @@ -203,6 +204,7 @@ const addReactjiToReactable: MutationResolvers['addReactjiToReactable'] = async
meetingId,
meetingName: meeting.name,
emoji: team.kudosEmoji,
emojiUnicode: team.kudosEmojiUnicode,
name: senderUser.preferredName,
picture: senderUser.picture
})
Expand Down
Loading

0 comments on commit 371d5f6

Please sign in to comment.