Skip to content

Commit

Permalink
Allow to import project on search and fetch latest projects (#32)
Browse files Browse the repository at this point in the history
* allow to fetch a project and also use paging on browse

* For now only fetch the last 20 project form the relay or allow the user to search for a project with the project id

* change text color

* Adding metadata

* Fix chronological stages bug
  • Loading branch information
dangershony authored Dec 30, 2023
1 parent 53dcb60 commit 0837788
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 31 deletions.
192 changes: 166 additions & 26 deletions src/Angor/Client/Pages/Browse.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
@using Angor.Shared.Models
@using Angor.Shared.Services
@using Nostr.Client.Keys
@using Nostr.Client.Messages
@using System.Text.Json
@using Angor.Client.Models
@inject ICacheStorage SessionStorage;
@inject NavigationManager NavigationManager
@inject IRelayService _RelayService
Expand All @@ -15,42 +18,105 @@

<div class="row">
<div class="col">

<!-- Search Section -->
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Enter search query" id="searchQuery" @bind="searchQuery">
<button class="btn btn-primary" type="button" @onclick="FindProject" disabled="@findInProgress">Find</button>
</div>

@if (findInProgress)
{
<div class="d-flex justify-content-center">
<div class="loader"></div>
</div>
}

<!-- Search Result -->
@if (findProject != null)
{
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title">@findProject.ProjectIdentifier</h5>
<p class="card-text">Nostr ID: @(NostrPublicKey.FromHex(findProject.NostrPubKey).Bech32)</p>

<!-- Search form -->
<form @onsubmit="SearchProjects">
<div class="mb-3">
<label for="searchQuery" class="form-label">Search</label>
<input type="text" id="searchQuery" @bind="searchQuery" class="form-control" placeholder="Enter search query"/>
@if (SessionStorage.IsProjectInStorageById(findProject.ProjectIdentifier))
{
@if (SessionStorage.IsProjectMetadataStorageByPubkey(findProject.NostrPubKey))
{
var metadata = SessionStorage.GetProjectMetadataByPubkey(findProject.NostrPubKey);
<hr />
<h3 class="card-title">@metadata?.Name</h3>
<p class="card-subtitle">@metadata?.About</p>
<hr />
}

//var info = SessionStorage.GetProjectById(findProject.ProjectIdentifier);
<button @onclick="() => ViewProjectDetails(findProject.ProjectIdentifier)" class="btn btn-primary">View</button>
}
else
{
<p class="text-warning-emphasis">Project not found in any relay!</p>
}
</div>
</div>
<button type="submit" class="btn btn-primary" disabled="@searchInProgress">Search</button>
<br/> <br/>
</form>
}

<div class="d-flex justify-content-center">
<button class="btn btn-secondary my-3" @onclick="SearchProjects">Fetch The Last 20 Projects</button>
</div>

<!-- List of projects -->
@if (searchInProgress)
{
<div class="loader"></div>
<div class="d-flex justify-content-center">
<div class="loader"></div>
</div>
}
else
{
@if (projects.Count == 0)
{
<p>No projects found.</p>
<div class="d-flex justify-content-center">
<p>No projects found.</p>
</div>
}
else
{
foreach (var project in projects.OrderByDescending(project => project.CreatedOnBlock))
foreach (var project in projects.OrderBy(project => project.CreatedOnBlock))
{
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title">@project.ProjectIdentifier</h5>
<p class="card-text">Nostr ID: <a href="/" target="_blank">@(NostrPublicKey.FromHex(project.NostrPubKey).Bech32)</a></p>
<button @onclick="() => ViewProjectDetails(project.ProjectIdentifier)" class="btn btn-primary">View</button>
<p class="card-text">Nostr ID: @(NostrPublicKey.FromHex(project.NostrPubKey).Bech32)</p>

@if (SessionStorage.IsProjectInStorageById(project.ProjectIdentifier))
{
@if (SessionStorage.IsProjectMetadataStorageByPubkey(project.NostrPubKey))
{
var metadata = SessionStorage.GetProjectMetadataByPubkey(project.NostrPubKey);
<hr />
<h3 class="card-title">@metadata?.Name</h3>
<p class="card-subtitle">@metadata?.About</p>
<hr />
}

//var info = SessionStorage.GetProjectById(project.ProjectIdentifier);
<button @onclick="() => ViewProjectDetails(project.ProjectIdentifier)" class="btn btn-primary">View</button>
}
else
{
<p class="text-warning-emphasis">Project not found in any relay!</p>
}
</div>
</div>
}
}
}


</div>
</div>
</div>
Expand All @@ -59,6 +125,9 @@
NotificationComponent notificationComponent;
private string searchQuery;
bool searchInProgress = false;
bool findInProgress = false;

ProjectIndexerData? findProject = null;

private List<ProjectIndexerData> projects = new();

Expand All @@ -67,11 +136,60 @@
projects = SessionStorage.GetProjectIndexerData() ?? new();
}

