using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using Unity.Cloud.Collaborate.Assets; using Unity.Cloud.Collaborate.Components; using Unity.Cloud.Collaborate.Models.Structures; using Unity.Cloud.Collaborate.Presenters; using Unity.Cloud.Collaborate.UserInterface; using Unity.Cloud.Collaborate.Utilities; using Unity.Cloud.Collaborate.Views.Adapters.ListAdapters; using UnityEditor; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UIElements; namespace Unity.Cloud.Collaborate.Views { internal class HistoryTabPageView : TabPageComponent, IHistoryView { [CanBeNull] IHistoryPresenter m_Presenter; public const string UssClassName = "history-page"; public const string PaginatorUssClassName = UssClassName + "__paginator"; public const string ContentUssClassName = UssClassName + "__content"; public const string NoticeUssClassName = UssClassName + "__notice"; static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(HistoryTabPageView)}.uxml"; readonly ScrollView m_Content; readonly ListNotice m_ListNotice; readonly Paginator m_Paginator; bool m_Active; public HistoryTabPageView() { AddToClassList(UssClassName); AssetDatabase.LoadAssetAtPath(k_LayoutPath).CloneTree(this); m_Paginator = this.Q(className: PaginatorUssClassName); m_Paginator.AddToClassList(UiConstants.ussHidden); m_Paginator.ClickedMovePage += OnClickedMovePage; m_Content = this.Q(className: ContentUssClassName); // Add loading notice m_ListNotice = this.Q(className: NoticeUssClassName); m_ListNotice.Text = StringAssets.loadingRevisions; } /// public IHistoryPresenter Presenter { set { m_Presenter = value; // If tab active before presenter has been added, call start once we have it. if (Active) { m_Presenter.Start(); } } } /// public void SetBusyStatus(bool busy) { m_Paginator.SetEnabled(!busy); m_Content.SetEnabled(!busy); } /// public void SetHistoryList(IReadOnlyList list) { // Clear out old content m_ListNotice.AddToClassList(UiConstants.ussHidden); m_Content.Clear(); // Show paginator m_Paginator.RemoveFromClassList(UiConstants.ussHidden); // Handle empty list case if (list.Count == 0) { m_ListNotice.Text = StringAssets.noticeNoRevisionsToDisplay; m_Content.RemoveFromClassList(UiConstants.ussHidden); return; } foreach (var entry in list) { // Add entry to the list m_Content.Add(CreateHistoryEntry(entry, false)); } } /// /// Event handler for receiving page change requests. /// /// /// Delta to change the page by: Paginator.MoveForwards, Paginator.MoveBackwards. Mapped to +1, -1 respectively. /// void OnClickedMovePage(int pageChange) { Assert.IsNotNull(m_Presenter, "Invalid state when requesting page change."); if (pageChange == Paginator.MoveBackwards) { m_Presenter.PrevPage(); } else { m_Presenter.NextPage(); } } /// public void SetPage(int page, int max) { m_Paginator.SetPage(page, max); } /// public void SetSelection(IHistoryEntry entry) { // Hide paginator m_Paginator.AddToClassList(UiConstants.ussHidden); // Clear out old content m_ListNotice.AddToClassList(UiConstants.ussHidden); m_Content.Clear(); // Add new content m_Content.Add(CreateHistoryEntry(entry, true)); } /// /// Takes a IHistoryEntry and binds it to a created HistoryEntryComponent to be used in the history list. /// /// History entry to bind /// Whether or not to show its list of changed entries. /// Inflated and bound component. HistoryEntryComponent CreateHistoryEntry([NotNull] IHistoryEntry entry, bool expanded) { Assert.IsNotNull(m_Presenter, "Invalid state when creating history entry"); var comp = new HistoryEntryComponent(); // Handle expanded vs compact layout if (expanded) { // Hide fields used for compact view comp.showFilesButton.AddToClassList(UiConstants.ussHidden); comp.cloudStatusText.AddToClassList(UiConstants.ussHidden); comp.changedFilesCount.text = $"Changes ( {entry.Changes.Count} )"; var listAdapter = new HistoryEntryChangeListAdapter(m_Presenter, entry.RevisionId, entry.Changes.ToList()); comp.changedFiles.SetAdapter(listAdapter); listAdapter.NotifyDataSetChanged(); // Configure button comp.gotoButton.text = entry.GetGotoText(); comp.gotoButton.clickable.clicked += () => m_Presenter.RequestGoto(entry.RevisionId, entry.Status); } else { // Hide fields used for expanded view comp.changedFilesCount.AddToClassList(UiConstants.ussHidden); comp.changedFiles.AddToClassList(UiConstants.ussHidden); comp.gotoButton.text = string.Empty; comp.gotoButton.AddToClassList(UiConstants.ussHidden); // Setup show button comp.showFilesButton.text = entry.Changes.Count == 1 ? StringAssets.showChange : string.Format(StringAssets.showChanges, entry.Changes.Count); comp.showFilesButton.clickable.clicked += () => m_Presenter.SelectedRevisionId = entry.RevisionId; // TODO: cloud status text } // Trim whitespace on either side and grab initial for profile circle var trimmedAuthorName = entry.AuthorName.Trim(); comp.profileInitial.text = trimmedAuthorName.Substring(0, 1).ToUpper(); comp.authorName.text = trimmedAuthorName; // Display relative or absolute timestamp. If relative, show absolute as a tooltip. comp.timestamp.text = TimeStamp.GetTimeStamp(entry.Time); if (TimeStamp.UseRelativeTimeStamps) { comp.timestamp.tooltip = TimeStamp.GetLocalisedTimeStamp(entry.Time); } // Display revision id and show full length id as a tooltip comp.revisionId.text = $"ID: {entry.RevisionId.Substring(0, 10)}"; comp.revisionId.tooltip = entry.RevisionId; comp.commitMessage.text = entry.Message; return comp; } /// public bool DisplayDialogue(string title, string message, string affirmative) { return EditorUtility.DisplayDialog(title, message, affirmative); } /// public bool DisplayDialogue(string title, string message, string affirmative, string negative) { return EditorUtility.DisplayDialog(title, message, affirmative, negative); } /// protected override void SetActive() { Assert.IsFalse(m_Active, "The view is already active."); m_Active = true; m_Presenter?.Start(); } /// protected override void SetInactive() { Assert.IsTrue(m_Active, "The view is already inactive."); m_Active = false; m_Presenter?.Stop(); } } }