Skip to content

Jednoduchý prohlížeč Neblio Blockchain adres

Štefan Prokop edited this page Feb 17, 2023 · 2 revisions

Tento článek ukazuje, jak vytvořit jednoduchý prohlížeč blockchainových transakcí, které spadají k určité adrese. V příkladu je použit Neblio Blockchain jako 1.vrstva.

VENeblioExplorer screen

Tato aplikace je Blazor WebAssembly. Pojmenovali jsme ji VENeblioExplorerWASM. Postup, jak vytvořit projekt a přidat do něj potřebné reference se nachází ve VEBlazor komponentě.

Postup nebude potřeba celý, protože pro jednoduchý prohlížeč bude stačit jen jedna stránka Index.razor. Ostatní je možné smazat, stejně jako menu. Aby se nezobrazil defaultní layout aplikace peněženky (například tak, jak vypadá nová VENFT app), je potřeba si v App.razor vybrat původní MainLayout.razor. Protože díky importům balíčků VEBlazoru existují dva MainLayouty, je potřeba připsat správný namespace originálního layoutu. V tomto případě to bude:

VENeblioExplorerWASM.Shared.MainLayout

Celé App.razor aplikace pak bude vypadat následovně:

@using VEDriversLite
@using VEDriversLite.NFT
@using VEDriversLite.NFT.Dto
@using VEDriversLite.StorageDriver.StorageDrivers
@inject AppData AppData

