From 57d74c05114cfc942181fc57451aef7ea700cf2c Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Wed, 4 Sep 2024 06:04:05 +0300 Subject: [PATCH 01/16] refactor for Create.razor --- src/Angor/Client/Pages/Create.razor | 823 ++++++++++++++++------------ 1 file changed, 465 insertions(+), 358 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index d6ba9ab8..b814c547 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -1,15 +1,13 @@ @page "/create" -@using Angor.Shared.Models -@using Angor.Shared +@using Angor.Client.Models @using Angor.Client.Storage -@using Blockcore.Consensus.TransactionInfo +@using Angor.Shared +@using Angor.Shared.Models @using Angor.Shared.ProtocolNew @using Angor.Shared.Services +@using Blockcore.Consensus.TransactionInfo @using Blockcore.NBitcoin -@using Nostr.Client.Messages -@using Angor.Client.Models -@using Nostr.Client.Messages.Metadata - +@using NBitcoin.DataEncoders @inherits BaseComponent @inject IDerivationOperations _derivationOperations @inject IWalletStorage _walletStorage; @@ -21,12 +19,12 @@ @inject ILogger _Logger; @inject IFounderTransactionActions _founderTransactionActions - - + + @if (!hasWallet) { - NavigationManager.NavigateTo($"/wallet"); + NavigationManager.NavigateTo("/wallet"); return; }
@@ -34,7 +32,7 @@
- +
@@ -49,9 +47,9 @@
-
-
-
+
+
+
@@ -60,8 +58,8 @@
-
-
+
+
@@ -70,8 +68,8 @@
-
-
+
+
@@ -83,413 +81,430 @@
- -
- @if (activeTab == 1) - { -
-
+@if (activeTab == 1) +{ +
+
+ +
+ + +
- -
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - + +
+
+
- - @*
- -
+ +
-
- - -
*@ - - -
- -
- + -
-
+
- @if (string.IsNullOrEmpty(project.Metadata?.About)) - { -

About

- } - else - { -

@project.Metadata.About

- } + @if (string.IsNullOrEmpty(project.Metadata?.About)) + { +

About

+ } + else + { +

@project.Metadata.About

+ } -
- + +
+ + View Project + +
+
- - - + +} - } - - @if (activeTab == 2) - { - +@if (activeTab == 2) +{ + + +
+ + +
- -
- - -
+ +
+ + +
- -
- - -
+ +
+ + +
- -
- - -
+ +
+ + +
- -
- - -
+ +
+ + +
- -
- - -
+ +
+ + +
- + +

Stages

+ @foreach (var stage in project.ProjectInfo.Stages) + {
- - -
- - -

Stages

- @foreach (var stage in project.ProjectInfo.Stages) - { -
- -
- - - -
+ +
+ + +
- } - - - - - -
-
-
+ } - Previous + + +} - +@if (activeTab == 3) +{ +
+ +

+ Project Name: @project.Metadata.Name +

+
+
+ +

+ Project About: @((MarkupString)project.Metadata.About) +

+
+
+ +

+ Project Identifier: @project.ProjectInfo.ProjectIdentifier +

+
+
+ +

Founder Key: @project.ProjectInfo.FounderKey.Substring(0, 10)...

+
+
+ +

+ Target amount: @project.ProjectInfo.TargetAmount @network.CoinTicker +

+
+
+ +

Start date: @project.ProjectInfo.StartDate.ToString("dd/MM/yyyy") in @((project.ProjectInfo.StartDate - DateTime.Now).Days) days

+
+
+ +

Expiry date: @project.ProjectInfo.ExpiryDate.ToString("dd/MM/yyyy") in @((project.ProjectInfo.ExpiryDate - DateTime.Now).Days) days

+
+
+ +

Penalty days: @project.ProjectInfo.PenaltyDays days

+
- -
-
-
- } +
Stages
- @if (activeTab == 3) + @foreach (var stage in project.ProjectInfo.Stages) { - -
- -

Project Name: @project.Metadata.Name

-
-
- -

Project About: @((MarkupString)project.Metadata.About)

-
-
- -

Project Identifier: @project.ProjectInfo.ProjectIdentifier

-
-
- -

Founder Key: @project.ProjectInfo.FounderKey.Substring(0, 10)...

-
-
- -

Target amount: @project.ProjectInfo.TargetAmount @network.CoinTicker

-
-
- -

Start date: @project.ProjectInfo.StartDate.ToString("dd/MM/yyyy") in @((project.ProjectInfo.StartDate - DateTime.Now).Days) days

-
-
- -

Expiry date: @project.ProjectInfo.ExpiryDate.ToString("dd/MM/yyyy") in @((project.ProjectInfo.ExpiryDate - DateTime.Now).Days) days

-
-
- -

Penalty days: @project.ProjectInfo.PenaltyDays days

+
+
+ +

Stage Percent: @stage.AmountToRelease %

+
+
+ +

Stage Date: @stage.ReleaseDate.ToString("dd/MM/yyyy") - @((stage.ReleaseDate - project.ProjectInfo.StartDate).Days) days after project starts

+
+ } + -
Stages
- - @foreach (var stage in project.ProjectInfo.Stages) + -
-
- - -
+
+
+ +
- } +
+} - @if (showCreateModal) - { - - @@ -273,6 +273,20 @@ } + +
+
+ + + +
+
} @@ -539,34 +553,18 @@ { project = fromStorage; - nostrMetadataCreated = fromStorage.NostrMetadataCreated(); + nostrMetadataCreated = fromStorage.NostrMetadataCreated(); // Set the correct initial state nostrApplicationSpecificDataCreated = fromStorage.NostrApplicationSpecificDataCreated(); } else { - project = new FounderProject - { - ProjectInfo = new ProjectInfo - { - StartDate = DateTime.UtcNow.AddHours(2), - PenaltyDays = 90, - ExpiryDate = DateTime.UtcNow.AddDays(120), - TargetAmount = 50, - FounderKey = projectsKeys.FounderKey, - FounderRecoveryKey = projectsKeys.FounderRecoveryKey, - ProjectIdentifier = projectsKeys.ProjectIdentifier, - NostrPubKey = projectsKeys.NostrPubKey - }, - Metadata = new ProjectMetadata(), - CreationTransactionId = null, - ProjectIndex = projectsKeys.Index - }; + project.ProjectIndex = projectsKeys.Index; + project.ProjectInfo.FounderKey = projectsKeys.FounderKey; + project.ProjectInfo.FounderRecoveryKey = projectsKeys.FounderRecoveryKey; + project.ProjectInfo.ProjectIdentifier = projectsKeys.ProjectIdentifier; + project.ProjectInfo.NostrPubKey = projectsKeys.NostrPubKey; } } - else - { - project = new FounderProject(); - } } @@ -620,13 +618,19 @@ private async void FinalizeNostrProfileCreation() { + if (nostrMetadataCreated) + { + notificationComponent.ShowErrorMessage("Nostr profile already created"); + return; + } + createProfileSpinner = true; StateHasChanged(); try { await CreatNostrProfileAnCheckPassword(); - nostrMetadataCreated = true; // Disable actions in Tab 1 + nostrMetadataCreated = true; // Set to true only after successful creation } catch (Exception e) { @@ -636,13 +640,11 @@ finally { createProfileSpinner = false; - StateHasChanged(); // Ensure the state is refreshed after the operation + StateHasChanged(); } - - // Debugging output to check states - _Logger.LogInformation($"createProfileSpinner: {createProfileSpinner}, nostrMetadataCreated: {nostrMetadataCreated}"); } + private async void FinalizeProjectInfoCreation() { createApplicationDataSpinner = true; @@ -697,7 +699,7 @@ if (string.IsNullOrEmpty(project.Metadata.Name)) { - notificationComponent.ShowErrorMessage("Nostr profile name be filled"); + notificationComponent.ShowErrorMessage("Nostr profile name must be filled"); return; } @@ -717,23 +719,21 @@ createProfileSpinner = false; if (!_.Accepted) - notificationComponent.ShowErrorMessage("Failed to store the project information on the relay!!!"); //TODO add export project info - - if (nostrMetadataCreated == false) + { + notificationComponent.ShowErrorMessage("Failed to store the project information on the relay!"); + } + else { nostrMetadataCreated = true; storage.AddFounderProject(project); } - activeTab = 2; - StateHasChanged(); }); - // todo: do we actually need this? does it actualy work to delete? if (string.IsNullOrEmpty(resultId)) { - createApplicationDataSpinner = false; + createProfileSpinner = false; notificationComponent.ShowErrorMessage("Failed to create nostr profile"); } } @@ -742,10 +742,9 @@ _Logger.LogError(e, e.Message); notificationComponent.ShowErrorMessage(e.Message); } - - StateHasChanged(); } + private async Task CreatProjectInfoOnNostrAnCheckPassword() { if (!passwordComponent.HasPassword()) @@ -1001,7 +1000,7 @@ activeTab = tabNumber; StateHasChanged(); } - + private void OnStagePresetChange(ChangeEventArgs e) { var selectedPreset = e.Value?.ToString(); @@ -1011,9 +1010,9 @@ // Preset 1: 3 Stages (10%, 30%, 60%) project.ProjectInfo.Stages = new List { - new Stage { AmountToRelease = 10, ReleaseDate = DateTime.UtcNow.AddDays(10) }, - new Stage { AmountToRelease = 30, ReleaseDate = DateTime.UtcNow.AddDays(20) }, - new Stage { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(30) } + new() { AmountToRelease = 10, ReleaseDate = DateTime.UtcNow.AddDays(10) }, + new() { AmountToRelease = 30, ReleaseDate = DateTime.UtcNow.AddDays(20) }, + new() { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(30) } }; } else if (selectedPreset == "preset2") @@ -1021,10 +1020,10 @@ // Preset 2: 4 Stages (25%, 25%, 25%, 25%) project.ProjectInfo.Stages = new List { - new Stage { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(10) }, - new Stage { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(20) }, - new Stage { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(30) }, - new Stage { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(40) } + new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(10) }, + new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(20) }, + new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(30) }, + new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(40) } }; } else if (selectedPreset == "preset3") @@ -1032,13 +1031,13 @@ // Preset 3: 2 Stages (60%, 20%, 20%) project.ProjectInfo.Stages = new List { - new Stage { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(15) }, - new Stage { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(15) }, - new Stage { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(30) } + new() { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(15) }, + new() { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(15) }, + new() { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(30) } }; } - StateHasChanged(); + StateHasChanged(); } From 2a06dab8fb7d2f74fad234b5b43b1e7b9acf2598 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Thu, 5 Sep 2024 03:05:50 +0300 Subject: [PATCH 04/16] temp fix for bug --- src/Angor/Client/Pages/Create.razor | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 5774e6dd..f34fbb73 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -620,7 +620,7 @@ { if (nostrMetadataCreated) { - notificationComponent.ShowErrorMessage("Nostr profile already created"); + notificationComponent.ShowErrorMessage("Nostr profile already created 2"); return; } @@ -685,11 +685,11 @@ private async Task CreatNostrProfile() { - if (nostrMetadataCreated) - { - notificationComponent.ShowErrorMessage("Nostr profile already created"); - return; - } + // if (nostrMetadataCreated) // todo check nostrMetadataCreated logic + // { + // notificationComponent.ShowErrorMessage("Nostr profile already created 3"); + // return; + // } if (project.Metadata is null) { From 336d0d8aa7b92f7876cd3f37089e827ab818bfda Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Thu, 5 Sep 2024 03:31:19 +0300 Subject: [PATCH 05/16] . --- src/Angor/Client/Pages/Create.razor | 152 +++++++++++++++------------- 1 file changed, 79 insertions(+), 73 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index f34fbb73..f6ed6545 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -592,13 +592,37 @@ private void ConfirmFinalizeNostrProfileCreation() { + if (nostrMetadataCreated) + { + notificationComponent.ShowErrorMessage("Nostr profile already created"); + return; + } + + if (project.Metadata is null) + { + notificationComponent.ShowErrorMessage("Nostr profile must be filled"); + return; + } + + if (string.IsNullOrEmpty(project.Metadata.Name)) + { + notificationComponent.ShowErrorMessage("Nostr profile name must be filled"); + return; + } + confirmationMessage = "Are you sure you want to finalize the Nostr Profile creation? Once done, changes can only be made on Nostr."; finalizeAction = FinalizeNostrProfileCreation; showConfirmationModal = true; } - private void ConfirmFinalizeProjectInfoCreation() + private async void ConfirmFinalizeProjectInfoCreation() { + if (!await ProjectInfoMetaDataTestInput()) + { + // If validation fails, return early + return; + } + confirmationMessage = "Are you sure you want to finalize the Project Info creation? Once done, changes can only be made on Nostr."; finalizeAction = FinalizeProjectInfoCreation; showConfirmationModal = true; @@ -618,12 +642,6 @@ private async void FinalizeNostrProfileCreation() { - if (nostrMetadataCreated) - { - notificationComponent.ShowErrorMessage("Nostr profile already created 2"); - return; - } - createProfileSpinner = true; StateHasChanged(); @@ -685,24 +703,6 @@ private async Task CreatNostrProfile() { - // if (nostrMetadataCreated) // todo check nostrMetadataCreated logic - // { - // notificationComponent.ShowErrorMessage("Nostr profile already created 3"); - // return; - // } - - if (project.Metadata is null) - { - notificationComponent.ShowErrorMessage("Nostr profile must be filled"); - return; - } - - if (string.IsNullOrEmpty(project.Metadata.Name)) - { - notificationComponent.ShowErrorMessage("Nostr profile name must be filled"); - return; - } - createProfileSpinner = true; StateHasChanged(); await Task.Delay(10); @@ -759,54 +759,6 @@ private async Task CreatProjectInfoOnNostr() { - if (nostrApplicationSpecificDataCreated) - { - notificationComponent.ShowErrorMessage("Nostr project already created"); - return; - } - - if (project.ProjectInfo.TargetAmount < (decimal)0.1) - { - notificationComponent.ShowErrorMessage("Project target amount must be higher then 0.1 BTC"); - return; - } - - if (project.ProjectInfo.StartDate < DateTime.UtcNow) - { - notificationComponent.ShowErrorMessage("Project must start in the future"); - return; - } - - if (project.ProjectInfo.ExpiryDate < project.ProjectInfo.StartDate) - { - notificationComponent.ShowErrorMessage("Project must expire after start date"); - return; - } - - if (project.ProjectInfo.Stages.Any() && project.ProjectInfo.ExpiryDate < project.ProjectInfo.Stages.Last().ReleaseDate) - { - notificationComponent.ShowErrorMessage("Project expire date must be after the last stage date"); - return; - } - - if (project.ProjectInfo.PenaltyDays < 10) - { - notificationComponent.ShowErrorMessage("Project penalty must be higher then 10 days"); - return; - } - - if (project.ProjectInfo.Stages.Count() < 3) - { - notificationComponent.ShowErrorMessage("There must be at least 3 stages"); - return; - } - - if (project.ProjectInfo.Stages.Sum(s => s.AmountToRelease) != 100) - { - notificationComponent.ShowErrorMessage("The stages must sum to 100%"); - return; - } - var projects = storage.GetFounderProjects().Where(_ => !string.IsNullOrEmpty(_.CreationTransactionId)).ToList(); if (projects.Any(a => project.ProjectInfo.ProjectIdentifier == a.ProjectInfo.ProjectIdentifier)) @@ -1040,5 +992,59 @@ StateHasChanged(); } + private async Task ProjectInfoMetaDataTestInput() + { + if (nostrApplicationSpecificDataCreated) + { + notificationComponent.ShowErrorMessage("Nostr project already created"); + return false; + } + + if (project.ProjectInfo.TargetAmount < (decimal)0.1) + { + notificationComponent.ShowErrorMessage("Project target amount must be higher than 0.1 BTC"); + return false; + } + + if (project.ProjectInfo.StartDate < DateTime.UtcNow) + { + notificationComponent.ShowErrorMessage("Project must start in the future"); + return false; + } + + if (project.ProjectInfo.ExpiryDate < project.ProjectInfo.StartDate) + { + notificationComponent.ShowErrorMessage("Project must expire after the start date"); + return false; + } + + if (project.ProjectInfo.Stages.Any() && project.ProjectInfo.ExpiryDate < project.ProjectInfo.Stages.Last().ReleaseDate) + { + notificationComponent.ShowErrorMessage("Project expiry date must be after the last stage date"); + return false; + } + + if (project.ProjectInfo.PenaltyDays < 10) + { + notificationComponent.ShowErrorMessage("Project penalty must be higher than 10 days"); + return false; + } + + if (project.ProjectInfo.Stages.Count() < 3) + { + notificationComponent.ShowErrorMessage("There must be at least 3 stages"); + return false; + } + + if (project.ProjectInfo.Stages.Sum(s => s.AmountToRelease) != 100) + { + notificationComponent.ShowErrorMessage("The stages must sum to 100%"); + return false; + } + + // If all checks pass, return true + return true; + } + } \ No newline at end of file From 71fbccdec4a92a89d0423d25af28d8a86ec0f191 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:49:48 +0200 Subject: [PATCH 06/16] reformat --- .../Client/Components/BalanceDisplay.razor | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Angor/Client/Components/BalanceDisplay.razor b/src/Angor/Client/Components/BalanceDisplay.razor index e77b4998..474f379c 100644 --- a/src/Angor/Client/Components/BalanceDisplay.razor +++ b/src/Angor/Client/Components/BalanceDisplay.razor @@ -14,17 +14,13 @@
@code { - [Parameter] - public decimal BtcBalance { get; set; } + [Parameter] public decimal BtcBalance { get; set; } - [Parameter] - public string BtcBalanceInFiat { get; set; } + [Parameter] public string BtcBalanceInFiat { get; set; } - [Parameter] - public string PreferredCurrency { get; set; } + [Parameter] public string PreferredCurrency { get; set; } - [Parameter] - public bool ShowFiatInline { get; set; } = false; + [Parameter] public bool ShowFiatInline { get; set; } = false; private Network _network; @@ -34,9 +30,11 @@ base.OnInitialized(); } - private string GetTooltip() => - PreferredCurrency != "BTC" && !string.IsNullOrEmpty(BtcBalanceInFiat) && !ShowFiatInline + private string GetTooltip() + { + return PreferredCurrency != "BTC" && !string.IsNullOrEmpty(BtcBalanceInFiat) && !ShowFiatInline ? $"Equivalent: {BtcBalanceInFiat}" : string.Empty; -} + } +} \ No newline at end of file From 5091ce52de2e4fd92727923ccdbcfd6020554f2d Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:00:14 +0200 Subject: [PATCH 07/16] refresh utxo --- src/Angor/Client/Pages/Create.razor | 10 +- src/Angor/Client/Pages/View.razor | 711 ++++++++++++++-------------- 2 files changed, 361 insertions(+), 360 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index f6ed6545..c23912cb 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -911,6 +911,13 @@ { showCreateModal = false; + // Step 1: Refresh UTXOs for the latest balance info + var accountInfo = storage.GetAccountInfo(network.Name); + await _WalletOperations.UpdateDataForExistingAddressesAsync(accountInfo); // Updates UTXO for current addresses + await _WalletOperations.UpdateAccountInfoWithNewAddressesAsync(accountInfo); // Updates UTXO for new addresses + storage.SetAccountInfo(network.Name, accountInfo); // Save updated account info + + // Step 2: Publish the transaction var response = await _WalletOperations.PublishTransactionAsync(network, signedTransaction.Transaction); if (!response.Success) @@ -919,8 +926,8 @@ return; } + // Step 3: Update the project with the transaction ID project.CreationTransactionId = signedTransaction.Transaction.GetHash().ToString(); - storage.UpdateFounderProject(project); } catch (Exception e) @@ -937,6 +944,7 @@ NavigationManager.NavigateTo($"/view/{project.ProjectInfo.ProjectIdentifier}"); } + private void AddStage() { project.ProjectInfo.Stages.Add(new Stage { ReleaseDate = DateTime.UtcNow }); diff --git a/src/Angor/Client/Pages/View.razor b/src/Angor/Client/Pages/View.razor index 413eba5b..0f40c0fa 100644 --- a/src/Angor/Client/Pages/View.razor +++ b/src/Angor/Client/Pages/View.razor @@ -1,13 +1,4 @@ @page "/view/{ProjectId}" -@using Angor.Shared -@using Angor.Client.Storage -@using Angor.Shared.Models -@using Angor.Shared.Utilities -@using Blockcore.NBitcoin -@using Angor.Shared.Services -@using Angor.Client.Models -@using Nostr.Client.Messages -@using System.Text.RegularExpressions @inject IDerivationOperations _derivationOperations @inject IClientStorage storage; @@ -21,75 +12,84 @@ @inject IJSRuntime Js; @inject ILogger Logger; -@inject Angor.Shared.Utilities.NostrConversionHelper NostrHelper - +@inject NostrConversionHelper NostrHelper +@using System.Text.RegularExpressions +@using Angor.Client.Models +@using Angor.Client.Storage +@using Angor.Shared +@using Angor.Shared.Models +@using Angor.Shared.Services +@using Angor.Shared.Utilities +@using Blockcore.NBitcoin +@using NBitcoin.DataEncoders +@using Nostr.Client.Messages @inherits BaseComponent - - + +
- @if (project?.ProjectInfo == null) +@if (project?.ProjectInfo == null) +{ + @if (findInProgress) { - @if (findInProgress) - { -
-
-
- } - else +
+
+
+ } + else + { + @if (!string.IsNullOrEmpty(error)) { - @if (!string.IsNullOrEmpty(error)) - { -
-
-
- - - - @error -
+
+
+
+ + + + @error
- } +
} } - else - { - -
- @if (project.ProjectInfo.NostrPubKey != null) - { - var metadata = project.Metadata; +} +else +{ + +
+ @if (project.ProjectInfo.NostrPubKey != null) + { + var metadata = project.Metadata; -
- - Profile Picture -
+
+ + Profile Picture +
-
-
-
- - - -
-
- @metadata?.Name -
-
+
+
+
+ + + +
+
-

@((MarkupString)metadata?.About)

+

@((MarkupString)metadata?.About)

-
-
- - @*
+
+
+ + @*

Project Identifier: @project.ProjectInfo.ProjectIdentifier

@@ -97,152 +97,152 @@

Founder Key: @project.ProjectInfo.FounderKey

*@ -
- -

Target Amount: @project.ProjectInfo.TargetAmount @network.CoinTicker

-
-
- -

Penalty duration: @project.ProjectInfo.PenaltyDays days

-
-
- -

- Project dates: @project.ProjectInfo.StartDate.ToString("dd/MM/yyyy") - @project.ProjectInfo.ExpiryDate.ToString("dd/MM/yyyy") -

-
- +
+ +

Target Amount: @project.ProjectInfo.TargetAmount @network.CoinTicker

+
+
+ +

Penalty duration: @project.ProjectInfo.PenaltyDays days

+
+
+ +

+ Project dates: @project.ProjectInfo.StartDate.ToString("dd/MM/yyyy") - @project.ProjectInfo.ExpiryDate.ToString("dd/MM/yyyy") +

-
-
- @{ - int startsInDays = (project.ProjectInfo.StartDate - DateTime.UtcNow).Days; - if (startsInDays > 0) - { -

Starts in @startsInDays days

- } - else +
+
+
+ @{ + var startsInDays = (project.ProjectInfo.StartDate - DateTime.UtcNow).Days; + + if (startsInDays > 0) + { +

Starts in @startsInDays days

+ } + else + { + var totalDays = (project.ProjectInfo.ExpiryDate - project.ProjectInfo.StartDate).Days; + var currentDays = (DateTime.Now - project.ProjectInfo.StartDate).Days; + currentDays = currentDays < 0 ? 0 : currentDays; + currentDays = currentDays > totalDays ? totalDays : currentDays; + + if (totalDays > 0) { - int totalDays = (project.ProjectInfo.ExpiryDate - project.ProjectInfo.StartDate).Days; - int currentDays = (DateTime.Now - project.ProjectInfo.StartDate).Days; - currentDays = currentDays < 0 ? 0 : currentDays; - currentDays = currentDays > totalDays ? totalDays : currentDays; - - if (totalDays > 0) - { -
-
-
- } +
+
+
} } -
+ }
+
-
-
-
-
-
-
+
+
+
+
+
-
- @if (projectStats.Loading) - { -
- } - else - { -
- -

Total Raised: @Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC) @network.CoinTicker

-
-
- -

Total Investors: @projectStats.TotalInvestors

-
- @*

Total Seeders: @projectStats.TotalSeeders

*@ -
- -

- @if (projectStats.TimeLeft > 0) - { - Time Left for Investing: @projectStats.TimeLeft days - } - else - { - The investing period is over - } -

-
- } -
- -
- } -
- - - -
-
- - - -
-
- Stages -
-
-
-
- - - - - - - - - - - - - @for (int index = 0; index < project.ProjectInfo.Stages.Count; index++) +
+ @if (projectStats.Loading) + { +
+ } + else { - var stage = project.ProjectInfo.Stages[index]; - var daysUntilStage = (stage.ReleaseDate - currentDate).Days; - var amountInStage = (Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC)) * stage.AmountToRelease / 100; -
- - - - - - - +

+ } - -
StageStage %Release DateDays Until StageAmount Per Stage
@(index + 1)@stage.AmountToRelease %@stage.ReleaseDate.ToString("dd/MM/yyyy") - @if (daysUntilStage < 0) +
+ +

Total Raised: @Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC) @network.CoinTicker

+
+
+ +

Total Investors: @projectStats.TotalInvestors

+
+ @*

Total Seeders: @projectStats.TotalSeeders

*@ +
+ +

+ @if (projectStats.TimeLeft > 0) { - Released + Time Left for Investing: @projectStats.TimeLeft days } else { - @daysUntilStage days + The investing period is over } -

@amountInStage @network.CoinTicker
+
+
+
+ } +
+ + + +
+
+ + + +
+
+ Stages +
+
+
+
+ + + + + + + + + + + + + @for (var index = 0; index < project.ProjectInfo.Stages.Count; index++) + { + var stage = project.ProjectInfo.Stages[index]; + var daysUntilStage = (stage.ReleaseDate - currentDate).Days; + var amountInStage = Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC) * stage.AmountToRelease / 100; + + + + + + + + + } + +
StageStage %Release DateDays Until StageAmount Per Stage
@(index + 1)@stage.AmountToRelease %@stage.ReleaseDate.ToString("dd/MM/yyyy") + @if (daysUntilStage < 0) + { + Released + } + else + { + @daysUntilStage days + } + @amountInStage @network.CoinTicker
+
- - @*
+ + @*

