diff --git a/editor/resources/editor/icons/light/semantic/star-black-14x14@2x.png b/editor/resources/editor/icons/light/semantic/star-black-14x14@2x.png
new file mode 100644
index 000000000000..aa4d22846057
Binary files /dev/null and b/editor/resources/editor/icons/light/semantic/star-black-14x14@2x.png differ
diff --git a/editor/resources/editor/icons/light/semantic/star-white-14x14@2x.png b/editor/resources/editor/icons/light/semantic/star-white-14x14@2x.png
new file mode 100644
index 000000000000..2becdb79332f
Binary files /dev/null and b/editor/resources/editor/icons/light/semantic/star-white-14x14@2x.png differ
diff --git a/editor/src/components/user-bar.tsx b/editor/src/components/user-bar.tsx
index dd8cc073ab22..4ede5292ec95 100644
--- a/editor/src/components/user-bar.tsx
+++ b/editor/src/components/user-bar.tsx
@@ -14,7 +14,7 @@ import {
} from '../core/shared/multiplayer'
import { MultiplayerWrapper } from '../utils/multiplayer-wrapper'
import { when } from '../utils/react-conditionals'
-import { Avatar, Tooltip, useColorTheme } from '../uuiui'
+import { Avatar, FlexRow, Icn, Tooltip, colorTheme } from '../uuiui'
import { notice } from './common/notice'
import type { EditorAction } from './editor/action-types'
import { showToast, switchEditorMode } from './editor/actions/action-creators'
@@ -37,15 +37,18 @@ export const UserBar = React.memo(() => {
if (!isLoggedIn(loginState)) {
return null
- } else if (roomStatus !== 'connected') {
- return
- } else {
- return (
-
-
-
- )
}
+ return (
+
+ {when(
+ roomStatus === 'connected',
+
+
+ ,
+ )}
+
+
+ )
})
UserBar.displayName = 'UserBar'
@@ -55,9 +58,23 @@ export const SinglePlayerUserBar = React.memo(() => {
(store) => getUserPicture(store.userState.loginState),
'SinglePlayerUserBar userPicture',
)
+ const amIOwner = useEditorState(
+ Substores.projectServerState,
+ (store) => store.projectServerState.isMyProject === 'yes',
+ 'SinglePlayerUserBar amIOwner',
+ )
return (
-
+
+
+ {amIOwner ?
: null}
+
)
})
@@ -65,11 +82,9 @@ SinglePlayerUserBar.displayName = 'SinglePlayerUserBar'
const MultiplayerUserBar = React.memo(() => {
const dispatch = useDispatch()
- const colorTheme = useColorTheme()
const collabs = useStorage((store) => store.collaborators)
const { user: myUser } = useMyUserAndPresence()
- const myName = React.useMemo(() => normalizeMultiplayerName(myUser.name), [myUser])
const others = useOthers((list) =>
normalizeOthersList(myUser.id, list).map((other) => {
@@ -93,6 +108,12 @@ const MultiplayerUserBar = React.memo(() => {
'MultiplayerUserBar mode',
)
+ const ownerId = useEditorState(
+ Substores.projectServerState,
+ (store) => store.projectServerState.ownerId,
+ 'MultiplayerUserBar ownerId',
+ )
+
const toggleFollowing = React.useCallback(
(targetId: string) => () => {
let actions: EditorAction[] = []
@@ -135,64 +156,43 @@ const MultiplayerUserBar = React.memo(() => {
style={{
display: 'flex',
alignItems: 'center',
+ justifyContent: 'center',
gap: 4,
}}
>
+ {visibleOthers.map((other) => {
+ if (other == null) {
+ return null
+ }
+ const name = normalizeMultiplayerName(other.name)
+ const isOwner = ownerId === other.id
+ return (
+
+ )
+ })}
{when(
- visibleOthers.length > 0,
-
0,
+ normalizeMultiplayerName(c.name)).join(', ')}
+ color={{
+ background: colorTheme.fg8.value,
+ foreground: colorTheme.fg0.value,
}}
- >
- {visibleOthers.map((other) => {
- if (other == null) {
- return null
- }
- const name = normalizeMultiplayerName(other.name)
- return (
-
- )
- })}
- {when(
- hiddenOthers.length > 0,
- normalizeMultiplayerName(c.name)).join(', ')}
- color={{
- background: colorTheme.fg8.value,
- foreground: colorTheme.fg0.value,
- }}
- picture={null}
- />,
- )}
-
,
+ picture={null}
+ />,
)}
-
-
-
)
})
@@ -221,12 +221,13 @@ MultiplayerAvatarWithTooltip.displayName = 'MultiplayerAvatarWithTooltip'
export type MultiplayerAvatarProps = {
name: string
color: MultiplayerColor
+ coloredTooltip?: boolean
picture?: string | null
- border?: boolean
onClick?: () => void
- active?: boolean
+ isBeingFollowed?: boolean
size?: number
follower?: boolean
+ isOwner?: boolean
style?: CSSProperties
}
@@ -234,8 +235,6 @@ export const MultiplayerAvatar = React.memo((props: MultiplayerAvatarProps) => {
const picture = React.useMemo(() => {
return isDefaultAuth0AvatarURL(props.picture ?? null) ? null : props.picture
}, [props.picture])
-
- const colorTheme = useColorTheme()
return (
{
alignItems: 'center',
justifyContent: 'center',
borderRadius: '100%',
- border: `3px solid ${props.active === true ? colorTheme.primary.value : 'transparent'}`,
fontSize: 9,
fontWeight: 700,
cursor: props.onClick != null ? 'pointer' : 'default',
- boxShadow: props.active === true ? `0px 0px 15px ${colorTheme.primary.value}` : undefined,
position: 'relative',
+ outline: `.3px solid ${colorTheme.bg1.value}`,
+ boxShadow:
+ props.isBeingFollowed === true
+ ? `0px 0px 8px ${colorTheme.dynamicBlue.value}`
+ : undefined,
...props.style,
}}
onClick={props.onClick}
>
-
- {when(props.follower === true,
)}
+
+ {props.isOwner ?
: null}
+ {props.follower ?
: null}
)
})
MultiplayerAvatar.displayName = 'MultiplayerAvatar'
const FollowerBadge = React.memo(() => {
- const colorTheme = useColorTheme()
-
return (
{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- border: `1px solid ${colorTheme.white.value}`,
+ border: `1px solid ${colorTheme.bg1.value}`,
}}
/>
)
})
FollowerBadge.displayName = 'FollowerBadge'
+const OwnerBadge = React.memo(() => {
+ return (
+
+ )
+})
+
+OwnerBadge.displayName = 'OwnerBadge'
+
interface AvatarPictureProps {
url: string | null | undefined
initials: string