Iceshrimp.NET/Iceshrimp.Frontend/Components/SearchComponent.razor
2024-09-13 21:44:31 +02:00

198 lines
No EOL
7 KiB
Text

@using System.Diagnostics.CodeAnalysis
@* ReSharper disable once RedundantUsingDirective *@
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<Localization> Loc;
@inject ApiService Api;
@inject NavigationManager Navigation;
<div class="search">
<input @bind="SearchString" class="input"/>
<InputSelect @bind-Value="@SearchType">
@foreach (var type in Enum.GetValues(typeof(SearchTypeEnum)))
{
<option value="@type">@type</option>
}
</InputSelect>
<button @onclick="Search">@Loc["Search"]</button>
</div>
@if (_resultType == ResultType.Notes)
{
<div class="note-results">
@if (NoteSearchInstance?.NoteResponses != null)
{
foreach (var note in NoteSearchInstance?.NoteResponses!)
{
<div class="wrapper">
<div @onclick="() => OpenNote(note.Id)" class="note-container">
<Note NoteResponse="note"/>
</div>
</div>
}
<div class="end">
<ScrollEnd ManualLoad="NoteSearchInstance.FetchOlder" IntersectionChange="async () => { if (!NoteSearchInstance.SearchComplete) { await NoteSearchInstance.FetchOlder(); } }"/>
</div>
}
</div>
}
@if (_resultType == ResultType.Users)
{
<div class="user-results">
@if (UserSearchInstance?.UserResponses != null)
{
foreach (var user in UserSearchInstance?.UserResponses!)
{
<div @onclick="() => OpenProfile(user)" class="wrapper">
<UserProfileCard User="user"/>
</div>
}
<div class="end">
<ScrollEnd ManualLoad="UserSearchInstance.FetchOlder" IntersectionChange="async () => { if (!UserSearchInstance.SearchComplete) { await UserSearchInstance.FetchOlder(); } }"/>
</div>
}
</div>
}
@code {
private string SearchString { get; set; } = "";
private SearchTypeEnum SearchType { get; set; } = SearchTypeEnum.Note;
private NoteSearch? NoteSearchInstance { get; set; }
private UserSearch? UserSearchInstance { get; set; }
private ResultType _resultType;
private async Task Search()
{
NoteSearchInstance = null;
_resultType = ResultType.None;
StateHasChanged();
try
{
var lookup = await Api.Search.Lookup(SearchString);
if (lookup is not null && lookup.TargetUrl.Contains("user"))
{
var user = await Api.Users.GetUser(lookup.TargetUrl.Split("users/")[1]);
if (user is not null)
{
var username = $"@{user.Username}";
if (user.Host != null) username += $"@{user.Host}";
Navigation.NavigateTo($"/{username}");
}
}
else if (lookup is not null && lookup.TargetUrl.Contains("notes"))
{
Navigation.NavigateTo(lookup.TargetUrl);
}
}
catch (ApiException)
{
if (SearchType == SearchTypeEnum.Note)
{
var searchRes = await Api.Search.SearchNotes(SearchString, new PaginationQuery { Limit = 20 });
if (searchRes.Count > 0)
{
NoteSearchInstance = new NoteSearch(searchString: SearchString, api: Api, noteResponses: searchRes);
_resultType = ResultType.Notes;
StateHasChanged();
}
}
if (SearchType == SearchTypeEnum.User)
{
var searchRes = await Api.Search.SearchUsers(SearchString, new PaginationQuery() { Limit = 20 });
if (searchRes.Count > 0)
{
UserSearchInstance = new UserSearch(searchString: SearchString, api: Api, userResponses: searchRes);
_resultType = ResultType.Users;
StateHasChanged();
}
}
}
}
private void OpenNote(string id)
{
Navigation.NavigateTo($"/notes/{id}");
}
private void OpenProfile(UserResponse user)
{
var username = $"@{user.Username}";
if (user.Host != null) username += $"@{user.Host}";
Navigation.NavigateTo($"/{username}");
}
private enum ResultType
{
Default,
Notes,
Users,
None
}
private enum SearchTypeEnum
{
Note,
User
}
[method: SetsRequiredMembers]
private class UserSearch(string searchString, ApiService api, List<UserResponse> userResponses)
{
internal required string MinId = userResponses.Last().Id;
internal required string MaxId = userResponses.First().Id;
internal required string SearchString = searchString;
internal required ApiService Api = api;
public required List<UserResponse> UserResponses { get; init; } = userResponses;
public bool SearchComplete = false;
public async Task FetchOlder()
{
var pq = new PaginationQuery { Limit = 15, MaxId = MinId };
var res = await Api.Search.SearchUsers(SearchString, pq);
switch (res.Count)
{
case > 0:
MinId = res.Last().Id;
UserResponses.AddRange(res);
break;
case 0:
SearchComplete = true;
break;
}
}
}
[method: SetsRequiredMembers]
private class NoteSearch(string searchString, ApiService api, List<NoteResponse> noteResponses)
{
internal required string MinId = noteResponses.Last().Id;
internal required string MaxId = noteResponses.First().Id;
internal required string SearchString = searchString;
internal required ApiService Api = api;
public required List<NoteResponse> NoteResponses { get; init; } = noteResponses;
public bool SearchComplete = false;
public async Task FetchOlder()
{
var pq = new PaginationQuery { Limit = 15, MaxId = MinId };
var res = await Api.Search.SearchNotes(SearchString, pq);
switch (res.Count)
{
case > 0:
MinId = res.Last().Id;
NoteResponses.AddRange(res);
break;
case 0:
SearchComplete = true;
break;
}
}
}
}