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);
|
||
|
}
|
||
|
}
|
||
|
}
|