BotFramework-WebChat v4: post activity to direct line from the UI to the bot - react-native

My WebChat code is based on the React minimizable-web-chat v4.
I want to send a message to the bot when user click the location button.
handleLocationButtonClick function is called and it sends latitude and longitude to the bot.
This is my code:
import React from 'react';
import { createStore, createStyleSet } from 'botframework-webchat';
import WebChat from './WebChat';
import './fabric-icons-inline.css';
import './MinimizableWebChat.css';
export default class extends React.Component{
constructor(props) {
super(props);
this.handleFetchToken = this.handleFetchToken.bind(this);
this.handleMaximizeButtonClick = this.handleMaximizeButtonClick.bind(this);
this.handleMinimizeButtonClick = this.handleMinimizeButtonClick.bind(this);
this.handleSwitchButtonClick = this.handleSwitchButtonClick.bind(this);
this.handleLocationButtonClick = this.handleLocationButtonClick.bind(this);
const store = createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
}
});
}
else if(action.type === 'DIRECT_LINE/INCOMING_ACTIVITY'){
if (action.payload.activity.name === 'locationRequest') {
this.setState(() => ({
locationRequested: true
}));
}
}
return next(action);
});
this.state = {
minimized: true,
newMessage: false,
locationRequested:false,
side: 'right',
store,
styleSet: createStyleSet({
backgroundColor: 'Transparent'
}),
token: 'token'
};
}
async handleFetchToken() {
if (!this.state.token) {
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
this.setState(() => ({ token }));
}
}
handleMaximizeButtonClick() {
this.setState(() => ({
minimized: false,
newMessage: false
}));
}
handleMinimizeButtonClick() {
this.setState(() => ({
minimized: true,
newMessage: false
}));
}
handleSwitchButtonClick() {
this.setState(({ side }) => ({
side: side === 'left' ? 'right' : 'left'
}));
}
handleLocationButtonClick(){
var x = document.getElementById("display");
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
this.setState(() => ({
locationRequested: false
}));
}
else
{
x.innerHTML = "Geolocation API is not supported by this browser.";
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude + "<br>Longitude: " + position.coords.longitude;
this.store.dispatch({
type: 'WEB_CHAT/SEND_MESSAGE',
payload: { text: 'latitude:'+position.coords.latitude+'longitude:'+position.coords.longitude }
});
}
}
render() {
const { state: {
minimized,
newMessage,
locationRequested,
side,
store,
styleSet,
token
} } = this;
return (
<div className="minimizable-web-chat">
{
minimized ?
<button
className="maximize"
onClick={ this.handleMaximizeButtonClick }
>
<span className={ token ? 'ms-Icon ms-Icon--MessageFill' : 'ms-Icon ms-Icon--Message' } />
{
newMessage &&
<span className="ms-Icon ms-Icon--CircleShapeSolid red-dot" />
}
</button>
:
<div
className={ side === 'left' ? 'chat-box left' : 'chat-box right' }
>
<header>
<div className="filler" />
<button
className="switch"
onClick={ this.handleSwitchButtonClick }
>
<span className="ms-Icon ms-Icon--Switch" />
</button>
<button
className="minimize"
onClick={ this.handleMinimizeButtonClick }
>
<span className="ms-Icon ms-Icon--ChromeMinimize" />
</button>
</header>
<WebChat
className="react-web-chat"
onFetchToken={ this.handleFetchToken }
store={ store }
styleSet={ styleSet }
token={ token }
/>
{
locationRequested ?
<div>
<p id="display"></p>
<button onClick={this.handleLocationButtonClick}>
Gélolocation
</button>
</div>
:
<div></div>
}
</div>
}
</div>
);
}
}
When I click the button, I have this error:
And in the console:
What is wrong ??

First, there have been some updates to the a.minimizable-web-chat sample that are worth looking over. Refer to the code, however, as the README.md file has not been fully updated to reflect the changes.
As for your question, try the following changes. When tested, it works successfully for me. Change the component to a function and define store via useMem0().
import React, { useCallback, useMemo, useState } from 'react';
const MinimizableWebChat = () => {
const store = useMemo(
() =>
createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: {
language: window.navigator.language
}
}
});
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
if (action.payload.activity.from.role === 'bot') {
setNewMessage(true);
}
}
return next(action);
}),
[]
);
[...]
return (
[...]
<WebChat
[...]
store={store}
);
}
export default MinimizableWebChat;
Given the changes implemented in this file, it likely will impact how the other files function with it. My recommendation would be to do a wholesale update to bring your project in line with the current sample. It's really just this file, WebChat.js, and possibly App.js. There are supporting CSS files and the like that can be downloaded, if you don't have them.
Hope of help!

