From 369619fdff954c870d09a4675a6c605af6617ac0 Mon Sep 17 00:00:00 2001 From: John Edvard Reiten Date: Wed, 17 Aug 2022 17:10:13 +0900 Subject: [PATCH] feat: Add NEAR API with login and logout button --- src/index.html | 5 ++ src/index.js | 6 ++ src/near/config.js | 63 +++++++++++++++++++++ src/near/nearConnection.js | 110 +++++++++++++++++++++++++++++++++++++ src/near/nearLogin.js | 29 ++++++++++ src/styles.css | 9 +-- 6 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 src/near/config.js create mode 100644 src/near/nearConnection.js create mode 100644 src/near/nearLogin.js diff --git a/src/index.html b/src/index.html index 0e7bc5f..6a72053 100644 --- a/src/index.html +++ b/src/index.html @@ -4,9 +4,14 @@ Skeleton rope + + diff --git a/src/index.js b/src/index.js index d4e9071..08e4407 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,12 @@ import { Game } from './Game'; +import { NearConnection } from './near/nearConnection'; +import { initLoginLogout } from './near/nearLogin'; const init = () => { new Game(); + const nearConnection = new NearConnection(); + nearConnection.initContract().then((res) => { + initLoginLogout(nearConnection); + }); }; init(); diff --git a/src/near/config.js b/src/near/config.js new file mode 100644 index 0000000..ff16cc2 --- /dev/null +++ b/src/near/config.js @@ -0,0 +1,63 @@ +const CONTRACT_NAME = 'dev-1618829854588-2430734'; + +function getConfig(env) { + switch (env) { + // case 'production': + // case 'mainnet': + // return { + // networkId: 'mainnet', + // nodeUrl: 'https://rpc.mainnet.near.org', + // contractName: CONTRACT_NAME, + // walletUrl: 'https://wallet.near.org', + // helperUrl: 'https://helper.mainnet.near.org', + // explorerUrl: 'https://explorer.mainnet.near.org', + // }; + case 'development': + case 'testnet': + return { + networkId: 'testnet', + nodeUrl: 'https://rpc.testnet.near.org', + contractName: CONTRACT_NAME, + walletUrl: 'https://wallet.testnet.near.org', + helperUrl: 'https://helper.testnet.near.org', + explorerUrl: 'https://explorer.testnet.near.org', + }; + // case 'betanet': + // return { + // networkId: 'betanet', + // nodeUrl: 'https://rpc.betanet.near.org', + // contractName: CONTRACT_NAME, + // walletUrl: 'https://wallet.betanet.near.org', + // helperUrl: 'https://helper.betanet.near.org', + // explorerUrl: 'https://explorer.betanet.near.org', + // }; + // case 'local': + // return { + // networkId: 'local', + // nodeUrl: 'http://localhost:3030', + // keyPath: `${process.env.HOME}/.near/validator_key.json`, + // walletUrl: 'http://localhost:4000/wallet', + // contractName: CONTRACT_NAME, + // }; + // case 'test': + // case 'ci': + // return { + // networkId: 'shared-test', + // nodeUrl: 'https://rpc.ci-testnet.near.org', + // contractName: CONTRACT_NAME, + // masterAccount: 'test.near', + // }; + // case 'ci-betanet': + // return { + // networkId: 'shared-test-staging', + // nodeUrl: 'https://rpc.ci-betanet.near.org', + // contractName: CONTRACT_NAME, + // masterAccount: 'test.near', + // }; + // default: + // throw Error( + // `Unconfigured environment '${env}'. Can be configured in src/config.js.` + // ); + } +} +module.exports = getConfig; diff --git a/src/near/nearConnection.js b/src/near/nearConnection.js new file mode 100644 index 0000000..0d47bed --- /dev/null +++ b/src/near/nearConnection.js @@ -0,0 +1,110 @@ +import getConfig from './config'; + +export class NearConnection { + walletConnection; + contract; + accountId; + userName; + ready; //promise + nearConfig = getConfig('development'); + resolveContract; + constructor() { + this.ready = new Promise((resolve, reject) => { + this.resolveContract = resolve; + }); + } + + // Initialize contract & set global variables + async initContract() { + // Initialize connection to the NEAR testnet + const keyStore = new window.nearApi.keyStores.BrowserLocalStorageKeyStore(); + const near = await window.nearApi.connect({ ...this.nearConfig, keyStore }); + + // Initializing Wallet based Account. It can work with NEAR testnet wallet that + // is hosted at https://wallet.testnet.near.org + this.walletConnection = new window.nearApi.WalletConnection(near, null); + + // Getting the Account ID. If still unauthorized, it's just empty string + this.accountId = this.walletConnection.getAccountId(); + + // Initializing our contract APIs by contract name and configuration + this.contract = await new window.nearApi.Contract( + this.walletConnection.account(), + this.nearConfig.contractName, + { + // View methods are read only. They don't modify the state, but usually return some value. + viewMethods: ['getScores', 'getScore', 'getName'], + // Change methods can modify the state. But you don't receive the returned value when called. + changeMethods: ['setGreeting', 'setScore', 'setName'], + } + ); + this.resolveContract(); + + return this.walletConnection; + } + + logout() { + this.walletConnection.signOut(); + // reload page + } + login() { + // Allow the current app to make calls to the specified contract on the + // user's behalf. + // This works by creating a new access key for the user's account and storing + // the private key in localStorage. + this.walletConnection.requestSignIn(this.nearConfig.contractName); + } + + setScore(levelName, score, name) { + const json = JSON.stringify({ score, name }); + return this.contract.setScore({ + levelName, + json, + }); + } + + getScores(levelName) { + const scoreBoard = this.contract.getScores({ levelName }); + return scoreBoard; + } + + getScore(levelName) { + const accountId = this.accountId; + return this.contract.getScore({ levelName, accountId }); + } + + setName(name) { + if ( + name && + name != this.userName && + this.walletConnection && + this.walletConnection.isSignedIn() + ) { + this.userName = name; + return this.contract.setName({ name }); + } + return Promise.resolve(); + } + + async getName() { + if (this.userName) { + return Promise.resolve(this.userName); + } + const accountId = this.accountId; + return new Promise((resolve, reject) => { + this.contract + .getName({ accountId }) + .then((res) => { + if (res && res.match(/[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/g)) { + this.userName = 'Invalid username'; + } else { + this.userName = res; + } + resolve(res); + }) + .catch((err) => { + reject(err); + }); + }); + } +} diff --git a/src/near/nearLogin.js b/src/near/nearLogin.js new file mode 100644 index 0000000..ec4bcd8 --- /dev/null +++ b/src/near/nearLogin.js @@ -0,0 +1,29 @@ +export const loginout = (loginoutEl, nearConnection) => { + if (!nearConnection) return; + if (nearConnection.walletConnection.isSignedIn()) { + nearConnection.logout(); + loginoutEl.innerHTML = 'Login to NEAR wallet'; + } else { + nearConnection.login(); + loginoutEl.innerHTML = 'Logout from NEAR wallet'; + } +}; + +export const initLoginLogout = (nearConnection) => { + const loginoutEl = document.getElementById('loginout'); + if ( + nearConnection && + nearConnection.walletConnection && + nearConnection.walletConnection.isSignedIn() + ) { + loginoutEl.innerHTML = 'Logout from NEAR wallet'; + nearConnection.getName().then((res) => { + loginoutEl.innerHTML = `Logout from NEAR wallet`; + }); + } else { + loginoutEl.innerHTML = 'Login to NEAR wallet'; + } + loginoutEl.addEventListener('click', () => + loginout(loginoutEl, nearConnection) + ); +}; diff --git a/src/styles.css b/src/styles.css index 9f61535..5e4903c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,12 +1,13 @@ body { + width: 100vw; + height: 100vh; margin: 0; padding: 0; display: flex; - align-items: center; - align-content: center; + flex-flow: column; justify-content: center; - height: 100vh; - width: 100vw; + align-items: center; + overflow: hidden; } canvas { background-color: gray;