private async Task FindProject()
{
findProject = projects.FirstOrDefault(_ => _.ProjectIdentifier == searchQuery);

if (findProject != null)
{
return;
}

findInProgress = true;

findProject = await _IndexerService.GetProjectByIdAsync(searchQuery);

if (findProject != null)
{
_RelayService.RequestProjectCreateEventsByPubKey(e =>
{
switch (e)
{
case { Kind: NostrKind.Metadata }:
var nostrMetadata = JsonSerializer.Deserialize<ProjectMetadata>(e.Content, Angor.Shared.Services.RelayService.settings);
if (!SessionStorage.IsProjectMetadataStorageByPubkey(e.Pubkey))
SessionStorage.StoreProjectMetadata(e.Pubkey, nostrMetadata);
break;
case { Kind: NostrKind.ApplicationSpecificData }:
var projectInfo = JsonSerializer.Deserialize<ProjectInfo>(e.Content, Angor.Shared.Services.RelayService.settings);
if (!SessionStorage.IsProjectInStorageById(projectInfo.ProjectIdentifier))
SessionStorage.StoreProjectInfo(projectInfo);
break;
}
}, () =>
{
StateHasChanged();
},
new[] { findProject.NostrPubKey });

//_RelayService.LookupProjectsInfoByPubKeys<ProjectInfo>(_ =>
//{
// if (!SessionStorage.IsProjectInStorageById(_.ProjectIdentifier))
// SessionStorage.StoreProjectInfo(_);
//},
//OnEndOfStreamAction: () =>
//{
//},
//nostrPubKey: new[] { findProject.NostrPubKey });
}

findInProgress = false;
}
private async Task SearchProjects()
{
searchInProgress = true;

var blockchainProjects = await _IndexerService.GetProjectsAsync();
var blockchainProjects = await _IndexerService.GetProjectsAsync(null, 20);

var projectsNotInList = blockchainProjects
.Where(blockchainProject => projects.All(_ => _.ProjectIdentifier != blockchainProject.ProjectIdentifier))
Expand All @@ -88,22 +206,44 @@
.ToArray();

if (projectsForLookup.Any())
_RelayService.LookupProjectsInfoByPubKeys<ProjectInfo>(_ =>
_RelayService.RequestProjectCreateEventsByPubKey(e =>
{
if (!SessionStorage.IsProjectInStorageById(_.ProjectIdentifier))
SessionStorage.StoreProjectInfo(_);
},
OnEndOfStreamAction: () =>
switch (e)
{
projects = projects //Remove projects that were not found on the relays
.Where(_ => SessionStorage.IsProjectInStorageById(_.ProjectIdentifier))
.ToList();
case { Kind: NostrKind.Metadata }:
var nostrMetadata = JsonSerializer.Deserialize<ProjectMetadata>(e.Content, Angor.Shared.Services.RelayService.settings);
SessionStorage.StoreProjectMetadata(e.Pubkey, nostrMetadata);
break;
case { Kind: NostrKind.ApplicationSpecificData }:
var projectInfo = JsonSerializer.Deserialize<ProjectInfo>(e.Content, Angor.Shared.Services.RelayService.settings);
if (!SessionStorage.IsProjectInStorageById(projectInfo.ProjectIdentifier))
SessionStorage.StoreProjectInfo(projectInfo);
break;
}
}, () =>
{
searchInProgress = false;
StateHasChanged();
},
projectsForLookup);

//if (projectsForLookup.Any())
// _RelayService.LookupProjectsInfoByPubKeys<ProjectInfo>(_ =>
// {
// if (!SessionStorage.IsProjectInStorageById(_.ProjectIdentifier))
// SessionStorage.StoreProjectInfo(_);
// },
// OnEndOfStreamAction: () =>
// {
// projects = projects //Remove projects that were not found on the relays
// .Where(_ => SessionStorage.IsProjectInStorageById(_.ProjectIdentifier))
// .ToList();
SessionStorage.SetProjectIndexerData(projects);
// SessionStorage.SetProjectIndexerData(projects);
StateHasChanged();
},
nostrPubKey: projectsForLookup);
// StateHasChanged();
// },
// nostrPubKey: projectsForLookup);
StateHasChanged();
}
Expand Down
5 changes: 5 additions & 0 deletions src/Angor/Client/Pages/Create.razor
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,12 @@
foreach (var stage in project.Stages)
{
if ((stage.ReleaseDate - prev).Days < 0)
{
notificationComponent.ShowErrorMessage("Stages must be chronological");
return;
}

prev = stage.ReleaseDate;
}

var operationResult = await notificationComponent.LongOperation(async () =>
Expand Down
11 changes: 11 additions & 0 deletions src/Angor/Client/Pages/View.razor
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@
</div>
<div class="card-body">
<h5 class="card-title">Project Identifier: @project.ProjectIdentifier</h5>

@if (SessionStorage.IsProjectMetadataStorageByPubkey(project.NostrPubKey))
{
var metadata = SessionStorage.GetProjectMetadataByPubkey(project.NostrPubKey);

<hr />
<h3 class="card-title">@metadata?.Name</h3>
<p class="card-subtitle">@metadata?.About</p>
<hr />
}

<a href="@projectExplorerLink" target="_blank">View the transaction on the explorer.</a>
<p class="card-text">Founder Key: @project.FounderKey</p>
<p class="card-text">Start Date: @project.StartDate.ToString("dd/MM/yyyy")</p>
Expand Down
17 changes: 17 additions & 0 deletions src/Angor/Client/Storage/LocalSessionStorage.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Angor.Client.Models;
using Angor.Client.Services;
using Angor.Shared.Models;
using Angor.Shared.Services;
Expand All @@ -21,10 +22,26 @@ public void StoreProjectInfo(ProjectInfo project)
_sessionStorageService.SetItem(project.ProjectIdentifier,project);
}

public ProjectMetadata? GetProjectMetadataByPubkey(string pubkey)
{
return _sessionStorageService.GetItem<ProjectMetadata>(pubkey);
}

public void StoreProjectMetadata(string pubkey, ProjectMetadata projectMetadata)
{
_sessionStorageService.SetItem(pubkey, projectMetadata);
}

public bool IsProjectMetadataStorageByPubkey(string pubkey)
{
return _sessionStorageService.ContainKey(pubkey);
}

public ProjectInfo? GetProjectById(string projectId)
{
return _sessionStorageService.GetItem<ProjectInfo>(projectId);
}

public bool IsProjectInStorageById(string projectId)
{
return _sessionStorageService.ContainKey(projectId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Nostr.Client.Messages.Metadata;

namespace Angor.Client.Models;
namespace Angor.Shared.Models;

public class ProjectMetadata
{
Expand Down
3 changes: 3 additions & 0 deletions src/Angor/Shared/Services/ICacheStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public interface ICacheStorage
void StoreProjectInfo(ProjectInfo project);
ProjectInfo? GetProjectById(string projectId);
bool IsProjectInStorageById(string projectId);
ProjectMetadata? GetProjectMetadataByPubkey(string pubkey);
void StoreProjectMetadata(string pubkey, ProjectMetadata projectMetadata);
bool IsProjectMetadataStorageByPubkey(string pubkey);
List<ProjectIndexerData>? GetProjectIndexerData();
void SetProjectIndexerData(List<ProjectIndexerData> list);
List<UtxoData> GetUnconfirmedInboundFunds();
Expand Down
12 changes: 8 additions & 4 deletions src/Angor/Shared/Services/IndexerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Angor.Client.Services
{
public interface IIndexerService
{
Task<List<ProjectIndexerData>> GetProjectsAsync();
Task<List<ProjectIndexerData>> GetProjectsAsync(int? offset, int limit);
Task<ProjectIndexerData?> GetProjectByIdAsync(string projectId);
Task<List<ProjectInvestment>> GetInvestmentsAsync(string projectId);
Task<string> PublishTransactionAsync(string trxHex);
Expand Down Expand Up @@ -58,18 +58,22 @@ public IndexerService(INetworkConfiguration networkConfiguration, HttpClient htt
_networkService = networkService;
}

public async Task<List<ProjectIndexerData>> GetProjectsAsync()
public async Task<List<ProjectIndexerData>> GetProjectsAsync(int? offset, int limit)
{
var indexer = _networkService.GetPrimaryIndexer();
// todo: dan - make this proper paging
var response = await _httpClient.GetAsync($"{indexer.Url}/api/query/Angor/projects?offset=0&limit=50");
var response = await _httpClient.GetAsync($"{indexer.Url}/api/query/Angor/projects?offset={offset}&limit={limit}");
_networkService.CheckAndHandleError(response);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<ProjectIndexerData>>();
}

public async Task<ProjectIndexerData?> GetProjectByIdAsync(string projectId)
{
if (string.IsNullOrEmpty(projectId))
{
return null;
}

var indexer = _networkService.GetPrimaryIndexer();
var response = await _httpClient.GetAsync($"{indexer.Url}/api/query/Angor/projects/{projectId}");
_networkService.CheckAndHandleError(response);
Expand Down

0 comments on commit 0837788

Please sign in to comment.