diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b4e0b104703..fb632dc6f0a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.33.0" + ".": "7.35.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e868c246ea..12642d0bcf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,31 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows conventions [outlined here](http://keepachangelog.com/). +## [7.35.0](https://github.com/ParabolInc/parabol/compare/v7.34.0...v7.35.0) (2024-05-30) + + +### Added + +* type safety for gql perms ([#9798](https://github.com/ParabolInc/parabol/issues/9798)) ([712f79e](https://github.com/ParabolInc/parabol/commit/712f79eb81087b3a86301de3e611703a8ef46826)) + + +### Fixed + +* clear kudos received notifications ([#9805](https://github.com/ParabolInc/parabol/issues/9805)) ([74d8dbc](https://github.com/ParabolInc/parabol/commit/74d8dbc76366959be4274bde1d12d7978a146a2c)) + +## [7.34.0](https://github.com/ParabolInc/parabol/compare/v7.33.0...v7.34.0) (2024-05-30) + + +### Added + +* Add Jira Server to Your Work ([#9794](https://github.com/ParabolInc/parabol/issues/9794)) ([eec025e](https://github.com/ParabolInc/parabol/commit/eec025e3e22202c0c4c5630d2e6a75db76e3008f)) + + +### Changed + +* Allow global Jira Server integration provider ([#9796](https://github.com/ParabolInc/parabol/issues/9796)) ([051e51c](https://github.com/ParabolInc/parabol/commit/051e51c7746dfb50c4853b07ecc5bf548bd99a4e)) +* remove kudos ([#9785](https://github.com/ParabolInc/parabol/issues/9785)) ([23d48c4](https://github.com/ParabolInc/parabol/commit/23d48c48c01471be8e1332765f5d7cd9f0168954)) + ## [7.33.0](https://github.com/ParabolInc/parabol/compare/v7.32.1...v7.33.0) (2024-05-29) diff --git a/codegen.json b/codegen.json index 2bfa7b6011f..46aa03a0329 100644 --- a/codegen.json +++ b/codegen.json @@ -75,7 +75,6 @@ "InviteToTeamPayload": "./types/InviteToTeamPayload#InviteToTeamPayloadSource", "JiraIssue": "./types/JiraIssue#JiraIssueSource", "JiraRemoteProject": "../types/JiraRemoteProject#JiraRemoteProjectSource", - "Kudos": "../../postgres/types/Kudos#Kudos", "MeetingSeries": "../../postgres/types/MeetingSeries#MeetingSeries", "MeetingTemplate": "../../database/types/MeetingTemplate#default", "NewMeeting": "../../postgres/types/Meeting#AnyMeeting", diff --git a/package.json b/package.json index 078d273103f..e4a4186934e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.33.0", + "version": "7.35.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/chronos/package.json b/packages/chronos/package.json index 1444cee8972..2bdc5f14c9a 100644 --- a/packages/chronos/package.json +++ b/packages/chronos/package.json @@ -1,6 +1,6 @@ { "name": "chronos", - "version": "7.33.0", + "version": "7.35.0", "description": "A cron job scheduler", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/chronos#readme", @@ -25,6 +25,6 @@ }, "dependencies": { "cron": "^2.3.1", - "parabol-server": "7.33.0" + "parabol-server": "7.35.0" } } diff --git a/packages/client/components/KudosReceivedNotification.tsx b/packages/client/components/KudosReceivedNotification.tsx deleted file mode 100644 index 8215f304e6b..00000000000 --- a/packages/client/components/KudosReceivedNotification.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import graphql from 'babel-plugin-relay/macro' -import React, {useEffect} from 'react' -import {useFragment} from 'react-relay' -import {Link} from 'react-router-dom' -import {KudosReceivedNotification_notification$key} from '~/__generated__/KudosReceivedNotification_notification.graphql' -import useAtmosphere from '../hooks/useAtmosphere' -import anonymousAvatar from '../styles/theme/images/anonymous-avatar.svg' -import SendClientSideEvent from '../utils/SendClientSideEvent' -import NotificationTemplate from './NotificationTemplate' - -interface Props { - notification: KudosReceivedNotification_notification$key -} - -const KudosReceivedNotification = (props: Props) => { - const atmosphere = useAtmosphere() - const {notification: notificationRef} = props - const notification = useFragment( - graphql` - fragment KudosReceivedNotification_notification on NotifyKudosReceived { - ...NotificationTemplate_notification - id - type - name - picture - meetingName - meetingId - emojiUnicode - status - } - `, - notificationRef - ) - const {type, name, picture, meetingName, emojiUnicode, meetingId, status} = notification - - useEffect(() => { - SendClientSideEvent(atmosphere, 'Notification Viewed', { - notificationType: type, - notificationStatus: status - }) - }, []) - - return ( - - {emojiUnicode} {name ?? 'Someone'} gave you kudos in{' '} - - {meetingName} - - - } - notification={notification} - avatar={picture ?? anonymousAvatar} - /> - ) -} - -export default KudosReceivedNotification diff --git a/packages/client/components/Mentioned.tsx b/packages/client/components/Mentioned.tsx index e22a97c40ba..ef23254d0b3 100644 --- a/packages/client/components/Mentioned.tsx +++ b/packages/client/components/Mentioned.tsx @@ -25,7 +25,6 @@ const Mentioned = (props: Props) => { status senderName senderPicture - kudosEmojiUnicode createdAt meetingId meetingName @@ -45,7 +44,6 @@ const Mentioned = (props: Props) => { senderPicture, meetingId, meetingName, - kudosEmojiUnicode, type, status, retroReflection, @@ -58,8 +56,7 @@ const Mentioned = (props: Props) => { useEffect(() => { SendClientSideEvent(atmosphere, 'Notification Viewed', { notificationType: type, - notificationStatus: status, - kudosEmojiUnicode + notificationStatus: status }) }, []) @@ -77,9 +74,7 @@ const Mentioned = (props: Props) => { } } - const message = !kudosEmojiUnicode - ? `${authorName} mentioned you in ${locationType} in ${meetingName}` - : `${kudosEmojiUnicode} ${authorName} gave you kudos in ${locationType} in ${meetingName}` + const message = `${authorName} mentioned you in ${locationType} in ${meetingName}` const goThere = () => { history.push(actionUrl) diff --git a/packages/client/components/NotificationPicker.tsx b/packages/client/components/NotificationPicker.tsx index 1d562f24283..dbd05100f9d 100644 --- a/packages/client/components/NotificationPicker.tsx +++ b/packages/client/components/NotificationPicker.tsx @@ -54,9 +54,6 @@ const typePicker: Record> = { MENTIONED: lazyPreload(() => import(/* webpackChunkName: 'Mentioned' */ './Mentioned')), RESPONSE_REPLIED: lazyPreload( () => import(/* webpackChunkName: 'ResponseReplied' */ './ResponseReplied') - ), - KUDOS_RECEIVED: lazyPreload( - () => import(/* webpackChunkName: 'KudosReceivedNotification' */ './KudosReceivedNotification') ) } @@ -86,7 +83,6 @@ const NotificationPicker = (props: Props) => { ...TeamsLimitReminderNotification_notification ...PromptToJoinOrgNotification_notification ...RequestToJoinOrgNotification_notification - ...KudosReceivedNotification_notification } `, notificationRef diff --git a/packages/client/components/ResponseMentioned.tsx b/packages/client/components/ResponseMentioned.tsx index 57a42589138..616c474c278 100644 --- a/packages/client/components/ResponseMentioned.tsx +++ b/packages/client/components/ResponseMentioned.tsx @@ -31,21 +31,19 @@ const ResponseMentioned = (props: Props) => { } type status - kudosEmojiUnicode } `, notificationRef ) const {history} = useRouter() const atmosphere = useAtmosphere() - const {meeting, response, kudosEmojiUnicode, type, status} = notification + const {meeting, response, type, status} = notification const {picture: authorPicture, preferredName: authorName} = response.user useEffect(() => { SendClientSideEvent(atmosphere, 'Notification Viewed', { notificationType: type, - notificationStatus: status, - kudosEmojiUnicode + notificationStatus: status }) }, []) @@ -54,9 +52,7 @@ const ResponseMentioned = (props: Props) => { 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}.` + const message = `${authorName} mentioned you in their response in ${meetingName}.` // :TODO: (jmtaber129): Show mention preview. return ( diff --git a/packages/client/components/TeamPrompt/TeamPromptWorkDrawer.tsx b/packages/client/components/TeamPrompt/TeamPromptWorkDrawer.tsx index 9f51d4e28cd..836d3aea0e2 100644 --- a/packages/client/components/TeamPrompt/TeamPromptWorkDrawer.tsx +++ b/packages/client/components/TeamPrompt/TeamPromptWorkDrawer.tsx @@ -8,12 +8,14 @@ import gcalLogo from '../../styles/theme/images/graphics/google-calendar.svg' import SendClientSideEvent from '../../utils/SendClientSideEvent' import GitHubSVG from '../GitHubSVG' import JiraSVG from '../JiraSVG' +import JiraServerSVG from '../JiraServerSVG' import ParabolLogoSVG from '../ParabolLogoSVG' import Tab from '../Tab/Tab' import Tabs from '../Tabs/Tabs' import GCalIntegrationPanel from './WorkDrawer/GCalIntegrationPanel' import GitHubIntegrationPanel from './WorkDrawer/GitHubIntegrationPanel' import JiraIntegrationPanel from './WorkDrawer/JiraIntegrationPanel' +import JiraServerIntegrationPanel from './WorkDrawer/JiraServerIntegrationPanel' import ParabolTasksPanel from './WorkDrawer/ParabolTasksPanel' interface Props { @@ -32,11 +34,26 @@ const TeamPromptWorkDrawer = (props: Props) => { ...GitHubIntegrationPanel_meeting ...JiraIntegrationPanel_meeting ...GCalIntegrationPanel_meeting + ...JiraServerIntegrationPanel_meeting + viewerMeetingMember { + teamMember { + teamId + integrations { + jiraServer { + sharedProviders { + id + } + } + } + } + } } `, meetingRef ) const atmosphere = useAtmosphere() + const hasJiraServer = + !!meeting.viewerMeetingMember?.teamMember?.integrations.jiraServer?.sharedProviders?.length useEffect(() => { SendClientSideEvent(atmosphere, 'Your Work Drawer Impression', { @@ -54,6 +71,16 @@ const TeamPromptWorkDrawer = (props: Props) => { label: 'Parabol', Component: ParabolTasksPanel }, + ...(hasJiraServer + ? [ + { + icon: , + service: 'jiraServer', + label: 'Jira Server', + Component: JiraServerIntegrationPanel + } + ] + : []), {icon: , service: 'github', label: 'GitHub', Component: GitHubIntegrationPanel}, {icon: , service: 'jira', label: 'Jira', Component: JiraIntegrationPanel}, { diff --git a/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationPanel.tsx b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationPanel.tsx new file mode 100644 index 00000000000..da6eedf1a05 --- /dev/null +++ b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationPanel.tsx @@ -0,0 +1,95 @@ +import graphql from 'babel-plugin-relay/macro' +import React from 'react' +import {useFragment} from 'react-relay' +import {JiraServerIntegrationPanel_meeting$key} from '../../../__generated__/JiraServerIntegrationPanel_meeting.graphql' +import useAtmosphere from '../../../hooks/useAtmosphere' +import useMutationProps from '../../../hooks/useMutationProps' +import jiraServerSVG from '../../../styles/theme/images/graphics/jira-software-blue.svg' +import JiraServerClientManager from '../../../utils/JiraServerClientManager' +import SendClientSideEvent from '../../../utils/SendClientSideEvent' +import JiraServerIntegrationResultsRoot from './JiraServerIntegrationResultsRoot' + +interface Props { + meetingRef: JiraServerIntegrationPanel_meeting$key +} + +const JiraServerIntegrationPanel = (props: Props) => { + const {meetingRef} = props + const meeting = useFragment( + graphql` + fragment JiraServerIntegrationPanel_meeting on TeamPromptMeeting { + id + teamId + viewerMeetingMember { + teamMember { + teamId + integrations { + jiraServer { + auth { + id + isActive + } + sharedProviders { + id + } + } + } + } + } + } + `, + meetingRef + ) + + const teamMember = meeting.viewerMeetingMember?.teamMember + const integration = teamMember?.integrations.jiraServer + const providerId = integration?.sharedProviders?.[0]?.id + const isActive = !!integration?.auth?.isActive + + const atmosphere = useAtmosphere() + const mutationProps = useMutationProps() + const {error, onError} = mutationProps + + const authJiraServer = () => { + if (!teamMember || !providerId) { + return onError(new Error('Could not find integration provider')) + } + JiraServerClientManager.openOAuth(atmosphere, providerId, teamMember.teamId, mutationProps) + + SendClientSideEvent(atmosphere, 'Your Work Drawer Integration Connected', { + teamId: meeting.teamId, + meetingId: meeting.id, + service: 'jira server' + }) + } + if (!teamMember || !teamMember) { + return null + } + + return ( + <> + {isActive ? ( + + ) : ( +
+
+ +
+ Connect to Jira Server +
+ Connect to Jira Server to view your issues. +
+ + {error &&
Error: {error.message}
} +
+ )} + + ) +} + +export default JiraServerIntegrationPanel diff --git a/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResults.tsx b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResults.tsx new file mode 100644 index 00000000000..3beed1d4c9c --- /dev/null +++ b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResults.tsx @@ -0,0 +1,115 @@ +import graphql from 'babel-plugin-relay/macro' +import React from 'react' +import {PreloadedQuery, usePaginationFragment, usePreloadedQuery} from 'react-relay' +import {Link} from 'react-router-dom' +import halloweenRetrospectiveTemplate from '../../../../../static/images/illustrations/halloweenRetrospectiveTemplate.png' +import {JiraServerIntegrationResultsQuery} from '../../../__generated__/JiraServerIntegrationResultsQuery.graphql' +import {JiraServerIntegrationResultsSearchPaginationQuery} from '../../../__generated__/JiraServerIntegrationResultsSearchPaginationQuery.graphql' +import {JiraServerIntegrationResults_search$key} from '../../../__generated__/JiraServerIntegrationResults_search.graphql' +import useLoadNextOnScrollBottom from '../../../hooks/useLoadNextOnScrollBottom' +import Ellipsis from '../../Ellipsis/Ellipsis' +import JiraServerObjectCard from './JiraServerObjectCard' + +interface Props { + queryRef: PreloadedQuery + teamId: string +} + +const JiraServerIntegrationResults = (props: Props) => { + const {queryRef, teamId} = props + const query = usePreloadedQuery( + graphql` + query JiraServerIntegrationResultsQuery($teamId: ID!) { + ...JiraServerIntegrationResults_search @arguments(teamId: $teamId) + } + `, + queryRef + ) + + const paginationRes = usePaginationFragment< + JiraServerIntegrationResultsSearchPaginationQuery, + JiraServerIntegrationResults_search$key + >( + graphql` + fragment JiraServerIntegrationResults_search on Query + @argumentDefinitions( + cursor: {type: "String"} + count: {type: "Int", defaultValue: 20} + teamId: {type: "ID!"} + ) + @refetchable(queryName: "JiraServerIntegrationResultsSearchPaginationQuery") { + viewer { + teamMember(teamId: $teamId) { + integrations { + jiraServer { + issues( + first: $count + after: $cursor + isJQL: true + queryString: "assignee = currentUser() order by updated DESC" + ) @connection(key: "JiraServerScopingSearchResults_issues") { + error { + message + } + edges { + node { + ...JiraServerObjectCard_result + id + summary + url + issueKey + } + } + } + } + } + } + } + } + `, + query + ) + + const lastItem = useLoadNextOnScrollBottom(paginationRes, {}, 20) + const {data, hasNext} = paginationRes + + const jira = data.viewer.teamMember?.integrations.jiraServer + const jiraResults = jira?.issues.edges.map((edge) => edge.node) + const error = jira?.issues.error ?? null + + return ( + <> +
+ {jiraResults && jiraResults.length > 0 ? ( + jiraResults?.map((result, idx) => { + if (!result) { + return null + } + return + }) + ) : ( +
+ +
+ {error?.message ? error.message : `Looks like you don’t have any issues to display.`} +
+ + Review your Jira Server configuration + +
+ )} + {lastItem} + {hasNext && ( +
+ +
+ )} +
+ + ) +} + +export default JiraServerIntegrationResults diff --git a/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResultsRoot.tsx b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResultsRoot.tsx new file mode 100644 index 00000000000..c58e4170e3d --- /dev/null +++ b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerIntegrationResultsRoot.tsx @@ -0,0 +1,31 @@ +import React, {Suspense} from 'react' +import {Loader} from '~/utils/relay/renderLoader' +import jiraIntegrationResultsQuery, { + JiraServerIntegrationResultsQuery +} from '../../../__generated__/JiraServerIntegrationResultsQuery.graphql' +import useQueryLoaderNow from '../../../hooks/useQueryLoaderNow' +import ErrorBoundary from '../../ErrorBoundary' +import JiraServerIntegrationResults from './JiraServerIntegrationResults' + +interface Props { + teamId: string +} + +const JiraServerIntegrationResultsRoot = (props: Props) => { + const {teamId} = props + const queryRef = useQueryLoaderNow( + jiraIntegrationResultsQuery, + { + teamId: teamId + } + ) + return ( + + }> + {queryRef && } + + + ) +} + +export default JiraServerIntegrationResultsRoot diff --git a/packages/client/components/TeamPrompt/WorkDrawer/JiraServerObjectCard.tsx b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerObjectCard.tsx new file mode 100644 index 00000000000..dd6da759a71 --- /dev/null +++ b/packages/client/components/TeamPrompt/WorkDrawer/JiraServerObjectCard.tsx @@ -0,0 +1,121 @@ +import {Link} from '@mui/icons-material' +import graphql from 'babel-plugin-relay/macro' +import React from 'react' +import CopyToClipboard from 'react-copy-to-clipboard' +import {useFragment} from 'react-relay' +import {JiraServerObjectCard_result$key} from '../../../__generated__/JiraServerObjectCard_result.graphql' +import useAtmosphere from '../../../hooks/useAtmosphere' +import {MenuPosition} from '../../../hooks/useCoords' +import useTooltip from '../../../hooks/useTooltip' +import jiraSVG from '../../../styles/theme/images/graphics/jira.svg' +import SendClientSideEvent from '../../../utils/SendClientSideEvent' +import relativeDate from '../../../utils/date/relativeDate' +import {mergeRefs} from '../../../utils/react/mergeRefs' + +interface Props { + resultRef: JiraServerObjectCard_result$key +} + +const JiraServerObjectCard = (props: Props) => { + const {resultRef} = props + + const result = useFragment( + graphql` + fragment JiraServerObjectCard_result on JiraServerIssue { + id + summary + url + issueKey + projectKey + projectName + updatedAt + } + `, + resultRef + ) + + const atmosphere = useAtmosphere() + + const {tooltipPortal, openTooltip, closeTooltip, originRef} = useTooltip( + MenuPosition.UPPER_CENTER + ) + + const { + tooltipPortal: copiedTooltipPortal, + openTooltip: openCopiedTooltip, + closeTooltip: closeCopiedTooltip, + originRef: copiedTooltipRef + } = useTooltip(MenuPosition.LOWER_CENTER) + + const trackLinkClick = () => { + SendClientSideEvent(atmosphere, 'Your Work Drawer Card Link Clicked', { + service: 'jira' + }) + } + + const trackCopy = () => { + SendClientSideEvent(atmosphere, 'Your Work Drawer Card Copied', { + service: 'jira' + }) + } + + const handleCopy = () => { + openCopiedTooltip() + trackCopy() + setTimeout(() => { + closeCopiedTooltip() + }, 2000) + } + + const {summary, url, issueKey, projectName, updatedAt} = result + + return ( +
+
+ + {issueKey} + +
Updated {relativeDate(updatedAt)}
+
+ +
+
+
+ +
+
{projectName}
+
+ +
+ +
+
+ {tooltipPortal('Copy link')} + {copiedTooltipPortal('Copied!')} +
+
+ ) +} + +export default JiraServerObjectCard diff --git a/packages/client/modules/email/components/EmailNotifications/EmailResponseMentioned.tsx b/packages/client/modules/email/components/EmailNotifications/EmailResponseMentioned.tsx index dec6b3acf0c..7d42fb562f7 100644 --- a/packages/client/modules/email/components/EmailNotifications/EmailResponseMentioned.tsx +++ b/packages/client/modules/email/components/EmailNotifications/EmailResponseMentioned.tsx @@ -28,12 +28,11 @@ const EmailResponseMentioned = (props: Props) => { id name } - kudosEmojiUnicode } `, notificationRef ) - const {meeting, response, kudosEmojiUnicode} = notification + const {meeting, response} = notification const {rasterPicture: authorPicture, preferredName: authorName} = response.user const {id: meetingId, name: meetingName} = meeting @@ -47,9 +46,7 @@ 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}.` + const message = `${authorName} mentioned you in their response in ${meetingName}.` // :TODO: (jmtaber129): Show mention preview. return ( diff --git a/packages/client/mutations/AddReactjiToReactableMutation.ts b/packages/client/mutations/AddReactjiToReactableMutation.ts index 37fca00f436..d12f8c89066 100644 --- a/packages/client/mutations/AddReactjiToReactableMutation.ts +++ b/packages/client/mutations/AddReactjiToReactableMutation.ts @@ -3,7 +3,6 @@ import {commitMutation} from 'react-relay' import createProxyRecord from '~/utils/relay/createProxyRecord' import {AddReactjiToReactableMutation as TAddReactjiToReactableMutation} from '../__generated__/AddReactjiToReactableMutation.graphql' import {StandardMutation} from '../types/relayMutations' -import SendClientSideEvent from '../utils/SendClientSideEvent' graphql` fragment AddReactjiToReactableMutation_meeting on AddReactjiToReactableSuccess { @@ -38,14 +37,6 @@ const mutation = graphql` message } } - ... on AddReactjiToReactableSuccess { - addedKudos { - emojiUnicode - receiverUser { - preferredName - } - } - } ...AddReactjiToReactableMutation_meeting @relay(mask: false) } } @@ -113,29 +104,7 @@ const AddReactjiToReactableMutation: StandardMutation { - const {isRemove} = variables - const addedKudos = res.addReactjiToReactable.addedKudos - if (!isRemove && addedKudos) { - const {emojiUnicode} = addedKudos - atmosphere.eventEmitter.emit('addSnackbar', { - key: 'youGaveKudos', - message: `You gave kudos to ${addedKudos.receiverUser.preferredName} ${emojiUnicode}`, - autoDismiss: 5, - onShow: () => { - SendClientSideEvent(atmosphere, 'Snackbar Viewed', { - snackbarType: 'kudosSent' - }) - }, - onManualDismiss: () => { - SendClientSideEvent(atmosphere, 'Snackbar Clicked', { - snackbarType: 'kudosSent' - }) - } - }) - } - onCompleted(res, errors) - }, + onCompleted, onError }) } diff --git a/packages/client/mutations/CreateReflectionMutation.ts b/packages/client/mutations/CreateReflectionMutation.ts index f4974284134..a341f69653b 100644 --- a/packages/client/mutations/CreateReflectionMutation.ts +++ b/packages/client/mutations/CreateReflectionMutation.ts @@ -7,7 +7,6 @@ import {commitMutation} from 'react-relay' import {CreateReflectionMutation_meeting$data} from '~/__generated__/CreateReflectionMutation_meeting.graphql' import {CreateReflectionMutation as TCreateReflectionMutation} from '../__generated__/CreateReflectionMutation.graphql' import {SharedUpdater, StandardMutation} from '../types/relayMutations' -import SendClientSideEvent from '../utils/SendClientSideEvent' import makeEmptyStr from '../utils/draftjs/makeEmptyStr' import clientTempId from '../utils/relay/clientTempId' import createProxyRecord from '../utils/relay/createProxyRecord' @@ -34,12 +33,6 @@ graphql` id isNavigableByFacilitator } - draftKudoses { - receiverUser { - preferredName - } - emojiUnicode - } } ` @@ -66,30 +59,7 @@ const CreateReflectionMutation: StandardMutation = ( return commitMutation(atmosphere, { mutation, variables, - onCompleted: (res, errors) => { - const draftKudoses = res.createReflection?.draftKudoses - if (draftKudoses && draftKudoses.length) { - const preferredNames = draftKudoses - .map((kudos) => kudos.receiverUser.preferredName) - .join(', ') - atmosphere.eventEmitter.emit('addSnackbar', { - key: `youGaveKudos:${res.createReflection.reflectionId}`, - message: `${preferredNames} will receive kudos at the end ot the meeting ${draftKudoses[0]?.emojiUnicode}`, - autoDismiss: 5, - onShow: () => { - SendClientSideEvent(atmosphere, 'Snackbar Viewed', { - snackbarType: 'kudosSent' - }) - }, - onManualDismiss: () => { - SendClientSideEvent(atmosphere, 'Snackbar Clicked', { - snackbarType: 'kudosSent' - }) - } - }) - } - onCompleted(res, errors) - }, + onCompleted, onError, updater: (store) => { const payload = store.getRootField('createReflection') diff --git a/packages/client/mutations/UpsertTeamPromptResponseMutation.ts b/packages/client/mutations/UpsertTeamPromptResponseMutation.ts index 6abc25f0660..3f010784faa 100644 --- a/packages/client/mutations/UpsertTeamPromptResponseMutation.ts +++ b/packages/client/mutations/UpsertTeamPromptResponseMutation.ts @@ -4,7 +4,6 @@ import {UpsertTeamPromptResponseMutation_meeting$data} from '~/__generated__/Ups import clientTempId from '~/utils/relay/clientTempId' import {UpsertTeamPromptResponseMutation as TUpsertTeamPromptResponseMutation} from '../__generated__/UpsertTeamPromptResponseMutation.graphql' import {LocalHandlers, SharedUpdater, StandardMutation} from '../types/relayMutations' -import SendClientSideEvent from '../utils/SendClientSideEvent' graphql` fragment UpsertTeamPromptResponseMutation_meeting on UpsertTeamPromptResponseSuccess { @@ -18,12 +17,6 @@ graphql` createdAt ...TeamPromptResponseEmojis_response } - addedKudoses { - receiverUser { - preferredName - } - emojiUnicode - } } ` @@ -104,30 +97,7 @@ const UpsertTeamPromptResponseMutation: StandardMutation< const payload = store.getRootField('upsertTeamPromptResponse') upsertTeamPromptResponseUpdater(payload as any, {atmosphere, store}) }, - 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) - }, + onCompleted, onError }) } diff --git a/packages/client/mutations/toasts/mapKudosReceivedToToast.ts b/packages/client/mutations/toasts/mapKudosReceivedToToast.ts deleted file mode 100644 index d8876a5cfe8..00000000000 --- a/packages/client/mutations/toasts/mapKudosReceivedToToast.ts +++ /dev/null @@ -1,47 +0,0 @@ -import graphql from 'babel-plugin-relay/macro' -import {mapKudosReceivedToToast_notification$data} from '../../__generated__/mapKudosReceivedToToast_notification.graphql' -import {Snack} from '../../components/Snackbar' -import {OnNextHistoryContext} from '../../types/relayMutations' -import SendClientSideEvent from '../../utils/SendClientSideEvent' -import makeNotificationToastKey from './makeNotificationToastKey' - -graphql` - fragment mapKudosReceivedToToast_notification on NotifyKudosReceived { - id - name - meetingName - meetingId - emojiUnicode - } -` - -const mapKudosReceivedToToast = ( - notification: mapKudosReceivedToToast_notification$data, - {atmosphere, history}: OnNextHistoryContext -): Snack => { - const {id: notificationId, meetingName, name, emojiUnicode, meetingId} = notification - return { - autoDismiss: 5, - showDismissButton: true, - key: makeNotificationToastKey(notificationId), - message: `${emojiUnicode} ${name ?? 'Someone'} gave you kudos in`, - action: { - label: meetingName, - callback: () => { - history.push(`/meet/${meetingId}`) - } - }, - onShow: () => { - SendClientSideEvent(atmosphere, 'Snackbar Viewed', { - snackbarType: 'kudosReceived' - }) - }, - onManualDismiss: () => { - SendClientSideEvent(atmosphere, 'Snackbar Clicked', { - snackbarType: 'kudosReceived' - }) - } - } -} - -export default mapKudosReceivedToToast diff --git a/packages/client/mutations/toasts/mapMentionedToToast.ts b/packages/client/mutations/toasts/mapMentionedToToast.ts index 3962f9adf6d..8244d3c282d 100644 --- a/packages/client/mutations/toasts/mapMentionedToToast.ts +++ b/packages/client/mutations/toasts/mapMentionedToToast.ts @@ -8,7 +8,6 @@ import makeNotificationToastKey from './makeNotificationToastKey' graphql` fragment mapMentionedToToast_notification on NotifyMentioned { id - kudosEmojiUnicode senderName meetingId meetingName @@ -28,7 +27,6 @@ const mapMentionedToToast = ( id: notificationId, senderName, meetingName, - kudosEmojiUnicode, retroReflection, retroDiscussStageIdx, meetingId @@ -51,9 +49,7 @@ const mapMentionedToToast = ( } } - const message = !kudosEmojiUnicode - ? `${authorName} mentioned you in ${locationType} in ${meetingName}` - : `${kudosEmojiUnicode} ${authorName} gave you kudos in ${locationType} in ${meetingName}` + const message = `${authorName} mentioned you in ${locationType} in ${meetingName}` const goThere = () => { history.push(actionUrl) @@ -69,14 +65,12 @@ const mapMentionedToToast = ( }, onShow: () => { SendClientSideEvent(atmosphere, 'Snackbar Viewed', { - snackbarType, - kudosEmojiUnicode + snackbarType }) }, onManualDismiss: () => { SendClientSideEvent(atmosphere, 'Snackbar Clicked', { - snackbarType, - kudosEmojiUnicode + snackbarType }) } } diff --git a/packages/client/mutations/toasts/mapResponseMentionedToToast.ts b/packages/client/mutations/toasts/mapResponseMentionedToToast.ts index ccf6199d1ef..f36a13eacac 100644 --- a/packages/client/mutations/toasts/mapResponseMentionedToToast.ts +++ b/packages/client/mutations/toasts/mapResponseMentionedToToast.ts @@ -18,7 +18,6 @@ graphql` id name } - kudosEmojiUnicode } ` @@ -27,13 +26,11 @@ const mapResponseMentionedToToast = ( {atmosphere, history}: OnNextHistoryContext ): Snack | null => { if (!notification) return null - const {id: notificationId, meeting, response, kudosEmojiUnicode} = notification + const {id: notificationId, meeting, response} = 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}.` + const message = `${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. @@ -50,14 +47,12 @@ const mapResponseMentionedToToast = ( }, onShow: () => { SendClientSideEvent(atmosphere, 'Snackbar Viewed', { - snackbarType: 'responseMentioned', - kudosEmojiUnicode + snackbarType: 'responseMentioned' }) }, onManualDismiss: () => { SendClientSideEvent(atmosphere, 'Snackbar Clicked', { - snackbarType: 'responseMentioned', - kudosEmojiUnicode + snackbarType: 'responseMentioned' }) } } diff --git a/packages/client/mutations/toasts/popNotificationToast.ts b/packages/client/mutations/toasts/popNotificationToast.ts index 9668d92814d..9c416d8643f 100644 --- a/packages/client/mutations/toasts/popNotificationToast.ts +++ b/packages/client/mutations/toasts/popNotificationToast.ts @@ -7,7 +7,6 @@ import {Snack} from '../../components/Snackbar' import {OnNextHandler, OnNextHistoryContext} from '../../types/relayMutations' import SetNotificationStatusMutation from '../SetNotificationStatusMutation' import mapDiscussionMentionedToToast from './mapDiscussionMentionedToToast' -import mapKudosReceivedToToast from './mapKudosReceivedToToast' import mapMentionedToToast from './mapMentionedToToast' import mapPromptToJoinOrgToToast from './mapPromptToJoinOrgToToast' import mapRequestToJoinOrgToToast from './mapRequestToJoinOrgToToast' @@ -26,8 +25,7 @@ const typePicker: Partial< TEAMS_LIMIT_EXCEEDED: mapTeamsLimitExceededToToast, TEAMS_LIMIT_REMINDER: mapTeamsLimitReminderToToast, PROMPT_TO_JOIN_ORG: mapPromptToJoinOrgToToast, - REQUEST_TO_JOIN_ORG: mapRequestToJoinOrgToToast, - KUDOS_RECEIVED: mapKudosReceivedToToast + REQUEST_TO_JOIN_ORG: mapRequestToJoinOrgToToast } graphql` @@ -43,7 +41,6 @@ graphql` ...mapTeamsLimitReminderToToast_notification @relay(mask: false) ...mapPromptToJoinOrgToToast_notification @relay(mask: false) ...mapRequestToJoinOrgToToast_notification @relay(mask: false) - ...mapKudosReceivedToToast_notification @relay(mask: false) } } ` diff --git a/packages/client/package.json b/packages/client/package.json index 7f42b509ef8..fa84db39acd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.33.0", + "version": "7.35.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/embedder/package.json b/packages/embedder/package.json index 0232cf845d0..59aa95203b6 100644 --- a/packages/embedder/package.json +++ b/packages/embedder/package.json @@ -1,6 +1,6 @@ { "name": "parabol-embedder", - "version": "7.33.0", + "version": "7.35.0", "description": "A service that computes embedding vectors from Parabol objects", "author": "Jordan Husney ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/embedder#readme", diff --git a/packages/gql-executor/package.json b/packages/gql-executor/package.json index 07d478b8cad..cbbeb8e3554 100644 --- a/packages/gql-executor/package.json +++ b/packages/gql-executor/package.json @@ -1,6 +1,6 @@ { "name": "gql-executor", - "version": "7.33.0", + "version": "7.35.0", "description": "A Stateless GraphQL Executor", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/gqlExecutor#readme", @@ -27,8 +27,8 @@ }, "dependencies": { "dd-trace": "^4.2.0", - "parabol-client": "7.33.0", - "parabol-server": "7.33.0", + "parabol-client": "7.35.0", + "parabol-server": "7.35.0", "undici": "^5.26.2" } } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 99e1b54ef3e..61634e7a21e 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -2,7 +2,7 @@ "name": "integration-tests", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.33.0", + "version": "7.35.0", "description": "", "main": "index.js", "scripts": { diff --git a/packages/server/database/types/Notification.ts b/packages/server/database/types/Notification.ts index f49fe990c45..fcea79d8f4e 100644 --- a/packages/server/database/types/Notification.ts +++ b/packages/server/database/types/Notification.ts @@ -17,7 +17,6 @@ export type NotificationEnum = | 'TEAMS_LIMIT_REMINDER' | 'PROMPT_TO_JOIN_ORG' | 'REQUEST_TO_JOIN_ORG' - | 'KUDOS_RECEIVED' export interface NotificationInput { type: NotificationEnum diff --git a/packages/server/database/types/NotificationKudosReceived.ts b/packages/server/database/types/NotificationKudosReceived.ts deleted file mode 100644 index 04a6efcf8ec..00000000000 --- a/packages/server/database/types/NotificationKudosReceived.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Notification from './Notification' - -interface Input { - userId: string - name: string | null - picture: string | null - senderUserId: string - meetingName: string - meetingId: string - emoji: string - emojiUnicode: string -} - -export default class NotificationKudosReceived extends Notification { - readonly type = 'KUDOS_RECEIVED' - name: string | null - picture: string | null - senderUserId: string - meetingName: string - meetingId: string - emoji: string - emojiUnicode: string - - constructor(input: 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 - } -} diff --git a/packages/server/database/types/NotificationMentioned.ts b/packages/server/database/types/NotificationMentioned.ts index 16abf39e8c0..c35dff270bb 100644 --- a/packages/server/database/types/NotificationMentioned.ts +++ b/packages/server/database/types/NotificationMentioned.ts @@ -9,8 +9,6 @@ interface Input { meetingId: string retroReflectionId?: string | null retroDiscussStageIdx?: number | null - kudosEmoji?: string | null - kudosEmojiUnicode?: string | null } // TODO: replace NotificationResponseMentioned and NotificationResponseReplied with NotificationMentioned @@ -23,8 +21,6 @@ export default class NotificationMentioned extends Notification { meetingId: string retroReflectionId?: string | null retroDiscussStageIdx?: number | null - kudosEmoji?: string | null - kudosEmojiUnicode?: string | null constructor(input: Input) { const { @@ -35,9 +31,7 @@ export default class NotificationMentioned extends Notification { meetingName, meetingId, retroReflectionId, - retroDiscussStageIdx, - kudosEmoji, - kudosEmojiUnicode + retroDiscussStageIdx } = input super({userId, type: 'MENTIONED'}) this.senderName = senderName @@ -46,8 +40,6 @@ export default class NotificationMentioned extends Notification { this.meetingName = meetingName this.meetingId = meetingId this.retroReflectionId = retroReflectionId - this.kudosEmoji = kudosEmoji - this.kudosEmojiUnicode = kudosEmojiUnicode this.retroDiscussStageIdx = retroDiscussStageIdx } } diff --git a/packages/server/database/types/NotificationResponseMentioned.ts b/packages/server/database/types/NotificationResponseMentioned.ts index 562f40d1321..3368b54babb 100644 --- a/packages/server/database/types/NotificationResponseMentioned.ts +++ b/packages/server/database/types/NotificationResponseMentioned.ts @@ -4,23 +4,17 @@ 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, kudosEmoji, kudosEmojiUnicode} = input + const {responseId, meetingId, userId} = input super({userId, type: 'RESPONSE_MENTIONED'}) this.responseId = responseId this.meetingId = meetingId - this.kudosEmoji = kudosEmoji - this.kudosEmojiUnicode = kudosEmojiUnicode } } diff --git a/packages/server/database/types/Team.ts b/packages/server/database/types/Team.ts index 0e6055d6281..b5bf6434a84 100644 --- a/packages/server/database/types/Team.ts +++ b/packages/server/database/types/Team.ts @@ -17,8 +17,6 @@ interface Input { orgId: string qualAIMeetingsCount?: number isOnboardTeam?: boolean - giveKudosWithEmoji?: boolean - kudosEmoji?: string updatedAt?: Date } @@ -36,8 +34,6 @@ export default class Team { trialStartDate?: Date | null orgId: string isOnboardTeam: boolean - giveKudosWithEmoji: boolean - kudosEmoji: string qualAIMeetingsCount: number updatedAt: Date constructor(input: Input) { @@ -48,8 +44,6 @@ export default class Team { id, isArchived, isOnboardTeam, - giveKudosWithEmoji, - kudosEmoji, lastMeetingType, isPaid, name, @@ -71,8 +65,6 @@ export default class Team { this.lastMeetingType = lastMeetingType ?? 'retrospective' this.isArchived = isArchived ?? false this.isOnboardTeam = isOnboardTeam ?? false - this.giveKudosWithEmoji = giveKudosWithEmoji ?? true - this.kudosEmoji = kudosEmoji ?? 'heart' this.isPaid = isPaid ?? true this.qualAIMeetingsCount = qualAIMeetingsCount ?? 0 } diff --git a/packages/server/dataloader/primaryKeyLoaderMakers.ts b/packages/server/dataloader/primaryKeyLoaderMakers.ts index 09f2b463024..187d6802720 100644 --- a/packages/server/dataloader/primaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/primaryKeyLoaderMakers.ts @@ -1,7 +1,6 @@ import getKysely from '../postgres/getKysely' import {getDiscussionsByIds} from '../postgres/queries/getDiscussionsByIds' import {getDomainJoinRequestsByIds} from '../postgres/queries/getDomainJoinRequestsByIds' -import {getKudosesByIds} from '../postgres/queries/getKudosesByIds' import getMeetingSeriesByIds from '../postgres/queries/getMeetingSeriesByIds' import getMeetingTemplatesByIds from '../postgres/queries/getMeetingTemplatesByIds' import {getTeamPromptResponsesByIds} from '../postgres/queries/getTeamPromptResponsesByIds' @@ -20,7 +19,6 @@ export const teamPromptResponses = primaryKeyLoaderMaker(getTeamPromptResponsesB export const meetingSeries = primaryKeyLoaderMaker(getMeetingSeriesByIds) export const meetingTemplates = primaryKeyLoaderMaker(getMeetingTemplatesByIds) export const domainJoinRequests = primaryKeyLoaderMaker(getDomainJoinRequestsByIds) -export const kudoses = primaryKeyLoaderMaker(getKudosesByIds) export const embeddingsMetadata = primaryKeyLoaderMaker((ids: readonly number[]) => { return getKysely().selectFrom('EmbeddingsMetadata').selectAll().where('id', 'in', ids).execute() diff --git a/packages/server/graphql/mutations/addIntegrationProvider.ts b/packages/server/graphql/mutations/addIntegrationProvider.ts index a2da4c321f3..bafee1d0c61 100644 --- a/packages/server/graphql/mutations/addIntegrationProvider.ts +++ b/packages/server/graphql/mutations/addIntegrationProvider.ts @@ -30,12 +30,19 @@ const addIntegrationProvider = { context: GQLContext ) => { const {authToken, dataLoader, socketId: mutatorId} = context - const {teamId} = input + const {teamId, scope} = input const operationId = dataLoader.share() const subOptions = {mutatorId, operationId} // AUTH - if (!isTeamMember(authToken, teamId) && !isSuperUser(authToken)) { + if (scope === 'global') { + if (!isSuperUser(authToken)) { + return {error: {message: 'Global scope requires su'}} + } + if (teamId !== 'aGhostTeam') { + return {error: {message: 'Global scope requires teamId to be aGhostTeam'}} + } + } else if (!isTeamMember(authToken, teamId) && !isSuperUser(authToken)) { return {error: {message: 'Must be on the team for which the provider is created'}} } diff --git a/packages/server/graphql/mutations/createReflection.ts b/packages/server/graphql/mutations/createReflection.ts index f9a7fd64760..7eaa94998f5 100644 --- a/packages/server/graphql/mutations/createReflection.ts +++ b/packages/server/graphql/mutations/createReflection.ts @@ -1,4 +1,3 @@ -import {RawDraftContentState} from 'draft-js' import {GraphQLNonNull} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import extractTextFromDraftString from 'parabol-client/utils/draftjs/extractTextFromDraftString' @@ -67,37 +66,6 @@ export default { // RESOLUTION const plaintextContent = extractTextFromDraftString(normalizedContent) - const contentJson = JSON.parse(normalizedContent) as RawDraftContentState - const draftKudoses: { - id: string - receiverUserId: string - emoji: string - emojiUnicode: string - }[] = [] - - const {giveKudosWithEmoji, kudosEmojiUnicode, kudosEmoji} = team - if ( - giveKudosWithEmoji && - kudosEmojiUnicode && - plaintextContent.includes(kudosEmojiUnicode) && - contentJson.entityMap - ) { - const mentions = Object.values(contentJson.entityMap).filter( - (entity) => entity.type === 'MENTION' - ) - const userIds = [...new Set(mentions.map((mention) => mention.data.userId))].filter( - (userId) => userId !== viewerId - ) - - userIds.forEach((userId) => { - draftKudoses.push({ - id: 'DRAFT_KUDOS_' + generateUID(), - receiverUserId: userId, - emoji: kudosEmoji, - emojiUnicode: kudosEmojiUnicode - }) - }) - } const [entities, sentimentScore] = await Promise.all([ getReflectionEntities(plaintextContent), @@ -155,8 +123,7 @@ export default { meetingId, reflectionId: reflection.id, reflectionGroupId, - unlockedStageIds, - draftKudoses + unlockedStageIds } publish(SubscriptionChannel.MEETING, meetingId, 'CreateReflectionPayload', data, subOptions) return data diff --git a/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts b/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts index 089ca295826..463eeecf935 100644 --- a/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts +++ b/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts @@ -284,9 +284,7 @@ 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}*` + const title = `*${author.preferredName}* mentioned you in their response in *${meeting.name}*` const options = { searchParams: { @@ -331,9 +329,7 @@ const getSlackMessageForNotification = async ( ) } - const title = notification.kudosEmojiUnicode - ? `${notification.kudosEmojiUnicode} *${authorName}* mentioned you and gave kudos in their ${location} in *${meeting.name}*` - : `*${authorName}* mentioned you in their ${location} in *${meeting.name}*` + const title = `*${authorName}* mentioned you in their ${location} in *${meeting.name}*` return { buttonUrl, diff --git a/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts b/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts index 9b47bbda356..2c9c282e648 100644 --- a/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts +++ b/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts @@ -1,4 +1,3 @@ -import {RawDraftContentState} from 'draft-js' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import {DISCUSS, PARABOL_AI_USER_ID} from 'parabol-client/utils/constants' import getMeetingPhase from 'parabol-client/utils/getMeetingPhase' @@ -7,7 +6,6 @@ import {checkTeamsLimit} from '../../../billing/helpers/teamLimitsCheck' import getRethink from '../../../database/rethinkDriver' import {RDatum} from '../../../database/stricterR' import MeetingRetrospective from '../../../database/types/MeetingRetrospective' -import NotificationMentioned from '../../../database/types/NotificationMentioned' import TimelineEventRetroComplete from '../../../database/types/TimelineEventRetroComplete' import getKysely from '../../../postgres/getKysely' import removeSuggestedAction from '../../../safeMutations/removeSuggestedAction' @@ -20,7 +18,6 @@ import publish from '../../../utils/publish' import sendToSentry from '../../../utils/sendToSentry' import standardError from '../../../utils/standardError' import {InternalContext} from '../../graphql' -import publishNotification from '../../public/mutations/helpers/publishNotification' import sendNewMeetingSummary from './endMeeting/sendNewMeetingSummary' import gatherInsights from './gatherInsights' import generateWholeMeetingSentimentScore from './generateWholeMeetingSentimentScore' @@ -37,148 +34,6 @@ const getTranscription = async (recallBotId?: string | null) => { return await manager.getBotTranscript(recallBotId) } -const sendKudos = async ( - meeting: MeetingRetrospective, - teamId: string, - context: InternalContext -) => { - const {dataLoader, socketId: mutatorId} = context - const operationId = dataLoader.share() - const subOptions = {mutatorId, operationId} - const {id: meetingId, disableAnonymity} = meeting - const isAnonymous = !disableAnonymity - const pg = getKysely() - const r = await getRethink() - - const [reflections, team, meetingMembers] = await Promise.all([ - dataLoader.get('retroReflectionsByMeetingId').load(meetingId), - dataLoader.get('teams').loadNonNull(teamId), - dataLoader.get('meetingMembersByMeetingId').load(meetingId) - ]) - - const {phases} = meeting - const discussPhase = getPhase(phases, 'discuss') - if (!discussPhase) { - return - } - const {stages} = discussPhase - - const {giveKudosWithEmoji, kudosEmojiUnicode, kudosEmoji} = team - - if (!giveKudosWithEmoji || !kudosEmojiUnicode) { - return - } - - const kudosToInsert: { - senderUserId: string - receiverUserId: any - teamId: string - emoji: string - emojiUnicode: string - isAnonymous: boolean - reflectionId: string - }[] = [] - - const notificationsToInsert: NotificationMentioned[] = [] - - for (const reflection of reflections) { - const {id: reflectionId, content, plaintextContent, creatorId} = reflection - const senderUser = await dataLoader.get('users').loadNonNull(creatorId) - - const contentJson = JSON.parse(content) as RawDraftContentState - - const mentions = contentJson.entityMap - ? Object.values(contentJson.entityMap).filter((entity) => entity.type === 'MENTION') - : [] - - if (mentions.length) { - const userIds = [...new Set(mentions.map((mention) => mention.data.userId))].filter( - (userId) => userId !== creatorId - ) - - if (userIds.length) { - const retroDiscussStageIdx = - stages.findIndex((stage) => stage.reflectionGroupId === reflection.reflectionGroupId) + 1 - - if (plaintextContent.includes(kudosEmojiUnicode)) { - userIds.forEach((userId) => { - kudosToInsert.push({ - senderUserId: creatorId, - receiverUserId: userId, - teamId, - emoji: kudosEmoji, - emojiUnicode: kudosEmojiUnicode, - isAnonymous, - reflectionId - }) - notificationsToInsert.push( - new NotificationMentioned({ - userId, - senderUserId: creatorId, - meetingId, - retroDiscussStageIdx, - retroReflectionId: reflectionId, - kudosEmoji: team.kudosEmoji, - kudosEmojiUnicode: team.kudosEmojiUnicode, - meetingName: meeting.name, - senderName: isAnonymous ? null : senderUser.preferredName, - senderPicture: isAnonymous ? null : senderUser.picture - }) - ) - }) - } else { - const absentUserIds = userIds.filter( - (userId) => !meetingMembers.find((member) => member.userId === userId) - ) - - absentUserIds.forEach((userId) => { - notificationsToInsert.push( - new NotificationMentioned({ - userId, - senderUserId: creatorId, - meetingId, - retroDiscussStageIdx, - retroReflectionId: reflectionId, - meetingName: meeting.name, - senderName: isAnonymous ? null : senderUser.preferredName, - senderPicture: isAnonymous ? null : senderUser.picture - }) - ) - }) - } - } - } - } - - if (kudosToInsert.length) { - const insertedKudoses = await pg - .insertInto('Kudos') - .values(kudosToInsert) - .returning(['id', 'senderUserId', 'receiverUserId', 'emoji', 'emojiUnicode']) - .execute() - - insertedKudoses.forEach((kudos) => { - analytics.kudosSent( - {id: kudos.senderUserId}, - teamId, - kudos.id, - kudos.receiverUserId, - 'mention', - 'retrospective', - isAnonymous - ) - }) - } - - if (notificationsToInsert.length) { - await r.table('Notification').insert(notificationsToInsert).run() - notificationsToInsert.forEach((notification) => { - IntegrationNotifier.sendNotificationToUser?.(dataLoader, notification.id, notification.userId) - publishNotification(notification, subOptions) - }) - } -} - const summarizeRetroMeeting = async (meeting: MeetingRetrospective, context: InternalContext) => { const {dataLoader, authToken} = context const {id: meetingId, phases, facilitatorUserId, teamId, recallBotId} = meeting @@ -323,8 +178,7 @@ const safeEndRetrospective = async ({ .filter({isActive: false}) .delete() .run(), - updateTeamInsights(teamId, dataLoader), - sendKudos(completedRetrospective, teamId, context) + updateTeamInsights(teamId, dataLoader) ]) // wait for removeEmptyTasks before summarizeRetroMeeting // don't await for the OpenAI response or it'll hang for a while when ending the retro diff --git a/packages/server/graphql/private/typeDefs/_legacy.graphql b/packages/server/graphql/private/typeDefs/_legacy.graphql index 0aeca980ef5..406fd1d39c0 100644 --- a/packages/server/graphql/private/typeDefs/_legacy.graphql +++ b/packages/server/graphql/private/typeDefs/_legacy.graphql @@ -475,7 +475,6 @@ type Mutation { subscriptionId: ID! ): Boolean - """ When a new invoiceitem is sent from stripe, tag it with metadata """ @@ -1096,6 +1095,7 @@ type JiraServerIssue implements TaskIntegration { id: ID! issueKey: ID! projectKey: ID! + projectName: String! """ The parabol teamId this issue was fetched for @@ -1122,6 +1122,11 @@ type JiraServerIssue implements TaskIntegration { The description converted into raw HTML """ descriptionHTML: String! + + """ + The timestamp the issue was last updated + """ + updatedAt: DateTime! } """ @@ -1657,7 +1662,6 @@ enum NotificationEnum { TEAMS_LIMIT_REMINDER PROMPT_TO_JOIN_ORG REQUEST_TO_JOIN_ORG - KUDOS_RECEIVED } """ diff --git a/packages/server/graphql/private/typeDefs/updateOrgFeatureFlag.graphql b/packages/server/graphql/private/typeDefs/updateOrgFeatureFlag.graphql index 6806f1f872a..095acdd2fd7 100644 --- a/packages/server/graphql/private/typeDefs/updateOrgFeatureFlag.graphql +++ b/packages/server/graphql/private/typeDefs/updateOrgFeatureFlag.graphql @@ -12,7 +12,6 @@ enum OrganizationFeatureFlagsEnum { noTeamInsights singleColumnStandups publicTeams - kudos aiTemplate relatedDiscussions } diff --git a/packages/server/graphql/public/mutations/addReactjiToReactable.ts b/packages/server/graphql/public/mutations/addReactjiToReactable.ts index 0a343f921f4..2729d9b0d83 100644 --- a/packages/server/graphql/public/mutations/addReactjiToReactable.ts +++ b/packages/server/graphql/public/mutations/addReactjiToReactable.ts @@ -1,3 +1,4 @@ +import {sql} from 'kysely' import TeamPromptResponseId from 'parabol-client/shared/gqlIds/TeamPromptResponseId' import {SubscriptionChannel, Threshold} from 'parabol-client/types/constEnums' import {ValueOf} from 'parabol-client/types/generics' @@ -7,23 +8,16 @@ import {RDatum} from '../../../database/stricterR' import Comment from '../../../database/types/Comment' import {Reactable} from '../../../database/types/Reactable' import Reflection from '../../../database/types/Reflection' +import getKysely from '../../../postgres/getKysely' import {analytics} from '../../../utils/analytics/analytics' import {getUserId} from '../../../utils/authorization' import emojiIds from '../../../utils/emojiIds' import getGroupedReactjis from '../../../utils/getGroupedReactjis' import publish from '../../../utils/publish' import {GQLContext} from '../../graphql' - -import {sql} from 'kysely' -import MeetingRetrospective from '../../../database/types/MeetingRetrospective' -import NotificationKudosReceived from '../../../database/types/NotificationKudosReceived' -import getKysely from '../../../postgres/getKysely' -import {TeamPromptResponse} from '../../../postgres/queries/getTeamPromptResponsesByIds' -import {AnyMeeting} from '../../../postgres/types/Meeting' import {ReactableEnumType} from '../../types/ReactableEnum' import getReactableType from '../../types/getReactableType' import {MutationResolvers} from '../resolverTypes' -import publishNotification from './helpers/publishNotification' const rethinkTableLookup = { COMMENT: 'Comment', @@ -34,28 +28,6 @@ const pgDataloaderLookup = { RESPONSE: 'teamPromptResponses' } as const -const getReactableCreatorId = ( - reactableType: ReactableEnumType, - reactable: Reactable, - meeting: AnyMeeting -) => { - if (reactableType === 'COMMENT') { - if ((reactable as Comment).isAnonymous) { - return null - } - return (reactable as Comment).createdBy - } else if (reactableType === 'REFLECTION') { - if ((meeting as MeetingRetrospective).disableAnonymity) { - return (reactable as Reflection).creatorId - } - return null - } else if (reactableType === 'RESPONSE') { - return (reactable as TeamPromptResponse).userId - } - - return null -} - const addReactjiToReactable: MutationResolvers['addReactjiToReactable'] = async ( _source: unknown, { @@ -171,54 +143,9 @@ const addReactjiToReactable: MutationResolvers['addReactjiToReactable'] = async } const meeting = await dataLoader.get('newMeetings').load(meetingId) - const {meetingType, teamId} = meeting - const team = await dataLoader.get('teams').loadNonNull(teamId) - - const reactableCreatorId = getReactableCreatorId(reactableType, reactable, meeting) - - let addedKudosId = null - if ( - !isRemove && - team.giveKudosWithEmoji && - reactji === team.kudosEmoji && - reactableCreatorId && - reactableCreatorId !== viewerId - ) { - addedKudosId = (await pg - .insertInto('Kudos') - .values({ - senderUserId: viewerId, - receiverUserId: reactableCreatorId, - reactableType: reactableType, - reactableId: reactableId, - teamId, - emoji: team.kudosEmoji, - emojiUnicode: team.kudosEmojiUnicode - }) - .returning('id') - .executeTakeFirst())!.id - - const senderUser = await dataLoader.get('users').loadNonNull(viewerId) - - const notificationsToInsert = new NotificationKudosReceived({ - userId: reactableCreatorId, - senderUserId: viewerId, - meetingId, - meetingName: meeting.name, - emoji: team.kudosEmoji, - emojiUnicode: team.kudosEmojiUnicode, - name: senderUser.preferredName, - picture: senderUser.picture - }) - - await r.table('Notification').insert(notificationsToInsert).run() - - publishNotification(notificationsToInsert, subOptions) - - analytics.kudosSent(viewer, teamId, addedKudosId, reactableCreatorId, 'reaction', meetingType) - } + const {meetingType} = meeting - const data = {reactableId, reactableType, addedKudosId} + const data = {reactableId, reactableType} analytics.reactjiInteracted( viewer, diff --git a/packages/server/graphql/public/mutations/helpers/__tests__/getKudosUserIdsFromJson.test.ts b/packages/server/graphql/public/mutations/helpers/__tests__/getKudosUserIdsFromJson.test.ts deleted file mode 100644 index 9bdff492939..00000000000 --- a/packages/server/graphql/public/mutations/helpers/__tests__/getKudosUserIdsFromJson.test.ts +++ /dev/null @@ -1,252 +0,0 @@ -import {JSONContent} from '@tiptap/core' -import {getKudosUserIdsFromJson} from '../getKudosUserIdsFromJson' - -describe('findMentionsByEmoji', () => { - let doc: JSONContent - - beforeAll(() => { - doc = { - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Paragraph1' - } - ] - }, - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Paragraph2' - } - ] - }, - { - type: 'paragraph' - }, - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Paragraph from new line' - } - ] - }, - { - type: 'paragraph' - }, - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Paragraph from new line' - }, - { - type: 'hardBreak' - }, - { - type: 'text', - text: 'and break in the same paragraph' - } - ] - }, - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Just new line with mention ' - }, - { - type: 'mention', - attrs: { - id: 'user_id_1', - label: 'userone' - } - }, - { - type: 'text', - text: ' ❤️' - } - ] - }, - { - type: 'paragraph' - }, - { - type: 'paragraph', - content: [ - { - type: 'text', - text: '❤️ Another mentions ' - }, - { - type: 'mention', - attrs: { - id: 'user_id_2', - label: 'usertwo' - } - }, - { - type: 'hardBreak' - }, - { - type: 'mention', - attrs: { - id: 'user_id_3', - label: 'userthree' - } - } - ] - }, - { - type: 'paragraph', - content: [ - { - type: 'mention', - attrs: { - id: 'user_id_supermention', - label: 'supermention' - } - }, - { - type: 'text', - text: ' 🌮' - } - ] - }, - { - type: 'paragraph' - }, - { - type: 'paragraph', - content: [ - { - type: 'mention', - attrs: { - id: 'user_id_4', - label: 'userone' - } - }, - { - type: 'text', - text: ' ' - }, - { - type: 'mention', - attrs: { - id: 'user_id_5', - label: 'userfour' - } - }, - { - type: 'text', - text: ' both mentioned ❤️' - } - ] - }, - { - type: 'bulletList', - content: [ - { - type: 'listItem', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'List item with mention ' - }, - { - type: 'mention', - attrs: { - id: 'user_id_list_1', - label: 'userlistone' - } - }, - { - type: 'text', - text: ' ❤️ in a list' - } - ] - }, - { - type: 'listItem', - content: [ - { - type: 'bulletList', - content: [ - { - type: 'listItem', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'mention', - attrs: { - id: 'user_id_nested_list', - label: 'usernestedlist' - } - }, - { - type: 'text', - text: ' Nested mention ❤️' - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - }) - - it('returns correct mention user IDs for emoji ❤️', () => { - const emoji = '❤️' - const result = getKudosUserIdsFromJson(doc, emoji) - expect(result).toEqual([ - 'user_id_1', - 'user_id_2', - 'user_id_3', - 'user_id_4', - 'user_id_5', - 'user_id_list_1', - 'user_id_nested_list' - ]) - }) - - it('returns correct mention user IDs for different emoji (🌮)', () => { - const emoji = '🌮' - const result = getKudosUserIdsFromJson(doc, emoji) - expect(result).toEqual(['user_id_supermention']) - }) - - it('returns an empty array for an emoji with no mentions (🔥)', () => { - const emoji = '🔥' - const result = getKudosUserIdsFromJson(doc, emoji) - expect(result).toEqual([]) - }) - - it('does not include duplicate IDs', () => { - const emoji = '❤️' - const result = getKudosUserIdsFromJson(doc, emoji) - const uniqueResult = Array.from(new Set(result)) - expect(result).toEqual(uniqueResult) - }) -}) diff --git a/packages/server/graphql/public/mutations/helpers/getKudosUserIdsFromJson.ts b/packages/server/graphql/public/mutations/helpers/getKudosUserIdsFromJson.ts deleted file mode 100644 index bfcedc7c256..00000000000 --- a/packages/server/graphql/public/mutations/helpers/getKudosUserIdsFromJson.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {JSONContent} from '@tiptap/core' - -export const getKudosUserIdsFromJson = (doc: JSONContent, emoji: string): string[] => { - const mentionedIds = new Set() - - const searchForMentionsAndEmojis = (node: JSONContent | undefined) => { - if (!node || !node.content) return - - node.content.forEach((contentNode) => { - if (contentNode.type === 'paragraph') { - const tempMentions: string[] = [] - let emojiFound = false - - contentNode.content?.forEach((item) => { - if (item.type === 'text' && item.text?.includes(emoji)) { - emojiFound = true - } - if (item.type === 'mention') { - tempMentions.push(item.attrs?.id) - } - }) - - if (emojiFound) { - tempMentions.forEach((id) => mentionedIds.add(id)) - } - } else if (contentNode.content) { - searchForMentionsAndEmojis(contentNode) - } - }) - } - - searchForMentionsAndEmojis(doc) - - return Array.from(mentionedIds) -} diff --git a/packages/server/graphql/public/mutations/helpers/publishTeamPromptMentions.ts b/packages/server/graphql/public/mutations/helpers/publishTeamPromptMentions.ts index b48fd4e37e7..e9c9805d0d8 100644 --- a/packages/server/graphql/public/mutations/helpers/publishTeamPromptMentions.ts +++ b/packages/server/graphql/public/mutations/helpers/publishTeamPromptMentions.ts @@ -17,30 +17,17 @@ const getMentionedUserIdsFromContent = (content: JSONContent): string[] => { const createTeamPromptMentionNotifications = async ( oldResponse: TeamPromptResponse | undefined, - newResponse: TeamPromptResponse, - addedKudoses: - | { - id: number - emoji: string | null - emojiUnicode: string | null - receiverUserId: string - }[] - | null + newResponse: TeamPromptResponse ) => { // Get mentions from previous and new content. const newResponseMentions = getMentionedUserIdsFromContent(newResponse.content) const oldResponseMentions = oldResponse ? getMentionedUserIdsFromContent(oldResponse.content) : [] - const addedKudosesUserIds = addedKudoses?.map((kudos) => kudos.receiverUserId) ?? [] - // Create notifications that should be added. const addedMentions = Array.from( new Set( newResponseMentions.filter( - (mention) => - (!oldResponseMentions.includes(mention) && newResponse.userId !== mention) || - // Send mention notification anyway in case it is also include kudos - addedKudosesUserIds.includes(mention) + (mention) => !oldResponseMentions.includes(mention) && newResponse.userId !== mention ) ) ) @@ -50,13 +37,10 @@ const createTeamPromptMentionNotifications = async ( } const notificationsToAdd = addedMentions.map((mention) => { - const kudos = addedKudoses?.find((kudos) => kudos.receiverUserId === mention) return new NotificationResponseMentioned({ userId: mention, responseId: newResponse.id, - meetingId: newResponse.meetingId, - kudosEmoji: kudos?.emoji, - kudosEmojiUnicode: kudos?.emojiUnicode + meetingId: newResponse.meetingId }) }) diff --git a/packages/server/graphql/public/mutations/upsertTeamPromptResponse.ts b/packages/server/graphql/public/mutations/upsertTeamPromptResponse.ts index 131fe934498..e59e43c53fe 100644 --- a/packages/server/graphql/public/mutations/upsertTeamPromptResponse.ts +++ b/packages/server/graphql/public/mutations/upsertTeamPromptResponse.ts @@ -2,7 +2,6 @@ import {generateText, JSONContent} from '@tiptap/core' import {createEditorExtensions} from 'parabol-client/components/promptResponse/tiptapConfig' import TeamPromptResponseId from 'parabol-client/shared/gqlIds/TeamPromptResponseId' import {SubscriptionChannel} from 'parabol-client/types/constEnums' -import getKysely from '../../../postgres/getKysely' import {TeamPromptResponse} from '../../../postgres/queries/getTeamPromptResponsesByIds' import {upsertTeamPromptResponse as upsertTeamPromptResponseQuery} from '../../../postgres/queries/upsertTeamPromptResponses' import {analytics} from '../../../utils/analytics/analytics' @@ -11,7 +10,6 @@ import publish from '../../../utils/publish' import standardError from '../../../utils/standardError' import {IntegrationNotifier} from '../../mutations/helpers/notifications/IntegrationNotifier' import {MutationResolvers} from '../resolverTypes' -import {getKudosUserIdsFromJson} from './helpers/getKudosUserIdsFromJson' import publishNotification from './helpers/publishNotification' import createTeamPromptMentionNotifications from './helpers/publishTeamPromptMentions' @@ -20,7 +18,6 @@ const upsertTeamPromptResponse: MutationResolvers['upsertTeamPromptResponse'] = {teamPromptResponseId: inputTeamPromptResponseId, meetingId, content}, {authToken, dataLoader, socketId: mutatorId} ) => { - const pg = getKysely() const viewerId = getUserId(authToken) const operationId = dataLoader.share() const subOptions = {mutatorId, operationId} @@ -81,47 +78,6 @@ const upsertTeamPromptResponse: MutationResolvers['upsertTeamPromptResponse'] = }) ) - const team = await dataLoader.get('teams').loadNonNull(teamId) - const {kudosEmoji, kudosEmojiUnicode} = team - - let insertedKudoses: - | { - id: number - receiverUserId: string - emoji: string | null - emojiUnicode: string - }[] - | null = null - if (team.giveKudosWithEmoji && kudosEmojiUnicode) { - const oldKudosUserIds = oldTeamPromptResponse - ? getKudosUserIdsFromJson(oldTeamPromptResponse.content, kudosEmojiUnicode) - : [] - const newKudosUserIds = getKudosUserIdsFromJson(contentJSON, kudosEmojiUnicode) - const kudosUserIds = newKudosUserIds.filter( - (userId) => !oldKudosUserIds.includes(userId) && userId !== viewerId - ) - if (kudosUserIds.length) { - const kudosRows = kudosUserIds.map((userId) => ({ - senderUserId: viewerId, - receiverUserId: userId, - teamId, - emoji: kudosEmoji, - emojiUnicode: kudosEmojiUnicode, - teamPromptResponseId: TeamPromptResponseId.split(teamPromptResponseId) - })) - - insertedKudoses = await pg - .insertInto('Kudos') - .values(kudosRows) - .returning(['id', 'receiverUserId', 'emoji', 'emojiUnicode']) - .execute() - - insertedKudoses.forEach((kudos) => { - analytics.kudosSent(user, teamId, kudos.id, kudos.receiverUserId, 'mention', 'teamPrompt') - }) - } - } - dataLoader.get('teamPromptResponses').clear(teamPromptResponseId) const newTeamPromptResponse = await dataLoader @@ -130,15 +86,13 @@ const upsertTeamPromptResponse: MutationResolvers['upsertTeamPromptResponse'] = const notifications = await createTeamPromptMentionNotifications( oldTeamPromptResponse, - newTeamPromptResponse, - insertedKudoses + newTeamPromptResponse ) const data = { meetingId, teamPromptResponseId, - addedNotificationIds: notifications.map((notification) => notification.id), - addedKudosesIds: insertedKudoses?.map((row) => row.id) + addedNotificationIds: notifications.map((notification) => notification.id) } notifications.forEach((notification) => { diff --git a/packages/server/graphql/public/permissions.ts b/packages/server/graphql/public/permissions.ts index edf2d4ccce6..c74d9bc023c 100644 --- a/packages/server/graphql/public/permissions.ts +++ b/packages/server/graphql/public/permissions.ts @@ -51,10 +51,16 @@ const permissionMap: PermissionMap = { verifyEmail: rateLimit({perMinute: 50, perHour: 100}), addApprovedOrganizationDomains: or( isSuperUser, - and(isViewerBillingLeader('args.orgId'), isOrgTier('args.orgId', 'enterprise')) + and( + isViewerBillingLeader<'Mutation.addApprovedOrganizationDomains'>('args.orgId'), + isOrgTier<'Mutation.addApprovedOrganizationDomains'>('args.orgId', 'enterprise') + ) ), - removeApprovedOrganizationDomains: or(isSuperUser, isViewerBillingLeader('args.orgId')), - uploadIdPMetadata: isViewerOnOrg('args.orgId'), + removeApprovedOrganizationDomains: or( + isSuperUser, + isViewerBillingLeader<'Mutation.removeApprovedOrganizationDomains'>('args.orgId') + ), + uploadIdPMetadata: isViewerOnOrg<'Mutation.uploadIdPMetadata'>('args.orgId'), updateTemplateCategory: isViewerOnTeam(getTeamIdFromArgTemplateId) }, Query: { @@ -63,7 +69,10 @@ const permissionMap: PermissionMap = { SAMLIdP: rateLimit({perMinute: 120, perHour: 3600}) }, Organization: { - saml: and(isViewerBillingLeader('source.id'), isOrgTier('source.id', 'enterprise')) + saml: and( + isViewerBillingLeader<'Organization.saml'>('source.id'), + isOrgTier<'Organization.saml'>('source.id', 'enterprise') + ) }, User: { domains: or(isSuperUser, isUserViewer) diff --git a/packages/server/graphql/public/rules/getResolverDotPath.ts b/packages/server/graphql/public/rules/getResolverDotPath.ts index cac815882a0..05e9098ff08 100644 --- a/packages/server/graphql/public/rules/getResolverDotPath.ts +++ b/packages/server/graphql/public/rules/getResolverDotPath.ts @@ -1,9 +1,39 @@ +import {FirstParam} from '../../../../client/types/generics' +import {Resolvers} from '../resolverTypes' + export const getResolverDotPath = ( - dotPath: ResolverDotPath, + dotPath: `${'source' | 'args'}.${string}`, source: Record, args: Record ) => { return dotPath.split('.').reduce((val: any, key) => val?.[key], {source, args}) } -export type ResolverDotPath = `source.${string}` | `args.${string}` +type SecondParam = T extends (arg1: any, arg2: infer A, ...args: any[]) => any ? A : never + +type ParseParent = T extends `${infer Parent extends string}.${string}` ? Parent : never +type ParseChild = T extends `${string}.${infer Child extends string}` ? Child : never + +type ExtractTypeof = '__isTypeOf' extends keyof NonNullable + ? NonNullable['__isTypeOf'] + : never +type ExtractParent = FirstParam>> + +type Source = + ParseParent extends keyof Resolvers + ? ExtractParent> extends never + ? never + : keyof ExtractParent> & string + : never + +type ExtractChild = TChild extends keyof TOp + ? NonNullable + : never + +type Arg = + ParseParent extends keyof Resolvers + ? keyof SecondParam]>, ParseChild>> & + string + : never + +export type ResolverDotPath = `source.${Source}` | `args.${Arg}` diff --git a/packages/server/graphql/public/rules/isOrgTier.ts b/packages/server/graphql/public/rules/isOrgTier.ts index 06e10388dac..7fb5a80eb6e 100644 --- a/packages/server/graphql/public/rules/isOrgTier.ts +++ b/packages/server/graphql/public/rules/isOrgTier.ts @@ -3,7 +3,7 @@ import {GQLContext} from '../../graphql' import {TierEnum} from '../resolverTypes' import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' -export const isOrgTier = (orgIdDotPath: ResolverDotPath, requiredTier: TierEnum) => +export const isOrgTier = (orgIdDotPath: ResolverDotPath, requiredTier: TierEnum) => rule(`isViewerOnOrg-${orgIdDotPath}-${requiredTier}`, {cache: 'strict'})( async (source, args, {dataLoader}: GQLContext) => { const orgId = getResolverDotPath(orgIdDotPath, source, args) diff --git a/packages/server/graphql/public/rules/isViewerBillingLeader.ts b/packages/server/graphql/public/rules/isViewerBillingLeader.ts index 28708113671..99391ad1baf 100644 --- a/packages/server/graphql/public/rules/isViewerBillingLeader.ts +++ b/packages/server/graphql/public/rules/isViewerBillingLeader.ts @@ -3,7 +3,7 @@ import {getUserId} from '../../../utils/authorization' import {GQLContext} from '../../graphql' import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' -export const isViewerBillingLeader = (orgIdDotPath: ResolverDotPath) => +export const isViewerBillingLeader = (orgIdDotPath: ResolverDotPath) => rule(`isViewerBillingLeader-${orgIdDotPath}`, {cache: 'strict'})( async (source, args, {authToken, dataLoader}: GQLContext) => { const orgId = getResolverDotPath(orgIdDotPath, source, args) diff --git a/packages/server/graphql/public/rules/isViewerOnOrg.ts b/packages/server/graphql/public/rules/isViewerOnOrg.ts index 01b0cbab18f..15532d23a86 100644 --- a/packages/server/graphql/public/rules/isViewerOnOrg.ts +++ b/packages/server/graphql/public/rules/isViewerOnOrg.ts @@ -3,7 +3,7 @@ import {getUserId} from '../../../utils/authorization' import {GQLContext} from '../../graphql' import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' -export const isViewerOnOrg = (orgIdDotPath: ResolverDotPath) => +export const isViewerOnOrg = (orgIdDotPath: ResolverDotPath) => rule(`isViewerOnOrg-${orgIdDotPath}`, {cache: 'strict'})( async (source, args, {authToken, dataLoader}: GQLContext) => { const orgId = getResolverDotPath(orgIdDotPath, source, args) diff --git a/packages/server/graphql/public/typeDefs/AddReactjiToReactablePayload.graphql b/packages/server/graphql/public/typeDefs/AddReactjiToReactablePayload.graphql index 6d2d27cae4b..3c0c3daca4f 100644 --- a/packages/server/graphql/public/typeDefs/AddReactjiToReactablePayload.graphql +++ b/packages/server/graphql/public/typeDefs/AddReactjiToReactablePayload.graphql @@ -1,6 +1,5 @@ type AddReactjiToReactableSuccess { reactable: Reactable! - addedKudos: Kudos } union AddReactjiToReactablePayload = AddReactjiToReactableSuccess | ErrorPayload diff --git a/packages/server/graphql/public/typeDefs/Kudos.graphql b/packages/server/graphql/public/typeDefs/Kudos.graphql deleted file mode 100644 index 946d7f5e6ba..00000000000 --- a/packages/server/graphql/public/typeDefs/Kudos.graphql +++ /dev/null @@ -1,30 +0,0 @@ -""" -A Kudos -""" -type Kudos { - """ - Id - """ - id: Int! - - """ - User who received kudos - """ - receiverUser: User! - - """ - Use who sent kudos - Can be null if kudos is anonymous - """ - senderUser: User - - """ - emoji name - """ - emoji: String! - - """ - emoji unicode character - """ - emojiUnicode: String! -} diff --git a/packages/server/graphql/public/typeDefs/NotifyKudosReceived.graphql b/packages/server/graphql/public/typeDefs/NotifyKudosReceived.graphql deleted file mode 100644 index 57fe098d3a4..00000000000 --- a/packages/server/graphql/public/typeDefs/NotifyKudosReceived.graphql +++ /dev/null @@ -1,52 +0,0 @@ -type NotifyKudosReceived implements Notification { - """ - A shortid for the notification - """ - id: ID! - - """ - UNREAD if new, READ if viewer has seen it, CLICKED if viewed clicked it - """ - status: NotificationStatusEnum! - - """ - The datetime to activate the notification & send it to the client - """ - createdAt: DateTime! - type: NotificationEnum! - - """ - The userId that should see this notification - """ - userId: ID! - - """ - Sender name - """ - name: String - - """ - Sender picture - """ - picture: URL - - """ - Meeting name - """ - meetingName: String! - - """ - Meeting id - """ - meetingId: String! - - """ - Kudos emoji - """ - emoji: String! - - """ - Kudos emoji unicode - """ - emojiUnicode: String! -} diff --git a/packages/server/graphql/public/typeDefs/NotifyMentioned.graphql b/packages/server/graphql/public/typeDefs/NotifyMentioned.graphql index c64241b170b..e6fc11e5248 100644 --- a/packages/server/graphql/public/typeDefs/NotifyMentioned.graphql +++ b/packages/server/graphql/public/typeDefs/NotifyMentioned.graphql @@ -49,14 +49,4 @@ type NotifyMentioned implements Notification { Linked discussion stage number """ retroDiscussStageIdx: Int - - """ - Kudos emoji if mention includes kudos - """ - kudosEmoji: String - - """ - Kudos emoji unicode character - """ - kudosEmojiUnicode: String } diff --git a/packages/server/graphql/public/typeDefs/NotifyResponseMentioned.graphql b/packages/server/graphql/public/typeDefs/NotifyResponseMentioned.graphql index 6af90db36ef..cf2bb5e4d5b 100644 --- a/packages/server/graphql/public/typeDefs/NotifyResponseMentioned.graphql +++ b/packages/server/graphql/public/typeDefs/NotifyResponseMentioned.graphql @@ -39,14 +39,4 @@ type NotifyResponseMentioned implements Notification { The meeting the user was mentioned in. """ meeting: TeamPromptMeeting! - - """ - Kudos emoji if mention includes kudos - """ - kudosEmoji: String - - """ - kudos emoji unicode character - """ - kudosEmojiUnicode: String } diff --git a/packages/server/graphql/public/typeDefs/Organization.graphql b/packages/server/graphql/public/typeDefs/Organization.graphql index 5c398e3330a..eda5e8f299a 100644 --- a/packages/server/graphql/public/typeDefs/Organization.graphql +++ b/packages/server/graphql/public/typeDefs/Organization.graphql @@ -187,7 +187,6 @@ type OrganizationFeatureFlags { noTeamInsights: Boolean! singleColumnStandups: Boolean! publicTeams: Boolean! - kudos: Boolean! aiTemplate: Boolean! relatedDiscussions: Boolean! } diff --git a/packages/server/graphql/public/typeDefs/Team.graphql b/packages/server/graphql/public/typeDefs/Team.graphql index 4ca3fb23ba3..718c296f4d1 100644 --- a/packages/server/graphql/public/typeDefs/Team.graphql +++ b/packages/server/graphql/public/typeDefs/Team.graphql @@ -180,16 +180,6 @@ type Team { """ viewerTeamMember: TeamMember - """ - Enable giving kudos with emoji - """ - giveKudosWithEmoji: Boolean! - - """ - Emoji that will be used for giving kudos - """ - kudosEmoji: String! - """ The team member that is the team lead """ diff --git a/packages/server/graphql/public/typeDefs/_legacy.graphql b/packages/server/graphql/public/typeDefs/_legacy.graphql index 1f65bf27ca9..8b6eb1bc78b 100644 --- a/packages/server/graphql/public/typeDefs/_legacy.graphql +++ b/packages/server/graphql/public/typeDefs/_legacy.graphql @@ -6774,11 +6774,6 @@ type CreateReflectionPayload { The stages that were unlocked by navigating """ unlockedStages: [NewMeetingStage!] - - """ - Kudoses that might be sent by the end of the retro - """ - draftKudoses: [Kudos!] } input CreateReflectionInput { @@ -8631,6 +8626,7 @@ The scope this provider was created on by a user (excluding global scope) enum IntegrationProviderEditableScopeEnum { org team + global } """ diff --git a/packages/server/graphql/public/typeDefs/upsertTeamPromptResponse.graphql b/packages/server/graphql/public/typeDefs/upsertTeamPromptResponse.graphql index d0cbd048545..7681dc4cd46 100644 --- a/packages/server/graphql/public/typeDefs/upsertTeamPromptResponse.graphql +++ b/packages/server/graphql/public/typeDefs/upsertTeamPromptResponse.graphql @@ -32,11 +32,6 @@ type UpsertTeamPromptResponseSuccess { the updated meeting """ meeting: NewMeeting - - """ - Kudos added with the response - """ - addedKudoses: [Kudos!] } union UpsertTeamPromptResponsePayload = UpsertTeamPromptResponseSuccess | ErrorPayload diff --git a/packages/server/graphql/public/types/AddReactjiToReactableSuccess.ts b/packages/server/graphql/public/types/AddReactjiToReactableSuccess.ts index 92627987433..2bb6dfee67e 100644 --- a/packages/server/graphql/public/types/AddReactjiToReactableSuccess.ts +++ b/packages/server/graphql/public/types/AddReactjiToReactableSuccess.ts @@ -4,15 +4,10 @@ import {ReactableEnumType} from '../../types/ReactableEnum' export type AddReactjiToReactableSuccessSource = { reactableId: string reactableType: ReactableEnumType - addedKudosId?: number | null } const AddReactjiToReactableSuccess: AddReactjiToReactableSuccessResolvers = { reactable: async ({reactableId, reactableType}, _args: unknown, {dataLoader}) => { return await dataLoader.get('reactables').load({id: reactableId, type: reactableType}) - }, - addedKudos: async ({addedKudosId}, _args: unknown, {dataLoader}) => { - if (!addedKudosId) return null - return dataLoader.get('kudoses').load(addedKudosId) } } diff --git a/packages/server/graphql/public/types/Kudos.ts b/packages/server/graphql/public/types/Kudos.ts deleted file mode 100644 index 26a3a42ed64..00000000000 --- a/packages/server/graphql/public/types/Kudos.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {KudosResolvers} from '../resolverTypes' - -const Kudos: KudosResolvers = { - receiverUser: async ({receiverUserId}, _args, {dataLoader}) => { - return dataLoader.get('users').loadNonNull(receiverUserId) - }, - senderUser: async ({senderUserId, isAnonymous}, _args, {dataLoader}) => { - return isAnonymous ? null : dataLoader.get('users').loadNonNull(senderUserId) - } -} - -export default Kudos diff --git a/packages/server/graphql/public/types/NotifyKudosReceived.ts b/packages/server/graphql/public/types/NotifyKudosReceived.ts deleted file mode 100644 index 53337d84bf2..00000000000 --- a/packages/server/graphql/public/types/NotifyKudosReceived.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {NotifyKudosReceivedResolvers} from '../resolverTypes' - -const NotifyKudosReceived: NotifyKudosReceivedResolvers = { - __isTypeOf: ({type}) => type === 'KUDOS_RECEIVED', - emojiUnicode: ({emojiUnicode}) => emojiUnicode ?? '❤️', - picture: async ({picture}, _args, {dataLoader}) => { - if (!picture) return null - return dataLoader.get('fileStoreAsset').load(picture) - } -} - -export default NotifyKudosReceived diff --git a/packages/server/graphql/public/types/OrganizationFeatureFlags.ts b/packages/server/graphql/public/types/OrganizationFeatureFlags.ts index 6dce6268a4a..cf3ba1898a5 100644 --- a/packages/server/graphql/public/types/OrganizationFeatureFlags.ts +++ b/packages/server/graphql/public/types/OrganizationFeatureFlags.ts @@ -11,7 +11,6 @@ const OrganizationFeatureFlags: OrganizationFeatureFlagsResolvers = { noTeamInsights: ({noTeamInsights}) => !!noTeamInsights, publicTeams: ({publicTeams}) => !!publicTeams, singleColumnStandups: ({singleColumnStandups}) => !!singleColumnStandups, - kudos: ({kudos}) => !!kudos, aiTemplate: ({aiTemplate}) => !!aiTemplate, relatedDiscussions: ({relatedDiscussions}) => !!relatedDiscussions } diff --git a/packages/server/graphql/public/types/UpsertTeamPromptResponseSuccess.ts b/packages/server/graphql/public/types/UpsertTeamPromptResponseSuccess.ts index 425879111b9..cf51634ce03 100644 --- a/packages/server/graphql/public/types/UpsertTeamPromptResponseSuccess.ts +++ b/packages/server/graphql/public/types/UpsertTeamPromptResponseSuccess.ts @@ -1,10 +1,8 @@ -import isValid from '../../../graphql/isValid' import {UpsertTeamPromptResponseSuccessResolvers} from '../resolverTypes' export type UpsertTeamPromptResponseSuccessSource = { teamPromptResponseId: string meetingId: string - addedKudosesIds?: number[] } const UpsertTeamPromptResponseSuccess: UpsertTeamPromptResponseSuccessResolvers = { @@ -15,14 +13,6 @@ const UpsertTeamPromptResponseSuccess: UpsertTeamPromptResponseSuccessResolvers meeting: async (source, _args, {dataLoader}) => { const {meetingId} = source return dataLoader.get('newMeetings').load(meetingId) - }, - addedKudoses: async (source, _args, {dataLoader}) => { - const {addedKudosesIds} = source - if (!addedKudosesIds) { - return null - } - - return (await dataLoader.get('kudoses').loadMany(addedKudosesIds)).filter(isValid) } } diff --git a/packages/server/graphql/types/IntegrationProviderEditableScopeEnum.ts b/packages/server/graphql/types/IntegrationProviderEditableScopeEnum.ts index 8d9f1534ec7..501c1d0b0cb 100644 --- a/packages/server/graphql/types/IntegrationProviderEditableScopeEnum.ts +++ b/packages/server/graphql/types/IntegrationProviderEditableScopeEnum.ts @@ -1,13 +1,14 @@ import {GraphQLEnumType} from 'graphql' -export type TIntegrationProviderEditableScopeEnum = 'org' | 'team' +export type TIntegrationProviderEditableScopeEnum = 'org' | 'team' | 'global' const IntegrationProviderEditableScopeEnum = new GraphQLEnumType({ name: 'IntegrationProviderEditableScopeEnum', description: 'The scope this provider was created on by a user (excluding global scope)', values: { org: {}, - team: {} + team: {}, + global: {} } }) diff --git a/packages/server/graphql/types/JiraServerIntegration.ts b/packages/server/graphql/types/JiraServerIntegration.ts index 2f4f3968b74..2f10905f418 100644 --- a/packages/server/graphql/types/JiraServerIntegration.ts +++ b/packages/server/graphql/types/JiraServerIntegration.ts @@ -75,9 +75,11 @@ const JiraServerIntegration = new GraphQLObjectType<{teamId: string; userId: str const orgTeams = await dataLoader.get('teamsByOrgIds').load(orgId) const orgTeamIds = orgTeams.map(({id}) => id) - const providers = await dataLoader - .get('sharedIntegrationProviders') - .load({service: 'jiraServer', orgTeamIds, teamIds: [teamId]}) + const providers = await dataLoader.get('sharedIntegrationProviders').load({ + service: 'jiraServer', + orgTeamIds: [...orgTeamIds, 'aGhostTeam'], + teamIds: [teamId] + }) return providers } }, @@ -92,7 +94,7 @@ const JiraServerIntegration = new GraphQLObjectType<{teamId: string; userId: str }, after: { type: GraphQLString, - defaultValue: '0' + defaultValue: '-1' }, queryString: { type: GraphQLString, @@ -162,21 +164,22 @@ const JiraServerIntegration = new GraphQLObjectType<{teamId: string; userId: str const {issues} = issueRes const mappedIssues = issues.map((issue) => { - const {project, issuetype, summary, description} = issue.fields + const {project, issuetype, summary, description, updated} = issue.fields return { ...issue, userId, teamId, providerId: provider.id, issueKey: issue.key, + description: description ?? '', descriptionHTML: issue.renderedFields.description, projectId: project.id, projectKey: project.key, + projectName: project.name, issueType: issuetype.id, summary, - description, service: 'jiraServer' as const, - updatedAt: new Date() + updatedAt: new Date(updated) } }) diff --git a/packages/server/graphql/types/JiraServerIssue.ts b/packages/server/graphql/types/JiraServerIssue.ts index 6d4a583c68a..3047f751c28 100644 --- a/packages/server/graphql/types/JiraServerIssue.ts +++ b/packages/server/graphql/types/JiraServerIssue.ts @@ -3,6 +3,7 @@ import JiraServerIssueId from '~/shared/gqlIds/JiraServerIssueId' import {JiraServerIssue as JiraServerRestIssue} from '../../dataloader/jiraServerLoaders' import connectionDefinitions from '../connectionDefinitions' import {GQLContext} from '../graphql' +import GraphQLISO8601Type from './GraphQLISO8601Type' import StandardMutationError from './StandardMutationError' import TaskIntegration from './TaskIntegration' @@ -40,6 +41,9 @@ const JiraServerIssue = new GraphQLObjectType projectKey: { type: new GraphQLNonNull(GraphQLID) }, + projectName: { + type: new GraphQLNonNull(GraphQLString) + }, teamId: { type: new GraphQLNonNull(GraphQLID), description: 'The parabol teamId this issue was fetched for' @@ -84,6 +88,10 @@ const JiraServerIssue = new GraphQLObjectType .map(({name}) => name) return fieldNames } + }, + updatedAt: { + type: new GraphQLNonNull(GraphQLISO8601Type), + description: 'The timestamp the issue was last updated' } }) }) diff --git a/packages/server/graphql/types/NotificationEnum.ts b/packages/server/graphql/types/NotificationEnum.ts index ad75a8dbe9e..df78dcedcb3 100644 --- a/packages/server/graphql/types/NotificationEnum.ts +++ b/packages/server/graphql/types/NotificationEnum.ts @@ -12,7 +12,6 @@ export type NotificationEnumType = | 'TEAMS_LIMIT_REMINDER' | 'PROMPT_TO_JOIN_ORG' | 'REQUEST_TO_JOIN_ORG' - | 'KUDOS_RECEIVED' const NotificationEnum = new GraphQLEnumType({ name: 'NotificationEnum', @@ -28,8 +27,7 @@ const NotificationEnum = new GraphQLEnumType({ TEAMS_LIMIT_EXCEEDED: {}, TEAMS_LIMIT_REMINDER: {}, PROMPT_TO_JOIN_ORG: {}, - REQUEST_TO_JOIN_ORG: {}, - KUDOS_RECEIVED: {} + REQUEST_TO_JOIN_ORG: {} } }) diff --git a/packages/server/integrations/jiraServer/JiraServerRestManager.ts b/packages/server/integrations/jiraServer/JiraServerRestManager.ts index 52416b07746..36b024379a1 100644 --- a/packages/server/integrations/jiraServer/JiraServerRestManager.ts +++ b/packages/server/integrations/jiraServer/JiraServerRestManager.ts @@ -57,7 +57,9 @@ export interface JiraServerIssue { id: string key: string name: string + self: string } + updated: string } renderedFields: { description: string diff --git a/packages/server/package.json b/packages/server/package.json index 4c100b77fc6..6cd52324516 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.33.0", + "version": "7.35.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" @@ -123,7 +123,7 @@ "oauth-1.0a": "^2.2.6", "openai": "^4.24.1", "oy-vey": "^0.12.1", - "parabol-client": "7.33.0", + "parabol-client": "7.35.0", "pg": "^8.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/packages/server/postgres/migrations/1716914102795_removeKudos.ts b/packages/server/postgres/migrations/1716914102795_removeKudos.ts new file mode 100644 index 00000000000..8bbb41800b6 --- /dev/null +++ b/packages/server/postgres/migrations/1716914102795_removeKudos.ts @@ -0,0 +1,21 @@ +import {Kysely, PostgresDialect} from 'kysely' +import getPg from '../getPg' + +export async function up() { + const pg = new Kysely({ + dialect: new PostgresDialect({ + pool: getPg() + }) + }) + + await pg.schema.dropTable('Kudos').execute() + await pg.schema + .alterTable('Team') + .dropColumn('giveKudosWithEmoji') + .dropColumn('kudosEmoji') + .execute() +} + +export async function down() { + // noop +} diff --git a/packages/server/postgres/migrations/1716995191300_allowGlobalOAuth1Provider.ts b/packages/server/postgres/migrations/1716995191300_allowGlobalOAuth1Provider.ts new file mode 100644 index 00000000000..51606b8c040 --- /dev/null +++ b/packages/server/postgres/migrations/1716995191300_allowGlobalOAuth1Provider.ts @@ -0,0 +1,19 @@ +import {Client} from 'pg' +import getPgConfig from '../getPgConfig' + +export async function up() { + const client = new Client(getPgConfig()) + await client.connect() + await client.query(` + DO $$ + BEGIN + ALTER TABLE "IntegrationProvider" + DROP CONSTRAINT global_provider_must_be_oauth2; + END $$; + `) + await client.end() +} + +export async function down() { + //noop, the constraint was a leftover and served no purpose +} diff --git a/packages/server/postgres/migrations/1717083323369_removeKudosNotifications.ts b/packages/server/postgres/migrations/1717083323369_removeKudosNotifications.ts new file mode 100644 index 00000000000..2607568be0b --- /dev/null +++ b/packages/server/postgres/migrations/1717083323369_removeKudosNotifications.ts @@ -0,0 +1,16 @@ +import {r} from 'rethinkdb-ts' +import connectRethinkDB from '../../database/connectRethinkDB' + +export async function up() { + try { + await connectRethinkDB() + await r.table('Notification').filter(r.row('type').eq('KUDOS_RECEIVED')).delete().run() + await r.getPoolMaster()?.drain() + } catch (e) { + console.log(e) + } +} + +export async function down() { + // noop +} diff --git a/packages/server/postgres/queries/getKudosesByIds.ts b/packages/server/postgres/queries/getKudosesByIds.ts deleted file mode 100644 index 97418629a70..00000000000 --- a/packages/server/postgres/queries/getKudosesByIds.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getKysely from '../../postgres/getKysely' - -export const getKudosesByIds = async (kudosIds: number[] | readonly number[]) => { - const pg = getKysely() - return pg.selectFrom('Kudos').selectAll().where('id', 'in', kudosIds).execute() -} diff --git a/packages/server/utils/analytics/analytics.ts b/packages/server/utils/analytics/analytics.ts index df6d07c69ce..741e73ab6ab 100644 --- a/packages/server/utils/analytics/analytics.ts +++ b/packages/server/utils/analytics/analytics.ts @@ -165,8 +165,6 @@ export type AnalyticsEvent = | 'Reset Groups Clicked' // Conversion Tracking | 'Conversion Modal Pay Later Clicked' - // kudos - | 'Kudos Sent' | 'Icebreaker Modified' // Deprecated Events // These will be replaced with tracking plan compliant versions by the data team @@ -699,26 +697,6 @@ class Analytics { this.track(user, 'AutoJoined Team', {userId: user.id, teamId}) } - kudosSent = ( - user: AnalyticsUser, - teamId: string, - kudosId: number, - receiverUserId: string, - kudosType: 'mention' | 'reaction', - meetingType: MeetingTypeEnum, - isAnonymous = false - ) => { - this.track(user, 'Kudos Sent', { - userId: user.id, - teamId, - kudosId, - receiverUserId, - kudosType, - meetingType, - isAnonymous - }) - } - icebreakerModified = ( user: AnalyticsUser, meetingId: string,