Selected Seeders

@@ -258,159 +258,157 @@
*@ - + -
- @if (founder) - { -
- -

Congratulations, you are the founder!

- @* TODO Add stats here for pending signatures or unspent funds *@ - - +
+ @if (founder) + { +
+ +

Congratulations, you are the founder!

+ @* TODO Add stats here for pending signatures or unspent funds *@ + + - } - else if (invested) - { -
- -

- Thank you for your investment in this project! +

+ } + else if (invested) + { +
+ +

+ Thank you for your investment in this project! - Track your transaction on the explorer. -

+ Track your transaction on the explorer. +

- -
- } - else - { -
- -

- Seize the opportunity to invest in this project. -

- -
+ +
+ } + else + { +
+ +

+ Seize the opportunity to invest in this project. +

+ +
+ } +
- } + + +
+
+ + + +
+
+ Nostr +
+
+ @if (project.ProjectInfo.NostrPubKey != null) + { +
+ @{ + var Npub = NostrHelper.ConvertHexToNpub(project.ProjectInfo.NostrPubKey); + } + - -
-
- - - -
-
- Nostr -
+
+ + +
+
+ +
+ +
-
- @if (project.ProjectInfo.NostrPubKey != null) - { -
- - @{ - var Npub = NostrHelper.ConvertHexToNpub(project.ProjectInfo.NostrPubKey); - } - -
- - -
-
- -
- - -
+ @if (founder) + { + - @if (founder) + @if (isGeneratingNsec) { - - - @if (isGeneratingNsec) - { -
-
-
- } +
+
+
+ } - @if (!string.IsNullOrEmpty(errorMessage)) - { -
- @errorMessage -
- } + @if (!string.IsNullOrEmpty(errorMessage)) + { +
+ @errorMessage +
+ } - @if (!string.IsNullOrEmpty(NostrHexSecKey) || !string.IsNullOrEmpty(NostrNsecSecKey)) - { -
- -
- - -
+ @if (!string.IsNullOrEmpty(NostrHexSecKey) || !string.IsNullOrEmpty(NostrNsecSecKey)) + { +
+ +
+ + +
-
- -
- - -
- } +
+ +
+ + +
} -
- } + } +
+ } -
-
Relays
- @foreach (var relay in NostrClients) - { - -
- -

@relay

-
-
- } -
- } +
+
Relays
+ @foreach (var relay in NostrClients) + { + +
+ +

@relay

+
+
+ } +
+}
@code { - [Parameter] - public string ProjectId { get; set; } + [Parameter] public string ProjectId { get; set; } private Project? project; @@ -423,15 +421,15 @@ private string NostrHexSecKey { get; set; } = string.Empty; - private bool isGeneratingNsec = false; + private bool isGeneratingNsec; private string errorMessage = string.Empty; private string error; private List<(string Hash, int Amount)> SelectedSeeders = new List<(string hash, int amount)> { - { (new uint256().ToString(), 10) }, - { (new uint256().ToString(), 20) }, + (new uint256().ToString(), 10), + (new uint256().ToString(), 20) }; public class ProjectStats @@ -442,23 +440,22 @@ public int TotalSeeders { get; set; } public int TimeLeft { get; set; } public int FundingProgressPercent { get; set; } + } + readonly ProjectStats projectStats = new() + { + Loading = true, + TimeLeft = 0, + TotalInvestors = 0, + TotalRaised = 0, + TotalSeeders = 0, + FundingProgressPercent = 0 }; - ProjectStats projectStats = new ProjectStats - { - Loading = true, - TimeLeft = 0, - TotalInvestors = 0, - TotalRaised = 0, - TotalSeeders = 0, - FundingProgressPercent = 0, - }; - - bool founder = false; - bool invested = false; - bool findInProgress = false; - DateTime currentDate = DateTime.UtcNow; + bool founder; + bool invested; + bool findInProgress; + readonly DateTime currentDate = DateTime.UtcNow; private List NostrClients = new(); @@ -522,9 +519,9 @@ // Handle case where project info is not available error = "Project not found..."; } + StateHasChanged(); - }, - new[] { projectIndexerData.NostrPubKey }); + }, projectIndexerData.NostrPubKey); } else { @@ -563,7 +560,7 @@ projectStats.TimeLeft = 0; var targetSat = Money.Coins(project.ProjectInfo.TargetAmount).Satoshi; - projectStats.FundingProgressPercent = (int)((projectStats.TotalRaised * 100) / targetSat); + projectStats.FundingProgressPercent = (int)(projectStats.TotalRaised * 100 / targetSat); } } } @@ -575,7 +572,6 @@ { projectStats.Loading = false; } - } private async Task RecoverFunds() @@ -596,10 +592,7 @@ { if (!passwordComponent.HasPassword()) { - passwordComponent.ShowPassword(async () => - { - await GenerateNsec(); - }); + passwordComponent.ShowPassword(async () => { await GenerateNsec(); }); } else { @@ -619,7 +612,7 @@ { var words = await passwordComponent.GetWalletAsync(); var nostrKey = _derivationOperations.DeriveProjectNostrPrivateKey(words, founderProject.ProjectIndex); - NostrHexSecKey = NBitcoin.DataEncoders.Encoders.Hex.EncodeData(nostrKey.ToBytes()); + NostrHexSecKey = Encoders.Hex.EncodeData(nostrKey.ToBytes()); NostrNsecSecKey = NostrHelper.ConvertHexToNsec(NostrHexSecKey)!; StateHasChanged(); @@ -643,7 +636,6 @@ } - private async Task CopyNsecSecKeyToClipboardAsync() { if (!string.IsNullOrEmpty(NostrNsecSecKey)) @@ -675,11 +667,11 @@ // Reopen password prompt passwordComponent.ShowPassword(async () => - { - isGeneratingNsec = true; - StateHasChanged(); - await GenerateNsec(); - }); + { + isGeneratingNsec = true; + StateHasChanged(); + await GenerateNsec(); + }); } private async void OpenInBrowseAsync() @@ -710,16 +702,16 @@ input = Regex.Replace(input, @".*?", string.Empty, RegexOptions.IgnoreCase | RegexOptions.Singleline); input = Regex.Replace(input, @"<([a-zA-Z][^\s>]*)(\s+[^>]*)?>", match => - { - string tag = match.Groups[1].Value; - string attributes = match.Groups[2].Value; + { + var tag = match.Groups[1].Value; + var attributes = match.Groups[2].Value; - attributes = Regex.Replace(attributes, @"\s+(style|class)\s*=\s*""[^""]*""", string.Empty, RegexOptions.IgnoreCase); + attributes = Regex.Replace(attributes, @"\s+(style|class)\s*=\s*""[^""]*""", string.Empty, RegexOptions.IgnoreCase); - return $"<{tag}{attributes}>"; - }, RegexOptions.IgnoreCase); + return $"<{tag}{attributes}>"; + }, RegexOptions.IgnoreCase); - string allowedTagsPattern = @"<(?!\/?(br|p|a|ul|ol|li|strong|em|b|i|u|hr|blockquote|img|div|span|table|thead|tbody|tr|td|th)\b)[^>]+>"; + var allowedTagsPattern = @"<(?!\/?(br|p|a|ul|ol|li|strong|em|b|i|u|hr|blockquote|img|div|span|table|thead|tbody|tr|td|th)\b)[^>]+>"; input = Regex.Replace(input, allowedTagsPattern, string.Empty, RegexOptions.IgnoreCase); @@ -728,7 +720,8 @@ public MarkupString ConvertToMarkupString(string input) { - string sanitizedInput = StripHtmlTags(input); + var sanitizedInput = StripHtmlTags(input); return new MarkupString(sanitizedInput); } -} + +} \ No newline at end of file From 2368b0c2c547a6aee8092277a1d9c8d6747cb914 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:12:14 +0200 Subject: [PATCH 08/16] fix view initialization, and add gpt comments for clarity --- src/Angor/Client/Pages/Create.razor | 18 ++-- src/Angor/Client/Pages/View.razor | 129 ++++++++++++++++------------ 2 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index c23912cb..978f588e 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -911,13 +911,11 @@ { showCreateModal = false; - // Step 1: Refresh UTXOs for the latest balance info var accountInfo = storage.GetAccountInfo(network.Name); - await _WalletOperations.UpdateDataForExistingAddressesAsync(accountInfo); // Updates UTXO for current addresses - await _WalletOperations.UpdateAccountInfoWithNewAddressesAsync(accountInfo); // Updates UTXO for new addresses - storage.SetAccountInfo(network.Name, accountInfo); // Save updated account info + await _WalletOperations.UpdateDataForExistingAddressesAsync(accountInfo); + await _WalletOperations.UpdateAccountInfoWithNewAddressesAsync(accountInfo); + storage.SetAccountInfo(network.Name, accountInfo); - // Step 2: Publish the transaction var response = await _WalletOperations.PublishTransactionAsync(network, signedTransaction.Transaction); if (!response.Success) @@ -926,9 +924,12 @@ return; } - // Step 3: Update the project with the transaction ID + // Set and save CreationTransactionId locally project.CreationTransactionId = signedTransaction.Transaction.GetHash().ToString(); - storage.UpdateFounderProject(project); + storage.UpdateFounderProject(project); // Ensure the ID is saved in local storage + + // Navigate to the view page immediately + NavigationManager.NavigateTo($"/view/{project.ProjectInfo.ProjectIdentifier}", true); } catch (Exception e) { @@ -939,9 +940,8 @@ { publishProjectSpinner = false; passwordComponent.ClearPassword(); + StateHasChanged(); // Ensure UI updates after processing } - - NavigationManager.NavigateTo($"/view/{project.ProjectInfo.ProjectIdentifier}"); } diff --git a/src/Angor/Client/Pages/View.razor b/src/Angor/Client/Pages/View.razor index 0f40c0fa..97ef5d18 100644 --- a/src/Angor/Client/Pages/View.razor +++ b/src/Angor/Client/Pages/View.razor @@ -153,9 +153,11 @@ else @if (projectStats.Loading) {
+ @project?.CreationTransactionId } else { + @project?.CreationTransactionId

Total Raised: @Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC) @network.CoinTicker

