Skip to content

Commit

Permalink
Highlight target scene of comment (#4597)
Browse files Browse the repository at this point in the history
* Highlight target scene of comment

* Fix review comments
  • Loading branch information
gbalint authored Dec 4, 2023
1 parent be08183 commit c5bef28
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 51 deletions.
41 changes: 17 additions & 24 deletions editor/src/components/canvas/controls/comment-indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ import {
multiplayerColorFromIndex,
multiplayerInitialsFromName,
normalizeMultiplayerName,
openCommentThreadActions,
} from '../../../core/shared/multiplayer'
import { MultiplayerWrapper } from '../../../utils/multiplayer-wrapper'
import { UtopiaStyles } from '../../../uuiui'
import { switchEditorMode } from '../../editor/actions/action-creators'
import { setHighlightedView, switchEditorMode } from '../../editor/actions/action-creators'
import { EditorModes, existingComment } from '../../editor/editor-modes'
import { useDispatch } from '../../editor/store/dispatch-context'
import { Substores, useEditorState, useRefEditorState } from '../../editor/store/store-hook'
import { AvatarPicture } from '../../user-bar'
import { canvasPointToWindowPoint, windowToCanvasCoordinates } from '../dom-lookup'
import { RemixNavigationAtom } from '../remix/utopia-remix-root-component'
import { stripNulls } from '../../../core/shared/array-utils'

const IndicatorSize = 20

Expand Down Expand Up @@ -89,7 +91,7 @@ const CommentIndicator = React.memo(({ thread }: CommentIndicatorProps) => {

const [remixNavigationState] = useAtom(RemixNavigationAtom)

const canvasLocation = useCanvasLocationOfThread(thread)
const { location, scene: commentScene } = useCanvasLocationOfThread(thread)

const remixLocationRoute = thread.metadata.remixLocationRoute ?? null

Expand All @@ -106,10 +108,16 @@ const CommentIndicator = React.memo(({ thread }: CommentIndicatorProps) => {
remixState.navigate(remixLocationRoute)
})
}
dispatch([
switchEditorMode(EditorModes.commentMode(existingComment(thread.id), 'not-dragging')),
])
}, [dispatch, thread.id, remixNavigationState, remixLocationRoute, isOnAnotherRoute])

dispatch(openCommentThreadActions(thread.id, commentScene))
}, [
dispatch,
thread.id,
remixNavigationState,
remixLocationRoute,
isOnAnotherRoute,
commentScene,
])

