diff --git a/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/LambdasEmotesCatalogService.cs b/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/LambdasEmotesCatalogService.cs index e39b1ea4be..c3a493757a 100644 --- a/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/LambdasEmotesCatalogService.cs +++ b/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/LambdasEmotesCatalogService.cs @@ -375,6 +375,8 @@ public async UniTask> RequestEmoteCollectionInBuilde if (!success) throw new Exception($"The request for collection of emotes '{collectionId}' failed!"); + if (response.data?.results == null) continue; + foreach (BuilderWearable bw in response.data.results) { var wearable = bw.ToWearableItem($"{domain}/storage/contents/", diff --git a/unity-renderer/Assets/DCLServices/WearablesCatalogService/IWearablesCatalogService.cs b/unity-renderer/Assets/DCLServices/WearablesCatalogService/IWearablesCatalogService.cs index 38fd46916e..ea1b399a64 100644 --- a/unity-renderer/Assets/DCLServices/WearablesCatalogService/IWearablesCatalogService.cs +++ b/unity-renderer/Assets/DCLServices/WearablesCatalogService/IWearablesCatalogService.cs @@ -26,7 +26,7 @@ public interface IWearablesCatalogService : IService UniTask RequestWearableAsync(string wearableId, CancellationToken ct); UniTask RequestWearableFromBuilderAsync(string wearableId, CancellationToken ct); UniTask> RequestWearableCollection(IEnumerable collectionIds, CancellationToken cancellationToken, List collectionBuffer = null); - UniTask> RequestWearableCollectionInBuilder(IEnumerable collectionIds, CancellationToken cancellationToken, List collectionBuffer = null); + UniTask<(IReadOnlyList wearables, int totalAmount)> RequestWearableCollectionInBuilder(IEnumerable collectionIds, CancellationToken cancellationToken, List collectionBuffer = null, string nameFilter = null, int pageNumber = 1, int pageSize = 5000); void AddWearablesToCatalog(IEnumerable wearableItems); void RemoveWearablesFromCatalog(IEnumerable wearableIds); diff --git a/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs b/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs index 62d8567f03..cc3d765f20 100644 --- a/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs +++ b/unity-renderer/Assets/DCLServices/WearablesCatalogService/LambdasWearablesCatalogService.cs @@ -346,18 +346,22 @@ public async UniTask> RequestWearableCollection(IEnu return wearables; } - public async UniTask> RequestWearableCollectionInBuilder(IEnumerable collectionIds, - CancellationToken cancellationToken, List collectionBuffer = null) + public async UniTask<(IReadOnlyList wearables, int totalAmount)> RequestWearableCollectionInBuilder(IEnumerable collectionIds, + CancellationToken cancellationToken, List collectionBuffer = null, string nameFilter = null, + int pageNumber = 1, int pageSize = 5000) { string domain = GetBuilderDomainUrl(); var wearables = collectionBuffer ?? new List(); var queryParams = new[] { - ("page", "1"), - ("limit", "5000"), + ("page", pageNumber.ToString()), + ("limit", pageSize.ToString()), + ("name", nameFilter), }; + var totalAmount = 0; + foreach (string collectionId in collectionIds) { var url = $"{domain}/collections/{collectionId}/items/"; @@ -366,6 +370,7 @@ public async UniTask> RequestWearableCollectionInBui (WearableCollectionResponseFromBuilder response, bool success) = await lambdasService.GetFromSpecificUrl( templateUrl, url, isSigned: true, + // urlEncodedParams: queryParams.Select(pair => (pair.Key, pair.Value)).ToArray(), urlEncodedParams: queryParams, cancellationToken: cancellationToken); @@ -382,9 +387,10 @@ public async UniTask> RequestWearableCollectionInBui AddWearablesToCatalog(ws); wearables.AddRange(ws); + totalAmount = response.data.total; } - return wearables; + return (wearables, totalAmount); } public void AddWearablesToCatalog(IEnumerable wearableItems) diff --git a/unity-renderer/Assets/DCLServices/WearablesCatalogService/WearablesCatalogServiceProxy.cs b/unity-renderer/Assets/DCLServices/WearablesCatalogService/WearablesCatalogServiceProxy.cs index acc61b1db4..f899122bf0 100644 --- a/unity-renderer/Assets/DCLServices/WearablesCatalogService/WearablesCatalogServiceProxy.cs +++ b/unity-renderer/Assets/DCLServices/WearablesCatalogService/WearablesCatalogServiceProxy.cs @@ -124,13 +124,15 @@ public async UniTask> RequestWearableCollection(IEnu return await lambdasWearablesCatalogService.RequestWearableCollection(collectionIds, cancellationToken, wearableBuffer); } - public async UniTask> RequestWearableCollectionInBuilder(IEnumerable collectionIds, - CancellationToken cancellationToken, List wearableBuffer = null) + public async UniTask<(IReadOnlyList wearables, int totalAmount)> RequestWearableCollectionInBuilder( + IEnumerable collectionIds, CancellationToken cancellationToken, + List collectionBuffer = null, string nameFilter = null, int pageNumber = 1, int pageSize = 5000) { if (!isInitialized) await UniTask.WaitUntil(() => isInitialized, cancellationToken: cancellationToken); - return await lambdasWearablesCatalogService.RequestWearableCollectionInBuilder(collectionIds, cancellationToken, wearableBuffer); + return await lambdasWearablesCatalogService.RequestWearableCollectionInBuilder(collectionIds, cancellationToken, + collectionBuffer, nameFilter, pageNumber, pageSize); } public void AddWearablesToCatalog(IEnumerable wearableItems) => diff --git a/unity-renderer/Assets/DCLServices/WearablesCatalogService/WebInterfaceWearablesCatalogService.cs b/unity-renderer/Assets/DCLServices/WearablesCatalogService/WebInterfaceWearablesCatalogService.cs index e86f7b51a9..8d9eed58c3 100644 --- a/unity-renderer/Assets/DCLServices/WearablesCatalogService/WebInterfaceWearablesCatalogService.cs +++ b/unity-renderer/Assets/DCLServices/WearablesCatalogService/WebInterfaceWearablesCatalogService.cs @@ -130,8 +130,9 @@ public UniTask> RequestWearableCollection(IEnumerabl CancellationToken cancellationToken, List wearableBuffer = null) => throw new NotImplementedException("Supported by LambdasWearablesCatalogService"); - public UniTask> RequestWearableCollectionInBuilder(IEnumerable collectionIds, - CancellationToken cancellationToken, List wearableBuffer = null) => + public UniTask<(IReadOnlyList wearables, int totalAmount)> RequestWearableCollectionInBuilder( + IEnumerable collectionIds, CancellationToken cancellationToken, + List collectionBuffer = null, string nameFilter = null, int pageNumber = 1, int pageSize = 5000) => throw new NotImplementedException("Supported by LambdasWearablesCatalogService"); private async UniTask> RequestWearablesByContextAsync( diff --git a/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/Tests/WearableGridControllerShould.cs b/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/Tests/WearableGridControllerShould.cs index db2a7134f4..d1581152e8 100644 --- a/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/Tests/WearableGridControllerShould.cs +++ b/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/Tests/WearableGridControllerShould.cs @@ -1121,12 +1121,13 @@ public IEnumerator IncludeCustomWearableCollection() }; wearablesCatalogService.RequestWearableCollectionInBuilder(Arg.Is>(i => i.Count() == 1 && i.ElementAt(0) == "builder:collection"), - Arg.Any(), Arg.Any>()) + Arg.Any(), Arg.Any>(), + Arg.Any(), Arg.Any(), Arg.Any()) .Returns(call => { List wearables = (List) call[2]; wearables.AddRange(nonPublishedWearableList); - return UniTask.FromResult(nonPublishedWearableList); + return UniTask.FromResult((nonPublishedWearableList, nonPublishedWearableList.Count)); }); wearablesCatalogService.RequestWearableCollection(Arg.Is>(i => i.Count() == 1 && i.ElementAt(0) == "urn:collection"), diff --git a/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/WearableGridController.cs b/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/WearableGridController.cs index 8d70627e5c..6f3a744f65 100644 --- a/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/WearableGridController.cs +++ b/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/WearableGridController.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using UnityEngine; using UnityEngine.Pool; @@ -266,29 +267,9 @@ private async UniTask RequestWearablesAndShowThem(int page, CancellationTok try { - int ownedWearablesCount = wearables.Count; - int ownedWearablesTotalAmount = totalAmount; - - customWearablesBuffer.Clear(); - - await UniTask.WhenAll(FetchCustomWearableCollections(customWearablesBuffer, cancellationToken), - FetchCustomWearableItems(customWearablesBuffer, cancellationToken)); - - int customWearablesCount = customWearablesBuffer.Count; - totalAmount += customWearablesCount; - - if (ownedWearablesCount < PAGE_SIZE && customWearablesCount > 0) - { - int ownedWearablesStartingPage = (ownedWearablesTotalAmount / PAGE_SIZE) + 1; - int customWearablesOffsetPage = page - ownedWearablesStartingPage; - int skip = customWearablesOffsetPage * PAGE_SIZE; - // Fill the page considering the existing owned wearables - int count = PAGE_SIZE - ownedWearablesCount; - int until = skip + count; - - for (int i = skip; i < customWearablesBuffer.Count && i < until; i++) - wearables.Add(customWearablesBuffer[i]); - } + totalAmount += await MergePublishedWearableCollections(page, wearables, totalAmount, cancellationToken); + totalAmount += await MergeBuilderWearableCollections(page, wearables, totalAmount, cancellationToken); + totalAmount += await MergeCustomWearableItems(page, wearables, totalAmount, cancellationToken); customWearablesBuffer.Clear(); } @@ -299,7 +280,7 @@ await UniTask.WhenAll(FetchCustomWearableCollections(customWearablesBuffer, canc currentWearables = wearables.Select(ToWearableGridModel) .ToDictionary(item => ExtendedUrnParser.GetShortenedUrn(item.WearableId), model => model); - view.SetWearablePages(page, Mathf.CeilToInt((float) totalAmount / PAGE_SIZE)); + view.SetWearablePages(page, Mathf.CeilToInt((float)totalAmount / PAGE_SIZE)); // TODO: mark the wearables to be disposed if no references left view.ClearWearables(); @@ -313,6 +294,53 @@ await UniTask.WhenAll(FetchCustomWearableCollections(customWearablesBuffer, canc return 0; } + private async UniTask MergeCustomWearableItems(int page, List wearables, + int totalAmount, CancellationToken cancellationToken) => + await MergeToWearableResults(page, wearables, totalAmount, FetchCustomWearableItems, cancellationToken); + + private async UniTask MergePublishedWearableCollections(int page, List wearables, int totalAmount, + CancellationToken cancellationToken) => + await MergeToWearableResults(page, wearables, totalAmount, FetchPublishedWearableCollections, cancellationToken); + + private async UniTask MergeToWearableResults(int page, List wearables, int totalAmount, + Func, CancellationToken, UniTask> fetchOperation, + CancellationToken cancellationToken) + { + int startingPage = (totalAmount / PAGE_SIZE) + 1; + int pageOffset = page - startingPage; + int pageSize = PAGE_SIZE - wearables.Count; + int skip = pageOffset * PAGE_SIZE; + int until = skip + pageSize; + + customWearablesBuffer.Clear(); + + await fetchOperation.Invoke(customWearablesBuffer, cancellationToken); + + if (skip < 0) return customWearablesBuffer.Count; + + for (int i = skip; i < customWearablesBuffer.Count && i < until; i++) + wearables.Add(customWearablesBuffer[i]); + + return customWearablesBuffer.Count; + } + + private async UniTask MergeBuilderWearableCollections(int page, List wearables, int totalAmount, CancellationToken cancellationToken) + { + int startingPage = (totalAmount / PAGE_SIZE) + 1; + int pageOffset = Mathf.Max(1, page - startingPage); + int pageSize = PAGE_SIZE - wearables.Count; + + customWearablesBuffer.Clear(); + + int collectionsWearableCount = await FetchBuilderWearableCollections(pageOffset, PAGE_SIZE, + customWearablesBuffer, cancellationToken); + + for (var i = 0; i < pageSize && i < customWearablesBuffer.Count; i++) + wearables.Add(customWearablesBuffer[i]); + + return collectionsWearableCount; + } + private async UniTask FetchCustomWearableItems(ICollection wearables, CancellationToken cancellationToken) { IReadOnlyList customItems = await customNftCollectionService.GetConfiguredCustomNftItemsAsync(cancellationToken); @@ -337,30 +365,46 @@ private async UniTask FetchCustomWearableItems(ICollection wearabl } } - private async UniTask FetchCustomWearableCollections( + private async UniTask FetchPublishedWearableCollections( List wearableBuffer, CancellationToken cancellationToken) { IReadOnlyList customCollections = await customNftCollectionService.GetConfiguredCustomNftCollectionAsync(cancellationToken); - Debug.Log($"FetchCustomWearableCollections: {customCollections}"); - - HashSet publishedCollections = HashSetPool.Get(); - HashSet collectionsInBuilder = HashSetPool.Get(); + HashSet collectionsToRequest = HashSetPool.Get(); foreach (string collectionId in customCollections) - { if (collectionId.StartsWith("urn", StringComparison.OrdinalIgnoreCase)) - publishedCollections.Add(collectionId); - else - collectionsInBuilder.Add(collectionId); - } + collectionsToRequest.Add(collectionId); - await UniTask.WhenAll(wearablesCatalogService.RequestWearableCollection(publishedCollections, cancellationToken, wearableBuffer), - wearablesCatalogService.RequestWearableCollectionInBuilder(collectionsInBuilder, cancellationToken, wearableBuffer)); + await wearablesCatalogService.RequestWearableCollection(collectionsToRequest, cancellationToken, wearableBuffer); - HashSetPool.Release(publishedCollections); - HashSetPool.Release(collectionsInBuilder); + HashSetPool.Release(collectionsToRequest); + } + + private async UniTask FetchBuilderWearableCollections( + int pageNumber, int pageSize, + List wearableBuffer, + CancellationToken cancellationToken) + { + IReadOnlyList customCollections = + await customNftCollectionService.GetConfiguredCustomNftCollectionAsync(cancellationToken); + + HashSet collectionsToRequest = HashSetPool.Get(); + + foreach (string collectionId in customCollections) + if (!collectionId.StartsWith("urn", StringComparison.OrdinalIgnoreCase)) + collectionsToRequest.Add(collectionId); + + (IReadOnlyList _, int totalAmount) = await wearablesCatalogService.RequestWearableCollectionInBuilder( + collectionsToRequest, cancellationToken, + collectionBuffer: wearableBuffer, + nameFilter: nameFilter, + pageNumber: pageNumber, pageSize: pageSize); + + HashSetPool.Release(collectionsToRequest); + + return totalAmount; } private WearableGridItemModel ToWearableGridModel(WearableItem wearable) @@ -369,8 +413,7 @@ private WearableGridItemModel ToWearableGridModel(WearableItem wearable) if (string.IsNullOrEmpty(wearable.rarity)) rarity = NftRarity.None; - else - if (!Enum.TryParse(wearable.rarity, true, out NftRarity result)) + else if (!Enum.TryParse(wearable.rarity, true, out NftRarity result)) { rarity = NftRarity.None; Debug.LogWarning($"Could not parse the rarity \"{wearable.rarity}\" of the wearable '{wearable.id}'. Fallback to common."); diff --git a/unity-renderer/Assets/Scripts/MainScripts/DCL/Environment/TestEnvironment/ServiceLocatorTestFactory.cs b/unity-renderer/Assets/Scripts/MainScripts/DCL/Environment/TestEnvironment/ServiceLocatorTestFactory.cs index 77ceb35f6b..64ef198143 100644 --- a/unity-renderer/Assets/Scripts/MainScripts/DCL/Environment/TestEnvironment/ServiceLocatorTestFactory.cs +++ b/unity-renderer/Assets/Scripts/MainScripts/DCL/Environment/TestEnvironment/ServiceLocatorTestFactory.cs @@ -39,8 +39,10 @@ public static ServiceLocator CreateMocked() { IWearablesCatalogService wearablesCatalogService = Substitute.For(); + + wearablesCatalogService.RequestWearableCollectionInBuilder(default, default, default) - .ReturnsForAnyArgs(UniTask.FromResult((IReadOnlyList) Array.Empty())); + .ReturnsForAnyArgs(UniTask.FromResult(((IReadOnlyList) Array.Empty(), 0))); wearablesCatalogService.RequestWearableFromBuilderAsync(default, default) .ReturnsForAnyArgs(UniTask.FromResult(null)); diff --git a/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/Tests/Helpers/AvatarAssetsTestHelpers.cs b/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/Tests/Helpers/AvatarAssetsTestHelpers.cs index 8d35a45909..ae2877a6c1 100644 --- a/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/Tests/Helpers/AvatarAssetsTestHelpers.cs +++ b/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/Tests/Helpers/AvatarAssetsTestHelpers.cs @@ -91,7 +91,7 @@ public static IWearablesCatalogService CreateTestCatalogLocal() .ReturnsForAnyArgs(UniTask.FromResult>(Array.Empty())); wearablesCatalogService.RequestWearableCollectionInBuilder(default, default, default) - .ReturnsForAnyArgs(UniTask.FromResult>(Array.Empty())); + .ReturnsForAnyArgs(UniTask.FromResult(((IReadOnlyList) Array.Empty(), 0))); return wearablesCatalogService; }