Skip to content

Commit

Permalink
make search scroll experience much better
Browse files Browse the repository at this point in the history
  • Loading branch information
NielsPilgaard committed Aug 10, 2024
1 parent 97c7871 commit dc3421d
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 26 deletions.
28 changes: 27 additions & 1 deletion src/shared/Jordnaer.Shared/Groups/GroupSearchFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;

namespace Jordnaer.Shared;

Expand All @@ -19,6 +20,32 @@ public record GroupSearchFilter

public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;

[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode()
{
unchecked // Allow arithmetic overflow, numbers will just "wrap around"
{
var hash = 17;

hash = hash * 23 + (Name?.GetHashCode() ?? 0);
hash = hash * 23 + (Categories != null ? Categories.Aggregate(0, (current, category) => current + category.GetHashCode()) : 0);
hash = hash * 23 + WithinRadiusKilometers.GetHashCode();
hash = hash * 23 + (Location?.GetHashCode() ?? 0);

return hash;
}
}

public virtual bool Equals(UserSearchFilter? other)
{
return other is not null &&
Name == other.Name &&
((Categories == null && other.Categories == null) ||
(Categories != null && other.Categories != null && Categories.SequenceEqual(other.Categories))) &&
WithinRadiusKilometers == other.WithinRadiusKilometers &&
Location == other.Location;
}
}

