Skip to content

Commit

Permalink
feat: wearables from builder pagination & name filtering (#6260)
Browse files Browse the repository at this point in the history
  • Loading branch information
lorux0 authored Dec 5, 2024
1 parent eebd86e commit ae68fec
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ public async UniTask<IReadOnlyList<WearableItem>> 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/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface IWearablesCatalogService : IService
UniTask<WearableItem> RequestWearableAsync(string wearableId, CancellationToken ct);
UniTask<WearableItem> RequestWearableFromBuilderAsync(string wearableId, CancellationToken ct);
UniTask<IReadOnlyList<WearableItem>> RequestWearableCollection(IEnumerable<string> collectionIds, CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null);
UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds, CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null);
UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds, CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null, string nameFilter = null, int pageNumber = 1, int pageSize = 5000);

void AddWearablesToCatalog(IEnumerable<WearableItem> wearableItems);
void RemoveWearablesFromCatalog(IEnumerable<string> wearableIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,18 +346,22 @@ public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollection(IEnu
return wearables;
}

public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds,
CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null)
public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds,
CancellationToken cancellationToken, List<WearableItem> collectionBuffer = null, string nameFilter = null,
int pageNumber = 1, int pageSize = 5000)
{
string domain = GetBuilderDomainUrl();
var wearables = collectionBuffer ?? new List<WearableItem>();

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/";
Expand All @@ -366,6 +370,7 @@ public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBui
(WearableCollectionResponseFromBuilder response, bool success) = await lambdasService.GetFromSpecificUrl<WearableCollectionResponseFromBuilder>(
templateUrl, url,
isSigned: true,
// urlEncodedParams: queryParams.Select(pair => (pair.Key, pair.Value)).ToArray(),
urlEncodedParams: queryParams,
cancellationToken: cancellationToken);

Expand All @@ -382,9 +387,10 @@ public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBui
AddWearablesToCatalog(ws);

wearables.AddRange(ws);
totalAmount = response.data.total;
}

return wearables;
return (wearables, totalAmount);
}

public void AddWearablesToCatalog(IEnumerable<WearableItem> wearableItems)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,15 @@ public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollection(IEnu
return await lambdasWearablesCatalogService.RequestWearableCollection(collectionIds, cancellationToken, wearableBuffer);
}

public async UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds,
CancellationToken cancellationToken, List<WearableItem> wearableBuffer = null)
public async UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestWearableCollectionInBuilder(
IEnumerable<string> collectionIds, CancellationToken cancellationToken,
List<WearableItem> 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<WearableItem> wearableItems) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ public UniTask<IReadOnlyList<WearableItem>> RequestWearableCollection(IEnumerabl
CancellationToken cancellationToken, List<WearableItem> wearableBuffer = null) =>
throw new NotImplementedException("Supported by LambdasWearablesCatalogService");

public UniTask<IReadOnlyList<WearableItem>> RequestWearableCollectionInBuilder(IEnumerable<string> collectionIds,
CancellationToken cancellationToken, List<WearableItem> wearableBuffer = null) =>
public UniTask<(IReadOnlyList<WearableItem> wearables, int totalAmount)> RequestWearableCollectionInBuilder(
IEnumerable<string> collectionIds, CancellationToken cancellationToken,
List<WearableItem> collectionBuffer = null, string nameFilter = null, int pageNumber = 1, int pageSize = 5000) =>
throw new NotImplementedException("Supported by LambdasWearablesCatalogService");

private async UniTask<IReadOnlyList<WearableItem>> RequestWearablesByContextAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1121,12 +1121,13 @@ public IEnumerator IncludeCustomWearableCollection()
};

wearablesCatalogService.RequestWearableCollectionInBuilder(Arg.Is<IEnumerable<string>>(i => i.Count() == 1 && i.ElementAt(0) == "builder:collection"),
Arg.Any<CancellationToken>(), Arg.Any<List<WearableItem>>())
Arg.Any<CancellationToken>(), Arg.Any<List<WearableItem>>(),
Arg.Any<string>(), Arg.Any<int>(), Arg.Any<int>())
.Returns(call =>
{
List<WearableItem> wearables = (List<WearableItem>) call[2];
wearables.AddRange(nonPublishedWearableList);
return UniTask.FromResult(nonPublishedWearableList);
return UniTask.FromResult((nonPublishedWearableList, nonPublishedWearableList.Count));
});