Related

How can I password protect every page in NextJS and Supabase, using the Supabase default auth helpers and UI?

I'm trying to add user authentication to every page in my NextJS project (pages, not app.) This tutorial was very helpful (and is exactly what I want to do) - https://alexsidorenko.com/blog/next-js-protected-routes/ - but I'm having trouble integrating Supabase's default auth UI and capabilities into that model (https://supabase.com/docs/guides/auth/auth-helpers/nextjs).
My basic goal is to move authentication branching into _app.tsx, rather than on each page:
// _app.tsx
import { useEffect, useState } from "react";
import { createBrowserSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { SessionContextProvider, useUser, useSession, useSupabaseClient, Session } from '#supabase/auth-helpers-react'
import { Auth, ThemeSupa } from '#supabase/auth-ui-react'
import { AppProps } from 'next/app'
import { UserContext } from "#components/user"
function MyApp({Component, pageProps}: AppProps<{ initialSession: Session }>) {
const [supabase] = useState(() => createBrowserSupabaseClient())
const session = useSession()
const user = useUser()
console.log("session:" + session);
console.log("user:" + user);
useEffect(() => {
if (
pageProps.protected
) {
return <Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} theme="dark" />
}
}, [])
return (
<SessionContextProvider supabaseClient={supabase} session={session} initialSession={pageProps.initialSession}>
<Component {...pageProps} />
</SessionContextProvider>
)
}
export default MyApp
A page I want to protect (for example, the index page) looks like this:
// index.tsx
import Account from "#components/account";
const Home = () => {
return (
<div>
<Account session={session} />
</div>
)
}
export async function getStaticProps(context) {
return {
props: {
protected: true,
},
}
}
export default Home
And then the Account component that's included on the index page is the Supabase out of the box profile panel, although it could be any content:
// #components/account.tsx
import { useState, useEffect } from 'react'
import { useUser, useSupabaseClient, Session } from '#supabase/auth-helpers-react'
import { Database } from '#utils/database.types'
type Profiles = Database['public']['Tables']['profiles']['Row']
export default function Account({ session }: { session: Session }) {
const supabase = useSupabaseClient<Database>()
const user = useUser()
const [loading, setLoading] = useState(true)
const [username, setUsername] = useState<Profiles['username']>(null)
useEffect(() => {
getProfile()
}, [session])
async function getProfile() {
try {
setLoading(true)
if (!user) throw new Error('No user')
let { data, error, status } = await supabase
.from('profiles')
.select(`username`)
.eq('id', user.id)
.single()
if (error && status !== 406) {
throw error
}
if (data) {
setUsername(data.username)
}
} catch (error) {
alert('Error loading user data!')
console.log(error)
} finally {
setLoading(false)
}
}
async function updateProfile({
username,
}: {
username: Profiles['username']
}) {
try {
setLoading(true)
if (!user) throw new Error('No user')
const updates = {
id: user.id,
username,
updated_at: new Date().toISOString(),
}
let { error } = await supabase.from('profiles').upsert(updates)
if (error) throw error
alert('Profile updated!')
} catch (error) {
alert('Error updating the data!')
console.log(error)
} finally {
setLoading(false)
}
}
return (
<div>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="text" value={session.user.email} disabled />
</div>
<div>
<label htmlFor="username">Username</label>
<input id="username" type="text" value={username || ''} onChange={(e) => setUsername(e.target.value)} />
</div>
<div>
<button onClick={() => updateProfile({ username })} disabled={loading} >
{loading ? 'Loading ...' : 'Update'}
</button>
</div>
<div>
<button onClick={() => supabase.auth.signOut()}>
Sign Out
</button>
</div>
</div>
)
}
I think I have a fundamental misunderstanding of the relationship between protected routes and Supabase's use of session and user.
Any help would be very much appreciated.
I'd recommend using Next.js middleware for this: https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware
import { createMiddlewareSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
const res = NextResponse.next()
// Create authenticated Supabase Client.
const supabase = createMiddlewareSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
// Check auth condition
if (session?.user.email?.endsWith('#gmail.com')) {
// Authentication successful, forward request to protected route.
return res
}
// Auth condition not met, redirect to home page.
const redirectUrl = req.nextUrl.clone()
redirectUrl.pathname = '/'
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
return NextResponse.redirect(redirectUrl)
}
export const config = {
matcher: '/middleware-protected/:path*',
}

