Building my first React.js app and I can't seem to get the app to redirect.
I am using the Twilio Voice TwiML (here) inside my React app. I have the frontend and server.
I can record what is said then transcribe it. Then redirect with an action: to a URL.
Below is my call.js Twilio function (server). The /Omg redirect isn't working.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
const recipient = event.recipient;
twiml.record({
// transcribeCallback: '/transcription'
action: '/Omg'
});
twiml.hangup();
return callback(null, twiml);
}
Below is my App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Omg from './Omg';
import './App.css';
const { Device } = require('twilio-client');
class App extends Component {
constructor(props) {
super(props)
this.state={
identity: '',
status: '',
ready: false
}
this.onChangeUpdateState = this.onChangeUpdateState.bind(this);
this.setup = this.setup.bind(this);
this.connect = this.connect.bind(this);
this.disconnect = this.disconnect.bind(this);
}
componentDidMount() {
const device = new Device();
this.setState({
device: device
});
device.on('incoming', connection => {
// immediately accepts incoming connection
connection.accept();
this.setState({
status: connection.status()
});
});
device.on('ready', device => {
this.setState({
status: "device ready",
ready: true
});
});
device.on('connect', connection => {
this.setState({
status: connection.status()
});
});
device.on('disconnect', connection => {
this.setState({
status: connection.status()
});
});
}
// This method sets the identity of the Twilio Device
// that your device is going to connect to. This
// example uses hardcoded values so that only devices
// with the identities friend1 and friend2 can connect.
// In a production application, this would have to be
// handled in a much different way. Most likely, the
// app would have users who are authenticated with a
// username and password. Their unique username would
// serve as their device’s identity and they would only
// be able to connect to device’s owned by users in their
// friend list.
connect() {
const recipient = this.state.identity === 'friend1' ? 'friend2' : 'friend1';
this.state.device.connect({recipient: recipient});
}
disconnect() {
this.state.device.disconnectAll();
}
setup(event) {
// prevents form submission and page refresh
event.preventDefault();
fetch(`https://blah-service-2000014-dev.twil.io/token?identity=${this.state.identity}`)
.then(response => response.json())
.then(data => {
this.state.device.setup(data.accessToken);
this.state.device.audio.incoming(false);
this.state.device.audio.outgoing(false);
this.state.device.audio.disconnect(false);
})
.catch(err => console.log(err))
}
onChangeUpdateState(event) {
this.setState({
identity: event.target.value
});
}
render() {
return (
<Router>
<div className="App">
{
this.state.ready
? <button className="noselect"
onMouseDown={this.connect}
onMouseUp={this.disconnect}>
Press 2 Talk
</button>
: <div>
<p>Enter your name to begin.</p>
<form onSubmit={this.setup}>
<input
value={this.state.identity}
onChange={this.onChangeUpdateState}
type="text"
placeholder="What's your name?"></input>
<input type="submit" value="Begin Session"></input>
</form>
</div>
}
<p>{ this.state.status }</p>
</div>
<Switch>
<Route path='/Omg' component={Omg} />
</Switch>
</Router>
);
}
}
export default App;
At this point, I am not sure if its a React rookie error or if its something I am doing wrong with Twilio?
React is a client-side application, not an HTTP server. It can't accept a POST request because no request will be made to it in the first place.
You need to write actual server side code to handle this the POST request and then redirect to a URL that serves up your React application to the browser.
Related
I am using expo React Native for developing my app, Redux for state management, ExpressJS for backend, MongoDB as database.
I am trying to fetch logged-in user data, JWT tokens are not saved in cookies. I don't know if cookies work with react native or not? I have tested the API with postman, API is working and also JWT Tokens are saved in cookies when I test it with postman or thunder-client VS Code extension. Here is the code:
Backend:
exports.getUserDetails = catchAsyncErrors(async (req, res, next) => {
const user = await User.findById(req.user.id);
if (!user) {
return next(new ErrorHandler("User not found", 404));
}
res.status(200).json({
success: true,
user,
});
});
Redux State:
export const LoadUser = () => async (dispatch) => {
try {
dispatch({ type: "LOAD_USER_REQUEST" });
const { data } = await axios.get(`${API_URL}/api/auth/me`);
dispatch({
type: "LOAD_USER_SUCCESS",
payload: data.user, //Not loading data.
});
} catch (error) {
dispatch({
type: "LOAD_USER_FAIL",
payload: error.response.data.message,
});
}
};
Frontend App.js file:
import { Provider as RNP_Provider } from "react-native-paper";
import { Provider } from "react-redux";
import Index from "./Index";
import { LoadUser } from "./actions/authAction";
import store from './store'
import { useEffect } from "react";
export default function App() {
useEffect(() => {
store.dispatch(LoadUser());
}, [])
return (
<Provider store={store}>
<RNP_Provider>
<Index />
</RNP_Provider>
</Provider>
);
}
There is no such thing as cookies in mobile apps created in ReactNative.
What you may use are AsyncStorages for Android/iOS and EncryptedStorage, search for those in npm and read the documentation, I guarantee you'll find your answers there!
You can also use some kind of context if you want your tokens to be saved for one session only, stores are suggested to use for values that you want to save for a longer time.
EncryptedStorage saves values even after app unninstall, so be careful with that one :)
I've searched on here already but it seems all the answers are very outdated or they are questions that haven't been answered.
I've got an expo app SDK 43 and I'm using their auth library to authorize a reddit login. I've followed the example here https://docs.expo.dev/guides/authentication/#reddit to produce this code
import React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native-paper';
import { useAppDispatch } from '../../common/hooks/redux';
import { setAuthCode } from '../../common/state/authSlice';
WebBrowser.maybeCompleteAuthSession();
// Endpoint
const discovery = {
authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
};
const LoginScreen = () => {
const dispatch = useAppDispatch();
const [request, response, promptAsync] = useAuthRequest(
{
responseType: ResponseType.Token,
clientId: 'MY_CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
scheme: undefined,
}),
},
discovery
);
React.useEffect(() => {
console.log(`response is ${response}`);
if (response?.type === 'success') {
const { access_token } = response.params;
dispatch(setAuthCode(access_token));
console.log(access_token);
} else {
console.log(response);
}
}, [response]);
return (
<Button
disabled={!request}
onPress={() => {
promptAsync();
}}
>
Login
</Button>
);
};
export default LoginScreen;
But despite the fact that the login button correctly takes me to the login screen, I successfully log in and allow the app (and if I go onto the web separately I can see in my account that the app is there under the authorized apps.)
At this point on my device one of two things happens: 1. something causes the app to disconnect from metro and it hangs on a loading wheel belonging to the greater expo stuff, or 2. It successfully gets back to the app but it redownloads the bundle and the response is null.
What is screwing up here?
I am building a react app that uses a simple login feature. I am only using google sign in, and am calling the signInWithPopop function to handle that. I have created a separate class to handle all the auth related code. On the navbar of my website I have a login button if the user is not signed in which switches to a profile button when the user has signed in.
This is how I am currently checking if the user is signed in (not working):
console.log(authHandler.getUser());
const[loginState, setLogin] = useState(authHandler.getUser()? true : false);
return(
<div className="navbar">
<div className="nav-options">
<NavItem name="About"></NavItem>
<NavItem name="Listings"></NavItem>
<NavItem name="Dashboard"></NavItem>
{loginState ? <NavItem name="Profile"><DropDown loginState={setLogin}></DropDown></NavItem> : <NavItem name="Login" click={() => authHandler.signIn(setLogin)}></NavItem>}
</div>
</div>
);
This is what I have for my authHandler class:
import firebase from 'firebase';
export default class Auth{
constructor(){
var firebaseConfig = {
...
};
!firebase.apps.length? firebase.initializeApp(firebaseConfig) : firebase.app();
firebase.analytics();
this.provider = new firebase.auth.GoogleAuthProvider();
}
signIn(state){
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION).then(() => {
return firebase.auth().signInWithPopup(this.provider).then((result) => {
console.log("signed in " + result.user.uid);
this.user = result.user
state(true);
}).catch((error) => {
console.log(error.message);
});
}).catch((error) => {
console.log(error.message);
})
}
getUser(){
return firebase.auth().currentUser;
}
logout(state){
//TODO: logout of firebase
state(false);
}
}
I have tried adding session and local persistence on firebase, but when I refresh the page, the user is signed out. What would be the proper way of maintaining persistence, in a separate class like this? I am trying to build this app with best practices in mind so that the code will be split up properly, and security is maintained.
Thanks!
You're supposed to use an auth state observer to get a callback whenever the user's sign in state changes. When a page first loads, the user is always immediately considered to be signed out. The callback will be invoked some time soon after the user's token has been loaded from persistence and verified. Use this state callback to determine what to render.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
You might want to show a loading screen until the first callback tells you for sure if the user was previously signed in or is definitely signed out.
I suggest reading this for more information.
The way I implemented the auth state in react :
Auth.provider.tsx
import React, {
FC,
createContext,
useContext,
useEffect,
useState,
} from 'react';
import { User, auth } from 'firebase/app';
interface AuthContext {
user: User | null;
loading: boolean;
}
const defaultAuthContext = { user: null, loading: false };
const AuthUserContext = createContext<AuthContext>({ ...defaultAuthContext });
export const AuthUserProvider: FC = ({ children }) => {
const [authContext, setAuthContext] = useState<AuthContext>({
user: null,
loading: true,
});
useEffect(
() =>
auth().onAuthStateChanged((authUser) =>
setAuthContext({ user: authUser, loading: false }),
),
[],
);
return (
<AuthUserContext.Provider value={authContext}>
{children}
</AuthUserContext.Provider>
);
};
export const useAuthUser = () => useContext(AuthUserContext);
App.tsx
const App: React.FC = () => {
return <AuthUserProvider>
// anything
</AuthUserProvider>;
}
anycomponent.tsx
const { user, loading } = useAuthUser();
return loading ? <Loader /> : !user ? <NotLogged /> : <Logged />;
You could implement the observer in your class but everytime you'll need your user you should implement an useEffect watching the user. Making it global in a provider make it easier to use.
There are many other way but I think this one is the easiest to use.
I am building a web application in which i need to verify the user's email sent via the client side (React.js and Next.js) and i'm following this youtube tutorial. However, the mentor is using create-react-app CLI and React-Router-Dom for the routing system which doesn't really go with my current needs.
Moreover, I found this method online using HOC :
import React from 'react';
import Router from 'next/router';
const login = '/register?redirected=true'; // Define your login route address.
const checkUserAuthentication = () => {
return { auth: null }; // change null to { isAdmin: true } for test it.
};
export default WrappedComponent => {
const hocComponent = ({ ...props }) => <WrappedComponent {...props} />;
hocComponent.getInitialProps = async (context) => {
const userAuth = await checkUserAuthentication();
// Are you an authorized user or not?
if (!userAuth?.auth) {
// Handle server-side and client-side rendering.
if (context.res) {
context.res?.writeHead(302, {
Location: login,
});
context.res?.end();
} else {
Router.replace(login);
}
} else if (WrappedComponent.getInitialProps) {
const wrappedProps = await WrappedComponent.getInitialProps({...context, auth: userAuth});
return { ...wrappedProps, userAuth };
}
return { userAuth };
};
return hocComponent;
};
The code above helps me to have a private route that the user cannot access unless he's authenticated (currently no programming included), but on the other hand i still need a page in the following route :
'pages/user/activate/[token].js' // the link sent via email from express back end.
What i need now is to create this page using Next routing system in order to get the token and decode it to move forward with the back end and save the user into MongoDB, and in order to accomplish that, i have created my [token].js page with the following code :
import React, {useState, useEffect} from 'react'
import { ToastContainer, toast } from 'react-toastify';
import axios from 'axios';
import jwt from 'jsonwebtoken';
import { authenticate, isAuth } from '../helpers/auth';
import { Link, Redirect } from 'react-router-dom';
const Activate = ({ match }) => {
const [formData, setFormData] = useState({
email: '',
token: '',
show: true
});
const { email, token, show } = formData;
useEffect(() => {
let token = match.params.token;
let { email } = jwt.decode(token);
if (token) {
setFormData({ ...formData, email, token });
}
console.log(token, email);
}, [match.params.token]);
return (
<>
{isAuth() ? <Redirect to="/" /> : null}
<p>Account activated, please log in</p>
</>
)
};
export default Activate;
However, i keep getting this error :
TypeError: Cannot read property 'params' of undefined
at Activate (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\user\activate\[token].js:245:13)
at processChild (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3353:14)
at resolve (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3270:5)
at ReactDOMServerRenderer.render (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-
dom-server.node.development.js:3753:22)
at ReactDOMServerRenderer.read (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3690:29)
at renderToString (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:4298:27)
at Object.renderPage (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:851)
at Function.getInitialProps (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\_document.js:293:19)
at loadGetInitialProps (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\lib\utils.js:5:101)
at renderToHTML (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:1142)
I couldn't find a solution because i believe that i'm doing something wrong whether in my code or in the logic implemented.
Is there any way that i can do this properly ?
Thank you in advance !
I'm attempting to make a simple RSS feed reader that will allow a user to enter a url in a search bar and display the results using Meteor and React. In my current set up, I have a SearchBar component with a function that makes a call to the meteor method on the server. How can I store the return of the API call in a client side collection? I've seen some examples on using publish and subscribe to do this, but haven't been able to follow. My goal is to save this data in a client side collection so I can access it from any components that will need it, and not have to render subsequent components through the SearchBar component's render method. This is how I currently have it set up:
feeds.js
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import parser from 'rss-parser';
if(Meteor.isServer) {
Meteor.methods({
getFeed(url) {
this.unblock();
const feed = {
title: '',
entries: []
};
try {
console.log('trying to get url');
const response = HTTP.get(url);
parser.parseString(response.content, function(err, parsed) {
feed.title = parsed.feed.title;
parsed.feed.entries.forEach(function(entry) {
feed.entries.push(entry);
})
});
console.log(feed.title);
return feed;
} catch(e) {
return false;
}
}
});
}
SearchBar.js
import React, { Component } from 'react';
import { Tracker } from 'meteor/tracker';
import FeedList from './FeedList';
export default class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {
results: null,
url: ''
}
}
onSubmit(e) {
const { url } = this.state;
e.preventDefault();
const response = Meteor.call('getFeed', url, (err, res) => {
if(!err) {
this.setState({
results:res.entries
});
console.log(this.state.results);
} else {
console.log(err.reason);
}
});
}
onChange(e) {
this.setState({
url: e.target.value
});
}
render() {
return (
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<input type="text" placeholder="Enter a URL" value={this.state.url} onChange={this.onChange.bind(this)}/>
<button type="submit">Get Feed</button>
</form>
{this.state.results ? <FeedList feedItems={this.state.results}/> : <p>Load a feed</p>}
</div>
);
}
}
Don't get the feed on the server at all. Get it on the client, and save it using a local collection defined like:
let localCollection = new Mongo.Collection(null)
Regarding the comments:
A typical pattern for this is for a cron job to populate a collection that is published to the client and rendered there.
This is going to be way over-engineered for your needs, and it's commonly regarded as a canonically wrong answer.