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 { useSelector } from 'react-redux';
3 | import { Link, Redirect } from 'react-router-dom';
4 | import LogoIcon from '../images/3DK_LOGO_ICON_1300.png';
5 | import EnterIcon from '../images/enter.png';
6 | import ExitIcon from '../images/exit.png';
7 | import { useHistory } from 'react-router-dom';
8 | import { UserService } from '../UserService';
9 | import { useDispatch } from 'react-redux';
10 | import { setPlayerLogout } from '../GlobalState/UserReducer';
11 |
12 | export const Menu = (props) => {
13 |
14 | const dispatch = useDispatch();
15 | const locationHistory = useHistory();
16 | const UserState = useSelector((store) => store.user);
17 |
18 | const handleLogin = () => {
19 | UserService.login(() => {
20 | if (UserService.isLogged()) {
21 | locationHistory.push('/home');
22 | } else {
23 | dispatch(setPlayerLogout());
24 | }
25 | });
26 | }
27 |
28 | const onHandleLogout = () => {
29 | UserService.logout();
30 | }
31 |
32 | return (
33 |
60 | );
61 | }
--------------------------------------------------------------------------------
/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 | 
6 |
7 | Please remember to install all needed dependencies beforehand:
8 | ```
9 | > npm i
10 | ```
11 | You can start the project by:
12 | ```
13 | > npm run start
14 | ```
15 |
16 | ## Dependencies used
17 | - Dependencies for WAX
18 | - @eosdacio/ual-wax
19 | - ual-anchor
20 | - ual-plainjs-renderer
21 | - anchor-link
22 | - Dependencies for React routes
23 | - react-router-dom
24 |
25 | - Global state dependencies
26 | - redux
27 | - react-redux
28 | - @reduxjs/toolkit
29 |
30 | - Style and design dependencies
31 | - bootstrap
32 | - glamor
33 |
34 | - Help dependencies
35 | - lodash
36 |
37 | ## Key files
38 |
39 | - **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.
40 |
41 | **Remember** to comment and uncomment as needed, depending on wether you will be working with mainnet or testnet.
42 |
43 | 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).
44 |
45 | - **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.
46 |
47 | - **App.js**: With this file we can manage the web's route system.
48 |
49 | - **index.js**: This file initiates **UserService** (UserService.init()) and also contains the for **store** from **redux**.
50 |
51 | - **pages**: In this folder we will save our web's pages. **Remember to configure new pages' routes in your App.js**.
52 |
53 | - **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.
54 |
55 | ## File Menu.jsx
56 | The file **components/Menu.jsx** is a component from our app or web's menu, which has four tabs: Main, Home, Page2, Login/Logout.
57 |
58 | 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.
59 |
60 | 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:
61 | ```jsx
62 | {
63 | !UserState.isLogged &&
64 |
65 | }
66 | {
67 | UserState.isLogged &&
68 | Logout
69 | }
70 | ```
71 | ## Login system
72 |
73 | 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.
74 | 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.
75 |
76 | ```jsx
77 | const handleLogin = () => {
78 | UserService.login(() => {
79 | if (UserService.isLogged()) {
80 | locationHistory.push('/home');
81 | } else {
82 | dispatch(setPlayerLogout());
83 | }
84 | });
85 | }
86 | ```
87 |
88 | ## React routes' protection
89 |
90 | 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**.
91 |
92 | 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**:
93 |
94 | ```jsx
95 | function App() {
96 | return (
97 |
108 | );
109 | }
110 | ```
111 |
112 | ## Send WAX
113 |
114 | You can see an example of how to send items from WAX in **pages/Page2.jsx**. The **UserService** saves the session and we simply have to access this session and sing any transaction. The code you will see in this example looks like this:
115 |
116 | ```js
117 | UserService.session.signTransaction(
118 | {
119 | actions: [{
120 | account: 'eosio.token',
121 | name: 'transfer',
122 | authorization: [{
123 | actor: UserService.authName,
124 | permission: 'active'
125 | }],
126 | data: {
127 | from: UserService.authName,
128 | to: '3dkrenderwax',
129 | quantity: '1.00000000 WAX',
130 | memo: 'This works!'
131 | }
132 | }]
133 | },
134 | {
135 | blocksBehind: 3,
136 | expireSeconds: 30
137 | }
138 |
139 | ).then((response) => {
140 | if(response.status === 'executed') {
141 | UserService.getBalance();
142 | }
143 | });
144 | ```
145 | ---
146 | 
147 |
148 | We hope that you find our tools useful and that you can keep developing your ideas with this awesome WAX technology.
149 |
150 | ---
151 |
152 |
153 |
154 | # [ES] - React Template for WAX (UAL)
155 |
156 | Template creado para facilitar el uso de WAX con React.
157 |
158 | 
159 |
160 | Recuerda que primero es necesario instalar las dependencias:
161 | ```
162 | > npm i
163 | ```
164 | Para iniciar el proyecto se puede hacer con:
165 | ```
166 | > npm run start
167 | ```
168 |
169 | ## Dependencias usadas
170 | - Dependencias para el uso de WAX
171 | - @eosdacio/ual-wax
172 | - ual-anchor
173 | - ual-plainjs-renderer
174 | - anchor-link
175 | - Dependencia de rutas de react
176 | - react-router-dom
177 |
178 | - Dependencias de estado global
179 | - redux
180 | - react-redux
181 | - @reduxjs/toolkit
182 |
183 | - Dependencias de diseño y estilos
184 | - bootstrap
185 | - glamor
186 |
187 | - Dependencias de ayuda
188 | - lodash
189 |
190 | ## Archivos importantes
191 |
192 | - **UserService.js**: Archivo necesario para gestionar el login del usuario y la configuración de UAL y distintos sistemas de login, además de que cuenta con un método que nos indica el estado del usuario (**isLogged()**) devolviendo un **true** o **false** si el usuario ya ha hecho login.
193 |
194 | **Recuerda** comentar o descomentar las líneas según te convenga para trabajar con mainnet o con testnet.
195 |
196 | Cuando el usuario haya hecho log-in, estos datos se guardarán en el estado global de la aplicación gracias a Redux y @reduxjs/toolkit (también guardamos un **isLogged** para tener una actualización en tiempo real en React).
197 |
198 | - Carpeta **GlobalState/**: En esta carpeta tenemos la configuración y el **store** de **redux** para poder guardar y gestionar los datos del usuario en un estado global.
199 |
200 | - Archivo **App.js**: En este archivo gestionamos el sistema de rutas de la página.
201 |
202 | - Archivo **index.js**: En este archivo iniciamos el **UserService** (UserService.init()) y también tenemos el del **store** de **redux**.
203 |
204 | - Carpeta **pages**: Dentro de esta carpeta guardaremos las páginas de nuestro sitio. **Recuerda configurar las rutas de las páginas nuevas en tu App.js**.
205 |
206 | - Carpeta **components**: Aquí se almacenarán los componentes de nuestra aplicación. Para este ejemplo solo tenemos **Menu.jsx**, que es un componente del menú y que nos ayuda a redireccionar al usuario cuando este haga log-in.
207 |
208 | ## Archivo Menu.jsx
209 | El archivo **components/Menu.jsx** es el componente del menú de nuestra aplicación/página y cuenta con cuatro pestañas: Main, Home, Page2 y Login/Logout.
210 |
211 | Si nos fijamos, veremos que tenemos dos pestañas deshabilitadas a las que no se nos permite el acceso: **Home** y **Page2**. Para lograr hacer esto, simplemente deberemos comprobar si el usuario ha hecho log-in o no, gracias a **UserState.isLogged** (en el estado de redux), y se mostrará u ocultará con algún estilo **CSS**.
212 |
213 | En cuanto a la pestaña Login/Logout, mostraremos una u otra dependiendo del estado del usuario. Esto se puede comprobar fácilmente al entrar en el archivo, pero lo que veremos será algo así:
214 | ```jsx
215 | {
216 | !UserState.isLogged &&
217 |
218 | }
219 | {
220 | UserState.isLogged &&
221 | Logout
222 | }
223 | ```
224 | ## Sistema de login
225 |
226 | El sistema de inicio de log-in está en el **components/Menu.jsx**. Una vez que se haga click sobre el botón de log-in en el menú, este llama a **handleLogin** y a la vez llama a la función de **UserService.login()**. Dentro de esta función se puede pasar una función anónima como callback, y cuando se haya hecho log-in, se recibirá una respuesta. Dentro de este callback comprobaremos si se ha hecho log-in o no.
227 | Si se hace log-in se redirigirá hacia una página (**en este caso, /home**). De lo contrario, se hará log-out del usuario para limpiar datos.
228 |
229 | ```jsx
230 | const handleLogin = () => {
231 | UserService.login(() => {
232 | if (UserService.isLogged()) {
233 | locationHistory.push('/home');
234 | } else {
235 | dispatch(setPlayerLogout());
236 | UserService.logout();
237 | }
238 | });
239 | }
240 | ```
241 |
242 | ## Protección de las rutas de React
243 |
244 | Las rutas deben protegerse, por lo que es necesario crear un componente llamado **ProtectedRouter.jsx**. Nosotros hemos creado uno para el ejemplo, el cual comprueba la ruta en la que estamos y obtiene el estado del usuario para saber si ha hecho log-in o no (**UserService.isLogged()**). Si no se ha hecho log-in, sacará al usuario de esa ruta y lo mandará hacia otra ruta que hemos preconfigurado, en este caso, hacia **/login**.
245 |
246 | Para usar el componente, simplemente iremos a nuestro **App.js** y ahí reemplazaremos el por nuestro . Si abrimos **App.js**, lo veremos fácilmente:
247 |
248 | ```jsx
249 | function App() {
250 | return (
251 |