Compare Contract Settings Goerli on Etherscan

I recently took over a project from another developer who has been uncooperative in providing assistance. The project includes a contract deployed on the Rinkeby network and a decentralized exchange (DEX) developed for it. Users were able to test the DEX using our own platform, with liquidity added via the Uniswap interface. The contract includes the Uniswap V2Router contract to fetch liquidity and use it on our DEX. I have completed all necessary web3 setup, including changing the RPC port to 5 and adding the correct URL.
No error so far on my console.
SITE FOR REFERENCE: https://doxastaking.netlify.app/, It is deployed on GOERLI ETHER.
Recently, I migrated the project to the Goerli network. However, since the migration, I have not been able to purchase tokens using our DEX. Transactions go through, but the equivalent tokens are not assigned to my wallet. It is unclear to me whether the issue is with the contract or the web3 setup.
The web3 swap code:
import React, { Component } from "react";
import eth from "../../assets/eth.png";
import transfer from "../../assets/transfer.png";
import buyLoader from "../../assets/doxa-ico-loader.gif";
import { connect } from "react-redux";
import { connectWallet } from '../../redux/WalletAction';
import miniLogo from "../../assets/logo.png";
import "./scss/bs.css";
class Buydoxa extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '0',
doxaValue: 0
}
}
componentDidMount() {
const { web3Modal } = this.props.wallet
if (web3Modal.cachedProvider) {
this.props.connectWallet();
}
}
connectToWallet = async () => {
await this.props.connectWallet();
}
buyToken = async () => {
const queryParams = new URLSearchParams(window.location.search);
const id = queryParams.get('email');
let inputValue = parseFloat(this.state.inputValue);
if (inputValue >= 0.00001 && inputValue <= 10) {
const { web3, doxa, wallet, address } = this.props.wallet;
const value = this.state.inputValue.toString();
const buyValue = web3.utils.toWei(value, 'ether');
const tokenPrice = web3.utils.toWei('0.00001', 'ether');
const totalTokens = (web3.utils.toBN(buyValue).div(web3.utils.toBN(tokenPrice))).toString();
try {
this.setState({ loading: true })
const res = await wallet.methods.swapEthToDoxa(id).send({ from: address, value: buyValue });
this.setState({ loading: false })
} catch (err) {
this.setState({ loading: false })
if (err.message) {
alert(err.message)
} else {
alert("Something went wrong!")
}
}
} else {
alert("ETH should be between 0.00001 and 10");
return
}
}
updateInputValue = async (e) => {
let totalTokens;
if (e.target.value != '') {
totalTokens = parseFloat(e.target.value) / 0.00001;
}
this.setState({
inputValue: e.target.value,
doxaValue: totalTokens
});
}
render() {
return (
<div className="bs-container h-100">
<div className="bs-main">
<h2>swap your crypto</h2>
<div className="bs-input">
<div className="inpt-cont center mb-3">
<label>Enter ETH</label>
<input type="number" value={this.state.inputValue} onChange={e => this.updateInputValue(e)} />
</div>
<div className="img-cont">
<img src={eth} alt="eth" />
<p>ETH</p>
</div>
</div>
{/* image */}
<img src={transfer} className="transfer" alt="transfer" />
<div className="bs-input">
<div className="inpt-cont center">
<p>{this.state.doxaValue}</p>
</div>
<div className="img-cont">
<img src={miniLogo} alt="miniLogo" />
<p>DOXAZO</p>
</div>
</div>
{/* btn */}
<button className="bs-btn" disabled={this.state.loading} onClick={() => this.props.wallet.connected ? this.buyToken() : this.connectToWallet()}>{this.props.wallet.connected ? this.state.loading ?
<span>Processing <img src={buyLoader}></img></span>
: 'Buy' : 'PROCEED TO SWAP'}</button>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
wallet: state.walletConnect
});
export default connect(mapStateToProps, { connectWallet })(Buydoxa);
My Wallet Action.js Codes:
// constants
import Web3 from "web3";
import Web3Modal from "web3modal";
import WalletConnectProvider from "#walletconnect/web3-provider";
import contract from "../contracts/staking.json";
import tokenContract from "../contracts/token.json";
import walletContract from "../contracts/wallet.json";
import store from './store';
const connectRequest = () => {
return {
type: "CONNECTION_REQUEST",
};
};
export const disconnectRequest = () => {
return {
type: "DISCONNECT"
};
}
export const connectSuccess = (payload) => {
return {
type: "CONNECTION_SUCCESS",
payload: payload,
};
};
const connectFailed = (payload) => {
return {
type: "CONNECTION_FAILED",
payload: payload,
};
};
export const updateAccountRequest = (payload) => {
return {
type: "UPDATE_ADDRESS",
payload: payload,
};
};
const getProviderOptions = () => {
const providerOptions = {
walletconnect: {
package: WalletConnectProvider,
options: {
rpc: {
5: "https://goerli.infura.io/v3/ea95b0776037479abf7a62fc14b55188",
1: "https://mainnet.infura.io/v3/ea95b0776037479abf7a62fc14b55188"
}
}
},
}
return providerOptions;
}
export const connectWallet = () => {
return async(dispatch) => {
dispatch(connectRequest());
try {
const web3Modal = new Web3Modal({
cacheProvider: true,
providerOptions: getProviderOptions() // required
});
const provider = await web3Modal.connect();
const stakingContractAddress = process.env.REACT_APP_DOXACONTRACT_ADDRESS;
const internalWalletAddress = process.env.REACT_APP_WALLET_ADDRESS;
const TokencontractAddress = process.env.REACT_APP_TOKEN_ADDRESS;
await subscribeProvider(provider, dispatch);
const web3 = new Web3(provider);
web3.eth.extend({
methods: [
{
name: "chainId",
call: "eth_chainId",
outputFormatter: web3.utils.hexToNumber
}
]
});
const accounts = await web3.eth.getAccounts();
const address = accounts[0];
const instance = new web3.eth.Contract(
contract,
stakingContractAddress
);
const tokenInstance = new web3.eth.Contract(
tokenContract,
TokencontractAddress
)
const walletInstance = new web3.eth.Contract(
walletContract,
internalWalletAddress
)
if(window.ethereum && window.ethereum.networkVersion !== '5') {
await addNetwork(5);
}
dispatch(
connectSuccess({
address,
web3,
staking: instance,
token: tokenInstance,
wallet: walletInstance,
provider,
connected: true,
web3Modal
})
);
} catch (e) {
dispatch(connectFailed(e));
}
}
}
export const disconnect = () => {
return async(dispatch)=> {
const { web3Modal } = store.getState().walletConnect;
console.log(web3Modal);
web3Modal.clearCachedProvider();
dispatch(disconnectRequest());
}
}
const subscribeProvider = async(provider) => {
if (!provider.on) {
return;
}
provider.on('connect', async(id) => {
console.log(id);
});
provider.on("networkChanged", async (networkId) => {
if(networkId !== '5') {
console.log(networkId);
await store.dispatch(connectFailed('Please switch to Ethereum mainnet'));
addNetwork(5);
} else {
store.dispatch(connectWallet());
}
});
}
export async function addNetwork(id) {
let networkData;
switch (id) {
//bsctestnet
case 5:
networkData = [
{
chainId: "0x4",
},
];
break;
//bscmainet
case 1:
networkData = [
{
chainId: "0x1",
},
];
break;
default:
break;
}
return window.ethereum.request({
method: "wallet_switchEthereumChain",
params: networkData,
});
}
(() => {
if(window.ethereum) {
window.ethereum.on('networkChanged', async function(networkId){
console.log('network change', networkId);
if(networkId !== '5') {
console.log(networkId);
await store.dispatch(connectFailed('Please switch to Binance mainnet'));
addNetwork(5);
} else {
store.dispatch(connectWallet());
}
});
}
})();
The following are the contract and wallet addresses for my Goerli deployments:
Token Address: https://goerli.etherscan.io/address/0x0f0283E1aC1f465cE2076a1F57EA0f1BAb4DDC21
Wallet Address Proxy: https://goerli.etherscan.io/address/0xdF6046711651AEC0d686F12Ed0039d5aC45517f3
The following are the contract and wallet addresses for the previous developer's Rinkeby deployments:
Token deployed by other developer on Rinkeby network: https://rinkeby.etherscan.io/address/0xD99b4BB049a6Dd490901CDfa33F15C4fAc097EF0
The wallet proxy deployed on Rinkeby: https://rinkeby.etherscan.io/address/0x5309E16fc58Dc900a08d92BE6559758D692f39Bb

