Skip to content

Commit

Permalink
Feat/refactor app (#66)
Browse files Browse the repository at this point in the history
* remove router directory

* add eslint sort import plugin

* implement useSiweAuth hook

* update protectedRoute

* remove dashboard route

* update vite.config

* refactor identifiers page

* fix import

* refactor attestation page

* fix issue

* remove garbage codes

* add reveal functionality

* fix loading issue

* add test case

* fix
  • Loading branch information
mehdi-torabiv authored Sep 1, 2024
1 parent f16afe1 commit 3ed3b06
Show file tree
Hide file tree
Showing 53 changed files with 1,297 additions and 1,066 deletions.
39 changes: 30 additions & 9 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,39 @@ module.exports = {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json', "./tsconfig.app.json"],
project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
},
plugins: ['react', '@typescript-eslint', 'prettier'],
plugins: ['react', '@typescript-eslint', 'prettier', 'simple-import-sort'],
rules: {
'simple-import-sort/imports': [
'error',
{
groups: [
// 1. Side effect imports at the start. For me this is important because I want to import reset.css and global styles at the top of my main file.
['^\\u0000'],
// 2. `react` and packages: Things that start with a letter (or digit or underscore), or `@` followed by a letter.
['^react$', '^@?\\w'],
// 3. Absolute imports and other imports such as Vue-style `@/foo`.
// Anything not matched in another group. (also relative imports starting with "../")
['^@', '^'],
// 4. relative imports from same folder "./" (I like to have them grouped together)
['^\\./'],
// 5. style module imports always come last, this helps to avoid CSS order issues
['^.+\\.(module.css|module.scss)$'],
// 6. media imports
['^.+\\.(gif|png|svg|jpg)$'],
],
},
],
'react/react-in-jsx-scope': 0,
'import/no-extraneous-dependencies': 'off',
'import/prefer-default-export': 'warn',
"import/prefer-default-export": "off",
"import/order": 'warn',
"import/extensions": 'warn',
'import/prefer-default-export': 'off',
'import/order': 'warn',
'import/extensions': 'warn',
'@typescript-eslint/no-exp': 'off',
"react/no-array": 'off',
"react/function-component-definition": "off"
'react/no-array': 'off',
'react/function-component-definition': 'off',
'react-hooks/exhaustive-deps': 'off',
'react/prop-types': 'off',
},
};
};
11 changes: 11 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.3",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^15.8.0",
"husky": "^9.0.11",
"jsdom": "^24.1.0",
Expand Down
99 changes: 16 additions & 83 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
import React, { useEffect, useState } from 'react';
import './App.css';
import '@rainbow-me/rainbowkit/styles.css';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from '@mui/material/styles';

import React from 'react';
import { LitNetwork } from '@lit-protocol/constants';
import CssBaseline from '@mui/material/CssBaseline';
import { Route, Routes, Navigate, useNavigate } from 'react-router-dom';
import { WagmiProvider } from 'wagmi';
import { ThemeProvider } from '@mui/material/styles';
import {
AuthenticationStatus,
createAuthenticationAdapter,
getDefaultConfig,
RainbowKitAuthenticationProvider,
RainbowKitProvider,
} from '@rainbow-me/rainbowkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Navigate, Route, Routes } from 'react-router-dom';
import { WagmiProvider } from 'wagmi';
import { sepolia } from 'wagmi/chains';
import { getAddress } from 'viem';
import { createSiweMessage } from 'viem/siwe';
import { LitNetwork } from '@lit-protocol/constants';
import Login from './pages/Auth/Login';
import theme from './libs/theme';
import { api } from './services/api';

import { CustomSnackbar } from './components/shared/CustomSnackbar';
import { LitProvider } from './hooks/LitProvider';
import useSiweAuth from './hooks/useSiweAuth';
import DefaultLayout from './layouts/DefaultLayout';

// import Dashboard from './pages/Dashboard';
import theme from './libs/theme';
import Login from './pages/Auth/Login';
import Callback from './pages/Callback';
import Identifiers from './pages/Identifiers';
import Permissions from './pages/Permissions';
import Attestation from './pages/Identifiers/Attestation';
import Callback from './pages/Callback';
import Permissions from './pages/Permissions';
import ProtectedRoute from './ProtectedRoute';
import { LitProvider } from './hooks/LitProvider';
import { CustomSnackbar } from './components/shared/CustomSnackbar';

