I'm currently carrying over a web project over to Electron, with the use of ReactJs, Redux, Express, MongoDB & Tailwind. I'm currently stuck on my authentication, where previously I made use of universal-cookies and history.push() for the usual redirecting and validation of cookies for the authentication process. Everything works perfectly fine, when I'm in the development server. I can see the cookies being added and redirection is working as expected. Now when I build the application, I try to login in, but nothing happens at all, but I can see the login request being successfully sent from the express server. I'm unsure if cookies are supported in Electron or either the use of history.push() (react-router-dom), but it's just odd seeing it work in the development server. Any help would be appreciated.
Login.js
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import Cookies from 'universal-cookie'
import { useDispatch } from 'react-redux'
import login from '../../redux/actions/auth/auth-login'
function Login({history}) {
const cookies = new Cookies();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const dispatch = useDispatch();
useEffect(() => {
if (!cookies.get("authToken")) {
history.push("/login");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [history]);
const loginHandler = () => {
dispatch(login(email, password)).then(() => {
history.push('/hub/calendar')
});
};
return (
<div>
<div style={{paddingLeft: '42%', paddingTop: '10%'}}>
<div className="w-80">
<img className="mb-4" src="https://res.cloudinary.com/my-studios-pro-primary/image/upload/v1639303304/LogoMakr-8hg510_rtlypr.png" alt=""/>
<input value={email} onChange={(e) => setEmail(e.target.value)} type="text" placeholder="Enter email" className="mt-6 shadow-md bg-grey-100 rounded-xl h-10 pl-2 w-80 outline-none"/>
<input value={password} onChange={(e) => setPassword(e.target.value)} type="password" placeholder="Enter password" className="mt-2 shadow-md bg-grey-100 rounded-xl h-10 pl-2 w-80 outline-none"/>
<p className="mt-6 text-gray-400">Have you <Link to="/forgotpassword"><mark style={{backgroundColor: 'transparent'}} className="cursor-pointer transition duration-300 ease-in-out hover:text-grey-700 font-bold text-gray-500">forgotten your password?</mark></Link></p>
<p onClick={loginHandler} className="cursor-pointer transition duration-300 ease-in-out hover:bg-primarygreen-600 text-center outline-none bg-primarygreen-500 pl-4 pr-4 pt-2 pb-2 rounded-xl w-full text-white-100 font-medium text-xl float-right mt-14">Login</p>
</div>
</div>
</div>
)
}
export default Login
Redux - login action
import * as actionTypes from '../action-types';
import axios from "axios";
import Cookies from 'universal-cookie';
export const Login = (email, password) => async (dispatch) => {
const cookies = new Cookies();
const config = {
headers: {
"Content-Type": "application/json"
},
};
try {
dispatch({type: actionTypes.USER_LOGIN_REQUEST});
const { data } = await axios.post('/api/auth/login', {email, password}, config);
dispatch({type: actionTypes.USER_LOGIN_SUCCESS, payload: data});
localStorage.setItem('userInfo', JSON.stringify(data));
cookies.set('authToken', data.token);
} catch (error) {
dispatch({
type: actionTypes.USER_LOGIN_FAIL,
payload: error.response && error.response.data.message
? error.response.data.message : error.message
});
}
}
// eslint-disable-next-line import/no-anonymous-default-export
export default Login
PrivateRoute
import React from 'react';
import {Redirect, Route} from 'react-router-dom';
import Cookies from 'universal-cookie';
const PrivateRoute = ({component: Component, ...rest}) => {
const cookies = new Cookies();
return (
<Route
{...rest}
render = {
(props) =>
cookies.get("authToken") ? (
<Component {...props} />
) : (
<Redirect to="/login"/>
)
}
/>
)
}
export default PrivateRoute
App.js
import React, { Component } from 'react';
import './App.css';
import { channels } from '../shared/constants';
import {HashRouter as Router, Route, Switch} from 'react-router-dom';
import PrivateRoute from '../routes/privateRoute';
import Forgotpassword from '../screens/auth/forgotpassword';
import Reviews from '../screens/hub/reviews';
import Journal from '../screens/hub/journal';
import Login from '../screens/auth/login';
import Hub from '../screens/hub/calendar';
import Test from '../screens/test';
const { ipcRenderer } = window;
class App extends Component {
constructor(props) {
super(props);
this.state = {
appName: '',
appVersion: '',
}
ipcRenderer.send(channels.APP_INFO);
ipcRenderer.on(channels.APP_INFO, (event, arg) => {
ipcRenderer.removeAllListeners(channels.APP_INFO);
const { appName, appVersion } = arg;
this.setState({ appName, appVersion });
});
}
render() {
return (
<Router>
<div className="font-poppins">
<Switch>
<PrivateRoute exact path="/" component={Test}/>
<PrivateRoute exact path="/hub/calendar" component={Hub}/>
<PrivateRoute exact path="/hub/reviews" component={Reviews} />
<PrivateRoute exact path="/hub/journal" component={Journal} />
<Route exact path="/login" component={Login} />
<Route exact path="/forgotpassword" component={Forgotpassword} />
</Switch>
</div>
</Router>
);
}
}
export default App;
Electron (main.js)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const url = require('url');
const { channels } = require('../src/shared/constants');
let mainWindow;
function createWindow () {
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, '../index.html'),
protocol: 'file:',
slashes: true,
});
mainWindow = new BrowserWindow({
width: 1500,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadURL(startUrl);
mainWindow.on('closed', function () {
mainWindow = null;
});
}
app.on('ready', createWindow);
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', function () {
if (mainWindow === null) {
createWindow();
}
});
ipcMain.on(channels.APP_INFO, (event) => {
event.sender.send(channels.APP_INFO, {
appName: app.getName(),
appVersion: app.getVersion(),
});
});
Preload
const { ipcRenderer } = require('electron');
window.ipcRenderer = ipcRenderer;
Package.json
"scripts": {
"start": "export BROWSER=none && craco start",
"start-win": "set BROWSER=none && craco start",
"start-electron": "export ELECTRON_START_URL=http://localhost:3000 && electron .",
"start-electron-win": "set ELECTRON_START_URL=http://localhost:3000 && electron .",
"clean": "rm -rf build dist",
"clean-win": "rmdir build /S /Q & rmdir dist/S /Q",
"build": "craco build",
"build-electron": "mkdir build/src && cp -r electron/. build/electron && cp -r src/shared/. build/src/shared",
"build-electron-win": "mkdir build/src && robocopy electron build/electron /S & robocopy src/shared build/src/shared /S",
"package": "electron-builder build --mac --win -c.extraMetadata.main=build/electron/main.js --publish never",
"package-win": "electron-builder build --win -c.extraMetadata.main=build/electron/main.js --publish never",
"test": "craco test",
"eject": "react-scripts eject"
},
"build": {
"files": [
"build/**/*",
"node_modules/**/*"
],
"publish": {
"provider": "github",
"repo": "mps",
"owner": "marlborojamez"
}
},