diff --git a/src/react-components/room/object-hooks.js b/src/react-components/room/object-hooks.js index 50553a5966c..ce337166b08 100644 --- a/src/react-components/room/object-hooks.js +++ b/src/react-components/room/object-hooks.js @@ -1,12 +1,15 @@ import { useEffect, useState, useCallback, useMemo } from "react"; import { removeNetworkedObject } from "../../utils/removeNetworkedObject"; -import { shouldUseNewLoader } from "../../utils/bit-utils"; +import { findAncestorWithComponent, shouldUseNewLoader } from "../../utils/bit-utils"; import { rotateInPlaceAroundWorldUp, affixToWorldUp } from "../../utils/three-utils"; import { getPromotionTokenForFile } from "../../utils/media-utils"; import { hasComponent } from "bitecs"; +import { isPinned as isObjectPinned } from "../../bit-systems/networking"; import { isPinned as getPinnedState } from "../../bit-systems/networking"; -import { MediaInfo, Static } from "../../bit-components"; +import { MediaContentBounds, MediaInfo, MediaLoader, Owned, Static } from "../../bit-components"; import { deleteTheDeletableAncestor } from "../../bit-systems/delete-entity-system"; +import { setPinned } from "../../utils/bit-pinning-helper"; +import { debounce } from "lodash"; export function isMe(object) { return object.id === "avatar-rig"; @@ -14,7 +17,8 @@ export function isMe(object) { export function isPlayer(object) { if (shouldUseNewLoader()) { - // TODO Add when networked avatar is migrated + // TODO Add when networked avatar is migrated? + // We don't list players in the objects list so do we need this function at all? return false; } else { return !!object.el.components["networked-avatar"]; @@ -43,18 +47,28 @@ export function usePinObject(hubChannel, scene, object) { const [isPinned, setIsPinned] = useState(getPinnedState(object.eid)); const pinObject = useCallback(() => { - const el = object.el; - if (!NAF.utils.isMine(el) && !NAF.utils.takeOwnership(el)) return; - window.APP.pinningHelper.setPinned(el, true); - }, [object]); + if (shouldUseNewLoader()) { + const mediaRootEid = findAncestorWithComponent(APP.world, MediaContentBounds, object.eid); + setPinned(hubChannel, APP.world, mediaRootEid, true); + } else { + const el = object.el; + if (!NAF.utils.isMine(el) && !NAF.utils.takeOwnership(el)) return; + window.APP.pinningHelper.setPinned(el, true); + } + }, [object, hubChannel]); const unpinObject = useCallback(() => { - const el = object.el; - if (!NAF.utils.isMine(el) && !NAF.utils.takeOwnership(el)) return; - window.APP.pinningHelper.setPinned(el, false); - }, [object]); + if (shouldUseNewLoader()) { + const mediaRootEid = findAncestorWithComponent(APP.world, MediaContentBounds, object.eid); + setPinned(hubChannel, APP.world, mediaRootEid, false); + } else { + const el = object.el; + if (!NAF.utils.isMine(el) && !NAF.utils.takeOwnership(el)) return; + window.APP.pinningHelper.setPinned(el, false); + } + }, [object, hubChannel]); - const togglePinned = useCallback(() => { + const _togglePinned = useCallback(() => { if (isPinned) { unpinObject(); } else { @@ -62,10 +76,22 @@ export function usePinObject(hubChannel, scene, object) { } }, [isPinned, pinObject, unpinObject]); + const togglePinned = useMemo(() => debounce(_togglePinned, 100), [_togglePinned]); + useEffect(() => { + return () => { + togglePinned.cancel(); + }; + }, [togglePinned]); + useEffect(() => { - // TODO Add when pinning is migrated if (shouldUseNewLoader()) { - return; + const handler = setInterval(() => { + const mediaRootEid = findAncestorWithComponent(APP.world, MediaContentBounds, object.eid); + setIsPinned(isObjectPinned(mediaRootEid)); + }, 100); + return () => { + clearInterval(handler); + }; } const el = object.el; @@ -82,24 +108,33 @@ export function usePinObject(hubChannel, scene, object) { }; }, [object]); - if (shouldUseNewLoader()) { - // TODO Add when pinning is migrated - return false; - } - - const el = object.el; - let userOwnsFile = false; - if (el.components["media-loader"]) { - const { fileIsOwned, fileId } = el.components["media-loader"].data; + if (shouldUseNewLoader()) { + const fileId = MediaLoader.fileId[object.eid]; + const mediaRootEid = findAncestorWithComponent(APP.world, MediaContentBounds, object.eid); + const fileIsOwned = hasComponent(APP.world, Owned, mediaRootEid); userOwnsFile = fileIsOwned || (fileId && getPromotionTokenForFile(fileId)); + } else { + const el = object.el; + if (el.components["media-loader"]) { + const { fileIsOwned, fileId } = el.components["media-loader"].data; + userOwnsFile = fileIsOwned || (fileId && getPromotionTokenForFile(fileId)); + } + } + + let targetEid; + if (shouldUseNewLoader()) { + targetEid = findAncestorWithComponent(APP.world, MediaContentBounds, object.eid); + } else { + targetEid = object.el.eid; } + const isStatic = hasComponent(APP.world, Static, targetEid); const canPin = !!( scene.is("entered") && !isPlayer(object) && - !hasComponent(APP.world, Static, el.eid) && + !isStatic && hubChannel.can("pin_objects") && userOwnsFile ); diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js index 9d0da98e7e1..5a775e2c798 100644 --- a/src/scene-entry-manager.js +++ b/src/scene-entry-manager.js @@ -40,6 +40,9 @@ export default class SceneEntryManager { this.leftCursorController = document.getElementById("left-cursor-controller"); this.avatarRig = document.getElementById("avatar-rig"); this._entered = false; + /** + * @type {Function} + */ this.performConditionalSignIn = () => {}; this.history = history; } diff --git a/src/utils/bit-pinning-helper.ts b/src/utils/bit-pinning-helper.ts index 55333ca8d9d..1a82df39005 100644 --- a/src/utils/bit-pinning-helper.ts +++ b/src/utils/bit-pinning-helper.ts @@ -4,6 +4,7 @@ import HubChannel from "./hub-channel"; import { EntityID } from "./networking-types"; import { takeOwnership } from "./take-ownership"; import { createMessageDatas, isNetworkInstantiated, isPinned } from "../bit-systems/networking"; +import { SignInMessages } from "../react-components/auth/SignInModal"; export const setPinned = async (hubChannel: HubChannel, world: HubsWorld, eid: EntityID, shouldPin: boolean) => { _signInAndPinOrUnpinElement(hubChannel, world, eid, shouldPin); @@ -29,8 +30,14 @@ const unpinElement = (hubChannel: HubChannel, world: HubsWorld, eid: EntityID) = const _signInAndPinOrUnpinElement = (hubChannel: HubChannel, world: HubsWorld, eid: EntityID, shouldPin: boolean) => { const action = shouldPin ? () => _pinElement(hubChannel, world, eid) : () => unpinElement(hubChannel, world, eid); - // TODO: Perform conditional sign in - action(); + APP.entryManager!.performConditionalSignIn( + () => hubChannel.signedIn, + action, + shouldPin ? SignInMessages.pin : SignInMessages.unpin, + (e: Error) => { + console.warn(`PinningHelper: Conditional sign-in failed. ${e}`); + } + ); }; export const canPin = (hubChannel: HubChannel, eid: EntityID): boolean => {