Skip to content

Commit

Permalink
Separator buttons #62
Browse files Browse the repository at this point in the history
  • Loading branch information
Webreaper committed Mar 1, 2021
1 parent 9748c2b commit 7307e92
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 94 deletions.
Binary file modified .DS_Store
Binary file not shown.
32 changes: 16 additions & 16 deletions Damselfly.Core/Services/SearchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,22 @@ private async Task LoadMoreData(int first, int count)

images = images.Include(x => x.Folder);

if( query.TagId != -1 )
// Add in the ordering for the group by
switch (query.Grouping)
{
case GroupingType.None:
case GroupingType.Date:
images = images.OrderByDescending(x => x.SortDate);
break;
case GroupingType.Folder:
images = images.OrderBy(x => x.Folder.Path)
.ThenByDescending(x => x.SortDate);
break;
default:
throw new ArgumentException("Unexpected grouping type.");
}

if ( query.TagId != -1 )
{
images = images.Where(x => x.ImageTags.Any(y => y.TagId == query.TagId));
}
Expand Down Expand Up @@ -171,21 +186,6 @@ private async Task LoadMoreData(int first, int count)

images = images.Include(x => x.BasketEntry);

// Add in the ordering for the group by
switch( query.Grouping )
{
case GroupingType.None:
case GroupingType.Date:
images = images.OrderByDescending(x => x.SortDate);
break;
case GroupingType.Folder:
images = images.OrderBy(x => x.Folder.Path)
.ThenByDescending(x => x.SortDate);
break;
default:
throw new ArgumentException("Unexpected grouping type.");
}

// Disable this for now - it's slow due to the EFCore subquery bug.
// We mitigate it by loading the tags in a separate query below.
// images = images.Include(x => x.ImageTags)
Expand Down
35 changes: 20 additions & 15 deletions Damselfly.Core/Services/SelectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ public void SelectImages(List<Image> images)
NotifyStateChanged();
}

/// <summary>
/// Add images into the selection
/// </summary>
/// <param name="images"></param>
public void DeselectImages(List<Image> images)
{
bool removed = false;

foreach (var img in images)
{
if (selectedImages.Remove(img.ImageId))
removed = true;
}

if (removed)
NotifyStateChanged();
}

/// <summary>
/// Add images into the selection
/// </summary>
Expand All @@ -74,27 +92,14 @@ public void ToggleSelection(List<Image> images)
/// Add a single image into the selection
/// </summary>
/// <param name="img"></param>
public void SelectImage(Image img)
{
if (selectedImages.TryAdd(img.ImageId, img) )
NotifyStateChanged();
}
public void SelectImage(Image img) => SelectImages(new List<Image> { img });

/// <summary>
/// Remove an image from the selection
/// </summary>
/// <param name="img"></param>
/// <returns></returns>
public bool DeselectImage(Image img)
{
if( selectedImages.Remove( img.ImageId ) )
{
NotifyStateChanged();
return true;
}

return false;
}
public void DeselectImage(Image img) => DeselectImages(new List<Image> { img });

public int SelectionCount { get { return selectedImages.Count; } }

