48 | );
49 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/components/Menu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { css } from 'glamor';
3 | import { useSelector } from 'react-redux';
4 | import { Link, Redirect } from 'react-router-dom';
5 | import LogoIcon from '../images/3DK_LOGO_ICON_1300.png';
6 | import EnterIcon from '../images/enter.png';
7 | import ExitIcon from '../images/exit.png';
8 | import { useHistory } from 'react-router-dom';
9 | import { UserService } from '../UserService';
10 | import { useDispatch } from 'react-redux';
11 | import { setPlayerLogout } from '../GlobalState/UserReducer';
12 |
13 | export const Menu = (props) => {
14 |
15 | const dispatch = useDispatch();
16 | const locationHistory = useHistory();
17 | const UserState = useSelector((store) => store.user);
18 |
19 | const disabledStyle = css({
20 | opacity: 0.5
21 | })
22 |
23 | const handleLogin = () => {
24 | UserService.login(() => {
25 | if (UserService.isLogged()) {
26 | console.log(33)
27 | locationHistory.push('/home');
28 | } else {
29 | console.log(44)
30 | dispatch(setPlayerLogout());
31 | }
32 | });
33 | }
34 |
35 | const onHandleLogout = () => {
36 | UserService.logout();
37 | }
38 |
39 | return (
40 |
69 | );
70 | }
--------------------------------------------------------------------------------
/src/UserService.js:
--------------------------------------------------------------------------------
1 | import { UALJs } from 'ual-plainjs-renderer';
2 | import { Wax } from '@eosdacio/ual-wax';
3 | import { isEmpty } from 'lodash';
4 | import { Anchor } from 'ual-anchor';
5 |
6 | import {storeAppDispatch} from './GlobalState/Store';
7 | import { setPlayerBalance, setPlayerData, setPlayerLogout } from './GlobalState/UserReducer';
8 |
9 | /**
10 | * Class to manage user data; it will be saved on Login and deleted on Logout
11 | */
12 | export class User {
13 |
14 | appName = 'ual_template';
15 |
16 | /**
17 | * WAX Mainnet configuration
18 | */
19 | myChain = {
20 | chainId: '1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4',
21 | rpcEndpoints: [{
22 | protocol: 'https',
23 | host: 'apiwax.3dkrender.com',
24 | port: ''
25 | }]
26 | };
27 |
28 | /**
29 | * WAX Testnet configuration
30 | */
31 | // myChain = {
32 | // chainId: 'f16b1833c747c43682f4386fca9cbb327929334a762755ebec17f6f23c9b8a12',
33 | // rpcEndpoints: [{
34 | // protocol: 'https',
35 | // host: 'testnet-wax.3dkrender.com',
36 | // port: ''
37 | // }]
38 | // };
39 |
40 | ual;
41 |
42 | // User session data
43 | authName = undefined;
44 | serviceLoginName = undefined;
45 | // Shows petition signing and current balance obtaining methods
46 | session = undefined;
47 |
48 | // Current balance
49 | userBalance = 0;
50 |
51 | // Callback to refer to successful login
52 | callbackServerUserData = undefined;
53 |
54 | getName() {
55 | return this.authName;
56 | }
57 |
58 | login(callback) {
59 | const ualButton = document.querySelector(".ual-button-gen");
60 | ualButton.click();
61 |
62 | this.callbackServerUserData = callback;
63 | }
64 |
65 | isLogged() {
66 | const auth = !isEmpty(this.authName) && !isEmpty(this.session);
67 | return auth;
68 | }
69 |
70 | logout() {
71 | console.log("Logout");
72 | this.authName = undefined;
73 | this.session = undefined;
74 |
75 | this.ual.logoutUser();
76 |
77 | storeAppDispatch(setPlayerLogout());
78 |
79 | if(this.callbackServerUserData !== undefined) {
80 | this.callbackServerUserData();
81 | }
82 | }
83 |
84 | // UAL API call response
85 | async ualCallback(userObject) {
86 |
87 | this.session = userObject[0];
88 | this.serviceLoginName = this.session.constructor.name;
89 | this.authName = this.session.accountName;
90 |
91 | storeAppDispatch(setPlayerData({
92 | name: this.authName,
93 | isLogged: this.isLogged(),
94 | balance: (this.balance !== undefined) ? this.balance : 0
95 | }));
96 |
97 | this.getBalance();
98 |
99 | if(this.callbackServerUserData !== undefined) {
100 | this.callbackServerUserData();
101 | }
102 | }
103 |
104 | getBalance() {
105 | const balance = this.session.rpc.get_account(this.authName);
106 | balance.then((balance) => {
107 | this.balance = balance.core_liquid_balance;
108 | storeAppDispatch(setPlayerBalance((this.balance !== undefined) ? this.balance : 0));
109 | });
110 | return balance;
111 | }
112 |
113 | // UserService init called to prepare UAL Login.
114 | init() {
115 | // Binding:
116 | this.ualCallback = this.ualCallback.bind(this);
117 |
118 | const wax = new Wax([this.myChain], { appName: this.appName });
119 |
120 | const anchor = new Anchor([this.myChain], { appName: this.appName });
121 |
122 | const divUal = document.createElement('div')
123 | divUal.setAttribute('id', 'ual-login');
124 | document.body.appendChild(divUal);
125 |
126 | const divLoginRoot = document.getElementById('ual-login');
127 | this.ual = new UALJs(this.ualCallback, [this.myChain], this.appName, [wax, anchor], { containerElement: divLoginRoot });
128 | this.ual.init()
129 | }
130 |
131 | static new() {
132 | if (!User.instance) {
133 | User.instance = new User();
134 | }
135 |
136 | return User.instance;
137 | }
138 | }
139 |
140 | export const UserService = User.new();
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Template for WAX (UAL)
2 |
3 | This template was created to make the use of WAX with React easier.
4 |
5 | Please remember to install all needed dependencies beforehand:
6 | ```
7 | > npm i
8 | ```
9 | You can start the project by:
10 | ```
11 | > npm run start
12 | ```
13 |
14 | ## Dependencies used
15 | - Dependencies for WAX
16 | - @eosdacio/ual-wax
17 | - ual-anchor
18 | - ual-plainjs-renderer
19 | - anchor-link
20 | - Dependencies for React routes
21 | - react-router-dom
22 |
23 | - Global state dependencies
24 | - redux
25 | - react-redux
26 | - @reduxjs/toolkit
27 |
28 | - Style and design dependencies
29 | - bootstrap
30 | - glamor
31 |
32 | - Help dependencies
33 | - lodash
34 |
35 | ## Key files
36 |
37 | - **UserService.js**: A file needed to manage the user's login, UAL configuration and different login methods. It also has a certain mode that lets us know the user's status (**isLogged()**) with the answers **true** or **false** if the user has previously logged in.
38 |
39 | **Remember** to comment and uncomment as needed, depending on wether you will be working with mainnet or testnet.
40 |
41 | When the user logs in, this data will be saved in the app's global state thanks to Redux and @reduxjs/toolkit (we will also save an **isLogged** in order to maintain a live actualization in React).
42 |
43 | - **GlobalState/**: In this folder we will keep our configuration and **store** from **redux**, so we can save and manage the user's data in a global state.
44 |
45 | - **App.js**: With this file we can manage the web's route system.
46 |
47 | - **index.js**: This file initiates **UserService** (UserService.init()) and also contains the for **store** from **redux**.
48 |
49 | - **pages**: In this folder we will save our web's pages. **Remember to configure new pages' routes in your App.js**.
50 |
51 | - **components**: In this folder we will keep our app's components. For this example we only have **Menu.jsx**, a component for our menu, which will help us redirect the user when they log in.
52 |
53 | ## File Menu.jsx
54 | The file **components/Menu.jsx** is a component from our app or web's menu, which has four tabs: Main, Home, Page2, Login/Logout.
55 |
56 | As we can see, we have two tabs which are disabled and we are not allowed to access them. These are: **Home** and **Page2**. To achieve this, we will simply check out if the user has logged in or not, thanks to **UserState.isLogged** (in the redux state), and we will show or lock them with any **CSS** style.
57 |
58 | As for the Login/Logout tab, we will show one or the other depending on the user's state. This is easily ckeched by opening the file, but you will probably see something like this:
59 | ```jsx
60 | {
61 | !UserState.isLogged &&
62 |
63 | }
64 | {
65 | UserState.isLogged &&
66 | Logout
67 | }
68 | ```
69 | ## Login system
70 |
71 | The system to start login is located in **components/Menu.jsx**. Once we click on the login button in the menu, this will trigger **handleLogin** and at the same time will activate the **UserService.login()** fuction. In this function it is possible to add an anonymous function as a callback, and the answer will be delivered once we log in. This callback will allow us to check if the user has logged in, if they have.
72 | If that is the case, we will be redirected to a page (**/home, in this case**). Otherwise, it will log out in order to clear data.
73 |
74 | ```jsx
75 | const handleLogin = () => {
76 | UserService.login(() => {
77 | if (UserService.isLogged()) {
78 | locationHistory.push('/home');
79 | } else {
80 | dispatch(setPlayerLogout());
81 | }
82 | });
83 | }
84 | ```
85 |
86 | ## React routes' protection
87 |
88 | Routes must be protected. For that, we will need to create a component called **ProtectedRouter.jsx**. We have created one for this example, and its function will be to ckeck the route we're in and to show us the user's state so we can know if they have logged in or not (**UserService.isLogged()**). If the user has not logged in, it will take the user from this route and redirect them to another one we have configured previously, which in this case leads to **/login**.
89 |
90 | In order to use this component, we will simply open our **App.js**, where we will substitute for our . You will see this if you open **App.js**:
91 |
92 | ```jsx
93 | function App() {
94 | return (
95 |