Create custom components

All of the existing components can be found in PlutoFramework/Components folder. Feel free to take a look at them and get inspired.

I recommend creating a new folder to keep the components sorted. (Or use an existing folder if relevant)

To keep consistency, please utilise MVVM architecture patterns.

View + ViewModel
// Constructor
public TemplateView()
{
    InitializeComponent();
    
    // Add the ViewModel to BindingContext
    BindingContext = new TemplateViewModel();
}

In View.xaml, programme the visual UI using xaml syntax:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PlutoFramework.Components._000ComponentTemplate.TemplateView">
    <!-- Your code here -->
</ContentView>

We have created these unified interfaces to handle parallel data loading. Feel free to implement them in View.xaml.cs file.

interface ILocalLoadableView
{
    /// <summary>
    /// This function is called first in the queue.
    /// Used for loading locally stored data.
    /// </summary>
    void Load();
}

interface ILocalLoadableAsyncView
{
    /// <summary>
    /// This function is called first in the queue.
    /// Used for loading locally stored data asynchronically.
    /// </summary>
    Task LoadAsync(CancellationToken token);
}

interface ISetEmptyView
{
    /// <summary>
    /// This is a fallback function that is called if the view has not loaded any data,
    /// to display a default empty state instead.
    /// </summary>
    void SetEmpty();
}

public interface ISubstrateClientLoadableView
{
    /// <summary>
    /// Loads information from the SubstrateClient.
    /// </summary>
    void Load(PlutoFrameworkSubstrateClient client);
}

public interface IMainSubstrateClientLoadableView
{
    /// <summary>
    /// Loads information from the main SubstrateClient.
    /// </summary>
    void MainLoad(PlutoFrameworkSubstrateClient client);
}

public interface ISubstrateClientLoadableAsyncView
{
    /// <summary>
    /// Loads information asynchronically from the SubstrateClient.
    /// Usually queries on-chain state.
    /// </summary>
    Task LoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token);
}

public interface IMainSubstrateClientLoadableAsyncView
{
    /// <summary>
    /// Loads information asynchronically from the main SubstrateClient.
    /// Usually queries on-chain state.
    /// </summary>
    Task MainLoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token);
}

Implement them like this:

using PlutoFramework.Model;

namespace PlutoFramework.Components._000ComponentTemplate;

public partial class TemplateView : ContentView, ISubstrateClientLoadableAsyncView, ISetEmptyView
{
    public TemplateView()
    {
    	InitializeComponent();

        BindingContext = new TemplateViewModel();
    }

    public async Task LoadAsync(PlutoFrameworkSubstrateClient client, CancellationToken token)
    {
        var viewModel = (TemplateViewModel)BindingContext;
        // Implement loading logic here
    }

    public void SetEmpty()
    {
        var viewModel = (TemplateViewModel)BindingContext;
        // Implement empty state logic here
    }
}

If you are curious, these interfaces are called in the PlutoFramework/Model/SubstrateClientModel.cs at the right time.

Register the new component to CustomLayoutModel

To support the new component in PlutoLayouts, add it to the PlutoFramework/Model/CustomLayoutModel.cs.

Firstly, register the unique component identifier

public enum ComponentId
{
    U,
    dApp,
    // ...
    YoudComponentId
}

Register this component id to the default layout.

public const string DEFAULT_PLUTO_LAYOUT = "plutolayout: [U, dApp, ..., YourComponentId];[Polkadot, Kusama, ..., YourChain]";

Now provide the View to your ComponentId in the GetView method.

public static IView GetView(ComponentId componentId)
{
    switch (componentId)
    {
        case ComponentId.U:
            return new UpdateView();
        case ComponentId.dApp:
            return new DAppConnectionView();
            
        // ...
        
        case ComponentId.YourComponentId:
            return new TemplateView();
    }

    throw new Exception("Could not parse the PlutoLayout");
}

Lastly, provide the ComponentInfo in the GetComponentInfo method.

public static ComponentInfo GetComponentInfo(ComponentId componentId)
{
    switch (componentId)
    {
        case ComponentId.U:
            return new ComponentInfo
            {
                Name = "Update notification",
                ComponentId = ComponentId.U
            };
        case ComponentId.dApp:
            return new ComponentInfo
            {
                Name = "dApp connection",
                ComponentId = ComponentId.dApp,
            };
        
        // ...
        
        case ComponentId.YourComponentId:
            return new ComponentInfo
            {
                Name = "<Name of your component>",
                ComponentId = ComponentId.YourComponentId,
            };
    }

    throw new Exception("Could not parse the ComponentId");
}

Now, your component should be done and compatible with the rest of PlutoFramework.

Last updated