how to write all list items from json one after another in react?

i want to show names on screen which i fetch from jsonplaceholder.com . all names are appearing at once but i want that names come one after another like 2sec.
i tried this ,but did not work.
import React, { Component } from 'react'
export default class UserList extends Component {
state={users:[],loading:true}
componentDidMount() {
setTimeout(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(result => result.json())
.then(jsonData =>this.setState({users:jsonData,loading:false}))
}, 4000);
}
render() {
<h1>USERS</h1>
{this.state.loading && <h1>Loading..........</h1> }
{this.state.users.length>0 && this.state.users.map(user=>
setTimeout(() => {
<div className="" key={user.id}>{user.name}</div>
}, 3000)
)}
</div>
)
}
}
You can try this, you componentDidUpdate to update the index
import React, { Component } from "react";
export default class UserList extends Component {
state = { users: [], loading: true, indexes: [] };
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then((result) => result.json())
.then((jsonData) => this.setState({ users: jsonData, loading: false }));
}
componentDidUpdate() {
const { users, indexes } = this.state;
if (indexes.length !== users.length) {
setTimeout(() => {
this.setState((prevState) => ({
...prevState,
indexes: [...prevState.indexes, prevState.indexes.length]
}));
}, 2000);
}
}
render() {
const { users, indexes, loading } = this.state;
return (
<div>
<h1>USERS</h1>
{loading && <h1>Loading..........</h1>}
{users.length > 0 &&
indexes.length > 0 &&
indexes.map((index) => <div>{users[index].name}</div>)}
</div>
);
}
}
But i'll strongly recommend you use hooks for such updates.