@@ -461,85 +463,102 @@ else protected override async Task OnInitializedAsync() { + // Set up default Nostr clients NostrClients = _NetworkConfiguration.GetDefaultRelayUrls().Select(_ => _.Url.ToString()).ToList(); - project = storage.GetInvestmentProjects().FirstOrDefault(p => p.ProjectInfo.ProjectIdentifier == ProjectId); + // Check for the project in local storage first + project = storage.GetFounderProjects().FirstOrDefault(p => p.ProjectInfo.ProjectIdentifier == ProjectId); - if (project is InvestorProject findProject) + if (project != null) { - invested = findProject.InvestedInProject(); // TODO: need to scan for the invested projects when opening on a new browser - myProjectExplorerLink = _NetworkConfiguration.GetExplorerUrl().Url + $"/transaction/{findProject.TransactionId}"; - } - else - { - project = storage.GetFounderProjects().FirstOrDefault(p => p.ProjectInfo.ProjectIdentifier == ProjectId); + founder = true; + projectStats.Loading = false; // Stop loading spinner as data is found locally - if (project != null) + // Check if CreationTransactionId is set; if not, notify the user + if (!string.IsNullOrEmpty(project.CreationTransactionId)) { - founder = true; + projectExplorerLink = _NetworkConfiguration.GetExplorerUrl().Url + $"/transaction/{project.CreationTransactionId}"; } else { - project = SessionStorage.GetProjectById(ProjectId); + error = "Project created; awaiting confirmation. Check back shortly."; + } - if (project == null) - { - findInProgress = true; + await RefreshBalance(); // Optionally refresh balance + StateHasChanged(); + return; // Data is ready; no need to proceed further + } - var projectIndexerData = await _IndexerService.GetProjectByIdAsync(ProjectId); + // Check for an investor project if not found as a founder project + project = storage.GetInvestmentProjects().FirstOrDefault(p => p.ProjectInfo.ProjectIdentifier == ProjectId); - if (projectIndexerData != null) + if (project is InvestorProject findProject) + { + invested = findProject.InvestedInProject(); + myProjectExplorerLink = _NetworkConfiguration.GetExplorerUrl().Url + $"/transaction/{findProject.TransactionId}"; + projectStats.Loading = false; // Stop loading spinner as data is found + await RefreshBalance(); + StateHasChanged(); + return; + } + + // If project is not found in local storage, proceed with session storage and indexer fallback + project = SessionStorage.GetProjectById(ProjectId); + + if (project == null) + { + findInProgress = true; + + // Attempt to retrieve project data from the indexer + var projectIndexerData = await _IndexerService.GetProjectByIdAsync(ProjectId); + + if (projectIndexerData != null) + { + project = new Project { CreationTransactionId = projectIndexerData.TrxId }; + _RelayService.RequestProjectCreateEventsByPubKey(e => + { + if (project != null) { - project = new Project { CreationTransactionId = projectIndexerData.TrxId }; - _RelayService.RequestProjectCreateEventsByPubKey(e => + switch (e) { - if (project != null) - { - switch (e) - { - case { Kind: NostrKind.Metadata }: - var nostrMetadata = serializer.Deserialize(e.Content); - project.Metadata ??= nostrMetadata; - break; - case { Kind: NostrKind.ApplicationSpecificData }: - var projectInfo = serializer.Deserialize(e.Content); - project.ProjectInfo ??= projectInfo; - break; - } - } - }, () => - { - findInProgress = false; - if (project?.ProjectInfo != null) - { - SessionStorage.StoreProject(project); - } - else - { - // Handle case where project info is not available - error = "Project not found..."; - } + case { Kind: NostrKind.Metadata }: + var nostrMetadata = serializer.Deserialize(e.Content); + project.Metadata ??= nostrMetadata; + break; + case { Kind: NostrKind.ApplicationSpecificData }: + var projectInfo = serializer.Deserialize(e.Content); + project.ProjectInfo ??= projectInfo; + break; + } + } + }, () => + { + findInProgress = false; + projectStats.Loading = false; // Stop loading spinner once data is available - StateHasChanged(); - }, projectIndexerData.NostrPubKey); + if (project?.ProjectInfo != null) + { + SessionStorage.StoreProject(project); } else { - findInProgress = false; error = "Project not found..."; - StateHasChanged(); } - } - } - } - if (project?.CreationTransactionId != null) - { - projectExplorerLink = _NetworkConfiguration.GetExplorerUrl().Url + $"/transaction/{project.CreationTransactionId}"; - await RefreshBalance(); + StateHasChanged(); + }, projectIndexerData.NostrPubKey); + } + else + { + findInProgress = false; + error = "Project not found..."; + projectStats.Loading = false; // Stop loading spinner as no data is found + StateHasChanged(); + } } } + private async Task RefreshBalance() { try From 1aaae711e64cda0b3d2ddf8eb0a9de2c25bc4db1 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:49:19 +0200 Subject: [PATCH 09/16] fix bug and polish --- src/Angor/Client/Pages/View.razor | 178 +++++++++++++--------- src/Angor/Client/Storage/ClientStorage.cs | 162 +++++++++----------- 2 files changed, 176 insertions(+), 164 deletions(-) diff --git a/src/Angor/Client/Pages/View.razor b/src/Angor/Client/Pages/View.razor index 97ef5d18..a0641c04 100644 --- a/src/Angor/Client/Pages/View.razor +++ b/src/Angor/Client/Pages/View.razor @@ -153,11 +153,9 @@ else @if (projectStats.Loading) {
- @project?.CreationTransactionId } else { - @project?.CreationTransactionId

Total Raised: @Money.Satoshis(projectStats.TotalRaised).ToUnit(MoneyUnit.BTC) @network.CoinTicker

@@ -170,14 +168,19 @@ else

- @if (projectStats.TimeLeft > 0) + @{ + var daysLeft = (project.ProjectInfo.StartDate - DateTime.UtcNow).Days; + } + + @if (daysLeft >= 0) { - Time Left for Investing: @projectStats.TimeLeft days + Time Left for Investing: @daysLeft days } else { The investing period is over } +

} @@ -297,11 +300,14 @@ else

