Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cursors Based On Connections Instead Of Users. #4794

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion editor/liveblocks.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export type ConnectionInfo = {
id: number
startedAt: number
lastSeen: number
colorIndex: number | null
}

// Optionally, UserMeta represents static/readonly metadata on each user, as
Expand All @@ -83,7 +84,6 @@ export type UserMeta = {
id: string // Accessible through `user.id`
name: string | null
avatar: string | null
colorIndex: number | null
}

// Optionally, the type of custom events broadcast and listened to in this
Expand Down
17 changes: 3 additions & 14 deletions editor/src/components/canvas/controls/comment-indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ CommentIndicators.displayName = 'CommentIndicators'

interface TemporaryCommentIndicatorProps {
position: CanvasPoint
bgColor: string
fgColor: string
avatarUrl: string | null
initials: string
}
Expand Down Expand Up @@ -123,14 +121,12 @@ function useCommentBeingComposed(): TemporaryCommentIndicatorProps | null {
if (collaborator == null) {
return {
initials: 'AN',
color: multiplayerColorFromIndex(null),
avatar: null,
}
}

return {
initials: multiplayerInitialsFromName(normalizeMultiplayerName(collaborator.name)),
color: multiplayerColorFromIndex(collaborator.colorIndex),
avatar: collaborator.avatar,
}
}, [collabs, myUserId])
Expand All @@ -141,8 +137,6 @@ function useCommentBeingComposed(): TemporaryCommentIndicatorProps | null {

return {
position: location,
bgColor: collaboratorInfo.color.background,
fgColor: collaboratorInfo.color.foreground,
avatarUrl: collaboratorInfo.avatar,
initials: collaboratorInfo.initials,
}
Expand All @@ -161,8 +155,6 @@ const CommentIndicatorsInner = React.memo(() => {
<CommentIndicatorUI
position={temporaryIndicatorData.position}
resolved={false}
bgColor={temporaryIndicatorData.bgColor}
fgColor={temporaryIndicatorData.fgColor}
avatarUrl={temporaryIndicatorData.avatarUrl}
avatarInitials={temporaryIndicatorData.initials}
isActive={true}
Expand All @@ -177,8 +169,6 @@ CommentIndicatorsInner.displayName = 'CommentIndicatorInner'
interface CommentIndicatorUIProps {
position: CanvasPoint
resolved: boolean
bgColor: string
fgColor: string
avatarInitials: string
avatarUrl?: string | null
isActive: boolean
Expand Down Expand Up @@ -232,8 +222,7 @@ function useIndicatorStyle(
}

export const CommentIndicatorUI = React.memo<CommentIndicatorUIProps>((props) => {
const { position, bgColor, fgColor, avatarUrl, avatarInitials, resolved, isActive, isRead } =
props
const { position, avatarUrl, avatarInitials, resolved, isActive, isRead } = props

const style = useIndicatorStyle(position, {
isRead: isRead ?? true,
Expand All @@ -246,7 +235,7 @@ export const CommentIndicatorUI = React.memo<CommentIndicatorUIProps>((props) =>
return (
<div style={style}>
<MultiplayerAvatar
color={{ background: bgColor, foreground: fgColor }}
color={multiplayerColorFromIndex(null)}
picture={avatarUrl}
name={avatarInitials}
/>
Expand Down Expand Up @@ -626,7 +615,7 @@ const CommentIndicatorWrapper = React.memo((props: CommentIndicatorWrapper) => {
>
<MultiplayerAvatar
name={multiplayerInitialsFromName(normalizeMultiplayerName(user.name))}
color={multiplayerColorFromIndex(user.colorIndex)}
color={multiplayerColorFromIndex(null)}
picture={user.avatar}
style={{ outline: 'none' }}
/>
Expand Down
36 changes: 28 additions & 8 deletions editor/src/components/canvas/multiplayer-presence.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import {
} from '../../../liveblocks.config'
import {
getCollaborator,
getConnectionById,
useAddMyselfToCollaborators,
useCanComment,
useMyUserAndPresence,
useConnections,
} from '../../core/commenting/comment-hooks'
import { MetadataUtils } from '../../core/model/element-metadata-utils'
import { mapDropNulls } from '../../core/shared/array-utils'
Expand All @@ -32,7 +34,9 @@ import {
windowPoint,
zeroRectIfNullOrInfinity,
} from '../../core/shared/math-utils'
import type { MultiplayerColor } from '../../core/shared/multiplayer'
import {
excludeMyConnection,
isPlayerOnAnotherRemixRoute,
multiplayerColorFromIndex,
normalizeOthersList,
Expand Down Expand Up @@ -173,13 +177,15 @@ const MultiplayerCursors = React.memo(() => {
const me = useSelf()
const collabs = useStorage((store) => store.collaborators)
const others = useOthers((list) => {
const presences = normalizeOthersList(me.id, list)
const presences = excludeMyConnection(me.id, me.connectionId, list)
return presences.map((p) => ({
presenceInfo: p,
userInfo: getCollaborator(collabs, p),
}))
})

const connections = useConnections()

return (
<div
style={{
Expand All @@ -206,7 +212,10 @@ const MultiplayerCursors = React.memo(() => {
<MultiplayerCursor
key={`cursor-${other.presenceInfo.id}`}
name={other.userInfo.name}
colorIndex={other.userInfo.colorIndex}
colorIndex={
getConnectionById(connections, other.userInfo.id, other.presenceInfo.connectionId)
?.colorIndex ?? null
}
position={position}
userId={other.userInfo.id}
/>
Expand Down Expand Up @@ -433,12 +442,19 @@ const FollowingOverlay = React.memo(() => {
dispatch([switchEditorMode(EditorModes.selectMode(null, false, 'none'))])
}, [dispatch])

const followedUserColor = React.useMemo(() => {
return multiplayerColorFromIndex(followedUser?.colorIndex ?? null)
}, [followedUser])
const connections = useConnections()

const followedUserColor: MultiplayerColor = React.useMemo(() => {
if (followed == null) {
return multiplayerColorFromIndex(null)
} else {
return multiplayerColorFromIndex(
getConnectionById(connections, followed.id, followed.connectionId)?.colorIndex ?? null,
)
}
}, [connections, followed])

const collabs = useStorage((store) => store.collaborators)
const connections = useStorage((store) => store.connections)

const { user: myUser, presence: myPresence } = useMyUserAndPresence()
const others = useOthers((list) =>
Expand Down Expand Up @@ -564,15 +580,19 @@ const MultiplayerShadows = React.memo(() => {
}))
})

const connections = useConnections()

const shadows = React.useMemo(() => {
return others.flatMap(
(other) =>
other.presenceInfo.presence.activeFrames?.map((activeFrame) => ({
activeFrame: activeFrame,
colorIndex: other.userInfo.colorIndex,
colorIndex:
getConnectionById(connections, other.userInfo.id, other.presenceInfo.connectionId)
?.colorIndex ?? null,
})) ?? [],
)
}, [others])
}, [connections, others])

const myActiveFrames = useEditorState(
Substores.restOfEditor,
Expand Down
59 changes: 37 additions & 22 deletions editor/src/components/user-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import { jsx } from '@emotion/react'
import type { CSSProperties } from 'react'
import { useOthers, useStatus, useStorage } from '../../liveblocks.config'
import { getUserPicture, isLoggedIn } from '../common/user'
import { getCollaborator, useMyUserAndPresence } from '../core/commenting/comment-hooks'
import {
getCollaborator,
getConnectionById,
useConnections,
useGetMyConnection,
useMyUserAndPresence,
} from '../core/commenting/comment-hooks'
import type { FollowTarget, MultiplayerColor } from '../core/shared/multiplayer'
import {
canFollowTarget,
excludeMyConnection,
followTarget,
isDefaultAuth0AvatarURL,
multiplayerColorFromIndex,
Expand Down Expand Up @@ -127,24 +134,25 @@ const MultiplayerUserBar = React.memo(() => {
}, [dispatch, url])

const collabs = useStorage((store) => store.collaborators)
const connections = useStorage((store) => store.connections)

const connections = useConnections()

const { user: myUser, presence: myPresence } = useMyUserAndPresence()
const sortAvatars = useSortMultiplayerUsers()
const isBeingFollowed = useIsBeingFollowed()

const others = useOthers((list) =>
list
.filter((entry) => entry.connectionId !== myPresence.connectionId)
.map((other) => {
return {
...getCollaborator(collabs, other),
following: other.presence.following,
connectionId: other.connectionId,
connectedAt: connections?.[other.id]?.[other.connectionId]?.startedAt ?? 0,
}
}),
)
const others = useOthers((list) => {
return excludeMyConnection(myPresence.id, myPresence.connectionId, list).map((other) => {
return {
...getCollaborator(collabs, other),
following: other.presence.following,
colorIndex:
getConnectionById(connections, other.id, other.connectionId)?.colorIndex ?? null,
connectionId: other.connectionId,
connectedAt: connections?.[other.id]?.[other.connectionId]?.startedAt ?? 0,
}
})
})

const mode = useEditorState(
Substores.restOfEditor,
Expand Down Expand Up @@ -278,7 +286,6 @@ const MultiplayerUserBar = React.memo(() => {
<MultiplayerAvatar
name={multiplayerInitialsFromName(name)}
tooltip={{ text: name, colored: false }}
color={multiplayerColorFromIndex(other.colorIndex)}
picture={other.avatar}
isOwner={isOwner}
isOffline={true}
Expand Down Expand Up @@ -307,7 +314,9 @@ const MultiplayerUserBar = React.memo(() => {
>
<MultiplayerAvatar
name={multiplayerInitialsFromName(myUser.name)}
color={multiplayerColorFromIndex(myUser.colorIndex)}
color={multiplayerColorFromIndex(
getConnectionById(connections, myUser.id, myPresence.connectionId)?.colorIndex ?? null,
)}
picture={myUser.avatar}
isOwner={amIOwner}
size={AvatarSize}
Expand All @@ -322,7 +331,7 @@ MultiplayerUserBar.displayName = 'MultiplayerUserBar'

export type MultiplayerAvatarProps = {
name: string
color: MultiplayerColor
color?: MultiplayerColor
picture?: string | null
onClick?: () => void
isBeingFollowed?: boolean
Expand Down Expand Up @@ -356,15 +365,21 @@ export const MultiplayerAvatar = React.memo((props: MultiplayerAvatarProps) => {
disabled={props.tooltip == null}
title={tooltipWithLineBreak}
placement='bottom'
backgroundColor={props.tooltip?.colored === true ? props.color.background : undefined}
textColor={props.tooltip?.colored === true ? props.color.foreground : undefined}
backgroundColor={
props.tooltip?.colored === true && props.color != null ? props.color.background : undefined
}
textColor={
props.tooltip?.colored === true && props.color != null ? props.color.foreground : undefined
}
>
<div
style={{
width: props.size ?? 25.5,
height: props.size ?? 25.5,
backgroundColor: props.isOffline ? colorTheme.bg4.value : props.color.background,
color: props.isOffline ? colorTheme.fg2.value : props.color.foreground,
backgroundColor:
props.isOffline || props.color == null ? colorTheme.bg4.value : props.color.background,
color:
props.isOffline || props.color == null ? colorTheme.fg2.value : props.color.foreground,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
Expand All @@ -378,7 +393,7 @@ export const MultiplayerAvatar = React.memo((props: MultiplayerAvatarProps) => {
? `1px solid ${colorTheme.bg1.value}`
: `1px solid ${colorTheme.transparent.value}`,
boxShadow:
props.isBeingFollowed === true
props.isBeingFollowed === true && props.color != null
? `0px 0px 0px 2.5px ${props.color.background}`
: `0px 0px 0px 2.5px ${colorTheme.transparent.value}`,
...props.style,
Expand Down
Loading
Loading