<ThemeProvider Theme="@theme">
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(VENeblioExplorerWASM.Shared.MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(VENeblioExplorerWASM.Shared.MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

    <VEFramework.VEBlazor.Components.VEBlazorServicesComponents />
</ThemeProvider>
@code {
    Theme theme = VEFramework.VEBlazor.Models.Themes.DefaultTheme.GetDefaultTheme();

    protected override async Task OnInitializedAsync()
    {
        AppData.AppName = "VENFT";
        AppData.AppNick = "VENFT";
        AppData.AppHomeWebsiteUrl = "https://about.ve-nft.com/";
        AppData.AppShareNFTUrl = "https://apptest.ve-nft.com/";
        AppData.AllowWorkTabs = false;
        AppData.AllowSend = true;
        AppData.AllowDestroy = true;
        AppData.DisplayGettingStartedMenuItem = true;
        AppData.GettingStartedPageName = "gettingstarted";
        AppData.AppTokenId = VEDriversLite.NFT.NFTHelpers.TokenId;

        VEDriversLite.StorageDriver.Helpers.IPFSHelpers.GatewayURL = "https://ve-framework.com/ipfs/"; //veframework public gateway

        var res = await VEDLDataContext.Storage.AddDriver(new VEDriversLite.StorageDriver.StorageDrivers.Dto.StorageDriverConfigDto()
            {
                Type = "IPFS",
                Name = "VEF",
                Location = "Cloud",
                ID = "VEF",
                IsPublicGateway = true,
                IsLocal = false,
                ConnectionParams = new StorageDriverConnectionParams()
                {
                    APIUrl = "https://ve-framework.com/",
                    APIPort = 443,
                    Secured = false,
                    GatewayURL = "https://ve-framework.com/ipfs/",
                    GatewayPort = 443,
                }
            });

        AppData.AllowedNFTTypes = new List<VEDriversLite.NFT.NFTTypes>()
        {
            VEDriversLite.NFT.NFTTypes.Profile,
            VEDriversLite.NFT.NFTTypes.Image,
            VEDriversLite.NFT.NFTTypes.Post,
            VEDriversLite.NFT.NFTTypes.Ticket,
            VEDriversLite.NFT.NFTTypes.Event,
            VEDriversLite.NFT.NFTTypes.Music,
            VEDriversLite.NFT.NFTTypes.Xray,
            VEDriversLite.NFT.NFTTypes.XrayImage,
            VEDriversLite.NFT.NFTTypes.Product,
            VEDriversLite.NFT.NFTTypes.Invoice,
            VEDriversLite.NFT.NFTTypes.Order,
            VEDriversLite.NFT.NFTTypes.IoTDevice,
            VEDriversLite.NFT.NFTTypes.IoTMessage,
            VEDriversLite.NFT.NFTTypes.CoruzantProfile,
            VEDriversLite.NFT.NFTTypes.CoruzantArticle,
            VEDriversLite.NFT.NFTTypes.App
        };

        await base.OnInitializedAsync();
    }
}

Důležitou změnou oproti balíčkům < 0.3.3 je přidání Storage Driveru. Ten umožňuje práci s více IPFS najednou a má i základní driver pro filesystem (zatím není integrovaná v API). Postupně bude sjednocovat přístup k souborům, jejich klonování, apod. V tomto příkladu se odkazuje na instanci IPFS na doméně ve-framework.com.

V tomto příkladu jsou všechny části jen v Index.razor. Soubor vypadá následovně:

@using VEFramework.VEBlazor.Components.Account
@using VEFramework.VEBlazor.Components.Account.Transactions
@using VEDriversLite
@using VEDriversLite.NFT
@inject AppData AppData

@page "/"

<PageTitle>Neblio Address Explorer</PageTitle>

<h1>Simple Address Explorer</h1>

<Row Margin="Margin.Is2.FromTop" Width="Width.Is100" Flex="Flex.JustifyContent.Center">
    <Column>
        <NeblioAddressInput @bind-Receiver="@NewTabAddress" WithBookmarksList=false />
    </Column>
</Row>

<Row Margin="Margin.Is2.FromTop" Flex="Flex.JustifyContent.Center">
    <Column>
        <Button Color="Color.Primary" Loading="@LoadingAccount" Clicked="@OpenSearchTabAction" Block><Icon Name="IconName.Search" Block /> Search Address Details</Button>
    </Column>
</Row>

@if (LoadingAccount)
{
    <Row Margin="Margin.Is2.FromTop">
        <Column>
            <Alert Visible Color="Color.Info">
                <AlertMessage>Loading Account Status</AlertMessage>
                <AlertDescription>@state</AlertDescription>
            </Alert>
        </Column>
    </Row>
}

@if (Loaded)
{
    <Row Margin="Margin.Is3.FromBottom.Is3.FromTop" Flex="Flex.JustifyContent.Between">
        <Column>
            <ErrorBoundary>
                <VEFramework.VEBlazor.Components.NFTs.Profile.AccountProfile NFT="@(Account.Profile as INFT)" NFTChanged="@NFTChangedHandler" />
            </ErrorBoundary>
        </Column>
        <Column>
            <AccountBalances NeblioBalance="@Account.TotalSpendableBalance"
                             NeblioBalanceUnconfirmed="@Account.TotalUnconfirmedBalance"
                             TokensBalance="@TokenSupply"
                             BDPBalance="@BDPSupply"
                             WDOGEBalance="@WDOGESupply"
                             NFTsCount="@Account.NFTs.Count" />
        </Column>
    </Row>
    <Row Margin="Margin.Is3.FromBottom.Is3.FromTop" Flex="Flex.JustifyContent.Between">
        <TransactionsList @ref=TxListRef Address="@Account.Address" IsSubAccount="false" />
    </Row>
}

@code {

    [Inject] INotificationService? NotificationService { get; set; }
    [Inject] IPageProgressService? PageProgressService { get; set; }

    TransactionsList? TxListRef;

    public string NewTabAddress { get; set; } = string.Empty;
    public bool Loaded { get; set; } = false;
    public NeblioAccount Account { get; set; } = new NeblioAccount();

    bool LoadingAccount = false;
    string state = "Loading Address...";

    public int TokenSupply
    {
        get
        {
            if (Account.TokensSupplies.TryGetValue(NFTHelpers.TokenId, out var tokens))
                return Convert.ToInt32(tokens.Amount);
            else
                return 0;
        }
    }
    public int BDPSupply
    {
        get
        {
            if (Account.TokensSupplies.TryGetValue(NFTHelpers.BDPTokenId, out var tokens))
                return Convert.ToInt32(tokens.Amount);
            else
                return 0;
        }
    }
    public int WDOGESupply
    {
        get
        {
            if (Account.TokensSupplies.TryGetValue(NFTHelpers.WDOGETokenId, out var tokens))
                return Convert.ToInt32(tokens.Amount);
            else
                return 0;
        }
    }

    void NFTChangedHandler(INFT e)
    {
        if (e != null && e.Type == NFTTypes.Profile)
            Account.Profile = e as ProfileNFT;
    }

    async Task OpenSearchTabAction()
    {
        if (!string.IsNullOrEmpty(NewTabAddress))
        {
            LoadingAccount = true;
            Loaded = false;
            if (PageProgressService != null)
                await PageProgressService.Go(null, options => { options.Color = Color.Primary; });

            await InvokeAsync(StateHasChanged);
            await Task.Delay(500);

            Account = new NeblioAccount();
            AppData.Account = new NeblioAccount();

            Account.FirsLoadingStatus -= FirstLoadingStatusHandler;
            Account.FirsLoadingStatus += FirstLoadingStatusHandler;

            if (await Account.LoadAccountWithDummyKey("", NewTabAddress, false))
            {
                AppData.Account = Account;
                Loaded = true;
                await InvokeAsync(StateHasChanged);
                if (TxListRef != null)
                    await TxListRef.Load();
            }
            Account.FirsLoadingStatus -= FirstLoadingStatusHandler;

            if (PageProgressService != null)
                await PageProgressService.Go(-1);
            LoadingAccount = false;
            await InvokeAsync(StateHasChanged);
            await Task.Delay(500);

        }
    }

    async void FirstLoadingStatusHandler(object sender, string e)
    {
        if (!string.IsNullOrEmpty(e)) state = e;
        await InvokeAsync(StateHasChanged);
    }
}

Aplikace je jednoduchá - uživatel zadá Neblio adresu a stiskne tlačítko. Poté se adresa načte do Account (jen pro čtení, protože neznáme privátní klíč) a po dokončení načtení se zobrazí seznam transakcí. Vyzkoušejte si online demo. Můžete si zkusit vyhledat adresu Nh7712QcTBT49NA7f9sB3WqX5WjbGLUmo8

Pro zadání Neblio adresy je možné použít komponentu NeblioAddressInput. Ta provede validaci adresy, zda se skutečně jedná o platnou Neblio adresu.

<Row Margin="Margin.Is2.FromTop" Width="Width.Is100" Flex="Flex.JustifyContent.Center">
    <Column>
        <NeblioAddressInput @bind-Receiver="@NewTabAddress" WithBookmarksList=false />
    </Column>
</Row>

Hodnota je napojena na proměnou NewTabAddress.

Po kliknutí na tlačítko se spustí načítání. Nejdůležitější část je tato:

   Account = new NeblioAccount();
   AppData.Account = new NeblioAccount();

   Account.FirsLoadingStatus -= FirstLoadingStatusHandler;
   Account.FirsLoadingStatus += FirstLoadingStatusHandler;

   if (await Account.LoadAccountWithDummyKey("", NewTabAddress, false))
   {
        AppData.Account = Account;
        Loaded = true;
        await InvokeAsync(StateHasChanged);
        if (TxListRef != null)
            await TxListRef.Load();
   }
   Account.FirsLoadingStatus -= FirstLoadingStatusHandler;

Ta vyčistí objekt Account (i sdílený AppData.Account) a načte zadanou adresu pomocí funkce LoadAccountWithDummyKey. Ta si nejdřív vytvoří dummy privátní klíč (jen pro správný běh některých funkcí, ale není možné s nim podepisovat transakce hledané adresy). Poté si načte data zadané adresy. Pokud je poslední parametr false, tak i všechna NFT a pokud true, tak se NFT nenačtou (jejich transakce však ano, jen se nezpracují do formy instance driveru daného NFT).

Kolem funkce načítání je nastavení handlování eventu procesu prvního načítání adresy. To je užitečné, protože načítání občas trvá.

Clone this wiki locally