Skip to content

Commit

Permalink
Remove useEffect from FollowingOverlay (#4513)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruggi authored Nov 20, 2023
1 parent 60c26fd commit aca6ae6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 40 deletions.
1 change: 1 addition & 0 deletions editor/liveblocks.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const {
useUpdateMyPresence,
useSelf,
useOthers,
useOthersListener,
useOthersMapped,
useOthersConnectionIds,
useOther,
Expand Down
99 changes: 66 additions & 33 deletions editor/src/components/canvas/multiplayer-cursors.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { User } from '@liveblocks/client'
import { motion } from 'framer-motion'
import React from 'react'
import { useOthers, useRoom, useSelf, useUpdateMyPresence } from '../../../liveblocks.config'
import type { Presence, UserMeta } from '../../../liveblocks.config'
import {
useOthers,
useOthersListener,
useRoom,
useSelf,
useUpdateMyPresence,
} from '../../../liveblocks.config'
import { useAddMyselfToCollaborators } from '../../core/commenting/comment-hooks'
import type { CanvasPoint } from '../../core/shared/math-utils'
import { pointsEqual, windowPoint } from '../../core/shared/math-utils'
Expand All @@ -10,6 +18,7 @@ import {
normalizeOthersList,
possiblyUniqueColor,
} from '../../core/shared/multiplayer'
import { assertNever } from '../../core/shared/utils'
import { useKeepShallowReferenceEquality } from '../../utils/react-performance'
import { UtopiaTheme, useColorTheme } from '../../uuiui'
import type { EditorAction } from '../editor/action-types'
Expand All @@ -25,18 +34,18 @@ export const MultiplayerPresence = React.memo(() => {
const dispatch = useDispatch()

const room = useRoom()
const self = useSelf()
const others = useOthers((list) => normalizeOthersList(self.id, list))
const me = useSelf()
const others = useOthers((list) => normalizeOthersList(me.id, list))
const updateMyPresence = useUpdateMyPresence()

const selfColorIndex = React.useMemo(() => self.presence.colorIndex, [self.presence])
const myColorIndex = React.useMemo(() => me.presence.colorIndex, [me.presence])
const otherColorIndices = useKeepShallowReferenceEquality(
others.map((other) => other.presence.colorIndex),
)

const getColorIndex = React.useCallback(() => {
return selfColorIndex ?? possiblyUniqueColor(otherColorIndices)
}, [selfColorIndex, otherColorIndices])
return myColorIndex ?? possiblyUniqueColor(otherColorIndices)
}, [myColorIndex, otherColorIndices])
const loginState = useEditorState(
Substores.userState,
(store) => store.userState.loginState,
Expand Down Expand Up @@ -107,8 +116,8 @@ export const MultiplayerPresence = React.memo(() => {
MultiplayerPresence.displayName = 'MultiplayerPresence'

const MultiplayerCursors = React.memo(() => {
const self = useSelf()
const others = useOthers((list) => normalizeOthersList(self.id, list))
const me = useSelf()
const others = useOthers((list) => normalizeOthersList(me.id, list))

return (
<div
Expand Down Expand Up @@ -224,8 +233,7 @@ const FollowingOverlay = React.memo(() => {
const colorTheme = useColorTheme()
const dispatch = useDispatch()

const self = useSelf()
const others = useOthers((list) => normalizeOthersList(self.id, list))
const room = useRoom()

const mode = useEditorState(
Substores.restOfEditor,
Expand All @@ -243,34 +251,59 @@ const FollowingOverlay = React.memo(() => {
'FollowingOverlay canvasOffset',
)

const isFollowTarget = React.useCallback(
(other: User<Presence, UserMeta>): boolean => {
return isFollowMode(mode) && other.id === mode.playerId
},
[mode],
)

const resetFollowed = React.useCallback(() => {
dispatch([switchEditorMode(EditorModes.selectMode(null, false, 'none'))])
}, [dispatch])

const followed = React.useMemo(() => {
return others.find((other) => isFollowMode(mode) && other.id === mode.playerId)
}, [others, mode])
return room.getOthers().find(isFollowTarget) ?? null
}, [room, isFollowTarget])

React.useEffect(() => {
// when following another player, apply its canvas constraints
if (followed == null) {
if (isFollowMode(mode)) {
// reset if the other player disconnects
dispatch([switchEditorMode(EditorModes.selectMode(null, false, 'none'))])
const updateCanvasFromOtherPresence = React.useCallback(
(presence: Presence) => {
let actions: EditorAction[] = []
if (presence.canvasScale != null && presence.canvasScale !== canvasScale) {
actions.push(CanvasActions.zoom(presence.canvasScale, null))
}
return
}
if (presence.canvasOffset != null && !pointsEqual(presence.canvasOffset, canvasOffset)) {
actions.push(CanvasActions.positionCanvas(presence.canvasOffset))
}
if (actions.length > 0) {
dispatch(actions)
}
},
[dispatch, canvasScale, canvasOffset],
)

let actions: EditorAction[] = []
if (followed.presence.canvasScale != null && followed.presence.canvasScale !== canvasScale) {
actions.push(CanvasActions.zoom(followed.presence.canvasScale, null))
}
if (
followed.presence.canvasOffset != null &&
!pointsEqual(followed.presence.canvasOffset, canvasOffset)
) {
actions.push(CanvasActions.positionCanvas(followed.presence.canvasOffset))
}
if (actions.length > 0) {
dispatch(actions)
useOthersListener((event) => {
if (isFollowMode(mode)) {
switch (event.type) {
case 'enter':
case 'update':
if (isFollowTarget(event.user)) {
updateCanvasFromOtherPresence(event.user.presence)
}
break
case 'leave':
if (isFollowTarget(event.user)) {
resetFollowed()
}
break
case 'reset':
resetFollowed()
break
default:
assertNever(event)
}
}
}, [followed, canvasScale, canvasOffset, dispatch, mode])
})

if (followed == null) {
return null
Expand Down
10 changes: 5 additions & 5 deletions editor/src/components/user-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ const MultiplayerUserBar = React.memo(() => {
const dispatch = useDispatch()
const colorTheme = useColorTheme()

const self = useSelf()
const myName = normalizeMultiplayerName(self.presence.name)
const me = useSelf()
const myName = normalizeMultiplayerName(me.presence.name)

const others = useOthers((list) =>
normalizeOthersList(self.id, list).map((other) => ({
normalizeOthersList(me.id, list).map((other) => ({
id: other.id,
name: other.presence.name,
colorIndex: other.presence.colorIndex,
Expand Down Expand Up @@ -98,7 +98,7 @@ const MultiplayerUserBar = React.memo(() => {
[dispatch, mode],
)

if (self.presence.name == null) {
if (me.presence.name == null) {
// it may still be loading, so fallback until it sorts itself out
return <SinglePlayerUserBar />
}
Expand Down Expand Up @@ -162,7 +162,7 @@ const MultiplayerUserBar = React.memo(() => {
name={multiplayerInitialsFromName(myName)}
tooltip={`${myName} (you)`}
color={{ background: colorTheme.bg3.value, foreground: colorTheme.fg1.value }}
picture={self.presence.picture}
picture={me.presence.picture}
/>
</a>
</div>
Expand Down
4 changes: 2 additions & 2 deletions editor/src/core/commenting/comment-hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export function useCanvasCommentThread(x: number, y: number): ThreadData<ThreadM
}

export function useMyMultiplayerColorIndex() {
const self = useSelf()
return self.presence.colorIndex
const me = useSelf()
return me.presence.colorIndex
}

export function useAddMyselfToCollaborators() {
Expand Down

0 comments on commit aca6ae6

Please sign in to comment.