From 50ecdcd666af4b70904d404319ef6db2ff8c3657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Deniz=20KIVRAK?= Date: Sat, 23 Sep 2023 22:11:04 +0300 Subject: [PATCH] refactor services (#4) --- .eslintrc.cjs | 38 ++++++- package-lock.json | 22 ++-- package.json | 32 +++++- src/App.tsx | 58 ++-------- src/__mocks__/customRender.tsx | 5 +- .../i18n/translations/en-US/Layout.json | 1 - .../i18n/translations/tr-TR/Layout.json | 1 - .../CookieConsent/CookieConsent.stories.tsx | 15 --- .../CookieConsentMessage.stories.tsx | 17 +++ ...test.tsx => CookieConsentMessage.test.tsx} | 8 +- ...ieConsent.tsx => CookieConsentMessage.tsx} | 10 +- ...nap => CookieConsentMessage.test.tsx.snap} | 4 +- .../components/Layout/Copyright/Copyright.tsx | 2 +- .../ErrorFallback/ErrorFallback.test.tsx | 2 +- .../Layout/ErrorFallback/ErrorFallback.tsx | 58 ++++++++-- .../Layout/ErrorFallback/styles.css | 60 ++++++++++ .../ui/components/Layout/Header/Header.tsx | 17 ++- .../ui/components/Layout/Layout.test.tsx | 4 +- src/features/ui/components/Layout/Layout.tsx | 84 ++++++-------- .../ScrollToTopButton/ScrollToTopButton.tsx | 4 +- .../Layout/SocialMenu/SocialMenu.test.tsx | 8 +- .../Layout/SocialMenu/SocialMenu.tsx | 44 ++++---- .../__snapshots__/SocialMenu.test.tsx.snap | 2 +- .../Layout/ThemeSwitcher/ThemeSwitcher.tsx | 3 +- .../wallet/components/BlockInfo/BlockInfo.tsx | 2 +- .../ConnectButton/Button/Button.tsx | 2 +- .../ConnectButton/ConnectButton.tsx | 17 +-- .../ConnectionModal/Modal/Modal.tsx | 10 +- .../ConnectionModal/Steps/CheckWallet.tsx | 2 +- .../ProfileDropdownMenu/DropdownMenu.tsx | 7 +- src/features/wallet/models/IWalletAPI.ts | 8 -- .../actionEffects/disconnectWallet.test.ts | 2 +- .../account/actionEffects/disconnectWallet.ts | 2 +- .../account/actionEffects/loadAccount.test.ts | 2 +- .../account/actionEffects/loadAccount.ts | 18 +-- .../account/actionEffects/signIn.test.ts | 2 +- .../models/account/actionEffects/signIn.ts | 32 +++--- .../actionEffects/unlockWallet.test.ts | 2 +- .../account/actionEffects/unlockWallet.ts | 26 ++--- .../network/actionEffects/latestBlock.test.ts | 2 +- .../network/actionEffects/latestBlock.ts | 2 +- .../network/actionEffects/loadNetwork.test.ts | 2 +- .../network/actionEffects/loadNetwork.ts | 30 ++--- .../actionEffects/switchNetwork.test.ts | 2 +- .../network/actionEffects/switchNetwork.ts | 28 ++--- .../actionEffects/loadProvider.test.ts | 4 +- .../provider/actionEffects/loadProvider.ts | 20 ++-- src/features/wallet/sagas.ts | 3 +- src/pages/Home/Home.tsx | 2 +- .../{Avvy => ethersV5/avvy}/AvvyAPI.ts | 0 .../ResolutionUtilsV2/ResolutionUtilsV2.ts | 2 +- .../ResolutionUtilsV2__factory.ts | 3 +- .../interfaces/IWalletEthersV5ProviderApi.ts} | 5 +- .../ethersV5}/types/common.ts | 0 .../wallet/WalletAPI.ts} | 103 +++++++++--------- src/services/interfaces/IAvvyAPI.ts | 4 + src/services/interfaces/IWalletAPI.ts | 8 ++ .../interfaces}/IWalletAccountApi.ts | 2 +- .../interfaces}/IWalletNetworkApi.ts | 2 +- src/services/interfaces/IWalletProviderApi.ts | 3 + src/{jest.setup.ts => setupTests.ts} | 0 src/store/StoreLoader.tsx | 16 --- src/store/store.ts | 18 +-- tsconfig.json | 6 +- vite.config.ts | 20 +++- 65 files changed, 508 insertions(+), 412 deletions(-) delete mode 100644 src/features/ui/components/Layout/CookieConsent/CookieConsent.stories.tsx create mode 100644 src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.stories.tsx rename src/features/ui/components/Layout/CookieConsent/{CookieConsent.test.tsx => CookieConsentMessage.test.tsx} (52%) rename src/features/ui/components/Layout/CookieConsent/{CookieConsent.tsx => CookieConsentMessage.tsx} (74%) rename src/features/ui/components/Layout/CookieConsent/__snapshots__/{CookieConsent.test.tsx.snap => CookieConsentMessage.test.tsx.snap} (93%) create mode 100644 src/features/ui/components/Layout/ErrorFallback/styles.css delete mode 100644 src/features/wallet/models/IWalletAPI.ts rename src/services/{Avvy => ethersV5/avvy}/AvvyAPI.ts (100%) rename src/services/{Avvy => ethersV5/avvy}/ResolutionUtilsV2/ResolutionUtilsV2.ts (98%) rename src/services/{Avvy => ethersV5/avvy}/ResolutionUtilsV2/ResolutionUtilsV2__factory.ts (99%) rename src/{features/wallet/models/provider/IWalletProviderApi.ts => services/ethersV5/interfaces/IWalletEthersV5ProviderApi.ts} (61%) rename src/{blockchain => services/ethersV5}/types/common.ts (100%) rename src/services/{Ethers/WalletAPI/EthersWalletAPI.ts => ethersV5/wallet/WalletAPI.ts} (73%) create mode 100644 src/services/interfaces/IAvvyAPI.ts create mode 100644 src/services/interfaces/IWalletAPI.ts rename src/{features/wallet/models/account => services/interfaces}/IWalletAccountApi.ts (83%) rename src/{features/wallet/models/network => services/interfaces}/IWalletNetworkApi.ts (81%) create mode 100644 src/services/interfaces/IWalletProviderApi.ts rename src/{jest.setup.ts => setupTests.ts} (100%) delete mode 100644 src/store/StoreLoader.tsx diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 77d64d3..ae5e95c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -4,11 +4,47 @@ module.exports = { extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'], ignorePatterns: ['dist', '.eslintrc.cjs'], parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + plugins: ['react-refresh', "import"], rules: { + "@typescript-eslint/no-explicit-any": "off", + 'no-unused-vars': "off", + "@typescript-eslint/no-unused-vars": "off", 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, ], + "import/order": [ + "warn", + { + "newlines-between": "always", + "groups": [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index" + ], + "pathGroups": [ + { + "pattern": "**/*.stories.ts?", + "group": "external", + "position": "before" + }, + { + "pattern": "**/*.test.ts?", + "group": "external", + "position": "before" + } + ], + "pathGroupsExcludedImportTypes": [ + "builtin" + ], + "alphabetize": { + "order": "asc", + "caseInsensitive": true + } + } + ] }, } diff --git a/package-lock.json b/package-lock.json index b79fa01..0737b79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "vitedapp", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vitedapp", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@chakra-ui/react": "^2.8.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@metamask/jazzicon": "^2.0.0", - "@react-icons/all-files": "^4.1.0", "@reduxjs/toolkit": "^1.9.5", "ethers": "^5.7.2", "framer-motion": "^10.16.4", @@ -25,6 +24,7 @@ "react-error-boundary": "^4.0.11", "react-helmet-async": "^1.3.0", "react-i18next": "^13.2.2", + "react-icons": "^4.11.0", "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", "redux-saga": "^1.2.3" @@ -6376,14 +6376,6 @@ "@babel/runtime": "^7.13.10" } }, - "node_modules/@react-icons/all-files": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@react-icons/all-files/-/all-files-4.1.0.tgz", - "integrity": "sha512-hxBI2UOuVaI3O/BhQfhtb4kcGn9ft12RWAFVMUeNjqqhLsHvFtzIkFaptBJpFDANTKoDfdVoHTKZDlwKCACbMQ==", - "peerDependencies": { - "react": "*" - } - }, "node_modules/@redux-saga/core": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", @@ -19504,6 +19496,14 @@ } } }, + "node_modules/react-icons": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", + "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-inspector": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", diff --git a/package.json b/package.json index fd78217..cd3cf79 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "name": "vitedapp", "private": true, - "version": "0.0.2", + "version": "0.1.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts,tsx --max-warnings 0", "preview": "vite preview", "test": "jest --no-cache", + "coverage": "jest --coverage --no-cache", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "extract": "i18next 'src/**/*.{ts,tsx}'" @@ -18,7 +19,6 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@metamask/jazzicon": "^2.0.0", - "@react-icons/all-files": "^4.1.0", "@reduxjs/toolkit": "^1.9.5", "ethers": "^5.7.2", "framer-motion": "^10.16.4", @@ -31,6 +31,7 @@ "react-error-boundary": "^4.0.11", "react-helmet-async": "^1.3.0", "react-i18next": "^13.2.2", + "react-icons": "^4.11.0", "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", "redux-saga": "^1.2.3" @@ -86,7 +87,28 @@ "react-i18next": "/src/__mocks__/i18nextMock.tsx" }, "setupFilesAfterEnv": [ - "/src/jest.setup.ts" - ] + "/src/setupTests.ts" + ], + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/reportWebVitals.ts", + "!src/serviceWorker.ts", + "!src/serviceWorkerRegistration.ts", + "!src/test-utils.tsx", + "!src/index.tsx", + "!src/App.tsx", + "!src/**/*.stories.{js,jsx,ts,tsx}", + "!/node_modules/", + "!/template/" + ], + "coveragePathIgnorePatterns": [], + "coverageThreshold": { + "global": { + "branches": 30, + "functions": 40, + "lines": 40, + "statements": 40 + } + } } } diff --git a/src/App.tsx b/src/App.tsx index ccd455a..dca0f97 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,68 +1,24 @@ -import { EnhancedStore } from '@reduxjs/toolkit'; import log from 'loglevel'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; +import { Provider } from 'react-redux'; -import './features/i18n/i18n'; - -import { theme } from './features/ui/components/Layout/Theme/theme'; import { Router } from './pages/Router'; +import store from './store/store'; + +import './features/i18n/i18n'; log.setDefaultLevel('silent'); -if (process.env.NODE_ENV !== 'production') { +if (import.meta.env.MODE !== 'production') { log.enableAll(); } else { log.disableAll(); } -const Provider = React.lazy(() => - import(/* webpackChunkName: "Redux" */ 'react-redux').then(module => ({ - default: module.Provider, - })) -); - -const StoreLoader = React.lazy(() => - import(/* webpackChunkName: "Store" */ './store/StoreLoader').then( - module => ({ - default: module.StoreLoader, - }) - ) -); - -const ChakraProvider = React.lazy(() => - import(/* webpackChunkName: "ChakraUI" */ '@chakra-ui/react').then( - module => ({ - default: module.ChakraProvider, - }) - ) -); - export const App: React.FC = () => { - const [store, setStore] = useState(); - - useEffect(() => { - if (!store) { - return; - } - // Use lazy loaded module store - }, [store]); - - const handleStoreLoaded = (module: EnhancedStore) => { - // Careful, always do it using a function, - // else the module itself will be initialized! - setStore(() => module); - }; - - if (!store) { - // Lazy load it - return ; - } - return ( - - - + ); }; diff --git a/src/__mocks__/customRender.tsx b/src/__mocks__/customRender.tsx index f4a3044..410f649 100644 --- a/src/__mocks__/customRender.tsx +++ b/src/__mocks__/customRender.tsx @@ -2,10 +2,11 @@ import { ChakraProvider } from '@chakra-ui/react'; import { render, RenderOptions } from '@testing-library/react'; import * as React from 'react'; +import { I18nextProvider } from 'react-i18next'; + +import { theme } from '../features/ui/components/Layout/Theme/theme'; -import { theme } from '../features/ui/components/Layout/Theme/theme'; import i18n from './i18nextMock'; -import { I18nextProvider } from 'react-i18next'; const AllProviders = ({ children }: { children?: React.ReactNode }) => ( diff --git a/src/features/i18n/translations/en-US/Layout.json b/src/features/i18n/translations/en-US/Layout.json index d49365f..f2ca1cd 100644 --- a/src/features/i18n/translations/en-US/Layout.json +++ b/src/features/i18n/translations/en-US/Layout.json @@ -2,7 +2,6 @@ "Accept all cookies": "Accept all cookies", "Refuse non-essential cookies": "Refuse non-essential cookies", "This website uses cookies to enhance the user experience.": "This website uses cookies to enhance the user experience.", - "An error occured!": "An error occured!", "Open Menu": "Open Menu", "SITE_NAME": "My Awesome dApp", "SITE_DESCRIPTION": "dApp demonstration website that created with React dApp Template (Vite)", diff --git a/src/features/i18n/translations/tr-TR/Layout.json b/src/features/i18n/translations/tr-TR/Layout.json index 0d2bb36..1d291ff 100644 --- a/src/features/i18n/translations/tr-TR/Layout.json +++ b/src/features/i18n/translations/tr-TR/Layout.json @@ -2,7 +2,6 @@ "Accept all cookies": "Tüm çerezleri kabul et", "Refuse non-essential cookies": "Zorunlu olmayan çerezleri reddet", "This website uses cookies to enhance the user experience.": "Bu web sitesi, kullanıcı deneyimini geliştirmek için tanımlama tarayıcı çerezleri kullanır.", - "An error occured!": "Bir hata oluştu!", "Open Menu": "Menüyü Aç", "SITE_NAME": "Harika Merkeziyetsiz Uygulamam", "SITE_DESCRIPTION": "React dApp Template (Vite) ile oluşturulmuş örnek dApp sitesi", diff --git a/src/features/ui/components/Layout/CookieConsent/CookieConsent.stories.tsx b/src/features/ui/components/Layout/CookieConsent/CookieConsent.stories.tsx deleted file mode 100644 index 2f3af02..0000000 --- a/src/features/ui/components/Layout/CookieConsent/CookieConsent.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -// ConnectButton.stories.ts|tsx -import type { Meta, StoryObj } from '@storybook/react'; - -import { CookieConsent } from './CookieConsent'; - -const meta: Meta = { component: CookieConsent }; -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - debug: true, - }, -}; diff --git a/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.stories.tsx b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.stories.tsx new file mode 100644 index 0000000..f33d98c --- /dev/null +++ b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.stories.tsx @@ -0,0 +1,17 @@ +// ConnectButton.stories.ts|tsx +import type { Meta, StoryObj } from '@storybook/react'; + +import { CookieConsentMessage } from './CookieConsentMessage'; + +const meta: Meta = { + component: CookieConsentMessage, +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + debug: true, + }, +}; diff --git a/src/features/ui/components/Layout/CookieConsent/CookieConsent.test.tsx b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.test.tsx similarity index 52% rename from src/features/ui/components/Layout/CookieConsent/CookieConsent.test.tsx rename to src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.test.tsx index b727236..0b9a9d8 100644 --- a/src/features/ui/components/Layout/CookieConsent/CookieConsent.test.tsx +++ b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.test.tsx @@ -1,16 +1,16 @@ import { render } from '@testing-library/react'; -import { CookieConsent } from './CookieConsent'; +import { CookieConsentMessage } from './CookieConsentMessage'; describe('Feature: UI', () => { - describe('Component: Layout/CookieConsent', () => { + describe('Component: Layout/CookieConsentMessage', () => { it('should render with default props', () => { - const { asFragment } = render(); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); it('should render with debug enabled', () => { - const { asFragment } = render(); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/src/features/ui/components/Layout/CookieConsent/CookieConsent.tsx b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.tsx similarity index 74% rename from src/features/ui/components/Layout/CookieConsent/CookieConsent.tsx rename to src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.tsx index fd22309..fd0e07a 100644 --- a/src/features/ui/components/Layout/CookieConsent/CookieConsent.tsx +++ b/src/features/ui/components/Layout/CookieConsent/CookieConsentMessage.tsx @@ -1,19 +1,19 @@ import { Button, Text } from '@chakra-ui/react'; -import { CookieConsent as CookieConsetMessage } from 'react-cookie-consent'; +import { CookieConsent } from 'react-cookie-consent'; import { useTranslation } from 'react-i18next'; -export interface CookieConsentProps { +export interface CookieConsentMessageProps { debug?: boolean; expires?: number; } -export const CookieConsent: React.FC = ({ +export const CookieConsentMessage: React.FC = ({ debug = false, expires = 365, }) => { const { t } = useTranslation('Layout'); return ( - = ({ {t('This website uses cookies to enhance the user experience.')} - + ); }; diff --git a/src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsent.test.tsx.snap b/src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsentMessage.test.tsx.snap similarity index 93% rename from src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsent.test.tsx.snap rename to src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsentMessage.test.tsx.snap index 0c26e6a..2e794f6 100644 --- a/src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsent.test.tsx.snap +++ b/src/features/ui/components/Layout/CookieConsent/__snapshots__/CookieConsentMessage.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Feature: UI Component: Layout/CookieConsent should render with debug enabled 1`] = ` +exports[`Feature: UI Component: Layout/CookieConsentMessage should render with debug enabled 1`] = `
`; -exports[`Feature: UI Component: Layout/CookieConsent should render with default props 1`] = ` +exports[`Feature: UI Component: Layout/CookieConsentMessage should render with default props 1`] = `
{ return ( - +