198 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			198 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using Unity.Multiplayer.Center.Analytics; | ||
|  | using Unity.Multiplayer.Center.Common; | ||
|  | using Unity.Multiplayer.Center.Questionnaire; | ||
|  | using Unity.Multiplayer.Center.Recommendations; | ||
|  | using Unity.Multiplayer.Center.Window.UI; | ||
|  | using Unity.Multiplayer.Center.Window.UI.RecommendationView; | ||
|  | using UnityEditor; | ||
|  | using UnityEngine; | ||
|  | using UnityEngine.UIElements; | ||
|  | 
 | ||
|  | namespace Unity.Multiplayer.Center.Window | ||
|  | { | ||
|  |     internal class RecommendationTabView : ITabView | ||
|  |     { | ||
|  |         QuestionnaireView m_QuestionnaireView; | ||
|  |         RecommendationView m_RecommendationView; | ||
|  | 
 | ||
|  |         RecommendationViewBottomBar m_BottomBarView; | ||
|  | 
 | ||
|  |         bool m_IsVisible; | ||
|  |         bool m_ShouldRefresh = true; | ||
|  | 
 | ||
|  |         [SerializeField] | ||
|  |         PreReleaseHandling m_PreReleaseHandling = new(); | ||
|  | 
 | ||
|  |         [field: SerializeField] // Marked as redundant by Rider, but it is not. | ||
|  |         public string Name { get; private set; } | ||
|  | 
 | ||
|  |         public VisualElement RootVisualElement { get; set; } | ||
|  |         public IMultiplayerCenterAnalytics MultiplayerCenterAnalytics { get; set; } | ||
|  | 
 | ||
|  |         // Access to QuestionnaireView for testing purposes | ||
|  |         internal QuestionnaireView QuestionnaireView => m_QuestionnaireView; | ||
|  | 
 | ||
|  |         public RecommendationTabView(string name = "Recommendation") | ||
|  |         { | ||
|  |             Name = name; | ||
|  |             m_PreReleaseHandling.OnAllChecksFinished += PatchData; | ||
|  |             m_PreReleaseHandling.CheckForUpdates(); | ||
|  |             Undo.undoRedoPerformed += OnUndoRedoPerformed; | ||
|  |         } | ||
|  | 
 | ||
|  |         void OnUndoRedoPerformed() | ||
|  |         { | ||
|  |             UserChoicesObject.instance.Save(); | ||
|  |             m_QuestionnaireView?.Refresh(); | ||
|  |             UpdateRecommendation(keepSelection:true); | ||
|  |         } | ||
|  | 
 | ||
|  |         public void Clear() | ||
|  |         { | ||
|  |             Undo.undoRedoPerformed -= OnUndoRedoPerformed; | ||
|  |             m_RecommendationView?.Clear(); | ||
|  |             m_QuestionnaireView?.Clear(); | ||
|  |             RootVisualElement?.Clear(); | ||
|  |             m_QuestionnaireView = null; | ||
|  |             m_RecommendationView = null; | ||
|  |         } | ||
|  | 
 | ||
|  |         public void SetVisible(bool visible) | ||
|  |         { | ||
|  |             RootVisualElement.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None; | ||
|  |             m_IsVisible = visible; | ||
|  |         } | ||
|  | 
 | ||
|  |         public void Refresh() | ||
|  |         { | ||
|  |             Debug.Assert(MultiplayerCenterAnalytics != null, "MultiplayerCenterAnalytics != null"); | ||
|  |             RefreshPreReleaseHandling(); | ||
|  |             if (!m_ShouldRefresh && RootVisualElement.childCount > 0) return; | ||
|  |             CreateStandardView(); | ||
|  |             m_ShouldRefresh = false; | ||
|  |         } | ||
|  | 
 | ||
|  |         void RefreshPreReleaseHandling() | ||
|  |         { | ||
|  |             if (!m_PreReleaseHandling.IsReady) | ||
|  |             { | ||
|  |                 m_PreReleaseHandling.OnAllChecksFinished += PatchData; | ||
|  |                 m_PreReleaseHandling.CheckForUpdates(); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 m_PreReleaseHandling.PatchRecommenderSystemData(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void CreateStandardView() | ||
|  |         { | ||
|  |             Clear(); | ||
|  |             Undo.undoRedoPerformed -= OnUndoRedoPerformed; | ||
|  |             Undo.undoRedoPerformed += OnUndoRedoPerformed; | ||
|  |             MigrateUserChoices(); | ||
|  | 
 | ||
|  |             // We need this because Bottom bar is a part of the Recommendations Tab and it should always stay | ||
|  |             // at the bottom of the view. So we need to make sure that the root tab element is always 100% height. | ||
|  |             RootVisualElement.style.height = Length.Percent(100); | ||
|  | 
 | ||
|  |             var horizontalContainer = new TwoPaneSplitView(0, 250, TwoPaneSplitViewOrientation.Horizontal); | ||
|  |             horizontalContainer.AddToClassList(StyleClasses.MainSplitView); | ||
|  |             horizontalContainer.name = "recommendation-tab-split-view"; | ||
|  | 
 | ||
|  |             // This is used to make sure the left side does not grow to 100% as this is what would happen by default. | ||
|  |             // It feels not 100% correct. But it seems to be the only way to match the height of the 2 sides with how | ||
|  |             // our views are build currently. | ||
|  |             horizontalContainer.contentContainer.style.position = Position.Relative; | ||
|  | 
 | ||
|  |             m_QuestionnaireView = new QuestionnaireView(QuestionnaireObject.instance.Questionnaire); | ||
|  |             m_QuestionnaireView.OnQuestionnaireDataChanged += HandleQuestionnaireDataChanged; | ||
|  |             m_QuestionnaireView.OnPresetSelected += OnPresetSelected; | ||
|  |             m_QuestionnaireView.Root.AddToClassList(StyleClasses.MainSplitViewLeft); | ||
|  |             horizontalContainer.Add(m_QuestionnaireView.Root); | ||
|  | 
 | ||
|  |             m_RecommendationView = new RecommendationView(); | ||
|  |             m_RecommendationView.Root.AddToClassList(StyleClasses.MainSplitViewRight); | ||
|  |             horizontalContainer.Add(m_RecommendationView.Root); | ||
|  |             RootVisualElement.Add(horizontalContainer); | ||
|  | 
 | ||
|  |             m_BottomBarView = new RecommendationViewBottomBar(MultiplayerCenterAnalytics); | ||
|  |             m_RecommendationView.OnPackageSelectionChanged +=  | ||
|  |                 () => m_BottomBarView.UpdatePackagesToInstall(m_RecommendationView.CurrentRecommendation, m_RecommendationView.AllPackages); | ||
|  |             RootVisualElement.Add(m_BottomBarView); | ||
|  |             UpdateRecommendation(keepSelection: true); | ||
|  |              | ||
|  |             m_BottomBarView.SetInfoTextForCheckingPackages(!m_PreReleaseHandling.IsReady); | ||
|  |         } | ||
|  | 
 | ||
|  |         void HandleQuestionnaireDataChanged() | ||
|  |         { | ||
|  |             UpdateRecommendation(keepSelection: false); | ||
|  |         } | ||
|  | 
 | ||
|  |         static void MigrateUserChoices() | ||
|  |         { | ||
|  |             var questionnaire = QuestionnaireObject.instance.Questionnaire; | ||
|  |             var userChoices = UserChoicesObject.instance; | ||
|  | 
 | ||
|  |             // make sure the version of the questionnaire is the same as the one in the user choices. | ||
|  |             if (questionnaire.Version != userChoices.QuestionnaireVersion && userChoices.UserAnswers.Answers.Count > 0) | ||
|  |             { | ||
|  |                 Logic.MigrateUserChoices(questionnaire, userChoices); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void UpdateRecommendation(bool keepSelection) | ||
|  |         { | ||
|  |             var questionnaire = QuestionnaireObject.instance.Questionnaire; | ||
|  |             var userChoices = UserChoicesObject.instance; | ||
|  | 
 | ||
|  |             var errors = Logic.ValidateAnswers(questionnaire, userChoices.UserAnswers); | ||
|  |             foreach (var error in errors) | ||
|  |             { | ||
|  |                 Debug.LogError(error); | ||
|  |             } | ||
|  | 
 | ||
|  |             var recommendation = userChoices.Preset == Preset.None? null | ||
|  |                 : RecommenderSystem.GetRecommendation(questionnaire, userChoices.UserAnswers); | ||
|  |             m_PreReleaseHandling.PatchPackages(recommendation); | ||
|  |             if (keepSelection) | ||
|  |             { | ||
|  |                 RecommendationUtils.ApplyPreviousSelection(recommendation, userChoices.SelectedSolutions); | ||
|  |             } | ||
|  |             else if (recommendation != null) // we only send the event if there is a recommendation and it is a new one | ||
|  |             { | ||
|  |                 MultiplayerCenterAnalytics.SendRecommendationEvent(userChoices.UserAnswers, userChoices.Preset); | ||
|  |             } | ||
|  | 
 | ||
|  |             m_RecommendationView.UpdateRecommendation(recommendation, m_PreReleaseHandling); | ||
|  |             m_BottomBarView.UpdatePackagesToInstall(recommendation, m_RecommendationView.AllPackages); | ||
|  |         } | ||
|  | 
 | ||
|  |         void PatchData() | ||
|  |         { | ||
|  |             m_PreReleaseHandling.PatchRecommenderSystemData(); | ||
|  |             m_PreReleaseHandling.OnAllChecksFinished -= PatchData; | ||
|  |             m_ShouldRefresh = true; | ||
|  |             if(m_IsVisible)  | ||
|  |                 Refresh(); | ||
|  |         } | ||
|  | 
 | ||
|  |         void OnPresetSelected(Preset preset) | ||
|  |         { | ||
|  |             var (resultAnswerData, recommendation) = Logic.ApplyPresetToAnswerData( | ||
|  |                 UserChoicesObject.instance.UserAnswers, preset, QuestionnaireObject.instance.Questionnaire); | ||
|  | 
 | ||
|  |             UserChoicesObject.instance.UserAnswers = resultAnswerData; | ||
|  |             UserChoicesObject.instance.Save(); | ||
|  | 
 | ||
|  |             if (recommendation != null) | ||
|  |                 MultiplayerCenterAnalytics.SendRecommendationEvent(resultAnswerData, preset); | ||
|  | 
 | ||
|  |             m_QuestionnaireView.Refresh(); | ||
|  |             m_RecommendationView.UpdateRecommendation(recommendation, m_PreReleaseHandling); | ||
|  |         } | ||
|  |     } | ||
|  | } |