const queryClient = new QueryClient({
defaultOptions: {
Expand All @@ -50,69 +45,7 @@ const config = getDefaultConfig({
});

const App: React.FC = () => {
const navigate = useNavigate();
const [authStatus, setAuthStatus] =
useState<AuthenticationStatus>('unauthenticated');

const authenticationAdapter = createAuthenticationAdapter({
getNonce: async () => {
const { data } = await api.get('auth/siwe/nonce');
return data.nonce;
},
createMessage: ({ nonce, address, chainId }) => {
return createSiweMessage({
address: getAddress(address),
chainId,
domain: window.location.host,
nonce,
uri: window.location.origin,
version: '1',
statement: 'Sign in with Ethereum to the app.',
});
},
getMessageBody: ({ message }) => message,
verify: async ({ message, signature }) => {
const { data } = await api.post('auth/siwe/verify', {
message,
signature,
chainId: 11155111,
});

if (!data) {
throw new Error('Verification response data is empty');
}

if (data?.jwt) {
localStorage.setItem('OCI_TOKEN', data.jwt);
setAuthStatus('authenticated');
return true;
}

return false;
},
signOut: async () => {
localStorage.removeItem('OCI_TOKEN');
navigate('/auth/login');
setAuthStatus('unauthenticated');
},
});

useEffect(() => {
const checkStoredToken = () => {
const OCI_TOKEN = localStorage.getItem('OCI_TOKEN');
if (OCI_TOKEN) {
setAuthStatus('authenticated');
} else {
setAuthStatus('unauthenticated');
}
};

checkStoredToken();
}, []);

useEffect(() => {
console.log('authStatus', authStatus);
}, [authStatus]);
const { authStatus, authenticationAdapter } = useSiweAuth();

globalThis.Buffer = Buffer;

Expand Down Expand Up @@ -148,7 +81,7 @@ const App: React.FC = () => {
<Route path="/" element={<Navigate to="/identifiers" />} />
<Route path="/identifiers" element={<Identifiers />} />
<Route
path="identifiers/:providers/attestation"
path="identifiers/:provider/attestation"
element={<Attestation />}
/>
<Route path="/permissions" element={<Permissions />} />
Expand Down
24 changes: 10 additions & 14 deletions src/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Navigate } from 'react-router-dom';
import CircularProgress from '@mui/material/CircularProgress';
import Backdrop from '@mui/material/Backdrop';
import CircularProgress from '@mui/material/CircularProgress';
import { Navigate } from 'react-router-dom';
import { useAccount } from 'wagmi';

interface ProtectedRouteProps {
Expand All @@ -15,23 +15,19 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const { isConnected } = useAccount();

useEffect(() => {
if (!isConnected) {
setIsAuthenticated(false);
localStorage.removeItem('OCI_TOKEN');
}
}, [isConnected]);

useEffect(() => {
const checkAuthStatus = async () => {
const token = localStorage.getItem('OCI_TOKEN');
if (token) {
setIsAuthenticated(true);
const checkAuthStatus = () => {
if (isConnected) {
const token = localStorage.getItem('OCI_TOKEN');
setIsAuthenticated(!!token);
} else {
setIsAuthenticated(false);
localStorage.removeItem('OCI_TOKEN');
}
setLoading(false);
};

checkAuthStatus();
}, []);
}, [isConnected]);

if (loading) {
return (
Expand Down
5 changes: 3 additions & 2 deletions src/components/layouts/AccountPopover.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';

import AccountPopover from './AccountPopover';

describe('AccountPopover', () => {
Expand Down
10 changes: 5 additions & 5 deletions src/components/layouts/AccountPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useState, MouseEvent } from 'react';
import { MouseEvent, useState } from 'react';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import LogoutIcon from '@mui/icons-material/Logout';
import {
Avatar,
Popover,
MenuItem,
IconButton,
MenuItem,
Popover,
Typography,
} from '@mui/material';
import { alpha } from '@mui/material/styles';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import LogoutIcon from '@mui/icons-material/Logout';

function AccountPopover() {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
Expand Down
3 changes: 2 additions & 1 deletion src/components/layouts/AppbarApp.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';

import AppbarApp from './AppbarApp';

// Mocking the ConnectButton from RainbowKit
Expand Down
8 changes: 5 additions & 3 deletions src/components/layouts/SidebarApp.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import SidebarApp from './SidebarApp';
import { describe, expect, it, vi } from 'vitest';

import { SIDEBAR_MENU } from '../../libs/constants';

import SidebarApp from './SidebarApp';

// Mock the constants
vi.mock('../../libs/constants', () => ({
DRAWER_WIDTH: 240,
Expand Down
5 changes: 3 additions & 2 deletions src/components/layouts/SidebarApp.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import {
Box,
Divider,
Drawer,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Box,
Toolbar,
Divider,
Typography,
} from '@mui/material';
import { useLocation, useNavigate } from 'react-router-dom';

import { DRAWER_WIDTH, SIDEBAR_MENU } from '../../libs/constants';

function SidebarApp() {
Expand Down
26 changes: 26 additions & 0 deletions src/components/pages/attestations/StepOne.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import '@testing-library/jest-dom';

import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';

import { Provider } from '../../../enums';

import StepOne from './StepOne';

describe('StepOne Component', () => {
it('renders the component with the correct provider', () => {
render(
<MemoryRouter>
<StepOne provider={Provider.Google} handleNextStep={() => {}} />
</MemoryRouter>
);

expect(screen.getByText('Let’s get started!')).toBeInTheDocument();
expect(
screen.getByText('Please authenticate with Google to continue.')
).toBeInTheDocument();
expect(
screen.getByRole('button', { name: /Authorize with Google/i })
).toBeInTheDocument();
});
});
Loading

0 comments on commit 3ed3b06

Please sign in to comment.