Expand Down
2 changes: 2 additions & 0 deletions Damselfly.Web/JsMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Damselfly.Web
{
public static class JsMethods
{
public const string JSGetDesktopVersion = "getDesktopVersion";

/// <summary>
/// Called from the Javascript in the Desktop App. The Electron Node code
/// calls the checkDesktopUpgrade in _Host.cshtml, passing in its version
Expand Down
2 changes: 1 addition & 1 deletion Damselfly.Web/Shared/About.razor
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
if (firstRender)
{
// Probe for a js function which indicates we're in the electron container
DesktopVersion = await JsRuntime.InvokeAsync<string>("getDesktopVersion");
DesktopVersion = await JsRuntime.InvokeAsync<string>(JsMethods.JSGetDesktopVersion);

isDesktopHosted = !string.IsNullOrEmpty(DesktopVersion);

Expand Down
35 changes: 4 additions & 31 deletions Damselfly.Web/Shared/ExportSettings.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@inject NavigationManager navManager

<div class="damselfly-exportpanel">
<LocalFileExporter @ref="FileExporter"/>
<ExportConfigManager CurrentConfig="@selectedConfig"/>
<div class="damselfly-exportsettings">
<div>
Expand Down Expand Up @@ -41,9 +42,9 @@
<input id="watermarkText" @bind-value="selectedConfig.WatermarkText" />
</div>
<div>
@if (isDesktopHosted)
@if( FileExporter.IsDesktopHosted )
{
<button @onclick="@ExportToLocalFS" class="btn btn-primary">Save Locally</button>
<button @onclick="@FileExporter.ExportBasketToLocalFilesystem" class="btn btn-primary">Save Locally</button>
}
<button @onclick="@Export" class="btn btn-primary">Download as Zip</button>
</div>
Expand All @@ -52,6 +53,7 @@
</div>

@code {
private LocalFileExporter FileExporter;
public ExportType[] exportTypes { get; private set; } = new ExportType[0];
public ExportSize[] exportSizes { get; private set; } = new ExportSize[0];

Expand All @@ -61,7 +63,6 @@
public ExportConfig selectedConfig = new ExportConfig { Name = "Default" };
public string StatusMessage { get; set; }
public bool KeepFolders { get; set; }
public bool isDesktopHosted { get; set; }

private void ChangeConfig(string name)
{
Expand All @@ -85,30 +86,6 @@
StateHasChanged();
}

private async Task ExportToLocalFS()
{
var baseUrl = new Uri(navManager.BaseUri);

foreach (var image in basketService.SelectedImages)
{
var imageUrl = new Uri(baseUrl, image.RawImageUrl);

// TODO: Save XMP and On1 sidecars with the images here.
// Image folder is fully qualified, so we need to make it relative to the pictures folder
// The electron container will use its local settings to create the full path of where
// the image will be written locally on the client-side
string localPath = image.FullPath.MakePathRelativeTo(IndexingService.RootFolder);

StatusService.Instance.StatusText = $"Writing {image.FileName} to {localPath}...";

// Now, shell out to Javascript, which will trigger the download in the Electron container
await JsRuntime.InvokeAsync<string>("writeFileLocally", imageUrl, localPath);
}

StatusService.Instance.StatusText = "Selected images saved locally.";
}

private void Export()
{
InvokeAsync(() => { _ = ProcessExport(); });
Expand All @@ -131,10 +108,6 @@
exportSizes = (ExportSize[])Enum.GetValues(typeof(ExportSize));
await LoadConfigs();

// Probe for a js function which indicates we're in the electron container
var version = await JsRuntime.InvokeAsync<string>("getDesktopVersion");
isDesktopHosted = !string.IsNullOrEmpty( version );

StateHasChanged();
}
}
Expand Down
83 changes: 56 additions & 27 deletions Damselfly.Web/Shared/Images/ImageGrid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@using Damselfly.Web.Shared.Images
@using Damselfly.Core.ImageProcessing

<LocalFileExporter @ref="FileExporter" />
<div class="damselfly-imagegrid" id="scroll-area">
@if (!gridImages.Any())
{
Expand All @@ -34,19 +35,36 @@
if (!string.IsNullOrEmpty(grouping.Key))
{
<div class="damselfly-imageseparator">
@grouping.Key
<button @onclick="() => { AddGroupToBasket(grouping); }" class="btn btn-primary">
<i class="fas fa-shopping-basket" />
&nbsp;Add
</button>
<button @onclick="() => { SelectGroup(grouping); }" class="btn btn-primary">
<i class="fas fa-shopping-basket" />
&nbsp;Select
</button>
<div class="damselfly-imageseparatortitle">
@grouping.Key
</div>
<div class="damselfly-imageseparatorbuttons">
<button @onclick="() => { AddGroupToBasket(grouping); }" class="btn btn-primary damselfly-imageseparatorbutton">
<i class="fas fa-shopping-basket" />
&nbsp;Add
</button>
<button @onclick="() => { RemoveGroupFromBasket(grouping); }" class="btn btn-primary damselfly-imageseparatorbutton">
<i class="fas fa-shopping-basket" />
&nbsp;Remove
</button>
<button @onclick="() => { SelectGroup(grouping); }" class="btn btn-primary damselfly-imageseparatorbutton">
&nbsp;Select
</button>
<button @onclick="() => { DeselectGroup(grouping); }" class="btn btn-primary damselfly-imageseparatorbutton">
&nbsp;De-select
</button>
@if (FileExporter.IsDesktopHosted)
{
<button @onclick="async () => { await FileExporter.ExportImagesToLocalFilesystem(grouping.Images); }" class="btn btn-primary">
<i class="fas fa-shopping-basket" />
&nbsp;Save Locally
</button>
}
</div>
</div>
}

foreach( var image in grouping.Images )
foreach (var image in grouping.Images)
{
var info = new SelectionInfo { image = image, index = allImages++ };

Expand Down Expand Up @@ -83,8 +101,8 @@
{
<label class="damselfly-browsetoollabel">
<input name="thumbSize" value="@choice" type="radio"
checked="@(SearchService.Instance.Grouping == choice)"
@onchange="@(() => { ChangeGroupType( choice ); })" />
checked="@(SearchService.Instance.Grouping == choice)"
@onchange="@(() => { ChangeGroupType( choice ); })" />
@choice.ToString()
</label>
}
Expand All @@ -93,17 +111,18 @@
</div>

@code {
private LocalFileExporter FileExporter;
const int scrollTriggerImages = 30;
const int imagesPerPage = 250;
private bool showDateSeparators = ConfigService.Instance.GetBool( "DateSeparators", false );
private bool showDateSeparators = ConfigService.Instance.GetBool("DateSeparators", false);

bool IsLoading { get; set; } = false;
bool endOfImages = false;
private EventConflator conflator = new EventConflator(2000);

ThumbSize CurrentThumbSize = ConfigService.Instance.Get<ThumbSize>("ThumbSize", ThumbSize.Small);

private void StoreImage( Image image )
private void StoreImage(Image image)
{
// Todo - save an image to local storage
}
Expand All @@ -114,10 +133,20 @@
StatusService.Instance.StatusText = $"{grouping.Images.Count()} images added to the basket";
}

void RemoveGroupFromBasket(ImageGrouping grouping)
{
BasketService.Instance.SetBasketState(grouping.Images, false);
StatusService.Instance.StatusText = $"{grouping.Images.Count()} images removed from the basket";
}

void SelectGroup(ImageGrouping grouping)
{
SelectionService.Instance.SelectImages(grouping.Images);
StatusService.Instance.StatusText = $"{grouping.Images.Count()} images added to the basket";
}

void DeselectGroup(ImageGrouping grouping)
{
SelectionService.Instance.DeselectImages(grouping.Images);
}

private List<ImageGrouping> GroupedImages
Expand All @@ -135,7 +164,7 @@
else if (SearchService.Instance.Grouping == SearchQuery.GroupingType.Date)
{
return gridImages.GroupBy(x => x.SortDate.Date)
.OrderByDescending( x => x.Key )
.OrderByDescending(x => x.Key)
.Select(x => new ImageGrouping { Key = x.Key.ToString("dddd, dd MMMM yyyy"), Images = x.ToList() })
.ToList();
}
Expand All @@ -145,7 +174,7 @@

}

private void ChangeGroupType(SearchQuery.GroupingType newType )
private void ChangeGroupType(SearchQuery.GroupingType newType)
{
SearchService.Instance.Grouping = newType;
}
Expand Down Expand Up @@ -174,15 +203,15 @@
{
int initialLoadCount, scrollPos;

if ( !int.TryParse(ConfigService.Instance.Get("LoadedImages"), out initialLoadCount) || initialLoadCount < imagesPerPage)
if (!int.TryParse(ConfigService.Instance.Get("LoadedImages"), out initialLoadCount) || initialLoadCount < imagesPerPage)
initialLoadCount = imagesPerPage;

if ( !int.TryParse(ConfigService.Instance.Get("ImageScrollTop"), out scrollPos))
if (!int.TryParse(ConfigService.Instance.Get("ImageScrollTop"), out scrollPos))
scrollPos = 0;

searchService.OnChange += SearchQueryChanged;
await LoadData( initialLoadCount );
await InitJsListenerAsync( scrollPos );
await LoadData(initialLoadCount);
await InitJsListenerAsync(scrollPos);
}
}

Expand Down Expand Up @@ -213,22 +242,22 @@
_ = LoadData(imagesPerPage);
}

protected async Task InitJsListenerAsync( int initialScrollPos )
protected async Task InitJsListenerAsync(int initialScrollPos)
{
await JsRuntime.InvokeVoidAsync("InfiniteScroll.Init", "scroll-area", "list-end", DotNetObjectReference.Create(this), initialScrollPos);
}

private void SaveScrollState( int scrollTop )
private void SaveScrollState(int scrollTop)
{
ConfigService.Instance.Set("ImageScrollTop", scrollTop.ToString() );
ConfigService.Instance.Set("ImageScrollTop", scrollTop.ToString());
ConfigService.Instance.Set("LoadedImages", gridImages.Count.ToString());
}

[JSInvokable]
// Debugging method to help us differentiate between JS calls and other data loads
public void HandleScroll( int scrollTop )
public void HandleScroll(int scrollTop)
{
conflator.HandleEvent( (x) => { SaveScrollState( scrollTop ); } );
conflator.HandleEvent((x) => { SaveScrollState(scrollTop); });
}

[JSInvokable]
Expand All @@ -238,7 +267,7 @@
await LoadData(imagesPerPage);
}

public async Task LoadData( int imagesToLoad )
public async Task LoadData(int imagesToLoad)
{
if (!IsLoading)
{
Expand Down
Loading

0 comments on commit 7307e92

Please sign in to comment.