using comal.timesheets.CustomControls; using comal.timesheets.Data_Classes; using Comal.Classes; using InABox.Clients; using InABox.Core; using Syncfusion.TreeView.Engine; using Syncfusion.XForms.PopupLayout; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; using PRSSecurity = InABox.Core.Security; namespace comal.timesheets { public delegate void OnFolderListChanged(); [XamlCompilation(XamlCompilationOptions.Compile)] public partial class JobDocViewer : ContentPage { #region Fields and Constructor / Navigation ObservableCollection folderList = new ObservableCollection(); ObservableCollection displayList = new ObservableCollection(); DeviceIdiom idiom; bool foldersLoaded = false; bool filesLoaded = false; bool specificQueryLoading = false; Guid jobID = Guid.Empty; List loadedFolders = new List(); List mileStoneShells = new List(); List shells = new List(); List searchList = new List(); List specificQueryMileStones = new List(); List specificQueryFileShells = new List(); List currentQueryFileShells = new List(); public JobDocViewer(Job job) { InitializeComponent(); NavigationPage.SetHasBackButton(this, false); titleLbl.Text = "Job " + job.JobNumber; treeView.QueryNodeSize += TreeView_QueryNodeSize; idiom = DeviceInfo.Idiom; if (Device.RuntimePlatform.Equals(Device.iOS)) { expandImg.HeightRequest = 50; expandImg.WidthRequest = 50; collapseImg.HeightRequest = 50; collapseImg.WidthRequest = 50; } jobID = job.ID; LoadFiles(new Filter(x => x.DocumentSet.Job.ID).IsEqualTo(jobID)); LoadFolders(jobID); } void ExitBtn_Clicked(object sender, EventArgs e) { Navigation.PopAsync(); } #endregion #region TreeView / Folder Interaction void Folder_Tapped(object sender, EventArgs e) { var folder = treeView.SelectedItem as FolderViewItem; if (folder.Documents == 0) return; if (specificQueryLoading) return; if (filesLoaded) { if (folder.ItemName == "All") { AddSearchItems(shells); } else { var list = shells.Where(x => x.FolderID == folder.ID); AddSearchItems(list); } } else if (loadedFolders.Contains(folder.ID)) { var list = specificQueryFileShells.Where(x => x.FolderID == folder.ID); AddSearchItems(list); } else LoadFilesForFolder(folder.ID); } private async void LoadFilesForFolder(Guid folderID) { currentQueryFileShells.Clear(); specificQueryLoading = true; ShowLoading(); LoadFiles(new Filter(x => x.DocumentSet.Folder.ID).IsEqualTo(folderID), true, folderID); } void Expand_Tapped(object sender, EventArgs e) { if (!foldersLoaded) return; treeViewFrame.HeightRequest = 28 * folderList.Count; expandImg.IsVisible = false; collapseImg.IsVisible = true; } void Collapse_Tapped(object sender, EventArgs e) { treeViewFrame.HeightRequest = 120; expandImg.IsVisible = true; collapseImg.IsVisible = false; } #endregion #region Searching private void SearchEnt_Changed(object sender, EventArgs e) { if (specificQueryLoading) return; RunSearch(); } private void AddSearchItems(IEnumerable list) { searchList.Clear(); foreach (var v in list) { searchList.Add(v); } RunSearch(); } private void RunSearch() { Device.BeginInvokeOnMainThread(() => { listView.ItemsSource = null; if (string.IsNullOrWhiteSpace(searchEnt.Text)) { listView.ItemsSource = searchList; fileCountLbl.Text = "Files (" + searchList.Count() + ")"; } else { var list = searchList.Where ( x => x.FileName.Contains(searchEnt.Text) || x.FileName.Contains(searchEnt.Text.ToLower()) || x.FileName.Contains(searchEnt.Text.ToUpper()) || x.FileName.Contains(SearchUtils.UpperCaseFirst(searchEnt.Text)) || x.DocSetDescription.Contains(searchEnt.Text) || x.DocSetDescription.Contains(searchEnt.Text.ToLower()) || x.DocSetDescription.Contains(searchEnt.Text.ToUpper()) || x.DocSetDescription.Contains(SearchUtils.UpperCaseFirst(searchEnt.Text)) || x.Issued.Contains(searchEnt.Text) || x.Issued.Contains(searchEnt.Text.ToLower()) || x.Issued.Contains(searchEnt.Text.ToUpper()) || x.Issued.Contains(SearchUtils.UpperCaseFirst(searchEnt.Text)) || x.TrimmedIssued.Contains(searchEnt.Text) || x.TrimmedIssued.Contains(searchEnt.Text.ToLower()) || x.TrimmedIssued.Contains(searchEnt.Text.ToUpper()) || x.TrimmedIssued.Contains(SearchUtils.UpperCaseFirst(searchEnt.Text)) || x.TrimmedIssued.Contains(SearchUtils.UpperCaseSecond(searchEnt.Text)) || x.TrimmedIssued.Contains(SearchUtils.UpperCaseThird(searchEnt.Text)) || x.TrimmedIssued.Contains(SearchUtils.UpperCaseFourth(searchEnt.Text)) ); listView.ItemsSource = list; fileCountLbl.Text = "Files (" + list.Count() + ")"; } }); } #endregion #region Loading private void TreeView_QueryNodeSize(object sender, Syncfusion.XForms.TreeView.QueryNodeSizeEventArgs e) { e.Height = e.GetActualNodeHeight(); e.Handled = true; } private void LoadFolders(Guid jobid) { CoreTable table = new Client().Query(new Filter(x => x.Job.ID).IsEqualTo(jobid), new Columns(x => x.ID, x => x.Parent.ID, x => x.Name, x => x.Documents)); foreach (CoreRow row in table.Rows) { FolderViewItem folderItem = new FolderViewItem { ID = row.Get("ID"), ParentID = row.Get("Parent.ID"), ItemName = row.Get("Name"), Documents = row.Get("Documents") }; folderList.Add(folderItem); } foreach (var folder in folderList) { folder.List = folderList; if (folder.ParentID == Guid.Empty || folder.ParentID == CoreUtils.FullGuid) displayList.Add(folder); } displayList = new ObservableCollection(displayList.OrderBy(x => x.ItemName)); treeView.ItemsSource = displayList; foldersLoaded = true; } private async void LoadFiles(Filter filter, bool specificQuery = false, Guid loadFolder = new Guid()) { await Task.Run(() => { try { filter = filter.And(x => x.Status).IsEqualTo(JobDocumentSetMileStoneStatus.Approved) .And(x => x.Type.SiteVisible).IsEqualTo(true); CoreTable milestones = new Client().Query(filter, new Columns( x => x.ID, x => x.DocumentSet.Folder.Name, x => x.DocumentSet.Folder.ID, x => x.Submitted, x => x.Employee.Name, x => x.Type.Description, x => x.DocumentSet.Description ) ); if (milestones.Rows.Any()) { var table = QueryFiles(milestones, specificQuery); if (table.Rows.Any()) { foreach (CoreRow filerow in table.Rows) { AddFileShell(filerow, specificQuery); } Device.BeginInvokeOnMainThread(() => { if (specificQuery) { ShowFiles(); specificQueryLoading = false; loadedFolders.Add(loadFolder); AddSearchItems(currentQueryFileShells); } else { filesLoaded = true; displayList.Insert(0, new FolderViewItem { ItemName = "All", Documents = shells.Count }); folderList.Insert(0, new FolderViewItem { ItemName = "All", Documents = shells.Count }); treeView.ItemsSource = null; treeView.ItemsSource = displayList; } }); } } else { specificQueryLoading = false; ShowFiles(); } } catch { specificQueryLoading = false; ShowFiles(); } }); } private CoreTable QueryFiles(CoreTable milestones, bool specificQuery = false) { Filter filter = new Filter(x => x.EntityLink.ID).IsEqualTo(milestones.Rows.FirstOrDefault().Get("ID")); foreach (CoreRow milestonerow in milestones.Rows) { Guid id = AddMileStoneShell(milestonerow, specificQuery); filter = filter.Or(x => x.EntityLink.ID).IsEqualTo(milestonerow.Get("ID")); } CoreTable table = new Client().Query( filter, new Columns( x => x.DocumentLink.ID, x => x.Thumbnail, x => x.DocumentLink.FileName, x => x.EntityLink.ID )); return table; } private Guid AddMileStoneShell(CoreRow milestonerow, bool specificQuery = false) { Guid id = milestonerow.Get("ID"); MileStoneShell shell = new MileStoneShell { ID = milestonerow.Get("ID"), FolderName = milestonerow.Get("DocumentSet.Folder.Name"), FolderID = milestonerow.Get("DocumentSet.Folder.ID"), EmployeeName = milestonerow.Get("Employee.Name"), Type = milestonerow.Get("Type.Description"), DocSetDescription = milestonerow.Get("DocumentSet.Description"), Issued = "Issued: " + milestonerow.Get("Issued").ToString("dd MMM yy") }; if (specificQuery) specificQueryMileStones.Add(shell); else mileStoneShells.Add(shell); return id; } private void AddFileShell(CoreRow filerow, bool specificQuery = false) { JobDocSetFileShell file = new JobDocSetFileShell { DocLinkID = filerow.Get("DocumentLink.ID"), FileName = filerow.Get("DocumentLink.FileName"), MileStoneID = filerow.Get("EntityLink.ID") }; MileStoneShell mileStone = mileStoneShells.Find(x => x.ID == file.MileStoneID); file.FolderName = mileStone.FolderName; file.FolderID = mileStone.FolderID; file.EmployeeName = mileStone.EmployeeName; file.Type = mileStone.Type; file.DocSetDescription = mileStone.DocSetDescription; file.Issued = mileStone.Issued; file.TrimmedIssued = TrimWhiteSpace(file.Issued); byte[] data = filerow.Get("Thumbnail"); if (data != null) { file.ImageSource = ImageSource.FromStream(() => new MemoryStream(data)); if (idiom == DeviceIdiom.Tablet) { file.HeightRequest = 400; file.WidthRequest = 500; } file.Thumbnail = data; } if (specificQuery) { specificQueryFileShells.Add(file); currentQueryFileShells.Add(file); } else shells.Add(file); } private void ShowFiles() { Device.BeginInvokeOnMainThread(() => { treeView.IsEnabled = true; loadingLayout.IsVisible = false; loadingColumn.Width = 0; filesLayout.IsVisible = true; filesColumn.Width = new GridLength(1, GridUnitType.Star); }); } private void ShowLoading() { Device.BeginInvokeOnMainThread(async () => { treeView.IsEnabled = false; filesLayout.IsVisible = false; filesColumn.Width = 0; loadingLayout.IsVisible = true; loadingColumn.Width = new GridLength(1, GridUnitType.Star); Random random = new Random(); uint number = (uint)random.Next(500, 3000); await loadingLbl.TranslateTo(0, 15, 500); await loadingLbl.TranslateTo(0, 0, 500); loadingLbl.RotateTo(360, number); await loadingLbl.TranslateTo(0, 15, 500); await loadingLbl.TranslateTo(0, 0, 500); await loadingLbl.TranslateTo(0, 15, 500); await loadingLbl.TranslateTo(0, 0, 500); await loadingLbl.TranslateTo(0, 15, 500); number = (uint)random.Next(500, 3000); await loadingLbl.TranslateTo(0, 0, 500); loadingLbl.RotateTo(360, number); }); } static string TrimWhiteSpace(string s) { s = String.Concat(s.Where(c => !Char.IsWhiteSpace(c))); return s; } #endregion #region ListView Interaction void List_Tapped(object sender, EventArgs e) { var shell = listView.SelectedItem as JobDocSetFileShell; if (Device.RuntimePlatform.Equals(Device.Android) && PRSSecurity.IsAllowed()) OpenNativeViewer(shell.DocLinkID); else { PDFViewer viewer = new PDFViewer(shell.DocLinkID); Navigation.PushAsync(viewer); } } private async void OpenNativeViewer(Guid docID) { CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(docID), new Columns(x => x.Data, x => x.FileName)); Document doc = table.Rows.First().ToObject(); var filePath = Path.Combine(FileSystem.AppDataDirectory, doc.FileName); File.WriteAllBytes(filePath, doc.Data); await Launcher.OpenAsync(new OpenFileRequest { File = new ReadOnlyFile(filePath) }); } void Image_Tapped(object sender, EventArgs e) { var shell = ((TappedEventArgs)e).Parameter as JobDocSetFileShell; if (shell == null) return; if (shell.Thumbnail != null) { Image popupContent = new Image(); popupContent.HeightRequest = 500; popupContent.WidthRequest = 600; popupContent.HorizontalOptions = LayoutOptions.FillAndExpand; popupContent.VerticalOptions = LayoutOptions.FillAndExpand; popupContent.Aspect = Aspect.AspectFit; popupContent.Source = ImageSource.FromStream(() => new MemoryStream(shell.Thumbnail)); popupLayout.PopupView.ShowHeader = false; popupLayout.PopupView.ShowFooter = false; popupLayout.PopupView.ContentTemplate = new DataTemplate(() => { return popupContent; }); popupLayout.Show(); } } #endregion } #region Classes public class FolderViewItem : INotifyPropertyChanged { public event OnFolderListChanged OnFolderListChanged; public event PropertyChangedEventHandler PropertyChanged; public Guid ID { get; set; } public Guid ParentID { get; set; } public ImageSource ImageIcon { get; set; } private ObservableCollection list; public ObservableCollection List { get { return list; } set { list = value; OnFolderListChanged?.Invoke(); } } private string itemName; public string ItemName { get { return itemName; } set { itemName = value; RaisedOnPropertyChanged("ItemName"); } } public int Documents { get; set; } public ObservableCollection SubFiles { get; set; } public Guid DocID { get; set; } public FolderViewItem() { ID = Guid.Empty; ItemName = ""; ImageIcon = "folder.png"; DocID = Guid.Empty; ParentID = Guid.Empty; OnFolderListChanged += FolderViewItem_OnFolderListChanged; Documents = 0; } private void FolderViewItem_OnFolderListChanged() { GetChildren(); } public void RaisedOnPropertyChanged(string _PropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(_PropertyName)); } } private void GetChildren() { SubFiles = new ObservableCollection(list.Where(x => x.ParentID.Equals(ID))); } } public class MileStoneShell { public Guid ID { get; set; } public string FolderName { get; set; } public Guid FolderID { get; set; } public string EmployeeName { get; set; } public string Type { get; set; } public string DocSetDescription { get; set; } public string Issued { get; set; } public MileStoneShell() { ID = Guid.Empty; FolderID = Guid.Empty; FolderName = ""; EmployeeName = ""; Type = ""; DocSetDescription = ""; Issued = ""; } } public class JobDocSetFileShell { public Guid DocLinkID { get; set; } public string FileName { get; set; } public string FolderName { get; set; } public Guid FolderID { get; set; } public string EmployeeName { get; set; } public string Type { get; set; } public string DocSetDescription { get; set; } public string Issued { get; set; } public string TrimmedIssued { get; set; } public ImageSource ImageSource { get; set; } public double HeightRequest { get; set; } public double WidthRequest { get; set; } public byte[] Thumbnail { get; set; } public Guid MileStoneID { get; set; } public JobDocSetFileShell() { DocLinkID = Guid.Empty; FileName = ""; FolderName = ""; EmployeeName = ""; Type = ""; DocSetDescription = ""; ImageSource = null; Issued = ""; TrimmedIssued = ""; HeightRequest = 150; WidthRequest = 200; Thumbnail = null; MileStoneID = Guid.Empty; FolderID = Guid.Empty; } } #endregion }