Next Dapp Alpha

Next Dapp Alpha

  • Tutorial
  • Plugins
  • API
  • Github

›Tutorial

Tutorial

  • Quick Start
  • Plugins
  • Building Plugins
  • Todo App Example
  • Wallet App Example
  • DFINITY Example
  • How to Contribute to Docs
  • Troubleshooting
  • Updates

DFINITY Example

Installation

NextDapp can initialize and deploy DFINITY apps in a matter of minutes. The dfinity template includes a simple todo app you can set up and deploy with 6 lines of commands.

Create an App

nextdapp create todo --template dfinity
cd todo
yarn

Deploy Canisters

Make sure you have dfx installed.

dfx start --background
dfx deploy

Run the App Locally

yarn dev

Now your dapp is running at localhost:3000.

Source Code

Let's look at some core files.

Motoko Smart Contract

Our motoko smart contract at /src/todo/main.mo extends the todo example from this DFINITY tutorial.

import Array "mo:base/Array";
import Nat "mo:base/Nat";
import Debug "mo:base/Debug";

actor Todos {

  stable var todos : [ToDo] = [];
  stable var nextId : Nat = 1;

  type ToDo = {
    id : Nat;
    description : Text;
    completed : Bool;
  };

  func add(todos : [ToDo], description : Text, id : Nat) : [ToDo] {
    let todo : ToDo = {
      id = id;
      description = description;
      completed = false;
    };
    Array.append(todos, [todo])
  };

  public query func getTodos() : async [ToDo] {
    todos;
  };

  public func addTodo (description : Text) : async () {
    todos := add(todos, description, nextId);
    nextId += 1;
  };

  public func markDone(id : Nat) : async () {
    todos := Array.map<ToDo,ToDo>(todos, func (todo : ToDo) : ToDo {
      if (todo.id == id) {
        return {
          id = todo.id;
          description = todo.description;
          completed = true;
        };
      };
      todo
    })
  };

  public func removeTodo(id : Nat) : async () {
    todos := Array.filter<ToDo>(todos, func (todo : ToDo) : Bool {
        return todo.id != id;
    })
  };

};

dfx.json

You can assign arbitrary canister names. NextDapp will read from this file and link to the right canisters.

{
  "canisters": {
    "todo": {
      "main": "src/todo/main.mo",
      "type": "motoko"
    },
    "todo_assets": {
      "dependencies": ["todo"],
      "source": ["out"],
      "type": "assets"
    }
  },
  "defaults": {
    "build": {
      "args": "",
      "packtool": ""
    }
  },
  "dfx": "0.8.3",
  "networks": {
    "local": {
      "bind": "127.0.0.1:8000",
      "type": "ephemeral"
    }
  },
  "version": 1
}

index.js

This is the single page todo app located at /pages/index.js.

import { useState, useEffect } from "react"
import { isNil, map } from "ramda"
import { bind } from "nd"
import { Box, Flex } from "rebass"
import { Input } from "@rebass/forms"

import dfx from "nd/dfx"

const style = {
  add: {
    bg: "#eee",
    p: 2,
    justifyContent: "center",
    alignItems: "center",
    width: "50px",
    cursor: "pointer",
    ":hover": { opacity: 0.75 },
  },
  task: {
    bg: "#eee",
    my: 1,
    px: 3,
    py: 2,
    cursor: "pointer",
    ":hover": { opacity: 0.75 },
  },
  remove: {
    ml: 1,
    bg: "#eee",
    my: 1,
    px: 3,
    py: 2,
    justifyContent: "center",
    alignItems: "center",
    cursor: "pointer",
    width: "35px",
    ":hover": { opacity: 0.75 },
  },
}

export default () => {
  const [task, setTask] = useState("")
  const [todos, setTodos] = useState([])
  const [processing, setProcessing] = useState(null)

  useEffect(() => {
    ;(async () => setTodos(await dfx("todo").getTodos()))()
  }, [])

  return (
    <Flex justifyContent="center">
      <Box maxWidth="600px" p={3}>
        <Flex mb={2}>
          <Input
            disabled={adding ? "disabled" : ""}
            value={task}
            onChange={e => setTask(e.target.value)}
            flex={1}
          />
          <Flex
            sx={style.add}
            onClick={async () => {
              if (!adding && !/^\s*$/.test(task)) {
                setAdding(true)
                await dfx("todo").addTodo(task)
                setTodos(await dfx("todo").getTodos())
                setTask("")
                setAdding(false)
              }
            }}
          >
            {adding ? (
              <Box as="i" className="fas fa-spin fa-circle-notch" />
            ) : (
              <Box as="i" className="fas fa-plus" />
            )}
          </Flex>
        </Flex>
        {map(v => (
          <Flex width={1}>
            <Box
              flex={1}
              sx={style.task}
              onClick={async () => {
                if (!v.completed) {
                  setProcessing(v.id)
                  await dfx("todo").markDone(v.id)
                  setTodos(await dfx("todo").getTodos())
                  setProcessing(null)
                }
              }}
            >
              <Box
                color={v.completed ? "red" : "black"}
                as={v.completed ? "s" : ""}
              >
                {v.description}
              </Box>
            </Box>
            <Flex
              width="50px"
              sx={style.remove}
              onClick={async () => {
                if (isNil(processing)) {
                  setProcessing(v.id)
                  await dfx("todo").removeTodo(v.id)
                  setTodos(await dfx("todo").getTodos())
                  setProcessing(null)
                }
              }}
            >
              {processing === v.id ? (
                <Box as="i" className="fas fa-spin fa-circle-notch" />
              ) : (
                <Box as="i" className="fas fa-times" />
              )}
            </Flex>
          </Flex>
        ))(todos)}
      </Box>
    </Flex>
  )
}

The only thing you need to pay attention to here is how to connect to canisters.

Import nd/dfx.

import dfx from "nd/dfx"

Then, use it like below to connect to a DFINITY canister (todo) and execute a function (getTodos).

await dfx("todo").getTodos()

The rest is the same as how non-dfinity NextDapp apps work.

Deploy to DFINITY Mainnet

Make sure you have enough cycles.

Note that this todo app is not suitable to be deployed without implementeng some access control logics, as there is only one todo list shared with everyone.

dfx deploy --network ic
← Wallet App ExampleHow to Contribute to Docs →
  • Installation
    • Create an App
    • Deploy Canisters
    • Run the App Locally
  • Source Code
    • Motoko Smart Contract
    • dfx.json
    • index.js
  • Deploy to DFINITY Mainnet
Next Dapp Alpha
Docs
Quick StartPluginsTodo App ExampleDFINITY ExampleAPI ReferenceTroubleshootingUpdates
Community
Discord
Contributors
More
BlogBit PluginsGitHubStar
Copyright © 2021 Warashibe, Inc.