diff --git a/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs b/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs index 2f97004301..3482adfa3e 100644 --- a/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs +++ b/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs @@ -2041,18 +2041,108 @@ private static void CreateShareableListEntry(BasisShareableEntry entry, RectTran itemTextInfo.Descriptor.SetHeight(50); itemTextInfo.Descriptor.SetWidth(400); - PanelButton removeItem = PanelButton.CreateNew(ButtonStyles.CancelButton, itemListPanel.TabButtonParent); - removeItem.Descriptor.SetTitle(string.Empty); - removeItem.SetIcon(AddressableAssets.Sprites.Trash); - removeItem.SetSize(new Vector2(80, 80)); - removeItem.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); - removeItem.Descriptor.SetTooltip(BasisLocalization.Get("library.instantiated.remove.tooltip")); - removeItem.OnClicked += async () => - { - bool confirmed = await LibraryProviderDialogRemove.PromptUserForRemoval(panel, ShareableDisplayName(entry), ShareableKindLabel(entry.Kind)); - if (!confirmed) return; - entry.Remove?.Invoke(); - }; + // Buttons registered by the entry's provider, rendered in order — e.g. a + // Share/Unshare toggle followed by a remove button. Removal is just a + // Destructive action; the Library has no dedicated remove path. + if (entry.Actions != null) + { + foreach (BasisShareableAction action in entry.Actions) + { + if (action == null || action.Invoke == null) continue; + CreateShareableActionButton(entry, itemListPanel, action); + } + } + } + + private static void CreateShareableActionButton(BasisShareableEntry entry, PanelTabGroup itemListPanel, BasisShareableAction action) + { + bool destructive = action.Style == BasisShareableActionStyle.Destructive; + // A destructive action with no label is the standard icon-only trash affordance. + bool trashButton = destructive && string.IsNullOrEmpty(action.Label); + + BuildEntryActionButton(itemListPanel.TabButtonParent, new EntryActionButton + { + Style = destructive ? ButtonStyles.CancelButton : ButtonStyles.AcceptButton, + Icon = trashButton ? AddressableAssets.Sprites.Trash : null, + Label = action.Label, + Tooltip = trashButton ? BasisLocalization.Get("library.instantiated.remove.tooltip") : null, + OnClick = async () => + { + if (!await ConfirmShareableAction(entry, action)) return; + action.Invoke?.Invoke(); + }, + }); + } + + // Returns true if the action should proceed. Explicit confirm text wins; otherwise + // a Destructive action falls back to the Library's standard "remove {name}?" prompt. + private static async Task ConfirmShareableAction(BasisShareableEntry entry, BasisShareableAction action) + { + if (!string.IsNullOrEmpty(action.ConfirmTitle)) + { + DialogBox confirmDialog = DialogBox.Create(panel, new Vector2(650, 180), + action.ConfirmTitle, + action.ConfirmBody ?? string.Empty, + AddressableAssets.Sprites.Information, + true + ); + LibraryProviderDialogRemove.BuildDialogButtons(confirmDialog); + return await confirmDialog.WaitAsync(); + } + + if (action.Style == BasisShareableActionStyle.Destructive) + { + return await LibraryProviderDialogRemove.PromptUserForRemoval(panel, ShareableDisplayName(entry), ShareableKindLabel(entry.Kind)); + } + + return true; + } + + // One small action button on a Library list entry. Used by both the shareables tab + // and the instantiated-objects tab so every entry button shares the same sizing, + // icon inset, tooltip and hidden/disabled handling. Field defaults (all false/null) + // are the common case: visible, enabled, no tooltip. + private struct EntryActionButton + { + public string Style; // ButtonStyles.* prefab + public string Icon; // sprite key; ignored when Label is set + public string Label; // set => labeled 180x80 button; empty => icon-only 80x80 + public string Tooltip; + public bool Hidden; // hides the button (e.g. not applicable to this entry) + public bool Disabled; + public string DisabledReason; // surfaced when Disabled + public Func OnClick; + } + + private static PanelButton BuildEntryActionButton(Component parent, in EntryActionButton spec) + { + PanelButton button = PanelButton.CreateNew(spec.Style, parent); + + if (string.IsNullOrEmpty(spec.Label)) + { + button.Descriptor.SetTitle(string.Empty); + if (!string.IsNullOrEmpty(spec.Icon)) + { + button.SetIcon(spec.Icon); + // Inset the icon so its strokes stay clear of the bevel — matches PE Image Simple Square's pattern. + button.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); + } + button.SetSize(new Vector2(80, 80)); + } + else + { + button.Descriptor.SetTitle(spec.Label); + button.SetSize(new Vector2(180, 80)); + } + + if (!string.IsNullOrEmpty(spec.Tooltip)) button.Descriptor.SetTooltip(spec.Tooltip); + if (spec.Hidden) button.Descriptor.SetActive(false); + if (spec.Disabled) button.SetInteractable(false, spec.DisabledReason); + + Func onClick = spec.OnClick; + if (onClick != null) button.OnClicked += async () => await onClick(); + + return button; } private static string ShareableIcon(BasisShareableKind kind) @@ -2227,69 +2317,63 @@ private static void CreateListEntry(BasisRuntimeSpawnRegistry.SpawnInstance item bool isScene = itemKey.SpawnMode == BasisRuntimeSpawnRegistry.SpawnMode.Scene; - PanelButton selectItem = PanelButton.CreateNew(ButtonStyles.AcceptButton, itemListPanel.TabButtonParent); - selectItem.Descriptor.SetTitle(string.Empty); - selectItem.SetIcon(AddressableAssets.Sprites.Select); - selectItem.SetSize(new Vector2(80, 80)); - // Inset the icon so its strokes stay clear of the bevel — matches PE Image Simple Square's pattern. - selectItem.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); - - selectItem.Descriptor.SetActive(!isScene); - selectItem.Descriptor.SetTooltip(BasisLocalization.Get("library.instantiated.select.tooltip")); - selectItem.OnClicked += async () => + BuildEntryActionButton(itemListPanel.TabButtonParent, new EntryActionButton { - if(hasSelected) + Style = ButtonStyles.AcceptButton, + Icon = AddressableAssets.Sprites.Select, + Tooltip = BasisLocalization.Get("library.instantiated.select.tooltip"), + Hidden = isScene, + OnClick = async () => { - PlacementManager.RemoveSelectionSpawnInstanceID(itemKey); - - await RefreshCurrentTab(); - } - else - { - // send the selection - PlacementManager.SetActiveSelection(itemKey); - - // close the menu - BasisMainMenu.Close(); - } - - }; - - PanelButton TeleportToItem = PanelButton.CreateNew(ButtonStyles.StandardButton, itemListPanel.TabButtonParent); - TeleportToItem.Descriptor.SetTitle(string.Empty); - TeleportToItem.SetIcon(AddressableAssets.Sprites.TeleportTo); - TeleportToItem.SetSize(new Vector2(80, 80)); - TeleportToItem.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); + if (hasSelected) + { + PlacementManager.RemoveSelectionSpawnInstanceID(itemKey); + await RefreshCurrentTab(); + } + else + { + // send the selection + PlacementManager.SetActiveSelection(itemKey); + // close the menu + BasisMainMenu.Close(); + } + }, + }); - TeleportToItem.Descriptor.SetActive(!isScene); - TeleportToItem.Descriptor.SetTooltip(BasisLocalization.Get("library.instantiated.teleport.tooltip")); - TeleportToItem.OnClicked += () => + BuildEntryActionButton(itemListPanel.TabButtonParent, new EntryActionButton { - - switch(itemKey.SpawnMode) + Style = ButtonStyles.StandardButton, + Icon = AddressableAssets.Sprites.TeleportTo, + Tooltip = BasisLocalization.Get("library.instantiated.teleport.tooltip"), + Hidden = isScene, + OnClick = () => { - case BasisRuntimeSpawnRegistry.SpawnMode.Avatar: - case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: - - // find the object in the BasisRuntimeSpawnRegistry - if (BasisRuntimeSpawnRegistry.SpawnedGameobjects.TryGetValue(itemKey.LoadedNetID, out GameObject go) && go != null) - { - Vector3 offsetTarget = go.transform.position; + switch (itemKey.SpawnMode) + { + case BasisRuntimeSpawnRegistry.SpawnMode.Avatar: + case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: - if(itemKey.bundleConnector != null) + // find the object in the BasisRuntimeSpawnRegistry + if (BasisRuntimeSpawnRegistry.SpawnedGameobjects.TryGetValue(itemKey.LoadedNetID, out GameObject go) && go != null) { - offsetTarget.y = offsetTarget.y + itemKey.bundleConnector.Bounds.max.y; - } + Vector3 offsetTarget = go.transform.position; - BasisLocalPlayer.Instance.Teleport( offsetTarget, Quaternion.identity, mode: BasisTeleportMode.WorldFeet ); - } + if (itemKey.bundleConnector != null) + { + offsetTarget.y = offsetTarget.y + itemKey.bundleConnector.Bounds.max.y; + } - break; - case BasisRuntimeSpawnRegistry.SpawnMode.Scene: - BasisDebug.LogWarning( "LibraryProvider.cs -> Teleport To Item button for scene is not implemented!" ); - break; - } - }; + BasisLocalPlayer.Instance.Teleport( offsetTarget, Quaternion.identity, mode: BasisTeleportMode.WorldFeet ); + } + + break; + case BasisRuntimeSpawnRegistry.SpawnMode.Scene: + BasisDebug.LogWarning( "LibraryProvider.cs -> Teleport To Item button for scene is not implemented!" ); + break; + } + return Task.CompletedTask; + }, + }); // Static / lock toggle — networked game objects only; applies for everyone (server-authoritative). // Cycles None -> Static (creator or moderator) -> Admin-locked (moderator only) -> None. @@ -2321,131 +2405,127 @@ private static void CreateListEntry(BasisRuntimeSpawnRegistry.SpawnInstance item break; } - PanelButton staticToggle = PanelButton.CreateNew(ButtonStyles.StandardButton, itemListPanel.TabButtonParent); - staticToggle.Descriptor.SetTitle(string.Empty); - staticToggle.SetIcon(lockLevel == 0 ? AddressableAssets.Sprites.Unlocked - : lockLevel == 1 ? AddressableAssets.Sprites.Locked - : AddressableAssets.Sprites.Admin); - staticToggle.SetSize(new Vector2(80, 80)); - staticToggle.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); - staticToggle.Descriptor.SetTooltip(BasisLocalization.Get( - lockLevel == 0 ? "library.instantiated.static.tooltip" - : lockLevel == 1 ? "library.instantiated.static.lockedTooltip" - : "library.instantiated.static.adminTooltip")); - // Disabled reason depends on why: an admin-locked item can only be changed by a moderator. string disabledReason = lockLevel == 2 ? BasisLocalization.Get("library.instantiated.static.adminOnly") : BasisLocalization.Get("library.instantiated.static.noPermission"); - staticToggle.SetInteractable(canToggle, canToggle ? null : disabledReason); - staticToggle.OnClicked += () => + BuildEntryActionButton(itemListPanel.TabButtonParent, new EntryActionButton { - // Mode 0 = GameObject. The server authorizes per tier and rebroadcasts; the row - // icon updates when that broadcast arrives (RegistryChangeType.Modified). - BasisNetworkSpawnItem.RequestSetStatic(itemKey.LoadedNetID, 0, nextStatic, nextAdmin); - }; + Style = ButtonStyles.StandardButton, + Icon = lockLevel == 0 ? AddressableAssets.Sprites.Unlocked + : lockLevel == 1 ? AddressableAssets.Sprites.Locked + : AddressableAssets.Sprites.Admin, + Tooltip = BasisLocalization.Get( + lockLevel == 0 ? "library.instantiated.static.tooltip" + : lockLevel == 1 ? "library.instantiated.static.lockedTooltip" + : "library.instantiated.static.adminTooltip"), + Disabled = !canToggle, + DisabledReason = disabledReason, + OnClick = () => + { + // Mode 0 = GameObject. The server authorizes per tier and rebroadcasts; the row + // icon updates when that broadcast arrives (RegistryChangeType.Modified). + BasisNetworkSpawnItem.RequestSetStatic(itemKey.LoadedNetID, 0, nextStatic, nextAdmin); + return Task.CompletedTask; + }, + }); } - PanelButton removeItem = PanelButton.CreateNew(ButtonStyles.CancelButton, itemListPanel.TabButtonParent); - removeItem.Descriptor.SetTitle(string.Empty); - removeItem.SetIcon(AddressableAssets.Sprites.Trash); - removeItem.SetSize(new Vector2(80, 80)); - removeItem.Descriptor.IconImage.rectTransform.sizeDelta = new Vector2(-30, -30); - removeItem.Descriptor.SetTooltip(BasisLocalization.Get("library.instantiated.remove.tooltip")); - - // only apply this to items that are spawned on the network - if(itemKey.SpawnMethod == BasisRuntimeSpawnRegistry.SpawnMethod.Network) - { - // determine if we can actually remove this via admin - // if the item is embedded only allow an admin to interact - bool canRemove = !itemKey.isProtected || IsProtected; - removeItem.SetInteractable(canRemove, canRemove ? null : BasisLocalization.Get("library.disabled.protected")); - } + // Network items can be protected; only an admin may remove those. Non-network items always removable. + bool canRemove = itemKey.SpawnMethod != BasisRuntimeSpawnRegistry.SpawnMethod.Network || !itemKey.isProtected || IsProtected; - removeItem.OnClicked += async () => + BuildEntryActionButton(itemListPanel.TabButtonParent, new EntryActionButton { - BasisDebug.Log($"CreateListEntry() -> requested removal of item = {itemKey.Url} of instanceID = {instanceID} of SpawnMethod = {itemKey.SpawnMethod} and SpawnMode = {itemKey.SpawnMode}"); + Style = ButtonStyles.CancelButton, + Icon = AddressableAssets.Sprites.Trash, + Tooltip = BasisLocalization.Get("library.instantiated.remove.tooltip"), + Disabled = !canRemove, + DisabledReason = canRemove ? null : BasisLocalization.Get("library.disabled.protected"), + OnClick = async () => + { + BasisDebug.Log($"CreateListEntry() -> requested removal of item = {itemKey.Url} of instanceID = {instanceID} of SpawnMethod = {itemKey.SpawnMethod} and SpawnMode = {itemKey.SpawnMode}"); - bool result = await LibraryProviderDialogRemove.PromptUserForRemoval(panel, title, itemKey.SpawnMode.ToString()); + bool result = await LibraryProviderDialogRemove.PromptUserForRemoval(panel, title, itemKey.SpawnMode.ToString()); - if (!result) // if the result is false - { - return; // guard key stop here - } + if (!result) // if the result is false + { + return; // guard key stop here + } - // clear up front; network removals fire OnRegistryChanged.Removed async after a round-trip - PlacementManager.RemoveSelectionSpawnInstanceID(itemKey); + // clear up front; network removals fire OnRegistryChanged.Removed async after a round-trip + PlacementManager.RemoveSelectionSpawnInstanceID(itemKey); - switch (itemKey.SpawnMethod) - { - case BasisRuntimeSpawnRegistry.SpawnMethod.Local: - case BasisRuntimeSpawnRegistry.SpawnMethod.Embedded: + switch (itemKey.SpawnMethod) + { + case BasisRuntimeSpawnRegistry.SpawnMethod.Local: + case BasisRuntimeSpawnRegistry.SpawnMethod.Embedded: - // If this content was set to load on boot, removing it here also stops it - // from coming back next launch. No-op when it was never a boot item. - _ = BasisPreloadContentStore.Remove(itemKey.Url); + // If this content was set to load on boot, removing it here also stops it + // from coming back next launch. No-op when it was never a boot item. + _ = BasisPreloadContentStore.Remove(itemKey.Url); - switch(itemKey.SpawnMode) - { - case BasisRuntimeSpawnRegistry.SpawnMode.Avatar: - case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: + switch(itemKey.SpawnMode) + { + case BasisRuntimeSpawnRegistry.SpawnMode.Avatar: + case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: - // if the item is local and embedded lets actually try get the gameobject first - if (BasisRuntimeSpawnRegistry.SpawnedGameobjects.TryGetValue(itemKey.LoadedNetID, out GameObject go) && go != null) - { - // if the gameobject is not null then lets remove its registery - bool success = await BasisRuntimeSpawnRegistry.RemoveByLoadedNetId(itemKey.LoadedNetID); - if (success) + // if the item is local and embedded lets actually try get the gameobject first + if (BasisRuntimeSpawnRegistry.SpawnedGameobjects.TryGetValue(itemKey.LoadedNetID, out GameObject go) && go != null) { - // we should delete the embedded item - GameObject.Destroy(go); - } - else - { - BasisDebug.LogError($"failed to remove item = {instanceID} that has itemKey.SpawnMethod = {itemKey.SpawnMethod} from basis BasisRuntimeSpawnRegistry"); - } + // if the gameobject is not null then lets remove its registery + bool success = await BasisRuntimeSpawnRegistry.RemoveByLoadedNetId(itemKey.LoadedNetID); + if (success) + { + // we should delete the embedded item + GameObject.Destroy(go); + } + else + { + BasisDebug.LogError($"failed to remove item = {instanceID} that has itemKey.SpawnMethod = {itemKey.SpawnMethod} from basis BasisRuntimeSpawnRegistry"); + } - } + } - break; + break; - case BasisRuntimeSpawnRegistry.SpawnMode.Scene: + case BasisRuntimeSpawnRegistry.SpawnMode.Scene: - if(BasisRuntimeSpawnRegistry.SpawnedScenes.TryGetValue(itemKey.LoadedNetID, out Scene scene) && scene.IsValid()) - { - bool success = await BasisRuntimeSpawnRegistry.RemoveByLoadedNetId(itemKey.LoadedNetID); - if(success) + if(BasisRuntimeSpawnRegistry.SpawnedScenes.TryGetValue(itemKey.LoadedNetID, out Scene scene) && scene.IsValid()) { - BasisDebug.Log( $"successfully removed scene with LoadedNetID = {itemKey.LoadedNetID}" ); + bool success = await BasisRuntimeSpawnRegistry.RemoveByLoadedNetId(itemKey.LoadedNetID); + if(success) + { + BasisDebug.Log( $"successfully removed scene with LoadedNetID = {itemKey.LoadedNetID}" ); + } + else + { + BasisDebug.LogError($"failed to remove scene with LoadedNetID = {instanceID}"); + } } - else - { - BasisDebug.LogError($"failed to remove scene with LoadedNetID = {instanceID}"); - } - } - - break; - } - break; - case BasisRuntimeSpawnRegistry.SpawnMethod.Network: - switch (itemKey.SpawnMode) - { - case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: - BasisNetworkSpawnItem.RequestGameObjectUnLoad(itemKey.LoadedNetID); break; - case BasisRuntimeSpawnRegistry.SpawnMode.Scene: - BasisNetworkSpawnItem.RequestSceneUnLoad(itemKey.LoadedNetID); - break; - default: - BasisDebug.LogWarning($"Missing Spawn Method! {itemKey.SpawnMode}"); - break; - } - break; - } - await RefreshCurrentTab(); - }; + } + + break; + case BasisRuntimeSpawnRegistry.SpawnMethod.Network: + switch (itemKey.SpawnMode) + { + case BasisRuntimeSpawnRegistry.SpawnMode.GameObject: + BasisNetworkSpawnItem.RequestGameObjectUnLoad(itemKey.LoadedNetID); + break; + case BasisRuntimeSpawnRegistry.SpawnMode.Scene: + BasisNetworkSpawnItem.RequestSceneUnLoad(itemKey.LoadedNetID); + break; + default: + BasisDebug.LogWarning($"Missing Spawn Method! {itemKey.SpawnMode}"); + break; + } + break; + } + await RefreshCurrentTab(); + }, + }); } #endregion diff --git a/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisContentShareManager.cs b/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisContentShareManager.cs index 5084de237d..e8d054dbf2 100644 --- a/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisContentShareManager.cs +++ b/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisContentShareManager.cs @@ -7,6 +7,7 @@ using Basis.Scripts.TransformBinders.BoneControl; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; @@ -299,7 +300,14 @@ private static void CreateSphere(ServerContentShareMessage serverMsg) Kind = ToShareableKind(msg.ContentType), Title = shareDetail, SharerName = serverMsg.SharerDisplayName, - Remove = () => RequestRemoveSphere(sphereId), + Actions = new List + { + new BasisShareableAction + { + Style = BasisShareableActionStyle.Destructive, + Invoke = () => RequestRemoveSphere(sphereId), + }, + }, }); } } diff --git a/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisShareableRegistry.cs b/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisShareableRegistry.cs index d7b7ad8f91..0e28331e45 100644 --- a/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisShareableRegistry.cs +++ b/Basis/Packages/com.basis.framework/Networking/ContentShare/BasisShareableRegistry.cs @@ -11,13 +11,44 @@ public enum BasisShareableKind Other } +/// How the Library should present a . +/// Semantic (not a concrete UI style) so this framework type stays decoupled from +/// BasisUI prefabs/sprites — the Library maps these to buttons. +public enum BasisShareableActionStyle +{ + /// A normal/affirmative action (e.g. "Share"), rendered as an accept button. + Positive, + /// A removal action, rendered as the trash button. When no explicit + /// confirm text is supplied the Library shows its standard "remove {name}?" prompt. + Destructive, +} + +/// One button shown on a Library shareable entry. The registering package owns +/// the semantics (label + callback); the Library just presents it. +public sealed class BasisShareableAction +{ + /// Button text. Empty = icon-only (only meaningful for + /// , which uses the trash icon). + public string Label; + public BasisShareableActionStyle Style; + public Action Invoke; + /// Non-null = the Library shows a yes/no dialog with this title/body before + /// invoking (for consent-style actions). + public string ConfirmTitle; + public string ConfirmBody; +} + public sealed class BasisShareableEntry { public string Id; public BasisShareableKind Kind; public string Title; public string SharerName; - public Action Remove; + + /// Buttons rendered on the entry (in order), e.g. a "Share"/"Unshare" toggle + /// followed by a remove button. Removal is just a + /// action here — the Library has no dedicated remove path. + public List Actions; } /// @@ -55,5 +86,29 @@ public static void SetDetail(string id, string detail) } } + /// Replace an entry's action buttons — e.g. flipping a "Share" toggle to + /// "Unshare" after it's invoked. Pass an empty list (or null) to leave no buttons. + public static void SetActions(string id, List actions) + { + if (string.IsNullOrEmpty(id)) return; + if (Entries.TryGetValue(id, out BasisShareableEntry entry)) + { + entry.Actions = actions; + OnChanged?.Invoke(); + } + } + + /// Update who shared an entry (rendered as "shared by …") after + /// registration — e.g. once a received share's sharer is identified. + public static void SetSharerName(string id, string sharerName) + { + if (string.IsNullOrEmpty(id)) return; + if (Entries.TryGetValue(id, out BasisShareableEntry entry)) + { + entry.SharerName = sharerName; + OnChanged?.Invoke(); + } + } + public static IReadOnlyCollection GetAll() => Entries.Values; } diff --git a/Basis/Packages/com.basis.imagepickup/BasisImagePickupManager.cs b/Basis/Packages/com.basis.imagepickup/BasisImagePickupManager.cs index 9fe54c1541..ef29b77319 100644 --- a/Basis/Packages/com.basis.imagepickup/BasisImagePickupManager.cs +++ b/Basis/Packages/com.basis.imagepickup/BasisImagePickupManager.cs @@ -127,7 +127,14 @@ public bool SpawnFromFile(string path) Kind = BasisShareableKind.Image, Title = $"{result.Width}x{result.Height}", SharerName = ownerName, - Remove = () => { if (Instance != null) Instance.RequestDespawn(id); }, + Actions = new List + { + new BasisShareableAction + { + Style = BasisShareableActionStyle.Destructive, + Invoke = () => { if (Instance != null) Instance.RequestDespawn(id); }, + }, + }, }); if (HasNetworkID) @@ -345,7 +352,14 @@ private void FinalizeTransfer(InboundTransfer transfer) Kind = BasisShareableKind.Image, Title = $"{transfer.Width}x{transfer.Height}", SharerName = transfer.OwnerName, - Remove = () => { if (Instance != null) Instance.RequestDespawn(imageId); }, + Actions = new List + { + new BasisShareableAction + { + Style = BasisShareableActionStyle.Destructive, + Invoke = () => { if (Instance != null) Instance.RequestDespawn(imageId); }, + }, + }, }); }