Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(login): add hook for login + interceptor #22

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
extends: ['airbnb-typescript-prettier'],
ignorePatterns: ['.eslintrc.js'],
// Rules can be here to override the preset of eslint from airbnb, if they are too strict.
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
Expand All @@ -22,6 +23,8 @@ module.exports = {
'no-console': 'off',

'react/destructuring-assignment': 0,
'react/jsx-props-no-spreading': 'off',
'import/prefer-default-export': 0,
// 'react/prop-types': 0, -> this is an example
},
overrides: [
Expand Down
52 changes: 52 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@hookform/resolvers": "^1.3.4",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.21.1",
"brace": "^0.11.1",
"jsoneditor": "^9.1.9",
"jsoneditor-react": "^3.1.0",
"node-sass": "^4.14.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-hook-form": "^6.15.1",
"react-icons": "^4.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.0.3",
"typescript": "^4.1.3",
"web-vitals": "^0.2.4"
"web-vitals": "^0.2.4",
"yup": "^0.32.8"
},
"scripts": {
"start": "react-scripts start",
Expand Down
48 changes: 35 additions & 13 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.scss';
import BaseLayout from './components/layouts/BaseLayout';
import IpfsList from './pages/ipfs/IpfsList';
import { storageNames } from './config';
import UserContext from './hooks/UserContext';
import Ipfs from './pages/ipfs/Ipfs';

import './App.scss';
import IpfsList from './pages/ipfs/IpfsList';
import Login from './pages/Login/Login';
import { ProtectedRoute } from './shared/ProtectedRoute';

function App(): JSX.Element {
// Handle user state
const accessToken = localStorage.getItem(storageNames.user) || '';
const [isLoggedIn, setLoggedIn] = useState(!!accessToken);

// Handle user login by setting the storage and state
const login = (token: string): void => {
setLoggedIn(true);
localStorage.setItem(storageNames.user, token);
};

// Handle user logout
const logout = (): void => {
setLoggedIn(false);
localStorage.removeItem(storageNames.user);
};

return (
<Router>
<BaseLayout>
<Switch>
<Route path="/ipfs/:hash" component={Ipfs} />
<Route path={['/', '/ipfs']} component={IpfsList} />
</Switch>
</BaseLayout>
</Router>
<UserContext.Provider value={{ isLoggedIn, login, logout }}>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought this would be the best to handle the user state. Open to suggestions, if there is a better way to do it with React

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think saving user information in localStorage is not a safe way. We have to ask the user to save user info("Remember me" checkbox).
And for the states that need to be used in several places, prefer to use redux.

<Router>
<BaseLayout>
<Switch>
<Route path="/login" component={Login} />
<ProtectedRoute path="/ipfs/:hash" component={Ipfs} />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if we couldn't just handle this at a higher level, defining all the routes as protected seems overkill when we only have one route for not logged in ( /login )
But not sure if we can define two routers or instead conditional show one or the other ( if it's a good practice )

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both are good. But to make code cleaner, prefer to have 2 routers.

<ProtectedRoute path={['/', '/ipfs']} component={IpfsList} />
</Switch>
</BaseLayout>
</Router>
</UserContext.Provider>
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/layouts/BaseLayout/BaseLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Container } from 'semantic-ui-react';
import NavigationBar from '../NavigationBar';
import { NavigationBar } from '../NavigationBar';

interface BaseLayoutProps {
children: any;
Expand Down
26 changes: 18 additions & 8 deletions src/components/layouts/NavigationBar/NavigationBar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from 'react';
import React, { useContext } from 'react';
import { FaUserCircle } from 'react-icons/fa';
import { Link } from 'react-router-dom';

import UserContext from '../../../hooks/UserContext';
import './NavigationBar.scss';

const NavigationBar = (): JSX.Element => {
const userContext = useContext(UserContext);

const logout = (): void => {
userContext.logout();
};

return (
<div className="navigation-bar">
<nav className="navigation-bar__nav">
Expand All @@ -23,12 +29,16 @@ const NavigationBar = (): JSX.Element => {
</ul>
</div>
</div>
<div className="navigation-bar__user">
{/* TODO: Need to be a user page */}
<a className="navigation-bar__link navigation-bar__user-link" href="/">
<FaUserCircle />
</a>
</div>
{userContext.isLoggedIn ? (
<div className="navigation-bar__user">
{/* TODO: Need to have this as a small menu showing up */}
<a className="navigation-bar__link navigation-bar__user-link" onClick={logout} href="/login">
<FaUserCircle />
</a>
</div>
) : (
''
)}
</nav>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/layouts/NavigationBar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as NavigationBar } from './NavigationBar';
3 changes: 0 additions & 3 deletions src/components/layouts/NavigationBar/index.tsx

This file was deleted.

5 changes: 5 additions & 0 deletions src/config/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const apis = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought it would be good to handle everything in a config folder @NiiCoder
Apis urls, environment variables, etc.

// TODO: Replace this by the BE api urls we would be using. Probably storing this in a config file
url: 'https://jsonplaceholder.typicode.com',
timeout: 2 * 60 * 1000, // 2 minutes timeout
};
2 changes: 2 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { apis } from './api';
export { storageNames } from './storage-names';
3 changes: 3 additions & 0 deletions src/config/storage-names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const storageNames = {
user: 'pl11',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just random "paralink11" just so it's not easy to guess when looking

};
11 changes: 11 additions & 0 deletions src/hooks/UserContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';

interface UserContextState {
isLoggedIn: boolean;
login: (token: string) => void;
logout: () => void;
}

const UserContext = createContext({} as UserContextState);

export default UserContext;
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as UserContext } from './UserContext';
83 changes: 83 additions & 0 deletions src/pages/Login/Login.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
$elevation-card: 0 0 1px 0 rgba(8, 11, 14, 0.6), 0 16px 16px -1px rgba(8, 11, 14, 0.1);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am gonna create a ticket to define a theme & variables to use accross the app & a global style. Right now this is using a lot of "custom" things. But if we can come up with a global styling ( for inputs, buttons, etc. ) this would make it easier

$color-secondary: #e1e4e8;
$error-color: red;

.login {
&__card {
max-width: 400px;
margin: auto;
box-shadow: $elevation-card;
padding: 30px;
background: white;
}

&__title {
font-size: 22px;
line-height: 32px;
font-weight: 500;
text-align: center;
margin: 8px 0 20px;
font-weight: bold;
}

&__input-container {
position: relative;
margin-top: 20px;
}

&__input {
border-radius: 100px;
border: 1px solid $color-secondary;
font-size: 14px;
line-height: 24px;
font-weight: 500;
height: 40px;
width: 100%;
outline: none;
padding: 0 15px;

&--error {
border-color: $error-color;
}
}

&__input-error-msg {
font-size: 12px;
margin-left: 15px;
color: $error-color;
}

&__input-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
outline: none;
display: inline-block;
padding: 10px;
color: $color-secondary;
}

&__password-icon {
color: black;
cursor: pointer;
}

&__button {
border-radius: 100px;
font-size: 14px;
line-height: 24px;
background: black;
color: white;
padding: 0 10px;
height: 40px;
width: 100%;
border: none;
margin-top: 40px;
cursor: pointer;
padding: 0 20px;
outline: none;
}
}
Loading