wearablesCatalogService.RequestWearableCollection(Arg.Is<IEnumerable<string>>(i => i.Count() == 1 && i.ElementAt(0) == "urn:collection"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;

Expand Down Expand Up @@ -266,29 +267,9 @@ private async UniTask<int> 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();
}
Expand All @@ -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();
Expand All @@ -313,6 +294,53 @@ await UniTask.WhenAll(FetchCustomWearableCollections(customWearablesBuffer, canc
return 0;
}

private async UniTask<int> MergeCustomWearableItems(int page, List<WearableItem> wearables,
int totalAmount, CancellationToken cancellationToken) =>
await MergeToWearableResults(page, wearables, totalAmount, FetchCustomWearableItems, cancellationToken);

private async UniTask<int> MergePublishedWearableCollections(int page, List<WearableItem> wearables, int totalAmount,
CancellationToken cancellationToken) =>
await MergeToWearableResults(page, wearables, totalAmount, FetchPublishedWearableCollections, cancellationToken);

private async UniTask<int> MergeToWearableResults(int page, List<WearableItem> wearables, int totalAmount,
Func<List<WearableItem>, 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<int> MergeBuilderWearableCollections(int page, List<WearableItem> 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<WearableItem> wearables, CancellationToken cancellationToken)
{
IReadOnlyList<string> customItems = await customNftCollectionService.GetConfiguredCustomNftItemsAsync(cancellationToken);
Expand All @@ -337,30 +365,46 @@ private async UniTask FetchCustomWearableItems(ICollection<WearableItem> wearabl
}
}

private async UniTask FetchCustomWearableCollections(
private async UniTask FetchPublishedWearableCollections(
List<WearableItem> wearableBuffer, CancellationToken cancellationToken)
{
IReadOnlyList<string> customCollections =
await customNftCollectionService.GetConfiguredCustomNftCollectionAsync(cancellationToken);

Debug.Log($"FetchCustomWearableCollections: {customCollections}");

HashSet<string> publishedCollections = HashSetPool<string>.Get();
HashSet<string> collectionsInBuilder = HashSetPool<string>.Get();
HashSet<string> collectionsToRequest = HashSetPool<string>.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<string>.Release(publishedCollections);
HashSetPool<string>.Release(collectionsInBuilder);
HashSetPool<string>.Release(collectionsToRequest);
}

private async UniTask<int> FetchBuilderWearableCollections(
int pageNumber, int pageSize,
List<WearableItem> wearableBuffer,
CancellationToken cancellationToken)
{
IReadOnlyList<string> customCollections =
await customNftCollectionService.GetConfiguredCustomNftCollectionAsync(cancellationToken);

HashSet<string> collectionsToRequest = HashSetPool<string>.Get();

foreach (string collectionId in customCollections)
if (!collectionId.StartsWith("urn", StringComparison.OrdinalIgnoreCase))
collectionsToRequest.Add(collectionId);

(IReadOnlyList<WearableItem> _, int totalAmount) = await wearablesCatalogService.RequestWearableCollectionInBuilder(
collectionsToRequest, cancellationToken,
collectionBuffer: wearableBuffer,
nameFilter: nameFilter,
pageNumber: pageNumber, pageSize: pageSize);

HashSetPool<string>.Release(collectionsToRequest);

return totalAmount;
}

private WearableGridItemModel ToWearableGridModel(WearableItem wearable)
Expand All @@ -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.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ public static ServiceLocator CreateMocked()
{
IWearablesCatalogService wearablesCatalogService = Substitute.For<IWearablesCatalogService>();



wearablesCatalogService.RequestWearableCollectionInBuilder(default, default, default)
.ReturnsForAnyArgs(UniTask.FromResult((IReadOnlyList<WearableItem>) Array.Empty<WearableItem>()));
.ReturnsForAnyArgs(UniTask.FromResult(((IReadOnlyList<WearableItem>) Array.Empty<WearableItem>(), 0)));

wearablesCatalogService.RequestWearableFromBuilderAsync(default, default)
.ReturnsForAnyArgs(UniTask.FromResult<WearableItem>(null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static IWearablesCatalogService CreateTestCatalogLocal()
.ReturnsForAnyArgs(UniTask.FromResult<IReadOnlyList<WearableItem>>(Array.Empty<WearableItem>()));

wearablesCatalogService.RequestWearableCollectionInBuilder(default, default, default)
.ReturnsForAnyArgs(UniTask.FromResult<IReadOnlyList<WearableItem>>(Array.Empty<WearableItem>()));
.ReturnsForAnyArgs(UniTask.FromResult(((IReadOnlyList<WearableItem>) Array.Empty<WearableItem>(), 0)));

return wearablesCatalogService;
}
Expand Down

0 comments on commit ae68fec

Please sign in to comment.