AddEditTask.xaml.cs 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Threading.Tasks;
  6. using Comal.Classes;
  7. using InABox.Clients;
  8. using InABox.Core;
  9. using Xamarin.Forms;
  10. using Xamarin.Forms.Xaml;
  11. using XF.Material.Forms.UI.Dialogs;
  12. using System.IO;
  13. using PRSSecurity = InABox.Core.Security;
  14. using Xamarin.Essentials;
  15. namespace PRS.Mobile
  16. {
  17. public delegate void TaskSavedEvent(int TaskNumber);
  18. [XamlCompilation(XamlCompilationOptions.Compile)]
  19. public partial class AddEditTask
  20. {
  21. public Kanban kanban = new Kanban();
  22. bool newKanban = false;
  23. bool searching = false;
  24. bool displaying = false;
  25. List<KanbanForm> kanbanFormList = new List<KanbanForm>();
  26. List<KanbanSubscriber> observerList = new List<KanbanSubscriber>();
  27. Guid kanbanID = Guid.Empty;
  28. int estimatedTime;
  29. List<Image> imageList = new List<Image>();
  30. Dictionary<ImageSource, Document> imagesourcedocs = new Dictionary<ImageSource, Document>();
  31. public TaskSavedEvent OnTaskSaved;
  32. string kanbanTitle = "";
  33. public AddEditTask(Guid selectedID = default(Guid), string title = "")
  34. {
  35. InitializeComponent();
  36. kanbanID = selectedID;
  37. Title = "Loading";
  38. AddToolBars();
  39. kanbanTitle = title;
  40. if (selectedID == Guid.Empty)
  41. {
  42. NewKanbanTrack();
  43. UpdateScreen();
  44. }
  45. else
  46. {
  47. ExistingKanbanTrack();
  48. if (PRSSecurity.IsAllowed<CanShareTaskDetails>())
  49. shareBtn.IsVisible = true;
  50. }
  51. }
  52. #region OnAppearing and Screen Population
  53. protected override void OnAppearing()
  54. {
  55. base.OnAppearing();
  56. searching = false;
  57. CheckForDigitalForms();
  58. }
  59. private void NewKanbanTrack()
  60. {
  61. newKanban = true;
  62. kanban.DueDate = DateTime.Today;
  63. kanban.Category = "Open";
  64. kanban.StartDate = DateTime.Today;
  65. kanban.EmployeeLink.ID = App.Data.Me.ID;
  66. kanban.ManagerLink.ID = App.Data.Me.ID;
  67. kanban.EmployeeLink.Name = App.Data.Me.Name;
  68. kanban.ManagerLink.Name = App.Data.Me.Name;
  69. kanban.Title = kanbanTitle;
  70. }
  71. private void ExistingKanbanTrack()
  72. {
  73. Task.Run(() =>
  74. {
  75. CoreTable table = QueryKanban();
  76. while (table == null)
  77. table = QueryKanban();
  78. kanban = table.Rows.FirstOrDefault().ToObject<Kanban>();
  79. UpdateImages();
  80. UpdateScreen();
  81. });
  82. }
  83. private CoreTable QueryKanban()
  84. {
  85. try
  86. {
  87. return new Client<Kanban>().Query(
  88. new Filter<Kanban>(x => x.ID).IsEqualTo(kanbanID),
  89. new Columns<Kanban>(
  90. x => x.ID,
  91. x => x.Title,
  92. x => x.Category,
  93. x => x.StartDate,
  94. x => x.Number,
  95. x => x.Notes,
  96. x => x.DueDate,
  97. x => x.JobLink.ID,
  98. x => x.JobLink.Name,
  99. x => x.JobLink.JobNumber,
  100. x => x.Private,
  101. x => x.Description,
  102. x => x.Summary,
  103. x => x.Type.Description,
  104. x => x.EmployeeLink.ID,
  105. x => x.EmployeeLink.Name,
  106. x => x.EmployeeLink.Code,
  107. x => x.ManagerLink.ID,
  108. x => x.ManagerLink.Name,
  109. x => x.ManagerLink.Code,
  110. x => x.EstimatedTime,
  111. x => x.Completed,
  112. x => x.ActualTime,
  113. x => x.Locked,
  114. x => x.Closed,
  115. x => x.Attachments,
  116. x => x.Delivery.ID
  117. )
  118. );
  119. }
  120. catch (Exception ex)
  121. {
  122. InABox.Mobile.MobileLogging.Log(ex);
  123. return null;
  124. }
  125. }
  126. private void AddToolBars()
  127. {
  128. NavigationPage.SetHasBackButton(this, false);
  129. ToolbarItems.Add(new ToolbarItem("Cancel", "", () =>
  130. {
  131. Navigation.PopAsync();
  132. }));
  133. ToolbarItems.Add(new ToolbarItem(" ", "", () =>
  134. {
  135. //button added to create space on toolbar
  136. }));
  137. ToolbarItems.Add(new ToolbarItem("Save", "", () =>
  138. {
  139. SubmitBtn_Clicked();
  140. }));
  141. }
  142. public void UpdateScreen(bool lockTaskType = false)
  143. {
  144. Device.BeginInvokeOnMainThread(() =>
  145. {
  146. if (newKanban)
  147. {
  148. Title = "New Task";
  149. }
  150. else
  151. {
  152. Title = "Task " + kanban.Number;
  153. }
  154. titleEdt.Text = kanban.Title;
  155. jobNoLbl.Text = (kanban.JobLink.JobNumber + " " + kanban.JobLink.Name);
  156. descriptionEdt.Text = kanban.Summary;
  157. descriptionEdt.IsEnabled = kanban.ID == Guid.Empty ? true : false;
  158. existingNotesLbl.Text = BuildNotes(kanban.Notes);
  159. taskTypeLbl.Text = kanban.Type.Description;
  160. if (lockTaskType)
  161. taskTypeBtn.IsEnabled = false;
  162. assignedToLbl.Text = kanban.EmployeeLink.Name;
  163. allocatedByLbl.Text = kanban.ManagerLink.Name;
  164. categoryPck.SelectedIndex = chooseIndex();
  165. dueDatePck.Date = kanban.DueDate;
  166. startDatePck.Date = kanban.StartDate;
  167. DisplayEstimatedTime();
  168. DisplayObserverList();
  169. if (kanban.Private)
  170. {
  171. privateCheckBox.IsChecked = true;
  172. }
  173. if (kanban.Locked)
  174. {
  175. categoryPck.IsEnabled = false;
  176. }
  177. });
  178. }
  179. private string BuildNotes(string[] notes)
  180. {
  181. string result = "";
  182. foreach (string note in notes)
  183. result = result + note + System.Environment.NewLine;
  184. return result;
  185. }
  186. private void AddNotes_Clicked(object sender, EventArgs e)
  187. {
  188. if (kanban.Notes.Count() == 0)
  189. {
  190. kanban.Notes = new string[] { DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + App.Data.Me.Name + ": " + notesEdt.Text };
  191. notesEdt.Text = "";
  192. }
  193. else
  194. {
  195. var list = kanban.Notes.ToList();
  196. list.Add("===================================");
  197. list.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + App.Data.Me.Name + ": " + notesEdt.Text);
  198. kanban.Notes = list.ToArray();
  199. notesEdt.Text = "";
  200. }
  201. UpdateScreen();
  202. }
  203. private void NotesEdt_TextChanged(object sender, EventArgs e)
  204. {
  205. addNotesBtn.IsEnabled = string.IsNullOrWhiteSpace(notesEdt.Text) ? false : true;
  206. }
  207. private async void CheckForDigitalForms()
  208. {
  209. if (newKanban) return;
  210. formsBtn.Text = "Checking";
  211. await Task.Run(() =>
  212. {
  213. kanbanFormList.Clear();
  214. try
  215. {
  216. CoreTable table = QueryKanbanForms();
  217. while (table == null)
  218. table = QueryKanbanForms();
  219. if (table.Rows.Any())
  220. {
  221. foreach (CoreRow row in table.Rows)
  222. {
  223. KanbanForm kanbanForm = row.ToObject<KanbanForm>();
  224. kanbanFormList.Add(kanbanForm);
  225. }
  226. Device.BeginInvokeOnMainThread(() =>
  227. {
  228. formsBtn.Text = "Forms";
  229. formsBtn.IsEnabled = true;
  230. });
  231. }
  232. else
  233. {
  234. Device.BeginInvokeOnMainThread(() =>
  235. {
  236. formsBtn.Text = "Forms";
  237. formsBtn.IsEnabled = true;
  238. });
  239. }
  240. }
  241. catch
  242. { }
  243. });
  244. }
  245. private CoreTable QueryKanbanForms()
  246. {
  247. try
  248. {
  249. return new Client<KanbanForm>().Query(
  250. new Filter<KanbanForm>(x => x.Parent.ID).IsEqualTo(kanbanID),
  251. new Columns<KanbanForm>(
  252. x => x.ID,
  253. x => x.Parent.ID,
  254. x => x.Form.ID,
  255. x => x.Form.Description,
  256. x => x.Form.AppliesTo,
  257. x => x.Created,
  258. x => x.FormData,
  259. x => x.BlobData,
  260. x => x.FormCompleted,
  261. x => x.FormCompletedBy.ID,
  262. x => x.FormCompletedBy.UserID,
  263. x => x.FormOpen,
  264. x => x.FormStarted
  265. ),
  266. new SortOrder<KanbanForm>(x => x.Created)
  267. );
  268. }
  269. catch (Exception ex)
  270. {
  271. InABox.Mobile.MobileLogging.Log(ex);
  272. return null;
  273. }
  274. }
  275. #endregion
  276. #region Fields Changed
  277. private async void ShareBtn_Clicked(object sender, EventArgs e)
  278. {
  279. try
  280. {
  281. CoreTable table = QueryKanban();
  282. while (table == null)
  283. table = QueryKanban();
  284. if (table.Rows.Any())
  285. {
  286. var detail = GenerateDetail(table.Rows.First());
  287. var message = new EmailMessage
  288. {
  289. Subject = "Task Details shared from: " + App.Data.Me.Name,
  290. Body = detail,
  291. };
  292. await Xamarin.Essentials.Email.ComposeAsync(message);
  293. }
  294. }
  295. catch { }
  296. }
  297. private string GenerateDetail(CoreRow row)
  298. {
  299. string detail = "";
  300. if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Title)))
  301. detail = "TITLE: " + row.Get<Kanban, string>(x => x.Title) + System.Environment.NewLine + System.Environment.NewLine;
  302. if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Summary)))
  303. detail = detail + "SUMMARY: " + row.Get<Kanban, string>(x => x.Summary) + System.Environment.NewLine + System.Environment.NewLine;
  304. if (row.Get<Kanban, string[]>(x => x.Notes).Any())
  305. {
  306. detail = detail + "NOTES: ";
  307. foreach (var note in row.Get<Kanban, string[]>(x => x.Notes))
  308. detail = detail + note + System.Environment.NewLine;
  309. }
  310. if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.ManagerLink.Name)))
  311. detail = detail + "MANAGER: " + row.Get<Kanban, string>(x => x.ManagerLink.Name) + System.Environment.NewLine + System.Environment.NewLine;
  312. if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Category)))
  313. detail = detail + "CATEGORY: " + row.Get<Kanban, string>(x => x.Category) + System.Environment.NewLine + System.Environment.NewLine;
  314. if (!string.IsNullOrWhiteSpace(row.Get<string>("IssueNumber")))
  315. detail = detail + "ISSUE NUMBER: " + row.Get<string>("IssueNumber") + System.Environment.NewLine + System.Environment.NewLine;
  316. if (row.Get<Kanban, DateTime>(x => x.Created) != DateTime.MinValue)
  317. detail = detail + "CREATED: " + row.Get<Kanban, DateTime>(x => x.Created).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
  318. if (row.Get<Kanban, DateTime>(x => x.StartDate) != DateTime.MinValue)
  319. detail = detail + "STARTED: " + row.Get<Kanban, DateTime>(x => x.StartDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
  320. if (row.Get<Kanban, DateTime>(x => x.DueDate) != DateTime.MinValue)
  321. detail = detail + "DUE: " + row.Get<Kanban, DateTime>(x => x.DueDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
  322. return detail;
  323. }
  324. private void TitleEdt_Changed(object sender, EventArgs e)
  325. {
  326. kanban.Title = titleEdt.Text;
  327. }
  328. private void DescriptionEdt_Changed(object sender, EventArgs e)
  329. {
  330. kanban.Description = descriptionEdt.Text;
  331. }
  332. private void DueDatePck_Selected(object sender, EventArgs e)
  333. {
  334. kanban.DueDate = dueDatePck.Date;
  335. }
  336. private void StartDatePck_Selected(object sender, EventArgs e)
  337. {
  338. kanban.StartDate = startDatePck.Date;
  339. }
  340. private void JobNoBtn_Clicked(object sender, EventArgs e)
  341. {
  342. if (searching)
  343. return;
  344. else
  345. {
  346. searching = true;
  347. JobSelectionPage jobSelectionPage = new JobSelectionPage(
  348. (job) =>
  349. {
  350. kanban.JobLink.ID = job.ID;
  351. kanban.JobLink.Name = job.Name;
  352. kanban.JobLink.JobNumber = job.JobNumber;
  353. UpdateScreen();
  354. }
  355. );
  356. Navigation.PushAsync(jobSelectionPage);
  357. }
  358. }
  359. private void TaskType_Clicked(object sender, EventArgs e)
  360. {
  361. if (searching)
  362. return;
  363. else
  364. {
  365. searching = true;
  366. GenericSelectionPage page = new GenericSelectionPage
  367. (
  368. "Select Type",
  369. new SelectionViewModel<KanbanType>
  370. (
  371. new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
  372. new Expression<Func<KanbanType, object>>[] { x => x.Description },
  373. new Expression<Func<KanbanType, object>>[] { x => x.Hidden },
  374. new SortOrder<KanbanType>(x => x.Description)
  375. ));
  376. page.OnItemSelected += (o,e) =>
  377. {
  378. var kanbanType = e.Row.ToObject<KanbanType>();
  379. kanban.Type.ID = kanbanType.ID;
  380. kanban.Type.Synchronise(kanbanType);
  381. UpdateScreen();
  382. };
  383. Navigation.PushAsync(page);
  384. }
  385. }
  386. private void AllocatedByBtn_Clicked(object sender, EventArgs e)
  387. {
  388. EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage(
  389. (employee) =>
  390. {
  391. kanban.ManagerLink.ID = employee.ID;
  392. kanban.ManagerLink.Name = employee.Name;
  393. UpdateScreen();
  394. }
  395. );
  396. Navigation.PushAsync(employeeSelectionPage);
  397. }
  398. private void AssignedToBtn_Clicked(object sender, EventArgs e)
  399. {
  400. EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage(
  401. (employee) =>
  402. {
  403. kanban.EmployeeLink.ID = employee.ID;
  404. kanban.EmployeeLink.Name = employee.Name;
  405. UpdateScreen();
  406. }
  407. );
  408. Navigation.PushAsync(employeeSelectionPage);
  409. }
  410. private void CheckPrivateChanged(object sender, CheckedChangedEventArgs e)
  411. {
  412. if (privateCheckBox.IsChecked)
  413. {
  414. Employee employee = new Employee();
  415. var table = new Client<Employee>().Query(new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid));
  416. foreach (CoreRow row in table.Rows)
  417. {
  418. employee = row.ToObject<Employee>();
  419. }
  420. kanban.ManagerLink.ID = employee.ID;
  421. kanban.ManagerLink.Synchronise(employee);
  422. kanban.EmployeeLink.ID = employee.ID;
  423. kanban.EmployeeLink.Synchronise(employee);
  424. kanban.Private = true;
  425. assignedToBtn.IsEnabled = false;
  426. allocatedByBtn.IsEnabled = false;
  427. UpdateScreen();
  428. }
  429. if (!privateCheckBox.IsChecked)
  430. {
  431. kanban.Private = false;
  432. assignedToBtn.IsEnabled = true;
  433. allocatedByBtn.IsEnabled = true;
  434. }
  435. }
  436. private void category_Changed(object sender, EventArgs e)
  437. {
  438. if (categoryPck.SelectedIndex == 0)
  439. {
  440. kanban.Category = "Open";
  441. }
  442. if (categoryPck.SelectedIndex == 1)
  443. {
  444. kanban.Category = "In Progress";
  445. }
  446. if (categoryPck.SelectedIndex == 2)
  447. {
  448. kanban.Category = "Waiting";
  449. }
  450. if (categoryPck.SelectedIndex == 3)
  451. {
  452. kanban.Category = "Complete";
  453. }
  454. }
  455. private int chooseIndex()
  456. {
  457. int indexNo = -1;
  458. if (kanban.Category != null)
  459. {
  460. if (kanban.Category.Equals("Open"))
  461. {
  462. indexNo = 0;
  463. }
  464. if (kanban.Category.Equals("In Progress"))
  465. {
  466. indexNo = 1;
  467. }
  468. if (kanban.Category.Equals("Waiting"))
  469. {
  470. indexNo = 2;
  471. }
  472. if (kanban.Category.Equals("Complete"))
  473. {
  474. indexNo = 3;
  475. }
  476. }
  477. return indexNo;
  478. }
  479. #endregion
  480. #region Estimated Time
  481. private void DecreaseBtn_Clicked(object sender, EventArgs e)
  482. {
  483. if (estimatedTime == 0 || estimatedTime < 0)
  484. return;
  485. else
  486. {
  487. estimatedTime = estimatedTime - 15;
  488. kanban.EstimatedTime = new TimeSpan(0, estimatedTime, 0);
  489. DisplayEstimatedTime();
  490. }
  491. }
  492. private void IncreaseBtn_Clicked(object sender, EventArgs e)
  493. {
  494. estimatedTime = estimatedTime + 15;
  495. kanban.EstimatedTime = new TimeSpan(0, estimatedTime, 0);
  496. DisplayEstimatedTime();
  497. }
  498. private void EstimatedHoursEdt_Changed(object sender, EventArgs e)
  499. {
  500. if (displaying)
  501. return;
  502. else
  503. CalculateEstimatedTime();
  504. }
  505. private void EstimatedMinsEdt_Changed(object sender, EventArgs e)
  506. {
  507. if (displaying)
  508. return;
  509. else
  510. CalculateEstimatedTime();
  511. }
  512. private async void CalculateEstimatedTime() //to timespan
  513. {
  514. try
  515. {
  516. int minutes = 0;
  517. int hours = 0;
  518. if (!string.IsNullOrWhiteSpace(estimatedHoursEdt.Text))
  519. {
  520. hours = Convert.ToInt32(estimatedHoursEdt.Text);
  521. }
  522. if (!string.IsNullOrWhiteSpace(estimatedMinsEdt.Text))
  523. {
  524. minutes = Convert.ToInt32(estimatedMinsEdt.Text);
  525. }
  526. kanban.EstimatedTime = new TimeSpan(hours, minutes, 0);
  527. estimatedTime = Convert.ToInt32(kanban.EstimatedTime.TotalMinutes);
  528. }
  529. catch
  530. {
  531. await DisplayAlert("Error", "Only whole numbers for estimated time fields", "OK");
  532. int isNumber;
  533. if (!int.TryParse(estimatedHoursEdt.Text, out isNumber))
  534. {
  535. estimatedHoursEdt.Text = "0";
  536. };
  537. if (!int.TryParse(estimatedMinsEdt.Text, out isNumber))
  538. {
  539. estimatedMinsEdt.Text = "0";
  540. };
  541. }
  542. }
  543. private async void DisplayEstimatedTime() //from timespan
  544. {
  545. await Task.Run(() =>
  546. {
  547. displaying = true;
  548. estimatedTime = Convert.ToInt32(kanban.EstimatedTime.TotalMinutes);
  549. int hours = estimatedTime / 60;
  550. int minutes = estimatedTime % 60;
  551. Device.BeginInvokeOnMainThread(() =>
  552. {
  553. estimatedHoursEdt.Text = hours.ToString();
  554. estimatedMinsEdt.Text = minutes.ToString();
  555. displaying = false;
  556. });
  557. });
  558. }
  559. #endregion
  560. #region Display or add images
  561. private async void UpdateImages()
  562. {
  563. try
  564. {
  565. Device.BeginInvokeOnMainThread(() =>
  566. {
  567. if (kanban.Attachments != 0)
  568. {
  569. int count = kanban.Attachments;
  570. photosLbl.TextColor = Color.Orange;
  571. photosLbl.Text = "Loading " + kanban.Attachments + " Photos";
  572. Task.Run(() =>
  573. {
  574. var table = QueryKanbanDocuments();
  575. while (table == null)
  576. table = QueryKanbanDocuments();
  577. if (table.Rows.Count != 0)
  578. {
  579. foreach (var row in table.Rows)
  580. {
  581. CoreTable docstable = QueryDocument(row.Get<KanbanDocument, Guid>(x => x.DocumentLink.ID));
  582. while (docstable == null)
  583. docstable = QueryDocument(row.Get<KanbanDocument, Guid>(x => x.DocumentLink.ID));
  584. CoreRow docrow = docstable.Rows.FirstOrDefault();
  585. if (docrow != null)
  586. {
  587. byte[] data = docrow.Get<Document, byte[]>(x => x.Data);
  588. ImageSource src = ImageSource.FromStream(() => new MemoryStream(data));
  589. Image img = new Image();
  590. img.HeightRequest = 150;
  591. img.WidthRequest = 150;
  592. img.Aspect = Aspect.AspectFit;
  593. img.Source = src;
  594. img.GestureRecognizers.Add(new TapGestureRecognizer
  595. {
  596. Command = new Command(OnTap),
  597. CommandParameter = src,
  598. NumberOfTapsRequired = 1
  599. });
  600. imageList.Add(img);
  601. Device.BeginInvokeOnMainThread(() =>
  602. {
  603. ImageScroller.IsVisible = true;
  604. images.Children.Add(img);
  605. count = count - 1;
  606. photosLbl.Text = "Loading " + count + " Photo(s)";
  607. if (count == 0)
  608. {
  609. photosLbl.Text = "Photos";
  610. photosLbl.TextColor = Color.Default;
  611. }
  612. });
  613. }
  614. }
  615. }
  616. });
  617. }
  618. });
  619. }
  620. catch { }
  621. }
  622. private CoreTable QueryKanbanDocuments()
  623. {
  624. try
  625. {
  626. return new Client<KanbanDocument>().Query(
  627. new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID),
  628. new Columns<KanbanDocument>(x => x.DocumentLink.ID),
  629. null
  630. );
  631. }
  632. catch (Exception ex)
  633. {
  634. InABox.Mobile.MobileLogging.Log(ex);
  635. return null;
  636. }
  637. }
  638. private CoreTable QueryDocument(Guid id)
  639. {
  640. try
  641. {
  642. return new Client<Document>().Query(new Filter<Document>(x => x.ID).IsEqualTo(id),
  643. new Columns<Document>(x => x.Data));
  644. }
  645. catch (Exception ex)
  646. {
  647. InABox.Mobile.MobileLogging.Log(ex);
  648. return null;
  649. }
  650. }
  651. private void OnTap(object obj)
  652. {
  653. ImageViewerEditor imageViewEditor = new ImageViewerEditor(
  654. obj as ImageSource,
  655. (data) =>
  656. {
  657. try
  658. {
  659. Image img = imageList.Find(x => x.Source.Equals(obj as ImageSource));
  660. imageList.Remove(img);
  661. imagesourcedocs.Remove(obj as ImageSource);
  662. }
  663. catch { }
  664. DataToImage(data);
  665. },
  666. null
  667. );
  668. Navigation.PushAsync(imageViewEditor);
  669. }
  670. public void DataToImage(byte[] data)
  671. {
  672. try
  673. {
  674. ImageSource src = ImageSource.FromStream(() => new MemoryStream(data));
  675. Image img = new Image();
  676. img.HeightRequest = 150;
  677. img.WidthRequest = 150;
  678. img.Aspect = Aspect.AspectFit;
  679. img.VerticalOptions = LayoutOptions.FillAndExpand;
  680. img.HorizontalOptions = LayoutOptions.FillAndExpand;
  681. img.Source = src;
  682. img.GestureRecognizers.Add(new TapGestureRecognizer
  683. {
  684. Command = new Command(OnTap),
  685. CommandParameter = src,
  686. NumberOfTapsRequired = 1
  687. });
  688. if (img != null)
  689. {
  690. imageList.Add(img);
  691. RefreshView();
  692. }
  693. String filename = String.Format("{0:yyyy-MM-dd HH:mm:ss.fff}.png", DateTime.Now);
  694. Document doc = new Document()
  695. {
  696. FileName = filename,
  697. Data = data,
  698. CRC = CoreUtils.CalculateCRC(data),
  699. TimeStamp = DateTime.Now
  700. };
  701. imagesourcedocs.Add(src, doc);
  702. }
  703. catch
  704. { }
  705. }
  706. private void RefreshView()
  707. {
  708. Device.BeginInvokeOnMainThread(() =>
  709. {
  710. images.Children.Clear();
  711. foreach (Image img in imageList)
  712. {
  713. images.Children.Add(img);
  714. }
  715. });
  716. }
  717. async void TakePhoto_Clicked(object sender, EventArgs e)
  718. {
  719. try
  720. {
  721. var file = await MediaPicker.CapturePhotoAsync();
  722. if (file == null)
  723. return;
  724. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding Photo"))
  725. {
  726. Image img = null;
  727. var memoryStream = new MemoryStream();
  728. using (var stream = await file.OpenReadAsync())
  729. await stream.CopyToAsync(memoryStream);
  730. var data = memoryStream.ToArray();
  731. Document doc = new Document()
  732. {
  733. FileName = Path.GetFileName(file.FileName),
  734. Data = data,
  735. CRC = CoreUtils.CalculateCRC(data),
  736. TimeStamp = DateTime.Now
  737. };
  738. ImageSource src = ImageSource.FromStream(() => new MemoryStream(data));
  739. imagesourcedocs.Add(src, doc);
  740. img = new Image();
  741. img.HeightRequest = 150;
  742. img.WidthRequest = 150;
  743. img.Aspect = Aspect.AspectFit;
  744. img.Source = src;
  745. img.GestureRecognizers.Add(new TapGestureRecognizer
  746. {
  747. Command = new Command(OnTap),
  748. CommandParameter = src,
  749. NumberOfTapsRequired = 1
  750. });
  751. if (img != null)
  752. {
  753. Device.BeginInvokeOnMainThread(() =>
  754. {
  755. ImageScroller.IsVisible = true;
  756. images.Children.Add(img);
  757. });
  758. }
  759. await pageScroller.ScrollToAsync(photoFrame, ScrollToPosition.Center, false);
  760. }
  761. }
  762. catch { }
  763. }
  764. async void ChooseImage_Clicked(object sender, EventArgs e)
  765. {
  766. try
  767. {
  768. var file = await MediaPicker.PickPhotoAsync();
  769. if (file == null)
  770. return;
  771. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding Photo"))
  772. {
  773. Image img = null;
  774. var memoryStream = new MemoryStream();
  775. using (var stream = await file.OpenReadAsync())
  776. await stream.CopyToAsync(memoryStream);
  777. var data = memoryStream.ToArray();
  778. Document doc = new Document()
  779. {
  780. FileName = Path.GetFileName(file.FileName),
  781. Data = data,
  782. CRC = CoreUtils.CalculateCRC(data),
  783. TimeStamp = DateTime.Now
  784. };
  785. ImageSource src = ImageSource.FromStream(() => new MemoryStream(data));
  786. imagesourcedocs.Add(src, doc);
  787. img = new Image();
  788. img.HeightRequest = 150;
  789. img.WidthRequest = 150;
  790. img.Aspect = Aspect.AspectFit;
  791. img.Source = src;
  792. img.GestureRecognizers.Add(new TapGestureRecognizer
  793. {
  794. Command = new Command(OnTap),
  795. CommandParameter = src,
  796. NumberOfTapsRequired = 1
  797. });
  798. if (img != null)
  799. {
  800. Device.BeginInvokeOnMainThread(() =>
  801. {
  802. ImageScroller.IsVisible = true;
  803. images.Children.Add(img);
  804. });
  805. }
  806. await pageScroller.ScrollToAsync(photoFrame, ScrollToPosition.Center, false);
  807. }
  808. }
  809. catch { }
  810. }
  811. #endregion
  812. #region Digital Forms
  813. private async void Forms_Clicked(object sender, EventArgs e)
  814. {
  815. try
  816. {
  817. string chosenOptionOne = "";
  818. if (kanbanFormList.Count == 0)
  819. {
  820. chosenOptionOne = await DisplayActionSheet("Choose An Option", "Cancel", null, "Add Form to Task");
  821. }
  822. else if (kanbanFormList.Count > 0)
  823. {
  824. chosenOptionOne = await DisplayActionSheet("Choose An Option", "Cancel", null, "Add Form to Task", "View Form(s)");
  825. }
  826. switch (chosenOptionOne)
  827. {
  828. case "Cancel":
  829. return;
  830. case "Add Form to Task":
  831. DigitalFormsPicker digitalFormPicker = new DigitalFormsPicker("Kanban", kanban.ID);
  832. Navigation.PushAsync(digitalFormPicker);
  833. break;
  834. case "View Form(s)":
  835. ChooseForm();
  836. break;
  837. default: break;
  838. }
  839. }
  840. catch { }
  841. }
  842. private async void ChooseForm()
  843. {
  844. ListSelectionPage page = new ListSelectionPage(CreatePairs(), "Forms");
  845. page.OnDictionaryItemTapped += (id, value) => { LaunchForm(id); };
  846. Navigation.PushAsync(page);
  847. }
  848. private Dictionary<Guid, string> CreatePairs()
  849. {
  850. Dictionary<Guid, string> pairs = new Dictionary<Guid, string>();
  851. foreach (KanbanForm kanbanForm in kanbanFormList)
  852. {
  853. string formDescription = CreateDescription(kanbanForm);
  854. pairs.Add(kanbanForm.ID, formDescription);
  855. }
  856. return pairs;
  857. }
  858. private string CreateDescription(KanbanForm kanbanForm)
  859. {
  860. string formDescription = kanbanForm.Form.Description;
  861. if (kanbanForm.FormCompleted != DateTime.MinValue)
  862. formDescription = formDescription
  863. + " (Completed: "
  864. + kanbanForm.FormCompleted.ToString("hh:mm - dd MMM yy")
  865. + " by "
  866. + kanbanForm.FormCompletedBy.UserID
  867. + ")";
  868. else
  869. formDescription = formDescription
  870. + " (Created: "
  871. + kanbanForm.Created.ToString("hh:mm - dd MMM yy")
  872. + ")";
  873. return formDescription;
  874. }
  875. private async void LaunchForm(Guid id)
  876. {
  877. KanbanForm form = kanbanFormList.FirstOrDefault(x => x.ID == id);
  878. CoreTable table = QueryDigitalFormLayout(form);
  879. while (table == null)
  880. table = QueryDigitalFormLayout(form);
  881. CoreRow row = table.Rows.FirstOrDefault();
  882. DigitalFormLayout layout = row.ToObject<DigitalFormLayout>();
  883. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
  884. {
  885. DigitalFormHost host = new DigitalFormHost(
  886. DigitalFormsHelper.LoadModel(
  887. layout, typeof(KanbanForm), kanban, Guid.Empty,
  888. new ExistingFormShell
  889. {
  890. ID = form.ID,
  891. ParentID = kanban.ID,
  892. Type = typeof(KanbanForm),
  893. FormID = form.ID,
  894. }
  895. ));
  896. Navigation.PushAsync(host);
  897. }
  898. }
  899. private CoreTable QueryDigitalFormLayout(KanbanForm form)
  900. {
  901. try
  902. {
  903. return new Client<DigitalFormLayout>().Query(
  904. new Filter<DigitalFormLayout>(x => x.Type).IsEqualTo(DFLayoutType.Mobile).And(x => x.Active).IsEqualTo(true).And(x => x.Form.Description).IsEqualTo(form.Form.Description),
  905. new Columns<DigitalFormLayout>(x => x.Description, x => x.ID, x => x.Code, x => x.Form.AppliesTo, x => x.Form.ID, x => x.Layout),
  906. new SortOrder<DigitalFormLayout>(x => x.Description)
  907. );
  908. }
  909. catch (Exception ex)
  910. {
  911. InABox.Mobile.MobileLogging.Log(ex);
  912. return null;
  913. }
  914. }
  915. #endregion
  916. #region Submit btn + photos save
  917. private async void SubmitBtn_Clicked()
  918. {
  919. try
  920. {
  921. if (searching)
  922. return;
  923. else
  924. {
  925. searching = true;
  926. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Saving"))
  927. {
  928. SaveKanban();
  929. SaveDocuments();
  930. Task.Run(() => { SaveSubscribers(); });
  931. SavePhotos();
  932. }
  933. string successMessage = "Task number : " + kanban.Number + System.Environment.NewLine
  934. + "New Photo(s): " + imagesourcedocs.Values.Count;
  935. await DisplayAlert("Success", successMessage, "OK");
  936. OnTaskSaved?.Invoke(kanban.Number);
  937. await Navigation.PopAsync();
  938. }
  939. }
  940. catch (Exception ex)
  941. {
  942. DisplayAlert("Error saving", ex.Message, "OK");
  943. }
  944. }
  945. private void SaveKanban()
  946. {
  947. try
  948. {
  949. new Client<Kanban>().Save(kanban, "Updated From Mobile Device");
  950. }
  951. catch (Exception ex)
  952. {
  953. InABox.Mobile.MobileLogging.Log(ex);
  954. SaveKanban();
  955. }
  956. }
  957. private void SaveDocuments()
  958. {
  959. try
  960. {
  961. if (imagesourcedocs.Values.Count != 0)
  962. new Client<Document>().Save(imagesourcedocs.Values, "Photo Taken on Device");
  963. }
  964. catch (Exception ex)
  965. {
  966. InABox.Mobile.MobileLogging.Log(ex);
  967. SaveDocuments();
  968. }
  969. }
  970. private void SavePhotos()
  971. {
  972. try
  973. {
  974. if (imagesourcedocs.Values.Count != 0)
  975. {
  976. List<KanbanDocument> newKanbanDocuments = new List<KanbanDocument>();
  977. foreach (Document doc in imagesourcedocs.Values)
  978. {
  979. var kanbanDocument = new KanbanDocument();
  980. kanbanDocument.EntityLink.ID = kanban.ID;
  981. kanbanDocument.DocumentLink.ID = doc.ID;
  982. kanbanDocument.DocumentLink.FileName = doc.FileName;
  983. newKanbanDocuments.Add(kanbanDocument);
  984. }
  985. Task.Run(() =>
  986. {
  987. SaveKanbanDocuments(newKanbanDocuments);
  988. });
  989. }
  990. }
  991. catch { }
  992. }
  993. private void SaveKanbanDocuments(List<KanbanDocument> newKanbanDocuments)
  994. {
  995. try
  996. {
  997. new Client<KanbanDocument>().Save(newKanbanDocuments, "Photo Taken on Device");
  998. }
  999. catch (Exception ex)
  1000. {
  1001. InABox.Mobile.MobileLogging.Log(ex);
  1002. SaveKanbanDocuments(newKanbanDocuments);
  1003. }
  1004. }
  1005. #endregion
  1006. #region Subscribers Buttons / Functionality
  1007. private async void DisplayObserverList()
  1008. {
  1009. try
  1010. {
  1011. await Task.Run(() =>
  1012. {
  1013. if (!newKanban)
  1014. {
  1015. CoreTable table = QueryObservers();
  1016. while (table == null)
  1017. table = QueryObservers();
  1018. foreach (CoreRow row in table.Rows)
  1019. {
  1020. KanbanSubscriber subscriber = row.ToObject<KanbanSubscriber>();
  1021. if (!subscriber.Manager && !subscriber.Assignee)
  1022. {
  1023. observerList.Add(subscriber);
  1024. AddSubscriberLabel(subscriber);
  1025. }
  1026. }
  1027. }
  1028. });
  1029. }
  1030. catch { }
  1031. }
  1032. private CoreTable QueryObservers()
  1033. {
  1034. try
  1035. {
  1036. return new Client<KanbanSubscriber>().Query(
  1037. new Filter<KanbanSubscriber>(x => x.Kanban.ID).IsEqualTo(kanban.ID).And(x => x.Observer).IsEqualTo(true)
  1038. );
  1039. }
  1040. catch (Exception ex)
  1041. {
  1042. InABox.Mobile.MobileLogging.Log(ex);
  1043. return null;
  1044. }
  1045. }
  1046. private void AddSubscriberLabel(KanbanSubscriber subscriber)
  1047. {
  1048. Label label = new Label();
  1049. label.Text = subscriber.Employee.Name;
  1050. label.FontSize = Device.GetNamedSize(NamedSize.Medium, label);
  1051. label.Margin = 5;
  1052. label.HorizontalTextAlignment = TextAlignment.Start;
  1053. label.VerticalTextAlignment = TextAlignment.Center;
  1054. Device.BeginInvokeOnMainThread(() =>
  1055. {
  1056. observerStackLayout.Children.Add(label);
  1057. });
  1058. }
  1059. private async void AddSubscriberBtn_Clicked(object sender, EventArgs e)
  1060. {
  1061. string chosenOption = await DisplayActionSheet("Add", "Cancel", null, "Person", "Team");
  1062. switch (chosenOption)
  1063. {
  1064. case "Cancel":
  1065. break;
  1066. case "Person":
  1067. SelectEmployeeForSubscriber();
  1068. break;
  1069. case "Team":
  1070. SelectTeamForSubscriber();
  1071. break;
  1072. default:
  1073. break;
  1074. }
  1075. }
  1076. private void SelectEmployeeForSubscriber()
  1077. {
  1078. EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage(
  1079. (employee) =>
  1080. {
  1081. KanbanSubscriber subscriber = new KanbanSubscriber();
  1082. subscriber.Kanban.ID = kanban.ID;
  1083. subscriber.Observer = true;
  1084. subscriber.Employee.ID = employee.ID;
  1085. subscriber.Employee.Name = employee.Name;
  1086. CheckListAndAddSubscriber(subscriber);
  1087. }
  1088. );
  1089. Navigation.PushAsync(employeeSelectionPage);
  1090. }
  1091. private async void SelectTeamForSubscriber()
  1092. {
  1093. string[] array = App.Data.EmployeeTeams.Select(x=>x.TeamName).Distinct().ToArray();
  1094. string chosenTeam = await DisplayActionSheet("Choose Team", "Cancel", null, array);
  1095. switch (chosenTeam)
  1096. {
  1097. case "Cancel":
  1098. return;
  1099. break;
  1100. }
  1101. if (!string.IsNullOrWhiteSpace(chosenTeam))
  1102. {
  1103. List<EmployeeTeamShell> employees = App.Data.EmployeeTeams.Where(x => x.TeamName == chosenTeam).ToList();
  1104. foreach (var employee in employees)
  1105. {
  1106. KanbanSubscriber subscriber = new KanbanSubscriber();
  1107. subscriber.Kanban.ID = kanban.ID;
  1108. subscriber.Employee.ID = employee.ID;
  1109. subscriber.Employee.Name = employee.Name;
  1110. subscriber.Observer = true;
  1111. CheckListAndAddSubscriber(subscriber);
  1112. }
  1113. }
  1114. }
  1115. private void CheckListAndAddSubscriber(KanbanSubscriber subscriber)
  1116. {
  1117. List<Guid> guids = new List<Guid>();
  1118. foreach (KanbanSubscriber sub in observerList)
  1119. {
  1120. guids.Add(sub.Employee.ID);
  1121. }
  1122. if (!guids.Contains(subscriber.Employee.ID))
  1123. {
  1124. if (subscriber.Employee.ID != kanban.EmployeeLink.ID)
  1125. {
  1126. if (subscriber.Employee.ID != kanban.ManagerLink.ID)
  1127. {
  1128. observerList.Add(subscriber);
  1129. AddSubscriberLabel(subscriber);
  1130. }
  1131. }
  1132. }
  1133. }
  1134. private async void RemoveSubscriberBtn_Clicked(object sender, EventArgs e)
  1135. {
  1136. try
  1137. {
  1138. Dictionary<string, KanbanSubscriber> nameSubscriberPairs = new Dictionary<string, KanbanSubscriber>();
  1139. foreach (KanbanSubscriber subscriber in observerList)
  1140. {
  1141. nameSubscriberPairs.Add(subscriber.Employee.Name, subscriber);
  1142. }
  1143. string[] array = nameSubscriberPairs.Keys.ToArray();
  1144. string chosenOption = await DisplayActionSheet("Remove", "Cancel", null, array);
  1145. if (chosenOption == "Cancel" || string.IsNullOrWhiteSpace(chosenOption))
  1146. {
  1147. return;
  1148. }
  1149. else
  1150. {
  1151. KanbanSubscriber subscriber = nameSubscriberPairs[chosenOption];
  1152. observerList.Remove(subscriber);
  1153. observerStackLayout.Children.Clear();
  1154. foreach (KanbanSubscriber sub in observerList)
  1155. {
  1156. AddSubscriberLabel(sub);
  1157. }
  1158. }
  1159. }
  1160. catch { }
  1161. }
  1162. #endregion
  1163. #region Subscribers Saving
  1164. private void SaveSubscribers()
  1165. {
  1166. Task.Run(() =>
  1167. {
  1168. if (newKanban)
  1169. {
  1170. SaveNewSubs();
  1171. }
  1172. else
  1173. {
  1174. SaveExistingSubs();
  1175. }
  1176. });
  1177. }
  1178. private void SaveNewSubs()
  1179. {
  1180. try
  1181. {
  1182. List<KanbanSubscriber> subscribers = new List<KanbanSubscriber>();
  1183. KanbanSubscriber sub = null;
  1184. if (kanban.EmployeeLink.ID != Guid.Empty)
  1185. {
  1186. sub = new KanbanSubscriber();
  1187. sub.Kanban.ID = kanban.ID;
  1188. sub.Employee.ID = kanban.EmployeeLink.ID;
  1189. sub.Assignee = true;
  1190. if (kanban.EmployeeLink.ID == kanban.ManagerLink.ID)
  1191. {
  1192. sub.Manager = true;
  1193. }
  1194. subscribers.Add(sub);
  1195. }
  1196. if (kanban.ManagerLink.ID != Guid.Empty)
  1197. {
  1198. if (kanban.ManagerLink.ID != kanban.EmployeeLink.ID)
  1199. {
  1200. sub = new KanbanSubscriber();
  1201. sub.Kanban.ID = kanban.ID;
  1202. sub.Employee.ID = kanban.ManagerLink.ID;
  1203. sub.Manager = true;
  1204. subscribers.Add(sub);
  1205. }
  1206. }
  1207. foreach (KanbanSubscriber subscriber in observerList)
  1208. {
  1209. subscriber.Kanban.ID = kanban.ID;
  1210. subscribers.Add(subscriber);
  1211. }
  1212. DoSaveSubscribers(subscribers);
  1213. }
  1214. catch (Exception ex)
  1215. {
  1216. InABox.Mobile.MobileLogging.Log(ex);
  1217. }
  1218. }
  1219. private void DoSaveSubscribers(List<KanbanSubscriber> subscribers)
  1220. {
  1221. try
  1222. {
  1223. new Client<KanbanSubscriber>().Save(subscribers, "Updated from mobile device");
  1224. }
  1225. catch (Exception ex)
  1226. {
  1227. InABox.Mobile.MobileLogging.Log(ex);
  1228. DoSaveSubscribers(subscribers);
  1229. }
  1230. }
  1231. private void SaveExistingSubs()
  1232. {
  1233. try
  1234. {
  1235. KanbanSubscriber oldAssignee = new KanbanSubscriber();
  1236. KanbanSubscriber oldManager = new KanbanSubscriber();
  1237. KanbanSubscriber oldBoth = new KanbanSubscriber();
  1238. List<KanbanSubscriber> oldObservers = new List<KanbanSubscriber>();
  1239. List<KanbanSubscriber> subscribersToDelete = new List<KanbanSubscriber>();
  1240. List<KanbanSubscriber> subscribersToSave = new List<KanbanSubscriber>();
  1241. List<KanbanSubscriber> subscribers = new List<KanbanSubscriber>();
  1242. CoreTable table = QuerySubscribers();
  1243. while (table == null)
  1244. table = QuerySubscribers();
  1245. foreach (CoreRow row in table.Rows)
  1246. {
  1247. KanbanSubscriber subscriber = row.ToObject<KanbanSubscriber>();
  1248. if (subscriber.Assignee && subscriber.Manager)
  1249. {
  1250. oldBoth = subscriber;
  1251. }
  1252. else
  1253. {
  1254. if (subscriber.Assignee)
  1255. {
  1256. oldAssignee = subscriber;
  1257. }
  1258. else if (subscriber.Manager)
  1259. {
  1260. oldManager = subscriber;
  1261. }
  1262. else if (subscriber.Observer)
  1263. {
  1264. oldObservers.Add(subscriber);
  1265. }
  1266. }
  1267. }
  1268. if (kanban.ManagerLink.ID == kanban.EmployeeLink.ID)
  1269. {
  1270. if (kanban.ManagerLink.ID != oldBoth.Employee.ID && oldBoth.Employee.ID != Guid.Empty)
  1271. {
  1272. subscribersToDelete.Add(oldBoth);
  1273. KanbanSubscriber subscriber = new KanbanSubscriber();
  1274. subscriber.Assignee = true;
  1275. subscriber.Manager = true;
  1276. subscriber.Kanban.ID = kanban.ID;
  1277. subscriber.Employee.ID = kanban.EmployeeLink.ID;
  1278. subscribersToSave.Add(subscriber);
  1279. }
  1280. }
  1281. if (oldAssignee.Employee.ID != kanban.EmployeeLink.ID && oldAssignee.Employee.ID != Guid.Empty)
  1282. {
  1283. subscribersToDelete.Add(oldAssignee);
  1284. KanbanSubscriber subscriber = new KanbanSubscriber();
  1285. subscriber.Assignee = true;
  1286. subscriber.Manager = false;
  1287. subscriber.Kanban.ID = kanban.ID;
  1288. subscriber.Employee.ID = kanban.EmployeeLink.ID;
  1289. subscribersToSave.Add(subscriber);
  1290. }
  1291. if (oldManager.Employee.ID != kanban.ManagerLink.ID && oldManager.Employee.ID != Guid.Empty)
  1292. {
  1293. subscribersToDelete.Add(oldManager);
  1294. KanbanSubscriber subscriber = new KanbanSubscriber();
  1295. subscriber.Assignee = false;
  1296. subscriber.Manager = true;
  1297. subscriber.Kanban.ID = kanban.ID;
  1298. subscriber.Employee.ID = kanban.ManagerLink.ID;
  1299. subscribersToSave.Add(subscriber);
  1300. }
  1301. List<Guid> oldGuids = new List<Guid>();
  1302. List<Guid> newGuids = new List<Guid>();
  1303. foreach (KanbanSubscriber sub in observerList)
  1304. {
  1305. newGuids.Add(sub.Employee.ID);
  1306. }
  1307. foreach (KanbanSubscriber sub in oldObservers)
  1308. {
  1309. oldGuids.Add(sub.Employee.ID);
  1310. if (!newGuids.Contains(sub.Employee.ID))
  1311. {
  1312. subscribersToDelete.Add(sub);
  1313. }
  1314. }
  1315. foreach (KanbanSubscriber sub in observerList)
  1316. {
  1317. if (!oldGuids.Contains(sub.Employee.ID))
  1318. {
  1319. subscribersToSave.Add(sub);
  1320. }
  1321. }
  1322. DoSaveSubscribers(subscribersToSave);
  1323. DeleteSubscribers(subscribersToDelete);
  1324. }
  1325. catch (Exception ex)
  1326. {
  1327. InABox.Mobile.MobileLogging.Log(ex);
  1328. }
  1329. }
  1330. private CoreTable QuerySubscribers()
  1331. {
  1332. try
  1333. {
  1334. return new Client<KanbanSubscriber>().Query(
  1335. new Filter<KanbanSubscriber>(x => x.Kanban.ID).IsEqualTo(kanban.ID)
  1336. );
  1337. }
  1338. catch (Exception ex)
  1339. {
  1340. InABox.Mobile.MobileLogging.Log(ex);
  1341. return null;
  1342. }
  1343. }
  1344. private void DeleteSubscribers(List<KanbanSubscriber> subscribersToDelete)
  1345. {
  1346. try
  1347. {
  1348. new Client<KanbanSubscriber>().Delete(subscribersToDelete, "Updated from mobile device");
  1349. }
  1350. catch (Exception ex)
  1351. {
  1352. InABox.Mobile.MobileLogging.Log(ex);
  1353. DeleteSubscribers(subscribersToDelete);
  1354. }
  1355. }
  1356. #endregion
  1357. }
  1358. }