Frontend

The first thing to note is, this is created using Create-React-App, as mentioned in the repo. So just go to that link and follow how to create a React App, before starting.

After you bootstrapped the app, go to src, and you'll find several items in it. Compare that to the repo, and if you find differences, just delete them and rewrite them. That will do.

Next, change the package.json to fit the ones you found in repo. You could also make changes to the versions yourself, just make sure that it'll still run fine if that's the case.

Another thing is one removed the building of gh-pages as we don't need that here.

Of course the frontend is important. In fact, whether end users want to use your product depends on how well you designed your frontend, not the functionalities of your backend, for first impression. Only then the backend functionalities come as second impression. You could be a really good contract developer and no one will use your product if your frontend is not attractive compared to alternative solutions by your competitors that might not have the best backend (at least might not be as good as yours) but have a flattering, easy to use, easy to understand, frontend.

One ever read in a book written by Basecamp (Jason and/or DHH) mentioning that: (rephrased)

The first thing to design is your frontend. Whether or not to include a functionality depends on whether you have enough space on your frontend, whether it'll make it cluttered, whether it's necessary, etc.

And honestly speaking, I'm a backend developer, not a frontend. At least, one never did UI design before, nor writing frontend code.

And for frontend, the provided frontend in NEAR contracts might not be the best; most people are backend developers with some knowledge of frontend to make it usable, not to impress. You might prefer to learn from professional UI designer for better UI design, better UX, etc.

Another thing to think about, aside from looking good, is "words expression". Meaning, how well do you express what you want to convey, and how simple it is such that it could be understood by most audiences, not just native English speakers? Do you have support for other languages, so native speakers in other languages doesn't feel foreign? This also means that learning how to write well is another thing. All in all, frontend is like trying to communicate to other people: if you cannot phrase your words well, others cannot understand. Usually when we speak we might complain that others cannot understand how we speak; but actually that is the wrong consideration, as we're the one trying to convey something to others, so the most important thing is whether other understand what you're trying to convey or not, and the conveyor requires to fit to the listener for his/her best experience rather than the listener fitting to the conveyor trying to understand what is being conveyed. Do understand that the listener could choose not to listen to you if you cannot convey it clearly, in his language, making it easy for him/her to understand; but you can't afford to lose your listeners as it means losing a source of income if you're trying to ask people to use your product.

A lot of the things we have are frontend configuration, and one won't talk about it in depth, as different design have different code. You can look more into it, as there are more tutorials from BerryClub about this. There are indeed one more important ones that one want to talk about, is how to connect with the backend. After you deploy your contract, how does your frontend interact with the deployed backend, which is hosted on-chain?

  async _init_Near() {
    const near_config = {
      network_id: 'default',
      node_url: 'https://rpc.testnet.near.org',
      contract_name: ContractName,
      wallet_url: 'https://wallet.testnet.near.org',
    };
    const keystore = new nearAPI.keyStores.BrowserLocalStorageKeyStore();
    const near = await nearAPI.connect(Object.assign({
      deps: { keystore }
    }, near_config));

    this._keystore = keystore;
    this._near_config = near_config;
    this._near = near;

    this._wallet_connection = new nearAPI.WalletConnection(near, ContractName);
    this._account_id = this._wallet_connection.getAccountId();

    this._account = this._wallet_connection.account();
    this._contract = new nearAPI.Contract(this._account, ContractName, {
      viewMethods: ['get_lines', 'get_line_versions', 
      'get_pixel_cost', 'get_account_balance', 
      'get_account_num_pixels', 'get_account_id_by_index'
      ],
      changeMethods: ['draw', 'buy_tokens'],
    });

    this._pixel_cost = parseFloat(
      await this._contract.get_pixel_cost()
    );

    if (this._account_id) {
      await this.refresh_account_stats();
    }

    this._line_versions = Array(BoardHeight).fill(-1);
    this._lines = Array(BoardHeight).fill(false);
    await this.refresh_board(true);
  }

The important thing here is App.js being the main place where design are done.

(P.S. Given how I love using snake-case for functions and variables and JS opposes that rule, and you can see a mixture of snake-case for custom-defined functions and variables above while camelCase for predefined ones. Don't follow one's way of doing it, though. )

This speaks about the core of the program: the others are mostly just the design and speaks of interaction with users (how to react to user inputs); this function speaks with the backend: how frontend interacts with backend. We use the near-api-js library to deal with the interaction.

The above function includes configuration to connect to testnet (check for more information), how to connect to the keystore and the wallet, and define the methods available in the smart contract manually, after you decide what goes into viewMethods and what goes to changeMethods. Other things that goes in here are more specific towards the application, like initialization requires to know the specification of Board at the start rather than Null, and how much each PixelCost.

  async request_sign_in() {
    const app_title = 'NEAR Place';
    await this._wallet_connection.requestSignIn(
      ContractName,
      app_title
    )
  }

  async logout() {
    this._wallet_connection.signOut();
    this._account_id = null;
    this.setState({
      signed_in: !!this._account_id,
      account_id: this._account_id,
    })
  }

There exist two additional public functions which allow users to sign in and logout of their accounts.

That's it we'll talk about today. It's a good idea to look at more contracts, so let's do that. Next, we'll look at another contract

References