React-typescript: TypeError: Cannot read property 'id' of undefined

I am using React-typescript for my. I have one login page where there are two input fields. one is email and password. i have created one state and inside the state there email password and loading. Default input fields works fine, I have target my input fields with id. I have decided, I will create global input fields and custom props. When I used global input fields then it does not target the id and throws me this error: React-typescript: TypeError: Cannot read property 'id' of undefined. I am pretty sure in my typescript onChange throws me that error. But Don't know to how to fix it. Here is my code in Codesandbox.
This is my Login form
import React, { ReactElement, useState } from "react";
import { TextInput } from "./input";
interface Props extends PageProps {}
export default function SignIn({}: Props): ReactElement {
const [state, setState] = useState({
email: ``,
password: ``,
loading: false
});
const { loading, email, password } = state;
const handleSignIn = (e: React.ChangeEvent<HTMLInputElement>) => {
setState({
...state,
[e.target.id]: e.target.value //This is id which throws me error
});
};
const onSubmit = async (e) => {
e.preventDefault();
console.log(state);
setState({
loading: true,
...state
});
const response = await fetch(
"https://run.mocky.io/v3/beec46b8-8536-4cb1-9304-48e96d341461",
{
method: `POST`,
headers: {
Accept: `application/json`,
"Content-Type": `application/json`
},
body: { state }
}
);
console.log(response);
if (response.ok) {
setState({ ...state, loading: false });
alert(`login succs`);
} else {
alert(`login failed`);
}
};
return (
<div>
<TextInput
type="text"
value={email}
onChange={handleSignIn}
id="email"
required
/>
<TextInput
type="password"
value={password}
onChange={handleSignIn}
id="password"
required
/>
<button type="submit" name="action" onClick={onSubmit} disabled={loading}>
{" "}
{loading ? `loading...` : `save`}
</button>
</div>
);
}
this is my global input
import React, { ReactElement } from "react";
import styled from "styled-components";
interface Props {
value: string;
onChange: (e: string) => void;
error?: string;
id?: string;
}
const Input = styled.input``;
// eslint-disable-next-line func-style
export function TextInput({ value, onChange, error, id }: Props): ReactElement {
return (
<div>
<Input
value={value}
onChange={(e) => onChange(e.target.value)}
error={error}
id={id}
/>
</div>
);
}
I found my solution: I have created two handle changes functions, one for email and one for password..
`const handleEmail = (email: string) => {
setState({
...state,
email
});
};
const handlePassword = (password: string) => {
setState({
...state,
password
});
};`

