Commit 1c847220 authored by jang dong hyeok's avatar jang dong hyeok
Browse files

.

parent 076f0c68
using System;
namespace Unity.Cloud.Collaborate.Models.Structures
{
internal struct ProgressInfo : IProgressInfo
{
public ProgressInfo(string title = default, string details = default, int currentCount = default, int totalCount = default, string lastErrorString = default, ulong lastError = default, bool canCancel = false, bool percentageProgressType = false, int percentageComplete = default)
{
Title = title;
Details = details;
CurrentCount = currentCount;
TotalCount = totalCount;
LastErrorString = lastErrorString;
LastError = lastError;
CanCancel = canCancel;
PercentageProgressType = percentageProgressType;
PercentageComplete = percentageComplete;
}
public string Title { get; }
public string Details { get; }
public int CurrentCount { get; }
public int TotalCount { get; }
public string LastErrorString { get; }
public ulong LastError { get; }
public bool CanCancel { get; }
public bool PercentageProgressType { get; }
public int PercentageComplete { get; }
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components.Menus;
using Unity.Cloud.Collaborate.Models;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Utilities;
using Unity.Cloud.Collaborate.Views;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Presenters
{
internal class ChangesPresenter : IChangesPresenter
{
[NotNull]
readonly IChangesView m_View;
[NotNull]
readonly IChangesModel m_Model;
[NotNull]
readonly IMainModel m_MainModel;
bool m_IsStarted;
public ChangesPresenter([NotNull] IChangesView view, [NotNull] IChangesModel model, [NotNull] IMainModel mainModel)
{
m_View = view;
m_Model = model;
m_MainModel = mainModel;
}
/// <inheritdoc />
public void Start()
{
Assert.IsFalse(m_IsStarted, "The presenter has already been started.");
m_IsStarted = true;
m_Model.UpdatedChangeList += OnUpdatedChangeList;
m_Model.OnUpdatedSelectedChanges += OnUpdatedPartiallySelectedChanges;
m_Model.BusyStatusUpdated += OnBusyStatusUpdated;
m_Model.StateChanged += OnStateChanged;
m_MainModel.RemoteRevisionsAvailabilityChange += OnRemoteRevisionsAvailabilityChange;
m_MainModel.ConflictStatusChange += OnConflictStatusChange;
PopulateInitialData();
}
/// <inheritdoc />
public void Stop()
{
Assert.IsTrue(m_IsStarted, "The presenter has already been stopped.");
m_IsStarted = false;
m_Model.UpdatedChangeList -= OnUpdatedChangeList;
m_Model.OnUpdatedSelectedChanges -= OnUpdatedPartiallySelectedChanges;
m_Model.BusyStatusUpdated -= OnBusyStatusUpdated;
m_Model.StateChanged -= OnStateChanged;
m_MainModel.RemoteRevisionsAvailabilityChange -= OnRemoteRevisionsAvailabilityChange;
m_MainModel.ConflictStatusChange -= OnConflictStatusChange;
}
/// <summary>
/// Refresh state from the model.
/// </summary>
void OnStateChanged()
{
PopulateInitialData();
}
/// <summary>
/// Populate the view with the initial data from the model.
/// </summary>
void PopulateInitialData()
{
m_View.SetRevisionSummary(m_Model.SavedRevisionSummary);
m_View.SetSearchQuery(m_Model.SavedSearchQuery);
m_View.SetBusyStatus(m_Model.Busy);
m_Model.RequestInitialData();
}
/// <summary>
/// Event handler to receive updated remote changes available status.
/// </summary>
/// <param name="available">Whether or not remote changes are available.</param>
protected void OnRemoteRevisionsAvailabilityChange(bool available)
{
UpdatePublishButton();
}
/// <summary>
/// Event handler to receive updated busy status.
/// </summary>
/// <param name="busy">New busy status.</param>
void OnBusyStatusUpdated(bool busy)
{
m_View.SetBusyStatus(busy);
}
/// <summary>
/// Event handler for when the model reports an updated change list.
/// </summary>
protected void OnUpdatedChangeList()
{
UpdatePublishButton();
UpdateChangeList();
}
/// <summary>
/// Request the change or conflict list depending on the state of the model. The result is then given to the
/// view to populate itself. Fire and forget method -- must be run on main thread.
/// </summary>
void UpdateChangeList()
{
Assert.IsTrue(Threading.IsMainThread, "Updating the change lists must be done from the main thread.");
// Fetch and send data to the UI depending on what's the current display mode.
if (m_Model.Conflicted)
{
Task.Run(() => m_Model.GetConflictedEntries(m_Model.SavedSearchQuery))
.ContinueWith(r => m_View.SetConflicts(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
Task.Run(() => m_Model.GetAllEntries(m_Model.SavedSearchQuery))
.ContinueWith(r => m_View.SetChanges(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
}
/// <summary>
/// Inform view to refresh its selections.
/// </summary>
protected void OnUpdatedPartiallySelectedChanges()
{
UpdatePublishButton();
m_View.SetSelectedChanges();
}
/// <summary>
/// Update changelist display in response to the conflict status changing.
/// </summary>
/// <param name="conflicted">New conflicted status.</param>
protected void OnConflictStatusChange(bool conflicted)
{
UpdatePublishButton();
UpdateChangeList();
}
/// <inheritdoc />
public bool UpdateEntryToggle(string path, bool toggled)
{
var result = m_Model.UpdateEntryToggle(path, toggled);
m_View.SetToggledCount(ToggledCount);
UpdatePublishButton();
return result;
}
/// <inheritdoc />
public int ToggledCount => m_Model.ToggledCount;
/// <inheritdoc />
public int TotalCount => m_Model.TotalCount;
/// <inheritdoc />
public int ConflictedCount => m_Model.ConflictedCount;
/// <inheritdoc />
public bool Searching => !string.IsNullOrEmpty(m_Model.SavedSearchQuery);
/// <inheritdoc />
public void RequestPublish()
{
Assert.IsFalse(Searching, "Cannot publish while searching");
m_Model.RequestPublish(m_Model.SavedRevisionSummary, m_Model.GetToggledEntries().Select(i => i.Entry).ToList());
}
/// <inheritdoc />
public void RequestDiscard(IChangeEntry entry)
{
if (m_View.DisplayDialogue(StringAssets.confirmDiscardChangesTitle,
StringAssets.confirmDiscardChangeMessage, StringAssets.discardChanges,
StringAssets.cancel))
{
m_Model.RequestDiscard(entry);
}
}
/// <summary>
/// Discard all toggled entries. Fire and forget method -- must be called on main thread.
/// </summary>
void RequestDiscardToggled()
{
var entries = m_Model.GetToggledEntries(m_Model.SavedSearchQuery).Select(e => e.Entry).ToList();
if (m_View.DisplayDialogue(StringAssets.confirmDiscardChangesTitle,
string.Format(StringAssets.confirmDiscardChangesMessage, entries.Count), StringAssets.discardChanges,
StringAssets.cancel))
{
m_Model.RequestBulkDiscard(entries);
}
}
/// <summary>
/// Update the state of the publish button in the view based on the state of the model.
/// </summary>
void UpdatePublishButton()
{
if (m_Model.Conflicted)
{
m_View.SetPublishEnabled(false, StringAssets.cannotPublishWhileConflicted);
}
else if (m_MainModel.RemoteRevisionsAvailable)
{
m_View.SetPublishEnabled(false, StringAssets.cannotPublishWithIncomingChanges);
}
else if (m_Model.ToggledCount < 1)
{
m_View.SetPublishEnabled(false, StringAssets.cannotPublishWithoutFiles);
}
else if (Searching)
{
m_View.SetPublishEnabled(false, StringAssets.cannotPublishWhileSearching);
}
else
{
m_View.SetPublishEnabled(true);
}
}
/// <inheritdoc />
public void RequestDiffChanges(string path)
{
m_Model.RequestDiffChanges(path);
}
/// <inheritdoc />
public void SetSearchQuery(string query)
{
var value = StringUtility.TrimAndToLower(query);
m_Model.SavedSearchQuery = value;
m_View.SetSearchQuery(query);
UpdateChangeList();
UpdatePublishButton();
}
/// <inheritdoc />
public void SetRevisionSummary(string message)
{
m_View.SetRevisionSummary(message);
m_Model.SavedRevisionSummary = message;
}
/// <inheritdoc />
public int GroupOverflowEntryCount => 1;
/// <inheritdoc />
public void OnClickGroupOverflow(float x, float y)
{
new FloatingMenu()
.AddEntry(StringAssets.menuDiscardToggledChanges, RequestDiscardToggled, ToggledCount > 0)
.SetOpenDirection(MenuUtilities.OpenDirection.DownLeft)
.Open(x, y);
}
/// <inheritdoc />
public int ConflictGroupOverflowEntryCount => 2;
/// <inheritdoc />
public void OnClickConflictGroupOverflow(float x, float y)
{
new FloatingMenu()
.AddEntry(StringAssets.useMyChanges, OnBulkUseMine, true)
.AddEntry(StringAssets.useRemoteChanges, OnBulkUseRemote, true)
.SetOpenDirection(MenuUtilities.OpenDirection.DownLeft)
.Open(x, y);
}
/// <summary>
/// Perform bulk choose mine on all conflicted entries.
/// </summary>
void OnBulkUseMine()
{
m_Model.RequestChooseMine(m_Model.GetConflictedEntries().Select(e => e.Entry.Path).ToArray());
}
/// <summary>
/// Perform bulk choose theirs on all conflicted entries.
/// </summary>
void OnBulkUseRemote()
{
m_Model.RequestChooseRemote(m_Model.GetConflictedEntries().Select(e => e.Entry.Path).ToArray());
}
/// <inheritdoc />
public void RequestShowConflictedDifferences(string path)
{
m_Model.RequestShowConflictedDifferences(path);
}
/// <inheritdoc />
public void RequestChooseMerge(string path)
{
m_Model.RequestChooseMerge(path);
}
/// <inheritdoc />
public void RequestChooseMine(string path)
{
m_Model.RequestChooseMine(new [] { path });
}
/// <inheritdoc />
public void RequestChooseRemote(string path)
{
m_Model.RequestChooseRemote(new [] { path });
}
}
}
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Models;
using Unity.Cloud.Collaborate.Views;
using Unity.Cloud.Collaborate.Models.Structures;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Presenters
{
internal class HistoryPresenter : IHistoryPresenter
{
internal const int pageSize = 10;
internal const string historyEntrySelectedId = "history-entry-selected";
int m_MaxPages;
bool m_IsStarted;
[NotNull]
readonly IHistoryView m_View;
[NotNull]
readonly IHistoryModel m_HistoryModel;
[NotNull]
readonly IMainModel m_MainModel;
public HistoryPresenter([NotNull] IHistoryView view, [NotNull] IHistoryModel historyModel, [NotNull] IMainModel mainModel)
{
m_View = view;
m_HistoryModel = historyModel;
m_MainModel = mainModel;
}
/// <inheritdoc />
public void Start()
{
Assert.IsFalse(m_IsStarted, "The presenter has already been started.");
m_IsStarted = true;
m_HistoryModel.HistoryListUpdated += OnHistoryListUpdated;
m_HistoryModel.SelectedRevisionReceived += OnSelectedRevisionReceived;
m_HistoryModel.EntryCountUpdated += OnEntryCountUpdated;
m_HistoryModel.HistoryListReceived += OnHistoryListReceived;
m_HistoryModel.BusyStatusUpdated += OnBusyStatusUpdated;
m_HistoryModel.StateChanged += OnStateChanged;
PopulateInitialData();
}
/// <inheritdoc />
public void Stop()
{
Assert.IsTrue(m_IsStarted, "The presenter has already been stopped.");
m_IsStarted = false;
m_HistoryModel.HistoryListUpdated -= OnHistoryListUpdated;
m_HistoryModel.SelectedRevisionReceived -= OnSelectedRevisionReceived;
m_HistoryModel.EntryCountUpdated -= OnEntryCountUpdated;
m_HistoryModel.HistoryListReceived -= OnHistoryListReceived;
m_HistoryModel.BusyStatusUpdated -= OnBusyStatusUpdated;
m_HistoryModel.StateChanged -= OnStateChanged;
m_MainModel.UnregisterBackNavigation(historyEntrySelectedId);
}
/// <summary>
/// Refresh state from the model.
/// </summary>
void OnStateChanged()
{
PopulateInitialData();
}
/// <summary>
/// Populate the view with the initial data from the model.
/// </summary>
void PopulateInitialData()
{
m_View.SetBusyStatus(m_HistoryModel.Busy);
if (!string.IsNullOrEmpty(m_HistoryModel.SelectedRevisionId))
{
m_HistoryModel.RequestSingleRevision(m_HistoryModel.SelectedRevisionId);
}
else if (!string.IsNullOrEmpty(m_HistoryModel.SavedRevisionId))
{
m_HistoryModel.RequestSingleRevision(m_HistoryModel.SavedRevisionId);
}
else
{
// Request initial data
m_HistoryModel.RequestPageOfRevisions(pageSize);
}
m_HistoryModel.RequestEntryNumber();
}
/// <summary>
/// Event handler to receive updated busy status.
/// </summary>
/// <param name="busy">New busy status.</param>
void OnBusyStatusUpdated(bool busy)
{
m_View.SetBusyStatus(busy);
}
/// <summary>
/// Event handler to receive requested history list.
/// </summary>
/// <param name="list">Received history list.</param>
void OnHistoryListReceived(IReadOnlyList<IHistoryEntry> list)
{
if (list == null)
{
// Return back to first page of entries
m_HistoryModel.PageNumber = 0;
m_HistoryModel.RequestPageOfRevisions(pageSize);
Debug.LogError("Request page does not exist.");
return;
}
m_MainModel.UnregisterBackNavigation(historyEntrySelectedId);
m_View.SetHistoryList(list);
}
/// <summary>
/// Event handler to receive updated history entry count.
/// </summary>
/// <param name="count">New entry count.</param>
void OnEntryCountUpdated(int? count)
{
if (count == null)
{
Debug.LogError("Unable to fetch number of revisions");
return;
}
m_MaxPages = (count.Value - 1) / pageSize;
m_View.SetPage(m_HistoryModel.PageNumber, m_MaxPages);
}
/// <summary>
/// Event handler to receive requested single revision.
/// </summary>
/// <param name="entry">Received single revision.</param>
void OnSelectedRevisionReceived(IHistoryEntry entry)
{
if (entry == null)
{
// Return back to all revisions list
m_HistoryModel.RequestPageOfRevisions(pageSize);
Debug.LogError("Unable to find requested revision");
return;
}
m_MainModel.RegisterBackNavigation(historyEntrySelectedId, StringAssets.allHistory, OnBackEvent);
m_View.SetSelection(entry);
}
/// <summary>
/// Event handler for when the model has received a message of an updated history list.
/// </summary>
void OnHistoryListUpdated()
{
// Request updated number of entries.
m_HistoryModel.RequestEntryNumber();
// Request either single revision or list of revisions depending on current state.
if (m_HistoryModel.IsRevisionSelected)
{
Assert.AreNotEqual(string.Empty, m_HistoryModel.SelectedRevisionId, "There should be a revision id at this point.");
m_HistoryModel.RequestSingleRevision(m_HistoryModel.SelectedRevisionId);
}
else
{
m_HistoryModel.RequestPageOfRevisions(pageSize);
}
}
/// <summary>
/// Event handler for when the back button is pressed.
/// </summary>
void OnBackEvent()
{
// Return back to all revisions list
m_HistoryModel.RequestPageOfRevisions(pageSize);
}
/// <inheritdoc />
public void PrevPage()
{
m_HistoryModel.PageNumber = Math.Max(m_HistoryModel.PageNumber - 1, 0);
m_HistoryModel.RequestPageOfRevisions(pageSize);
m_View.SetPage(m_HistoryModel.PageNumber, m_MaxPages);
}
/// <inheritdoc />
public void NextPage()
{
m_HistoryModel.PageNumber = Math.Min(m_HistoryModel.PageNumber + 1, m_MaxPages);
m_HistoryModel.RequestPageOfRevisions(pageSize);
m_View.SetPage(m_HistoryModel.PageNumber, m_MaxPages);
}
/// <inheritdoc />
public string SelectedRevisionId
{
set
{
if (m_HistoryModel.SelectedRevisionId == value) return;
m_HistoryModel.RequestSingleRevision(value);
}
}
/// <inheritdoc />
public void RequestGoto(string revisionId, HistoryEntryStatus status)
{
switch (status) {
case HistoryEntryStatus.Ahead:
m_HistoryModel.RequestUpdateTo(revisionId);
break;
case HistoryEntryStatus.Current:
m_HistoryModel.RequestRestoreTo(revisionId);
break;
case HistoryEntryStatus.Behind:
m_HistoryModel.RequestGoBackTo(revisionId);
break;
default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);
}
}
/// <inheritdoc />
public bool SupportsRevert => m_HistoryModel.SupportsRevert;
/// <inheritdoc />
public void RequestRevert(string revisionId, IReadOnlyList<string> files)
{
m_HistoryModel.RequestRevert(revisionId, files);
}
}
}
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Models.Structures;
namespace Unity.Cloud.Collaborate.Presenters
{
internal interface IChangesPresenter : IPresenter
{
/// <summary>
/// Count of the number of toggled entries.
/// </summary>
int ToggledCount { get; }
/// <summary>
/// Count of the total number of entries.
/// </summary>
int TotalCount { get; }
/// <summary>
/// Count of the number of conflicted entries.
/// </summary>
int ConflictedCount { get; }
/// <summary>
/// Whether or no a search is occuring.
/// </summary>
bool Searching { get; }
/// <summary>
/// Toggles or untoggles the entry associated with the give path. If the toggle impacts more than the existing
/// entry, then this will return true to indicate that the toggled states must be refreshed.
/// </summary>
/// <param name="path">Path of the associated entry.</param>
/// <param name="toggled">Whether or not the entry is now toggled.</param>
/// <returns>True if the list must be refreshed.</returns>
bool UpdateEntryToggle([NotNull] string path, bool toggled);
/// <summary>
/// Request a publish with the existing values known by the presenter.
/// </summary>
void RequestPublish();
/// <summary>
/// Request a discard for the file at the given path.
/// </summary>
/// <param name="entry">Entry to discard.</param>
void RequestDiscard([NotNull] IChangeEntry entry);
/// <summary>
/// Request a diff of the file at the given path.
/// </summary>
/// <param name="path">Path of the file to diff.</param>
void RequestDiffChanges([NotNull] string path);
/// <summary>
/// Provide the presenter with the latest search query.
/// </summary>
/// <param name="query">Latest search query.</param>
void SetSearchQuery([NotNull] string query);
/// <summary>
/// Provide the latest revision summary to the presenter.
/// </summary>
/// <param name="message">Latest commit message.</param>
void SetRevisionSummary([NotNull] string message);
/// <summary>
/// Number of entries in the group overflow menu.
/// </summary>
int GroupOverflowEntryCount { get; }
/// <summary>
/// Event handler for clicking the overflow button in the changes list.
/// </summary>
/// <param name="x">X position of the click.</param>
/// <param name="y">Y position of the click.</param>
void OnClickGroupOverflow(float x, float y);
/// <summary>
/// Number of entries in the conflict group overflow menu.
/// </summary>
int ConflictGroupOverflowEntryCount { get; }
/// <summary>
/// Event handler for clicking the overflow button in the conflicts list.
/// </summary>
/// <param name="x">X position of the click.</param>
/// <param name="y">Y position of the click.</param>
void OnClickConflictGroupOverflow(float x, float y);
/// <summary>
/// Show the difference between both version of a conlicted file.
/// </summary>
/// <param name="path">Path of the file to show.</param>
void RequestShowConflictedDifferences([NotNull] string path);
/// <summary>
/// Request to choose merge for the provided conflict.
/// </summary>
/// <param name="path">Path of the file to choose merge for.</param>
void RequestChooseMerge([NotNull] string path);
/// <summary>
/// Request to choose mine for the provided conflict.
/// </summary>
/// <param name="path">Path of the file to choose mine for.</param>
void RequestChooseMine([NotNull] string path);
/// <summary>
/// Request to choose remote for the provided conflict.
/// </summary>
/// <param name="path">Path of the file to choose remote for.</param>
void RequestChooseRemote([NotNull] string path);
}
}
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Models.Structures;
namespace Unity.Cloud.Collaborate.Presenters
{
internal interface IHistoryPresenter : IPresenter
{
/// <summary>
/// Request a move to the previous page. Ensures page number never goes below 0.
/// </summary>
void PrevPage();
/// <summary>
/// Request a move to the next page. Ensures page number doesn't go beyond the max number of pages.
/// </summary>
void NextPage();
/// <summary>
/// Set the revision id to request.
/// </summary>
[NotNull]
string SelectedRevisionId { set; }
/// <summary>
/// Request to update the state of the project to a provided revision. If revision is in the past, then the
/// state of the project at that point simply will be applied on top of the current without impacting history.
/// </summary>
/// <param name="revisionId">Revision id of the project to update to.</param>
/// <param name="status">Status of the revision.</param>
void RequestGoto([NotNull] string revisionId, HistoryEntryStatus status);
/// <summary>
/// Returns true if revert is supported.
/// </summary>
bool SupportsRevert { get; }
/// <summary>
/// Request to revert the specified files to the given revision.
/// </summary>
/// <param name="revisionId">Revision to revert the files back to.</param>
/// <param name="files">Files to revert back.</param>
void RequestRevert([NotNull] string revisionId, [NotNull] IReadOnlyList<string> files);
}
}
using System;
using Unity.Cloud.Collaborate.Views;
namespace Unity.Cloud.Collaborate.Presenters
{
internal interface IMainPresenter : IPresenter
{
/// <summary>
/// Create the history presenter and wire it up to the history view.
/// </summary>
/// <param name="view">View to wire to.</param>
/// <returns>The created presenter.</returns>
IHistoryPresenter AssignHistoryPresenter(IHistoryView view);
/// <summary>
/// Create the changes presenter and wire it up to the changes view.
/// </summary>
/// <param name="view">View to wire to.</param>
/// <returns>The created presenter.</returns>
IChangesPresenter AssignChangesPresenter(IChangesView view);
/// <summary>
/// Request cancel current job.
/// </summary>
void RequestCancelJob();
/// <summary>
/// Update value of current tab index.
/// </summary>
/// <param name="index">New tab index.</param>
void UpdateTabIndex(int index);
/// <summary>
/// Request a back navigation.
/// </summary>
void NavigateBack();
}
}
namespace Unity.Cloud.Collaborate.Presenters
{
internal interface IPresenter
{
/// <summary>
/// Called when the view is ready to receive data. For example when it comes into view.
/// </summary>
void Start();
/// <summary>
/// Called when the view is no longer available to receive data. For example when it goes out of view.
/// </summary>
void Stop();
}
}
namespace Unity.Cloud.Collaborate.Presenters
{
internal interface IStartPresenter : IPresenter
{
void RequestStart();
}
}
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Models;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Views;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Presenters
{
internal class MainPresenter : IMainPresenter
{
[NotNull]
readonly IMainView m_View;
[NotNull]
readonly IMainModel m_Model;
bool m_IsStarted;
const string k_ErrorOccuredId = "error_occured";
const string k_ConflictsDetectedId = "conflicts_detected";
const string k_RevisionsAvailableId = "revisions_available";
public MainPresenter([NotNull] IMainView view, [NotNull] IMainModel model)
{
m_View = view;
m_Model = model;
}
/// <inheritdoc />
public void Start()
{
Assert.IsFalse(m_IsStarted, "The presenter has already been started.");
m_IsStarted = true;
// Setup listeners.
m_Model.ConflictStatusChange += OnConflictStatusChange;
m_Model.OperationStatusChange += OnOperationStatusChange;
m_Model.OperationProgressChange += OnOperationProgressChange;
m_Model.ErrorOccurred += OnErrorOccurred;
m_Model.ErrorCleared += OnErrorCleared;
m_Model.RemoteRevisionsAvailabilityChange += OnRemoteRevisionsAvailabilityChange;
m_Model.BackButtonStateUpdated += OnBackButtonStateUpdated;
m_Model.StateChanged += OnStateChanged;
// Update progress info.
var progressInfo = m_Model.ProgressInfo;
if (progressInfo != null)
{
OnOperationStatusChange(true);
OnOperationProgressChange(m_Model.ProgressInfo);
}
// Update error info.
var errorInfo = m_Model.ErrorInfo;
if (errorInfo != null)
{
OnErrorOccurred(errorInfo);
}
else
{
OnErrorCleared();
}
// Get initial values.
OnConflictStatusChange(m_Model.Conflicted);
OnRemoteRevisionsAvailabilityChange(m_Model.RemoteRevisionsAvailable);
PopulateInitialData();
}
/// <inheritdoc />
public void Stop()
{
Assert.IsTrue(m_IsStarted, "The presenter has already been stopped.");
m_IsStarted = false;
m_Model.ConflictStatusChange -= OnConflictStatusChange;
m_Model.OperationStatusChange -= OnOperationStatusChange;
m_Model.OperationProgressChange -= OnOperationProgressChange;
m_Model.ErrorOccurred -= OnErrorOccurred;
m_Model.ErrorCleared -= OnErrorCleared;
m_Model.RemoteRevisionsAvailabilityChange -= OnRemoteRevisionsAvailabilityChange;
m_Model.BackButtonStateUpdated -= OnBackButtonStateUpdated;
m_Model.StateChanged -= OnStateChanged;
}
/// <summary>
/// Refresh state from the model.
/// </summary>
void OnStateChanged()
{
PopulateInitialData();
}
/// <summary>
/// Populate the view with the initial data from the model.
/// </summary>
void PopulateInitialData()
{
// Set tab.
m_View.SetTab(m_Model.CurrentTabIndex);
// Update back navigation
OnBackButtonStateUpdated(m_Model.GetBackNavigation()?.text);
}
/// <inheritdoc />
public IHistoryPresenter AssignHistoryPresenter(IHistoryView view)
{
var presenter = new HistoryPresenter(view, m_Model.ConstructHistoryModel(), m_Model);
view.Presenter = presenter;
return presenter;
}
/// <inheritdoc />
public IChangesPresenter AssignChangesPresenter(IChangesView view)
{
var presenter = new ChangesPresenter(view, m_Model.ConstructChangesModel(), m_Model);
view.Presenter = presenter;
return presenter;
}
/// <inheritdoc />
public void RequestCancelJob()
{
m_Model.RequestCancelJob();
}
/// <inheritdoc />
public void UpdateTabIndex(int index)
{
m_Model.CurrentTabIndex = index;
}
/// <inheritdoc />
public void NavigateBack()
{
// Grab back action from the model, clear it, then invoke it.
var nav = m_Model.GetBackNavigation();
if (nav == null) return;
m_Model.UnregisterBackNavigation(nav.Value.id);
nav.Value.backAction.Invoke();
}
/// <summary>
/// Display an alert if there is conflicts detected.
/// </summary>
/// <param name="conflicts">True if conflicts exist.</param>
void OnConflictStatusChange(bool conflicts)
{
if (conflicts)
{
m_View.AddAlert(k_ConflictsDetectedId, AlertBox.AlertLevel.Alert, StringAssets.conflictsDetected);
}
else
{
m_View.RemoveAlert(k_ConflictsDetectedId);
}
}
/// <summary>
/// Display a progress bar if an operation has started.
/// </summary>
/// <param name="inProgress"></param>
void OnOperationStatusChange(bool inProgress)
{
if (inProgress)
{
m_View.AddOperationProgress();
}
else
{
m_View.RemoveOperationProgress();
}
}
/// <summary>
/// Update progress bar with incremental details.
/// </summary>
/// <param name="progressInfo"></param>
void OnOperationProgressChange(IProgressInfo progressInfo)
{
m_View.SetOperationProgress(progressInfo.Title, progressInfo.Details,
progressInfo.PercentageComplete, progressInfo.CurrentCount,
progressInfo.TotalCount, progressInfo.PercentageProgressType, progressInfo.CanCancel);
}
/// <summary>
/// Display an error.
/// </summary>
/// <param name="errorInfo"></param>
void OnErrorOccurred(IErrorInfo errorInfo)
{
if (errorInfo.Behaviour == ErrorInfoBehavior.Alert)
{
m_View.AddAlert(k_ErrorOccuredId, AlertBox.AlertLevel.Alert, errorInfo.Message, (StringAssets.clear, m_Model.ClearError));
}
}
/// <summary>
/// Clear the error state.
/// </summary>
void OnErrorCleared()
{
m_View.RemoveAlert(k_ErrorOccuredId);
}
/// <summary>
/// Show or clear the revisions to fetch alert based on whether or not they are available.
/// </summary>
/// <param name="remoteRevisionsAvailable">True if there are remote revisions to pull down.</param>
void OnRemoteRevisionsAvailabilityChange(bool remoteRevisionsAvailable)
{
if (remoteRevisionsAvailable)
{
m_View.AddAlert(k_RevisionsAvailableId, AlertBox.AlertLevel.Info, StringAssets.syncRemoteRevisionsMessage, (StringAssets.sync, m_Model.RequestSync));
}
else
{
m_View.RemoveAlert(k_RevisionsAvailableId);
}
}
/// <summary>
/// Clear or show back navigation button.
/// </summary>
/// <param name="title">Text to display next to the back navigation. Null means no back navigation.</param>
void OnBackButtonStateUpdated([CanBeNull] string title)
{
if (title == null)
{
m_View.ClearBackNavigation();
}
else
{
m_View.DisplayBackNavigation(title);
}
}
}
}
# Presenters
In this directory, we have all of the interfaces and implementations of the Presenters in the package's **MVP** architecture.
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Models;
using Unity.Cloud.Collaborate.Models.Enums;
using Unity.Cloud.Collaborate.Views;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Presenters
{
internal class StartPresenter : IStartPresenter
{
[NotNull]
readonly IStartView m_View;
[NotNull]
readonly IStartModel m_Model;
bool m_IsStarted;
public StartPresenter([NotNull] IStartView view, [NotNull] IStartModel model)
{
m_View = view;
m_Model = model;
}
/// <inheritdoc />
public void Start()
{
Assert.IsFalse(m_IsStarted, "The presenter has already been started.");
m_IsStarted = true;
m_Model.ProjectStatusChanged += OnProjectStatusChanged;
m_Model.StateChanged += OnStateChanged;
PopulateInitialData();
}
/// <inheritdoc />
public void Stop()
{
Assert.IsTrue(m_IsStarted, "The presenter has already been stopped.");
m_IsStarted = false;
m_Model.ProjectStatusChanged -= OnProjectStatusChanged;
m_Model.StateChanged -= OnStateChanged;
}
/// <summary>
/// Refresh state from the model.
/// </summary>
void OnStateChanged()
{
PopulateInitialData();
}
/// <summary>
/// Populate the view with the initial data from the model.
/// </summary>
void PopulateInitialData()
{
OnProjectStatusChanged(m_Model.ProjectStatus);
}
void OnProjectStatusChanged(ProjectStatus status)
{
switch (status) {
case ProjectStatus.Offline:
m_View.Text = StringAssets.projectStatusTitleOffline;
m_View.ButtonText = string.Empty;
m_View.SetButtonVisible(false);
break;
case ProjectStatus.Maintenance:
m_View.Text = StringAssets.projectStatusTitleMaintenance;
m_View.ButtonText = string.Empty;
m_View.SetButtonVisible(false);
break;
case ProjectStatus.LoggedOut:
m_View.Text = StringAssets.projectStatusTitleLoggedOut;
m_View.ButtonText = StringAssets.projectStatusButtonLoggedOut;
m_View.SetButtonVisible(true);
break;
case ProjectStatus.Unbound:
m_View.Text = StringAssets.projectStatusTitleUnbound;
m_View.ButtonText = StringAssets.projectStatusButtonUnbound;
m_View.SetButtonVisible(true);
break;
case ProjectStatus.NoSeat:
m_View.Text = StringAssets.projectStatusTitleNoSeat;
m_View.ButtonText = StringAssets.projectStatusButtonNoSeat;
m_View.SetButtonVisible(true);
break;
case ProjectStatus.Bound:
m_View.Text = StringAssets.projectStatusTitleBound;
m_View.ButtonText = StringAssets.projectStatusButtonBound;
m_View.SetButtonVisible(true);
break;
case ProjectStatus.Loading:
m_View.Text = StringAssets.projectStatusTitleLoading;
m_View.ButtonText = string.Empty;
m_View.SetButtonVisible(false);
break;
case ProjectStatus.Ready:
m_View.Text = string.Empty;
m_View.ButtonText = string.Empty;
m_View.SetButtonVisible(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "Unexpected project status.");
}
}
/// <inheritdoc />
public void RequestStart()
{
var status = m_Model.ProjectStatus;
switch (status) {
case ProjectStatus.Unbound:
m_Model.ShowServicePage();
break;
case ProjectStatus.LoggedOut:
m_Model.ShowLoginPage();
break;
case ProjectStatus.NoSeat:
m_Model.ShowNoSeatPage();
break;
case ProjectStatus.Bound:
// Turn on collab Service. This is where we do a Genesis request apparently.
m_Model.RequestTurnOnService();
break;
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "Unexpected project status.");
}
}
}
}
using System;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleToAttribute("Unity.CollabProxy.EditorTests")]
# Unity Collaborate Editor Package Code
In this directory, we have all of the package code of Collaborate including its **MVP** architecture.
## Overview
This is the structure of the directory:
```none
<root>
├── Unity.SourceControl.asmdef
├── Assets/
│ ├── Icons/
│ ├── Layouts/
│ ├── Styles/
│ └── UiConstants.cs
├── Models/
│ ├── Api/
│ │ └── ISourceControlProvider.cs
│ └── Providers/
│ └── Collab.cs
├── Views/
│ └── Adaptors/
├── Presenters/
├── Common/
├── Settings/
├── Components/
├── Utilities/
└── UserInterface/
├── Bootstrap.cs
├── WindowCache.cs
├── ToolbarButton.cs
└── CollaborateWindow.cs
```
// using UnityEditor;
// using UnityEditor.SettingsManagement;
// using UnityEngine;
//
// namespace Unity.Cloud.Collaborate.Settings
// {
// internal class CollabSetting<T> : UserSetting<T>
// {
// public CollabSetting(string key, T value, SettingsScope scope = SettingsScope.Project)
// : base(CollabSettingsManager.instance, key, value, scope)
// {}
//
// CollabSetting(UnityEditor.SettingsManagement.Settings settings, string key, T value, SettingsScope scope = SettingsScope.Project)
// : base(settings, key, value, scope) { }
// }
// }
using System;
using JetBrains.Annotations;
// using UnityEditor.SettingsManagement;
using UnityEngine;
namespace Unity.Cloud.Collaborate.Settings
{
[UsedImplicitly]
internal class CollabSettings
{
public enum DisplayMode
{
Simple,
Advanced
}
public enum OpenLocation
{
Docked,
Window
}
// List of setting keys
public const string settingRelativeTimestamp = "general.relativeTimestamps";
// public const string settingAutoFetch = "general.autoFetch";
// public const string settingDisplayMode = "general.displayMode";
public const string settingDefaultOpenLocation = "general.defaultOpenLocation";
// [UserSetting] attribute registers this setting with the UserSettingsProvider so that it can be automatically
// shown in the UI.
// [UserSetting("General Settings", "Default Open Location")]
// [UsedImplicitly]
// static CollabSetting<OpenLocation> s_DefaultOpenLocation = new CollabSetting<OpenLocation>(settingDefaultOpenLocation, OpenLocation.Docked);
//
// [UserSetting("General Settings", "Relative Timestamps")]
// [UsedImplicitly]
// static CollabSetting<bool> s_RelativeTimestamps = new CollabSetting<bool>(settingRelativeTimestamp, true);
//
// [UserSetting("General Settings", "Automatic Fetch")]
// [UsedImplicitly]
// static CollabSetting<bool> s_AutoFetch = new CollabSetting<bool>(settingAutoFetch, true);
//
// [UserSetting("General Settings", "Display Mode")]
// [UsedImplicitly]
// static CollabSetting<DisplayMode> s_DisplayMode = new CollabSetting<DisplayMode>(settingDisplayMode, DisplayMode.Simple);
}
}
using UnityEditor;
namespace Unity.Cloud.Collaborate.Settings
{
/// <summary>
/// This class will act as a manager for the <see cref="Settings"/> singleton.
/// </summary>
internal static class CollabSettingsManager
{
// Project settings will be stored in a JSON file in a directory matching this name.
// const string k_PackageName = "com.unity.collab-proxy";
// static UnityEditor.SettingsManagement.Settings s_Instance;
//
// internal static UnityEditor.SettingsManagement.Settings instance =>
// s_Instance ?? (s_Instance = new UnityEditor.SettingsManagement.Settings(k_PackageName));
// The rest of this file is just forwarding the various setting methods to the instance.
// public static void Save()
// {
// instance.Save();
// }
public static T Get<T>(string key, SettingsScope scope = SettingsScope.Project, T fallback = default)
{
return fallback;
//return instance.Get(key, scope, fallback);
}
// public static void Set<T>(string key, T value, SettingsScope scope = SettingsScope.Project)
// {
// instance.Set(key, value, scope);
// }
//
// public static bool ContainsKey<T>(string key, SettingsScope scope = SettingsScope.Project)
// {
// return instance.ContainsKey<T>(key, scope);
// }
}
}
// using JetBrains.Annotations;
// using UnityEditor;
// using UnityEditor.SettingsManagement;
//
// namespace Unity.Cloud.Collaborate.Settings
// {
// [UsedImplicitly]
// internal class CollabSettingsProvider
// {
// const string k_PreferencesPath = "Preferences/Collaborate";
//
// [SettingsProvider]
// [UsedImplicitly]
// static SettingsProvider CreateSettingsProvider()
// {
// var provider = new UserSettingsProvider(k_PreferencesPath,
// CollabSettingsManager.instance,
// new [] { typeof(CollabSettingsProvider).Assembly });
//
// return provider;
// }
// }
// }
# Unity Collaborate Settings
This directory contains implementations and bindings for the Unity Settings package. All settings for the package are managed in this directory.
{
"name": "Unity.CollabProxy.Editor",
"references": [
"Unity.Settings.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
\ No newline at end of file
using UnityEditor;
using UnityEditor.Collaboration;
using UnityEngine;
using Unity.Cloud.Collaborate.UserInterface;
namespace CollabProxy.UI
{
[InitializeOnLoad]
public class Bootstrap
{
static Bootstrap()
{
var toolbar = new ToolbarButton { Width = 32f };
Toolbar.AddSubToolbar(toolbar);
toolbar.Update();
Collab.ShowHistoryWindow += () =>
{
CollaborateWindow.Init(CollaborateWindow.FocusTarget.History);
};
Collab.ShowChangesWindow += () =>
{
CollaborateWindow.Init(CollaborateWindow.FocusTarget.Changes);
};
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment