Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into btc_to_fiat
Browse files Browse the repository at this point in the history
  • Loading branch information
itailiors committed Aug 29, 2024
2 parents fd3ecdf + 21f0296 commit 84e3fba
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 111 deletions.
117 changes: 60 additions & 57 deletions src/Angor/Client/Pages/Investor.razor
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
@inject IRelayService _RelayService
@inject ISignService _SignService
@inject ISerializer serializer
@inject ISignService _SignService
@inject IEncryptionService _encryptionService

@inject IJSRuntime JS
Expand Down Expand Up @@ -140,70 +139,74 @@


<div class="card-body">
<div class="table-responsive form-control">
<table class="table align-items-center mb-0">
<div class="table-responsive form-control" style="overflow-x: hidden;">
<table class="table table-sm table-striped align-items-center mb-0" style="table-layout: fixed; width: 100%;">
<thead>
<tr>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Name</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Funding Target (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Raised (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Raised (% Target)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Project Status</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">My Investment (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Spent by Founder</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Available to Founder</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">In Recovery</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7">Founder Approval</th>
</tr>
<tr>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7" style="padding: 4px;">Name</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">Funding Target (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">Raised (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">Raised (% Target)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7" style="padding: 4px;">Project Status</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">My Investment (@network.CoinTicker)</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">Spent by Founder</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">Available to Founder</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7 text-end" style="padding: 4px;">In Recovery</th>
<th class="text-uppercase text-xxs font-weight-bolder opacity-7" style="padding: 4px;">Founder Approval</th>
</tr>

</thead>
<tbody>
@foreach (var project in projects)
{
Stats.TryGetValue(project.ProjectInfo.ProjectIdentifier, out var stats);
var nostrPubKey = project.ProjectInfo.NostrPubKey;
investmentRequestsMap.TryGetValue(nostrPubKey, out bool hasInvestmentRequests);

@foreach (var project in projects)
{
Stats.TryGetValue(project.ProjectInfo.ProjectIdentifier, out var stats);
var nostrPubKey = project.ProjectInfo.NostrPubKey;
investmentRequestsMap.TryGetValue(nostrPubKey, out bool hasInvestmentRequests);

<tr>
<td>
<a href=@($"/view/{project.ProjectInfo.ProjectIdentifier}")>@project.Metadata?.Name</a>
</td>
<td>@project.ProjectInfo.TargetAmount @network.CoinTicker</td>
<td>@Money.Satoshis(stats?.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker </td>
<td>@((stats?.AmountInvested ?? 0) * 100 / Money.Coins(project.ProjectInfo.TargetAmount).Satoshi) %</td>
<td>
@if (project.ProjectInfo.StartDate < DateTime.UtcNow)
{
<span class="text-info">Funding</span>
}
else
{
<span class="text-success">Live</span>
}
</td>
<td>
@Money.Satoshis(project.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker
@if (!project.SignaturesInfo?.Signatures.Any() ?? false)
{
<a href=@($"/invest/{project.ProjectInfo.ProjectIdentifier}") class="btn btn-link" data-toggle="tooltip" title="Pending"> <i class="oi oi-clock"></i></a>
}
</td>
<td>-</td>
<td>-</td>
<td>@Money.Satoshis(project.AmountInRecovery ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker</td>
<td>
@if (hasInvestmentRequests)
{
<a href="@($"/invest/{project.ProjectInfo.ProjectIdentifier}")" class="text-info">Approved</a>
}

</td>
</tr>
}
<tr>
<td>
<a href=@($"/view/{project.ProjectInfo.ProjectIdentifier}") class="text-truncate">@project.Metadata?.Name</a>
</td>
<td class="text-end">@project.ProjectInfo.TargetAmount @network.CoinTicker</td>
<td class="text-end">@Money.Satoshis(stats?.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker</td>
<td class="text-end">@((stats?.AmountInvested ?? 0) * 100 / Money.Coins(project.ProjectInfo.TargetAmount).Satoshi) %</td>
<td>
@if (project.ProjectInfo.StartDate < DateTime.UtcNow)
{
<span class="text-info">Funding</span>
}
else
{
<span class="text-success">Live</span>
}
</td>
<td class="text-end">
@Money.Satoshis(project.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker
@if (!project.SignaturesInfo?.Signatures.Any() ?? false)
{
<a href=@($"/invest/{project.ProjectInfo.ProjectIdentifier}") class="btn btn-link p-0" data-toggle="tooltip" title="Pending"> <i class="oi oi-clock"></i></a>
}
</td>
<td class="text-end">-</td>
<td class="text-end">-</td>
<td class="text-end">@Money.Satoshis(project.AmountInRecovery ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker</td>
<td>
@if (hasInvestmentRequests)
{
<a href="@($"/invest/{project.ProjectInfo.ProjectIdentifier}")" class="text-info">Approved</a>
}
else
{
<span class="text-warning">Pending</span>
}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>

</div>

</div>
Expand Down
106 changes: 83 additions & 23 deletions src/Angor/Client/Pages/Spend.razor
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
bool stageisActive = stage.Stage.ReleaseDate < DateTime.UtcNow;
var investedCount = stage.Items.Count(c => c.IsSpent == false);
var investedAmount = stage.Items.Where(c => c.IsSpent == false).Sum(c => c.Amount);
bool noCoinsToClaim = investedCount == 0 || stage.StagePinner == true;
bool noCoinsToClaim = investedCount == 0 || stage.StageSpinner == true;


<div class="mb-4">
Expand All @@ -85,8 +85,8 @@
@if (stageisActive)
{

<button class="btn @((noCoinsToClaim) ? "btn-light" : "btn-success")" disabled="@noCoinsToClaim" @onclick="() => ClaimCoinsCheckPassword(stage.StageIndex)">
@if (stage.StagePinner)
<button class="btn @((noCoinsToClaim) ? "btn-light" : "btn-success")" disabled="@((noCoinsToClaim || !IsCheckboxSelectedForStage(stage.StageIndex)))" @onclick="() => ClaimCoinsCheckPassword(stage.StageIndex)">
@if (stage.StageSpinner)
{
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Building trx...</span>
Expand Down Expand Up @@ -122,12 +122,12 @@
<div class="mt-3">
@foreach (var transaction in stage.Items)
{
bool isTicked = selected.ContainsKey(transaction.Trxid);
bool isTicked = IsUtxoSelected(transaction.Trxid, transaction.Outputindex);
string statusClass = transaction.IsSpent ? "bg-warning text-dark" : "bg-success text-light";
string statusText = transaction.IsSpent ? "Spent" : "Unspent";

<div class="d-flex mb-2">
<input id="@transaction.Trxid" type="checkbox" disabled="@(!stageisActive || transaction.IsSpent)" value="@isTicked" @onclick="() => HandleCheckboxChange(transaction.Trxid)" />
<input id="@transaction.Trxid" type="checkbox" disabled="@(!stageisActive || transaction.IsSpent)" checked="@isTicked" @onclick="() => HandleCheckboxChange(transaction.Trxid, transaction.Outputindex)"/>
<label for="@transaction.Trxid">
@transaction.Amount @network.CoinTicker - utxo :
<span style="cursor: pointer; text-decoration: underline;" @onclick="() => CopyTRXToClipboard(transaction.Trxid)">@transaction.Trxid</span> -@transaction.Outputindex
Expand All @@ -136,8 +136,8 @@
</span>
</label>
</div>

}

</div>
}
</div>
Expand Down Expand Up @@ -174,11 +174,19 @@
<hr>

<h6 class="mt-3 mb-2">Stages</h6>
@foreach (var item in selected)
@foreach (var utxo in selectedUtxos)
{
<p style="font-size: 0.7em;" class="mb-1">@item.Key</p>
if (utxo.Value) // check if the UTXO is selected
{
var utxoKey = utxo.Key;

var stageIndex = GetStageIndexForUtxo(utxoKey);

<p style="font-size: 0.7em;" class="mb-1">Stage: @stageIndex, Transaction: @utxoKey.Trxid, Output Index: @utxoKey.Outputindex</p>
}
}


<hr>

<p class="mt-3">Are you sure you want to continue?</p>
Expand Down Expand Up @@ -255,14 +263,14 @@

List<(Transaction Transaction, string TrxId)> transactions = new();

Dictionary<string, string> selected = new();
Dictionary<UtxoKey, bool> selectedUtxos = new Dictionary<UtxoKey, bool>();

public class StageData
{
public int StageIndex;
public Stage Stage;
public List<StageDataTrx> Items = new();
public bool StagePinner = false;
public bool StageSpinner = false;
}

public class StageDataTrx
Expand Down Expand Up @@ -425,8 +433,6 @@
}
}



private async Task CheckSpentFund()
{
List<QueryTransaction> trxs = new();
Expand Down Expand Up @@ -480,16 +486,15 @@
{
passwordComponent.ShowPassword(async () =>
{
await ClaimCoins(stageId); ;
await ClaimCoins(stageId);
});
}
}

private async Task ClaimCoins(int stageId)
{
var stage = stageDatas.First(s => s.StageIndex == stageId);

stage.StagePinner = true;
stage.StageSpinner = true;

StateHasChanged();
await Task.Delay(10);
Expand All @@ -507,13 +512,15 @@

founderContext = new FounderContext { ProjectInfo = project, ProjectSeeders = new ProjectSeeders() };

foreach (var item in selected)
foreach (var utxo in selectedUtxos.Where(kv => GetStageIndexForUtxo(kv.Key) == stageId && kv.Value))
{
var trx = transactions.First(f => f.TrxId == item.Key);
var trxId = utxo.Key.Trxid;
var trx = transactions.First(f => f.TrxId == trxId);

founderContext.InvestmentTrasnactionsHex.Add(trx.Transaction.ToHex(network.Consensus.ConsensusFactory));
}


var accountInfo = storage.GetAccountInfo(network.Name);
var address = accountInfo.GetNextReceiveAddress();
var addressScript = BitcoinWitPubKeyAddress.Create(address, network).ScriptPubKey;
Expand All @@ -533,12 +540,19 @@
}
finally
{
stage.StagePinner = false;
CalculateTotalValues();

if (selectedUtxos.Keys.Any(utxoKey => GetStageIndexForUtxo(utxoKey) == selectedStageId))
{
selectedUtxos.Clear();
}
stage.StageSpinner = false;
}

StateHasChanged();
}


private async Task FeeRangeChanged(ChangeEventArgs e)
{
var selectedItem = e.Value?.ToString();
Expand Down Expand Up @@ -628,20 +642,39 @@
private void Expand(int stageId)
{
expandedStageId = expandedStageId == stageId ? null : stageId;
selectedUtxos.Clear();
}

private void HandleCheckboxChange(string trxId)
private void HandleCheckboxChange(string trxId, int outputIndex)
{
var key = new UtxoKey(trxId, outputIndex);

if (selected.ContainsKey(trxId))
if (selectedUtxos.ContainsKey(key))
{
selected.Remove(trxId);
selectedUtxos.Remove(key);
}
else
{
selected.Add(trxId, null);
selectedUtxos[key] = true;
}
}

private bool IsUtxoSelected(string trxId, int outputIndex)
{
return selectedUtxos.ContainsKey(new UtxoKey(trxId, outputIndex));
}

private int GetStageIndexForUtxo(UtxoKey utxoKey)
{
return stageDatas.First(stage => stage.Items.Any(item => item.Trxid == utxoKey.Trxid && item.Outputindex == utxoKey.Outputindex)).StageIndex;
}


private bool IsCheckboxSelectedForStage(int stageIndex)
{
return selectedUtxos.Any(utxo => GetStageIndexForUtxo(utxo.Key) == stageIndex && utxo.Value);
}


private async Task CopyTRXToClipboard(string trxData)
{
Expand All @@ -653,4 +686,31 @@
await _clipboardService.WriteTextAsync(trxData);
StateHasChanged();
}

public struct UtxoKey
{
public string Trxid { get; set; }
public int Outputindex { get; set; }

public UtxoKey(string trxid, int outputindex)
{
Trxid = trxid;
Outputindex = outputindex;
}

public override bool Equals(object obj)
{
if (!(obj is UtxoKey))
return false;

var key = (UtxoKey)obj;
return Trxid == key.Trxid && Outputindex == key.Outputindex;
}

public override int GetHashCode()
{
return HashCode.Combine(Trxid, Outputindex);
}
}

}
3 changes: 2 additions & 1 deletion src/Angor/Shared/Services/IRelayService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace Angor.Shared.Services;
public interface IRelayService
{
Task<string> AddProjectAsync(ProjectInfo project, string nsec,Action<NostrOkResponse> action);
Task<string> CreateNostrProfileAsync(NostrMetadata metadata, string nsec,Action<NostrOkResponse> action);
Task<string> CreateNostrProfileAsync(NostrMetadata metadata, string nsec,
Action<NostrOkResponse> action);
Task<string> DeleteProjectAsync(string eventId, string hexPrivateKey);
void LookupProjectsInfoByPubKeys<T>(Action<T> responseDataAction, Action? OnEndOfStreamAction,
params string[] nostrPubKey);
Expand Down
Loading

0 comments on commit 84e3fba

Please sign in to comment.