From 813a52c6ad234b0c9537ba2b51b098c92771e68f Mon Sep 17 00:00:00 2001 From: davidejensen Date: Fri, 17 Jul 2026 08:53:59 +0200 Subject: [PATCH 1/3] feat: add deep link param to force open backpack on client open --- .../Global/AppArgs/AppArgsFlags.cs | 2 +- .../Global/Dynamic/DynamicWorldContainer.cs | 2 +- .../PluginSystem/Global/ExplorePanelPlugin.cs | 15 ++++++++++ .../DeepLinkHandleImplementation.cs | 28 ++++++++++++++++++- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs index 2e759b3116c..ff313005d81 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs @@ -31,7 +31,7 @@ public static class AppArgsFlags /// The community received here (by its ID) will be shown through a notification inviting the user to click on it and open that community card. /// public const string COMMUNITY = "community"; - + public const string FORCE_OPEN_BACKPACK = "force-open-backpack"; public const string FORCED_EMOTES = "self-force-emotes"; public const string SELF_PREVIEW_EMOTES = "self-preview-emotes"; public const string SELF_PREVIEW_WEARABLES = "self-preview-wearables"; diff --git a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs index c6a5d3b78a7..40a9eba6d8b 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs @@ -450,7 +450,7 @@ await MapRendererContainer // Local scene development scenes are excluded from deeplink runtime handling logic if (!appArgs.HasFlag(AppArgsFlags.LOCAL_SCENE)) { - var deepLinkHandleImplementation = new DeepLinkHandle(dynamicWorldParams.StartParcel, chatContainer.ChatTeleporter, ct, communitiesDataService); + var deepLinkHandleImplementation = new DeepLinkHandle(dynamicWorldParams.StartParcel, chatContainer.ChatTeleporter, ct, communitiesDataService, uiShellContainer.MvcManager, staticContainer.LoadingStatus); deepLinkHandleImplementation.StartListenForDeepLinksAsync(ct).Forget(); } diff --git a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs index c85416b1861..2725d4eeccb 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs @@ -17,6 +17,7 @@ using DCL.Backpack.BackpackBus; using DCL.Browser; using DCL.CharacterPreview; +using DCL.Diagnostics; using DCL.ExplorePanel; using DCL.Input; using DCL.Landscape.Settings; @@ -626,6 +627,20 @@ public async UniTask InitializeAsync(ExplorePanelSettings settings, Cancellation if (isCommunitiesFeatureEnabled) dclInput.Shortcuts.Communities.performed += OnInputShortcutsCommunitiesPerformed; + + if (appArgs.HasFlag(AppArgsFlags.FORCE_OPEN_BACKPACK)) + ForceOpenBackpackOnLandingAsync(ct).Forget(); + } + + private async UniTaskVoid ForceOpenBackpackOnLandingAsync(CancellationToken ct) + { + try + { + await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed, cancellationToken: ct); + await mvcManager.ShowAsync(ExplorePanelController.IssueCommand(new ExplorePanelParameter(ExploreSections.Backpack)), ct); + } + catch (OperationCanceledException) { } + catch (Exception e) { ReportHub.LogException(e, ReportCategory.UI); } } private async UniTask> InitializePlaceElementsPoolAsync(SearchResultPanelView view, CancellationToken ct) diff --git a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs index a9bdc4b78dc..21db09617bb 100644 --- a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs +++ b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs @@ -2,9 +2,14 @@ using Cysharp.Threading.Tasks; using DCL.Chat.Commands; using DCL.Communities; +using DCL.Diagnostics; +using DCL.ExplorePanel; using DCL.RealmNavigation; +using DCL.UI; using DCL.Utility.Types; using Global.AppArgs; +using MVC; +using System; using System.Threading; using UnityEngine; @@ -16,13 +21,17 @@ public class DeepLinkHandle : IDeepLinkHandle private readonly ChatTeleporter chatTeleporter; private readonly CancellationToken token; private readonly CommunityDataService communityDataService; + private readonly IMVCManager mvcManager; + private readonly ILoadingStatus loadingStatus; - public DeepLinkHandle(StartParcel startParcel, ChatTeleporter chatTeleporter, CancellationToken token, CommunityDataService communityDataService) + public DeepLinkHandle(StartParcel startParcel, ChatTeleporter chatTeleporter, CancellationToken token, CommunityDataService communityDataService, IMVCManager mvcManager, ILoadingStatus loadingStatus) { this.startParcel = startParcel; this.chatTeleporter = chatTeleporter; this.token = token; this.communityDataService = communityDataService; + this.mvcManager = mvcManager; + this.loadingStatus = loadingStatus; } public string Name => "Real Implementation"; @@ -62,9 +71,26 @@ public Result HandleDeepLink(DeepLink deeplink) result = Result.SuccessResult(); } + if (deeplink.ValueOf(AppArgsFlags.FORCE_OPEN_BACKPACK) != null) + { + OpenBackpackWhenLandedAsync().Forget(); + result = Result.SuccessResult(); + } + return result; } + private async UniTaskVoid OpenBackpackWhenLandedAsync() + { + try + { + await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed, cancellationToken: token); + await mvcManager.ShowAsync(ExplorePanelController.IssueCommand(new ExplorePanelParameter(ExploreSections.Backpack)), token); + } + catch (OperationCanceledException) { } + catch (Exception e) { ReportHub.LogException(e, ReportCategory.UI); } + } + private static URLDomain? RealmFrom(DeepLink deepLink) { string? rawRealm = deepLink.ValueOf(AppArgsFlags.REALM); From c3c5d4f079822b03570e8dabf1d31fa557ad7101 Mon Sep 17 00:00:00 2001 From: davidejensen Date: Fri, 17 Jul 2026 10:37:49 +0200 Subject: [PATCH 2/3] fixed credits modal overlap and added apparg to docs --- .../Infrastructure/Global/AppArgs/AppArgsFlags.cs | 2 ++ .../MarketplaceCreditsMenuController.cs | 5 +++-- docs/app-arguments.md | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs index ff313005d81..b10cd10e6f5 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs @@ -31,7 +31,9 @@ public static class AppArgsFlags /// The community received here (by its ID) will be shown through a notification inviting the user to click on it and open that community card. /// public const string COMMUNITY = "community"; + public const string FORCE_OPEN_BACKPACK = "force-open-backpack"; + public const string FORCED_EMOTES = "self-force-emotes"; public const string SELF_PREVIEW_EMOTES = "self-preview-emotes"; public const string SELF_PREVIEW_WEARABLES = "self-preview-wearables"; diff --git a/Explorer/Assets/DCL/MarketplaceCredits/MarketplaceCreditsMenuController.cs b/Explorer/Assets/DCL/MarketplaceCredits/MarketplaceCreditsMenuController.cs index e5d00d1d128..8a3a9f6e1b7 100644 --- a/Explorer/Assets/DCL/MarketplaceCredits/MarketplaceCreditsMenuController.cs +++ b/Explorer/Assets/DCL/MarketplaceCredits/MarketplaceCreditsMenuController.cs @@ -335,8 +335,9 @@ private async UniTaskVoid CheckForSidebarButtonStateAsync(CancellationToken ct) if (!creditsProgramProgressResponse.HasUserStartedProgram() || TrySetAsShownThisWeek(creditsProgramProgressResponse)) { - // Open the Marketplace Credits panel by default when the user didn't start the program and has landed in Genesis City. - await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed && realmData.IsGenesis(), cancellationToken: ct); + // Open the Marketplace Credits panel by default when the user didn't start the program and has landed in Genesis City, + // holding off while any other popup or fullscreen view is on screen (e.g. the backpack opened via force-open-backpack). + await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed && realmData.IsGenesis() && !mvcManager.IsAnyModalViewShowing(), cancellationToken: ct); await mvcManager.ShowAsync(MarketplaceCreditsMenuController.IssueCommand(new Params(isOpenedFromNotification: false)), ct); } diff --git a/docs/app-arguments.md b/docs/app-arguments.md index 139655e3de4..7f8f6938688 100644 --- a/docs/app-arguments.md +++ b/docs/app-arguments.md @@ -252,6 +252,21 @@ More detailed instructions on how to test can be found in the description of rel --- +## UI Flags + +### `force-open-backpack` +**Description:** Automatically opens the Backpack panel once the user lands in the world (after authentication and the loading screen). Presence-only: any value triggers it. Also works when a deep link reaches an already-running client — the Backpack opens immediately (or once loading completes). + +**Usage:** +```bash +--force-open-backpack +``` +``` +decentraland://?force-open-backpack=true +``` + +--- + ## Performance & Caching Flags ### `disable-disk-cache` From f3e32a03c0d3155d387e377a8022e799094affc1 Mon Sep 17 00:00:00 2001 From: davidejensen Date: Fri, 17 Jul 2026 11:01:52 +0200 Subject: [PATCH 3/3] code improvement --- .../ExplorePanel/BackpackDeepLinkOpener.cs | 24 +++++++++++++++++++ .../BackpackDeepLinkOpener.cs.meta | 11 +++++++++ .../Global/AppArgs/AppArgsFlags.cs | 2 +- .../PluginSystem/Global/ExplorePanelPlugin.cs | 14 +---------- .../DeepLinkHandleImplementation.cs | 16 +------------ 5 files changed, 38 insertions(+), 29 deletions(-) create mode 100644 Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs create mode 100644 Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs.meta diff --git a/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs b/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs new file mode 100644 index 00000000000..d6a74d06c12 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs @@ -0,0 +1,24 @@ +using Cysharp.Threading.Tasks; +using DCL.Diagnostics; +using DCL.RealmNavigation; +using DCL.UI; +using MVC; +using System; +using System.Threading; + +namespace DCL.ExplorePanel +{ + public static class BackpackDeepLinkOpener + { + public static async UniTaskVoid OpenBackpackWhenLandedAsync(IMVCManager mvcManager, ILoadingStatus loadingStatus, CancellationToken ct) + { + try + { + await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed, cancellationToken: ct); + await mvcManager.ShowAsync(ExplorePanelController.IssueCommand(new ExplorePanelParameter(ExploreSections.Backpack)), ct); + } + catch (OperationCanceledException) { } + catch (Exception e) { ReportHub.LogException(e, ReportCategory.UI); } + } + } +} diff --git a/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs.meta b/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs.meta new file mode 100644 index 00000000000..dd607b8b71b --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/BackpackDeepLinkOpener.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd49ae39a64a4ea5ab42d313e801cead +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs index b10cd10e6f5..0adfefe18e1 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/AppArgs/AppArgsFlags.cs @@ -33,7 +33,7 @@ public static class AppArgsFlags public const string COMMUNITY = "community"; public const string FORCE_OPEN_BACKPACK = "force-open-backpack"; - + public const string FORCED_EMOTES = "self-force-emotes"; public const string SELF_PREVIEW_EMOTES = "self-preview-emotes"; public const string SELF_PREVIEW_WEARABLES = "self-preview-wearables"; diff --git a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs index 2725d4eeccb..17c7105f38d 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs @@ -17,7 +17,6 @@ using DCL.Backpack.BackpackBus; using DCL.Browser; using DCL.CharacterPreview; -using DCL.Diagnostics; using DCL.ExplorePanel; using DCL.Input; using DCL.Landscape.Settings; @@ -629,18 +628,7 @@ public async UniTask InitializeAsync(ExplorePanelSettings settings, Cancellation dclInput.Shortcuts.Communities.performed += OnInputShortcutsCommunitiesPerformed; if (appArgs.HasFlag(AppArgsFlags.FORCE_OPEN_BACKPACK)) - ForceOpenBackpackOnLandingAsync(ct).Forget(); - } - - private async UniTaskVoid ForceOpenBackpackOnLandingAsync(CancellationToken ct) - { - try - { - await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed, cancellationToken: ct); - await mvcManager.ShowAsync(ExplorePanelController.IssueCommand(new ExplorePanelParameter(ExploreSections.Backpack)), ct); - } - catch (OperationCanceledException) { } - catch (Exception e) { ReportHub.LogException(e, ReportCategory.UI); } + BackpackDeepLinkOpener.OpenBackpackWhenLandedAsync(mvcManager, loadingStatus, ct).Forget(); } private async UniTask> InitializePlaceElementsPoolAsync(SearchResultPanelView view, CancellationToken ct) diff --git a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs index 21db09617bb..127e18c0c0d 100644 --- a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs +++ b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkHandleImplementation.cs @@ -2,14 +2,11 @@ using Cysharp.Threading.Tasks; using DCL.Chat.Commands; using DCL.Communities; -using DCL.Diagnostics; using DCL.ExplorePanel; using DCL.RealmNavigation; -using DCL.UI; using DCL.Utility.Types; using Global.AppArgs; using MVC; -using System; using System.Threading; using UnityEngine; @@ -73,24 +70,13 @@ public Result HandleDeepLink(DeepLink deeplink) if (deeplink.ValueOf(AppArgsFlags.FORCE_OPEN_BACKPACK) != null) { - OpenBackpackWhenLandedAsync().Forget(); + BackpackDeepLinkOpener.OpenBackpackWhenLandedAsync(mvcManager, loadingStatus, token).Forget(); result = Result.SuccessResult(); } return result; } - private async UniTaskVoid OpenBackpackWhenLandedAsync() - { - try - { - await UniTask.WaitUntil(() => loadingStatus.CurrentStage.Value == LoadingStatus.LoadingStage.Completed, cancellationToken: token); - await mvcManager.ShowAsync(ExplorePanelController.IssueCommand(new ExplorePanelParameter(ExploreSections.Backpack)), token); - } - catch (OperationCanceledException) { } - catch (Exception e) { ReportHub.LogException(e, ReportCategory.UI); } - } - private static URLDomain? RealmFrom(DeepLink deepLink) { string? rawRealm = deepLink.ValueOf(AppArgsFlags.REALM);