diff --git a/Src/NFTWallet/Assets/Code/NFTWallet/Windows/MyCollectionWindow.cs b/Src/NFTWallet/Assets/Code/NFTWallet/Windows/MyCollectionWindow.cs index f696921..2c80259 100644 --- a/Src/NFTWallet/Assets/Code/NFTWallet/Windows/MyCollectionWindow.cs +++ b/Src/NFTWallet/Assets/Code/NFTWallet/Windows/MyCollectionWindow.cs @@ -95,16 +95,12 @@ private async UniTask showAsync(bool hideOtherWindows = true) int rows = (int) Math.Ceiling(((decimal) SpawnedItems.Count / 3)); contentTransform.sizeDelta = new Vector2(contentTransform.sizeDelta.x, (cellSize + spacing) * rows); - // Load images - List> loadTasks = new List>(); - Debug.Log("Loading json metadata for " + SpawnedItems.Count + " items."); this.cancellation.Token.ThrowIfCancellationRequested(); // Preload json - Dictionary jsonUriToJson = - await this.LoadJsonFilesAsync(SpawnedItems.Select(x => x.NFTUri).Where(x => x.EndsWith(".json")), this.cancellation.Token); + Dictionary jsonUriToJson = await this.LoadJsonFilesAsync(SpawnedItems.Select(x => x.NFTUri).Where(x => x.EndsWith(".json")), this.cancellation.Token); Debug.Log("Loading textures"); this.StatusText.text = "loading textures..."; @@ -115,23 +111,37 @@ private async UniTask showAsync(bool hideOtherWindows = true) this.StatusText.text = "Requesting " + SpawnedItems.Count + " textures"; + // Load images + List> loadTexturesTasks = new List>(); + for (int i = 0; i < SpawnedItems.Count; i++) { this.cancellation.Token.ThrowIfCancellationRequested(); try { - string uri = SpawnedItems[i].NFTUri; + string nftUri = SpawnedItems[i].NFTUri; string imageUri; bool animationAvailable = false; string animationUrl = null; - string json = jsonUriToJson[uri]; + string json = jsonUriToJson[nftUri]; var settings = new JsonSerializerSettings(); settings.DateFormatString = "YYYY-MM-DD"; settings.ContractResolver = new CustomMetadataResolver(); - NFTMetadataModel model = JsonConvert.DeserializeObject(json, settings); + + NFTMetadataModel model = null; + + if (json == null) + { + model = new NFTMetadataModel(); + model.Name = model.Description = model.Image = "[No metadata]"; + } + else + { + model = JsonConvert.DeserializeObject(json, settings); + } metadataModels.Add(model); @@ -157,11 +167,11 @@ private async UniTask showAsync(bool hideOtherWindows = true) !string.IsNullOrEmpty(imageUri); if (image) { - UniTask loadTask = this.GetRemoteTextureAsync(imageUri, this.cancellation.Token); - loadTasks.Add(loadTask); + UniTask loadTask = this.GetRemoteTextureAsync(nftUri, imageUri, this.cancellation.Token); + loadTexturesTasks.Add(loadTask); } else - loadTasks.Add(GetNullTextureAsync()); + loadTexturesTasks.Add(GetNullTextureAsync(nftUri)); if (animationAvailable) { @@ -176,7 +186,7 @@ await NFTWalletWindowManager.Instance.AnimationWindow.ShowPopupAsync(animationUr } catch (Exception e) { - Debug.Log(e.ToString()); + Debug.LogError(e.ToString()); } } @@ -186,29 +196,31 @@ await NFTWalletWindowManager.Instance.AnimationWindow.ShowPopupAsync(animationUr // Convert animations. Fire and forget PreconvertAnimations(animationsToConvert); - Texture2D[] loaded = await UniTask.WhenAll(loadTasks); + NFTUriToTexture[] loadedTextures = await UniTask.WhenAll(loadTexturesTasks); this.StatusText.text = "creating sprites..."; - for (int i = 0; i < loaded.Length; i++) + for (int i = 0; i < loadedTextures.Length; i++) { this.cancellation.Token.ThrowIfCancellationRequested(); - Texture2D texture = loaded[i]; + Texture2D texture = loadedTextures[i].Texture; + + CollectionItem targetItem = SpawnedItems.FirstOrDefault(x => x.NFTUri == loadedTextures[i].NFTUri); - SpawnedItems[i].ImageLoadedOrAttemptedToLoad = true; + targetItem.ImageLoadedOrAttemptedToLoad = true; if (texture == null) { - if (!SpawnedItems[i].DisplayAnimationButton.gameObject.activeSelf) - SpawnedItems[i].NFTImage.sprite = ImageNotAvailableSprite; + if (!targetItem.DisplayAnimationButton.gameObject.activeSelf) + targetItem.NFTImage.sprite = ImageNotAvailableSprite; continue; } Sprite sprite = Sprite.Create(texture, new Rect(0.0f, 0.0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100.0f); - SpawnedItems[i].NFTImage.sprite = sprite; + targetItem.NFTImage.sprite = sprite; } this.StatusText.text = "Collection loaded"; @@ -355,17 +367,21 @@ private async Task> LoadJsonFilesAsync(IEnumerable GetRemoteTextureAsync(string url, CancellationToken token) + private async UniTask GetRemoteTextureAsync(string NFTUri, string url, CancellationToken token, int timeoutSeconds = 15) { Texture2D texture; + int msPassed = 0; + using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url)) { - var asyncOp = www.SendWebRequest(); + UnityWebRequestAsyncOperation asyncOp = www.SendWebRequest(); + int waitTime = 1000 / 30; - while (asyncOp.isDone == false) + while (asyncOp.isDone == false && msPassed < timeoutSeconds * 1000) { - await Task.Delay(1000 / 30);//30 hertz + await Task.Delay(waitTime);//30 hertz + msPassed += waitTime; token.ThrowIfCancellationRequested(); } @@ -373,7 +389,7 @@ private async UniTask GetRemoteTextureAsync(string url, CancellationT if (www.result != UnityWebRequest.Result.Success) { UnityEngine.Debug.Log($"{www.error}, URL:{www.url}"); - return null; + return new NFTUriToTexture(NFTUri, null); } texture = DownloadHandlerTexture.GetContent(www); @@ -385,10 +401,10 @@ private async UniTask GetRemoteTextureAsync(string url, CancellationT Object.Destroy(texture); - return resized; + return new NFTUriToTexture(NFTUri, resized); } - return texture; + return new NFTUriToTexture(NFTUri, texture); } private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) @@ -406,9 +422,9 @@ private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) return nTex; } - private async UniTask GetNullTextureAsync() + private async UniTask GetNullTextureAsync(string nftUri) { - return null; + return new NFTUriToTexture(nftUri, null); } private void PreconvertAnimations(List externalLinks) @@ -419,4 +435,17 @@ private void PreconvertAnimations(List externalLinks) await MediaConverterManager.Instance.Client.RequestLinksConversionAsync(externalLinks); }); } + + private class NFTUriToTexture + { + public NFTUriToTexture(string NFTUri, Texture2D texture) + { + this.NFTUri = NFTUri; + this.Texture = texture; + } + + public string NFTUri { get; set; } + + public Texture2D Texture { get; set; } + } } diff --git a/Src/NFTWallet/Assets/Scenes/NFTWallet.unity b/Src/NFTWallet/Assets/Scenes/NFTWallet.unity index 38b53a5..b53bf55 100644 --- a/Src/NFTWallet/Assets/Scenes/NFTWallet.unity +++ b/Src/NFTWallet/Assets/Scenes/NFTWallet.unity @@ -13054,7 +13054,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.07848487, y: 0.85810846} m_AnchorMax: {x: 0.95466226, y: 0.9006627} - m_AnchoredPosition: {x: -0.63012695, y: 1.0400085} + m_AnchoredPosition: {x: -0.6298828, y: 1.0400085} m_SizeDelta: {x: 268.74, y: 26.299988} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &377111036 @@ -13212,7 +13212,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.07848487, y: 0.6665543} m_AnchorMax: {x: 0.8095541, y: 0.687229} - m_AnchoredPosition: {x: 3.579834, y: 1.6500244} + m_AnchoredPosition: {x: 3.5800781, y: 1.6500244} m_SizeDelta: {x: 217.53003, y: 12.190002} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &384708593 @@ -17329,7 +17329,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.89900005} m_AnchorMax: {x: 0.43800002, y: 0.9588151} - m_AnchoredPosition: {x: 5.30896, y: 0.5628891} + m_AnchoredPosition: {x: 5.308838, y: 0.5628891} m_SizeDelta: {x: 146.97528, y: 14.417908} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &521928120 @@ -32860,7 +32860,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.89900005} m_AnchorMax: {x: 0.43800002, y: 0.9588151} - m_AnchoredPosition: {x: 5.30896, y: 0.5628891} + m_AnchoredPosition: {x: 5.308838, y: 0.5628891} m_SizeDelta: {x: 146.97528, y: 14.417908} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &951974116 @@ -38841,7 +38841,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.889185} m_AnchorMax: {x: 0.16138412, y: 0.89900005} - m_AnchoredPosition: {x: 2.7002563, y: -0.39134216} + m_AnchoredPosition: {x: 2.7003174, y: -0.39134216} m_SizeDelta: {x: 0.9552307, y: 0.027526855} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1169984517 @@ -56178,7 +56178,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.889185} m_AnchorMax: {x: 0.16138412, y: 0.89900005} - m_AnchoredPosition: {x: 2.7002563, y: -0.39130402} + m_AnchoredPosition: {x: 2.7003174, y: -0.39130402} m_SizeDelta: {x: 0.9552307, y: 0.027526855} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1720043223 @@ -57423,7 +57423,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.8715152, y: 0.7936747} m_AnchorMax: {x: 0.94445467, y: 0.8302169} - m_AnchoredPosition: {x: -1.3999023, y: 0.2999878} + m_AnchoredPosition: {x: -1.3994141, y: 0.2999878} m_SizeDelta: {x: -2.6400146, y: -6.3599854} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1757156913 @@ -59010,7 +59010,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.889185} m_AnchorMax: {x: 0.16138412, y: 0.89900005} - m_AnchoredPosition: {x: 2.7002563, y: 86.38283} + m_AnchoredPosition: {x: 2.7003174, y: 86.38283} m_SizeDelta: {x: 0.9552307, y: -8.014282} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1816157220 @@ -59279,7 +59279,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.89900005} m_AnchorMax: {x: 0.43800002, y: 0.9588151} - m_AnchoredPosition: {x: 5.30896, y: 58.811493} + m_AnchoredPosition: {x: 5.308838, y: 58.811493} m_SizeDelta: {x: 146.97528, y: -34.59088} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1826494356 @@ -64130,7 +64130,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.41700003, y: 0.014308338} m_AnchorMax: {x: 0.54200006, y: 0.07427084} - m_AnchoredPosition: {x: 1.5, y: 3.6000977} + m_AnchoredPosition: {x: 1.5, y: 3.5996094} m_SizeDelta: {x: -6.839966, y: -11.719971} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1960746544 @@ -65248,7 +65248,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.06818183, y: 0.71955425} m_AnchorMax: {x: 0.96687883, y: 0.84045786} - m_AnchoredPosition: {x: -1.2199707, y: -0.76000977} + m_AnchoredPosition: {x: -1.2197266, y: -0.76000977} m_SizeDelta: {x: -2.6799927, y: -6.51001} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1996544025 @@ -68620,10 +68620,10 @@ RectTransform: m_Father: {fileID: 1792828620} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.032662004, y: 0.031000001} - m_AnchorMax: {x: 0.965338, y: 0.1} - m_AnchoredPosition: {x: -1.1820068, y: 0.48901367} - m_SizeDelta: {x: 100.909, y: 10.261} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -243.38, y: 66.28} + m_SizeDelta: {x: 554.21, y: 65.26} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2083897182 MonoBehaviour: @@ -68649,10 +68649,10 @@ MonoBehaviour: m_Font: {fileID: 12800000, guid: 653e2fd78293f4941a834ab753d2da83, type: 3} m_FontSize: 32 m_FontStyle: 0 - m_BestFit: 0 + m_BestFit: 1 m_MinSize: 3 - m_MaxSize: 40 - m_Alignment: 2 + m_MaxSize: 70 + m_Alignment: 5 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 @@ -68972,7 +68972,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.38743892} m_AnchorMax: {x: 0.13263656, y: 0.85810286} - m_AnchoredPosition: {x: 8.809814, y: -2.4100342} + m_AnchoredPosition: {x: 8.810059, y: -2.4100342} m_SizeDelta: {x: -12.66, y: -15.72} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2096601807 @@ -69252,7 +69252,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.047692064, y: 0.889185} m_AnchorMax: {x: 0.16138412, y: 0.89900005} - m_AnchoredPosition: {x: 2.7002563, y: 81.2269} + m_AnchoredPosition: {x: 2.7003174, y: 81.2269} m_SizeDelta: {x: 0.9552307, y: -7.536621} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2104379670