< Summary

Class:DCL.Backpack.BackpackEditorHUDController
Assembly:BackpackEditorHUDV2
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/BackpackEditorHUDController.cs
Covered lines:341
Uncovered lines:162
Coverable lines:503
Total lines:987
Line coverage:67.7% (341 of 503)
Covered branches:0
Total branches:0
Covered methods:38
Total methods:56
Method coverage:67.8% (38 of 56)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
BackpackEditorHUDController(...)0%110100%
OnVRMDetailsOpened()0%2100%
OnOutfitEquipped(...)0%12300%
LoadAndEquipOutfitWearables()0%1321100%
OnOutfitsOpened()0%6200%
Dispose()0%220100%
UpdateOverrideHides(...)0%2100%
UpdateOverrideHides(...)0%2.012085.71%
OnBackpackVisibleChanged(...)0%110100%
<SetVisibility()0%8.048091.3%
SetVisibility(...)0%110100%
CloseView()0%2.012085.71%
ConfigureBackpackInFullscreenMenuChanged(...)0%110100%
SaveBackpackBeforeSignUpFinishes(...)0%6200%
LoadUserProfileFromProfileUpdate(...)0%330100%
LoadUserProfile(...)0%5.935066.67%
<LoadUserProfile()0%32.9613050.94%
UpdateAvatarPreview()0%3.213071.43%
OnNewEmoteAdded(...)0%110100%
OnEmotePreviewed(...)0%110100%
OnEmoteEquipped(...)0%2100%
OnEmoteUnEquipped(...)0%2100%
EquipBodyShape(...)0%3.043083.33%
SaveAvatarAndContinueSignupProcess()0%3.192033.33%
SaveAsync()0%7.734038.46%
TakeSnapshotsAndSaveAvatarAsync(...)0%110100%
TakeSnapshots(...)0%110100%
SaveAvatar(...)0%11.1510077.42%
SelectWearable(...)0%6200%
EquipWearableFromGrid(...)0%110100%
UnEquipWearableFromGrid(...)0%110100%
OnCategoryFilterRemoved()0%110100%
UnEquipWearableFromSlot(...)0%2100%
EquipWearable(...)0%3.713057.14%
EquipWearable(...)0%10.2610086.21%
ReplaceIncompatibleWearablesWithDefaultWearables()0%8.068090.48%
UnEquipCurrentBodyShape(...)0%3.213071.43%
UnEquipWearable(...)0%2.262060%
UnEquipWearable(...)0%6.246081.25%
UpdateVRMExportWarning()0%110100%
ResetOverridesOfAffectedCategories(...)0%330100%
ToggleSlot(...)0%11.5611083.33%
OnWearableColorChanged(...)0%56700%
SetAllColors(...)0%2100%
UpdateAvatarModel(...)0%110100%
OnColorPickerToggled()0%2100%
OnAvatarUpdated()0%6200%
PlayEquipAnimation(...)0%2100%
GetEquipEmoteByCategory(...)0%7022600%
GetRandomizedName(...)0%2100%
OnVrmExport()0%12300%
OnSignUpBack(...)0%330100%
VrmExport()0%41.0920062.5%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/BackpackEditorHUDV2/BackpackEditorHUDController.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL.Helpers;
 3using DCL.Tasks;
 4using DCLServices.DCLFileBrowser;
 5using DCLServices.WearablesCatalogService;
 6using MainScripts.DCL.Components.Avatar.VRMExporter;
 7using MainScripts.DCL.Controllers.HUD.CharacterPreview;
 8using System;
 9using System.Collections.Generic;
 10using System.Linq;
 11using System.Text;
 12using System.Threading;
 13using UnityEngine;
 14
 15namespace DCL.Backpack
 16{
 17    public class BackpackEditorHUDController
 18    {
 19        private const string NEW_TOS_AND_EMAIL_SUBSCRIPTION_FF = "new_terms_of_service_and_email_subscription";
 20        private const string EMOTE_ID = "wave";
 21        private const string NOT_LOADED = "NOT_LOADED";
 22
 23        private readonly IBackpackEditorHUDView view;
 24        private readonly DataStore dataStore;
 25        private readonly IWearablesCatalogService wearablesCatalogService;
 26        private readonly IBackpackEmotesSectionController backpackEmotesSectionController;
 27        private readonly IBackpackAnalyticsService backpackAnalyticsService;
 28        private readonly IUserProfileBridge userProfileBridge;
 29        private readonly RendererState rendererState;
 30        private readonly WearableGridController wearableGridController;
 31        private readonly AvatarSlotsHUDController avatarSlotsHUDController;
 32        private readonly OutfitsController outfitsController;
 33        private readonly IVRMExporter vrmExporter;
 34        private readonly VRMDetailsController vrmDetailsController;
 35        private readonly IDCLFileBrowserService fileBrowser;
 36        private readonly IEmotesCatalogService emotesCatalogService;
 2537        private readonly Dictionary<string, string> extendedWearableUrns = new ();
 2538        private readonly Dictionary<string, Dictionary<string, string>> fallbackWearables = new ()
 39        {
 40            {"urn:decentraland:off-chain:base-avatars:BaseFemale", new Dictionary<string, string>
 41            {
 42                {WearableLiterals.Categories.UPPER_BODY, "urn:decentraland:off-chain:base-avatars:white_top"},
 43                {WearableLiterals.Categories.LOWER_BODY, "urn:decentraland:off-chain:base-avatars:f_jeans"},
 44                {WearableLiterals.Categories.FEET, "urn:decentraland:off-chain:base-avatars:ruby_blue_loafer"},
 45                {WearableLiterals.Categories.HAIR, "urn:decentraland:off-chain:base-avatars:pony_tail"},
 46                {WearableLiterals.Categories.MOUTH, "urn:decentraland:off-chain:base-avatars:f_mouth_05"},
 47                {WearableLiterals.Categories.EYEBROWS, "urn:decentraland:off-chain:base-avatars:f_eyebrows_02"},
 48                {WearableLiterals.Categories.EYES, "urn:decentraland:off-chain:base-avatars:f_eyes_06"},
 49            }},
 50            {"urn:decentraland:off-chain:base-avatars:BaseMale", new Dictionary<string, string>
 51            {
 52                {WearableLiterals.Categories.UPPER_BODY, "urn:decentraland:off-chain:base-avatars:m_sweater_02"},
 53                {WearableLiterals.Categories.LOWER_BODY, "urn:decentraland:off-chain:base-avatars:soccer_pants"},
 54                {WearableLiterals.Categories.FEET, "urn:decentraland:off-chain:base-avatars:sport_colored_shoes"},
 55                {WearableLiterals.Categories.HAIR, "urn:decentraland:off-chain:base-avatars:cool_hair"},
 56                {WearableLiterals.Categories.FACIAL_HAIR, "urn:decentraland:off-chain:base-avatars:beard"},
 57                {WearableLiterals.Categories.EYEBROWS, "urn:decentraland:off-chain:base-avatars:eyebrows_00"},
 58                {WearableLiterals.Categories.EYES, "urn:decentraland:off-chain:base-avatars:eyes_00"},
 59            }}
 60        };
 2561        private readonly BackpackEditorHUDModel model = new ();
 2562        private readonly Dictionary<string, bool> vrmBlockingWearables = new (); // Key: id, Value: canBeUnEquipped
 63
 64        private string currentSlotSelected;
 65        private bool avatarIsDirty;
 2566        private CancellationTokenSource loadProfileCancellationToken = new ();
 2567        private CancellationTokenSource setVisibilityCancellationToken = new ();
 2568        private CancellationTokenSource outfitLoadCancellationToken = new ();
 2569        private CancellationTokenSource saveCancellationToken = new ();
 70        private string categoryPendingToPlayEmote;
 71        private bool isTakingSnapshot;
 72
 1473        private bool isNewTermsOfServiceAndEmailSubscriptionEnabled => dataStore.featureFlags.flags.Get().IsFeatureEnabl
 74
 2975        private BaseCollection<string> previewEquippedWearables => dataStore.backpackV2.previewEquippedWearables;
 76
 10277        private UserProfile ownUserProfile => userProfileBridge.GetOwn();
 78
 79        private int currentAnimationIndexShown;
 2580        private bool shouldRequestOutfits = true;
 81
 82        private CancellationTokenSource vrmExportCts;
 83
 2584        public BackpackEditorHUDController(
 85            IBackpackEditorHUDView view,
 86            DataStore dataStore,
 87            RendererState rendererState,
 88            IUserProfileBridge userProfileBridge,
 89            IWearablesCatalogService wearablesCatalogService,
 90            IBackpackEmotesSectionController backpackEmotesSectionController,
 91            IBackpackAnalyticsService backpackAnalyticsService,
 92            WearableGridController wearableGridController,
 93            AvatarSlotsHUDController avatarSlotsHUDController,
 94            OutfitsController outfitsController,
 95            IVRMExporter vrmExporter,
 96            VRMDetailsController vrmDetailsController,
 97            IDCLFileBrowserService fileBrowser,
 98            IEmotesCatalogService emotesCatalogService)
 99        {
 25100            this.view = view;
 25101            this.dataStore = dataStore;
 25102            this.rendererState = rendererState;
 25103            this.userProfileBridge = userProfileBridge;
 25104            this.wearablesCatalogService = wearablesCatalogService;
 25105            this.backpackEmotesSectionController = backpackEmotesSectionController;
 25106            this.backpackAnalyticsService = backpackAnalyticsService;
 25107            this.wearableGridController = wearableGridController;
 25108            this.avatarSlotsHUDController = avatarSlotsHUDController;
 25109            this.outfitsController = outfitsController;
 25110            this.vrmExporter = vrmExporter;
 25111            this.vrmDetailsController = vrmDetailsController;
 25112            this.fileBrowser = fileBrowser;
 25113            this.emotesCatalogService = emotesCatalogService;
 114
 25115            avatarSlotsHUDController.GenerateSlots();
 25116            ownUserProfile.OnUpdate += LoadUserProfileFromProfileUpdate;
 25117            dataStore.HUDs.avatarEditorVisible.OnChange += OnBackpackVisibleChanged;
 25118            dataStore.HUDs.isAvatarEditorInitialized.Set(true);
 25119            dataStore.exploreV2.configureBackpackInFullscreenMenu.OnChange += ConfigureBackpackInFullscreenMenuChanged;
 25120            dataStore.backpackV2.isWaitingToBeSavedAfterSignUp.OnChange += SaveBackpackBeforeSignUpFinishes;
 121
 25122            ConfigureBackpackInFullscreenMenuChanged(dataStore.exploreV2.configureBackpackInFullscreenMenu.Get(), null);
 123
 25124            backpackEmotesSectionController.OnNewEmoteAdded += OnNewEmoteAdded;
 25125            backpackEmotesSectionController.OnEmotePreviewed += OnEmotePreviewed;
 25126            backpackEmotesSectionController.OnEmoteEquipped += OnEmoteEquipped;
 25127            backpackEmotesSectionController.OnEmoteUnEquipped += OnEmoteUnEquipped;
 128
 25129            wearableGridController.OnWearableSelected += SelectWearable;
 25130            wearableGridController.OnWearableEquipped += EquipWearableFromGrid;
 25131            wearableGridController.OnWearableUnequipped += UnEquipWearableFromGrid;
 25132            wearableGridController.OnCategoryFilterRemoved += OnCategoryFilterRemoved;
 133
 25134            avatarSlotsHUDController.OnToggleSlot += ToggleSlot;
 25135            avatarSlotsHUDController.OnUnequipFromSlot += UnEquipWearableFromSlot;
 25136            avatarSlotsHUDController.OnHideUnhidePressed += UpdateOverrideHides;
 137
 25138            view.SetColorPickerVisibility(false);
 25139            view.OnContinueSignup += SaveAvatarAndContinueSignupProcess;
 25140            view.OnColorChanged += OnWearableColorChanged;
 25141            view.OnColorPickerToggle += OnColorPickerToggled;
 25142            view.OnAvatarUpdated += OnAvatarUpdated;
 25143            view.OnOutfitsOpened += OnOutfitsOpened;
 25144            view.OnSignUpBackClicked += OnSignUpBack;
 25145            outfitsController.OnOutfitEquipped += OnOutfitEquipped;
 146
 25147            view.SetOutfitsEnabled(dataStore.featureFlags.flags.Get().IsFeatureEnabled("outfits"));
 25148            SetVisibility(dataStore.HUDs.avatarEditorVisible.Get(), saveAvatar: false);
 149
 25150            bool vrmFeatureEnabled = this.dataStore.featureFlags.flags.Get().IsFeatureEnabled("vrm_export");
 25151            view.OnVRMExport += OnVrmExport;
 25152            view.OnVRMDetailsOpened += OnVRMDetailsOpened;
 25153            view.OnVRMDetailsClosed += UpdateVRMExportWarning;
 25154            view.SetVRMButtonActive(vrmFeatureEnabled);
 25155            view.SetVRMButtonEnabled(vrmFeatureEnabled);
 25156            view.SetVRMSuccessToastActive(false);
 157
 25158            vrmDetailsController.OnWearableUnequipped += UnEquipWearableFromGrid;
 25159            vrmDetailsController.OnWearableEquipped += EquipWearableFromGrid;
 25160            UpdateVRMExportWarning();
 25161        }
 162
 163        private void OnVRMDetailsOpened()
 164        {
 0165            vrmDetailsController.Initialize(vrmBlockingWearables);
 0166        }
 167
 168        private void OnOutfitEquipped(OutfitItem outfit)
 169        {
 0170            Dictionary<string, WearableItem> keyValuePairs = new Dictionary<string, WearableItem>(model.wearables);
 171
 0172            foreach (KeyValuePair<string, WearableItem> keyValuePair in keyValuePairs)
 0173                UnEquipWearable(keyValuePair.Key, UnequipWearableSource.None, false, false);
 174
 0175            foreach (string forcedCategory in model.forceRender)
 0176                UpdateOverrideHides(forcedCategory, false, false);
 177
 0178            outfitLoadCancellationToken = new CancellationTokenSource();
 0179            LoadAndEquipOutfitWearables(outfit, outfitLoadCancellationToken.Token).Forget();
 0180        }
 181
 182        private async UniTaskVoid LoadAndEquipOutfitWearables(OutfitItem outfit, CancellationToken cancellationToken)
 183        {
 0184            if (!wearablesCatalogService.WearablesCatalog.ContainsKey(outfit.outfit.bodyShape))
 0185                await wearablesCatalogService.RequestWearableAsync(outfit.outfit.bodyShape, cancellationToken);
 186
 0187            foreach (string outfitWearable in outfit.outfit.wearables)
 188            {
 0189                if (wearablesCatalogService.WearablesCatalog.ContainsKey(ExtendedUrnParser.GetShortenedUrn(outfitWearabl
 190
 0191                try { await wearablesCatalogService.RequestWearableAsync(outfitWearable, cancellationToken); }
 0192                catch (Exception e) { Debug.LogWarning($"Cannot resolve the wearable {outfitWearable} for the outfit {ou
 0193            }
 194
 0195            EquipWearable(outfit.outfit.bodyShape, EquipWearableSource.Outfit, setAsDirty: false, updateAvatarPreview: f
 196
 0197            foreach (string outfitWearable in outfit.outfit.wearables)
 0198                EquipWearable(outfitWearable, EquipWearableSource.Outfit, setAsDirty: true, updateAvatarPreview: true);
 199
 0200            SetAllColors(outfit.outfit.eyes.color, outfit.outfit.hair.color, outfit.outfit.skin.color);
 201
 0202            foreach (string forcedCategory in outfit.outfit.forceRender)
 0203                UpdateOverrideHides(forcedCategory, true, true);
 0204        }
 205
 206        private void OnOutfitsOpened()
 207        {
 0208            if (!shouldRequestOutfits) return;
 209
 0210            outfitsController.RequestOwnedOutfits();
 0211            shouldRequestOutfits = false;
 0212        }
 213
 214        public void Dispose()
 215        {
 25216            vrmExportCts?.SafeCancelAndDispose();
 25217            ownUserProfile.OnUpdate -= LoadUserProfileFromProfileUpdate;
 25218            dataStore.HUDs.avatarEditorVisible.OnChange -= OnBackpackVisibleChanged;
 25219            dataStore.exploreV2.configureBackpackInFullscreenMenu.OnChange -= ConfigureBackpackInFullscreenMenuChanged;
 25220            dataStore.backpackV2.isWaitingToBeSavedAfterSignUp.OnChange -= SaveBackpackBeforeSignUpFinishes;
 221
 25222            backpackEmotesSectionController.OnNewEmoteAdded -= OnNewEmoteAdded;
 25223            backpackEmotesSectionController.OnEmotePreviewed -= OnEmotePreviewed;
 25224            backpackEmotesSectionController.OnEmoteEquipped -= OnEmoteEquipped;
 25225            backpackEmotesSectionController.OnEmoteUnEquipped -= OnEmoteUnEquipped;
 25226            backpackEmotesSectionController.Dispose();
 227
 25228            wearableGridController.OnWearableSelected -= SelectWearable;
 25229            wearableGridController.OnWearableEquipped -= EquipWearableFromGrid;
 25230            wearableGridController.OnWearableUnequipped -= UnEquipWearableFromGrid;
 25231            wearableGridController.OnCategoryFilterRemoved -= OnCategoryFilterRemoved;
 25232            wearableGridController.Dispose();
 233
 25234            avatarSlotsHUDController.OnToggleSlot -= ToggleSlot;
 25235            avatarSlotsHUDController.OnUnequipFromSlot -= UnEquipWearableFromSlot;
 25236            avatarSlotsHUDController.OnHideUnhidePressed -= UpdateOverrideHides;
 25237            avatarSlotsHUDController.Dispose();
 238
 25239            view.OnColorChanged -= OnWearableColorChanged;
 25240            view.OnContinueSignup -= SaveAvatarAndContinueSignupProcess;
 25241            view.OnColorPickerToggle -= OnColorPickerToggled;
 25242            view.OnAvatarUpdated -= OnAvatarUpdated;
 25243            view.OnOutfitsOpened -= OnOutfitsOpened;
 25244            view.OnVRMExport -= OnVrmExport;
 25245            view.OnSignUpBackClicked -= OnSignUpBack;
 25246            outfitsController.OnOutfitEquipped -= OnOutfitEquipped;
 25247            view.Dispose();
 248
 25249            vrmBlockingWearables.Clear();
 25250            view.OnVRMDetailsOpened -= OnVRMDetailsOpened;
 25251            view.OnVRMDetailsClosed -= UpdateVRMExportWarning;
 25252            vrmDetailsController.OnWearableUnequipped -= UnEquipWearableFromGrid;
 25253            vrmDetailsController.OnWearableEquipped -= EquipWearableFromGrid;
 25254        }
 255
 256        private void UpdateOverrideHides(string category, bool toggleOn) =>
 0257            UpdateOverrideHides(category, toggleOn, true);
 258
 259        private void UpdateOverrideHides(string category, bool toggleOn, bool setAsDirty)
 260        {
 5261            if (toggleOn)
 0262                model.forceRender.Add(category);
 263            else
 5264                model.forceRender.Remove(category);
 265
 5266            avatarIsDirty = setAsDirty;
 5267            avatarSlotsHUDController.Recalculate(model.forceRender);
 5268            UpdateAvatarPreview();
 5269        }
 270
 271        private void OnBackpackVisibleChanged(bool current, bool _) =>
 13272            SetVisibility(current, saveAvatar: avatarIsDirty);
 273
 274        private void SetVisibility(bool visible, bool saveAvatar = true)
 275        {
 276            async UniTaskVoid SetVisibilityAsync(bool visible, bool saveAvatar, CancellationToken cancellationToken)
 277            {
 39278                if (visible)
 279                {
 9280                    avatarIsDirty = false;
 9281                    dataStore.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InEditor);
 9282                    backpackEmotesSectionController.RestoreEmoteSlots();
 9283                    backpackEmotesSectionController.LoadEmotes();
 9284                    wearableGridController.LoadWearables();
 9285                    wearableGridController.LoadCollections();
 9286                    LoadUserProfile(ownUserProfile);
 9287                    view.Show();
 9288                    view.SetSignUpModeActive(dataStore.common.isSignUpFlow.Get() && isNewTermsOfServiceAndEmailSubscript
 289
 9290                    if (dataStore.common.isSignUpFlow.Get())
 291                    {
 5292                        if (isNewTermsOfServiceAndEmailSubscriptionEnabled)
 2293                            view.HideContinueSignup();
 294                        else
 3295                            view.ShowContinueSignup();
 296
 5297                        avatarSlotsHUDController.SelectSlot(WearableLiterals.Categories.BODY_SHAPE);
 298                    }
 299                    else
 4300                        view.HideContinueSignup();
 301                }
 302                else
 303                {
 30304                    if (saveAvatar)
 5305                        await SaveAsync(cancellationToken);
 306                    else
 25307                        CloseView();
 308
 30309                    wearableGridController.CancelWearableLoading();
 310                }
 39311            }
 312
 39313            setVisibilityCancellationToken = setVisibilityCancellationToken.SafeRestart();
 39314            SetVisibilityAsync(visible, saveAvatar, setVisibilityCancellationToken.Token).Forget();
 39315        }
 316
 317        private void CloseView()
 318        {
 30319            if (isTakingSnapshot)
 0320                return;
 321
 30322            view.Hide();
 30323            view.ResetPreviewPanel();
 30324            wearableGridController.ResetFilters();
 30325            dataStore.skyboxConfig.avatarMatProfile.Set(AvatarMaterialProfile.InWorld);
 30326        }
 327
 328        private void ConfigureBackpackInFullscreenMenuChanged(Transform currentParentTransform, Transform previousParent
 25329            view.SetAsFullScreenMenuMode(currentParentTransform);
 330
 331        private void SaveBackpackBeforeSignUpFinishes(bool isBackpackWaitingToBeSaved, bool _)
 332        {
 0333            if (!isBackpackWaitingToBeSaved)
 0334                return;
 335
 0336            view.SetSignUpStage(SignUpStage.CustomizeAvatar);
 0337            saveCancellationToken = saveCancellationToken.SafeRestart();
 0338            SaveAsync(saveCancellationToken.Token).Forget();
 0339        }
 340
 341        private void LoadUserProfileFromProfileUpdate(UserProfile userProfile)
 342        {
 7343            bool isEditorVisible = rendererState.Get() && view.isVisible;
 13344            if (!isEditorVisible) return;
 1345            LoadUserProfile(userProfile);
 1346        }
 347
 348        private void LoadUserProfile(UserProfile userProfile)
 349        {
 10350            if (avatarIsDirty)
 351            {
 0352                Debug.LogWarning("Skip the load of the user profile: avatarIsDirty=true");
 0353                return;
 354            }
 355
 10356            if (userProfile == null)
 357            {
 0358                Debug.LogWarning("Skip the load of the user profile: userProfile=null");
 0359                return;
 360            }
 361
 10362            if (userProfile.avatar == null || string.IsNullOrEmpty(userProfile.avatar.bodyShape))
 363            {
 8364                Debug.LogWarning("Skip the load of the user profile: the avatar body shape is invalid");
 8365                return;
 366            }
 367
 368            async UniTaskVoid LoadUserAvatarAsync(AvatarModel avatar, CancellationToken cancellationToken)
 369            {
 370                try
 371                {
 4372                    foreach (AvatarModel.AvatarEmoteEntry emote in avatar.emotes)
 373                    {
 0374                        string shortenedEmotedUrn = ExtendedUrnParser.GetShortenedUrn(emote.urn);
 0375                        if (!emote.urn.Equals(shortenedEmotedUrn))
 0376                            extendedWearableUrns[shortenedEmotedUrn] = emote.urn;
 377                    }
 378
 2379                    wearablesCatalogService.WearablesCatalog.TryGetValue(avatar.bodyShape, out var bodyShape);
 2380                    bodyShape ??= await wearablesCatalogService.RequestWearableAsync(avatar.bodyShape, cancellationToken
 381
 2382                    UnEquipCurrentBodyShape(false);
 2383                    EquipBodyShape(bodyShape, false);
 384
 2385                    model.skinColor = avatar.skinColor;
 2386                    model.hairColor = avatar.hairColor;
 2387                    model.eyesColor = avatar.eyeColor;
 2388                    model.forceRender = new HashSet<string>(avatar.forceRender);
 2389                    model.wearables.Clear();
 2390                    previewEquippedWearables.Clear();
 391
 2392                    int wearablesCount = avatar.wearables.Count;
 393
 24394                    for (var i = 0; i < wearablesCount; i++)
 395                    {
 10396                        string wearableId = avatar.wearables[i];
 10397                        string shortenedWearableId = ExtendedUrnParser.GetShortenedUrn(wearableId);
 398
 10399                        if (!wearableId.Equals(shortenedWearableId))
 0400                            extendedWearableUrns[shortenedWearableId] = wearableId;
 401
 10402                        if (!wearablesCatalogService.WearablesCatalog.TryGetValue(shortenedWearableId, out WearableItem 
 403                        {
 0404                            try { wearable = await wearablesCatalogService.RequestWearableAsync(shortenedWearableId, can
 0405                            catch (OperationCanceledException) { throw; }
 406                            catch (Exception e)
 407                            {
 0408                                Debug.LogError($"Cannot load the wearable {shortenedWearableId}");
 0409                                Debug.LogException(e);
 0410                                continue;
 411                            }
 412                        }
 413
 20414                        try { EquipWearable(wearableId, wearable, EquipWearableSource.None, false, false, false); }
 0415                        catch (OperationCanceledException) { throw; }
 416                        catch (Exception e)
 417                        {
 0418                            Debug.LogError($"Cannot equip the wearable {shortenedWearableId}");
 0419                            Debug.LogException(e);
 0420                        }
 10421                    }
 422
 2423                    avatarSlotsHUDController.Recalculate(model.forceRender);
 2424                    UpdateAvatarModel(model.ToAvatarModel());
 2425                }
 0426                catch (OperationCanceledException) { Debug.LogWarning("Skip the load of the user profile: the operation 
 0427                catch (Exception e) { Debug.LogException(e); }
 2428            }
 429
 2430            loadProfileCancellationToken = loadProfileCancellationToken.SafeRestart();
 2431            LoadUserAvatarAsync(userProfile.avatar, loadProfileCancellationToken.Token).Forget();
 2432        }
 433
 434        private void UpdateAvatarPreview()
 435        {
 6436            AvatarModel modelToUpdate = model.ToAvatarModel();
 437
 438            // We always keep the loaded emotes into the Avatar Preview
 12439            foreach (string emoteId in dataStore.emotesCustomization.currentLoadedEmotes.Get())
 0440                modelToUpdate.emotes.Add(new AvatarModel.AvatarEmoteEntry
 441                    { urn = emoteId });
 442
 6443            UpdateAvatarModel(modelToUpdate);
 6444        }
 445
 446        private void OnNewEmoteAdded(string emoteId) =>
 1447            UpdateAvatarPreview();
 448
 449        private void OnEmotePreviewed(string emoteId) =>
 1450            view.PlayPreviewEmote(emoteId);
 451
 452        private void OnEmoteEquipped(string emoteId) =>
 0453            avatarIsDirty = true;
 454
 455        private void OnEmoteUnEquipped(string emoteId) =>
 0456            avatarIsDirty = true;
 457
 458        private void EquipBodyShape(WearableItem bodyShape, bool setAsDirty = true)
 459        {
 7460            if (bodyShape.data.category != WearableLiterals.Categories.BODY_SHAPE)
 461            {
 0462                Debug.LogError($"Item ({bodyShape.id} is not a body shape");
 0463                return;
 464            }
 465
 7466            model.bodyShape = bodyShape;
 7467            dataStore.backpackV2.previewBodyShape.Set(bodyShape.id);
 7468            avatarSlotsHUDController.Equip(bodyShape, bodyShape.id, model.forceRender);
 7469            backpackEmotesSectionController.SetEquippedBodyShape(bodyShape.id);
 7470            wearableGridController.Equip(bodyShape.id);
 7471            wearableGridController.UpdateBodyShapeCompatibility(bodyShape.id);
 472
 7473            if (setAsDirty)
 5474                avatarIsDirty = true;
 7475        }
 476
 477        private void SaveAvatarAndContinueSignupProcess()
 478        {
 1479            if (!isNewTermsOfServiceAndEmailSubscriptionEnabled)
 480            {
 1481                SetVisibility(false, saveAvatar: true);
 1482                return;
 483            }
 484
 0485            dataStore.HUDs.signupVisible.Set(true);
 0486            view.SetSignUpStage(SignUpStage.SetNameAndEmail);
 0487            view.SetAvatarPreviewFocus(PreviewCameraFocus.FaceEditing);
 0488            view.PlayPreviewEmote(EMOTE_ID);
 0489            backpackAnalyticsService.SendAvatarEditSuccessNuxAnalytic();
 0490        }
 491
 492        private async UniTask SaveAsync(CancellationToken cancellationToken)
 493        {
 494            try
 495            {
 5496                await TakeSnapshotsAndSaveAvatarAsync(cancellationToken);
 497
 5498                if (dataStore.backpackV2.isWaitingToBeSavedAfterSignUp.Get())
 0499                    dataStore.backpackV2.isWaitingToBeSavedAfterSignUp.Set(false);
 500
 5501                CloseView();
 5502            }
 0503            catch (OperationCanceledException) { }
 504            catch (Exception e)
 505            {
 0506                Debug.LogException(e);
 0507                CloseView();
 0508            }
 5509        }
 510
 511        private UniTask TakeSnapshotsAndSaveAvatarAsync(CancellationToken cancellationToken)
 512        {
 5513            UniTaskCompletionSource task = new ();
 5514            isTakingSnapshot = true;
 515
 5516            TakeSnapshots(
 517                onSuccess: (face256Snapshot, bodySnapshot) =>
 518                {
 6519                    cancellationToken.ThrowIfCancellationRequested();
 6520                    SaveAvatar(face256Snapshot, bodySnapshot);
 6521                    isTakingSnapshot = false;
 6522                    task.TrySetResult();
 6523                },
 524                onFailed: () =>
 525                {
 0526                    cancellationToken.ThrowIfCancellationRequested();
 0527                    SaveAvatar(new Texture2D(256, 256), new Texture2D(256, 256));
 0528                    isTakingSnapshot = false;
 0529                    task.TrySetException(new Exception("Error taking avatar screenshots."));
 0530                });
 531
 5532            return task.Task;
 533        }
 534
 535        private void TakeSnapshots(IBackpackEditorHUDView.OnSnapshotsReady onSuccess, Action onFailed)
 536        {
 5537            view.TakeSnapshotsAfterStopPreviewAnimation(
 6538                onSuccess: (face256Snapshot, bodySnapshot) => onSuccess?.Invoke(face256Snapshot, bodySnapshot),
 0539                onFailed: () => onFailed?.Invoke());
 5540        }
 541
 542        private void SaveAvatar(Texture2D face256Snapshot, Texture2D bodySnapshot)
 543        {
 6544            var avatarModel = model.ToAvatarModel();
 545
 546            // Restore extended urns
 34547            for (var i = 0; i < avatarModel.wearables.Count; i++)
 548            {
 11549                string shortenedUrn = avatarModel.wearables[i];
 550
 11551                if (extendedWearableUrns.TryGetValue(shortenedUrn, out string urn))
 11552                    avatarModel.wearables[i] = urn;
 553            }
 554
 555            // Add the equipped emotes to the avatar model
 6556            List<AvatarModel.AvatarEmoteEntry> emoteEntries = new List<AvatarModel.AvatarEmoteEntry>();
 6557            int equippedEmotesCount = dataStore.emotesCustomization.unsavedEquippedEmotes.Count();
 558
 132559            for (var i = 0; i < equippedEmotesCount; i++)
 560            {
 60561                var equippedEmote = dataStore.emotesCustomization.unsavedEquippedEmotes[i];
 562
 60563                if (equippedEmote == null)
 564                    continue;
 565
 0566                string id = equippedEmote.id;
 567
 0568                if (extendedWearableUrns.TryGetValue(id, out string urn))
 0569                    id = urn;
 0570                else if (emotesCatalogService.TryGetOwnedUrn(id, out string extendedUrn))
 0571                    if (!string.IsNullOrEmpty(extendedUrn))
 0572                        id = extendedUrn;
 573
 0574                emoteEntries.Add(new AvatarModel.AvatarEmoteEntry { slot = i, urn = id });
 575            }
 576
 6577            avatarModel.emotes = emoteEntries;
 578
 6579            dataStore.emotesCustomization.equippedEmotes.Set(dataStore.emotesCustomization.unsavedEquippedEmotes.Get());
 580
 6581            userProfileBridge.SendSaveAvatar(avatarModel, face256Snapshot, bodySnapshot, dataStore.common.isSignUpFlow.G
 6582            ownUserProfile.OverrideAvatar(avatarModel, face256Snapshot);
 583
 6584            if (dataStore.common.isSignUpFlow.Get())
 585            {
 3586                dataStore.HUDs.signupVisible.Set(true);
 587
 3588                if (!isNewTermsOfServiceAndEmailSubscriptionEnabled)
 3589                    backpackAnalyticsService.SendAvatarEditSuccessNuxAnalytic();
 590            }
 591
 6592            avatarIsDirty = false;
 6593        }
 594
 595        private void SelectWearable(string wearableId)
 596        {
 0597            if (!wearablesCatalogService.WearablesCatalog.TryGetValue(wearableId, out WearableItem wearable))
 598            {
 0599                Debug.LogError($"Cannot pre-visualize wearable {wearableId}");
 0600                return;
 601            }
 0602        }
 603
 604        private void EquipWearableFromGrid(string wearableId, EquipWearableSource source)
 605        {
 13606            AudioScriptableObjects.equip.Play(true);
 13607            EquipWearable(wearableId, source);
 13608        }
 609
 610        private void UnEquipWearableFromGrid(string wearableId, UnequipWearableSource source)
 611        {
 3612            AudioScriptableObjects.unequip.Play(true);
 3613            UnEquipWearable(wearableId, source);
 3614        }
 615
 616        private void OnCategoryFilterRemoved() =>
 9617            view.SetColorPickerVisibility(false);
 618
 619        private void UnEquipWearableFromSlot(string wearableId, UnequipWearableSource source)
 620        {
 0621            AudioScriptableObjects.unequip.Play(true);
 0622            UnEquipWearable(wearableId, source);
 0623        }
 624
 625        private void EquipWearable(string wearableId,
 626            EquipWearableSource source,
 627            bool setAsDirty = true,
 628            bool updateAvatarPreview = true,
 629            bool resetOverride = true)
 630        {
 15631            string shortenedWearableId = ExtendedUrnParser.GetShortenedUrn(wearableId);
 632
 15633            if (!wearablesCatalogService.WearablesCatalog.TryGetValue(shortenedWearableId, out WearableItem wearable))
 634            {
 0635                if (!wearablesCatalogService.WearablesCatalog.TryGetValue(wearableId, out wearable))
 636                {
 0637                    Debug.LogError($"Cannot equip wearable {shortenedWearableId}");
 0638                    return;
 639                }
 640            }
 641
 15642            EquipWearable(wearableId, wearable, source, setAsDirty, updateAvatarPreview, resetOverride);
 15643        }
 644
 645        private void EquipWearable(
 646            string extendedWearableId,
 647            WearableItem wearable,
 648            EquipWearableSource source,
 649            bool setAsDirty = true,
 650            bool updateAvatarPreview = true,
 651            bool resetOverride = true)
 652        {
 25653            string shortenWearableId = ExtendedUrnParser.GetShortenedUrn(wearable.id);
 25654            extendedWearableUrns[shortenWearableId] = extendedWearableId;
 655
 25656            if (wearable.data.category == WearableLiterals.Categories.BODY_SHAPE)
 657            {
 5658                UnEquipCurrentBodyShape();
 5659                EquipBodyShape(wearable);
 5660                ReplaceIncompatibleWearablesWithDefaultWearables();
 661            }
 662            else
 663            {
 108664                foreach (var w in model.wearables.Values)
 665                {
 34666                    if (w.data.category != wearable.data.category)
 667                        continue;
 668
 0669                    UnEquipWearable(w.id, UnequipWearableSource.None);
 0670                    break;
 671                }
 672
 20673                model.wearables.Add(shortenWearableId, wearable);
 20674                previewEquippedWearables.Add(shortenWearableId);
 675
 20676                if (resetOverride)
 10677                    ResetOverridesOfAffectedCategories(wearable, setAsDirty);
 678
 20679                avatarSlotsHUDController.Equip(wearable, model.bodyShape.id, model.forceRender);
 20680                wearableGridController.Equip(shortenWearableId);
 681            }
 682
 25683            if (setAsDirty)
 15684                avatarIsDirty = true;
 685
 25686            if (source != EquipWearableSource.None)
 13687                backpackAnalyticsService.SendEquipWearableAnalytic(wearable.id, wearable.data.category, wearable.rarity,
 688
 25689            if (updateAvatarPreview)
 690            {
 13691                UpdateAvatarModel(model.ToAvatarModel());
 13692                categoryPendingToPlayEmote = wearable.data.category;
 693            }
 694
 25695            if (wearable.data.blockVrmExport && !vrmBlockingWearables.ContainsKey(shortenWearableId))
 696            {
 0697                vrmBlockingWearables.Add(shortenWearableId, wearable.CanBeUnEquipped());
 0698                UpdateVRMExportWarning();
 699            }
 25700        }
 701
 702        private void ReplaceIncompatibleWearablesWithDefaultWearables()
 703        {
 5704            WearableItem bodyShape = model.bodyShape;
 705
 5706            if (bodyShape == null) return;
 5707            if (!fallbackWearables.ContainsKey(bodyShape.id)) return;
 708
 5709            HashSet<string> replacedCategories = new ();
 710
 16711            foreach (var w in model.wearables.Values.ToArray())
 712            {
 3713                if (w.SupportsBodyShape(bodyShape.id)) continue;
 714
 2715                UnEquipWearable(w, UnequipWearableSource.None, true, false);
 716
 2717                string category = w.data.category;
 718
 2719                if (!string.IsNullOrEmpty(category))
 2720                    replacedCategories.Add(category);
 721            }
 722
 5723            Dictionary<string, string> fallbackWearablesByCategory = fallbackWearables[bodyShape.id];
 724
 14725            foreach (string category in replacedCategories)
 726            {
 2727                if (!fallbackWearablesByCategory.ContainsKey(category)) continue;
 2728                EquipWearable(fallbackWearablesByCategory[category], EquipWearableSource.None, true, false);
 729            }
 5730        }
 731
 732        private void UnEquipCurrentBodyShape(bool setAsDirty = true)
 733        {
 12734            if (model.bodyShape.id == NOT_LOADED) return;
 735
 2736            if (!wearablesCatalogService.WearablesCatalog.TryGetValue(model.bodyShape.id, out WearableItem wearable))
 737            {
 0738                Debug.LogError($"Cannot unequip body shape {model.bodyShape.id}");
 0739                return;
 740            }
 741
 2742            UnEquipWearable(wearable, UnequipWearableSource.None, setAsDirty);
 2743        }
 744
 745        private void UnEquipWearable(string wearableId, UnequipWearableSource source, bool setAsDirty = true, bool updat
 746        {
 3747            if (!wearablesCatalogService.WearablesCatalog.TryGetValue(wearableId, out WearableItem wearable))
 748            {
 0749                Debug.LogError($"Cannot unequip wearable {wearableId}");
 0750                return;
 751            }
 752
 3753            UnEquipWearable(wearable, source, setAsDirty, updateAvatarPreview);
 3754        }
 755
 756        private void UnEquipWearable(WearableItem wearable,
 757            UnequipWearableSource source = UnequipWearableSource.None,
 758            bool setAsDirty = true,
 759            bool updateAvatarPreview = true)
 760        {
 7761            string shortenedWearableId = ExtendedUrnParser.GetShortenedUrn(wearable.id);
 762
 7763            if (source != UnequipWearableSource.None)
 0764                backpackAnalyticsService.SendUnequippedWearableAnalytic(wearable.id, wearable.data.category, wearable.ra
 765
 7766            ResetOverridesOfAffectedCategories(wearable, setAsDirty);
 767
 7768            avatarSlotsHUDController.UnEquip(wearable.data.category, model.forceRender);
 7769            model.wearables.Remove(shortenedWearableId);
 7770            previewEquippedWearables.Remove(shortenedWearableId);
 7771            wearableGridController.UnEquip(shortenedWearableId);
 772
 7773            if (setAsDirty)
 7774                avatarIsDirty = true;
 775
 7776            if (updateAvatarPreview)
 5777                UpdateAvatarModel(model.ToAvatarModel());
 778
 7779            if (wearable.data.blockVrmExport)
 780            {
 0781                if (vrmBlockingWearables.Remove(shortenedWearableId))
 0782                    UpdateVRMExportWarning();
 783            }
 7784        }
 785
 786        private void UpdateVRMExportWarning()
 787        {
 25788            bool vrmWarningEnabled = vrmBlockingWearables.Count > 0;
 25789            view.SetWarningForVRMExportButton(vrmWarningEnabled);
 25790        }
 791
 792        private void ResetOverridesOfAffectedCategories(WearableItem wearable, bool setAsDirty = true)
 793        {
 17794            if (wearable.GetHidesList(ownUserProfile.avatar.bodyShape) != null)
 44795                foreach (string s in wearable.GetHidesList(ownUserProfile.avatar.bodyShape))
 5796                    UpdateOverrideHides(s, false, setAsDirty);
 17797        }
 798
 799        private void ToggleSlot(string slotCategory, bool supportColor, PreviewCameraFocus previewCameraFocus, bool isSe
 800        {
 3801            currentSlotSelected = isSelected ? slotCategory : null;
 3802            view.UpdateHideUnhideStatus(currentSlotSelected, model.forceRender);
 3803            view.SetColorPickerVisibility(isSelected && supportColor);
 804
 3805            if (isSelected && supportColor)
 3806                view.SetColorPickerAsSkinMode(slotCategory == WearableLiterals.Categories.BODY_SHAPE);
 807
 808            switch (slotCategory)
 809            {
 810                case WearableLiterals.Categories.EYES:
 1811                    view.SetColorPickerValue(model.eyesColor);
 1812                    break;
 813                case WearableLiterals.Categories.HAIR or WearableLiterals.Categories.EYEBROWS or WearableLiterals.Catego
 0814                    view.SetColorPickerValue(model.hairColor);
 0815                    break;
 816                case WearableLiterals.Categories.BODY_SHAPE:
 1817                    view.SetColorPickerValue(model.skinColor);
 818                    break;
 819            }
 820
 3821            view.SetAvatarPreviewFocus(currentSlotSelected != null ? previewCameraFocus : PreviewCameraFocus.DefaultEdit
 3822        }
 823
 824        private void OnWearableColorChanged(Color newColor)
 825        {
 0826            var colorChanged = false;
 827
 0828            switch (currentSlotSelected)
 829            {
 830                case WearableLiterals.Categories.EYES:
 0831                    model.eyesColor = newColor;
 0832                    colorChanged = true;
 0833                    break;
 834                case WearableLiterals.Categories.HAIR or WearableLiterals.Categories.EYEBROWS or WearableLiterals.Catego
 0835                    model.hairColor = newColor;
 0836                    colorChanged = true;
 0837                    break;
 838                case WearableLiterals.Categories.BODY_SHAPE:
 0839                    model.skinColor = newColor;
 0840                    colorChanged = true;
 841                    break;
 842            }
 843
 0844            if (!colorChanged)
 0845                return;
 846
 0847            avatarIsDirty = true;
 0848            UpdateAvatarModel(model.ToAvatarModel());
 0849        }
 850
 851        private void SetAllColors(Color eyesColor, Color hairColor, Color bodyColor)
 852        {
 0853            model.eyesColor = eyesColor;
 0854            model.hairColor = hairColor;
 0855            model.skinColor = bodyColor;
 856
 0857            avatarIsDirty = true;
 0858            UpdateAvatarModel(model.ToAvatarModel());
 0859        }
 860
 861        private void UpdateAvatarModel(AvatarModel avatarModel)
 862        {
 26863            view.UpdateAvatarPreview(avatarModel);
 26864            outfitsController.UpdateAvatarPreview(model.ToAvatarModel(extendedWearableUrns));
 26865        }
 866
 867        private void OnColorPickerToggled() =>
 0868            backpackAnalyticsService.SendAvatarColorPick();
 869
 870        private void OnAvatarUpdated()
 871        {
 0872            if (string.IsNullOrEmpty(categoryPendingToPlayEmote))
 0873                return;
 874
 0875            PlayEquipAnimation(categoryPendingToPlayEmote);
 0876            categoryPendingToPlayEmote = null;
 0877        }
 878
 879        private void PlayEquipAnimation(string category)
 880        {
 0881            view.PlayPreviewEmote(
 882                GetEquipEmoteByCategory(category),
 883                DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
 0884        }
 885
 886        private string GetEquipEmoteByCategory(string category)
 887        {
 0888            string equipEmote = category switch
 889                                {
 0890                                    WearableLiterals.Categories.FEET => GetRandomizedName(WearableLiterals.DefaultEmotes
 0891                                    WearableLiterals.Categories.LOWER_BODY => GetRandomizedName(WearableLiterals.Default
 0892                                    WearableLiterals.Categories.UPPER_BODY => GetRandomizedName(WearableLiterals.Default
 0893                                    WearableLiterals.Categories.EYEWEAR => GetRandomizedName(WearableLiterals.DefaultEmo
 0894                                    WearableLiterals.Categories.TIARA => GetRandomizedName(WearableLiterals.DefaultEmote
 0895                                    WearableLiterals.Categories.EARRING => GetRandomizedName(WearableLiterals.DefaultEmo
 0896                                    WearableLiterals.Categories.HAT => GetRandomizedName(WearableLiterals.DefaultEmotes.
 0897                                    WearableLiterals.Categories.TOP_HEAD => GetRandomizedName(WearableLiterals.DefaultEm
 0898                                    WearableLiterals.Categories.HELMET => GetRandomizedName(WearableLiterals.DefaultEmot
 0899                                    WearableLiterals.Categories.MASK => GetRandomizedName(WearableLiterals.DefaultEmotes
 0900                                    WearableLiterals.Categories.SKIN => GetRandomizedName(WearableLiterals.DefaultEmotes
 0901                                    _ => string.Empty,
 902                                };
 903
 0904            return equipEmote;
 905        }
 906
 907        private string GetRandomizedName(string baseString, int limit)
 908        {
 0909            currentAnimationIndexShown = (currentAnimationIndexShown + 1) % limit;
 0910            return $"{baseString}{(currentAnimationIndexShown + 1).ToString()}";
 911        }
 912
 913        private void OnVrmExport()
 914        {
 0915            if(vrmBlockingWearables.Count > 0) return;
 916
 0917            vrmExportCts?.SafeCancelAndDispose();
 0918            vrmExportCts = new CancellationTokenSource();
 0919            VrmExport(vrmExportCts.Token).Forget();
 0920        }
 921
 922        private void OnSignUpBack(SignUpStage stage)
 923        {
 924            switch (stage)
 925            {
 926                default:
 927                case SignUpStage.CustomizeAvatar:
 1928                    userProfileBridge.LogOut();
 1929                    break;
 930                case SignUpStage.SetNameAndEmail:
 1931                    dataStore.HUDs.signupVisible.Set(false);
 1932                    view.SetSignUpStage(SignUpStage.CustomizeAvatar);
 1933                    view.SetAvatarPreviewFocus(PreviewCameraFocus.DefaultEditing);
 934                    break;
 935            }
 1936        }
 937
 938        internal async UniTask VrmExport(CancellationToken ct)
 939        {
 940            const int SUCCESS_TOAST_ACTIVE_TIME = 2000;
 941
 942            try
 943            {
 1944                view?.SetVRMButtonEnabled(false);
 1945                view?.SetVRMSuccessToastActive(false);
 946
 1947                backpackAnalyticsService.SendVRMExportStarted();
 948
 1949                StringBuilder reference = new StringBuilder();
 950
 951                try
 952                {
 1953                    var wearables = await this.ownUserProfile.avatar.wearables.Select(x => this.wearablesCatalogService.
 954
 2955                    foreach (WearableItem wearableItem in wearables)
 956                    {
 0957                        reference.AppendLine(string.Join(":",
 958                            wearableItem.data.category,
 959                            wearableItem.GetName(),
 960                            wearableItem.GetMarketplaceLink()
 961                        ));
 962                    }
 1963                }
 0964                catch (Exception)
 965                {
 966                    // ignored
 0967                }
 968
 1969                byte[] bytes = await vrmExporter.Export($"{this.ownUserProfile.userName} Avatar", reference.ToString(), 
 970
 1971                string fileName = $"{this.ownUserProfile.userName.Replace("#", "_")}_{DateTime.Now.ToString("yyyyMMddhhm
 1972                await fileBrowser.SaveFileAsync("Save your VRM", Application.persistentDataPath, fileName, bytes, new Ex
 973
 1974                view?.SetVRMSuccessToastActive(true);
 3975                await UniTask.Delay(SUCCESS_TOAST_ACTIVE_TIME, cancellationToken: ct);
 1976            }
 0977            catch (OperationCanceledException) { }
 978            finally
 979            {
 1980                view?.SetVRMButtonEnabled(true);
 1981                view?.SetVRMSuccessToastActive(false);
 982            }
 983
 1984            backpackAnalyticsService.SendVRMExportSucceeded();
 1985        }
 986    }
 987}

Methods/Properties

BackpackEditorHUDController(DCL.Backpack.IBackpackEditorHUDView, DCL.DataStore, RendererState, IUserProfileBridge, DCLServices.WearablesCatalogService.IWearablesCatalogService, DCL.Backpack.IBackpackEmotesSectionController, DCL.Backpack.IBackpackAnalyticsService, DCL.Backpack.WearableGridController, DCL.Backpack.AvatarSlotsHUDController, DCL.Backpack.OutfitsController, MainScripts.DCL.Components.Avatar.VRMExporter.IVRMExporter, DCL.Backpack.VRMDetailsController, DCLServices.DCLFileBrowser.IDCLFileBrowserService, IEmotesCatalogService)
isNewTermsOfServiceAndEmailSubscriptionEnabled()
previewEquippedWearables()
ownUserProfile()
OnVRMDetailsOpened()
OnOutfitEquipped(DCL.Backpack.OutfitItem)
LoadAndEquipOutfitWearables()
OnOutfitsOpened()
Dispose()
UpdateOverrideHides(System.String, System.Boolean)
UpdateOverrideHides(System.String, System.Boolean, System.Boolean)
OnBackpackVisibleChanged(System.Boolean, System.Boolean)
<SetVisibility()
SetVisibility(System.Boolean, System.Boolean)
CloseView()
ConfigureBackpackInFullscreenMenuChanged(UnityEngine.Transform, UnityEngine.Transform)
SaveBackpackBeforeSignUpFinishes(System.Boolean, System.Boolean)
LoadUserProfileFromProfileUpdate(UserProfile)
LoadUserProfile(UserProfile)
<LoadUserProfile()
UpdateAvatarPreview()
OnNewEmoteAdded(System.String)
OnEmotePreviewed(System.String)
OnEmoteEquipped(System.String)
OnEmoteUnEquipped(System.String)
EquipBodyShape(WearableItem, System.Boolean)
SaveAvatarAndContinueSignupProcess()
SaveAsync()
TakeSnapshotsAndSaveAvatarAsync(System.Threading.CancellationToken)
TakeSnapshots(DCL.Backpack.IBackpackEditorHUDView/OnSnapshotsReady, System.Action)
SaveAvatar(UnityEngine.Texture2D, UnityEngine.Texture2D)
SelectWearable(System.String)
EquipWearableFromGrid(System.String, DCL.Backpack.EquipWearableSource)
UnEquipWearableFromGrid(System.String, DCL.Backpack.UnequipWearableSource)
OnCategoryFilterRemoved()
UnEquipWearableFromSlot(System.String, DCL.Backpack.UnequipWearableSource)
EquipWearable(System.String, DCL.Backpack.EquipWearableSource, System.Boolean, System.Boolean, System.Boolean)
EquipWearable(System.String, WearableItem, DCL.Backpack.EquipWearableSource, System.Boolean, System.Boolean, System.Boolean)
ReplaceIncompatibleWearablesWithDefaultWearables()
UnEquipCurrentBodyShape(System.Boolean)
UnEquipWearable(System.String, DCL.Backpack.UnequipWearableSource, System.Boolean, System.Boolean)
UnEquipWearable(WearableItem, DCL.Backpack.UnequipWearableSource, System.Boolean, System.Boolean)
UpdateVRMExportWarning()
ResetOverridesOfAffectedCategories(WearableItem, System.Boolean)
ToggleSlot(System.String, System.Boolean, MainScripts.DCL.Controllers.HUD.CharacterPreview.PreviewCameraFocus, System.Boolean)
OnWearableColorChanged(UnityEngine.Color)
SetAllColors(UnityEngine.Color, UnityEngine.Color, UnityEngine.Color)
UpdateAvatarModel(AvatarModel)
OnColorPickerToggled()
OnAvatarUpdated()
PlayEquipAnimation(System.String)
GetEquipEmoteByCategory(System.String)
GetRandomizedName(System.String, System.Int32)
OnVrmExport()
OnSignUpBack(DCL.Backpack.SignUpStage)
VrmExport()