Seize the opportunity to invest in this project.

+ @{ + var timeLeft = (project.ProjectInfo.StartDate - DateTime.UtcNow).Days; + } - -
-
} +
@@ -961,6 +968,22 @@ StateHasChanged(); } + private string GetProgressMessage() + { + if (!nostrMetadataCreated) + { + return "Step 1: Create Nostr Profile"; + } + + if (!nostrApplicationSpecificDataCreated) + { + return "Step 2: Enter Project Info"; + } + + return "Step 3: Finalize Project Creation"; + } + + private void OnStagePresetChange(ChangeEventArgs e) { var selectedPreset = e.Value?.ToString(); From 52a23d8d180f88bcbfbddf9e34b090964741e65b Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:22:32 +0200 Subject: [PATCH 11/16] fix utxo bug --- src/Angor/Client/Pages/Create.razor | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 8a97f4ae..666686dd 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -919,10 +919,31 @@ showCreateModal = false; var accountInfo = storage.GetAccountInfo(network.Name); + + // refresh UTXOs await _WalletOperations.UpdateDataForExistingAddressesAsync(accountInfo); await _WalletOperations.UpdateAccountInfoWithNewAddressesAsync(accountInfo); storage.SetAccountInfo(network.Name, accountInfo); + var walletWords = await passwordComponent.GetWalletAsync(); + + // is this the correct way to get the address @dangershony @davidgershony + var changeAddress = accountInfo.ChangeAddressesInfo.FirstOrDefault(addr => !addr.UtxoData.Any())?.Address; + if (string.IsNullOrEmpty(changeAddress)) + { + notificationComponent.ShowErrorMessage("No available change address found."); + return; + } + + // reconstruct the transaction using refreshed UTXO data + signedTransaction = _WalletOperations.AddInputsAndSignTransaction( + changeAddress, + new Transaction(), + walletWords, + accountInfo, + feeData.SelectedFeeEstimation + ); + var response = await _WalletOperations.PublishTransactionAsync(network, signedTransaction.Transaction); if (!response.Success) @@ -931,11 +952,9 @@ return; } - // Set and save CreationTransactionId locally project.CreationTransactionId = signedTransaction.Transaction.GetHash().ToString(); - storage.UpdateFounderProject(project); // Ensure the ID is saved in local storage + storage.UpdateFounderProject(project); - // Navigate to the view page immediately NavigationManager.NavigateTo($"/view/{project.ProjectInfo.ProjectIdentifier}", true); } catch (Exception e) @@ -947,7 +966,7 @@ { publishProjectSpinner = false; passwordComponent.ClearPassword(); - StateHasChanged(); // Ensure UI updates after processing + StateHasChanged(); } } From a3a0b257f7fcccbca557e2433512bdf00de89a50 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:54:02 +0200 Subject: [PATCH 12/16] Add reset functionality with confirmation for Nostr and Project Info statuses --- src/Angor/Client/Pages/Create.razor | 116 +++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 666686dd..6904a5ce 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -396,6 +396,18 @@ Create Project } + + + +
} @@ -472,6 +484,8 @@ Confirm } + +
@@ -499,6 +513,28 @@
} +@if (showResetConfirmation) +{ + +} + +
@@ -510,6 +546,9 @@ private string confirmationMessage; private Action finalizeAction; + private bool showResetConfirmation; + private string resetTarget; + Transaction unsignedTransaction; TransactionInfo signedTransaction; @@ -766,26 +805,6 @@ private async Task CreatProjectInfoOnNostr() { - var projects = storage.GetFounderProjects().Where(_ => !string.IsNullOrEmpty(_.CreationTransactionId)).ToList(); - - if (projects.Any(a => project.ProjectInfo.ProjectIdentifier == a.ProjectInfo.ProjectIdentifier)) - { - notificationComponent.ShowErrorMessage("This wallet already has a project with this index"); - return; - } - - var prev = DateTime.UtcNow; - foreach (var stage in project.ProjectInfo.Stages) - { - if ((stage.ReleaseDate - prev).Days < 0) - { - notificationComponent.ShowErrorMessage("Stages must be chronological"); - return; - } - - prev = stage.ReleaseDate; - } - createApplicationDataSpinner = true; StateHasChanged(); await Task.Delay(10); @@ -1092,9 +1111,66 @@ return false; } + var projects = storage.GetFounderProjects().Where(_ => !string.IsNullOrEmpty(_.CreationTransactionId)).ToList(); + + if (projects.Any(a => project.ProjectInfo.ProjectIdentifier == a.ProjectInfo.ProjectIdentifier)) + { + notificationComponent.ShowErrorMessage("This wallet already has a project with this index"); + return false; + } + + var prev = DateTime.UtcNow; + foreach (var stage in project.ProjectInfo.Stages) + { + if ((stage.ReleaseDate - prev).Days < 0) + { + notificationComponent.ShowErrorMessage("Stages must be chronological"); + return false; + } + + prev = stage.ReleaseDate; + } + // If all checks pass, return true return true; } + private void ShowResetConfirmation(string target) + { + resetTarget = target; + confirmationMessage = target switch + { + "nostrMetadataCreated" => "Are you sure you want to reset the Nostr Profile creation status? This will allow you to re-attempt the profile creation.", + "nostrApplicationSpecificDataCreated" => "Are you sure you want to reset the Project Info creation status? This will allow you to re-attempt the project info creation.", + _ => "" + }; + showResetConfirmation = true; + StateHasChanged(); + } + + private void CloseResetConfirmation() + { + showResetConfirmation = false; + resetTarget = string.Empty; + confirmationMessage = string.Empty; + StateHasChanged(); + } + + private void ConfirmReset() + { + if (resetTarget == "nostrMetadataCreated") + { + nostrMetadataCreated = false; + } + else if (resetTarget == "nostrApplicationSpecificDataCreated") + { + nostrApplicationSpecificDataCreated = false; + } + + storage.UpdateFounderProject(project); + + CloseResetConfirmation(); + } + } \ No newline at end of file From eeacaa9b660fb603d007b8e048c38d1a147f4944 Mon Sep 17 00:00:00 2001 From: itailiors <78041027+itailiors@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:11:34 +0200 Subject: [PATCH 13/16] fix for inject order --- src/Angor/Client/Pages/View.razor | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Angor/Client/Pages/View.razor b/src/Angor/Client/Pages/View.razor index a0641c04..290f5fe0 100644 --- a/src/Angor/Client/Pages/View.razor +++ b/src/Angor/Client/Pages/View.razor @@ -1,4 +1,14 @@ @page "/view/{ProjectId}" +@using System.Text.RegularExpressions +@using Angor.Client.Models +@using Angor.Client.Storage +@using Angor.Shared +@using Angor.Shared.Models +@using Angor.Shared.Services +@using Angor.Shared.Utilities +@using Blockcore.NBitcoin +@using NBitcoin.DataEncoders +@using Nostr.Client.Messages @inject IDerivationOperations _derivationOperations @inject IClientStorage storage; @@ -13,16 +23,8 @@ @inject ILogger Logger; @inject NostrConversionHelper NostrHelper -@using System.Text.RegularExpressions -@using Angor.Client.Models -@using Angor.Client.Storage -@using Angor.Shared -@using Angor.Shared.Models -@using Angor.Shared.Services -@using Angor.Shared.Utilities -@using Blockcore.NBitcoin -@using NBitcoin.DataEncoders -@using Nostr.Client.Messages + + @inherits BaseComponent From 292fe538b55a65a20dc79ef80a1dbb2d20d20168 Mon Sep 17 00:00:00 2001 From: itail Date: Mon, 18 Nov 2024 07:31:56 +0200 Subject: [PATCH 14/16] add automatic stage creation, and add info for days --- src/Angor/Client/Pages/Create.razor | 80 +++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 6904a5ce..0d53e043 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -257,6 +257,18 @@ +
+ + + + +
+
+ + +
+ +
@@ -266,13 +278,21 @@
- - + +
+ @if (project.ProjectInfo.Stages.IndexOf(stage) > 0) + { +

+ Days since last stage: + @(stage.ReleaseDate - project.ProjectInfo.Stages[project.ProjectInfo.Stages.IndexOf(stage) - 1].ReleaseDate).Days days +

+ }
} +
@@ -568,6 +588,9 @@ }; private int activeTab = 1; + + private int totalDuration; + private int numberOfStages; bool createProfileSpinner; bool createApplicationDataSpinner; @@ -1024,43 +1047,43 @@ private void OnStagePresetChange(ChangeEventArgs e) { - var selectedPreset = e.Value?.ToString(); + totalDuration = 0; + numberOfStages = 0; + var selectedPreset = e.Value?.ToString(); if (selectedPreset == "preset1") { - // Preset 1: 3 Stages (10%, 30%, 60%) project.ProjectInfo.Stages = new List { - new() { AmountToRelease = 10, ReleaseDate = DateTime.UtcNow.AddDays(10) }, - new() { AmountToRelease = 30, ReleaseDate = DateTime.UtcNow.AddDays(20) }, - new() { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(30) } + new() { AmountToRelease = 10, ReleaseDate = project.ProjectInfo.StartDate.AddDays(10) }, + new() { AmountToRelease = 30, ReleaseDate = project.ProjectInfo.StartDate.AddDays(20) }, + new() { AmountToRelease = 60, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) } }; } else if (selectedPreset == "preset2") { - // Preset 2: 4 Stages (25%, 25%, 25%, 25%) project.ProjectInfo.Stages = new List { - new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(10) }, - new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(20) }, - new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(30) }, - new() { AmountToRelease = 25, ReleaseDate = DateTime.UtcNow.AddDays(40) } + new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(10) }, + new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(20) }, + new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) }, + new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(40) } }; } else if (selectedPreset == "preset3") { - // Preset 3: 2 Stages (60%, 20%, 20%) project.ProjectInfo.Stages = new List { - new() { AmountToRelease = 60, ReleaseDate = DateTime.UtcNow.AddDays(15) }, - new() { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(15) }, - new() { AmountToRelease = 20, ReleaseDate = DateTime.UtcNow.AddDays(30) } + new() { AmountToRelease = 60, ReleaseDate = project.ProjectInfo.StartDate.AddDays(15) }, + new() { AmountToRelease = 20, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) }, + new() { AmountToRelease = 20, ReleaseDate = project.ProjectInfo.StartDate.AddDays(45) } }; } StateHasChanged(); } + private async Task ProjectInfoMetaDataTestInput() { if (nostrApplicationSpecificDataCreated) @@ -1171,6 +1194,31 @@ CloseResetConfirmation(); } + + private void GenerateSmartStages() + { + if (totalDuration <= 0 || numberOfStages <= 0) + { + notificationComponent.ShowErrorMessage("Please enter valid values for duration and number of stages."); + return; + } + + project.ProjectInfo.Stages.Clear(); // Reset existing stages + + var stageDuration = totalDuration / numberOfStages; + var stagePercentage = 100m / numberOfStages; + + for (int i = 0; i < numberOfStages; i++) + { + project.ProjectInfo.Stages.Add(new Stage + { + AmountToRelease = stagePercentage, + ReleaseDate = project.ProjectInfo.StartDate.AddDays(i * stageDuration) + }); + } + + StateHasChanged(); + } } \ No newline at end of file From 8e02c8d99d56ea7607c5b08655a2f177299ebd64 Mon Sep 17 00:00:00 2001 From: itail Date: Mon, 18 Nov 2024 07:49:06 +0200 Subject: [PATCH 15/16] fix after merge --- src/Angor/Client/Pages/Create.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 44b056aa..110cfb8d 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -1090,7 +1090,6 @@ StateHasChanged(); } -} private async Task ProjectInfoMetaDataTestInput() { @@ -1228,5 +1227,6 @@ StateHasChanged(); } +} } \ No newline at end of file From 2fb82c2df9a5e20f63bc82f921431b6091ac1a54 Mon Sep 17 00:00:00 2001 From: itail Date: Mon, 18 Nov 2024 09:04:29 +0200 Subject: [PATCH 16/16] refactor and add info --- src/Angor/Client/Pages/Create.razor | 107 +++++++++++++++++----------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/src/Angor/Client/Pages/Create.razor b/src/Angor/Client/Pages/Create.razor index 110cfb8d..835e6708 100644 --- a/src/Angor/Client/Pages/Create.razor +++ b/src/Angor/Client/Pages/Create.razor @@ -256,8 +256,9 @@ - + +
@@ -276,22 +277,21 @@

Stages

@foreach (var stage in project.ProjectInfo.Stages) { -
- -
- - - -
- @if (project.ProjectInfo.Stages.IndexOf(stage) > 0) - { -

- Days since last stage: - @(stage.ReleaseDate - project.ProjectInfo.Stages[project.ProjectInfo.Stages.IndexOf(stage) - 1].ReleaseDate).Days days -

- } +
+ +
+ +
+ @if (project.ProjectInfo.Stages.IndexOf(stage) > 0) + { + var previousStage = project.ProjectInfo.Stages[project.ProjectInfo.Stages.IndexOf(stage) - 1]; + var daysBetween = (stage.ReleaseDate - previousStage.ReleaseDate).Days; +

Days since last stage: @daysBetween days

+ } +
} + @@ -587,6 +587,14 @@ Metadata = new ProjectMetadata(), CreationTransactionId = null }; + + private readonly Dictionary presets = new() + { + { "preset1", (new[] { 10, 30, 60 }, 10) }, + { "preset2", (new[] { 25, 25, 25, 25 }, 10) }, + { "preset3", (new[] { 60, 20, 20 }, 15) } + }; + private int activeTab = 1; @@ -1054,43 +1062,29 @@ private void OnStagePresetChange(ChangeEventArgs e) { - totalDuration = 0; - numberOfStages = 0; - var selectedPreset = e.Value?.ToString(); - if (selectedPreset == "preset1") - { - project.ProjectInfo.Stages = new List - { - new() { AmountToRelease = 10, ReleaseDate = project.ProjectInfo.StartDate.AddDays(10) }, - new() { AmountToRelease = 30, ReleaseDate = project.ProjectInfo.StartDate.AddDays(20) }, - new() { AmountToRelease = 60, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) } - }; - } - else if (selectedPreset == "preset2") + + if (selectedPreset != null && presets.TryGetValue(selectedPreset, out var preset)) { - project.ProjectInfo.Stages = new List - { - new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(10) }, - new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(20) }, - new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) }, - new() { AmountToRelease = 25, ReleaseDate = project.ProjectInfo.StartDate.AddDays(40) } - }; - } - else if (selectedPreset == "preset3") + // Clear existing stages to prevent duplication + project.ProjectInfo.Stages.Clear(); + + // Generate and assign new stages + project.ProjectInfo.Stages = GenerateStages(preset.Percentages, preset.IntervalDays); + + } + else { - project.ProjectInfo.Stages = new List - { - new() { AmountToRelease = 60, ReleaseDate = project.ProjectInfo.StartDate.AddDays(15) }, - new() { AmountToRelease = 20, ReleaseDate = project.ProjectInfo.StartDate.AddDays(30) }, - new() { AmountToRelease = 20, ReleaseDate = project.ProjectInfo.StartDate.AddDays(45) } - }; + notificationComponent.ShowErrorMessage("Invalid preset selected."); + project.ProjectInfo.Stages.Clear(); } StateHasChanged(); } + + private async Task ProjectInfoMetaDataTestInput() { if (nostrApplicationSpecificDataCreated) @@ -1202,6 +1196,33 @@ CloseResetConfirmation(); } + private List GenerateStages(int[] percentages, int intervalDays) + { + var stages = new List(); + var currentDate = project.ProjectInfo.StartDate; + + for (int i = 0; i < percentages.Length; i++) + { + stages.Add(new Stage + { + AmountToRelease = percentages[i], + ReleaseDate = currentDate + }); + + if (i > 0) + { + var daysBetween = (currentDate - stages[i - 1].ReleaseDate).Days; + _Logger.LogInformation($"Days between stage {i} and {i + 1}: {daysBetween} days"); + } + + currentDate = currentDate.AddDays(intervalDays); + } + + return stages; + } + + + private void GenerateSmartStages() { if (totalDuration <= 0 || numberOfStages <= 0)