Skip to content

Commit

Permalink
Preview pane error handling improvements (#4517)
Browse files Browse the repository at this point in the history
  • Loading branch information
winston-de authored Apr 8, 2021
1 parent 0b8e4f6 commit abde6e2
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 139 deletions.
64 changes: 40 additions & 24 deletions Files/ViewModels/PreviewPaneViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,41 +210,57 @@ private async Task LoadPreviewControlAsync(CancellationToken token)

private async Task<UIElement> LoadPreviewControlFromExtension(ListedItem item, Extension extension)
{
try
{
UIElement control = null;
var file = await StorageFile.GetFileFromPathAsync(item.ItemPath);
string sharingToken = SharedStorageAccessManager.AddFile(file);
var result = await extension.Invoke(new ValueSet() { { "token", sharingToken } });

if (result.TryGetValue("preview", out object preview))
{
control = XamlReader.Load(preview as string) as UIElement;
}

if (result.TryGetValue("details", out object details))
{
var detailsList = JsonConvert.DeserializeObject<List<FileProperty>>(details as string);
await BasePreviewModel.LoadDetailsOnly(item, detailsList);
}
UIElement control = null;
var file = await StorageFile.GetFileFromPathAsync(item.ItemPath);
string sharingToken = SharedStorageAccessManager.AddFile(file);
var result = await extension.Invoke(new ValueSet() { { "token", sharingToken } });

return control;
if (result.TryGetValue("preview", out object preview))
{
control = XamlReader.Load(preview as string) as UIElement;
}
catch (Exception e)

if (result.TryGetValue("details", out object details))
{
Debug.WriteLine(e.ToString());
var detailsList = JsonConvert.DeserializeObject<List<FileProperty>>(details as string);
await BasePreviewModel.LoadDetailsOnly(item, detailsList);
}
return null;

return control;
}

private async void SelectedItemChanged()
{
loadCancellationTokenSource?.Cancel();
if (SelectedItem != null && SelectedItems.Count == 1)
{
DetailsListVisibility = Visibility.Visible;
loadCancellationTokenSource = new CancellationTokenSource();
await LoadPreviewControlAsync(loadCancellationTokenSource.Token);
try
{
DetailsListVisibility = Visibility.Visible;
loadCancellationTokenSource = new CancellationTokenSource();
await LoadPreviewControlAsync(loadCancellationTokenSource.Token);
}
catch (Exception e)
{
Debug.WriteLine(e);
loadCancellationTokenSource?.Cancel();
// If initial loading fails, attempt to load a basic preivew (thumbnail and details only)
// If that fails, revert to no preview/details available
try
{
var basicModel = new BasicPreviewViewModel(SelectedItem);
await basicModel.LoadAsync();
PreviewPaneContent = new BasicPreview(basicModel);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
PreviewPaneContent = null;
DetailsListVisibility = Visibility.Collapsed;
DetailsErrorText = "PreviewPaneDetailsNotAvailableText".GetLocalized();
PreviewErrorText = "DetailsPanePreviewNotAvaliableText".GetLocalized();
}
}
}
else if (SelectedItem != null)
{
Expand Down
96 changes: 55 additions & 41 deletions Files/ViewModels/Previews/BasePreviewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.Toolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -21,42 +20,32 @@ public BasePreviewModel(ListedItem item) : base()
Item = item;
}

public delegate void LoadedEventHandler(object sender, EventArgs e);

public event LoadedEventHandler LoadedEvent;
public ListedItem Item { get; internal set; }

public List<FileProperty> DetailsFromPreview { get; set; }
public ListedItem Item { get; internal set; }
public CancellationTokenSource LoadCancelledTokenSource { get; } = new CancellationTokenSource();

public static async Task LoadDetailsOnly(ListedItem item, List<FileProperty> details = null)
{
var temp = new DetailsOnlyPreviewModel(item) { DetailsFromPreview = details };
await temp.LoadAsync();
}
/// <summary>
/// This is cancelled when the user has selected another file or closed the pane.
/// </summary>
public CancellationTokenSource LoadCancelledTokenSource { get; } = new CancellationTokenSource();

public virtual async Task LoadAsync()
/// <summary>
/// Override this if the preview control needs to handle the unloaded event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public virtual void PreviewControlBase_Unloaded(object sender, RoutedEventArgs e)
{
// Files can be corrupt, in use, and stuff
try
{
var detailsFull = new List<FileProperty>();
Item.ItemFile ??= await StorageFile.GetFileFromPathAsync(Item.ItemPath);
DetailsFromPreview = await LoadPreviewAndDetails();
RaiseLoadedEvent();
var props = await GetSystemFileProperties();

DetailsFromPreview?.ForEach(i => detailsFull.Add(i));
props?.ForEach(i => detailsFull.Add(i));

Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection<FileProperty>(detailsFull);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
LoadCancelledTokenSource.Cancel();
}

/// <summary>
/// Override this and place the code to load the file preview here.
/// You can return details that may have been obtained while loading the preview (eg. word count).
/// This details will be displayed *before* the system file properties.
/// If there are none, return an empty list.
/// </summary>
/// <returns>A list of details</returns>
public async virtual Task<List<FileProperty>> LoadPreviewAndDetails()
{
var (IconData, OverlayData, IsCustom) = await FileThumbnailHelper.LoadIconOverlayAsync(Item.ItemPath, 400);
Expand All @@ -75,17 +64,6 @@ public async virtual Task<List<FileProperty>> LoadPreviewAndDetails()
return new List<FileProperty>();
}

public virtual void PreviewControlBase_Unloaded(object sender, RoutedEventArgs e)
{
LoadCancelledTokenSource.Cancel();
}

protected virtual void RaiseLoadedEvent()
{
// Raise the event in a thread-safe manner using the ?. operator.
LoadedEvent?.Invoke(this, new EventArgs());
}

private async Task<List<FileProperty>> GetSystemFileProperties()
{
if (Item.IsShortcutItem)
Expand All @@ -100,6 +78,42 @@ private async Task<List<FileProperty>> GetSystemFileProperties()
return list.Where(i => i.Value != null).ToList();
}

/// <summary>
/// Call this function when you are ready to load the preview and details.
/// Override if you need custom loading code.
/// </summary>
/// <returns>The task to run</returns>
public virtual async Task LoadAsync()
{
var detailsFull = new List<FileProperty>();
Item.ItemFile ??= await StorageFile.GetFileFromPathAsync(Item.ItemPath);
DetailsFromPreview = await LoadPreviewAndDetails();
RaiseLoadedEvent();
var props = await GetSystemFileProperties();

// Add the details from the preview function, then the system file properties
DetailsFromPreview?.ForEach(i => detailsFull.Add(i));
props?.ForEach(i => detailsFull.Add(i));

Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection<FileProperty>(detailsFull);
}

public event LoadedEventHandler LoadedEvent;

public delegate void LoadedEventHandler(object sender, EventArgs e);

protected virtual void RaiseLoadedEvent()
{
// Raise the event in a thread-safe manner using the ?. operator.
LoadedEvent?.Invoke(this, new EventArgs());
}

public static async Task LoadDetailsOnly(ListedItem item, List<FileProperty> details = null)
{
var temp = new DetailsOnlyPreviewModel(item) { DetailsFromPreview = details };
await temp.LoadAsync();
}

internal class DetailsOnlyPreviewModel : BasePreviewModel
{
public DetailsOnlyPreviewModel(ListedItem item) : base(item)
Expand Down
8 changes: 1 addition & 7 deletions Files/ViewModels/Previews/FolderPreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ public FolderPreviewViewModel(ListedItem item)

public async Task LoadAsync()
{
try
{
await LoadPreviewAndDetailsAsync();
}
catch (Exception)
{
}
await LoadPreviewAndDetailsAsync();
}

private async Task LoadPreviewAndDetailsAsync()
Expand Down
9 changes: 1 addition & 8 deletions Files/ViewModels/Previews/HtmlPreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,7 @@ public string TextValue

public async override Task<List<FileProperty>> LoadPreviewAndDetails()
{
try
{
TextValue = await FileIO.ReadTextAsync(Item.ItemFile);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
TextValue = await FileIO.ReadTextAsync(Item.ItemFile);
return new List<FileProperty>();
}
}
Expand Down
29 changes: 11 additions & 18 deletions Files/ViewModels/Previews/ImagePreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,20 @@ public ImageSource ImageSource

public override async Task<List<FileProperty>> LoadPreviewAndDetails()
{
try
{
FileRandomAccessStream stream = (FileRandomAccessStream)await Item.ItemFile.OpenAsync(FileAccessMode.Read);
FileRandomAccessStream stream = (FileRandomAccessStream)await Item.ItemFile.OpenAsync(FileAccessMode.Read);

// svg files require a different type of source
if (!Item.ItemPath.EndsWith(".svg"))
{
var bitmap = new BitmapImage();
ImageSource = bitmap;
await bitmap.SetSourceAsync(stream);
}
else
{
var bitmap = new SvgImageSource();
ImageSource = bitmap;
await bitmap.SetSourceAsync(stream);
}
// svg files require a different type of source
if (!Item.ItemPath.EndsWith(".svg"))
{
var bitmap = new BitmapImage();
ImageSource = bitmap;
await bitmap.SetSourceAsync(stream);
}
catch (Exception e)
else
{
Debug.WriteLine(e);
var bitmap = new SvgImageSource();
ImageSource = bitmap;
await bitmap.SetSourceAsync(stream);
}

return new List<FileProperty>();
Expand Down
13 changes: 3 additions & 10 deletions Files/ViewModels/Previews/MarkdownPreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,9 @@ public string TextValue

public override async Task<List<FileProperty>> LoadPreviewAndDetails()
{
try
{
var text = await FileIO.ReadTextAsync(Item.ItemFile);
var displayText = text.Length < Constants.PreviewPane.TextCharacterLimit ? text : text.Remove(Constants.PreviewPane.TextCharacterLimit);
TextValue = displayText;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
var text = await FileIO.ReadTextAsync(Item.ItemFile);
var displayText = text.Length < Constants.PreviewPane.TextCharacterLimit ? text : text.Remove(Constants.PreviewPane.TextCharacterLimit);
TextValue = displayText;

return new List<FileProperty>();
}
Expand Down
33 changes: 24 additions & 9 deletions Files/ViewModels/Previews/PDFPreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Data.Pdf;
using Windows.Storage.Streams;
Expand Down Expand Up @@ -35,20 +36,34 @@ public Visibility LoadingBarVisibility
public async override Task<List<FileProperty>> LoadPreviewAndDetails()
{
var pdf = await PdfDocument.LoadFromFileAsync(Item.ItemFile);
var details = new List<FileProperty>();

LoadPagesAsync(pdf);
// Add the number of pages to the details
details.Add(new FileProperty()
TryLoadPagesAsync(pdf);
var details = new List<FileProperty>
{
NameResource = "PropertyPageCount",
Value = pdf.PageCount,
});

// Add the number of pages to the details
new FileProperty()
{
NameResource = "PropertyPageCount",
Value = pdf.PageCount,
}
};

return details;
}

private async void LoadPagesAsync(PdfDocument pdf)
public async void TryLoadPagesAsync(PdfDocument pdf)
{
try
{
await LoadPagesAsync(pdf);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}

private async Task LoadPagesAsync(PdfDocument pdf)
{
// This fixes an issue where loading an absurdly large PDF would take to much RAM
// and eventually cause a crash
Expand Down
10 changes: 1 addition & 9 deletions Files/ViewModels/Previews/RichTextPreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,7 @@ public RichTextPreviewViewModel(ListedItem item) : base(item)

public async override Task<List<FileProperty>> LoadPreviewAndDetails()
{
try
{
Stream = await Item.ItemFile.OpenReadAsync();
}
catch (Exception e)
{
Debug.WriteLine(e);
}

Stream = await Item.ItemFile.OpenReadAsync();
return new List<FileProperty>();
}
}
Expand Down
Loading

0 comments on commit abde6e2

Please sign in to comment.