const { initials, color, avatar } = (() => {
const firstComment = thread.comments[0]
Expand All @@ -127,10 +135,10 @@ const CommentIndicator = React.memo(({ thread }: CommentIndicatorProps) => {
}
})()

const { onMouseDown, dragPosition } = useDragging(thread, canvasLocation)
const { onMouseDown, dragPosition } = useDragging(thread, location)
const position = React.useMemo(
() => canvasPointToWindowPoint(dragPosition ?? canvasLocation, canvasScale, canvasOffset),
[canvasLocation, canvasScale, canvasOffset, dragPosition],
() => canvasPointToWindowPoint(dragPosition ?? location, canvasScale, canvasOffset),
[location, canvasScale, canvasOffset, dragPosition],
)

return (
Expand Down Expand Up @@ -247,18 +255,3 @@ function useDragging(thread: ThreadData<ThreadMetadata>, originalLocation: Canva

return { onMouseDown, dragPosition }
}

function mousePositionToIndicatorPosition(
canvasScale: number,
canvasOffset: CanvasVector,
windowPosition: WindowPoint,
): CanvasPoint {
return offsetPoint(
windowToCanvasCoordinates(
canvasScale,
canvasOffset,
windowPoint({ x: windowPosition.x, y: windowPosition.y }),
).canvasPositionRounded,
canvasPoint({ x: -IndicatorSize / 2, y: -IndicatorSize / 2 }),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import type { MouseCallbacks } from '../select-mode/select-mode-hooks'
import { NO_OP } from '../../../../core/shared/utils'
import { useKeepShallowReferenceEquality } from '../../../../utils/react-performance'
import { useDispatch } from '../../../editor/store/dispatch-context'
import { switchEditorMode } from '../../../editor/actions/action-creators'
import {
clearHighlightedViews,
setHighlightedView,
switchEditorMode,
} from '../../../editor/actions/action-creators'
import type { CommentId } from '../../../editor/editor-modes'
import {
EditorModes,
Expand All @@ -13,6 +17,7 @@ import {
} from '../../../editor/editor-modes'
import { useRefEditorState } from '../../../editor/store/store-hook'
import { windowToCanvasCoordinates } from '../../dom-lookup'
import type { CanvasPoint } from '../../../../core/shared/math-utils'
import {
getLocalPointInNewParentContext,
isNotNullFiniteRectangle,
Expand Down Expand Up @@ -43,6 +48,26 @@ export function useCommentModeSelectAndHover(comment: CommentId | null): MouseCa
}
})

const onMouseMove = React.useCallback(
(event: React.MouseEvent) => {
if (comment == null) {
const loc = windowToCanvasCoordinates(
storeRef.current.scale,
storeRef.current.canvasOffset,
windowPoint({ x: event.clientX, y: event.clientY }),
)
const scene = getSceneWithIdUnderPoint(loc.canvasPositionRaw, scenes)

if (scene == null) {
dispatch([clearHighlightedViews()])
} else {
dispatch([setHighlightedView(scene.elementPath)])
}
}
},
[dispatch, comment, storeRef, scenes],
)

const onMouseUp = React.useCallback(
(event: React.MouseEvent) => {
if (comment == null) {
Expand All @@ -52,15 +77,7 @@ export function useCommentModeSelectAndHover(comment: CommentId | null): MouseCa
windowPoint({ x: event.clientX, y: event.clientY }),
)

const scenesUnderTheMouse = scenes.filter((scene) => {
const sceneId = getIdOfScene(scene)
return (
sceneId != null &&
isNotNullFiniteRectangle(scene.globalFrame) &&
rectContainsPoint(scene.globalFrame, loc.canvasPositionRaw)
)
})
const scene = safeIndex(scenesUnderTheMouse, 0) // TODO: choose the topmost one in z-order
const scene = getSceneWithIdUnderPoint(loc.canvasPositionRaw, scenes)
const sceneId = optionalMap(getIdOfScene, scene)

const offset =
Expand All @@ -81,6 +98,7 @@ export function useCommentModeSelectAndHover(comment: CommentId | null): MouseCa
}

dispatch([
setHighlightedView(scene.elementPath),
switchEditorMode(
EditorModes.commentMode(
newComment(sceneCommentLocation(sceneId, offset)),
Expand All @@ -96,12 +114,27 @@ export function useCommentModeSelectAndHover(comment: CommentId | null): MouseCa
)

return useKeepShallowReferenceEquality({
onMouseMove: NO_OP,
onMouseMove: onMouseMove,
onMouseDown: NO_OP,
onMouseUp: onMouseUp,
})
}

function getSceneWithIdUnderPoint(
location: CanvasPoint,
scenes: ElementInstanceMetadata[],
): ElementInstanceMetadata | null {
const scenesUnderTheMouse = scenes.filter((scene) => {
const sceneId = getIdOfScene(scene)
return (
sceneId != null &&
isNotNullFiniteRectangle(scene.globalFrame) &&
rectContainsPoint(scene.globalFrame, location)
)
})
return safeIndex(scenesUnderTheMouse, 0) ?? null // TODO: choose the topmost one in z-order
}

export function getIdOfScene(scene: ElementInstanceMetadata): string | null {
const sceneElement = scene.element
if (isLeft(sceneElement) || !isJSXElement(sceneElement.value)) {
Expand Down
28 changes: 17 additions & 11 deletions editor/src/components/inspector/sections/comment-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,13 @@ import { stopPropagation } from '../common/inspector-utils'
import type { ThreadMetadata } from '../../../../liveblocks.config'
import type { ThreadData } from '@liveblocks/client'
import { useDispatch } from '../../editor/store/dispatch-context'
import { canvasPoint, canvasRectangle } from '../../../core/shared/math-utils'
import { canvasRectangle } from '../../../core/shared/math-utils'
import {
scrollToPosition,
setShowResolvedThreads,
switchEditorMode,
} from '../../editor/actions/action-creators'
import {
EditorModes,
existingComment,
isCommentMode,
isExistingComment,
} from '../../editor/editor-modes'
import { EditorModes, isCommentMode, isExistingComment } from '../../editor/editor-modes'
import { MultiplayerWrapper } from '../../../utils/multiplayer-wrapper'
import { useAtom } from 'jotai'
import { RemixNavigationAtom } from '../../canvas/remix/utopia-remix-root-component'
Expand All @@ -32,9 +27,11 @@ import {
useIsOnAnotherRemixRoute,
useResolveThread,
useResolvedThreads,
useCanvasLocationOfThread,
} from '../../../core/commenting/comment-hooks'
import { Substores, useEditorState } from '../../editor/store/store-hook'
import { unless, when } from '../../../utils/react-conditionals'
import { openCommentThreadActions } from '../../../core/shared/multiplayer'

export const CommentSection = React.memo(() => {
return (
Expand Down Expand Up @@ -111,7 +108,6 @@ const ThreadPreview = React.memo(({ thread }: ThreadPreviewProps) => {
const [remixNavigationState] = useAtom(RemixNavigationAtom)

const { remixLocationRoute } = thread.metadata
const point = canvasPoint(thread.metadata)

const isOnAnotherRoute = useIsOnAnotherRemixRoute(remixLocationRoute ?? null)

Expand All @@ -125,8 +121,10 @@ const ThreadPreview = React.memo(({ thread }: ThreadPreviewProps) => {
'ThreadPreview isSelected',
)

const { location, scene: commentScene } = useCanvasLocationOfThread(thread)

const onClick = React.useCallback(() => {
const rect = canvasRectangle({ x: point.x, y: point.y, width: 25, height: 25 })
const rect = canvasRectangle({ x: location.x, y: location.y, width: 25, height: 25 })

if (isOnAnotherRoute && remixLocationRoute != null) {
// TODO: after we have scene identifier in the comment metadata we should only navigate the scene with the comment
Expand All @@ -139,10 +137,18 @@ const ThreadPreview = React.memo(({ thread }: ThreadPreviewProps) => {
})
}
dispatch([
switchEditorMode(EditorModes.commentMode(existingComment(thread.id), 'not-dragging')),
...openCommentThreadActions(thread.id, commentScene),
scrollToPosition(rect, 'to-center'),
])
}, [dispatch, remixNavigationState, isOnAnotherRoute, remixLocationRoute, point, thread.id])
}, [
dispatch,
remixNavigationState,
isOnAnotherRoute,
remixLocationRoute,
location,
thread.id,
commentScene,
])

const resolveThread = useResolveThread()

Expand Down
15 changes: 11 additions & 4 deletions editor/src/core/commenting/comment-hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '../shared/math-utils'
import { MetadataUtils } from '../model/element-metadata-utils'
import { getIdOfScene } from '../../components/canvas/controls/comment-mode/comment-mode-hooks'
import type { ElementPath } from '../shared/project-file-types'

export function useCanvasCommentThreadAndLocation(comment: CommentId): {
location: CanvasPoint | null
Expand Down Expand Up @@ -193,18 +194,24 @@ export function useScenesWithId() {
)
}

export function useCanvasLocationOfThread(thread: ThreadData<ThreadMetadata>): CanvasPoint {
export function useCanvasLocationOfThread(thread: ThreadData<ThreadMetadata>): {
location: CanvasPoint
scene: ElementPath | null
} {
const scenes = useScenesWithId()

if (thread.metadata.sceneId == null) {
return canvasPoint(thread.metadata)
return { location: canvasPoint(thread.metadata), scene: null }
}
const scene = scenes.find((s) => getIdOfScene(s) === thread.metadata.sceneId)

if (scene == null || !isNotNullFiniteRectangle(scene.globalFrame)) {
return canvasPoint(thread.metadata)
return { location: canvasPoint(thread.metadata), scene: null }
}
return {
location: getCanvasPointWithCanvasOffset(scene.globalFrame, localPoint(thread.metadata)),
scene: scene.elementPath,
}
return getCanvasPointWithCanvasOffset(scene.globalFrame, localPoint(thread.metadata))
}

export function useResolveThread() {
Expand Down
15 changes: 14 additions & 1 deletion editor/src/core/shared/multiplayer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type { User } from '@liveblocks/client'
import type { Presence, UserMeta } from '../../../liveblocks.config'
import { possiblyUniqueInArray, safeIndex, uniqBy } from './array-utils'
import { possiblyUniqueInArray, safeIndex, stripNulls, uniqBy } from './array-utils'
import { getPreferredColorScheme } from '../../uuiui'
import type { ElementPath } from './project-file-types'
import {
setHighlightedView,
switchEditorMode,
} from '../../components/editor/actions/action-creators'
import { EditorModes, existingComment } from '../../components/editor/editor-modes'

export type MultiplayerColor = {
background: string
Expand Down Expand Up @@ -163,3 +169,10 @@ export function maybeRemixPresence(
): RemixPresence | null {
return scene != null ? remixPresence(scene, locationRoute) : null
}

export function openCommentThreadActions(threadId: string, scene: ElementPath | null) {
return stripNulls([
switchEditorMode(EditorModes.commentMode(existingComment(threadId), 'not-dragging')),
scene != null ? setHighlightedView(scene) : null,
])
}

0 comments on commit c5bef28

Please sign in to comment.