ERROR in conditions to show a loading bar

I hope they are fine, my problem is that it does not take my conditions that when my data is not loaded in this case coupons, it does not show and only shows the bar loading, what I achieve is what is in the gif below
So in my interface State I have this property loadingCoupons: boolean,
In my state I have the property created above with value true... loadingCoupons: true
so in my updateCoupons method I put it false so that when loading the loading bar is removed
updateCoupons = async () => {
const coupons = await this.props.coupons.get(this.state.page, this.state.limit)
const couponsUsed = await this.props.coupons.getUsed(this.state.page, this.state.limit)
const couponsExpired = await this.props.coupons.getExpired(this.state.page, this.state.limit)
this.setState({
coupons,
couponsUsed,
couponsExpired,
loading: false,
loadingCoupons: false
})
}
In my render is the property loadingCoupons
So the code where I execute this, that everything is done is this, why my conditions are not met? What you should do is that when changing the tab, load and when you have the data loaded, show it, currently it always shows the loading bar
type IPathParams = {}
type IProps = RouteComponentProps<IPathParams> & {
coupons: Coupon
providerModel: ProviderModel
cartModel: CartModel
user: User
}
interface State {
msg: any
coupons: any
couponsUsed: any
couponsExpired: any
limit: any
loading: boolean
page: any
views: any
valueInput:string
spinner:boolean
couponsActvive:boolean
alert:boolean
couponValidate:any
message:string
alertDelete:boolean
messageDelete:string
segmentCoupons:any
loadingCoupons: boolean
}
class Coupons extends React.PureComponent<IProps, State> {
state: State = {
msg: { msg: null, active: false },
coupons: [],
couponsUsed: [],
couponsExpired: [],
limit: 10,
loading: true,
page: 0,
views: {
valid: { name: 0, active: true },
used: { name: 1, active: false },
expired: { name: 2, active: false }
},
valueInput:'',
spinner:false,
couponsActvive:false,
alert:false,
couponValidate:false,
message:'',
alertDelete:false,
messageDelete:'',
segmentCoupons:'',
loadingCoupons: true
}
async componentDidMount() {
this.updateCoupons()
}
updateCoupons = async () => {
const coupons = await this.props.coupons.get(this.state.page,
this.state.limit)
const couponsUsed = await this.props.coupons.getUsed(this.state.page,
this.state.limit)
const couponsExpired = await
this.props.coupons.getExpired(this.state.page,
this.state.limit)
this.setState({
coupons,
couponsUsed,
couponsExpired,
loading: false,
loadingCoupons: false
})
}
goToCart = () => {
// this.props.history.replace('/cart')
window.location.href = "/cart";
}
goToHome = () => {
this.props.history.replace('/home')
}
groupedCoupons = (coupons: any) => {
return groupBy(coupons, (coupon: any) => {
return coupon._id
})
}
activateCoupon = async (id: string) => {
const result = await this.props.coupons.activate(id)
const msg = !result.length && Object(result)
if (!result.length && msg) {
msg.active = true
msg.msg = Object(result).description
if (Object(result).code) {
msg.header = "Felicitaciones!!!"
} else {
msg.header = "Upps"
}
this.setState({
msg
})
} else if (result.length) {
msg.msg = 'Activación satisfactoria.'
msg.active = true
this.setState({
msg
})
}
const coupons = await this.props.coupons.get(this.state.page,
this.state.limit)
this.setState({
coupons,
loading: false,
loadingCoupons: false
})
}
renderCouponList(key: any, coupons: any) {
const { views } = this.state
return (
<div key={key} className="list-orders">
<div className="list">
{coupons.map((coupon: any) => {
const { _id, amount, minimum_amount, date_expires, discount_type,
code, image } = coupon
const expire = moment(new Date(date_expires)).format('DD/MM/YYYY')
return (
<Fragment key={_id}>
<div className="coupon">
<IonAvatar className="no-radius">
<img className="img_coupon" src={(image &&
`${process.env.REACT_APP_BFF_IMAGE}coupons/${image.url}`)} />
</IonAvatar>
<div className="provider">
<div className="code">CÓDIGO: #{code}</div>
<div className="expire">VENCE: {expire}</div>
{discount_type === 'porcent' ? (
<div className="amount"><strong>Descuento {amount}%
</strong></div>
) : discount_type === 'amount' && (
<div className="amount"><strong>{asClp(amount)} de
regalo</strong></div>
)}
<div className="minimum_amount">COMPRAS SOBRE
{asClp(minimum_amount)}</div>
</div>
{views.valid.active && (
<div className="button-wrapper">
<button onClick={() =>
this.activateCoupon(_id)}>Activar</button>
</div>
)}
{coupon.couponType === "referal" &&
<div className="button-garbage">
<IonIcon
className="icon"
src={Garbage}
onClick={() => this.deleteCouponReferal(coupon.code)}
/>
</div>
}
</div>
</Fragment>
)
})}
</div>
</div>
)
}
deleteCouponReferal = async (code:any) =>{
const user: any = (await this.props.user.getUser()) || {}
const result = await this.props.coupons.removeCouponReferal(code, user.id)
if(result.status===200){
this.setState({
alertDelete:true,
messageDelete:result.respuesta
})
setTimeout(() => {
window.location.reload()
}, 2000);
}
}
onChangeInput = async (value: any) => {
const val = Number(value)
const { views } = this.state
switch (val) {
case 0:
views.valid.active = true
views.used.active = false
views.expired.active = false
this.setState({
views, segmentCoupons:val
})
await this.updateCoupons()
break;
case 1:
views.valid.active = false
views.used.active = true
views.expired.active = false
this.setState({
views, segmentCoupons:val
})
await this.updateCoupons()
break;
case 2:
views.valid.active = false
views.used.active = false
views.expired.active = true
this.setState({
views, segmentCoupons:val
})
await this.updateCoupons()
break;
default:
break;
}
}
onChangeValidate = (event:any) =>{
this.setState({
valueInput:event
})
}
buttonValidate = async () =>{
const { valueInput } = this.state
const user: any = (await this.props.user.getUser()) || {}
this.setState({
spinner:true,
})
const result = await this.props.coupons.countReferal(valueInput, user.id)
if(result.status===400){
this.setState({
couponValidate:false,
message:result.respuesta
})
}else if(result.status===200){
this.setState({
couponValidate:false,
message:result.respuesta
})
}else if(result.status===404){
this.setState({
couponValidate:true,
message:result.respuesta
})
}else if(result.status===409){
this.setState({
couponValidate:'409',
message:result.respuesta
})
}
this.setState({
valueInput:'',
spinner:Object.keys(result).length > 0 ? false :true,
couponsActvive: Object.keys(result).length > 0 ? true :false,
alert:true,
})
}
render() {
const { history, cartModel } = this.props
const { coupons, couponsExpired, couponsUsed, loading, views,
msg,valueInput,
spinner,alert,
couponValidate, message, alertDelete, messageDelete, segmentCoupons,
loadingCoupons } = this.state
const length = coupons.length
const lengthExpired = couponsExpired.length
const lengthUsed = couponsUsed.length
const grouped = this.groupedCoupons(coupons)
const groupedExpired = this.groupedCoupons(couponsExpired)
const groupedUsed = this.groupedCoupons(couponsUsed)
const productCount = cartModel.getCart().length
return (
<IonPage className="orders-page-cupons">
<IonHeader>
<ToolBar
title="Cupones"
secondaryButtons={[{ type: 'back', onClick: history.goBack }]}
tertiaryButtons={[{ key: 'cart', onClick: this.goToCart, icon:
cartToolbarIcon, badge: productCount }]}
primaryButtons={[{ key: 'home', onClick: this.goToHome, icon:
homeIcon }]}
/>
</IonHeader>
{/* Without items */}
<IonContent>
{msg.active && (
<IonSlides pager={false}>
<IonSlide>
<IonAlert
isOpen={msg.active}
onDidDismiss={() => this.setState({
msg: { active: false, msg: null }
})}
header={msg.header}
message={msg.msg}
buttons={
[{
text: 'Cerrar',
handler: () => this.setState({
msg: { active: false, msg: null }
}),
}]
}
/>
</IonSlide>
</IonSlides>
)}
{alert && (
<IonAlert
isOpen={true}
header={!couponValidate ? '¡Felicitaciones!' : couponValidate ===
'409' ? '' :'Código Incorrecto'}
message={couponValidate ?
message :
message
}
buttons={
[{
text: 'Cerrar',
handler: () => this.setState({
alert:false
}),
}]
}
/>
) }
{loading && <IonProgressBar type="indeterminate"></IonProgressBar>}
{!loading && (
<IonContent>
<IonSegment onIonChange={e => this.onChangeInput(e.detail.value ||
'')}
value={views.valid.active ? views.valid.name :
views.used.active ? views.used.name :
views.expired.active && views.expired.name}>
<IonSegmentButton value={views.valid.name}>
<IonLabel>Disponibles</IonLabel>
</IonSegmentButton>
<IonSegmentButton value={views.used.name}>
<IonLabel>Activos</IonLabel>
</IonSegmentButton>
<IonSegmentButton value={views.expired.name}>
<IonLabel>Canjeados</IonLabel>
</IonSegmentButton>
</IonSegment>
{loadingCoupons && <IonProgressBar type="indeterminate">
</IonProgressBar>}
{views.valid.active && <div className="new-coupons">
<div className="cotainer-coupons-referal">
<div className="title-new-coupons">
Nuevo Cupón
</div>
<div>
<div className="container-input">
<IonInput
value={valueInput}
maxlength={13}
onIonChange={e => this.onChangeValidate(e.detail.value)}>
</IonInput>
</div>
<div className="container-btn-validate" >
<IonButton
onClick={this.buttonValidate}
disabled={valueInput ? false: true}
>Validar
{spinner && <IonSpinner name="crescent" />}
</IonButton>
</div>
</div>
</div>
</div>}
{length > 0 && segmentCoupons === 0 && !loadingCoupons? (
<div className="wrapper-orders">
{Object.keys(grouped).map((key: any) => {
return this.renderCouponList(key, grouped[key])
})}
</div>
) : views.valid.active && (
<div className="without-products">
<IonImg src={withoutCouponsIcon} />
<div className="message">Sin cupones.</div>
</div>
)}
{lengthExpired > 0 && segmentCoupons === 2 && !loadingCoupons? (
<div className="wrapper-orders">
{Object.keys(groupedExpired).map((key: any) => {
return this.renderCouponList(key, groupedExpired[key])
})}
</div>
) : views.expired.active && (
<div className="without-products">
<IonImg src={withoutCouponsIcon} />
<div className="message">Sin cupones.</div>
</div>
)}
{(lengthUsed > 0 && segmentCoupons === 1 && !loadingCoupons) ? (
<div className="wrapper-orders">
{Object.keys(groupedUsed).map((key: any) => {
return this.renderCouponList(key, groupedUsed[key])
})}
</div>
) : views.used.active && (
<div className="without-products">
<IonImg src={withoutCouponsIcon} />
<div className="message">Sin cupones.</div>
</div>
)}
</IonContent>
)}
{alertDelete && (
<IonAlert
isOpen={true}
message={messageDelete}
buttons={
[{
text: 'Cerrar',
handler: () => this.setState({
alert:false
}),
}]
}
/>
) }
</IonContent>
</IonPage>
)
}
}
export default withRouter(withIonLifeCycle(Coupons))
There are a lot of async functions without error handling in your code, otherwise it looks good.
Therefore I'd guess that there is probably an error thrown somewhere before setState is called which is lost due to the nature of async functions. Async functions will always return a Promise which is rejected with the error in case an unhandled error is thrown.
Therefore you should have at least error handling (in case of async/await usually a try ... catch block) for every react lifecycle function, otherwise the error might get lost. The async function will just return a promise which is rejected with the error and react wont care about the return value. Therefore you wont notice the error.
You can find more on async lifecycle functions here: https://www.valentinog.com/blog/await-react/