ChangesPresenter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using System;
  2. using System.Linq;
  3. using System.Threading.Tasks;
  4. using JetBrains.Annotations;
  5. using Unity.Cloud.Collaborate.Assets;
  6. using Unity.Cloud.Collaborate.Components.Menus;
  7. using Unity.Cloud.Collaborate.Models;
  8. using Unity.Cloud.Collaborate.Models.Structures;
  9. using Unity.Cloud.Collaborate.Utilities;
  10. using Unity.Cloud.Collaborate.Views;
  11. using UnityEngine;
  12. using UnityEngine.Assertions;
  13. namespace Unity.Cloud.Collaborate.Presenters
  14. {
  15. internal class ChangesPresenter : IChangesPresenter
  16. {
  17. [NotNull]
  18. readonly IChangesView m_View;
  19. [NotNull]
  20. readonly IChangesModel m_Model;
  21. [NotNull]
  22. readonly IMainModel m_MainModel;
  23. bool m_IsStarted;
  24. public ChangesPresenter([NotNull] IChangesView view, [NotNull] IChangesModel model, [NotNull] IMainModel mainModel)
  25. {
  26. m_View = view;
  27. m_Model = model;
  28. m_MainModel = mainModel;
  29. }
  30. /// <inheritdoc />
  31. public void Start()
  32. {
  33. Assert.IsFalse(m_IsStarted, "The presenter has already been started.");
  34. m_IsStarted = true;
  35. m_Model.UpdatedChangeList += OnUpdatedChangeList;
  36. m_Model.OnUpdatedSelectedChanges += OnUpdatedPartiallySelectedChanges;
  37. m_Model.BusyStatusUpdated += OnBusyStatusUpdated;
  38. m_Model.StateChanged += OnStateChanged;
  39. m_MainModel.RemoteRevisionsAvailabilityChange += OnRemoteRevisionsAvailabilityChange;
  40. m_MainModel.ConflictStatusChange += OnConflictStatusChange;
  41. PopulateInitialData();
  42. }
  43. /// <inheritdoc />
  44. public void Stop()
  45. {
  46. Assert.IsTrue(m_IsStarted, "The presenter has already been stopped.");
  47. m_IsStarted = false;
  48. m_Model.UpdatedChangeList -= OnUpdatedChangeList;
  49. m_Model.OnUpdatedSelectedChanges -= OnUpdatedPartiallySelectedChanges;
  50. m_Model.BusyStatusUpdated -= OnBusyStatusUpdated;
  51. m_Model.StateChanged -= OnStateChanged;
  52. m_MainModel.RemoteRevisionsAvailabilityChange -= OnRemoteRevisionsAvailabilityChange;
  53. m_MainModel.ConflictStatusChange -= OnConflictStatusChange;
  54. }
  55. /// <summary>
  56. /// Refresh state from the model.
  57. /// </summary>
  58. void OnStateChanged()
  59. {
  60. PopulateInitialData();
  61. }
  62. /// <summary>
  63. /// Populate the view with the initial data from the model.
  64. /// </summary>
  65. void PopulateInitialData()
  66. {
  67. m_View.SetRevisionSummary(m_Model.SavedRevisionSummary);
  68. m_View.SetSearchQuery(m_Model.SavedSearchQuery);
  69. m_View.SetBusyStatus(m_Model.Busy);
  70. m_Model.RequestInitialData();
  71. }
  72. /// <summary>
  73. /// Event handler to receive updated remote changes available status.
  74. /// </summary>
  75. /// <param name="available">Whether or not remote changes are available.</param>
  76. protected void OnRemoteRevisionsAvailabilityChange(bool available)
  77. {
  78. UpdatePublishButton();
  79. }
  80. /// <summary>
  81. /// Event handler to receive updated busy status.
  82. /// </summary>
  83. /// <param name="busy">New busy status.</param>
  84. void OnBusyStatusUpdated(bool busy)
  85. {
  86. m_View.SetBusyStatus(busy);
  87. }
  88. /// <summary>
  89. /// Event handler for when the model reports an updated change list.
  90. /// </summary>
  91. protected void OnUpdatedChangeList()
  92. {
  93. UpdatePublishButton();
  94. UpdateChangeList();
  95. }
  96. /// <summary>
  97. /// Request the change or conflict list depending on the state of the model. The result is then given to the
  98. /// view to populate itself. Fire and forget method -- must be run on main thread.
  99. /// </summary>
  100. void UpdateChangeList()
  101. {
  102. Assert.IsTrue(Threading.IsMainThread, "Updating the change lists must be done from the main thread.");
  103. // Fetch and send data to the UI depending on what's the current display mode.
  104. if (m_Model.Conflicted)
  105. {
  106. Task.Run(() => m_Model.GetConflictedEntries(m_Model.SavedSearchQuery))
  107. .ContinueWith(r => m_View.SetConflicts(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
  108. }
  109. else
  110. {
  111. Task.Run(() => m_Model.GetAllEntries(m_Model.SavedSearchQuery))
  112. .ContinueWith(r => m_View.SetChanges(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
  113. }
  114. }
  115. /// <summary>
  116. /// Inform view to refresh its selections.
  117. /// </summary>
  118. protected void OnUpdatedPartiallySelectedChanges()
  119. {
  120. UpdatePublishButton();
  121. m_View.SetSelectedChanges();
  122. }
  123. /// <summary>
  124. /// Update changelist display in response to the conflict status changing.
  125. /// </summary>
  126. /// <param name="conflicted">New conflicted status.</param>
  127. protected void OnConflictStatusChange(bool conflicted)
  128. {
  129. UpdatePublishButton();
  130. UpdateChangeList();
  131. }
  132. /// <inheritdoc />
  133. public bool UpdateEntryToggle(string path, bool toggled)
  134. {
  135. var result = m_Model.UpdateEntryToggle(path, toggled);
  136. m_View.SetToggledCount(ToggledCount);
  137. UpdatePublishButton();
  138. return result;
  139. }
  140. /// <inheritdoc />
  141. public int ToggledCount => m_Model.ToggledCount;
  142. /// <inheritdoc />
  143. public int TotalCount => m_Model.TotalCount;
  144. /// <inheritdoc />
  145. public int ConflictedCount => m_Model.ConflictedCount;
  146. /// <inheritdoc />
  147. public bool Searching => !string.IsNullOrEmpty(m_Model.SavedSearchQuery);
  148. /// <inheritdoc />
  149. public void RequestPublish()
  150. {
  151. Assert.IsFalse(Searching, "Cannot publish while searching");
  152. m_Model.RequestPublish(m_Model.SavedRevisionSummary, m_Model.GetToggledEntries().Select(i => i.Entry).ToList());
  153. }
  154. /// <inheritdoc />
  155. public void RequestDiscard(IChangeEntry entry)
  156. {
  157. if (m_View.DisplayDialogue(StringAssets.confirmDiscardChangesTitle,
  158. StringAssets.confirmDiscardChangeMessage, StringAssets.discardChanges,
  159. StringAssets.cancel))
  160. {
  161. m_Model.RequestDiscard(entry);
  162. }
  163. }
  164. /// <summary>
  165. /// Discard all toggled entries. Fire and forget method -- must be called on main thread.
  166. /// </summary>
  167. void RequestDiscardToggled()
  168. {
  169. var entries = m_Model.GetToggledEntries(m_Model.SavedSearchQuery).Select(e => e.Entry).ToList();
  170. if (m_View.DisplayDialogue(StringAssets.confirmDiscardChangesTitle,
  171. string.Format(StringAssets.confirmDiscardChangesMessage, entries.Count), StringAssets.discardChanges,
  172. StringAssets.cancel))
  173. {
  174. m_Model.RequestBulkDiscard(entries);
  175. }
  176. }
  177. /// <summary>
  178. /// Update the state of the publish button in the view based on the state of the model.
  179. /// </summary>
  180. void UpdatePublishButton()
  181. {
  182. if (m_Model.Conflicted)
  183. {
  184. m_View.SetPublishEnabled(false, StringAssets.cannotPublishWhileConflicted);
  185. }
  186. else if (m_MainModel.RemoteRevisionsAvailable)
  187. {
  188. m_View.SetPublishEnabled(false, StringAssets.cannotPublishWithIncomingChanges);
  189. }
  190. else if (m_Model.ToggledCount < 1)
  191. {
  192. m_View.SetPublishEnabled(false, StringAssets.cannotPublishWithoutFiles);
  193. }
  194. else if (Searching)
  195. {
  196. m_View.SetPublishEnabled(false, StringAssets.cannotPublishWhileSearching);
  197. }
  198. else
  199. {
  200. m_View.SetPublishEnabled(true);
  201. }
  202. }
  203. /// <inheritdoc />
  204. public void RequestDiffChanges(string path)
  205. {
  206. m_Model.RequestDiffChanges(path);
  207. }
  208. /// <inheritdoc />
  209. public void SetSearchQuery(string query)
  210. {
  211. var value = StringUtility.TrimAndToLower(query);
  212. m_Model.SavedSearchQuery = value;
  213. m_View.SetSearchQuery(query);
  214. UpdateChangeList();
  215. UpdatePublishButton();
  216. }
  217. /// <inheritdoc />
  218. public void SetRevisionSummary(string message)
  219. {
  220. m_View.SetRevisionSummary(message);
  221. m_Model.SavedRevisionSummary = message;
  222. }
  223. /// <inheritdoc />
  224. public int GroupOverflowEntryCount => 1;
  225. /// <inheritdoc />
  226. public void OnClickGroupOverflow(float x, float y)
  227. {
  228. new FloatingMenu()
  229. .AddEntry(StringAssets.menuDiscardToggledChanges, RequestDiscardToggled, ToggledCount > 0)
  230. .SetOpenDirection(MenuUtilities.OpenDirection.DownLeft)
  231. .Open(x, y);
  232. }
  233. /// <inheritdoc />
  234. public int ConflictGroupOverflowEntryCount => 2;
  235. /// <inheritdoc />
  236. public void OnClickConflictGroupOverflow(float x, float y)
  237. {
  238. new FloatingMenu()
  239. .AddEntry(StringAssets.useMyChanges, OnBulkUseMine, true)
  240. .AddEntry(StringAssets.useRemoteChanges, OnBulkUseRemote, true)
  241. .SetOpenDirection(MenuUtilities.OpenDirection.DownLeft)
  242. .Open(x, y);
  243. }
  244. /// <summary>
  245. /// Perform bulk choose mine on all conflicted entries.
  246. /// </summary>
  247. void OnBulkUseMine()
  248. {
  249. m_Model.RequestChooseMine(m_Model.GetConflictedEntries().Select(e => e.Entry.Path).ToArray());
  250. }
  251. /// <summary>
  252. /// Perform bulk choose theirs on all conflicted entries.
  253. /// </summary>
  254. void OnBulkUseRemote()
  255. {
  256. m_Model.RequestChooseRemote(m_Model.GetConflictedEntries().Select(e => e.Entry.Path).ToArray());
  257. }
  258. /// <inheritdoc />
  259. public void RequestShowConflictedDifferences(string path)
  260. {
  261. m_Model.RequestShowConflictedDifferences(path);
  262. }
  263. /// <inheritdoc />
  264. public void RequestChooseMerge(string path)
  265. {
  266. m_Model.RequestChooseMerge(path);
  267. }
  268. /// <inheritdoc />
  269. public void RequestChooseMine(string path)
  270. {
  271. m_Model.RequestChooseMine(new [] { path });
  272. }
  273. /// <inheritdoc />
  274. public void RequestChooseRemote(string path)
  275. {
  276. m_Model.RequestChooseRemote(new [] { path });
  277. }
  278. }
  279. }