< Summary

Class:AvatarEditorHUDController
Assembly:AvatarEditorHUD
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/AvatarEditorHUD/Scripts/AvatarEditorHUDController.cs
Covered lines:238
Uncovered lines:63
Coverable lines:301
Total lines:617
Line coverage:79% (238 of 301)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarEditorHUDController()0%110100%
AvatarEditorHUDController()0%110100%
Initialize(...)0%110100%
SetCatalog(...)0%2.062075%
LoadUserProfile(...)0%110100%
LoadOwnedWereables(...)0%8.744033.33%
RetryLoadOwnedWearables()0%2100%
PlayerRendererLoaded(...)0%7.056069.23%
LoadUserProfile(...)0%11.4211084.85%
EnsureWearablesCategoriesNotEmpty()0%7.027092.86%
WearableClicked(...)0%7.197084.21%
HairColorClicked(...)0%110100%
SkinColorClicked(...)0%110100%
EyesColorClicked(...)0%110100%
UpdateAvatarPreview()0%220100%
EquipHairColor(...)0%220100%
EquipEyesColor(...)0%220100%
EquipSkinColor(...)0%220100%
EquipBodyShape(...)0%6.046089.47%
EquipWearable(...)0%5.055087.5%
UnequipWearable(...)0%220100%
UnequipAllWearables()0%220100%
ProcessCatalog(...)0%330100%
AddWearable(...)0%440100%
RemoveWearable(...)0%20400%
RandomizeWearables()0%44093.75%
GetWearablesReplacedBy(...)0%7.017093.33%
SetVisibility(...)0%8.038092.31%
Dispose()0%110100%
CleanUp()0%220100%
SetConfiguration(...)0%2100%
SaveAvatar(...)0%2.012085.71%
DiscardAndClose()0%6200%
GoToMarketplace()0%6200%
SellCollectible(...)0%2100%
ToggleVisibility()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/AvatarEditorHUD/Scripts/AvatarEditorHUDController.cs

#LineLine coverage
 1using DCL;
 2using DCL.Helpers;
 3using DCL.Interface;
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using UnityEngine;
 8using UnityEngine.Rendering;
 9using UnityEngine.Rendering.Universal;
 10using Categories = WearableLiterals.Categories;
 11
 12public class AvatarEditorHUDController : IHUD
 13{
 14    private const int LOADING_OWNED_WEARABLES_RETRIES = 3;
 15    private const string LOADING_OWNED_WEARABLES_ERROR_MESSAGE = "There was a problem loading your wearables";
 16    private const string URL_MARKET_PLACE = "https://market.decentraland.org/browse?section=wearables";
 17    private const string URL_GET_A_WALLET = "https://docs.decentraland.org/get-a-wallet";
 18    private const string URL_SELL_COLLECTIBLE = "https://market.decentraland.org/account";
 19
 120    protected static readonly string[] categoriesThatMustHaveSelection = { Categories.BODY_SHAPE, Categories.UPPER_BODY,
 121    protected static readonly string[] categoriesToRandomize = { Categories.HAIR, Categories.EYES, Categories.EYEBROWS, 
 22
 23    [NonSerialized]
 24    public bool bypassUpdateAvatarPreview = false;
 25
 26    private UserProfile userProfile;
 27    private BaseDictionary<string, WearableItem> catalog;
 8728    bool renderingEnabled => CommonScriptableObjects.rendererState.Get();
 029    bool isPlayerRendererLoaded => DataStore.i.isPlayerRendererLoaded.Get();
 4630    private readonly Dictionary<string, List<WearableItem>> wearablesByCategory = new Dictionary<string, List<WearableIt
 4631    protected readonly AvatarEditorHUDModel model = new AvatarEditorHUDModel();
 32
 33    private ColorList skinColorList;
 34    private ColorList eyeColorList;
 35    private ColorList hairColorList;
 36    private bool prevMouseLockState = false;
 4637    private int ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 38    private bool ownedWearablesAlreadyLoaded = false;
 39
 40    public AvatarEditorHUDView view;
 41
 42    public event Action OnOpen;
 43    public event Action OnClose;
 44
 9245    public AvatarEditorHUDController() { }
 46
 47    public void Initialize(UserProfile userProfile, BaseDictionary<string, WearableItem> catalog, bool bypassUpdateAvata
 48    {
 4449        this.userProfile = userProfile;
 4450        this.bypassUpdateAvatarPreview = bypassUpdateAvatarPreview;
 51
 4452        view = AvatarEditorHUDView.Create(this);
 53
 4454        view.OnToggleActionTriggered += ToggleVisibility;
 4455        view.OnCloseActionTriggered += DiscardAndClose;
 56
 4457        skinColorList = Resources.Load<ColorList>("SkinTone");
 4458        hairColorList = Resources.Load<ColorList>("HairColor");
 4459        eyeColorList = Resources.Load<ColorList>("EyeColor");
 4460        view.SetColors(skinColorList.colors, hairColorList.colors, eyeColorList.colors);
 61
 4462        SetCatalog(catalog);
 63
 4464        LoadUserProfile(userProfile, true);
 4465        this.userProfile.OnUpdate += LoadUserProfile;
 4466        DataStore.i.isPlayerRendererLoaded.OnChange += PlayerRendererLoaded;
 4467    }
 68
 69    public void SetCatalog(BaseDictionary<string, WearableItem> catalog)
 70    {
 4471        if (this.catalog != null)
 72        {
 073            this.catalog.OnAdded -= AddWearable;
 074            this.catalog.OnRemoved -= RemoveWearable;
 75        }
 76
 4477        this.catalog = catalog;
 78
 4479        ProcessCatalog(this.catalog);
 4480        this.catalog.OnAdded += AddWearable;
 4481        this.catalog.OnRemoved += RemoveWearable;
 4482    }
 83
 84    private void LoadUserProfile(UserProfile userProfile)
 85    {
 4186        LoadOwnedWereables(userProfile);
 4187        LoadUserProfile(userProfile, false);
 4188    }
 89
 90    private void LoadOwnedWereables(UserProfile userProfile)
 91    {
 4192        if (ownedWearablesAlreadyLoaded || ownedWearablesRemainingRequests <= 0 || string.IsNullOrEmpty(userProfile.user
 4193            return;
 94
 095        view.ShowCollectiblesLoadingSpinner(true);
 096        view.ShowCollectiblesLoadingRetry(false);
 097        CatalogController.RequestOwnedWearables(userProfile.userId)
 98                         .Then((ownedWearables) =>
 99                         {
 0100                             ownedWearablesAlreadyLoaded = true;
 0101                             this.userProfile.SetInventory(ownedWearables.Select(x => x.id).ToArray());
 0102                             LoadUserProfile(userProfile, true);
 0103                             view.ShowCollectiblesLoadingSpinner(false);
 0104                         })
 105                         .Catch((error) =>
 106                         {
 0107                             ownedWearablesRemainingRequests--;
 0108                             if (ownedWearablesRemainingRequests > 0)
 109                             {
 0110                                 Debug.LogWarning("Retrying owned wereables loading...");
 0111                                 LoadOwnedWereables(userProfile);
 0112                             }
 113                             else
 114                             {
 0115                                 NotificationsController.i.ShowNotification(new Notification.Model
 116                                 {
 117                                     message = LOADING_OWNED_WEARABLES_ERROR_MESSAGE,
 118                                     type = NotificationFactory.Type.GENERIC,
 119                                     timer = 10f,
 120                                     destroyOnFinish = true
 121                                 });
 122
 0123                                 view.ShowCollectiblesLoadingSpinner(false);
 0124                                 view.ShowCollectiblesLoadingRetry(true);
 0125                                 Debug.LogError(error);
 126                             }
 0127                         });
 0128    }
 129
 130    public void RetryLoadOwnedWearables()
 131    {
 0132        ownedWearablesRemainingRequests = LOADING_OWNED_WEARABLES_RETRIES;
 0133        LoadOwnedWereables(userProfile);
 0134    }
 135
 136    private void PlayerRendererLoaded(bool current, bool previous)
 137    {
 2138        if (!current)
 0139            return;
 140
 2141        if (!ownedWearablesAlreadyLoaded)
 142        {
 2143            List<string> equippedOwnedWearables = new List<string>();
 4144            for (int i = 0; i < userProfile.avatar.wearables.Count; i++)
 145            {
 0146                if (catalog.TryGetValue(userProfile.avatar.wearables[i], out WearableItem wearable) &&
 147                    !wearable.data.tags.Contains("base-wearable"))
 148                {
 0149                    equippedOwnedWearables.Add(userProfile.avatar.wearables[i]);
 150                }
 151            }
 152
 2153            userProfile.SetInventory(equippedOwnedWearables.ToArray());
 154        }
 155
 2156        LoadUserProfile(userProfile, true);
 2157        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 2158    }
 159
 160    public void LoadUserProfile(UserProfile userProfile, bool forceLoading)
 161    {
 87162        bool avatarEditorNotVisible = renderingEnabled && !view.isOpen;
 87163        bool isPlaying = !Application.isBatchMode;
 164
 87165        if (!forceLoading)
 166        {
 41167            if (isPlaying && avatarEditorNotVisible)
 0168                return;
 169        }
 170
 87171        if (userProfile == null)
 0172            return;
 173
 87174        if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 3175            return;
 176
 84177        CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.bodyShape, out var bodyShape);
 178
 84179        if (bodyShape == null)
 180        {
 0181            return;
 182        }
 183
 84184        view.SetIsWeb3(userProfile.hasConnectedWeb3);
 185
 84186        ProcessCatalog(this.catalog);
 84187        EquipBodyShape(bodyShape);
 84188        EquipSkinColor(userProfile.avatar.skinColor);
 84189        EquipHairColor(userProfile.avatar.hairColor);
 84190        EquipEyesColor(userProfile.avatar.eyeColor);
 191
 84192        model.wearables.Clear();
 84193        view.UnselectAllWearables();
 194
 84195        int wearablesCount = userProfile.avatar.wearables.Count;
 196
 84197        if (isPlayerRendererLoaded)
 198        {
 184199            for (var i = 0; i < wearablesCount; i++)
 200            {
 53201                CatalogController.wearableCatalog.TryGetValue(userProfile.avatar.wearables[i], out var wearable);
 53202                if (wearable == null)
 203                {
 0204                    Debug.LogError($"Couldn't find wearable with ID {userProfile.avatar.wearables[i]}");
 0205                    continue;
 206                }
 207
 53208                EquipWearable(wearable);
 209            }
 210        }
 211
 84212        EnsureWearablesCategoriesNotEmpty();
 213
 84214        UpdateAvatarPreview();
 84215    }
 216
 217    private void EnsureWearablesCategoriesNotEmpty()
 218    {
 137219        var categoriesInUse = model.wearables.Select(x => x.data.category).ToArray();
 1344220        for (var i = 0; i < categoriesThatMustHaveSelection.Length; i++)
 221        {
 588222            var category = categoriesThatMustHaveSelection[i];
 588223            if (category != Categories.BODY_SHAPE && !(categoriesInUse.Contains(category)))
 224            {
 225                WearableItem wearable;
 462226                var defaultItemId = WearableLiterals.DefaultWearables.GetDefaultWearable(model.bodyShape.id, category);
 462227                if (defaultItemId != null)
 228                {
 462229                    CatalogController.wearableCatalog.TryGetValue(defaultItemId, out wearable);
 462230                }
 231                else
 232                {
 0233                    wearable = wearablesByCategory[category].FirstOrDefault(x => x.SupportsBodyShape(model.bodyShape.id)
 234                }
 235
 462236                if (wearable != null)
 237                {
 462238                    EquipWearable(wearable);
 239                }
 240            }
 241        }
 84242    }
 243
 244    public void WearableClicked(string wearableId)
 245    {
 20246        CatalogController.wearableCatalog.TryGetValue(wearableId, out var wearable);
 20247        if (wearable == null)
 3248            return;
 249
 17250        if (wearable.data.category == Categories.BODY_SHAPE)
 251        {
 3252            if (wearable.id == model.bodyShape.id)
 0253                return;
 3254            EquipBodyShape(wearable);
 3255        }
 256        else
 257        {
 14258            if (model.wearables.Contains(wearable))
 259            {
 2260                if (!categoriesThatMustHaveSelection.Contains(wearable.data.category))
 261                {
 0262                    UnequipWearable(wearable);
 0263                }
 264                else
 265                {
 2266                    return;
 267                }
 268            }
 269            else
 270            {
 91271                var sameCategoryEquipped = model.wearables.FirstOrDefault(x => x.data.category == wearable.data.category
 12272                if (sameCategoryEquipped != null)
 273                {
 2274                    UnequipWearable(sameCategoryEquipped);
 275                }
 276
 12277                EquipWearable(wearable);
 278            }
 279        }
 280
 15281        UpdateAvatarPreview();
 15282    }
 283
 284    public void HairColorClicked(Color color)
 285    {
 2286        EquipHairColor(color);
 2287        view.SelectHairColor(model.hairColor);
 2288        UpdateAvatarPreview();
 2289    }
 290
 291    public void SkinColorClicked(Color color)
 292    {
 2293        EquipSkinColor(color);
 2294        view.SelectSkinColor(model.skinColor);
 2295        UpdateAvatarPreview();
 2296    }
 297
 298    public void EyesColorClicked(Color color)
 299    {
 2300        EquipEyesColor(color);
 2301        view.SelectEyeColor(model.eyesColor);
 2302        UpdateAvatarPreview();
 2303    }
 304
 305    protected virtual void UpdateAvatarPreview()
 306    {
 106307        if (!bypassUpdateAvatarPreview)
 106308            view.UpdateAvatarPreview(model.ToAvatarModel());
 106309    }
 310
 311    private void EquipHairColor(Color color)
 312    {
 87313        var colorToSet = color;
 881314        if (!hairColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 315        {
 78316            colorToSet = hairColorList.colors[hairColorList.defaultColor];
 317        }
 318
 87319        model.hairColor = colorToSet;
 87320        view.SelectHairColor(model.hairColor);
 87321    }
 322
 323    private void EquipEyesColor(Color color)
 324    {
 87325        var colorToSet = color;
 883326        if (!eyeColorList.colors.Any(x => x.AproxComparison(color)))
 327        {
 78328            colorToSet = eyeColorList.colors[eyeColorList.defaultColor];
 329        }
 330
 87331        model.eyesColor = colorToSet;
 87332        view.SelectEyeColor(model.eyesColor);
 87333    }
 334
 335    private void EquipSkinColor(Color color)
 336    {
 86337        var colorToSet = color;
 877338        if (!skinColorList.colors.Any(x => x.AproxComparison(colorToSet)))
 339        {
 78340            colorToSet = skinColorList.colors[skinColorList.defaultColor];
 341        }
 342
 86343        model.skinColor = colorToSet;
 86344        view.SelectSkinColor(model.skinColor);
 86345    }
 346
 347    private void EquipBodyShape(WearableItem bodyShape)
 348    {
 87349        if (bodyShape.data.category != Categories.BODY_SHAPE)
 350        {
 0351            Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0352            return;
 353        }
 354
 87355        if (model.bodyShape == bodyShape)
 37356            return;
 357
 50358        model.bodyShape = bodyShape;
 50359        view.UpdateSelectedBody(bodyShape);
 360
 50361        int wearablesCount = model.wearables.Count;
 172362        for (var i = wearablesCount - 1; i >= 0; i--)
 363        {
 36364            UnequipWearable(model.wearables[i]);
 365        }
 366
 50367        var defaultWearables = WearableLiterals.DefaultWearables.GetDefaultWearables(bodyShape.id);
 812368        for (var i = 0; i < defaultWearables.Length; i++)
 369        {
 356370            if (catalog.TryGetValue(defaultWearables[i], out var wearable))
 356371                EquipWearable(wearable);
 372        }
 50373    }
 374
 375    private void EquipWearable(WearableItem wearable)
 376    {
 890377        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 0378            return;
 379
 890380        if (wearablesByCategory[wearable.data.category].Contains(wearable) && wearable.SupportsBodyShape(model.bodyShape
 381        {
 890382            var toReplace = GetWearablesReplacedBy(wearable);
 890383            toReplace.ForEach(UnequipWearable);
 890384            model.wearables.Add(wearable);
 890385            view.EquipWearable(wearable);
 386        }
 890387    }
 388
 389    private void UnequipWearable(WearableItem wearable)
 390    {
 40391        if (model.wearables.Contains(wearable))
 392        {
 40393            model.wearables.Remove(wearable);
 40394            view.UnequipWearable(wearable);
 395        }
 40396    }
 397
 398    public void UnequipAllWearables()
 399    {
 926400        foreach (var wearable in model.wearables)
 401        {
 395402            view.UnequipWearable(wearable);
 403        }
 404
 68405        model.wearables.Clear();
 68406    }
 407
 408    private void ProcessCatalog(BaseDictionary<string, WearableItem> catalog)
 409    {
 128410        wearablesByCategory.Clear();
 128411        view.RemoveAllWearables();
 128412        using (var iterator = catalog.Get().GetEnumerator())
 413        {
 3840414            while (iterator.MoveNext())
 415            {
 3712416                AddWearable(iterator.Current.Key, iterator.Current.Value);
 417            }
 128418        }
 128419    }
 420
 421    private void AddWearable(string id, WearableItem wearable)
 422    {
 3718423        if (!wearable.data.tags.Contains("base-wearable") && userProfile.GetItemAmount(id) == 0)
 424        {
 118425            return;
 426        }
 427
 3600428        if (!wearablesByCategory.ContainsKey(wearable.data.category))
 429        {
 1408430            wearablesByCategory.Add(wearable.data.category, new List<WearableItem>());
 431        }
 432
 3600433        wearablesByCategory[wearable.data.category].Add(wearable);
 3600434        view.AddWearable(wearable, userProfile.GetItemAmount(id));
 3600435    }
 436
 437    private void RemoveWearable(string id, WearableItem wearable)
 438    {
 0439        if (wearablesByCategory.ContainsKey(wearable.data.category))
 440        {
 0441            if (wearablesByCategory[wearable.data.category].Remove(wearable))
 442            {
 0443                if (wearablesByCategory[wearable.data.category].Count == 0)
 444                {
 0445                    wearablesByCategory.Remove(wearable.data.category);
 446                }
 447            }
 448        }
 449
 0450        view.RemoveWearable(wearable);
 0451    }
 452
 453    public void RandomizeWearables()
 454    {
 1455        EquipHairColor(hairColorList.colors[UnityEngine.Random.Range(0, hairColorList.colors.Count)]);
 1456        EquipEyesColor(eyeColorList.colors[UnityEngine.Random.Range(0, eyeColorList.colors.Count)]);
 457
 1458        model.wearables.Clear();
 1459        view.UnselectAllWearables();
 1460        using (var iterator = wearablesByCategory.GetEnumerator())
 461        {
 12462            while (iterator.MoveNext())
 463            {
 11464                string category = iterator.Current.Key;
 11465                if (!categoriesToRandomize.Contains(category))
 466                {
 467                    continue;
 468                }
 469
 29470                var supportedWearables = iterator.Current.Value.Where(x => x.SupportsBodyShape(model.bodyShape.id)).ToAr
 7471                if (supportedWearables.Length == 0)
 472                {
 0473                    Debug.LogError($"Couldn't get any wearable for category {category} and bodyshape {model.bodyShape.id
 474                }
 475
 7476                var wearable = supportedWearables[UnityEngine.Random.Range(0, supportedWearables.Length - 1)];
 7477                EquipWearable(wearable);
 478            }
 1479        }
 480
 1481        UpdateAvatarPreview();
 1482    }
 483
 484    public List<WearableItem> GetWearablesReplacedBy(WearableItem wearableItem)
 485    {
 890486        List<WearableItem> wearablesToReplace = new List<WearableItem>();
 487
 890488        HashSet<string> categoriesToReplace = new HashSet<string>(wearableItem.GetReplacesList(model.bodyShape.id) ?? ne
 489
 890490        int wearableCount = model.wearables.Count;
 6820491        for (int i = 0; i < wearableCount; i++)
 492        {
 2520493            var wearable = model.wearables[i];
 2520494            if (wearable == null)
 495                continue;
 496
 2520497            if (categoriesToReplace.Contains(wearable.data.category))
 498            {
 2499                wearablesToReplace.Add(wearable);
 2500            }
 501            else
 502            {
 503                //For retrocompatibility's sake we check current wearables against new one (compatibility matrix is symm
 2518504                HashSet<string> replacesList = new HashSet<string>(wearable.GetReplacesList(model.bodyShape.id) ?? new s
 2518505                if (replacesList.Contains(wearableItem.data.category))
 506                {
 0507                    wearablesToReplace.Add(wearable);
 508                }
 509            }
 510        }
 511
 890512        return wearablesToReplace;
 513    }
 514
 46515    private float prevRenderScale = 1.0f;
 516    private Camera mainCamera;
 517
 518    public void SetVisibility(bool visible)
 519    {
 40520        var currentRenderProfile = DCL.RenderProfileManifest.i.currentProfile;
 521
 40522        if (!visible && view.isOpen)
 523        {
 1524            DCL.Environment.i.messaging.manager.paused = false;
 1525            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.inWorld;
 1526            currentRenderProfile.avatarProfile.Apply();
 1527            if (prevMouseLockState)
 528            {
 0529                Utils.LockCursor();
 530            }
 531
 532            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 1533            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 1534            asset.renderScale = prevRenderScale;
 535
 1536            CommonScriptableObjects.isFullscreenHUDOpen.Set(false);
 1537            OnClose?.Invoke();
 0538        }
 39539        else if (visible && !view.isOpen)
 540        {
 39541            DCL.Environment.i.messaging.manager.paused = DataStore.i.isSignUpFlow.Get();
 39542            currentRenderProfile.avatarProfile.currentProfile = currentRenderProfile.avatarProfile.avatarEditor;
 39543            currentRenderProfile.avatarProfile.Apply();
 544
 39545            prevMouseLockState = Utils.isCursorLocked;
 39546            Utils.UnlockCursor();
 547
 548            // NOTE(Brian): SSAO doesn't work correctly with the offseted avatar preview if the renderScale != 1.0
 39549            var asset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
 39550            prevRenderScale = asset.renderScale;
 39551            asset.renderScale = 1.0f;
 552
 39553            CommonScriptableObjects.isFullscreenHUDOpen.Set(true);
 39554            OnOpen?.Invoke();
 555        }
 556
 40557        currentRenderProfile.avatarProfile.Apply();
 40558        view.SetVisibility(visible);
 40559    }
 560
 561    public void Dispose()
 562    {
 39563        view.OnToggleActionTriggered -= ToggleVisibility;
 39564        view.OnCloseActionTriggered -= DiscardAndClose;
 565
 39566        CleanUp();
 39567    }
 568
 569    public void CleanUp()
 570    {
 44571        UnequipAllWearables();
 572
 44573        if (view != null)
 44574            view.CleanUp();
 575
 44576        this.userProfile.OnUpdate -= LoadUserProfile;
 44577        this.catalog.OnAdded -= AddWearable;
 44578        this.catalog.OnRemoved -= RemoveWearable;
 44579        DataStore.i.isPlayerRendererLoaded.OnChange -= PlayerRendererLoaded;
 44580    }
 581
 0582    public void SetConfiguration(HUDConfiguration configuration) { SetVisibility(configuration.active); }
 583
 584    public void SaveAvatar(Texture2D faceSnapshot, Texture2D face128Snapshot, Texture2D face256Snapshot, Texture2D bodyS
 585    {
 1586        var avatarModel = model.ToAvatarModel();
 587
 1588        WebInterface.SendSaveAvatar(avatarModel, faceSnapshot, face128Snapshot, face256Snapshot, bodySnapshot, DataStore
 1589        userProfile.OverrideAvatar(avatarModel, face256Snapshot);
 1590        if (DataStore.i.isSignUpFlow.Get())
 0591            DataStore.i.HUDs.signupVisible.Set(true);
 592
 1593        SetVisibility(false);
 1594    }
 595
 596    public void DiscardAndClose()
 597    {
 0598        if (!DataStore.i.isSignUpFlow.Get())
 0599            LoadUserProfile(userProfile);
 600        else
 0601            WebInterface.SendCloseUserAvatar(true);
 602
 0603        SetVisibility(false);
 0604    }
 605
 606    public void GoToMarketplace()
 607    {
 0608        if (userProfile.hasConnectedWeb3)
 0609            WebInterface.OpenURL(URL_MARKET_PLACE);
 610        else
 0611            WebInterface.OpenURL(URL_GET_A_WALLET);
 0612    }
 613
 0614    public void SellCollectible(string collectibleId) { WebInterface.OpenURL(URL_SELL_COLLECTIBLE); }
 615
 0616    public void ToggleVisibility() { SetVisibility(!view.isOpen); }
 617}