Skip to content

Commit

Permalink
fix: tasks in poker scoping (#10563)
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Krick <[email protected]>
  • Loading branch information
mattkrick authored Dec 10, 2024
1 parent 1cdbf0b commit d2b1ef8
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 29 deletions.
11 changes: 8 additions & 3 deletions packages/client/components/ParabolScopingSearchResultItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ const ParabolScopingSearchResultItem = (props: Props) => {
const disabled = !isSelected && usedServiceTaskIds.size >= Threshold.MAX_POKER_STORIES
const atmosphere = useAtmosphere()
const {onCompleted, onError, submitMutation, submitting} = useMutationProps()
const {editor, linkState, setLinkState} = useTipTapTaskEditor(content, {atmosphere, teamId})
const isEditingThisItem = !plaintextContent
const {editor, linkState, setLinkState} = useTipTapTaskEditor(content, {
atmosphere,
teamId,
readOnly: !isEditingThisItem
})
const {useTaskChild, addTaskChild, removeTaskChild, isTaskFocused} =
useTaskChildFocus(serviceTaskId)
const isEditingThisItem = !plaintextContent

const updatePokerScope = () => {
if (submitting || disabled) return
Expand All @@ -96,7 +100,8 @@ const ParabolScopingSearchResultItem = (props: Props) => {
const handleTaskUpdate = () => {
if (!editor) return
const isFocused = isTaskFocused()
if (editor.isEmpty && !isFocused) {
if (isFocused) return
if (editor.isEmpty) {
DeleteTaskMutation(atmosphere, {taskId: serviceTaskId})
return
}
Expand Down
8 changes: 2 additions & 6 deletions packages/client/components/ParabolScopingSearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import {useEffect, useState} from 'react'
import {PreloadedQuery, useFragment, usePaginationFragment, usePreloadedQuery} from 'react-relay'
Expand All @@ -17,9 +16,6 @@ import NewIntegrationRecordButton from './NewIntegrationRecordButton'
import ParabolScopingSearchResultItem from './ParabolScopingSearchResultItem'
import ParabolScopingSelectAllTasks from './ParabolScopingSelectAllTasks'

const ResultScroller = styled('div')({
overflow: 'auto'
})
interface Props {
queryRef: PreloadedQuery<ParabolScopingSearchResultsQuery>
meetingRef: ParabolScopingSearchResults_meeting$key
Expand Down Expand Up @@ -145,7 +141,7 @@ const ParabolScopingSearchResults = (props: Props) => {
tasks={edges}
meetingId={meetingId}
/>
<ResultScroller>
<div className='overflow-auto'>
{edges.map(({node}) => {
return (
<ParabolScopingSearchResultItem
Expand All @@ -159,7 +155,7 @@ const ParabolScopingSearchResults = (props: Props) => {
)
})}
{lastItem}
</ResultScroller>
</div>
{!isEditing && (
<NewIntegrationRecordButton labelText={'New Task'} onClick={handleAddTaskClick} />
)}
Expand Down
2 changes: 1 addition & 1 deletion packages/client/components/promptResponse/TipTapEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
export const TipTapEditor = (props: Props) => {
const {editor, linkState, setLinkState, showBubbleMenu, useLinkEditor} = props
return (
<div className=' cursor-text px-4 text-sm leading-5'>
<div className='px-4 text-sm leading-5'>
{showBubbleMenu && setLinkState && (
<StandardBubbleMenu editor={editor} setLinkState={setLinkState} />
)}
Expand Down
20 changes: 15 additions & 5 deletions packages/client/components/promptResponse/TipTapLinkMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ export const TipTapLinkMenu = (props: Props) => {
return {link: '', text}
}
})

const handleEdit = useCallback(() => {
const oldLinkStateRef = useRef<LinkMenuState>(null)
const handleEdit = () => {
setLinkState('edit')
}, [])
oldLinkStateRef.current = 'preview'
}

const onSetLink = useCallback(
({text, url}: {text: string; url: string}) => {
Expand Down Expand Up @@ -104,7 +105,16 @@ export const TipTapLinkMenu = (props: Props) => {
setLinkState(null)
}, [editor])
const onOpenChange = (willOpen: boolean) => {
setLinkState(willOpen ? (editor.isActive('link') ? 'preview' : 'edit') : null)
const isLinkActive = editor.isActive('link')
if (willOpen) {
setLinkState(isLinkActive ? 'preview' : 'edit')
} else {
// special case when switching from preview to edit radix-ui triggers onOpenChange(false)
if (!(oldLinkStateRef.current === 'preview' && linkState === 'edit')) {
setLinkState(null)
}
oldLinkStateRef.current = null
}
}
const transformRef = useRef<undefined | string>(undefined)
const getTransform = () => {
Expand All @@ -122,7 +132,7 @@ export const TipTapLinkMenu = (props: Props) => {
<Popover.Portal>
<Popover.Content
onOpenAutoFocus={(e) => {
// necessary for link preview to preview focusing the first button
// necessary for link preview to prevent focusing the first button
e.preventDefault()
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {LinkMenuState} from '../../../../components/promptResponse/TipTapLinkMen
import TaskIntegrationLink from '../../../../components/TaskIntegrationLink'
import TaskWatermark from '../../../../components/TaskWatermark'
import useAtmosphere from '../../../../hooks/useAtmosphere'
import useTaskChildFocus, {UseTaskChild} from '../../../../hooks/useTaskChildFocus'
import {UseTaskChild} from '../../../../hooks/useTaskChildFocus'
import UpdateTaskMutation from '../../../../mutations/UpdateTaskMutation'
import {cardFocusShadow, cardHoverShadow, cardShadow, Elevation} from '../../../../styles/elevation'
import cardRootStyles from '../../../../styles/helpers/cardRootStyles'
Expand Down Expand Up @@ -55,8 +55,6 @@ const StatusIndicatorBlock = styled('div')({
display: 'flex'
})

const TaskEditorWrapper = styled('div')()

interface Props {
area: AreaEnum
isTaskFocused: boolean
Expand All @@ -70,10 +68,14 @@ interface Props {
task: OutcomeCard_task$key
useTaskChild: UseTaskChild
dataCy: string
addTaskChild(name: string): void
removeTaskChild(name: string): void
}

const OutcomeCard = memo((props: Props) => {
const {
addTaskChild,
removeTaskChild,
area,
isTaskFocused,
isTaskHovered,
Expand Down Expand Up @@ -140,7 +142,6 @@ const OutcomeCard = memo((props: Props) => {
const {viewerId} = atmosphere
const otherEditors = editors.filter((editor) => editor.userId !== viewerId)
const isEditing = editors.length > otherEditors.length
const {addTaskChild, removeTaskChild} = useTaskChildFocus(taskId)
const type = integration?.__typename
const statusTitle = `Card status: ${taskStatusLabels[status]}`
const privateTitle = ', marked as #private'
Expand Down Expand Up @@ -171,7 +172,8 @@ const OutcomeCard = memo((props: Props) => {
</EditingStatus>
<IntegratedTaskContent task={task} />
{!type && (
<TaskEditorWrapper
<div
className='cursor-text'
onBlur={() => {
removeTaskChild('root')
setTimeout(handleCardUpdate)
Expand All @@ -182,9 +184,11 @@ const OutcomeCard = memo((props: Props) => {
editor={editor}
linkState={linkState}
setLinkState={setLinkState}
useLinkEditor={() => useTaskChild('editor-link-changer')}
useLinkEditor={() => {
useTaskChild('editor-link-changer')
}}
/>
</TaskEditorWrapper>
</div>
)}
<TaskIntegrationLink dataCy={`${dataCy}`} integration={integration || null} />
<TaskFooter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import styled from '@emotion/styled'
import {Editor} from '@tiptap/core'
import graphql from 'babel-plugin-relay/macro'
import areEqual from 'fbjs/lib/areEqual'
import {memo, useEffect, useRef, useState} from 'react'
import {useFragment} from 'react-relay'
import {OutcomeCardContainer_task$key} from '~/__generated__/OutcomeCardContainer_task.graphql'
Expand Down Expand Up @@ -66,7 +67,7 @@ const OutcomeCardContainer = memo((props: Props) => {
const ref = useRef<HTMLDivElement>(null)
const [isTaskHovered, setIsTaskHovered] = useState(false)

const {useTaskChild, isTaskFocused} = useTaskChildFocus(taskId)
const {useTaskChild, isTaskFocused, addTaskChild, removeTaskChild} = useTaskChildFocus(taskId)

const isHighlighted = isTaskHovered || !!isDraggingOver
useEffect(() => {
Expand All @@ -81,12 +82,13 @@ const OutcomeCardContainer = memo((props: Props) => {

const handleCardUpdate = () => {
const isFocused = isTaskFocused()
if (isFocused) return
if (editor.isEmpty && !isFocused) {
DeleteTaskMutation(atmosphere, {taskId})
return
}
const nextContent = JSON.stringify(editor.getJSON())
if (content === nextContent) return
if (areEqual(JSON.parse(content), editor.getJSON())) return
const updatedTask = {
id: taskId,
content: nextContent
Expand Down Expand Up @@ -118,6 +120,8 @@ const OutcomeCardContainer = memo((props: Props) => {
isDraggingOver={isDraggingOver}
task={task}
useTaskChild={useTaskChild}
addTaskChild={addTaskChild}
removeTaskChild={removeTaskChild}
/>
</Wrapper>
)
Expand Down
12 changes: 7 additions & 5 deletions packages/client/mutations/handlers/handleUpsertTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import isTaskPrivate from '~/utils/isTaskPrivate'
import {parseQueryParams} from '~/utils/useQueryParameterParser'
import addNodeToArray from '../../utils/relay/addNodeToArray'
import safeRemoveNodeFromConn from '../../utils/relay/safeRemoveNodeFromConn'
import safeRemoveNodeFromUnknownConn from '../../utils/relay/safeRemoveNodeFromUnknownConn'
import getArchivedTasksConn from '../connections/getArchivedTasksConn'
import getScopingTasksConn from '../connections/getScopingTasksConn'
import getTeamTasksConn from '../connections/getTeamTasksConn'
Expand Down Expand Up @@ -53,12 +54,18 @@ const handleUpsertTask = (task: Task | null, store: RecordSourceSelectorProxy<an
if (isNowArchived) {
safeRemoveNodeFromConn(taskId, teamConn)
safeRemoveNodeFromConn(taskId, userConn)
safeRemoveNodeFromUnknownConn(store, viewerId, 'ParabolScopingSearchResults_tasks', taskId)
archiveConns.forEach((archiveConn) => safePutNodeInConn(archiveConn, task, store))
} else {
archiveConns.forEach((archiveConn) => safeRemoveNodeFromConn(taskId, archiveConn))
safePutNodeInConn(teamConn, task, store)
safePutNodeInConn(threadConn, task, store, 'threadSortOrder', true)
addNodeToArray(task, meeting, 'tasks', 'createdAt')
/* updates parabol search query if task is created from a sprint poker meeting
* should also implement updating parabol search query if task is created elsewhere?
*/
const scopingTasksConn = getScopingTasksConn(store, meetingId, viewer, [teamId])
safePutNodeInConn(scopingTasksConn, task, store, 'updatedAt', false)
if (userConn) {
const isPrivate = isTaskPrivate(tags)
const ownedByViewer = task.getValue('userId') === viewerId
Expand All @@ -69,11 +76,6 @@ const handleUpsertTask = (task: Task | null, store: RecordSourceSelectorProxy<an
}
}
}
/* updates parabol search query if task is created from a sprint poker meeting
* should also implement updating parabol search query if task is created elsewhere?
*/
const scopingTasksConn = getScopingTasksConn(store, meetingId, viewer, [teamId])
safePutNodeInConn(scopingTasksConn, task, store, 'updatedAt', false)
}

const handleUpsertTasks = pluralizeHandler(handleUpsertTask)
Expand Down

0 comments on commit d2b1ef8

Please sign in to comment.