diff --git a/packages/mattermost-plugin/components/CreateTaskModal/CreateTaskModal.tsx b/packages/mattermost-plugin/components/CreateTaskModal/CreateTaskModal.tsx index 5658c26f149..158062e5311 100644 --- a/packages/mattermost-plugin/components/CreateTaskModal/CreateTaskModal.tsx +++ b/packages/mattermost-plugin/components/CreateTaskModal/CreateTaskModal.tsx @@ -1,13 +1,17 @@ import graphql from 'babel-plugin-relay/macro' -import {useEffect, useState} from 'react' +import {Client4} from 'mattermost-redux/client' +import {useEffect, useMemo, useState} from 'react' import {useDispatch} from 'react-redux' import {useLazyLoadQuery, useMutation} from 'react-relay' -import {closeCreateTaskModal} from '../../reducers' +import {closeCreateTaskModal, openLinkTeamModal} from '../../reducers' +import {useCurrentChannel} from '../../hooks/useCurrentChannel' +import {useCurrentUser} from '../../hooks/useCurrentUser' import Select from '../Select' import SimpleSelect from '../SimpleSelect' +import {Post} from 'mattermost-redux/types/posts' import {TipTapEditor} from 'parabol-client/components/promptResponse/TipTapEditor' import useEventCallback from 'parabol-client/hooks/useEventCallback' import {convertTipTapTaskContent} from 'parabol-client/shared/tiptap/convertTipTapTaskContent' @@ -21,9 +25,13 @@ import Modal from '../Modal' const TaskStatus: TaskStatusEnum[] = ['active', 'done', 'future', 'stuck'] const CreateTaskModal = () => { + const channel = useCurrentChannel() + const mmUser = useCurrentUser() + const data = useLazyLoadQuery( graphql` - query CreateTaskModalQuery { + query CreateTaskModalQuery($channel: ID!) { + linkedTeamIds(channel: $channel) viewer { id teams { @@ -38,11 +46,17 @@ const CreateTaskModal = () => { } } `, - {} + { + channel: channel?.id ?? '' + } ) - const {viewer} = data + const {viewer, linkedTeamIds} = data const {id: userId, teams} = viewer + const linkedTeams = useMemo( + () => teams.filter(({id}) => linkedTeamIds && linkedTeamIds.includes(id)), + [teams, linkedTeamIds] + ) const [createTask, createTaskLoading] = useMutation(graphql` mutation CreateTaskModalMutation($newTask: CreateTaskInput!) { @@ -66,6 +80,7 @@ const CreateTaskModal = () => { } }, [teams, selectedTeam]) const teamId = selectedTeam?.id + const teamName = selectedTeam?.name const dispatch = useDispatch() const handleClose = () => { @@ -93,6 +108,20 @@ const CreateTaskModal = () => { } }) + if (channel) { + const message = `Task created in [${teamName}](www.example.com)` + Client4.doFetch(`${Client4.getPostsRoute()}/ephemeral`, { + method: 'post', + body: JSON.stringify({ + user_id: mmUser.id, + post: { + channel_id: channel.id, + message + } + } as Partial) + }) + } + handleClose() }) @@ -101,6 +130,23 @@ const CreateTaskModal = () => { return null } + if (linkedTeams.length === 0) { + const handleLink = () => { + dispatch(openLinkTeamModal()) + handleClose() + } + return ( + +

There are no Parabol teams linked to this channel yet.

+
+ ) + } + return ( { const data = useLazyLoadQuery( graphql` query InviteToMeetingModalQuery($channel: ID!) { - config { - parabolUrl - } linkedTeamIds(channel: $channel) viewer { teams { id - name activeMeetings { + ...useInviteToMeeting_meeting id - teamId name - meetingType } } } } `, { - channel: channel.id + channel: channel?.id ?? '' } ) - const {viewer, config, linkedTeamIds} = data - const parabolUrl = config?.parabolUrl + const {viewer, linkedTeamIds} = data const {teams} = viewer const linkedTeams = useMemo( () => viewer.teams.filter((team) => !linkedTeamIds || linkedTeamIds.includes(team.id)), @@ -65,12 +55,7 @@ const InviteToMeetingModal = () => { } }, [activeMeetings, selectedMeeting]) - const getToken = useMassInvitationToken({ - teamId: selectedMeeting?.teamId, - meetingId: selectedMeeting?.id - }) - - const currentUser = useSelector(getCurrentUser) + const invite = useInviteToMeeting(selectedMeeting) const dispatch = useDispatch() const handleClose = () => { @@ -78,52 +63,10 @@ const InviteToMeetingModal = () => { } const handleStart = async () => { - if (!selectedMeeting) { - return - } - const {name: meetingName, id: meetingId} = selectedMeeting - const token = await getToken() - if (!token) { + if (!selectedMeeting || !channel) { return } - const team = linkedTeams.find((team) => team.id === selectedMeeting.teamId)! - const teamName = team.name - const inviteUrl = `${parabolUrl}/invitation-link/${token}` - const meetingUrl = `${parabolUrl}/meet/${meetingId}` - const {username, nickname, first_name, last_name} = currentUser - const userName = nickname || username || `${first_name} ${last_name}` - const props = { - attachments: [ - { - fallback: `${userName} invited you to join the meeting ${meetingName}`, - title: `${userName} invited you to join a meeting in [Parabol](${meetingUrl})`, - color: PALETTE.GRAPE_500, - fields: [ - { - short: true, - title: 'Team', - value: teamName - }, - { - short: true, - title: 'Meeting', - value: meetingName - }, - { - short: false, - value: ` -| [Join Meeting](${inviteUrl}) | -|:--------------------:| -||` - } - ] - } - ] - } - Client4.createPost({ - channel_id: channel.id, - props - } as Partial as Post) + invite?.() handleClose() } diff --git a/packages/mattermost-plugin/components/InviteToTeamModal/InviteToTeamModal.tsx b/packages/mattermost-plugin/components/InviteToTeamModal/InviteToTeamModal.tsx index 4ad7304cee1..b42cd9b110b 100644 --- a/packages/mattermost-plugin/components/InviteToTeamModal/InviteToTeamModal.tsx +++ b/packages/mattermost-plugin/components/InviteToTeamModal/InviteToTeamModal.tsx @@ -1,19 +1,15 @@ import graphql from 'babel-plugin-relay/macro' import {useEffect, useState} from 'react' -import {useDispatch, useSelector} from 'react-redux' +import {useDispatch} from 'react-redux' import {useLazyLoadQuery} from 'react-relay' import {closeInviteToTeamModal} from '../../reducers' import Select from '../Select' -import {Client4} from 'mattermost-redux/client' -import {getCurrentUser} from 'mattermost-redux/selectors/entities/common' -import {Post} from 'mattermost-redux/types/posts' -import {PALETTE} from '~/styles/paletteV3' import {InviteToTeamModalQuery} from '../../__generated__/InviteToTeamModalQuery.graphql' import {useCurrentChannel} from '../../hooks/useCurrentChannel' -import useMassInvitationToken from '../../hooks/useMassInvitationToken' +import {useInviteToTeam} from '../../hooks/useInviteToTeam' import LoadingSpinner from '../LoadingSpinner' import Modal from '../Modal' @@ -21,11 +17,9 @@ const InviteToTeamModal = () => { const data = useLazyLoadQuery( graphql` query InviteToTeamModalQuery { - config { - parabolUrl - } viewer { teams { + ...useInviteToTeam_team id name } @@ -35,8 +29,7 @@ const InviteToTeamModal = () => { {} ) - const {viewer, config} = data - const parabolUrl = config?.parabolUrl + const {viewer} = data const {teams} = viewer const [selectedTeam, setSelectedTeam] = useState[number]>() @@ -48,9 +41,7 @@ const InviteToTeamModal = () => { } }, [teams, selectedTeam]) - const getToken = useMassInvitationToken({teamId: selectedTeam?.id}) - - const currentUser = useSelector(getCurrentUser) + const invite = useInviteToTeam(selectedTeam) const dispatch = useDispatch() const handleClose = () => { @@ -58,41 +49,10 @@ const InviteToTeamModal = () => { } const handleStart = async () => { - if (!selectedTeam) { - return - } - const {id: teamId, name: teamName} = selectedTeam - const token = await getToken() - if (!token) { + if (!selectedTeam || !channel) { return } - const inviteUrl = `${parabolUrl}/invitation-link/${token}` - const teamUrl = `${parabolUrl}/team/${teamId}` - const {username, nickname, first_name, last_name} = currentUser - const userName = nickname || username || `${first_name} ${last_name}` - const props = { - attachments: [ - { - title: `${userName} invited you to join a team in [Parabol](${teamUrl})`, - fallback: `${userName} invited you to join a team ${teamName} in Parabol`, - color: PALETTE.GRAPE_500, - fields: [ - {short: true, value: teamName}, - { - short: false, - value: ` -| [Join Team](${inviteUrl}) | -|:--------------------:| -||` - } - ] - } - ] - } - Client4.createPost({ - channel_id: channel.id, - props - } as Partial as Post) + invite() handleClose() } diff --git a/packages/mattermost-plugin/components/LinkTeamModal/LinkTeamModal.tsx b/packages/mattermost-plugin/components/LinkTeamModal/LinkTeamModal.tsx index 6a361a6535d..87de4c780f0 100644 --- a/packages/mattermost-plugin/components/LinkTeamModal/LinkTeamModal.tsx +++ b/packages/mattermost-plugin/components/LinkTeamModal/LinkTeamModal.tsx @@ -29,7 +29,7 @@ const LinkTeamModal = () => { } `, { - channel: channel.id + channel: channel?.id ?? '' } ) const {viewer, linkedTeamIds} = data @@ -58,7 +58,7 @@ const LinkTeamModal = () => { handleClose() } - if (!isVisible) { + if (!isVisible || !channel) { return null } diff --git a/packages/mattermost-plugin/components/LoadingSpinner.tsx b/packages/mattermost-plugin/components/LoadingSpinner.tsx index aad340e80a5..a19a1e51ab0 100644 --- a/packages/mattermost-plugin/components/LoadingSpinner.tsx +++ b/packages/mattermost-plugin/components/LoadingSpinner.tsx @@ -1,4 +1,3 @@ -//import classNames from 'classnames' import React from 'react' type Props = { @@ -7,12 +6,7 @@ type Props = { } const LoadingSpinner = ({text, style}: Props) => { return ( - + {text} diff --git a/packages/mattermost-plugin/components/Menu.tsx b/packages/mattermost-plugin/components/Menu.tsx index 8c1322df3e2..ea16be434aa 100644 --- a/packages/mattermost-plugin/components/Menu.tsx +++ b/packages/mattermost-plugin/components/Menu.tsx @@ -1,4 +1,4 @@ -import {DotsVerticalIcon} from '@mattermost/compass-icons/components' +import {MoreVert} from '@mui/icons-material' import {IconButton, Menu, MenuItem} from '@mui/material' import React from 'react' @@ -30,7 +30,7 @@ const MoreMenu = ({options}: Props) => { aria-haspopup='true' onClick={handleOpen} > - + { > - + {title} diff --git a/packages/mattermost-plugin/components/PushReflection/PushReflectionModal.tsx b/packages/mattermost-plugin/components/PushReflection/PushReflectionModal.tsx index b7161c97c87..ef947e858d2 100644 --- a/packages/mattermost-plugin/components/PushReflection/PushReflectionModal.tsx +++ b/packages/mattermost-plugin/components/PushReflection/PushReflectionModal.tsx @@ -1,17 +1,24 @@ -import {generateJSON, mergeAttributes} from '@tiptap/core' -import BaseLink from '@tiptap/extension-link' -import StarterKit from '@tiptap/starter-kit' -import graphql from 'babel-plugin-relay/macro' +import {Client4} from 'mattermost-redux/client' import {getPost} from 'mattermost-redux/selectors/entities/posts' import {GlobalState} from 'mattermost-redux/types/store' -import {TipTapEditor} from 'parabol-client/components/promptResponse/TipTapEditor' import React, {useEffect, useMemo} from 'react' import {useDispatch, useSelector} from 'react-redux' import {useLazyLoadQuery, useMutation} from 'react-relay' + +import {generateJSON, mergeAttributes} from '@tiptap/core' +import BaseLink from '@tiptap/extension-link' +import StarterKit from '@tiptap/starter-kit' +import graphql from 'babel-plugin-relay/macro' + +import {Post} from 'mattermost-redux/types/posts' +import {TipTapEditor} from 'parabol-client/components/promptResponse/TipTapEditor' +import {PALETTE} from 'parabol-client/styles/paletteV3' import {PushReflectionModalMutation} from '../../__generated__/PushReflectionModalMutation.graphql' import {PushReflectionModalQuery} from '../../__generated__/PushReflectionModalQuery.graphql' +import {useCurrentChannel} from '../../hooks/useCurrentChannel' +import {useCurrentUser} from '../../hooks/useCurrentUser' import {useTipTapTaskEditor} from '../../hooks/useTipTapTaskEditor' -import {closePushPostAsReflection} from '../../reducers' +import {closePushPostAsReflection, openLinkTeamModal, openStartActivityModal} from '../../reducers' import {getPostURL, pushPostAsReflection} from '../../selectors' import Modal from '../Modal' import Select from '../Select' @@ -22,10 +29,13 @@ const PushReflectionModal = () => { const postId = useSelector(pushPostAsReflection) const post = useSelector((state: GlobalState) => getPost(state, postId!)) const postUrl = useSelector((state: GlobalState) => getPostURL(state, postId!)) + const mmUser = useCurrentUser() + const channel = useCurrentChannel() const data = useLazyLoadQuery( graphql` - query PushReflectionModalQuery { + query PushReflectionModalQuery($channel: ID!) { + linkedTeamIds(channel: $channel) viewer { teams { id @@ -53,16 +63,23 @@ const PushReflectionModal = () => { } } `, - {} + { + channel: channel?.id ?? '' + } ) - const {viewer} = data + const {viewer, linkedTeamIds} = data const {teams} = viewer + + const linkedTeams = useMemo( + () => teams.filter(({id}) => linkedTeamIds && linkedTeamIds.includes(id)), + [teams, linkedTeamIds] + ) const retroMeetings = useMemo( () => - teams + linkedTeams .flatMap(({activeMeetings}) => activeMeetings) .filter(({meetingType}) => meetingType === 'retrospective'), - [teams] + [linkedTeams] ) const [selectedMeeting, setSelectedMeeting] = React.useState<(typeof retroMeetings)[number]>() const [selectedPrompt, setSelectedPrompt] = React.useState<{ @@ -139,20 +156,57 @@ const PushReflectionModal = () => { console.log('missing data', selectedPrompt, selectedMeeting, post.message) return } + const {id: meetingId, name: meetingName} = selectedMeeting + const {id: promptId, question} = selectedPrompt const content = JSON.stringify(editor.getJSON()) createReflection({ variables: { input: { - meetingId: selectedMeeting.id, - promptId: selectedPrompt.id, + meetingId, + promptId, content, sortOrder: 0 } } }) + const meetingUrl = `${window.location.origin}/meeting/${meetingId}` + const props = { + attachments: [ + { + fallback: `Reflection added to meeting ${meetingName}`, + title: `Reflection added to meeting [${meetingName}](${meetingUrl})`, + color: PALETTE.GRAPE_500, + fields: [ + { + short: true, + title: 'Meeting', + value: meetingName + }, + { + short: true, + title: 'Question', + value: question + } + ] + } + ] + } + + Client4.doFetch(`${Client4.getPostsRoute()}/ephemeral`, { + method: 'post', + body: JSON.stringify({ + user_id: mmUser.id, + post: { + channel_id: post.channel_id, + root_id: post.root_id || post.id, + props + } + } as Partial) + }) + handleClose() } @@ -165,6 +219,39 @@ const PushReflectionModal = () => { return null } + if (linkedTeams.length === 0) { + const handleLink = () => { + dispatch(openLinkTeamModal()) + handleClose() + } + return ( + +

There are no Parabol teams linked to this channel yet.

+
+ ) + } + if (retroMeetings.length === 0) { + const handleStart = () => { + dispatch(openStartActivityModal()) + handleClose() + } + return ( + +

There are currently no open retrospective meetings in the linked Parabol teams.

+
+ ) + } + return ( { handleClose={handleClose} handleCommit={handlePush} > -
-

- Choose an open Retro activity and the Prompt where you want to send the Mattermost - comment. A reference link back to Mattermost will be inlcuded in the reflection. -

-
{post && (