Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kudos): send kudos by text in standups #9259

Merged
merged 11 commits into from
Jan 9, 2024
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 Why did you change this? I had used unicode for team health and had to change it to the text representation to avoid issues with analytics. Wouldn't it be better if we kept it like before for kudos emojis? cc @tghanken

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tianrunhe You have a little more context over the emoji issues than I do. Did the original implementation only break in BQ? Or were we also seeing issues in places like Amplitude?

Overall, since we're not yet adding the ability to customize the emoji, and we're likely going to revamp how we extract this into BQ early next year, I'm not too worried about this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I currently store both: text representation and unicode. In this piece of code I just using emojiUnicode for rendering, text representation is still available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the original implementation only break in BQ? Or were we also seeing issues in places like Amplitude?

That's right. Amplitude can display them fine. It's only BQ that's broken.

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}.`
igorlesnenko marked this conversation as resolved.
Show resolved Hide resolved
: `${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`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing the meeting name like you used in the other messages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meeting name is still here, it is shown as an action button.
image

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 @@ -267,9 +267,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}*`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 similar to the above, I'd prefer just gave you kudos in meetingName

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nickoferrall The reason it is written this way is that we want to distinguish between kudos that left as a reaction and kudos that is a mention. If I just keep it as gave you kudos in meetingName it may not be clear that my standup response was not just liked, but I was somehow mentioned.
I think this is not the final version and we will improve something сс @acressall

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be a good one to at least collect internal feedback when both kinds of kudos are in place. I vote for Igor's more verbose but more clear option for now and we can adjust later depending on feedback

: `*${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 @@ -187,7 +187,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 @@ -200,6 +201,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
Loading