using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Comal.Classes; using InABox.Clients; using InABox.Core; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; using System.IO; using PRSSecurity = InABox.Core.Security; using Xamarin.Essentials; namespace PRS.Mobile { public delegate void TaskSavedEvent(int TaskNumber); [XamlCompilation(XamlCompilationOptions.Compile)] public partial class AddEditTask { public Kanban kanban = new Kanban(); bool newKanban = false; bool searching = false; bool displaying = false; List kanbanFormList = new List(); List observerList = new List(); Guid kanbanID = Guid.Empty; int estimatedTime; List imageList = new List(); Dictionary imagesourcedocs = new Dictionary(); public TaskSavedEvent OnTaskSaved; string kanbanTitle = ""; public AddEditTask(Guid selectedID = default(Guid), string title = "") { InitializeComponent(); kanbanID = selectedID; Title = "Loading"; AddToolBars(); kanbanTitle = title; if (selectedID == Guid.Empty) { NewKanbanTrack(); UpdateScreen(); } else { ExistingKanbanTrack(); if (PRSSecurity.IsAllowed()) shareBtn.IsVisible = true; } } #region OnAppearing and Screen Population protected override void OnAppearing() { base.OnAppearing(); searching = false; CheckForDigitalForms(); } private void NewKanbanTrack() { newKanban = true; kanban.DueDate = DateTime.Today; kanban.Status = KanbanStatus.Open; kanban.StartDate = DateTime.Today; kanban.EmployeeLink.ID = App.Data.Me.ID; kanban.ManagerLink.ID = App.Data.Me.ID; kanban.EmployeeLink.Name = App.Data.Me.Name; kanban.ManagerLink.Name = App.Data.Me.Name; kanban.Title = kanbanTitle; } private void ExistingKanbanTrack() { Task.Run(() => { CoreTable table = QueryKanban(); while (table == null) table = QueryKanban(); kanban = table.Rows.FirstOrDefault().ToObject(); UpdateImages(); UpdateScreen(); }); } private CoreTable QueryKanban() { try { return new Client().Query( new Filter(x => x.ID).IsEqualTo(kanbanID), new Columns(ColumnTypeFlags.None).Add( x => x.ID, x => x.Title, x => x.Status, x => x.StartDate, x => x.Number, x => x.Notes, x => x.DueDate, x => x.JobLink.ID, x => x.JobLink.Name, x => x.JobLink.JobNumber, x => x.Private, x => x.Description, x => x.Summary, x => x.Type.Description, x => x.EmployeeLink.ID, x => x.EmployeeLink.Name, x => x.EmployeeLink.Code, x => x.ManagerLink.ID, x => x.ManagerLink.Name, x => x.ManagerLink.Code, x => x.EstimatedTime, x => x.Completed, x => x.ActualTime, x => x.Locked, x => x.Closed, x => x.Attachments, x => x.Delivery.ID ) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private void AddToolBars() { NavigationPage.SetHasBackButton(this, false); ToolbarItems.Add(new ToolbarItem("Cancel", "", () => { Navigation.PopAsync(); })); ToolbarItems.Add(new ToolbarItem(" ", "", () => { //button added to create space on toolbar })); ToolbarItems.Add(new ToolbarItem("Save", "", () => { SubmitBtn_Clicked(); })); } public void UpdateScreen(bool lockTaskType = false) { Device.BeginInvokeOnMainThread(() => { if (newKanban) { Title = "New Task"; } else { Title = "Task " + kanban.Number; } titleEdt.Text = kanban.Title; jobNoLbl.Text = (kanban.JobLink.JobNumber + " " + kanban.JobLink.Name); descriptionEdt.Text = kanban.Summary; descriptionEdt.IsEnabled = kanban.ID == Guid.Empty ? true : false; existingNotesLbl.Text = BuildNotes(kanban.Notes); taskTypeLbl.Text = kanban.Type.Description; if (lockTaskType) taskTypeBtn.IsEnabled = false; assignedToLbl.Text = kanban.EmployeeLink.Name; allocatedByLbl.Text = kanban.ManagerLink.Name; categoryPck.SelectedIndex = chooseIndex(); dueDatePck.Date = kanban.DueDate; startDatePck.Date = kanban.StartDate; DisplayEstimatedTime(); DisplayObserverList(); if (kanban.Private) { privateCheckBox.IsChecked = true; } if (kanban.Locked) { categoryPck.IsEnabled = false; } }); } private string BuildNotes(string[] notes) { string result = ""; foreach (string note in notes) result = result + note + System.Environment.NewLine; return result; } private void AddNotes_Clicked(object sender, EventArgs e) { if (kanban.Notes.Count() == 0) { kanban.Notes = new string[] { DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + App.Data.Me.Name + ": " + notesEdt.Text }; notesEdt.Text = ""; } else { var list = kanban.Notes.ToList(); list.Add("==================================="); list.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + App.Data.Me.Name + ": " + notesEdt.Text); kanban.Notes = list.ToArray(); notesEdt.Text = ""; } UpdateScreen(); } private void NotesEdt_TextChanged(object sender, EventArgs e) { addNotesBtn.IsEnabled = string.IsNullOrWhiteSpace(notesEdt.Text) ? false : true; } private async void CheckForDigitalForms() { if (newKanban) return; formsBtn.Text = "Checking"; await Task.Run(() => { kanbanFormList.Clear(); try { CoreTable table = QueryKanbanForms(); while (table == null) table = QueryKanbanForms(); if (table.Rows.Any()) { foreach (CoreRow row in table.Rows) { KanbanForm kanbanForm = row.ToObject(); kanbanFormList.Add(kanbanForm); } Device.BeginInvokeOnMainThread(() => { formsBtn.Text = "Forms"; formsBtn.IsEnabled = true; }); } else { Device.BeginInvokeOnMainThread(() => { formsBtn.Text = "Forms"; formsBtn.IsEnabled = true; }); } } catch { } }); } private CoreTable QueryKanbanForms() { try { return new Client().Query( new Filter(x => x.Parent.ID).IsEqualTo(kanbanID), new Columns(ColumnTypeFlags.None).Add( x => x.ID, x => x.Parent.ID, x => x.Form.ID, x => x.Form.Description, x => x.Form.AppliesTo, x => x.Created, x => x.FormData, x => x.BlobData, x => x.FormCompleted, x => x.FormCompletedBy.ID, x => x.FormCompletedBy.UserID, x => x.FormOpen, x => x.FormStarted ), new SortOrder(x => x.Created) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } #endregion #region Fields Changed private async void ShareBtn_Clicked(object sender, EventArgs e) { try { CoreTable table = QueryKanban(); while (table == null) table = QueryKanban(); if (table.Rows.Any()) { var detail = GenerateDetail(table.Rows.First()); var message = new EmailMessage { Subject = "Task Details shared from: " + App.Data.Me.Name, Body = detail, }; await Xamarin.Essentials.Email.ComposeAsync(message); } } catch { } } private string GenerateDetail(CoreRow row) { string detail = ""; if (!string.IsNullOrWhiteSpace(row.Get(x => x.Title))) detail = "TITLE: " + row.Get(x => x.Title) + System.Environment.NewLine + System.Environment.NewLine; if (!string.IsNullOrWhiteSpace(row.Get(x => x.Summary))) detail = detail + "SUMMARY: " + row.Get(x => x.Summary) + System.Environment.NewLine + System.Environment.NewLine; if (row.Get(x => x.Notes).Any()) { detail = detail + "NOTES: "; foreach (var note in row.Get(x => x.Notes)) detail = detail + note + System.Environment.NewLine; } if (!string.IsNullOrWhiteSpace(row.Get(x => x.ManagerLink.Name))) detail = detail + "MANAGER: " + row.Get(x => x.ManagerLink.Name) + System.Environment.NewLine + System.Environment.NewLine; detail = detail + "STATUS: " + row.Get(x => x.Status).ToString() + System.Environment.NewLine + System.Environment.NewLine; if (!string.IsNullOrWhiteSpace(row.Get("IssueNumber"))) detail = detail + "ISSUE NUMBER: " + row.Get("IssueNumber") + System.Environment.NewLine + System.Environment.NewLine; if (row.Get(x => x.Created) != DateTime.MinValue) detail = detail + "CREATED: " + row.Get(x => x.Created).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine; if (row.Get(x => x.StartDate) != DateTime.MinValue) detail = detail + "STARTED: " + row.Get(x => x.StartDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine; if (row.Get(x => x.DueDate) != DateTime.MinValue) detail = detail + "DUE: " + row.Get(x => x.DueDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine; return detail; } private void TitleEdt_Changed(object sender, EventArgs e) { kanban.Title = titleEdt.Text; } private void DescriptionEdt_Changed(object sender, EventArgs e) { kanban.Description = descriptionEdt.Text; } private void DueDatePck_Selected(object sender, EventArgs e) { kanban.DueDate = dueDatePck.Date; } private void StartDatePck_Selected(object sender, EventArgs e) { kanban.StartDate = startDatePck.Date; } private void JobNoBtn_Clicked(object sender, EventArgs e) { if (searching) return; else { searching = true; JobSelectionPage jobSelectionPage = new JobSelectionPage( (job) => { kanban.JobLink.ID = job.ID; kanban.JobLink.Name = job.Name; kanban.JobLink.JobNumber = job.JobNumber; UpdateScreen(); } ); Navigation.PushAsync(jobSelectionPage); } } private void TaskType_Clicked(object sender, EventArgs e) { if (searching) return; else { searching = true; GenericSelectionPage page = new GenericSelectionPage ( "Select Type", new SelectionViewModel ( new Filter(x => x.Hidden).IsEqualTo(false), new Expression>[] { x => x.Description }, new Expression>[] { x => x.Hidden }, new SortOrder(x => x.Description) )); page.OnItemSelected += (o,e) => { var kanbanType = e.Row.ToObject(); kanban.Type.ID = kanbanType.ID; kanban.Type.Synchronise(kanbanType); UpdateScreen(); }; Navigation.PushAsync(page); } } private void AllocatedByBtn_Clicked(object sender, EventArgs e) { EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage( (employee) => { kanban.ManagerLink.ID = employee.ID; kanban.ManagerLink.Name = employee.Name; UpdateScreen(); } ); Navigation.PushAsync(employeeSelectionPage); } private void AssignedToBtn_Clicked(object sender, EventArgs e) { EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage( (employee) => { kanban.EmployeeLink.ID = employee.ID; kanban.EmployeeLink.Name = employee.Name; UpdateScreen(); } ); Navigation.PushAsync(employeeSelectionPage); } private void CheckPrivateChanged(object sender, CheckedChangedEventArgs e) { if (privateCheckBox.IsChecked) { Employee employee = new Employee(); var table = new Client().Query(new Filter(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)); foreach (CoreRow row in table.Rows) { employee = row.ToObject(); } kanban.ManagerLink.ID = employee.ID; kanban.ManagerLink.Synchronise(employee); kanban.EmployeeLink.ID = employee.ID; kanban.EmployeeLink.Synchronise(employee); kanban.Private = true; assignedToBtn.IsEnabled = false; allocatedByBtn.IsEnabled = false; UpdateScreen(); } if (!privateCheckBox.IsChecked) { kanban.Private = false; assignedToBtn.IsEnabled = true; allocatedByBtn.IsEnabled = true; } } private void category_Changed(object sender, EventArgs e) { kanban.Status = (KanbanStatus)categoryPck.SelectedIndex; } private int chooseIndex() { return (int)kanban.Status; } #endregion #region Estimated Time private void DecreaseBtn_Clicked(object sender, EventArgs e) { if (estimatedTime == 0 || estimatedTime < 0) return; else { estimatedTime = estimatedTime - 15; kanban.EstimatedTime = new TimeSpan(0, estimatedTime, 0); DisplayEstimatedTime(); } } private void IncreaseBtn_Clicked(object sender, EventArgs e) { estimatedTime = estimatedTime + 15; kanban.EstimatedTime = new TimeSpan(0, estimatedTime, 0); DisplayEstimatedTime(); } private void EstimatedHoursEdt_Changed(object sender, EventArgs e) { if (displaying) return; else CalculateEstimatedTime(); } private void EstimatedMinsEdt_Changed(object sender, EventArgs e) { if (displaying) return; else CalculateEstimatedTime(); } private async void CalculateEstimatedTime() //to timespan { try { int minutes = 0; int hours = 0; if (!string.IsNullOrWhiteSpace(estimatedHoursEdt.Text)) { hours = Convert.ToInt32(estimatedHoursEdt.Text); } if (!string.IsNullOrWhiteSpace(estimatedMinsEdt.Text)) { minutes = Convert.ToInt32(estimatedMinsEdt.Text); } kanban.EstimatedTime = new TimeSpan(hours, minutes, 0); estimatedTime = Convert.ToInt32(kanban.EstimatedTime.TotalMinutes); } catch { await DisplayAlert("Error", "Only whole numbers for estimated time fields", "OK"); int isNumber; if (!int.TryParse(estimatedHoursEdt.Text, out isNumber)) { estimatedHoursEdt.Text = "0"; }; if (!int.TryParse(estimatedMinsEdt.Text, out isNumber)) { estimatedMinsEdt.Text = "0"; }; } } private async void DisplayEstimatedTime() //from timespan { await Task.Run(() => { displaying = true; estimatedTime = Convert.ToInt32(kanban.EstimatedTime.TotalMinutes); int hours = estimatedTime / 60; int minutes = estimatedTime % 60; Device.BeginInvokeOnMainThread(() => { estimatedHoursEdt.Text = hours.ToString(); estimatedMinsEdt.Text = minutes.ToString(); displaying = false; }); }); } #endregion #region Display or add images private async void UpdateImages() { try { Device.BeginInvokeOnMainThread(() => { if (kanban.Attachments != 0) { int count = kanban.Attachments; photosLbl.TextColor = Color.Orange; photosLbl.Text = "Loading " + kanban.Attachments + " Photos"; Task.Run(() => { var table = QueryKanbanDocuments(); while (table == null) table = QueryKanbanDocuments(); if (table.Rows.Count != 0) { foreach (var row in table.Rows) { CoreTable docstable = QueryDocument(row.Get(x => x.DocumentLink.ID)); while (docstable == null) docstable = QueryDocument(row.Get(x => x.DocumentLink.ID)); CoreRow docrow = docstable.Rows.FirstOrDefault(); if (docrow != null) { byte[] data = docrow.Get(x => x.Data); ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); Image img = new Image(); img.HeightRequest = 150; img.WidthRequest = 150; img.Aspect = Aspect.AspectFit; img.Source = src; img.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(OnTap), CommandParameter = src, NumberOfTapsRequired = 1 }); imageList.Add(img); Device.BeginInvokeOnMainThread(() => { ImageScroller.IsVisible = true; images.Children.Add(img); count = count - 1; photosLbl.Text = "Loading " + count + " Photo(s)"; if (count == 0) { photosLbl.Text = "Photos"; photosLbl.TextColor = Color.Default; } }); } } } }); } }); } catch { } } private CoreTable QueryKanbanDocuments() { try { return new Client().Query( new Filter(x => x.EntityLink.ID).IsEqualTo(kanban.ID), new Columns(ColumnTypeFlags.None).Add(x => x.DocumentLink.ID), null ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private CoreTable QueryDocument(Guid id) { try { return new Client().Query(new Filter(x => x.ID).IsEqualTo(id), new Columns(ColumnTypeFlags.None).Add(x => x.Data)); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private void OnTap(object obj) { ImageViewerEditor imageViewEditor = new ImageViewerEditor( obj as ImageSource, (data) => { try { Image img = imageList.Find(x => x.Source.Equals(obj as ImageSource)); imageList.Remove(img); imagesourcedocs.Remove(obj as ImageSource); } catch { } DataToImage(data); }, null ); Navigation.PushAsync(imageViewEditor); } public void DataToImage(byte[] data) { try { ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); Image img = new Image(); img.HeightRequest = 150; img.WidthRequest = 150; img.Aspect = Aspect.AspectFit; img.VerticalOptions = LayoutOptions.FillAndExpand; img.HorizontalOptions = LayoutOptions.FillAndExpand; img.Source = src; img.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(OnTap), CommandParameter = src, NumberOfTapsRequired = 1 }); if (img != null) { imageList.Add(img); RefreshView(); } String filename = String.Format("{0:yyyy-MM-dd HH:mm:ss.fff}.png", DateTime.Now); Document doc = new Document() { FileName = filename, Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = DateTime.Now }; imagesourcedocs.Add(src, doc); } catch { } } private void RefreshView() { Device.BeginInvokeOnMainThread(() => { images.Children.Clear(); foreach (Image img in imageList) { images.Children.Add(img); } }); } async void TakePhoto_Clicked(object sender, EventArgs e) { try { var file = await MediaPicker.CapturePhotoAsync(); if (file == null) return; using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding Photo")) { Image img = null; var memoryStream = new MemoryStream(); using (var stream = await file.OpenReadAsync()) await stream.CopyToAsync(memoryStream); var data = memoryStream.ToArray(); Document doc = new Document() { FileName = Path.GetFileName(file.FileName), Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = DateTime.Now }; ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); imagesourcedocs.Add(src, doc); img = new Image(); img.HeightRequest = 150; img.WidthRequest = 150; img.Aspect = Aspect.AspectFit; img.Source = src; img.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(OnTap), CommandParameter = src, NumberOfTapsRequired = 1 }); if (img != null) { Device.BeginInvokeOnMainThread(() => { ImageScroller.IsVisible = true; images.Children.Add(img); }); } await pageScroller.ScrollToAsync(photoFrame, ScrollToPosition.Center, false); } } catch { } } async void ChooseImage_Clicked(object sender, EventArgs e) { try { var file = await MediaPicker.PickPhotoAsync(); if (file == null) return; using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding Photo")) { Image img = null; var memoryStream = new MemoryStream(); using (var stream = await file.OpenReadAsync()) await stream.CopyToAsync(memoryStream); var data = memoryStream.ToArray(); Document doc = new Document() { FileName = Path.GetFileName(file.FileName), Data = data, CRC = CoreUtils.CalculateCRC(data), TimeStamp = DateTime.Now }; ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); imagesourcedocs.Add(src, doc); img = new Image(); img.HeightRequest = 150; img.WidthRequest = 150; img.Aspect = Aspect.AspectFit; img.Source = src; img.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(OnTap), CommandParameter = src, NumberOfTapsRequired = 1 }); if (img != null) { Device.BeginInvokeOnMainThread(() => { ImageScroller.IsVisible = true; images.Children.Add(img); }); } await pageScroller.ScrollToAsync(photoFrame, ScrollToPosition.Center, false); } } catch { } } #endregion #region Digital Forms private async void Forms_Clicked(object sender, EventArgs e) { try { string chosenOptionOne = ""; if (kanbanFormList.Count == 0) { chosenOptionOne = await DisplayActionSheet("Choose An Option", "Cancel", null, "Add Form to Task"); } else if (kanbanFormList.Count > 0) { chosenOptionOne = await DisplayActionSheet("Choose An Option", "Cancel", null, "Add Form to Task", "View Form(s)"); } switch (chosenOptionOne) { case "Cancel": return; case "Add Form to Task": DigitalFormsPicker digitalFormPicker = new DigitalFormsPicker("Kanban", kanban.ID); Navigation.PushAsync(digitalFormPicker); break; case "View Form(s)": ChooseForm(); break; default: break; } } catch { } } private async void ChooseForm() { ListSelectionPage page = new ListSelectionPage(CreatePairs(), "Forms"); page.OnDictionaryItemTapped += (id, value) => { LaunchForm(id); }; Navigation.PushAsync(page); } private Dictionary CreatePairs() { Dictionary pairs = new Dictionary(); foreach (KanbanForm kanbanForm in kanbanFormList) { string formDescription = CreateDescription(kanbanForm); pairs.Add(kanbanForm.ID, formDescription); } return pairs; } private string CreateDescription(KanbanForm kanbanForm) { string formDescription = kanbanForm.Form.Description; if (kanbanForm.FormCompleted != DateTime.MinValue) formDescription = formDescription + " (Completed: " + kanbanForm.FormCompleted.ToString("hh:mm - dd MMM yy") + " by " + kanbanForm.FormCompletedBy.UserID + ")"; else formDescription = formDescription + " (Created: " + kanbanForm.Created.ToString("hh:mm - dd MMM yy") + ")"; return formDescription; } private async void LaunchForm(Guid id) { KanbanForm form = kanbanFormList.FirstOrDefault(x => x.ID == id); CoreTable table = QueryDigitalFormLayout(form); while (table == null) table = QueryDigitalFormLayout(form); CoreRow row = table.Rows.FirstOrDefault(); DigitalFormLayout layout = row.ToObject(); using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading")) { DigitalFormHost host = new DigitalFormHost( DigitalFormsHelper.LoadModel( layout, typeof(KanbanForm), kanban, Guid.Empty, new ExistingFormShell { ID = form.ID, ParentID = kanban.ID, Type = typeof(KanbanForm), FormID = form.ID, } )); Navigation.PushAsync(host); } } private CoreTable QueryDigitalFormLayout(KanbanForm form) { try { return new Client().Query( new Filter(x => x.Type).IsEqualTo(DFLayoutType.Mobile).And(x => x.Active).IsEqualTo(true).And(x => x.Form.Description).IsEqualTo(form.Form.Description), new Columns(ColumnTypeFlags.None).Add(x => x.Description, x => x.ID, x => x.Code, x => x.Form.AppliesTo, x => x.Form.ID, x => x.Layout), new SortOrder(x => x.Description) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } #endregion #region Submit btn + photos save private async void SubmitBtn_Clicked() { try { if (searching) return; else { searching = true; using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Saving")) { SaveKanban(); SaveDocuments(); Task.Run(() => { SaveSubscribers(); }); SavePhotos(); } string successMessage = "Task number : " + kanban.Number + System.Environment.NewLine + "New Photo(s): " + imagesourcedocs.Values.Count; await DisplayAlert("Success", successMessage, "OK"); OnTaskSaved?.Invoke(kanban.Number); await Navigation.PopAsync(); } } catch (Exception ex) { DisplayAlert("Error saving", ex.Message, "OK"); } } private void SaveKanban() { try { new Client().Save(kanban, "Updated From Mobile Device"); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); SaveKanban(); } } private void SaveDocuments() { try { if (imagesourcedocs.Values.Count != 0) new Client().Save(imagesourcedocs.Values, "Photo Taken on Device"); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); SaveDocuments(); } } private void SavePhotos() { try { if (imagesourcedocs.Values.Count != 0) { List newKanbanDocuments = new List(); foreach (Document doc in imagesourcedocs.Values) { var kanbanDocument = new KanbanDocument(); kanbanDocument.EntityLink.ID = kanban.ID; kanbanDocument.DocumentLink.ID = doc.ID; kanbanDocument.DocumentLink.FileName = doc.FileName; newKanbanDocuments.Add(kanbanDocument); } Task.Run(() => { SaveKanbanDocuments(newKanbanDocuments); }); } } catch { } } private void SaveKanbanDocuments(List newKanbanDocuments) { try { new Client().Save(newKanbanDocuments, "Photo Taken on Device"); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); SaveKanbanDocuments(newKanbanDocuments); } } #endregion #region Subscribers Buttons / Functionality private async void DisplayObserverList() { try { await Task.Run(() => { if (!newKanban) { CoreTable table = QueryObservers(); while (table == null) table = QueryObservers(); foreach (CoreRow row in table.Rows) { KanbanSubscriber subscriber = row.ToObject(); if (!subscriber.Manager && !subscriber.Assignee) { observerList.Add(subscriber); AddSubscriberLabel(subscriber); } } } }); } catch { } } private CoreTable QueryObservers() { try { return new Client().Query( new Filter(x => x.Kanban.ID).IsEqualTo(kanban.ID).And(x => x.Observer).IsEqualTo(true) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private void AddSubscriberLabel(KanbanSubscriber subscriber) { Label label = new Label(); label.Text = subscriber.Employee.Name; label.FontSize = Device.GetNamedSize(NamedSize.Medium, label); label.Margin = 5; label.HorizontalTextAlignment = TextAlignment.Start; label.VerticalTextAlignment = TextAlignment.Center; Device.BeginInvokeOnMainThread(() => { observerStackLayout.Children.Add(label); }); } private async void AddSubscriberBtn_Clicked(object sender, EventArgs e) { string chosenOption = await DisplayActionSheet("Add", "Cancel", null, "Person", "Team"); switch (chosenOption) { case "Cancel": break; case "Person": SelectEmployeeForSubscriber(); break; case "Team": SelectTeamForSubscriber(); break; default: break; } } private void SelectEmployeeForSubscriber() { EmployeeSelectionPage employeeSelectionPage = new EmployeeSelectionPage( (employee) => { KanbanSubscriber subscriber = new KanbanSubscriber(); subscriber.Kanban.ID = kanban.ID; subscriber.Observer = true; subscriber.Employee.ID = employee.ID; subscriber.Employee.Name = employee.Name; CheckListAndAddSubscriber(subscriber); } ); Navigation.PushAsync(employeeSelectionPage); } private async void SelectTeamForSubscriber() { string[] array = App.Data.EmployeeTeams.Select(x=>x.TeamName).Distinct().ToArray(); string chosenTeam = await DisplayActionSheet("Choose Team", "Cancel", null, array); switch (chosenTeam) { case "Cancel": return; break; } if (!string.IsNullOrWhiteSpace(chosenTeam)) { List employees = App.Data.EmployeeTeams.Where(x => x.TeamName == chosenTeam).ToList(); foreach (var employee in employees) { KanbanSubscriber subscriber = new KanbanSubscriber(); subscriber.Kanban.ID = kanban.ID; subscriber.Employee.ID = employee.ID; subscriber.Employee.Name = employee.Name; subscriber.Observer = true; CheckListAndAddSubscriber(subscriber); } } } private void CheckListAndAddSubscriber(KanbanSubscriber subscriber) { List guids = new List(); foreach (KanbanSubscriber sub in observerList) { guids.Add(sub.Employee.ID); } if (!guids.Contains(subscriber.Employee.ID)) { if (subscriber.Employee.ID != kanban.EmployeeLink.ID) { if (subscriber.Employee.ID != kanban.ManagerLink.ID) { observerList.Add(subscriber); AddSubscriberLabel(subscriber); } } } } private async void RemoveSubscriberBtn_Clicked(object sender, EventArgs e) { try { Dictionary nameSubscriberPairs = new Dictionary(); foreach (KanbanSubscriber subscriber in observerList) { nameSubscriberPairs.Add(subscriber.Employee.Name, subscriber); } string[] array = nameSubscriberPairs.Keys.ToArray(); string chosenOption = await DisplayActionSheet("Remove", "Cancel", null, array); if (chosenOption == "Cancel" || string.IsNullOrWhiteSpace(chosenOption)) { return; } else { KanbanSubscriber subscriber = nameSubscriberPairs[chosenOption]; observerList.Remove(subscriber); observerStackLayout.Children.Clear(); foreach (KanbanSubscriber sub in observerList) { AddSubscriberLabel(sub); } } } catch { } } #endregion #region Subscribers Saving private void SaveSubscribers() { Task.Run(() => { if (newKanban) { SaveNewSubs(); } else { SaveExistingSubs(); } }); } private void SaveNewSubs() { try { List subscribers = new List(); KanbanSubscriber sub = null; if (kanban.EmployeeLink.ID != Guid.Empty) { sub = new KanbanSubscriber(); sub.Kanban.ID = kanban.ID; sub.Employee.ID = kanban.EmployeeLink.ID; sub.Assignee = true; if (kanban.EmployeeLink.ID == kanban.ManagerLink.ID) { sub.Manager = true; } subscribers.Add(sub); } if (kanban.ManagerLink.ID != Guid.Empty) { if (kanban.ManagerLink.ID != kanban.EmployeeLink.ID) { sub = new KanbanSubscriber(); sub.Kanban.ID = kanban.ID; sub.Employee.ID = kanban.ManagerLink.ID; sub.Manager = true; subscribers.Add(sub); } } foreach (KanbanSubscriber subscriber in observerList) { subscriber.Kanban.ID = kanban.ID; subscribers.Add(subscriber); } DoSaveSubscribers(subscribers); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); } } private void DoSaveSubscribers(List subscribers) { try { new Client().Save(subscribers, "Updated from mobile device"); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); DoSaveSubscribers(subscribers); } } private void SaveExistingSubs() { try { KanbanSubscriber oldAssignee = new KanbanSubscriber(); KanbanSubscriber oldManager = new KanbanSubscriber(); KanbanSubscriber oldBoth = new KanbanSubscriber(); List oldObservers = new List(); List subscribersToDelete = new List(); List subscribersToSave = new List(); List subscribers = new List(); CoreTable table = QuerySubscribers(); while (table == null) table = QuerySubscribers(); foreach (CoreRow row in table.Rows) { KanbanSubscriber subscriber = row.ToObject(); if (subscriber.Assignee && subscriber.Manager) { oldBoth = subscriber; } else { if (subscriber.Assignee) { oldAssignee = subscriber; } else if (subscriber.Manager) { oldManager = subscriber; } else if (subscriber.Observer) { oldObservers.Add(subscriber); } } } if (kanban.ManagerLink.ID == kanban.EmployeeLink.ID) { if (kanban.ManagerLink.ID != oldBoth.Employee.ID && oldBoth.Employee.ID != Guid.Empty) { subscribersToDelete.Add(oldBoth); KanbanSubscriber subscriber = new KanbanSubscriber(); subscriber.Assignee = true; subscriber.Manager = true; subscriber.Kanban.ID = kanban.ID; subscriber.Employee.ID = kanban.EmployeeLink.ID; subscribersToSave.Add(subscriber); } } if (oldAssignee.Employee.ID != kanban.EmployeeLink.ID && oldAssignee.Employee.ID != Guid.Empty) { subscribersToDelete.Add(oldAssignee); KanbanSubscriber subscriber = new KanbanSubscriber(); subscriber.Assignee = true; subscriber.Manager = false; subscriber.Kanban.ID = kanban.ID; subscriber.Employee.ID = kanban.EmployeeLink.ID; subscribersToSave.Add(subscriber); } if (oldManager.Employee.ID != kanban.ManagerLink.ID && oldManager.Employee.ID != Guid.Empty) { subscribersToDelete.Add(oldManager); KanbanSubscriber subscriber = new KanbanSubscriber(); subscriber.Assignee = false; subscriber.Manager = true; subscriber.Kanban.ID = kanban.ID; subscriber.Employee.ID = kanban.ManagerLink.ID; subscribersToSave.Add(subscriber); } List oldGuids = new List(); List newGuids = new List(); foreach (KanbanSubscriber sub in observerList) { newGuids.Add(sub.Employee.ID); } foreach (KanbanSubscriber sub in oldObservers) { oldGuids.Add(sub.Employee.ID); if (!newGuids.Contains(sub.Employee.ID)) { subscribersToDelete.Add(sub); } } foreach (KanbanSubscriber sub in observerList) { if (!oldGuids.Contains(sub.Employee.ID)) { subscribersToSave.Add(sub); } } DoSaveSubscribers(subscribersToSave); DeleteSubscribers(subscribersToDelete); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); } } private CoreTable QuerySubscribers() { try { return new Client().Query( new Filter(x => x.Kanban.ID).IsEqualTo(kanban.ID) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private void DeleteSubscribers(List subscribersToDelete) { try { new Client().Delete(subscribersToDelete, "Updated from mobile device"); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); DeleteSubscribers(subscribersToDelete); } } #endregion } }