file class RadiusRequiredAttribute : ValidationAttribute
Expand Down Expand Up @@ -46,7 +73,6 @@ protected override ValidationResult IsValid(object? value, ValidationContext val
if (userSearchFilter.WithinRadiusKilometers is null && string.IsNullOrEmpty(userSearchFilter.Location))
{
return ValidationResult.Success!;

}

return string.IsNullOrEmpty(userSearchFilter.Location)
Expand Down
33 changes: 33 additions & 0 deletions src/shared/Jordnaer.Shared/UserSearch/UserSearchFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;

namespace Jordnaer.Shared;
public record UserSearchFilter
Expand All @@ -24,6 +25,38 @@ public record UserSearchFilter

public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;

[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode()
{
unchecked // Allow arithmetic overflow, numbers will just "wrap around"
{
var hash = 17;

hash = hash * 23 + (Name?.GetHashCode() ?? 0);
hash = hash * 23 + (Categories != null ? Categories.Aggregate(0, (current, category) => current + category.GetHashCode()) : 0);
hash = hash * 23 + WithinRadiusKilometers.GetHashCode();
hash = hash * 23 + (Location?.GetHashCode() ?? 0);
hash = hash * 23 + MinimumChildAge.GetHashCode();
hash = hash * 23 + MaximumChildAge.GetHashCode();
hash = hash * 23 + ChildGender.GetHashCode();

return hash;
}
}

public virtual bool Equals(UserSearchFilter? other)
{
return other is not null &&
Name == other.Name &&
((Categories == null && other.Categories == null) ||
(Categories != null && other.Categories != null && Categories.SequenceEqual(other.Categories))) &&
WithinRadiusKilometers == other.WithinRadiusKilometers &&
Location == other.Location &&
MinimumChildAge == other.MinimumChildAge &&
MaximumChildAge == other.MaximumChildAge &&
ChildGender == other.ChildGender;
}
}

file class RadiusRequiredAttribute : ValidationAttribute
Expand Down
3 changes: 3 additions & 0 deletions src/web/Jordnaer/Extensions/JsRuntimeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public static class JsRuntimeExtensions
public static async Task GoBackAsync(this IJSRuntime jsRuntime)
=> await jsRuntime.InvokeVoidAsyncWithErrorHandling("history.back");

public static async Task NavigateTo(this IJSRuntime jsRuntime, string newUrl)
=> await jsRuntime.InvokeVoidAsyncWithErrorHandling("utilities.updatePathAndQueryString", newUrl);

public static async ValueTask<GeoLocation?> GetGeolocation(this IJSRuntime jsRuntime)
{
var (success, geoLocation) = await jsRuntime.InvokeAsyncWithErrorHandling<GeoLocation>("utilities.getGeolocation");
Expand Down
19 changes: 11 additions & 8 deletions src/web/Jordnaer/Pages/GroupSearch/GroupSearch.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
@inject IGroupSearchService GroupSearchService
@inject NavigationManager Navigation
@inject GroupSearchResultCache Cache
@inject IJSRuntime JsRuntime
@inject ISnackbar Snackbar

<MetadataComponent Title="Grupper"
Description="Søg efter grupper" />
Expand Down Expand Up @@ -77,7 +79,11 @@

_searchResult = await GroupSearchService.GetGroupsAsync(_filter);

UpdateQueryString();
await UpdateQueryString();

Snackbar.Add(message: $"{_searchResult.TotalCount} brugere matchede søgningen.",
severity: _searchResult.TotalCount is 0 ? Severity.Info : Severity.Success,
options => options.VisibleStateDuration = 3500);

_hasSearched = true;

Expand All @@ -88,7 +94,7 @@
}

private static readonly Dictionary<string, object?> _queryStrings = [];
private void UpdateQueryString()
private async Task UpdateQueryString()
{
_queryStrings[nameof(_filter.Name).ToLower()] = _filter.Name;
_queryStrings[nameof(_filter.Categories).ToLower()] = _filter.Categories;
Expand All @@ -98,10 +104,8 @@
_queryStrings[nameof(_filter.PageNumber).ToLower()] = _filter.PageNumber;

var newUrl = Navigation.GetUriWithQueryParameters(_queryStrings);
if (Navigation.Uri != newUrl)
{
Navigation.NavigateTo(newUrl);
}

await JsRuntime.NavigateTo(newUrl);
}

private async ValueTask LoadFromQueryString()
Expand Down Expand Up @@ -138,8 +142,7 @@

_filter = filter;

if (_filter.Equals(Cache.SearchFilter) &&
Cache.SearchResult is not null)
if (_filter.Equals(Cache.SearchFilter) && Cache.SearchResult is not null)
{
_searchResult = Cache.SearchResult;

Expand Down
29 changes: 16 additions & 13 deletions src/web/Jordnaer/Pages/UserSearch/UserSearch.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
@inject IUserSearchService UserSearchService
@inject NavigationManager Navigation
@inject UserSearchResultCache Cache
@inject IJSRuntime JsRuntime
@inject ISnackbar Snackbar

<MetadataComponent Title="Søg efter brugere"
Description="Søg efter brugere" />
Expand Down Expand Up @@ -72,7 +74,11 @@

_searchResult = await UserSearchService.GetUsersAsync(_filter);

UpdateQueryString();
Snackbar.Add(message: $"{_searchResult.TotalCount} brugere matchede søgningen.",
severity: _searchResult.TotalCount is 0 ? Severity.Info : Severity.Success,
options => options.VisibleStateDuration = 3500);

await UpdateQueryString();

_hasSearched = true;

Expand All @@ -83,7 +89,7 @@
}

private static readonly Dictionary<string, object?> _queryStrings = [];
private void UpdateQueryString()
private async Task UpdateQueryString()
{
_queryStrings[nameof(_filter.Name).ToLower()] = _filter.Name;
_queryStrings[nameof(_filter.Categories).ToLower()] = _filter.Categories;
Expand All @@ -96,15 +102,13 @@
_queryStrings[nameof(_filter.PageNumber).ToLower()] = _filter.PageNumber;

var newUrl = Navigation.GetUriWithQueryParameters(_queryStrings);
if (Navigation.Uri != newUrl)
{
Navigation.NavigateTo(newUrl);
}

await JsRuntime.NavigateTo(newUrl);
}

private async ValueTask LoadFromQueryString()
{
var queryStrings = new Uri(Navigation.Uri).Query;
var queryStrings = new Uri(Navigation.Uri).Query;
if (string.IsNullOrEmpty(queryStrings))
{
return;
Expand Down Expand Up @@ -143,15 +147,14 @@

_filter = filter;

if (_filter.Equals(Cache.SearchFilter) &&
Cache.SearchResult is not null)
if (_filter.Equals(Cache.SearchFilter) && Cache.SearchResult is not null)
{
_searchResult = Cache.SearchResult;
_searchResult = Cache.SearchResult;

_hasSearched = true;
_isSearching = false;
_hasSearched = true;
_isSearching = false;

return;
return;
}

await Search();
Expand Down
15 changes: 13 additions & 2 deletions src/web/Jordnaer/wwwroot/js/scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ window.scrollFunctions = {
}
},
loadScrollPosition: function (prefix) {
window.scrollTo(0, sessionStorage.getItem(`${prefix}:scrollPosition`));
const scrollPosition = sessionStorage.getItem(`${prefix}:scrollPosition`);
if (!scrollPosition) {
return;
}

setTimeout(function () {
window.scrollTo({
top: scrollPosition,
left: 0,
behavior: 'instant' // 'auto', 'instant' or 'smooth' (default is 'auto')
});
}, 50); // The delay is required to ensure the scroll position is restored after the page has been rendered
},
scrollToBottomOfElement: function (selector) {
const element = document.querySelector(selector);

if (!element) return;

element.scrollTop = element.scrollHeight;
}
};
13 changes: 11 additions & 2 deletions src/web/Jordnaer/wwwroot/js/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ window.utilities = {

element.style.setProperty("display", "none", "important")
},

focusElement: function (selector) {
const element = document.querySelector(selector);

Expand All @@ -15,11 +15,20 @@ window.utilities = {
element.focus();
},

getGeolocation: async function() {
getGeolocation: async function () {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});

return position.coords;
},

updatePathAndQueryString: function (newUri) {
const currentUrl = new URL(window.location.href);
const newUrl = new URL(newUri, window.location.origin);

if (currentUrl.pathname !== newUrl.pathname || currentUrl.search !== newUrl.search) {
window.history.pushState({}, '', newUrl.pathname + newUrl.search);
}
}
};

0 comments on commit dc3421d

Please sign in to comment.