├── .eslintrc.js ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── config-overrides.js ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── modules.js ├── paths.js ├── pnpTs.js ├── webpack.config.js └── webpackDevServer.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── _redirects ├── icon.png ├── index.html ├── logo.svg ├── manifest.json └── robots.txt ├── src ├── actions │ ├── auth.actions.js │ ├── collections.actions.js │ ├── filter.actions.js │ ├── header.actions.js │ ├── modal.actions.js │ ├── noti.actions.js │ ├── price.actions.js │ ├── tokens.actions.js │ └── walletconnect.actions.js ├── api │ └── index.js ├── assets │ ├── icons │ │ ├── iconBundle.js │ │ ├── iconClock.js │ │ ├── iconHeart.js │ │ └── iconList.js │ ├── imgs │ │ ├── 404_man.png │ │ ├── avatar_bg.png │ │ ├── dai.png │ │ ├── example.png │ │ ├── facebook.png │ │ ├── fantom_logo.png │ │ ├── ftm.png │ │ ├── gasly.png │ │ ├── logo.png │ │ ├── metamask.png │ │ ├── sea-background.jpeg │ │ ├── sign.png │ │ ├── text_bg1.png │ │ ├── umans_example.png │ │ ├── upload.png │ │ ├── usdc.png │ │ ├── usdt.png │ │ └── wftm.png │ └── svgs │ │ ├── add.svg │ │ ├── art.svg │ │ ├── bear.svg │ │ ├── card1.svg │ │ ├── card2.svg │ │ ├── card3.svg │ │ ├── card4.svg │ │ ├── cardboard.svg │ │ ├── check.svg │ │ ├── check_blue.svg │ │ ├── close.svg │ │ ├── coinbase.svg │ │ ├── collapse.svg │ │ ├── collectibles.svg │ │ ├── copy.svg │ │ ├── discord.svg │ │ ├── domain.svg │ │ ├── edit.svg │ │ ├── error.svg │ │ ├── fantom_logo_white.svg │ │ ├── filter.svg │ │ ├── ftm.svg │ │ ├── grid.svg │ │ ├── info.svg │ │ ├── instagram.svg │ │ ├── joystick.svg │ │ ├── logo_black.svg │ │ ├── logo_blue.svg │ │ ├── logo_small_blue.svg │ │ ├── logo_white.svg │ │ ├── magnifier.svg │ │ ├── medium.svg │ │ ├── monster.svg │ │ ├── nft.svg │ │ ├── nft_active.svg │ │ ├── nft_black.svg │ │ ├── notification.svg │ │ ├── plus.svg │ │ ├── plus2.svg │ │ ├── rainbow.svg │ │ ├── search.svg │ │ ├── settings.svg │ │ ├── share.svg │ │ ├── soccerball.svg │ │ ├── sports.svg │ │ ├── star.svg │ │ ├── success.svg │ │ ├── swap.svg │ │ ├── telegram.svg │ │ ├── tools.svg │ │ ├── trading.svg │ │ ├── twitter.svg │ │ ├── twitter_blue.svg │ │ ├── user.svg │ │ ├── utility.svg │ │ ├── virtual.svg │ │ ├── warning.svg │ │ └── web.svg ├── components │ ├── AccountModal │ │ ├── index.js │ │ └── styles.module.scss │ ├── AuctionModal │ │ ├── index.jsx │ │ └── styles.css │ ├── BanCollectionModal │ │ └── index.jsx │ ├── BanItemModal │ │ └── index.jsx │ ├── BanUserModal │ │ └── index.jsx │ ├── BidModal │ │ └── index.jsx │ ├── BoostCollectionModal │ │ └── index.jsx │ ├── BootstrapTooltip │ │ └── index.js │ ├── CategoriesFilter │ │ ├── index.js │ │ └── styles.scss │ ├── CollectionsFilter │ │ ├── index.js │ │ └── styles.scss │ ├── ConnectWalletModal │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── FilterWrapper │ │ ├── index.js │ │ └── styles.css │ ├── FollowersModal │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── Gasly │ │ ├── Header │ │ │ ├── index.js │ │ │ └── styles.module.scss │ │ └── Home │ │ │ ├── gasly.png │ │ │ ├── index.js │ │ │ └── styles.module.scss │ ├── Identicon.jsx │ ├── InfiniteLoader │ │ ├── index.js │ │ └── styles.css │ ├── InputError │ │ └── index.jsx │ ├── LikesModal │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── Loader │ │ └── index.js │ ├── ModModal │ │ └── index.jsx │ ├── Modal │ │ ├── common.module.scss │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── NFTCard │ │ ├── index.js │ │ └── styles.module.scss │ ├── NFTsGrid │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── NewBundleModal │ │ ├── index.js │ │ └── styles.module.scss │ ├── NotFound │ │ ├── index.js │ │ └── styles.module.scss │ ├── OfferModal │ │ └── index.jsx │ ├── OwnersModal │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── PaintBoard │ │ ├── index.js │ │ └── styles.module.scss │ ├── Panel │ │ ├── index.js │ │ └── styles.module.scss │ ├── PriceInput.js │ ├── ProtectedRoute.js │ ├── SellModal │ │ └── index.jsx │ ├── StatusFilter │ │ ├── index.js │ │ └── styles.css │ ├── SuspenseImg.js │ ├── TransferModal │ │ └── index.jsx │ ├── TxButton │ │ └── index.js │ ├── WFTMModal │ │ ├── index.jsx │ │ └── styles.module.scss │ ├── Web3ReactManager │ │ └── index.js │ ├── app.js │ ├── header │ │ ├── index.js │ │ └── styles.module.scss │ └── icons │ │ └── index.js ├── connectors │ ├── NetworkConnector.js │ └── index.js ├── constants │ ├── auth.constants.js │ ├── authmessages.js │ ├── collections.constants.js │ ├── colors.js │ ├── errors.js │ ├── filter.constants.js │ ├── firebase.js │ ├── header.constants.js │ ├── index.js │ ├── ipfs.constants.js │ ├── modal.constants.js │ ├── networks.js │ ├── noti.constants.js │ ├── price.constants.js │ ├── smartcontracts │ │ └── fnft.constants.js │ ├── tokens.constants.js │ ├── wallet.js │ └── walletconnect.constants.js ├── contracts │ ├── abi.js │ ├── auctions.js │ ├── bundleSales.js │ ├── factory.js │ ├── index.js │ ├── sales.js │ ├── token.js │ └── wftm.js ├── hooks │ ├── useContract.js │ ├── useEagerConnect.js │ ├── useInactiveListener.js │ ├── usePrevious.js │ ├── useTokens.js │ └── useWindowDimensions.js ├── index.css ├── index.js ├── pages │ ├── AccountDetails │ │ ├── index.js │ │ └── styles.module.scss │ ├── Collection │ │ ├── Create │ │ │ ├── index.js │ │ │ └── styles.module.scss │ │ └── Review │ │ │ ├── index.js │ │ │ └── styles.module.scss │ ├── NFTItem │ │ ├── index.js │ │ └── styles.module.scss │ ├── NotificationSetting │ │ ├── index.js │ │ └── styles.module.scss │ ├── explorepage │ │ ├── Body │ │ │ ├── ExploreBody │ │ │ │ ├── index.js │ │ │ │ └── styles.css │ │ │ └── FilterHeader │ │ │ │ ├── index.js │ │ │ │ └── styles.css │ │ ├── index.js │ │ └── styles.module.scss │ └── landingpage │ │ ├── index.js │ │ └── styles.module.scss ├── reducers │ ├── auth.reducers.js │ ├── collections.reducers.js │ ├── connectwallet.reducers.js │ ├── filter.reducers.js │ ├── header.reducers.js │ ├── index.js │ ├── modal.reducers.js │ ├── noti.reducers.js │ ├── price.reducers.js │ └── tokens.reducers.js ├── stores │ └── reduxStore.js └── utils │ ├── erc721.js │ ├── getLibrary.js │ ├── index.js │ ├── ipfs.js │ ├── sc.interaction.js │ ├── toast │ ├── index.js │ └── styles.module.scss │ ├── userAgent.js │ └── wallet.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | }, 6 | extends: [ 7 | 'eslint:recommended', 8 | 'plugin:react/recommended', 9 | 'plugin:prettier/recommended', 10 | ], 11 | parser: 'babel-eslint', 12 | globals: { 13 | Atomics: 'readonly', 14 | SharedArrayBuffer: 'readonly', 15 | }, 16 | parserOptions: { 17 | ecmaFeatures: { 18 | jsx: true, 19 | }, 20 | ecmaVersion: 2018, 21 | sourceType: 'module', 22 | }, 23 | plugins: ['react'], 24 | rules: { 25 | 'react/prop-types': 'off', 26 | 'prettier/prettier': [ 27 | 'error', 28 | { 29 | endOfLine: 'auto', 30 | }, 31 | ], 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jest": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended", 10 | "plugin:prettier/recommended" 11 | ], 12 | "parser": "babel-eslint", 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | }, 17 | "ecmaVersion": 12, 18 | "sourceType": "module" 19 | }, 20 | "plugins": [ 21 | "react" 22 | ], 23 | "rules": { 24 | "react/react-in-jsx-scope": "off", 25 | "react/prop-types": "off", 26 | "prettier/prettier": ["error",{ 27 | "endOfLine": "auto"} 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .env -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "endOfLine":"auto" 10 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "compile-hero.disable-compile-files-on-did-save-code": false 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Client for Artion Marketplace - Powered by Fantom 2 | 3 | A client side implementation of the Artion Marketplace for NFT trading on Fantom Opera network. 4 | 5 | Artion allows users: 6 | - to explore existing NFTs on Fantom Opera network. 7 | - to create a new collection of a new NFT 8 | - to register a collection of already deployed NFT. 9 | - to buy / sell / collect rare NFTs. 10 | 11 | See [Guide](https://docs.fantom.foundation/tutorials/collection-and-bundle-guide-on-artion) 12 | 13 | ## Project Setup 14 | ``` 15 | npm install / yarn 16 | ``` 17 | 18 | ## Compile 19 | ``` 20 | npm run start 21 | ``` 22 | 23 | ### Compiles and minifies for production 24 | ``` 25 | npm run build 26 | ``` 27 | 28 | #### .env file sample 29 | ``` 30 | REACT_APP_API_URL= 31 | SKIP_PREFLIGHT_CHECK=true 32 | REACT_APP_ENV= 33 | REACT_APP_USDC= 34 | REACT_APP_FUSDT= 35 | ``` 36 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const { override, addDecoratorsLegacy } = require('customize-cra'); 2 | module.exports = override(addDecoratorsLegacy()); 3 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const camelcase = require('camelcase'); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFileName = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFileName}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /config/modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const resolve = require('resolve'); 8 | 9 | /** 10 | * Get the baseUrl of a compilerOptions object. 11 | * 12 | * @param {Object} options 13 | */ 14 | function getAdditionalModulePaths(options = {}) { 15 | const baseUrl = options.baseUrl; 16 | 17 | // We need to explicitly check for null and undefined (and not a falsy value) because 18 | // TypeScript treats an empty string as `.`. 19 | if (baseUrl == null) { 20 | // If there's no baseUrl set we respect NODE_PATH 21 | // Note that NODE_PATH is deprecated and will be removed 22 | // in the next major release of create-react-app. 23 | 24 | const nodePath = process.env.NODE_PATH || ''; 25 | return nodePath.split(path.delimiter).filter(Boolean); 26 | } 27 | 28 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 29 | 30 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is 31 | // the default behavior. 32 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { 33 | return null; 34 | } 35 | 36 | // Allow the user set the `baseUrl` to `appSrc`. 37 | if (path.relative(paths.appSrc, baseUrlResolved) === '') { 38 | return [paths.appSrc]; 39 | } 40 | 41 | // Otherwise, throw an error. 42 | throw new Error( 43 | chalk.red.bold( 44 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 45 | ' Create React App does not support other values at this time.' 46 | ) 47 | ); 48 | } 49 | 50 | function getModules() { 51 | // Check if TypeScript is setup 52 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 53 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 54 | 55 | if (hasTsConfig && hasJsConfig) { 56 | throw new Error( 57 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 58 | ); 59 | } 60 | 61 | let config; 62 | 63 | // If there's a tsconfig.json we assume it's a 64 | // TypeScript project and set up the config 65 | // based on tsconfig.json 66 | if (hasTsConfig) { 67 | const ts = require(resolve.sync('typescript', { 68 | basedir: paths.appNodeModules, 69 | })); 70 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; 71 | // Otherwise we'll check if there is jsconfig.json 72 | // for non TS projects. 73 | } else if (hasJsConfig) { 74 | config = require(paths.appJsConfig); 75 | } 76 | 77 | config = config || {}; 78 | const options = config.compilerOptions || {}; 79 | 80 | const additionalModulePaths = getAdditionalModulePaths(options); 81 | 82 | return { 83 | additionalModulePaths: additionalModulePaths, 84 | hasTsConfig, 85 | }; 86 | } 87 | 88 | module.exports = getModules(); 89 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: /static/ -------------------------------------------------------------------------------- /src/actions/auth.actions.js: -------------------------------------------------------------------------------- 1 | import { AuthConstants } from '../constants/auth.constants'; 2 | 3 | const AuthActions = { 4 | fetchStart, 5 | fetchSuccess, 6 | fetchFailed, 7 | signOut, 8 | }; 9 | 10 | function fetchStart() { 11 | return dispatch => { 12 | dispatch(_fetchStart()); 13 | }; 14 | } 15 | 16 | const _fetchStart = () => { 17 | return { 18 | type: AuthConstants.PROFILE_GET_START, 19 | }; 20 | }; 21 | 22 | function fetchSuccess(user) { 23 | return dispatch => { 24 | dispatch(_fetchSuccess(user)); 25 | }; 26 | } 27 | 28 | const _fetchSuccess = user => { 29 | return { 30 | type: AuthConstants.PROFILE_GET_SUCCESS, 31 | payload: user, 32 | }; 33 | }; 34 | 35 | function fetchFailed() { 36 | return dispatch => { 37 | dispatch(_fetchFailed()); 38 | }; 39 | } 40 | 41 | const _fetchFailed = () => { 42 | return { 43 | type: AuthConstants.PROFILE_GET_FAILED, 44 | }; 45 | }; 46 | 47 | function signOut() { 48 | return dispatch => { 49 | dispatch(_signOut()); 50 | }; 51 | } 52 | 53 | const _signOut = () => { 54 | return { 55 | type: AuthConstants.SIGN_OUT, 56 | }; 57 | }; 58 | 59 | export default AuthActions; 60 | -------------------------------------------------------------------------------- /src/actions/collections.actions.js: -------------------------------------------------------------------------------- 1 | import CollectionsConstants from '../constants/collections.constants'; 2 | 3 | const CollectionsActions = { 4 | fetchStart, 5 | fetchSuccess, 6 | fetchFailed, 7 | }; 8 | 9 | function fetchStart() { 10 | return dispatch => { 11 | dispatch({ 12 | type: CollectionsConstants.FETCH_COLLECTIONS_START, 13 | }); 14 | }; 15 | } 16 | 17 | function fetchSuccess(collections) { 18 | return dispatch => { 19 | dispatch(_fetchSuccess(collections)); 20 | }; 21 | } 22 | 23 | const _fetchSuccess = collections => { 24 | return { 25 | type: CollectionsConstants.FETCH_COLLECTIONS_SUCCESS, 26 | collections, 27 | }; 28 | }; 29 | 30 | function fetchFailed() { 31 | return dispatch => { 32 | dispatch({ 33 | type: CollectionsConstants.FETCH_COLLECTIONS_FAILED, 34 | }); 35 | }; 36 | } 37 | 38 | export default CollectionsActions; 39 | -------------------------------------------------------------------------------- /src/actions/filter.actions.js: -------------------------------------------------------------------------------- 1 | import FilterConstants from '../constants/filter.constants'; 2 | 3 | const FilterActions = { 4 | updateStatusFilter, 5 | updateCollectionsFilter, 6 | updateCategoryFilter, 7 | updateGroupTypeFilter, 8 | updateSortByFilter, 9 | }; 10 | 11 | function updateStatusFilter(field, selected) { 12 | return dispatch => { 13 | dispatch(_updateStatusFilter(field, selected)); 14 | }; 15 | } 16 | 17 | const _updateStatusFilter = (field, selected) => { 18 | return { 19 | type: FilterConstants.UPDATE_STATUS_FILTER, 20 | field, 21 | selected, 22 | }; 23 | }; 24 | 25 | function updateCollectionsFilter(collections) { 26 | return dispatch => { 27 | dispatch(_updateCollectionsFilter(collections)); 28 | }; 29 | } 30 | 31 | const _updateCollectionsFilter = collections => { 32 | return { 33 | type: FilterConstants.UPDATE_COLLECTIONS_FILTER, 34 | collections, 35 | }; 36 | }; 37 | 38 | function updateCategoryFilter(category) { 39 | return dispatch => { 40 | dispatch(_updateCategoryFilter(category)); 41 | }; 42 | } 43 | 44 | const _updateCategoryFilter = category => { 45 | return { 46 | type: FilterConstants.UPDATE_CATEGORIES_FILTER, 47 | category, 48 | }; 49 | }; 50 | 51 | function updateGroupTypeFilter(groupType) { 52 | return dispatch => { 53 | dispatch(_updateGroupTypeFilter(groupType)); 54 | }; 55 | } 56 | 57 | const _updateGroupTypeFilter = groupType => { 58 | return { 59 | type: FilterConstants.UPDATE_GROUP_TYPE_FILTER, 60 | groupType, 61 | }; 62 | }; 63 | 64 | function updateSortByFilter(sortBy) { 65 | return dispatch => { 66 | dispatch(_updateSortByFilter(sortBy)); 67 | }; 68 | } 69 | 70 | const _updateSortByFilter = sortBy => { 71 | return { 72 | type: FilterConstants.UPDATE_SORT_BY_FILTER, 73 | sortBy, 74 | }; 75 | }; 76 | 77 | export default FilterActions; 78 | -------------------------------------------------------------------------------- /src/actions/header.actions.js: -------------------------------------------------------------------------------- 1 | import HeaderConstants from '../constants/header.constants'; 2 | 3 | const HeaderActions = { 4 | toggleSearchbar, 5 | }; 6 | 7 | function toggleSearchbar(toggle) { 8 | return dispatch => { 9 | dispatch(_toggleSearchbar(toggle)); 10 | }; 11 | } 12 | 13 | const _toggleSearchbar = toggle => { 14 | return { 15 | type: HeaderConstants.TOGGLESEARCHBAR, 16 | toggle: toggle, 17 | }; 18 | }; 19 | 20 | export default HeaderActions; 21 | -------------------------------------------------------------------------------- /src/actions/modal.actions.js: -------------------------------------------------------------------------------- 1 | import ModalConstants from '../constants/modal.constants'; 2 | 3 | const ModalActions = { 4 | showAccountModal, 5 | hideAccountModal, 6 | showWFTMModal, 7 | hideWFTMModal, 8 | showConnectWalletModal, 9 | hideConnectWalletModal, 10 | }; 11 | 12 | function showAccountModal() { 13 | return dispatch => { 14 | dispatch(_showAccountModal()); 15 | }; 16 | } 17 | 18 | const _showAccountModal = () => { 19 | return { 20 | type: ModalConstants.SHOW_ACCOUNT_MODAL, 21 | }; 22 | }; 23 | 24 | function hideAccountModal() { 25 | return dispatch => { 26 | dispatch(_hideAccountModal()); 27 | }; 28 | } 29 | 30 | const _hideAccountModal = () => { 31 | return { 32 | type: ModalConstants.HIDE_ACCOUNT_MODAL, 33 | }; 34 | }; 35 | 36 | function showWFTMModal() { 37 | return dispatch => { 38 | dispatch(_showWFTMModal()); 39 | }; 40 | } 41 | 42 | const _showWFTMModal = () => { 43 | return { 44 | type: ModalConstants.SHOW_WFTM_MODAL, 45 | }; 46 | }; 47 | 48 | function hideWFTMModal() { 49 | return dispatch => { 50 | dispatch(_hideWFTMModal()); 51 | }; 52 | } 53 | 54 | const _hideWFTMModal = () => { 55 | return { 56 | type: ModalConstants.HIDE_WFTM_MODAL, 57 | }; 58 | }; 59 | 60 | function showConnectWalletModal() { 61 | return dispatch => { 62 | dispatch(_showConnectWalletModal()); 63 | }; 64 | } 65 | 66 | const _showConnectWalletModal = () => { 67 | return { 68 | type: ModalConstants.SHOW_CONNECT_WALLET_MODAL, 69 | }; 70 | }; 71 | 72 | function hideConnectWalletModal() { 73 | return dispatch => { 74 | dispatch(_hideConnectWalletModal()); 75 | }; 76 | } 77 | 78 | const _hideConnectWalletModal = () => { 79 | return { 80 | type: ModalConstants.HIDE_CONNECT_WALLET_MODAL, 81 | }; 82 | }; 83 | 84 | export default ModalActions; 85 | -------------------------------------------------------------------------------- /src/actions/noti.actions.js: -------------------------------------------------------------------------------- 1 | import { NotiConstants } from '../constants/noti.constants.js'; 2 | 3 | const NotiActions = { 4 | updateNotification, 5 | markAllNotificationsAsRead, 6 | newUnreadNotification, 7 | }; 8 | 9 | function updateNotification() { 10 | return dispatch => { 11 | dispatch(_updateNotification()); 12 | }; 13 | } 14 | 15 | const _updateNotification = () => { 16 | return { 17 | type: NotiConstants.UPDATENOTIFICATION, 18 | }; 19 | }; 20 | 21 | function markAllNotificationsAsRead() { 22 | return dispatch => { 23 | dispatch(_markAllNotificationsAsRead()); 24 | }; 25 | } 26 | 27 | const _markAllNotificationsAsRead = () => { 28 | return { 29 | type: NotiConstants.READALLNOTIFICATION, 30 | }; 31 | }; 32 | 33 | function newUnreadNotification(count) { 34 | return dispatch => { 35 | dispatch(_newUnreadNotificatioon(count)); 36 | }; 37 | } 38 | 39 | const _newUnreadNotificatioon = count => { 40 | return { 41 | type: NotiConstants.UNREADNOTIFICATION, 42 | count: count, 43 | }; 44 | }; 45 | 46 | export default NotiActions; 47 | -------------------------------------------------------------------------------- /src/actions/price.actions.js: -------------------------------------------------------------------------------- 1 | import { PriceConstants } from '../constants/price.constants'; 2 | 3 | const PriceActions = { 4 | updatePrice, 5 | }; 6 | 7 | function updatePrice(price) { 8 | return dispatch => { 9 | dispatch(_updatePrice(price)); 10 | }; 11 | } 12 | 13 | const _updatePrice = price => { 14 | return { 15 | type: PriceConstants.UPDATE_PRICE, 16 | price, 17 | }; 18 | }; 19 | 20 | export default PriceActions; 21 | -------------------------------------------------------------------------------- /src/actions/tokens.actions.js: -------------------------------------------------------------------------------- 1 | import TokensConstants from '../constants/tokens.constants'; 2 | 3 | const TokensActions = { 4 | resetTokens, 5 | updateTokens, 6 | startFetching, 7 | fetchingSuccess, 8 | fetchingFailed, 9 | }; 10 | 11 | function resetTokens() { 12 | return dispatch => { 13 | dispatch(_resetTokens()); 14 | }; 15 | } 16 | 17 | const _resetTokens = () => { 18 | return { 19 | type: TokensConstants.RESET_TOKENS, 20 | }; 21 | }; 22 | 23 | function updateTokens(tokens) { 24 | return dispatch => { 25 | dispatch(_updateTokens(tokens)); 26 | }; 27 | } 28 | 29 | const _updateTokens = tokens => { 30 | return { 31 | type: TokensConstants.UPDATE_TOKENS, 32 | payload: { 33 | tokens, 34 | }, 35 | }; 36 | }; 37 | 38 | function startFetching(direction) { 39 | return dispatch => { 40 | dispatch(_startFetching(direction)); 41 | }; 42 | } 43 | 44 | const _startFetching = direction => { 45 | return { 46 | type: TokensConstants.FETCHING_START, 47 | payload: { 48 | direction, 49 | }, 50 | }; 51 | }; 52 | 53 | function fetchingSuccess(count, tokens, from, to) { 54 | return dispatch => { 55 | dispatch(_fetchingSuccess(count, tokens, from, to)); 56 | }; 57 | } 58 | 59 | const _fetchingSuccess = (count, tokens, from, to) => { 60 | return { 61 | type: TokensConstants.FETCHING_SUCCESS, 62 | payload: { 63 | count, 64 | tokens, 65 | from, 66 | to, 67 | }, 68 | }; 69 | }; 70 | 71 | function fetchingFailed() { 72 | return dispatch => { 73 | dispatch(_fetchingFailed()); 74 | }; 75 | } 76 | 77 | const _fetchingFailed = () => { 78 | return { 79 | type: TokensConstants.FETCHING_FAILED, 80 | }; 81 | }; 82 | 83 | export default TokensActions; 84 | -------------------------------------------------------------------------------- /src/actions/walletconnect.actions.js: -------------------------------------------------------------------------------- 1 | import { WalletConnectConstants } from '../constants/walletconnect.constants'; 2 | 3 | const WalletConnectActions = { 4 | connectWallet, 5 | disconnectWallet, 6 | }; 7 | 8 | function connectWallet(authToken, isModerator) { 9 | return dispatch => { 10 | dispatch(_connectWallet(authToken, isModerator)); 11 | }; 12 | } 13 | 14 | const _connectWallet = (authToken, isModerator) => { 15 | return { 16 | type: WalletConnectConstants.WALLETCONNECTED, 17 | token: authToken, 18 | isModerator, 19 | }; 20 | }; 21 | 22 | function disconnectWallet() { 23 | return dispatch => { 24 | dispatch(_disconnectWallet()); 25 | }; 26 | } 27 | 28 | const _disconnectWallet = () => { 29 | return { 30 | type: WalletConnectConstants.WALLETDISCONNECTED, 31 | }; 32 | }; 33 | 34 | export default WalletConnectActions; 35 | -------------------------------------------------------------------------------- /src/assets/icons/iconBundle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconBundle = props => ( 4 | 12 | 20 | 28 | 36 | 44 | 45 | ); 46 | 47 | export default IconBundle; 48 | -------------------------------------------------------------------------------- /src/assets/icons/iconClock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconClock = props => ( 4 | 12 | 19 | 26 | 27 | ); 28 | 29 | export default IconClock; 30 | -------------------------------------------------------------------------------- /src/assets/icons/iconHeart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconHeart = props => ( 4 | 12 | 19 | 20 | ); 21 | 22 | export default IconHeart; 23 | -------------------------------------------------------------------------------- /src/assets/icons/iconList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconList = props => ( 4 | 12 | 19 | 26 | 33 | 40 | 47 | 54 | 55 | ); 56 | 57 | export default IconList; 58 | -------------------------------------------------------------------------------- /src/assets/imgs/404_man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/404_man.png -------------------------------------------------------------------------------- /src/assets/imgs/avatar_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/avatar_bg.png -------------------------------------------------------------------------------- /src/assets/imgs/dai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/dai.png -------------------------------------------------------------------------------- /src/assets/imgs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/example.png -------------------------------------------------------------------------------- /src/assets/imgs/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/facebook.png -------------------------------------------------------------------------------- /src/assets/imgs/fantom_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/fantom_logo.png -------------------------------------------------------------------------------- /src/assets/imgs/ftm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/ftm.png -------------------------------------------------------------------------------- /src/assets/imgs/gasly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/gasly.png -------------------------------------------------------------------------------- /src/assets/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/logo.png -------------------------------------------------------------------------------- /src/assets/imgs/metamask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/metamask.png -------------------------------------------------------------------------------- /src/assets/imgs/sea-background.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/sea-background.jpeg -------------------------------------------------------------------------------- /src/assets/imgs/sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/sign.png -------------------------------------------------------------------------------- /src/assets/imgs/text_bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/text_bg1.png -------------------------------------------------------------------------------- /src/assets/imgs/umans_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/umans_example.png -------------------------------------------------------------------------------- /src/assets/imgs/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/upload.png -------------------------------------------------------------------------------- /src/assets/imgs/usdc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/usdc.png -------------------------------------------------------------------------------- /src/assets/imgs/usdt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/usdt.png -------------------------------------------------------------------------------- /src/assets/imgs/wftm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/assets/imgs/wftm.png -------------------------------------------------------------------------------- /src/assets/svgs/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/art.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/card1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/card2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/card3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/svgs/card4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/assets/svgs/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/check_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/collectibles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/svgs/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/domain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/ftm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/grid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/svgs/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/instagram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/svgs/magnifier.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/nft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/nft_active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/nft_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/plus2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/svgs/sports.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/svgs/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/swap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svgs/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/svgs/trading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svgs/twitter_blue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/svgs/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svgs/utility.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/svgs/virtual.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/svgs/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/AccountModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | position: fixed; 3 | width: 100vw; 4 | height: 100vh; 5 | top: 0; 6 | left: 0; 7 | z-index: 1000; 8 | } 9 | 10 | .modal { 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | } 15 | 16 | .paper { 17 | width: calc(100% - 80px); 18 | max-width: 600px; 19 | padding: 40px; 20 | border-radius: 10px; 21 | box-sizing: border-box; 22 | background-color: #FFF; 23 | outline: none; 24 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); 25 | color: #3D3D3D; 26 | } 27 | 28 | .title { 29 | font-weight: 900; 30 | font-size: 28px; 31 | text-align: center; 32 | margin: 0 0 40px; 33 | } 34 | 35 | .formGroup { 36 | margin-bottom: 25px; 37 | position: relative; 38 | } 39 | 40 | .formLabel { 41 | margin: 0 0 8px; 42 | font-size: 18px; 43 | color: #3D3D3D; 44 | } 45 | 46 | .formInput { 47 | width: 100%; 48 | outline: none; 49 | height: 50px; 50 | border-radius: 10px; 51 | border: 1px solid #EAEAF1; 52 | padding: 8px 16px; 53 | box-sizing: border-box; 54 | font-size: 16px; 55 | } 56 | 57 | .longInput { 58 | resize: none; 59 | height: 100px; 60 | margin: 0; 61 | } 62 | 63 | .lengthIndicator { 64 | margin-top: 6px; 65 | margin-right: 6px; 66 | text-align: right; 67 | color: rgba(0, 0, 0, .6); 68 | 69 | & + .error { 70 | margin-top: -20px; 71 | } 72 | } 73 | 74 | .hasError { 75 | border: 1px solid rgb(235, 87, 87); 76 | } 77 | 78 | .error { 79 | margin: 6px 0 0 6px; 80 | color: rgb(235, 87, 87); 81 | } 82 | 83 | .footer { 84 | display: flex; 85 | justify-content: center; 86 | } 87 | 88 | .button { 89 | width: 168px; 90 | height: 48px; 91 | border-radius: 8px; 92 | font-weight: 700; 93 | font-size: 18px; 94 | background-color: #007bff; 95 | box-shadow: none; 96 | display: flex; 97 | align-items: center; 98 | justify-content: center; 99 | cursor: pointer; 100 | } 101 | 102 | .save { 103 | background-color: #1969FF; 104 | color: #FFF; 105 | margin-right: 40px 106 | }; 107 | 108 | .cancel { 109 | background-color: #FFF; 110 | border: 1px solid #1969FF; 111 | color: #1969FF; 112 | } 113 | 114 | .avatarBox { 115 | position: relative; 116 | width: 100px; 117 | height: 100px; 118 | background-color: #FAFAFA; 119 | border-radius: 100px; 120 | border: 1px solid rgba(0, 0, 0, 0.1); 121 | } 122 | 123 | .avatar { 124 | width: 100%; 125 | height: 100%; 126 | border-radius: 100%; 127 | object-fit: cover; 128 | } 129 | 130 | .upload { 131 | position: absolute; 132 | width: 37px; 133 | height: 37px; 134 | bottom: 4px; 135 | right: -4px; 136 | border: 1px solid rgba(0, 0, 0, 0.1); 137 | border-radius: 100%; 138 | background-color: #FFF; 139 | display: flex; 140 | align-items: center; 141 | justify-content: center; 142 | cursor: pointer; 143 | } 144 | 145 | .uploadIcon { 146 | width: 24px; 147 | height: 24px; 148 | color: #1969FF; 149 | } 150 | 151 | .disabled { 152 | cursor: not-allowed; 153 | box-shadow: none; 154 | opacity: 0.7; 155 | } 156 | 157 | @media only screen and (max-width: 600px) { 158 | .paper { 159 | padding: 40px 20px; 160 | } 161 | 162 | .title { 163 | font-size: 20px; 164 | } 165 | 166 | .footer { 167 | flex-direction: column; 168 | } 169 | 170 | .button { 171 | width: calc(100% - 48px); 172 | margin-left: auto; 173 | margin-right: auto; 174 | } 175 | 176 | .save { 177 | margin-bottom: 20px; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/components/AuctionModal/styles.css: -------------------------------------------------------------------------------- 1 | .calendarAboveInput .rdtPicker { 2 | bottom: 60px; 3 | } -------------------------------------------------------------------------------- /src/components/BanCollectionModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { ClipLoader } from 'react-spinners'; 4 | import { useWeb3React } from '@web3-react/core'; 5 | import { ethers } from 'ethers'; 6 | 7 | import toast from 'utils/toast'; 8 | import { useApi } from 'api'; 9 | import { getSigner } from 'contracts'; 10 | 11 | import Modal from '../Modal'; 12 | import styles from '../Modal/common.module.scss'; 13 | 14 | const BanCollectionModal = ({ visible, isBan, onClose }) => { 15 | const { getNonce, banCollection, unbanCollection } = useApi(); 16 | const { account } = useWeb3React(); 17 | 18 | const { authToken } = useSelector(state => state.ConnectWallet); 19 | 20 | const [banning, setBanning] = useState(false); 21 | const [address, setAddress] = useState(''); 22 | 23 | useEffect(() => { 24 | if (visible) { 25 | setBanning(false); 26 | } 27 | }, [visible]); 28 | 29 | const handleBanItem = async () => { 30 | if (banning) return; 31 | 32 | try { 33 | setBanning(true); 34 | 35 | const { data: nonce } = await getNonce(account, authToken); 36 | 37 | let signature; 38 | let addr; 39 | try { 40 | const signer = await getSigner(); 41 | const msg = `Approve Signature on Artion.io with nonce ${nonce}`; 42 | signature = await signer.signMessage(msg); 43 | addr = ethers.utils.verifyMessage(msg, signature); 44 | } catch (err) { 45 | toast( 46 | 'error', 47 | 'You need to sign the message to be able to ban/unban collection.' 48 | ); 49 | setBanning(false); 50 | return; 51 | } 52 | 53 | await (isBan ? banCollection : unbanCollection)( 54 | address, 55 | authToken, 56 | signature, 57 | addr 58 | ); 59 | toast('success', 'Success!'); 60 | } catch (e) { 61 | console.log(e); 62 | } 63 | setBanning(false); 64 | }; 65 | 66 | return ( 67 | 75 | ) : isBan ? ( 76 | 'Ban' 77 | ) : ( 78 | 'Unban' 79 | ) 80 | } 81 | onSubmit={!banning ? () => handleBanItem() : null} 82 | > 83 |
84 |
Collection Address
85 |
86 | setAddress(e.target.value)} 91 | disabled={banning} 92 | /> 93 |
94 |
95 |
96 | ); 97 | }; 98 | 99 | export default BanCollectionModal; 100 | -------------------------------------------------------------------------------- /src/components/BanItemModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { ClipLoader } from 'react-spinners'; 4 | import { useWeb3React } from '@web3-react/core'; 5 | import { ethers } from 'ethers'; 6 | 7 | import toast from 'utils/toast'; 8 | import { useApi } from 'api'; 9 | import { getSigner } from 'contracts'; 10 | 11 | import Modal from '../Modal'; 12 | import styles from '../Modal/common.module.scss'; 13 | 14 | const BanItemModal = ({ visible, onClose }) => { 15 | const { getNonce, banItems } = useApi(); 16 | const { account } = useWeb3React(); 17 | 18 | const { authToken } = useSelector(state => state.ConnectWallet); 19 | 20 | const [banning, setBanning] = useState(false); 21 | const [address, setAddress] = useState(''); 22 | const [tokenIDs, setTokenIDs] = useState(''); 23 | 24 | useEffect(() => { 25 | if (visible) { 26 | setBanning(false); 27 | } 28 | }, [visible]); 29 | 30 | const handleBanItem = async () => { 31 | if (banning) return; 32 | 33 | try { 34 | setBanning(true); 35 | 36 | const { data: nonce } = await getNonce(account, authToken); 37 | 38 | let signature; 39 | let addr; 40 | try { 41 | const signer = await getSigner(); 42 | const msg = `Approve Signature on Artion.io with nonce ${nonce}`; 43 | signature = await signer.signMessage(msg); 44 | addr = ethers.utils.verifyMessage(msg, signature); 45 | } catch (err) { 46 | toast( 47 | 'error', 48 | 'You need to sign the message to be able to ban NFT items.' 49 | ); 50 | setBanning(false); 51 | return; 52 | } 53 | 54 | await banItems(address, tokenIDs, authToken, signature, addr); 55 | toast('success', 'Item banned successfully!'); 56 | } catch (e) { 57 | console.log(e); 58 | } 59 | setBanning(false); 60 | }; 61 | 62 | return ( 63 | : 'Ban'} 69 | onSubmit={!banning ? () => handleBanItem() : null} 70 | > 71 |
72 |
Contract Address
73 |
74 | setAddress(e.target.value)} 79 | disabled={banning} 80 | /> 81 |
82 |
83 |
84 |
Token ID
85 |
86 | setTokenIDs(e.target.value)} 91 | disabled={banning} 92 | /> 93 |
94 |
95 |
96 | ); 97 | }; 98 | 99 | export default BanItemModal; 100 | -------------------------------------------------------------------------------- /src/components/BanUserModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { ClipLoader } from 'react-spinners'; 4 | import { useWeb3React } from '@web3-react/core'; 5 | import { ethers } from 'ethers'; 6 | 7 | import toast from 'utils/toast'; 8 | import { useApi } from 'api'; 9 | import { getSigner } from 'contracts'; 10 | 11 | import Modal from '../Modal'; 12 | import styles from '../Modal/common.module.scss'; 13 | 14 | const BanUserModal = ({ visible, onClose, isForBanning }) => { 15 | const { getNonce, banUser, unbanUser } = useApi(); //unban user 16 | const { account } = useWeb3React(); 17 | 18 | const { authToken } = useSelector(state => state.ConnectWallet); 19 | 20 | const [banning, setBanning] = useState(false); 21 | const [address, setAddress] = useState(''); 22 | 23 | useEffect(() => { 24 | if (visible) { 25 | setBanning(false); 26 | } 27 | }, [visible]); 28 | 29 | const handleBanUser = async () => { 30 | if (banning) return; 31 | 32 | try { 33 | setBanning(true); 34 | 35 | const { data: nonce } = await getNonce(account, authToken); 36 | 37 | let signature; 38 | let addr; 39 | try { 40 | const signer = await getSigner(); 41 | const msg = `Approve Signature on Artion.io with nonce ${nonce}`; 42 | signature = await signer.signMessage(msg); 43 | addr = ethers.utils.verifyMessage(msg, signature); 44 | } catch (err) { 45 | toast( 46 | 'error', 47 | 'You need to sign the message to be able to ban/unban user.' 48 | ); 49 | setBanning(false); 50 | return; 51 | } 52 | 53 | let response = isForBanning 54 | ? await banUser(address, authToken, signature, addr) 55 | : await unbanUser(address, authToken, signature, addr); 56 | 57 | response.status == 'success' 58 | ? isForBanning 59 | ? toast('success', 'User banned successfully!') 60 | : toast('success', 'User unbanned successfully!') 61 | : isForBanning 62 | ? toast('error', response.data) 63 | : toast('error', response.data); 64 | 65 | setAddress(''); 66 | setBanning(false); 67 | } catch (e) { 68 | console.log(e); 69 | toast('error', 'Error occured while banning/unbanning a user!'); 70 | } 71 | setBanning(false); 72 | }; 73 | 74 | return ( 75 | 83 | ) : isForBanning ? ( 84 | 'Ban User' 85 | ) : ( 86 | 'Unban User' 87 | ) 88 | } 89 | onSubmit={!banning ? () => handleBanUser() : null} 90 | > 91 |
92 |
User Address
93 |
94 | setAddress(e.target.value)} 99 | disabled={banning} 100 | /> 101 |
102 |
103 |
104 | ); 105 | }; 106 | 107 | export default BanUserModal; 108 | -------------------------------------------------------------------------------- /src/components/BoostCollectionModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { ClipLoader } from 'react-spinners'; 4 | 5 | import toast from 'utils/toast'; 6 | import { useApi } from 'api'; 7 | 8 | import Modal from '../Modal'; 9 | import styles from '../Modal/common.module.scss'; 10 | 11 | const BoostCollectionModal = ({ visible, onClose }) => { 12 | const { boostCollection } = useApi(); 13 | const { authToken } = useSelector(state => state.ConnectWallet); 14 | 15 | const [boosting, setBoosting] = useState(false); 16 | const [address, setAddress] = useState(''); 17 | 18 | useEffect(() => { 19 | if (visible) { 20 | setBoosting(false); 21 | } 22 | }, [visible]); 23 | 24 | const handleBoostCollection = async () => { 25 | if (boosting) return; 26 | 27 | try { 28 | setBoosting(true); 29 | await boostCollection(address, authToken); 30 | toast('success', 'Collection boosted successfully!'); 31 | } catch (e) { 32 | console.log(e); 33 | } 34 | setBoosting(false); 35 | }; 36 | 37 | return ( 38 | : 'Boost'} 44 | onSubmit={!boosting ? () => handleBoostCollection() : null} 45 | > 46 |
47 |
Contract Address
48 |
49 | setAddress(e.target.value)} 54 | disabled={boosting} 55 | /> 56 |
57 |
58 |
59 | ); 60 | }; 61 | 62 | export default BoostCollectionModal; 63 | -------------------------------------------------------------------------------- /src/components/BootstrapTooltip/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import { Tooltip } from '@material-ui/core'; 4 | 5 | const useStylesBootstrap = makeStyles(theme => ({ 6 | arrow: { 7 | color: theme.palette.common.black, 8 | }, 9 | tooltip: { 10 | backgroundColor: theme.palette.common.black, 11 | padding: '8px 16px', 12 | fontSize: 14, 13 | }, 14 | })); 15 | 16 | function BootstrapTooltip(props) { 17 | const classes = useStylesBootstrap(); 18 | 19 | return ; 20 | } 21 | 22 | export default BootstrapTooltip; 23 | -------------------------------------------------------------------------------- /src/components/CategoriesFilter/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | 5 | import FilterWrapper from 'components/FilterWrapper'; 6 | import { Categories } from 'constants/filter.constants'; 7 | import FilterActions from 'actions/filter.actions'; 8 | 9 | import iconCheck from 'assets/svgs/check_blue.svg'; 10 | 11 | import './styles.scss'; 12 | 13 | const useStyles = makeStyles(() => ({ 14 | body: { 15 | display: 'flex', 16 | flexDirection: 'column', 17 | height: '100%', 18 | }, 19 | collectionsList: { 20 | overflowY: 'auto', 21 | maxHeight: 260, 22 | }, 23 | collection: { 24 | height: 40, 25 | padding: '0 8px', 26 | margin: '12px 0', 27 | boxSizing: 'border-box', 28 | display: 'flex', 29 | flexDirection: 'row', 30 | alignItems: 'center', 31 | cursor: 'pointer', 32 | '&:first-child': { 33 | marginTop: 0, 34 | }, 35 | '&:last-child': { 36 | marginBottom: 0, 37 | }, 38 | }, 39 | logo: { 40 | width: 40, 41 | height: 40, 42 | borderRadius: '50%', 43 | border: '1px solid #D9E1EE', 44 | marginRight: 14, 45 | display: 'flex', 46 | alignItems: 'center', 47 | justifyContent: 'center', 48 | 49 | '& img': { 50 | width: 24, 51 | height: 24, 52 | }, 53 | }, 54 | name: { 55 | fontWeight: 700, 56 | fontSize: 16, 57 | color: '#000', 58 | opacity: 0.6, 59 | marginRight: 4, 60 | }, 61 | })); 62 | 63 | const CategoriesFilter = () => { 64 | const dispatch = useDispatch(); 65 | 66 | const classes = useStyles(); 67 | 68 | const { category } = useSelector(state => state.Filter); 69 | 70 | const handleSelectCategory = categoryId => { 71 | dispatch( 72 | FilterActions.updateCategoryFilter( 73 | category === categoryId ? null : categoryId 74 | ) 75 | ); 76 | }; 77 | 78 | return ( 79 | 80 |
81 | {Categories.map(cat => ( 82 |
handleSelectCategory(cat.id)} 86 | > 87 |
88 | 89 |
90 | {cat.label} 91 |
92 | ))} 93 |
94 |
95 | ); 96 | }; 97 | 98 | export default CategoriesFilter; 99 | -------------------------------------------------------------------------------- /src/components/CategoriesFilter/styles.scss: -------------------------------------------------------------------------------- 1 | .filter-root { 2 | .MuiCollapse-container { 3 | display: flex; 4 | } 5 | 6 | .MuiCollapse-wrapper { 7 | width: 100%; 8 | max-height: 100%; 9 | 10 | div[role=region] { 11 | height: 100%; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/components/CollectionsFilter/styles.scss: -------------------------------------------------------------------------------- 1 | .filter-root { 2 | .MuiCollapse-container { 3 | display: flex; 4 | } 5 | 6 | .MuiCollapse-wrapper { 7 | width: 100%; 8 | max-height: 100%; 9 | 10 | div[role=region] { 11 | height: 100%; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/components/ConnectWalletModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .option { 2 | width: 100%; 3 | height: 48px; 4 | padding: 0 16px; 5 | border: 1px solid #EAEAF1; 6 | margin-bottom: 16px; 7 | border-radius: 6px; 8 | box-sizing: border-box; 9 | display: flex; 10 | align-items: center; 11 | justify-content: space-between; 12 | cursor: pointer; 13 | 14 | &:last-child { 15 | margin-bottom: 0; 16 | } 17 | 18 | &:hover { 19 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, .2); 20 | } 21 | 22 | &.active { 23 | border-color: #1969FF; 24 | } 25 | } 26 | 27 | .header { 28 | font-weight: 500; 29 | font-size: 16px; 30 | color: #121223; 31 | } 32 | 33 | .icon { 34 | height: 24px; 35 | } 36 | 37 | .text { 38 | font-size: 16px; 39 | color: #121223; 40 | } 41 | 42 | .switchBtn { 43 | margin-top: 16px; 44 | width: 100%; 45 | height: 48px; 46 | padding: 0 16px; 47 | border: 1px solid #EAEAF1; 48 | border-radius: 6px; 49 | box-sizing: border-box; 50 | display: flex; 51 | align-items: center; 52 | justify-content: center; 53 | cursor: pointer; 54 | 55 | &:hover { 56 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, .2); 57 | } 58 | } -------------------------------------------------------------------------------- /src/components/FilterWrapper/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import cx from 'classnames'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import Accordion from '@material-ui/core/Accordion'; 5 | import AccordionDetails from '@material-ui/core/AccordionDetails'; 6 | import AccordionSummary from '@material-ui/core/AccordionSummary'; 7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; 8 | 9 | const useStyles = makeStyles(() => ({ 10 | root: { 11 | width: '100%', 12 | }, 13 | wrapper: { 14 | marginTop: '-1px !important', 15 | marginRight: '-1px !important', 16 | boxShadow: 'none', 17 | borderRadius: '0 !important', 18 | border: '1px solid #D9E1EE', 19 | overflow: 'hidden', 20 | }, 21 | header: { 22 | height: 64, 23 | minHeight: '64px !important', 24 | backgroundColor: '#fff', 25 | boxShadow: 'none', 26 | padding: '0 24px', 27 | }, 28 | heading: { 29 | fontWeight: 700, 30 | fontSize: 16, 31 | flexShrink: 0, 32 | color: '#3D3D3D', 33 | }, 34 | arrowIcon: { 35 | color: '#3D3D3D', 36 | opacity: '0.6', 37 | }, 38 | body: { 39 | padding: '32px 24px', 40 | borderTop: '1px solid #D9E1EE', 41 | boxSizing: 'border-box', 42 | backgroundColor: '#D9E1EE1A', 43 | }, 44 | statusSvgDiv: { 45 | display: 'flex', 46 | alignItems: 'center', 47 | }, 48 | })); 49 | 50 | const FilterWrapper = ({ title, classes: classnames, children }) => { 51 | const classes = useStyles(); 52 | const [expanded, setExpanded] = useState(false); 53 | 54 | const handleAccordionCollapse = (_, isExpanded) => { 55 | setExpanded(isExpanded); 56 | }; 57 | 58 | return ( 59 |
60 | 65 | } 67 | className={classes.header} 68 | > 69 |
70 | {title} 71 |
72 |
73 | 74 | {children} 75 | 76 |
77 |
78 | ); 79 | }; 80 | 81 | export default FilterWrapper; 82 | -------------------------------------------------------------------------------- /src/components/FilterWrapper/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/components/FilterWrapper/styles.css -------------------------------------------------------------------------------- /src/components/FollowersModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Skeleton from 'react-loading-skeleton'; 4 | 5 | import { shortenAddress, formatFollowers } from 'utils'; 6 | import Identicon from 'components/Identicon'; 7 | 8 | import Modal from '../Modal'; 9 | import styles from './styles.module.scss'; 10 | 11 | const Follower = ({ user, onClose, children }) => { 12 | if (!user) return
{children}
; 13 | 14 | return ( 15 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | const FollowersModal = ({ visible, onClose, title, users }) => { 26 | return ( 27 | 28 | {users.map((user, idx) => ( 29 | 30 |
31 |
32 | {!user ? ( 33 | 34 | ) : user.imageHash ? ( 35 | 40 | ) : ( 41 | 46 | )} 47 |
48 |
49 |
50 | {user ? ( 51 | user.alias || 'Unnamed' 52 | ) : ( 53 | 54 | )} 55 |
56 |
57 | {user ? ( 58 | shortenAddress(user.address) 59 | ) : ( 60 | 61 | )} 62 |
63 |
64 |
65 |
66 | {user ? ( 67 | `${formatFollowers(user.followers)} follower${ 68 | user.followers !== 1 ? 's' : '' 69 | }` 70 | ) : ( 71 | 72 | )} 73 |
74 |
75 | ))} 76 |
77 | ); 78 | }; 79 | 80 | export default FollowersModal; 81 | -------------------------------------------------------------------------------- /src/components/FollowersModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .holder { 2 | height: 60px; 3 | box-sizing: border-box; 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 8 | cursor: pointer; 9 | text-decoration: none; 10 | } 11 | 12 | .holderInfo { 13 | display: flex; 14 | align-items: center; 15 | } 16 | 17 | .avatarWrapper { 18 | width: 40px; 19 | height: 40px; 20 | border-radius: 50%; 21 | overflow: hidden; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | box-sizing: border-box; 26 | position: relative; 27 | } 28 | 29 | .avatar { 30 | position: absolute; 31 | top: 0; 32 | left: 0; 33 | width: 100%; 34 | height: 100%; 35 | } 36 | 37 | .info { 38 | margin-left: 16px; 39 | display: flex; 40 | flex-direction: column; 41 | justify-content: center; 42 | } 43 | 44 | .alias { 45 | font-weight: 500; 46 | font-size: 18px; 47 | color: #3D3D3D; 48 | } 49 | 50 | .address { 51 | margin-top: 4px; 52 | font-weight: 400; 53 | font-size: 16px; 54 | color: rgba(0, 0, 0, .4); 55 | } 56 | 57 | .followers { 58 | font-size: 18px; 59 | color: rgba(0, 0, 0, .6); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Gasly/Header/styles.module.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | top: 0; 4 | left: 50%; 5 | padding: 32px; 6 | width: 100%; 7 | max-width: 1504px; 8 | transform: translateX(-50%); 9 | display: flex; 10 | align-items: flex-start; 11 | justify-content: space-between; 12 | z-index: 100; 13 | box-sizing: border-box; 14 | } 15 | 16 | .left { 17 | flex: 1; 18 | display: flex; 19 | align-items: center; 20 | margin-right: 20px; 21 | } 22 | 23 | .logo { 24 | height: 24px; 25 | cursor: pointer; 26 | 27 | img { 28 | height: 100%; 29 | } 30 | } 31 | 32 | .logoSmall { 33 | display: none; 34 | } 35 | 36 | .menu { 37 | display: flex; 38 | align-items: center; 39 | 40 | .searchcont { 41 | display: none; 42 | } 43 | } 44 | 45 | .menuLink { 46 | margin-right: 48px; 47 | text-decoration: none; 48 | color: #FFF; 49 | font-weight: 700; 50 | font-size: 18px; 51 | line-height: 18px; 52 | user-select: none; 53 | cursor: pointer; 54 | position: relative; 55 | 56 | &:last-child { 57 | margin-right: 0; 58 | } 59 | } 60 | 61 | .account { 62 | display: flex; 63 | align-items: center; 64 | } 65 | 66 | .avatar { 67 | width: 32px !important; 68 | height: 32px !important; 69 | border-radius: 50% !important; 70 | margin-right: 12px; 71 | overflow: hidden; 72 | } 73 | 74 | .profile { 75 | margin-right: 4px; 76 | } 77 | 78 | .address { 79 | font-weight: 700; 80 | font-size: 16px; 81 | line-height: 16px; 82 | } 83 | 84 | .network { 85 | margin-top: 4px; 86 | font-weight: 600; 87 | font-size: 14px; 88 | line-height: 14px; 89 | opacity: 0.6; 90 | } 91 | 92 | .account, 93 | .connect { 94 | box-sizing: border-box; 95 | padding: 8px 12px 11px 12px; 96 | border-radius: 50px; 97 | border: 1px solid #D9E1EE; 98 | 99 | &:hover { 100 | box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); 101 | } 102 | } 103 | 104 | .connect { 105 | height: 55px; 106 | display: flex; 107 | align-items: center; 108 | padding-left: 20px; 109 | padding-right: 20px; 110 | } 111 | 112 | .expand { 113 | transition: transform ease 200ms; 114 | 115 | &.expanded { 116 | transform: rotate(180deg); 117 | } 118 | } 119 | 120 | .profilemenu { 121 | width: 250px; 122 | transform: translateY(75px) !important; 123 | padding: 0; 124 | border-radius: 8px !important; 125 | } 126 | 127 | .menuList { 128 | padding: 20px 0 !important; 129 | width: 100%; 130 | } 131 | 132 | .signOut { 133 | width: calc(100% - 40px); 134 | height: 48px; 135 | border-radius: 10px; 136 | margin: 0 auto; 137 | background-color: #1969FF; 138 | display: flex; 139 | align-items: center; 140 | justify-content: center; 141 | font-size: 16px; 142 | font-weight: 700; 143 | letter-spacing: -0.02em; 144 | color: #FFF; 145 | cursor: pointer; 146 | 147 | &:hover { 148 | box-shadow: 0 0 8px 4px rgba(0, 0, 0, .1); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/components/Gasly/Home/gasly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/components/Gasly/Home/gasly.png -------------------------------------------------------------------------------- /src/components/Gasly/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Header from '../Header'; 4 | 5 | import avatarBg from 'assets/imgs/avatar_bg.png'; 6 | import avatar from 'assets/imgs/gasly.png'; 7 | import sign from 'assets/imgs/sign.png'; 8 | import logo from 'assets/imgs/logo.png'; 9 | import textBg1 from 'assets/imgs/text_bg1.png'; 10 | 11 | import styles from './styles.module.scss'; 12 | 13 | const GaslyHomePage = () => { 14 | return ( 15 |
16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 | A unique limited edition NFT drop redeemable for an original{' '} 31 | Pierre Gasly helmet and 2 “ 32 | Golden tickets” redeemable for collectible physical 33 | items. 34 |
35 |
36 |
37 |
38 |
39 | ); 40 | }; 41 | 42 | export default GaslyHomePage; 43 | -------------------------------------------------------------------------------- /src/components/Gasly/Home/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | background-color: #090C10; 4 | color: #EAEAF1; 5 | } 6 | 7 | .main { 8 | width: 100%; 9 | max-width: 1440px; 10 | margin: 0 auto; 11 | padding-top: 120px; 12 | } 13 | 14 | .top { 15 | display: flex; 16 | position: relative; 17 | } 18 | 19 | .avatarBox { 20 | width: 47%; 21 | position: relative; 22 | } 23 | 24 | .avatarBg { 25 | width: 100%; 26 | } 27 | 28 | .avatar { 29 | position: absolute; 30 | width: 90%; 31 | bottom: 0; 32 | left: 50%; 33 | transform: translateX(-50%); 34 | } 35 | 36 | .sign { 37 | position: absolute; 38 | top: 100px; 39 | right: 40px; 40 | width: 60%; 41 | } 42 | 43 | .logo { 44 | position: absolute; 45 | bottom: 60px; 46 | right: 25%; 47 | transform: translateX(50%); 48 | width: 30%; 49 | } 50 | 51 | .info { 52 | position: relative; 53 | width: 100%; 54 | } 55 | 56 | .textBg1 { 57 | width: 100%; 58 | } 59 | 60 | .infoContent { 61 | position: absolute; 62 | top: 0; 63 | left: 0; 64 | width: 100%; 65 | height: 100%; 66 | display: flex; 67 | flex-direction: column; 68 | align-items: center; 69 | justify-content: center; 70 | } 71 | 72 | .infoText { 73 | font-family: 'Inter' !important; 74 | font-size: 36px; 75 | font-weight: 600; 76 | line-height: 43.57px; 77 | letter-spacing: -0.015em; 78 | text-align: center; 79 | color: #FFF; 80 | width: 75%; 81 | 82 | span { 83 | font-family: 'Inter' !important; 84 | font-weight: 800; 85 | color: #3D80FF; 86 | } 87 | } 88 | 89 | @media only screen and (max-width: 1000px) { 90 | 91 | .sign { 92 | top: 60px; 93 | } 94 | 95 | .logo { 96 | bottom: 40px; 97 | } 98 | } 99 | 100 | @media only screen and (max-width: 768px) { 101 | 102 | .sign { 103 | top: 30px; 104 | } 105 | 106 | .logo { 107 | bottom: 20px; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/components/Identicon.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import Jazzicon from 'jazzicon'; 3 | 4 | const Identicon = ({ account, size, ...rest }) => { 5 | const ref = useRef(); 6 | 7 | useEffect(() => { 8 | if (account && ref.current) { 9 | ref.current.innerHTML = ''; 10 | ref.current.appendChild( 11 | Jazzicon(size || 16, parseInt(account.slice(2, 10), 16)) 12 | ); 13 | } 14 | }, [account]); 15 | 16 | return
; 17 | }; 18 | 19 | export default Identicon; 20 | -------------------------------------------------------------------------------- /src/components/InfiniteLoader/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import faker from 'faker'; 3 | // import { FixedSizeList as List } from 'react-window'; 4 | import { FixedSizeGrid as Grid } from 'react-window'; 5 | 6 | import InfiniteLoader from 'react-window-infinite-loader'; 7 | import AutoSizer from 'react-virtualized-auto-sizer'; 8 | import { makeStyles } from '@material-ui/core/styles'; 9 | import Container from '@material-ui/core/Container'; 10 | import NFTCard from '../NFTCard'; 11 | 12 | const useStyles = makeStyles({ 13 | container: { 14 | position: 'relative', 15 | }, 16 | }); 17 | 18 | const ListContainer = props => { 19 | const classes = useStyles(); 20 | return ; 21 | }; 22 | 23 | const FInfiniteLoader = () => { 24 | const [data, setData] = useState([]); 25 | 26 | if (data.length === 0) { 27 | setData(Array.from({ length: 500 }).map(() => null)); 28 | } 29 | 30 | const isItemLoaded = index => index < data.length && data[index] !== null; 31 | const loadMoreItems = (startIndex, stopIndex) => { 32 | return new Promise(resolve => { 33 | setTimeout(() => { 34 | const newData = [...data]; 35 | for (let idx = startIndex; idx < stopIndex; idx++) { 36 | newData[idx] = faker.lorem.sentence(); 37 | } 38 | setData(newData); 39 | resolve(); 40 | }, 2000); 41 | }); 42 | }; 43 | 44 | if (ListContainer) console.log(); 45 | 46 | const resizeLoaderOnScreenChange = width => { 47 | if (width >= 1200) return 5; 48 | else if (width >= 1000 && width < 1200) return 4; 49 | else if (width >= 600 && width < 1000) return 3; 50 | else if (width > 480 && width < 600) return 2; 51 | else return 1; 52 | }; 53 | 54 | return ( 55 | 56 | {({ height, width }) => ( 57 | 62 | {({ onItemsRendered, ref }) => ( 63 | 75 | {NFTCard} 76 | 77 | )} 78 | 79 | )} 80 | 81 | ); 82 | }; 83 | 84 | export default FInfiniteLoader; 85 | -------------------------------------------------------------------------------- /src/components/InfiniteLoader/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/components/InfiniteLoader/styles.css -------------------------------------------------------------------------------- /src/components/InputError/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const InputError = ({ text }) => { 4 | return
{text}
; 5 | }; 6 | 7 | export default InputError; 8 | -------------------------------------------------------------------------------- /src/components/LikesModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Skeleton from 'react-loading-skeleton'; 4 | 5 | import { shortenAddress } from 'utils'; 6 | import Identicon from 'components/Identicon'; 7 | 8 | import Modal from '../Modal'; 9 | import styles from './styles.module.scss'; 10 | 11 | const User = ({ user, onClose, children }) => { 12 | if (!user) return
{children}
; 13 | 14 | return ( 15 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | const LikesModal = ({ visible, onClose, users }) => { 26 | return ( 27 | 28 | {users.map((user, idx) => ( 29 | 30 |
31 |
32 | {!user ? ( 33 | 34 | ) : user.imageHash ? ( 35 | 40 | ) : ( 41 | 46 | )} 47 |
48 |
49 |
50 | {user ? ( 51 | user.alias || 'Unnamed' 52 | ) : ( 53 | 54 | )} 55 |
56 |
57 | {user ? ( 58 | shortenAddress(user.address) 59 | ) : ( 60 | 61 | )} 62 |
63 |
64 |
65 |
66 | ))} 67 |
68 | ); 69 | }; 70 | 71 | export default LikesModal; 72 | -------------------------------------------------------------------------------- /src/components/LikesModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .holder { 2 | height: 60px; 3 | box-sizing: border-box; 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 8 | cursor: pointer; 9 | text-decoration: none; 10 | } 11 | 12 | .holderInfo { 13 | display: flex; 14 | align-items: center; 15 | } 16 | 17 | .avatarWrapper { 18 | width: 40px; 19 | height: 40px; 20 | border-radius: 50%; 21 | overflow: hidden; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | box-sizing: border-box; 26 | position: relative; 27 | } 28 | 29 | .avatar { 30 | position: absolute; 31 | top: 0; 32 | left: 0; 33 | width: 100%; 34 | height: 100%; 35 | } 36 | 37 | .info { 38 | margin-left: 16px; 39 | display: flex; 40 | flex-direction: column; 41 | justify-content: center; 42 | } 43 | 44 | .alias { 45 | font-weight: 500; 46 | font-size: 18px; 47 | color: #3D3D3D; 48 | } 49 | 50 | .address { 51 | margin-top: 4px; 52 | font-weight: 400; 53 | font-size: 16px; 54 | color: rgba(0, 0, 0, .4); 55 | } 56 | -------------------------------------------------------------------------------- /src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/react-in-jsx-scope */ 2 | /** 3 | * Takes in custom size and stroke for circle color, default to primary color as fill, 4 | * need ...rest for layered styles on top 5 | */ 6 | export default function Loader({ size = '16px', stroke = '#FFFFFF', ...rest }) { 7 | return ( 8 | 16 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Modal/common.module.scss: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | margin-bottom: 20px; 3 | 4 | &:last-child { 5 | margin-bottom: 0; 6 | } 7 | } 8 | 9 | .formLabel { 10 | font-weight: 400; 11 | font-size: 18px; 12 | letter-spacing: -0.2px; 13 | color: #121223; 14 | margin-bottom: 11px; 15 | margin-left: 10px; 16 | display: flex; 17 | align-items: center; 18 | } 19 | 20 | .formInputCont { 21 | height: 50px; 22 | box-sizing: border-box; 23 | display: flex; 24 | align-items: center; 25 | border-radius: 10px; 26 | border: 1px solid #EAEAF1; 27 | 28 | &.focused { 29 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); 30 | } 31 | } 32 | 33 | .formGroupDates { 34 | display: grid; 35 | grid-template-columns: 1fr 1fr; 36 | grid-gap: 10px; 37 | } 38 | 39 | .formInput { 40 | width: 100%; 41 | height: 50px; 42 | padding: 8px 16px; 43 | box-sizing: border-box; 44 | outline: none; 45 | border: none; 46 | background-color: transparent; 47 | font-size: 16px; 48 | } 49 | 50 | .usdPrice { 51 | min-width: 130px; 52 | height: 50px; 53 | border-left: 1px solid #EAEAF1; 54 | display: flex; 55 | align-items: center; 56 | justify-content: center; 57 | font-weight: 500; 58 | font-size: 16px; 59 | color: rgba(0, 0, 0, .4); 60 | } 61 | 62 | .select { 63 | margin: 2px; 64 | width: 140px !important; 65 | outline: none !important; 66 | height: 44px !important; 67 | border-radius: 8px !important; 68 | border: none !important; 69 | box-sizing: border-box !important; 70 | font-size: 16px !important; 71 | padding-right: 12px !important; 72 | background-color: #FAFAFB; 73 | box-shadow: none !important; 74 | } 75 | 76 | .selectedToken, 77 | .token { 78 | height: 44px; 79 | display: flex; 80 | align-items: center; 81 | padding: 5px 10px; 82 | box-sizing: border-box; 83 | } 84 | 85 | .token { 86 | width: 138px; 87 | padding: 5px 15px; 88 | 89 | &:hover { 90 | background-color: #EAEAF1; 91 | } 92 | } 93 | 94 | .tokenIcon { 95 | width: 20px; 96 | height: 20px; 97 | border-radius: 50%; 98 | } 99 | 100 | .tokenSymbol { 101 | margin-left: 8px; 102 | font-weight: 700; 103 | font-size: 16px; 104 | letter-spacing: -0.2px; 105 | color: #121223; 106 | } 107 | 108 | @media only screen and (max-width: 600px) { 109 | .formGroupDates { 110 | display: block; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/components/Modal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import closeIcon from 'assets/svgs/close.svg'; 5 | 6 | import styles from './styles.module.scss'; 7 | 8 | const Modal = ({ 9 | visible, 10 | title, 11 | onClose, 12 | children, 13 | submitDisabled, 14 | submitLabel, 15 | onSubmit, 16 | small, 17 | }) => { 18 | const handleClick = e => { 19 | e.preventDefault(); 20 | e.stopPropagation(); 21 | }; 22 | 23 | return ( 24 |
25 |
29 |
30 |
{title}
31 |
32 | 33 |
34 |
35 |
{children}
36 | {submitLabel && ( 37 |
38 |
45 | {submitLabel} 46 |
47 |
48 | )} 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default Modal; 55 | -------------------------------------------------------------------------------- /src/components/Modal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: none; 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | background-color: rgba(0, 0, 0, 0.4); 9 | z-index: 999; 10 | align-items: center; 11 | justify-content: center; 12 | 13 | &.visible { 14 | display: flex; 15 | } 16 | } 17 | 18 | .modal { 19 | max-width: 90%; 20 | width: 600px; 21 | box-sizing: border-box; 22 | border-radius: 10px; 23 | background-color: #FFF; 24 | box-shadow: 0 0 10px rgba(255, 255, 255, 0.4); 25 | position: relative; 26 | } 27 | 28 | .small { 29 | width: 400px; 30 | } 31 | 32 | .header { 33 | height: 70px; 34 | border-bottom: 1px solid #EAEAF1; 35 | display: flex; 36 | align-items: center; 37 | justify-content: space-between; 38 | padding: 0 24px; 39 | } 40 | 41 | .body { 42 | padding: 40px 24px; 43 | } 44 | 45 | .title { 46 | font-weight: 700; 47 | font-size: 18px; 48 | color: #121223; 49 | } 50 | 51 | .closeButton { 52 | width: 16px; 53 | height: 16px; 54 | cursor: pointer; 55 | 56 | img { 57 | width: 16px; 58 | height: 16px; 59 | } 60 | } 61 | 62 | .footer { 63 | margin-bottom: 32px; 64 | display: flex; 65 | align-items: center; 66 | justify-content: space-evenly; 67 | } 68 | 69 | .submitButton { 70 | min-width: 168px; 71 | height: 48px; 72 | box-sizing: border-box; 73 | padding: 0 12px; 74 | border-radius: 8px; 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | cursor: pointer; 79 | font-size: 18px; 80 | font-weight: 700; 81 | letter-spacing: 0.5px; 82 | user-select: none; 83 | 84 | &:hover { 85 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 86 | } 87 | } 88 | 89 | .submitButton { 90 | background-color: #007BFF; 91 | color: #FFF; 92 | } 93 | 94 | .disabled { 95 | cursor: not-allowed; 96 | box-shadow: none !important; 97 | opacity: 0.7; 98 | } 99 | 100 | @media only screen and (max-width: 600px) { 101 | .modal { 102 | padding: 40px 20px; 103 | } 104 | 105 | .title { 106 | font-size: 20px; 107 | } 108 | 109 | .footer { 110 | flex-direction: column; 111 | } 112 | 113 | .listButton { 114 | width: calc(100% - 48px); 115 | } 116 | 117 | .listButton { 118 | margin-right: 0; 119 | margin-bottom: 20px; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/components/NFTsGrid/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../NFTCard'; 5 | 6 | import styles from './styles.module.scss'; 7 | import { Categories } from '../../constants/filter.constants'; 8 | import { useDispatch } from 'react-redux'; 9 | import FilterActions from '../../actions/filter.actions'; 10 | 11 | const NFTsGrid = ({ 12 | items, 13 | numPerRow, 14 | uploading, 15 | loading, 16 | showCreate, 17 | category, 18 | onCreate = () => {}, 19 | onLike = () => {}, 20 | }) => { 21 | const dispatch = useDispatch(); 22 | const n = numPerRow || 6; 23 | const className = cx(styles.nft, styles[`num${n}`]); 24 | return ( 25 | <> 26 | {showCreate && ( 27 |
28 | 29 |
30 | )} 31 | {uploading && 32 | new Array(n * 2).fill(0).map((_, idx) => ( 33 |
34 | 35 |
36 | ))} 37 | {(items || []).map(item => ( 38 |
44 | 45 |
46 | ))} 47 | {loading && 48 | new Array(n * 2).fill(0).map((_, idx) => ( 49 |
50 | 51 |
52 | ))} 53 | {!items.length && category !== null && category !== undefined && ( 54 | <> 55 |
56 | No results found for the {Categories[category].label} category. 57 |
58 |
dispatch(FilterActions.updateCategoryFilter(null))} 60 | className={styles.link} 61 | > 62 | Select all categories 63 |
64 | 65 | )} 66 | 67 | ); 68 | }; 69 | 70 | export default NFTsGrid; 71 | -------------------------------------------------------------------------------- /src/components/NFTsGrid/styles.module.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | margin-left: auto; 3 | margin-right: auto; 4 | margin-bottom: 20px; 5 | width: 320px; 6 | height: 48px; 7 | border-radius: 8px; 8 | font-weight: 700; 9 | font-size: 18px; 10 | background-color: #1969FF; 11 | color: #FFF; 12 | box-shadow: none; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | cursor: pointer; 17 | } 18 | 19 | .link { 20 | color: #1969FF; 21 | cursor: pointer; 22 | padding: 0 5px; 23 | } 24 | 25 | .container { 26 | width: 100%; 27 | box-sizing: border-box; 28 | } 29 | 30 | .grid { 31 | width: 100%; 32 | display: grid; 33 | grid-template-columns: repeat( auto-fill, minmax(220px, 1fr) ); 34 | grid-gap: 24px; 35 | box-sizing: border-box; 36 | padding-bottom: 32px; 37 | padding: 4px; 38 | } 39 | 40 | .nft { 41 | margin: 12px; 42 | } 43 | 44 | .num1 { 45 | width: calc(100% - 24px); 46 | flex-basis: calc(100% - 24px); 47 | } 48 | 49 | .num2 { 50 | width: calc(50% - 24px); 51 | flex-basis: calc(50% - 24px); 52 | } 53 | 54 | .num3 { 55 | width: calc(33.33% - 24px); 56 | flex-basis: calc(33.33% - 24px); 57 | } 58 | 59 | .num4 { 60 | width: calc(25% - 24px); 61 | flex-basis: calc(25% - 24px); 62 | } 63 | 64 | .num5 { 65 | width: calc(20% - 24px); 66 | flex-basis: calc(20% - 24px); 67 | } 68 | 69 | .num6 { 70 | width: calc(16.66% - 24px); 71 | flex-basis: calc(16.66% - 24px); 72 | } 73 | 74 | .num7 { 75 | width: calc(14.28% - 24px); 76 | flex-basis: calc(14.28% - 24px); 77 | } 78 | 79 | .num8 { 80 | width: calc(12.5% - 24px); 81 | flex-basis: calc(12.5% - 24px); 82 | } 83 | 84 | .num9 { 85 | width: calc(11.11% - 24px); 86 | flex-basis: calc(11.11% - 24px); 87 | } 88 | 89 | .num10 { 90 | width: calc(10% - 24px); 91 | flex-basis: calc(10% - 24px); 92 | } 93 | 94 | .num11 { 95 | width: calc(9.09% - 24px); 96 | flex-basis: calc(9.09% - 24px); 97 | } 98 | 99 | .num12, 100 | .num13, 101 | .num14, 102 | .num15, 103 | .num16, 104 | .num17, 105 | .num18, 106 | .num19, 107 | .num20, 108 | .num21, 109 | .num22, 110 | .num23, 111 | .num24, 112 | .num25, 113 | .num26, 114 | .num27, 115 | .num28, 116 | .num29, 117 | .num30 { 118 | width: calc(8.33% - 24px); 119 | flex-basis: calc(8.33% - 24px); 120 | } 121 | -------------------------------------------------------------------------------- /src/components/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | import HeaderActions from 'actions/header.actions'; 6 | import Header from 'components/header'; 7 | 8 | import man from 'assets/imgs/404_man.png'; 9 | 10 | import styles from './styles.module.scss'; 11 | 12 | const NotFound = () => { 13 | const dispatch = useDispatch(); 14 | 15 | useEffect(() => { 16 | dispatch(HeaderActions.toggleSearchbar(false)); 17 | }, []); 18 | 19 | return ( 20 |
21 |
22 |
23 |
24 |
404
25 |
26 | Oooooops! We couldn’t find the page you’re looking for :( 27 |
28 | 29 | Back To Home 30 | 31 |
32 | man 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default NotFound; 39 | -------------------------------------------------------------------------------- /src/components/NotFound/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: absolute; 3 | width: 100%; 4 | height: 100vh; 5 | top: 0; 6 | padding-top: 80px; 7 | box-sizing: border-box; 8 | } 9 | 10 | .body { 11 | width: 1200px; 12 | max-width: 100%; 13 | padding-left: 40px; 14 | padding-right: 40px; 15 | box-sizing: border-box; 16 | margin-left: auto; 17 | margin-right: auto; 18 | display: flex; 19 | align-items: center; 20 | justify-content: space-between; 21 | padding-top: 80px; 22 | z-index: 1; 23 | } 24 | 25 | .main { 26 | margin-bottom: 120px; 27 | } 28 | 29 | .title { 30 | font-weight: 700; 31 | font-size: 250px; 32 | line-height: 304px; 33 | letter-spacing: 10px; 34 | color: #121223; 35 | } 36 | 37 | .subtitle { 38 | margin-top: -25px; 39 | font-weight: 700; 40 | font-size: 18px; 41 | line-height: 22px; 42 | letter-spacing: -0.2px; 43 | color: #A2A2AD; 44 | } 45 | 46 | .button { 47 | margin-top: 36px; 48 | width: 188px; 49 | height: 64px; 50 | display: flex; 51 | align-items: center; 52 | justify-content: center; 53 | font-weight: 700; 54 | font-size: 18px; 55 | letter-spacing: -0.228571px; 56 | box-shadow: none; 57 | border-radius: 11px; 58 | box-sizing: border-box; 59 | cursor: pointer; 60 | text-decoration: none; 61 | background-color: #1969FF; 62 | color: #FFF; 63 | 64 | &:hover { 65 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 66 | } 67 | } 68 | 69 | @media only screen and (max-width: 1100px) { 70 | .main { 71 | margin-right: -80px; 72 | } 73 | 74 | .man { 75 | margin-right: -30px; 76 | } 77 | 78 | .title { 79 | font-size: 160px; 80 | line-height: 170px; 81 | } 82 | 83 | .subtitle { 84 | font-size: 22px; 85 | line-height: 35px; 86 | margin-top: 8px; 87 | } 88 | } 89 | 90 | @media only screen and (max-width: 810px) { 91 | .body { 92 | flex-direction: column-reverse; 93 | } 94 | 95 | .main { 96 | margin: 0; 97 | display: flex; 98 | flex-direction: column; 99 | align-items: center; 100 | } 101 | 102 | .title { 103 | font-size: 144px; 104 | } 105 | 106 | .subtitle { 107 | font-size: 18px; 108 | line-height: 22px; 109 | text-align: center; 110 | } 111 | 112 | .button { 113 | width: 168px; 114 | height: 48px; 115 | font-size: 16px; 116 | } 117 | 118 | .man { 119 | width: 60%; 120 | min-width: 300px; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/components/OwnersModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import { shortenAddress, formatNumber } from 'utils'; 5 | import Identicon from 'components/Identicon'; 6 | 7 | import Modal from '../Modal'; 8 | import styles from './styles.module.scss'; 9 | 10 | const Holder = ({ holder, children }) => { 11 | if (!holder) return
{children}
; 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | const OwnersModal = ({ visible, onClose, holders }) => { 21 | return ( 22 | 23 | {holders.map((holder, idx) => ( 24 | 25 |
26 |
27 | {holder.imageHash ? ( 28 | 33 | ) : ( 34 | 39 | )} 40 |
41 |
42 |
{holder.alias || 'Unnamed'}
43 |
44 | {shortenAddress(holder.address)} 45 |
46 |
47 |
48 |
49 | {`${formatNumber(holder.supply)} item${ 50 | holder.supply !== 1 ? 's' : '' 51 | }`} 52 |
53 |
54 | ))} 55 |
56 | ); 57 | }; 58 | 59 | export default OwnersModal; 60 | -------------------------------------------------------------------------------- /src/components/OwnersModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .holder { 2 | height: 60px; 3 | box-sizing: border-box; 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-between; 7 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 8 | cursor: pointer; 9 | text-decoration: none; 10 | } 11 | 12 | .holderInfo { 13 | display: flex; 14 | align-items: center; 15 | } 16 | 17 | .avatarWrapper { 18 | width: 40px; 19 | height: 40px; 20 | border-radius: 50%; 21 | overflow: hidden; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | box-sizing: border-box; 26 | position: relative; 27 | } 28 | 29 | .avatar { 30 | position: absolute; 31 | top: 0; 32 | left: 0; 33 | width: 100%; 34 | height: 100%; 35 | } 36 | 37 | .info { 38 | margin-left: 16px; 39 | display: flex; 40 | flex-direction: column; 41 | justify-content: center; 42 | } 43 | 44 | .alias { 45 | font-weight: 500; 46 | font-size: 18px; 47 | color: #3D3D3D; 48 | } 49 | 50 | .address { 51 | margin-top: 4px; 52 | font-weight: 400; 53 | font-size: 16px; 54 | color: rgba(0, 0, 0, .4); 55 | } 56 | 57 | .holdCount { 58 | font-size: 18px; 59 | color: rgba(0, 0, 0, .6); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Panel/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import cx from 'classnames'; 3 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; 4 | import ExpandLessIcon from '@material-ui/icons/ExpandLess'; 5 | 6 | import styles from './styles.module.scss'; 7 | 8 | const Panel = ({ title, icon, expanded, fixed, responsive, children }) => { 9 | const [open, setOpen] = useState(!!expanded || !!fixed); 10 | 11 | const handleOpen = () => { 12 | if (!fixed) { 13 | setOpen(!open); 14 | } 15 | }; 16 | 17 | const Icon = icon; 18 | 19 | return ( 20 |
21 |
22 |
23 | {icon && } 24 | {title} 25 |
26 | {!fixed && 27 | (open ? ( 28 | 29 | ) : ( 30 | 31 | ))} 32 |
33 |
40 | {children} 41 |
42 |
43 | ); 44 | }; 45 | 46 | export default Panel; 47 | -------------------------------------------------------------------------------- /src/components/Panel/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-bottom: -1px; 3 | margin-top: 1px; 4 | border-bottom: 1px solid #EAEAF1; 5 | overflow: hidden; 6 | } 7 | 8 | .header { 9 | width: 100%; 10 | height: 60px; 11 | padding: 12px 24px; 12 | box-sizing: border-box; 13 | display: flex; 14 | align-items: center; 15 | justify-content: space-between; 16 | cursor: pointer; 17 | } 18 | 19 | .titleWrapper { 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .titleIcon { 25 | margin-right: 16px; 26 | width: 20px; 27 | height: 20px; 28 | color: #121223; 29 | } 30 | 31 | .title { 32 | font-size: 16px; 33 | font-weight: 700; 34 | color: #121223; 35 | } 36 | 37 | .icon { 38 | color: #A2A2AD; 39 | } 40 | 41 | .body { 42 | border-top: 1px solid #EAEAF1; 43 | max-height: 0; 44 | transition: max-height 100ms ease; 45 | 46 | &.open { 47 | max-height: 500px; 48 | } 49 | 50 | &.responsive.open { 51 | max-height: 800px; 52 | } 53 | 54 | &:not(.open) { 55 | border-top: none; 56 | } 57 | } 58 | 59 | @media only screen and (max-width: 600px) { 60 | .body { 61 | overflow-x: auto; 62 | overflow-y: hidden; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/components/PriceInput.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { PriceConstants } from '../constants/price.constants'; 3 | 4 | export const PriceInputErrors = { 5 | MAX_INPUT_EXCEEDED: 'Max input value exceeded', 6 | }; 7 | 8 | const PriceInput = ({ 9 | value, 10 | decimals = 0, 11 | onChange, 12 | onInputError = err => console.log(err), 13 | max = PriceConstants.MAX_PRICE, 14 | ...rest 15 | }) => { 16 | useEffect(() => { 17 | onChange(checkDecimals(value)); 18 | }, [decimals]); 19 | 20 | useEffect(() => { 21 | if (parseInt(value) > parseInt(max)) { 22 | onInputError(PriceInputErrors.MAX_INPUT_EXCEEDED); 23 | } else { 24 | onInputError(null); 25 | } 26 | }, [value]); 27 | 28 | const handleKeyDown = e => { 29 | const key = e.keyCode; 30 | if (key >= '0'.charCodeAt(0) && key <= '9'.charCodeAt(0)) { 31 | if (value === '0' && key === '0'.charCodeAt(0)) e.preventDefault(); 32 | } else if (key === 190) { 33 | if (value.length === 0 || value.includes('.') || decimals === 0) 34 | e.preventDefault(); 35 | } else if (key !== 8) { 36 | e.preventDefault(); 37 | } 38 | }; 39 | 40 | const checkDecimals = val => { 41 | if (!val) return ''; 42 | if (val.indexOf('.') > -1 && val.length - val.indexOf('.') - 1 > decimals) { 43 | const ret = Math.floor(+val * 10 ** decimals) / 10 ** decimals; 44 | return ret.toString(); 45 | } 46 | return val; 47 | }; 48 | 49 | const handleChange = e => { 50 | onChange(checkDecimals(e.target.value)); 51 | }; 52 | 53 | return ( 54 | 60 | ); 61 | }; 62 | 63 | export default PriceInput; 64 | -------------------------------------------------------------------------------- /src/components/ProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Redirect, Route } from 'react-router-dom'; 3 | 4 | function ProtectedRoute({ component: Component, ...restOfProps }) { 5 | return ( 6 | 9 | window.ethereum ? : 10 | } 11 | /> 12 | ); 13 | } 14 | 15 | export default ProtectedRoute; 16 | -------------------------------------------------------------------------------- /src/components/StatusFilter/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | 6 | import FilterActions from 'actions/filter.actions'; 7 | import FilterWrapper from 'components/FilterWrapper'; 8 | 9 | const useStyles = makeStyles(() => ({ 10 | body: { 11 | display: 'grid', 12 | gridTemplateColumns: '1fr 1fr', 13 | gridGap: '8px', 14 | }, 15 | formControl: { 16 | width: '100%', 17 | height: 40, 18 | boxSizing: 'border-box', 19 | borderRadius: 10, 20 | border: '1px solid #A2A2AD', 21 | cursor: 'pointer', 22 | margin: '0 !important', 23 | display: 'flex', 24 | alignItems: 'center', 25 | justifyContent: 'center', 26 | fontSize: 14, 27 | fontWeight: '400', 28 | color: '#121223', 29 | backgroundColor: '#FFF', 30 | }, 31 | selected: { 32 | backgroundColor: '#1969FF', 33 | color: '#FFF', 34 | fontWeight: 700, 35 | border: 0, 36 | }, 37 | })); 38 | 39 | const ExploreStatus = () => { 40 | const dispatch = useDispatch(); 41 | const classes = useStyles(); 42 | 43 | const { 44 | statusBuyNow, 45 | statusHasBids, 46 | statusHasOffers, 47 | statusOnAuction, 48 | } = useSelector(state => state.Filter); 49 | 50 | const handleStatusChange = (field, selected) => { 51 | dispatch(FilterActions.updateStatusFilter(field, selected)); 52 | }; 53 | 54 | return ( 55 | 56 |
handleStatusChange('statusBuyNow', !statusBuyNow)} 62 | > 63 | Buy Now 64 |
65 |
handleStatusChange('statusHasBids', !statusHasBids)} 71 | > 72 | Has Bids 73 |
74 |
handleStatusChange('statusHasOffers', !statusHasOffers)} 80 | > 81 | Has Offers 82 |
83 |
handleStatusChange('statusOnAuction', !statusOnAuction)} 89 | > 90 | On Auction 91 |
92 |
93 | ); 94 | }; 95 | 96 | export default ExploreStatus; 97 | -------------------------------------------------------------------------------- /src/components/StatusFilter/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/components/StatusFilter/styles.css -------------------------------------------------------------------------------- /src/components/SuspenseImg.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const imgCache = { 4 | __cache: {}, 5 | read(src) { 6 | if (!src) { 7 | return; 8 | } 9 | 10 | if (!this.__cache[src]) { 11 | this.__cache[src] = new Promise(resolve => { 12 | const img = new Image(); 13 | img.onload = () => { 14 | this.__cache[src] = true; 15 | resolve(this.__cache[src]); 16 | }; 17 | img.src = src; 18 | setTimeout(() => resolve({}), 7000); 19 | }).then(() => { 20 | this.__cache[src] = true; 21 | }); 22 | } 23 | 24 | if (this.__cache[src] instanceof Promise) { 25 | throw this.__cache[src]; 26 | } 27 | return this.__cache[src]; 28 | }, 29 | clearImg: src => { 30 | delete this.__cache[src]; 31 | }, 32 | }; 33 | 34 | const SuspenseImg = ({ src, ...rest }) => { 35 | imgCache.read(src); 36 | 37 | return ; 38 | }; 39 | 40 | export default SuspenseImg; 41 | -------------------------------------------------------------------------------- /src/components/TransferModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { ClipLoader } from 'react-spinners'; 3 | 4 | import Modal from '../Modal'; 5 | import styles from '../Modal/common.module.scss'; 6 | 7 | const TransferModal = ({ 8 | visible, 9 | totalSupply, 10 | transferring, 11 | onTransfer, 12 | onClose, 13 | }) => { 14 | const [address, setAddress] = useState(''); 15 | const [quantity, setQuantity] = useState('1'); 16 | 17 | useEffect(() => { 18 | if (visible) { 19 | setAddress(''); 20 | setQuantity('1'); 21 | } 22 | }, [visible]); 23 | 24 | const handleQuantityChange = e => { 25 | const val = e.target.value; 26 | if (!val) { 27 | setQuantity(''); 28 | return; 29 | } 30 | 31 | if (isNaN(val)) return; 32 | 33 | const _quantity = parseInt(val); 34 | setQuantity(Math.min(_quantity, totalSupply)); 35 | }; 36 | 37 | const handleTransfer = () => { 38 | let quant = 1; 39 | if (totalSupply > 1) { 40 | quant = parseInt(quantity); 41 | } 42 | onTransfer(address, quant); 43 | }; 44 | 45 | return ( 46 | : 'Transfer' 53 | } 54 | onSubmit={!transferring ? () => handleTransfer() : null} 55 | > 56 |
57 |
Transfer to
58 |
59 | setAddress(e.target.value)} 64 | disabled={transferring} 65 | /> 66 |
67 |
68 | {totalSupply !== null && ( 69 |
70 |
Quantity
71 |
72 | 79 |
80 |
81 | )} 82 |
83 | ); 84 | }; 85 | 86 | export default TransferModal; 87 | -------------------------------------------------------------------------------- /src/components/TxButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useWeb3React } from '@web3-react/core'; 3 | import { useDispatch } from 'react-redux'; 4 | 5 | import ModalActions from 'actions/modal.actions'; 6 | 7 | const TxButton = ({ onClick, children, ...rest }) => { 8 | const dispatch = useDispatch(); 9 | const { chainId } = useWeb3React(); 10 | 11 | const handleClick = e => { 12 | if (chainId) { 13 | onClick(e); 14 | } else { 15 | dispatch(ModalActions.showConnectWalletModal()); 16 | } 17 | }; 18 | 19 | return ( 20 |
21 | {children} 22 |
23 | ); 24 | }; 25 | 26 | export default TxButton; 27 | -------------------------------------------------------------------------------- /src/components/WFTMModal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .swapContainer { 2 | display: flex; 3 | flex-direction: column; 4 | border-radius: 10px; 5 | position: relative; 6 | } 7 | 8 | .reverse { 9 | flex-direction: column-reverse; 10 | } 11 | 12 | .swapbtn { 13 | width: 50px; 14 | height: 50px; 15 | border-radius: 50%; 16 | background-color: #FFF; 17 | box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.05); 18 | align-self: center; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | cursor: pointer; 23 | margin-top: 18px; 24 | } 25 | 26 | .icon { 27 | width: 26px; 28 | height: 26px; 29 | color: #3D3D3D; 30 | } 31 | 32 | .symbol { 33 | font-weight: 400; 34 | font-size: 18px; 35 | letter-spacing: -0.2px; 36 | color: #121223; 37 | margin-bottom: 11px; 38 | margin-left: 10px; 39 | } 40 | 41 | .swapBoxInner { 42 | display: flex; 43 | align-items: center; 44 | border-radius: 10px; 45 | border: 1px solid #EAEAF1; 46 | } 47 | 48 | .rightBox { 49 | flex: 1; 50 | display: flex; 51 | align-items: center; 52 | height: 50px; 53 | } 54 | 55 | .balance { 56 | font-weight: 400; 57 | font-size: 16px; 58 | color: #3D3D3D; 59 | display: flex; 60 | align-items: center; 61 | padding: 16px; 62 | white-space: nowrap; 63 | } 64 | 65 | .max { 66 | margin-left: 4px; 67 | color: #007BFF; 68 | cursor: pointer; 69 | } 70 | 71 | .input { 72 | margin-right: 10px; 73 | width: 50px; 74 | height: 100%; 75 | flex-grow: 1; 76 | font-size: 18px; 77 | color: #3D3D3D; 78 | background-color: transparent; 79 | text-align: right; 80 | border: none; 81 | outline: none; 82 | } 83 | 84 | .usdVal { 85 | flex: 0 0 120px; 86 | align-self: stretch; 87 | display: flex; 88 | align-items: center; 89 | justify-content: center; 90 | border-left: 1px solid #EAEAF1; 91 | font-weight: 500; 92 | font-size: 16px; 93 | color: rgba(0, 0, 0, 0.4); 94 | } 95 | 96 | @media only screen and (max-width: 600px) { 97 | .swapBoxInner { 98 | flex-direction: column; 99 | } 100 | 101 | .rightBox { 102 | width: 100%; 103 | flex: 0 0 50px; 104 | border-top: 1px solid #EAEAF1; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/components/Web3ReactManager/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | import React, { useEffect, useState } from 'react'; 3 | // eslint-disable-next-line no-unused-vars 4 | import Loader from '../Loader'; 5 | import { NetworkContextName } from '../../constants'; 6 | import { network } from '../../connectors'; 7 | import useEagerConnect from 'hooks/useEagerConnect'; 8 | import useInactiveListener from 'hooks/useInactiveListener'; 9 | import { useWeb3React } from '@web3-react/core'; 10 | 11 | export default function Web3ReactManager({ children }) { 12 | const { active } = useWeb3React(); 13 | const { 14 | active: networkActive, 15 | error: networkError, 16 | activate: activateNetwork, 17 | } = useWeb3React(NetworkContextName); 18 | 19 | // try to eagerly connect to an injected provider, if it exists and has granted access already 20 | const triedEager = useEagerConnect(); 21 | 22 | // after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd 23 | useEffect(() => { 24 | if (triedEager && !networkActive && !networkError && !active) { 25 | activateNetwork(network); 26 | } 27 | }, [triedEager, networkActive, networkError, activateNetwork, active]); 28 | 29 | // when there's no account connected, react to logins (broadly speaking) on the injected provider, if it exists 30 | useInactiveListener(!triedEager); 31 | 32 | // handle delayed loader state 33 | const [showLoader, setShowLoader] = useState(false); 34 | useEffect(() => { 35 | const timeout = setTimeout(() => { 36 | setShowLoader(true); 37 | }, 600); 38 | 39 | return () => { 40 | clearTimeout(timeout); 41 | }; 42 | }, []); 43 | console.log(showLoader); 44 | 45 | // on page load, do nothing until we've tried to connect to the injected connector 46 | if (!triedEager) { 47 | return null; 48 | } 49 | 50 | // if the account context isn't active, and there's an error on the network context, it's an irrecoverable error 51 | // if (!active && networkError) { 52 | // return ( 53 | //
54 | //
55 | // {`Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device`} 56 | //
57 | //
58 | // ); 59 | // } 60 | 61 | // if neither context is active, spin 62 | // if (!active && !networkActive) { 63 | // return showLoader ? ( 64 | //
65 | // 66 | //
67 | // ) : null; 68 | // } 69 | 70 | return children; 71 | } 72 | -------------------------------------------------------------------------------- /src/components/icons/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import { 4 | faUndoAlt, 5 | faRedoAlt, 6 | faPalette, 7 | faSave, 8 | faUpload, 9 | faTrash, 10 | faFillDrip, 11 | faEraser, 12 | faFileUpload, 13 | faDownload, 14 | } from '@fortawesome/free-solid-svg-icons'; 15 | 16 | export const UndoIcon = ; 17 | export const RedoIcon = ; 18 | export const PaletteIcon = ; 19 | export const SaveIcon = ; 20 | export const LoadIcon = ; 21 | export const TrashIcon = ; 22 | export const BGIcon = ; 23 | export const EraserIcon = ; 24 | export const DownloadIcon = ( 25 | 26 | ); 27 | export const fileUploadIcon = ( 28 | 29 | ); 30 | -------------------------------------------------------------------------------- /src/connectors/index.js: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | import { InjectedConnector } from '@web3-react/injected-connector'; 3 | import { WalletLinkConnector } from '@web3-react/walletlink-connector'; 4 | 5 | import { NetworkConnector } from './NetworkConnector'; 6 | 7 | import ARTION_LOGO_URL from '../assets/svgs/logo_blue.svg'; 8 | 9 | // eslint-disable-next-line no-undef 10 | const isMainnet = process.env.REACT_APP_ENV === 'MAINNET'; 11 | 12 | const RPC = isMainnet 13 | ? { 14 | [ChainId.FANTOM]: 'https://rpc.ftm.tools', 15 | } 16 | : { 17 | [ChainId.FANTOM_TESTNET]: 'https://rpc.testnet.fantom.network', 18 | }; 19 | 20 | export const network = new NetworkConnector({ 21 | defaultChainId: ChainId.FANTOM, 22 | urls: RPC, 23 | }); 24 | 25 | export const injected = new InjectedConnector({ 26 | supportedChainIds: isMainnet 27 | ? [ 28 | 250, // fantom 29 | ] 30 | : [ 31 | 4002, // fantom testnet 32 | ], 33 | }); 34 | 35 | export const walletlink = new WalletLinkConnector({ 36 | url: 'https://rpc.ftm.tools', 37 | appName: 'Artion', 38 | appLogoUrl: ARTION_LOGO_URL, 39 | }); 40 | -------------------------------------------------------------------------------- /src/constants/auth.constants.js: -------------------------------------------------------------------------------- 1 | export const AuthConstants = { 2 | PROFILE_GET_START: 'PROFILE_GET_START', 3 | PROFILE_GET_SUCCESS: 'PROFILE_GET_SUCCESS', 4 | PROFILE_GET_FAILED: 'PROFILE_GET_FAILED', 5 | SIGN_OUT: 'SIGN_OUT', 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/authmessages.js: -------------------------------------------------------------------------------- 1 | const AuthMessages = { 2 | USEREXISTS: 'user already exists', 3 | USERCREATED: 'new user has been created', 4 | USERCREATIONFAILED: 'cannot create a new user', 5 | INVALIDEMAIL: 'the email address is invalid', 6 | NOEMAIL: 'the email address cannot be blank', 7 | NONAME: 'user name cannot be blank', 8 | NOPASSWORD: 'password cannot be blank', 9 | SIGNINJWTTOKEN: 'sign in token has been created', 10 | SIGNINJWTTOKENFAILED: 'sign in token creation failed', 11 | SIGNOUTJWTTOKEN: 'sign out token has been created', 12 | SIGNOUTJWTTOKENFAILED: 'sign out token creation failed', 13 | USERUPDATED: 'user has been updated', 14 | UPDATEUSERFAILED: 'cannot update a user', 15 | USERFOUND: 'user found', 16 | USERNOTFOUND: 'cannot find a user', 17 | }; 18 | 19 | export default AuthMessages; 20 | -------------------------------------------------------------------------------- /src/constants/collections.constants.js: -------------------------------------------------------------------------------- 1 | const CollectionsConstants = { 2 | FETCH_COLLECTIONS_START: 'FETCH_COLLECTIONS_START', 3 | FETCH_COLLECTIONS_SUCCESS: 'FETCH_COLLECTIONS_SUCCESS', 4 | FETCH_COLLECTIONS_FAILED: 'FETCH_COLLECTIONS_FAILED', 5 | }; 6 | 7 | export default CollectionsConstants; 8 | -------------------------------------------------------------------------------- /src/constants/colors.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'black', 3 | 'grey', 4 | 'white', 5 | 'red', 6 | 'pink', 7 | 'orange', 8 | 'yellow', 9 | 'green', 10 | 'darkgreen', 11 | 'blue', 12 | 'indigo', 13 | 'violet', 14 | 'brown', 15 | 'saddlebrown', 16 | 'transparent', 17 | ]; 18 | -------------------------------------------------------------------------------- /src/constants/errors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | '-32700': 3 | 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text!', 4 | '-32600': 'The JSON sent is not a valid Request object!', 5 | '-32601': 'The method does not exist / is not available!', 6 | '-32602': 'Invalid method parameter(s)!', 7 | '-32603': 'Internal JSON-RPC error!', 8 | '-32000': 'Invalid input', 9 | '-32001': 'Resource not found!', 10 | '-32002': 'Resource unavailable!', 11 | '-32003': 'Transaction rejected!', 12 | '-32004': 'Method not supported!', 13 | '-32005': 'Request limit exceeded!', 14 | '4001': 'User rejected the request!', 15 | '4100': 16 | 'The requested account and/or method has not been authorized by the user!', 17 | '4200': 'The requested method is not supported by this Ethereum provider!', 18 | '4900': 'The provider is disconnected from all chains!', 19 | '4901': 'The provider is disconnected from the specified chain!', 20 | }; 21 | -------------------------------------------------------------------------------- /src/constants/filter.constants.js: -------------------------------------------------------------------------------- 1 | import iconArt from 'assets/svgs/rainbow.svg'; 2 | import iconCollectibles from 'assets/svgs/bear.svg'; 3 | import iconSports from 'assets/svgs/soccerball.svg'; 4 | import iconUtility from 'assets/svgs/tools.svg'; 5 | import iconTrading from 'assets/svgs/cardboard.svg'; 6 | import iconVirtual from 'assets/svgs/monster.svg'; 7 | import iconDomain from 'assets/svgs/domain.svg'; 8 | 9 | export const GroupFilters = [ 10 | { 11 | value: 'all', 12 | label: 'All Items', 13 | }, 14 | { 15 | value: 'single', 16 | label: 'Single Items', 17 | }, 18 | // { 19 | // value: 'bundle', 20 | // label: 'Bundles', 21 | // }, 22 | ]; 23 | 24 | export const Categories = [ 25 | { 26 | id: 0, 27 | icon: iconArt, 28 | label: 'Art', 29 | }, 30 | { 31 | id: 1, 32 | icon: iconCollectibles, 33 | label: 'Collectibles', 34 | }, 35 | { 36 | id: 2, 37 | icon: iconSports, 38 | label: 'Sports', 39 | }, 40 | { 41 | id: 3, 42 | icon: iconUtility, 43 | label: 'Utility', 44 | }, 45 | { 46 | id: 4, 47 | icon: iconTrading, 48 | label: 'Trading Cards', 49 | }, 50 | { 51 | id: 5, 52 | icon: iconVirtual, 53 | label: 'Virtual Worlds', 54 | }, 55 | { 56 | id: 6, 57 | icon: iconDomain, 58 | label: 'Domain Names', 59 | }, 60 | ]; 61 | 62 | export const SortByOptions = [ 63 | { 64 | id: 'createdAt', 65 | label: 'Recently Created', 66 | }, 67 | { 68 | id: 'oldest', 69 | label: 'Oldest', 70 | }, 71 | { 72 | id: 'listedAt', 73 | label: 'Recently Listed', 74 | }, 75 | { 76 | id: 'soldAt', 77 | label: 'Recently Sold', 78 | }, 79 | { 80 | id: 'saleEndsAt', 81 | label: 'Ending Soon', 82 | }, 83 | { 84 | id: 'price', 85 | label: 'Highest Price', 86 | }, 87 | { 88 | id: 'cheapest', 89 | label: 'Lowest Price', 90 | }, 91 | { 92 | id: 'lastSalePrice', 93 | label: 'Highest Last Sale', 94 | }, 95 | { 96 | id: 'viewed', 97 | label: 'Mostly Viewed', 98 | }, 99 | ]; 100 | 101 | const FilterConstants = { 102 | UPDATE_STATUS_FILTER: 'UPDATE_STATUS_FILTER', 103 | UPDATE_COLLECTIONS_FILTER: 'UPDATE_COLLECTIONS_FILTER', 104 | UPDATE_CATEGORIES_FILTER: 'UPDATE_CATEGORIES_FILTER', 105 | UPDATE_GROUP_TYPE_FILTER: 'UPDATE_GROUP_TYPE_FILTER', 106 | UPDATE_SORT_BY_FILTER: 'UPDATE_SORT_BY_FILTER', 107 | }; 108 | 109 | export default FilterConstants; 110 | -------------------------------------------------------------------------------- /src/constants/firebase.js: -------------------------------------------------------------------------------- 1 | const FirebaseCredentials = { 2 | APIKEY: 'AIzaSyDREDVv7du1dR7IGA3FvklGi1Z-uFxk5tc', 3 | AUTHDOMAIN: 'fantomsea-8a097.firebaseapp.com', 4 | PROJECTID: 'fantomsea-8a097', 5 | STORAGEBUCKET: 'fantomsea-8a097.appspot.com', 6 | MEASSAGINGSENDERID: '958318990578', 7 | APPID: '1:958318990578:web:45e8bc677d77cd871f2c6b', 8 | MEASUREMENTID: 'G-PDWH3TBPZY', 9 | }; 10 | 11 | export default FirebaseCredentials; 12 | -------------------------------------------------------------------------------- /src/constants/header.constants.js: -------------------------------------------------------------------------------- 1 | const HeaderConstants = { 2 | TOGGLESEARCHBAR: 'TOGGLESEARCHBAR', 3 | }; 4 | 5 | export default HeaderConstants; 6 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | export const NetworkContextName = 'NETWORK'; 2 | export const FTM_TOTAL_SUPPLY = 3175000000; 3 | export const ADMIN_ADDRESS = '0x1bffb3a232e06e06a5d9e93c8df3321f768197c2'; 4 | -------------------------------------------------------------------------------- /src/constants/ipfs.constants.js: -------------------------------------------------------------------------------- 1 | export const IPFSConstants = { 2 | HashURI: 'https://cloudflare-ipfs.com/ipfs/', 3 | }; 4 | 5 | export const IPFSUris = [ 6 | 'https://artion.mypinata.cloud/ipfs/', 7 | 'https://artion1.mypinata.cloud/ipfs/', 8 | 'https://artion2.mypinata.cloud/ipfs/', 9 | 'https://artion3.mypinata.cloud/ipfs/', 10 | 'https://artion4.mypinata.cloud/ipfs/', 11 | 'https://artion5.mypinata.cloud/ipfs/', 12 | 'https://artion6.mypinata.cloud/ipfs/', 13 | 'https://artion7.mypinata.cloud/ipfs/', 14 | 'https://artion8.mypinata.cloud/ipfs/', 15 | 'https://artion9.mypinata.cloud/ipfs/', 16 | 'https://artion10.mypinata.cloud/ipfs/', 17 | 'https://artion11.mypinata.cloud/ipfs/', 18 | 'https://artion12.mypinata.cloud/ipfs/', 19 | 'https://artion13.mypinata.cloud/ipfs/', 20 | ]; 21 | -------------------------------------------------------------------------------- /src/constants/modal.constants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | SHOW_ACCOUNT_MODAL: 'SHOW_ACCOUNT_MODAL', 3 | HIDE_ACCOUNT_MODAL: 'HIDE_ACCOUNT_MODAL', 4 | SHOW_WFTM_MODAL: 'SHOW_WFTM_MODAL', 5 | HIDE_WFTM_MODAL: 'HIDE_WFTM_MODAL', 6 | SHOW_CONNECT_WALLET_MODAL: 'SHOW_CONNECT_WALLET_MODAL', 7 | HIDE_CONNECT_WALLET_MODAL: 'HIDE_CONNECT_WALLET_MODAL', 8 | }; 9 | -------------------------------------------------------------------------------- /src/constants/networks.js: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | 3 | export const NETWORK_LABEL = { 4 | [ChainId.MAINNET]: 'Ethereum', 5 | [ChainId.RINKEBY]: 'Rinkeby', 6 | [ChainId.ROPSTEN]: 'Ropsten', 7 | [ChainId.GÖRLI]: 'Görli', 8 | [ChainId.KOVAN]: 'Kovan', 9 | [ChainId.FANTOM]: 'Fantom', 10 | [ChainId.FANTOM_TESTNET]: 'Fantom Testnet', 11 | [ChainId.MATIC]: 'Matic', 12 | [ChainId.MATIC_TESTNET]: 'Matic Testnet', 13 | [ChainId.XDAI]: 'xDai', 14 | [ChainId.BSC]: 'BSC', 15 | [ChainId.BSC_TESTNET]: 'BSC Testnet', 16 | [ChainId.MOONBASE]: 'Moonbase', 17 | [ChainId.AVALANCHE]: 'Avalanche', 18 | [ChainId.FUJI]: 'Fuji', 19 | [ChainId.HECO]: 'HECO', 20 | [ChainId.HECO_TESTNET]: 'HECO Testnet', 21 | [ChainId.HARMONY]: 'Harmony', 22 | [ChainId.HARMONY_TESTNET]: 'Harmony Testnet', 23 | }; 24 | 25 | export const Contracts = { 26 | [ChainId.FANTOM]: { 27 | auction: '0x951Cc69504d39b3eDb155CA99f555E47E044c2F1', 28 | sales: '0xa06aecbb8CD9328667f8E05f288e5b3ac1CFf852', 29 | bundleSales: '0x56aD389A02Ea9d63f13106cB0c161342f537a92e', 30 | factory: '0xCC7A2eC7A8A0564518fD3D2ca0Df8B2137626144', //FantomNFTFactory 31 | privateFactory: '0xa4fDb09e1796730bfBA8a352074F0dd65D400Dd4', //FantomNFTFactoryPrivate 32 | artFactory: '0x520DaB621f93F59d3557174280AB1B6d4FB8c956', //FantomArtFactory 33 | privateArtFactory: '0x736Eae40AdFf88570b92378c97a0D11b44E1C953', //FantomArtFactoryPrivate 34 | }, 35 | [ChainId.FANTOM_TESTNET]: { 36 | auction: '0xDC8e329b0bA326f7Fcdbb5d42B437FfC7EA7C7a8', 37 | sales: '0x35123486C0a742da0aA320d037e5226bA4F9bf21', 38 | bundleSales: '0x52352D4a5fB2a79722a875bBdF2a6D00A152a3C5', 39 | factory: '0x7C8a9F8D04d9f7601E04B4bd3f594F6aB42b1231', //FantomNFTFactory 40 | privateFactory: '0x7d3bb8dD1f3b123C6DFEf882709Fadc007ee4532', //FantomNFTFactoryPrivate 41 | artFactory: '0x980A2fAC219CD4e26033E82A44D6798F7488aDb2', //FantomArtFactory 42 | privateArtFactory: '0x0106fe87F41BAa91D6fe52c508723e8cf5082c49', //FantomArtFactoryPrivate 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /src/constants/noti.constants.js: -------------------------------------------------------------------------------- 1 | export const NotiConstants = { 2 | UPDATENOTIFICATION: 'UPDATENOTIFICATION', 3 | READALLNOTIFICATION: 'READALLNOTIFICATION', 4 | UNREADNOTIFICATION: 'UNREADNOTIFICATION', 5 | READALLEMAIL: 'READALLEMAIL', 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/price.constants.js: -------------------------------------------------------------------------------- 1 | export const PriceConstants = { 2 | UPDATE_PRICE: 'UPDATE_PRICE', 3 | MAX_PRICE: '999999999', 4 | }; 5 | -------------------------------------------------------------------------------- /src/constants/tokens.constants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | RESET_TOKENS: 'RESET_TOKENS', 3 | UPDATE_TOKENS: 'UPDATE_TOKENS', 4 | FETCHING_START: 'FETCHING_START', 5 | FETCHING_SUCCESS: 'FETCHING_SUCCESS', 6 | FETCHING_FAILED: 'FETCHING_FAILED', 7 | }; 8 | -------------------------------------------------------------------------------- /src/constants/wallet.js: -------------------------------------------------------------------------------- 1 | import COINBASE_ICON_URL from 'assets/svgs/coinbase.svg'; 2 | import METAMASK_ICON_URL from 'assets/imgs/metamask.png'; 3 | import { injected, walletlink } from '../connectors'; 4 | 5 | export const SUPPORTED_WALLETS = { 6 | METAMASK: { 7 | connector: injected, 8 | name: 'MetaMask', 9 | icon: METAMASK_ICON_URL, 10 | }, 11 | WALLET_LINK: { 12 | connector: walletlink, 13 | name: 'Coinbase Wallet', 14 | icon: COINBASE_ICON_URL, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/constants/walletconnect.constants.js: -------------------------------------------------------------------------------- 1 | export const WalletConnectConstants = { 2 | WALLETCONNECTED: 'WALLETCONNECTED', 3 | WALLETDISCONNECTED: 'WALLETDISCONNECTED', 4 | }; 5 | -------------------------------------------------------------------------------- /src/contracts/factory.js: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | 3 | import { calculateGasMargin, getHigherGWEI } from 'utils'; 4 | import { Contracts } from 'constants/networks'; 5 | import useContract from 'hooks/useContract'; 6 | 7 | import { FACTORY_ABI } from './abi'; 8 | 9 | // eslint-disable-next-line no-undef 10 | const isMainnet = process.env.REACT_APP_ENV === 'MAINNET'; 11 | const CHAIN = isMainnet ? ChainId.FANTOM : ChainId.FANTOM_TESTNET; 12 | 13 | export const useFactoryContract = () => { 14 | const { getContract } = useContract(); 15 | 16 | const getFactoryContract = async () => 17 | await getContract(Contracts[CHAIN].factory, FACTORY_ABI); 18 | 19 | const getPrivateFactoryContract = async () => 20 | await getContract(Contracts[CHAIN].privateFactory, FACTORY_ABI); 21 | 22 | const getArtFactoryContract = async () => 23 | await getContract(Contracts[CHAIN].artFactory, FACTORY_ABI); 24 | 25 | const getPrivateArtFactoryContract = async () => 26 | await getContract(Contracts[CHAIN].privateArtFactory, FACTORY_ABI); 27 | 28 | const createNFTContract = async (contract, name, symbol, value, from) => { 29 | const args = [name, symbol]; 30 | 31 | const options = { 32 | value, 33 | from, 34 | gasPrice: getHigherGWEI(), 35 | }; 36 | 37 | const gasEstimate = await contract.estimateGas.createNFTContract( 38 | ...args, 39 | options 40 | ); 41 | options.gasLimit = calculateGasMargin(gasEstimate); 42 | return await contract.createNFTContract(...args, options); 43 | }; 44 | 45 | return { 46 | getFactoryContract, 47 | getPrivateFactoryContract, 48 | getArtFactoryContract, 49 | getPrivateArtFactoryContract, 50 | createNFTContract, 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /src/contracts/index.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | 3 | export * from './abi'; 4 | export * from './auctions'; 5 | export * from './sales'; 6 | export * from './bundleSales'; 7 | export * from './token'; 8 | export * from './wftm'; 9 | export * from './factory'; 10 | 11 | export const getSigner = async () => { 12 | await window.ethereum.enable(); 13 | const provider = new ethers.providers.Web3Provider(window.ethereum); 14 | const signer = provider.getSigner(); 15 | return signer; 16 | }; 17 | -------------------------------------------------------------------------------- /src/contracts/token.js: -------------------------------------------------------------------------------- 1 | import useContract from 'hooks/useContract'; 2 | 3 | import { 4 | ERC20_CONTRACT_ABI, 5 | ERC721_CONTRACT_ABI, 6 | ERC1155_CONTRACT_ABI, 7 | } from './abi'; 8 | 9 | export const useNFTContract = () => { 10 | const { getContract } = useContract(); 11 | 12 | const getERC20Contract = async address => 13 | await getContract(address, ERC20_CONTRACT_ABI); 14 | 15 | const getERC721Contract = async address => 16 | await getContract(address, ERC721_CONTRACT_ABI); 17 | 18 | const getERC1155Contract = async address => 19 | await getContract(address, ERC1155_CONTRACT_ABI); 20 | 21 | return { getERC20Contract, getERC721Contract, getERC1155Contract }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/contracts/wftm.js: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | 3 | import { WFTM_ABI } from './abi'; 4 | import { calculateGasMargin, getHigherGWEI } from 'utils'; 5 | import useContract from 'hooks/useContract'; 6 | import { ethers } from 'ethers'; 7 | 8 | const WFTM_ADDRESS = { 9 | [ChainId.FANTOM]: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', 10 | [ChainId.FANTOM_TESTNET]: '0xf1277d1Ed8AD466beddF92ef448A132661956621', 11 | }; 12 | 13 | // eslint-disable-next-line no-undef 14 | const isMainnet = process.env.REACT_APP_ENV === 'MAINNET'; 15 | const CHAIN = isMainnet ? ChainId.FANTOM : ChainId.FANTOM_TESTNET; 16 | export const useWFTMContract = () => { 17 | const { getContract } = useContract(); 18 | 19 | const wftmAddress = WFTM_ADDRESS[CHAIN]; 20 | 21 | const getWFTMContract = async () => await getContract(wftmAddress, WFTM_ABI); 22 | 23 | const getWFTMBalance = async address => { 24 | const contract = await getWFTMContract(); 25 | return await contract.balanceOf(address); 26 | }; 27 | 28 | const wrapFTM = async (value, from) => { 29 | const contract = await getWFTMContract(); 30 | 31 | const options = { 32 | value, 33 | from, 34 | gasPrice: getHigherGWEI(), 35 | }; 36 | 37 | const gasEstimate = await contract.estimateGas.deposit(options); 38 | options.gasLimit = calculateGasMargin(gasEstimate); 39 | 40 | return await contract.deposit(options); 41 | }; 42 | 43 | const unwrapFTM = async value => { 44 | const contract = await getWFTMContract(); 45 | 46 | const options = { 47 | gasPrice: getHigherGWEI(), 48 | }; 49 | 50 | return await contract.withdraw(value, options); 51 | }; 52 | 53 | const getAllowance = async (owner, spender) => { 54 | const contract = await getWFTMContract(); 55 | return await contract.allowance(owner, spender); 56 | }; 57 | 58 | const approve = async (address, value) => { 59 | const contract = await getWFTMContract(); 60 | const tx = await contract.approve( 61 | address, 62 | ethers.constants.MaxUint256 || value 63 | ); 64 | await tx.wait(); 65 | }; 66 | 67 | return { 68 | wftmAddress, 69 | getWFTMBalance, 70 | wrapFTM, 71 | unwrapFTM, 72 | getAllowance, 73 | approve, 74 | }; 75 | }; 76 | -------------------------------------------------------------------------------- /src/hooks/useContract.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { ethers } from 'ethers'; 3 | import { useWeb3React } from '@web3-react/core'; 4 | 5 | // eslint-disable-next-line no-undef 6 | const isMainnet = process.env.REACT_APP_ENV === 'MAINNET'; 7 | 8 | export default () => { 9 | const { chainId } = useWeb3React(); 10 | 11 | const getContract = useCallback( 12 | async (address, abi) => { 13 | if (chainId) { 14 | await window.ethereum.enable(); 15 | const provider = new ethers.providers.Web3Provider(window.ethereum); 16 | const signer = provider.getSigner(); 17 | 18 | return new ethers.Contract(address, abi, signer); 19 | } else { 20 | const provider = new ethers.providers.JsonRpcProvider( 21 | isMainnet 22 | ? 'https://rpc.ftm.tools/' 23 | : 'https://rpc.testnet.fantom.network/', 24 | isMainnet ? 250 : 4002 25 | ); 26 | 27 | return new ethers.Contract(address, abi, provider); 28 | } 29 | }, 30 | [chainId] 31 | ); 32 | 33 | return { getContract }; 34 | }; 35 | -------------------------------------------------------------------------------- /src/hooks/useEagerConnect.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | import { injected } from '../connectors'; 4 | import { isMobile } from 'react-device-detect'; 5 | import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'; 6 | 7 | function useEagerConnect() { 8 | const { activate, active } = useWeb3ReactCore(); // specifically using useWeb3ReactCore because of what this hook does 9 | const [tried, setTried] = useState(false); 10 | 11 | useEffect(() => { 12 | injected.isAuthorized().then(isAuthorized => { 13 | if (isAuthorized) { 14 | activate(injected, undefined, true).catch(() => { 15 | setTried(true); 16 | }); 17 | } else { 18 | if (isMobile && window.ethereum) { 19 | activate(injected, undefined, true).catch(() => { 20 | setTried(true); 21 | }); 22 | } else { 23 | setTried(true); 24 | } 25 | } 26 | }); 27 | // intentionally only running on mount (make sure it's only mounted once :)) 28 | }, [activate]); 29 | 30 | // if the connection worked, wait until we get confirmation of that to flip the flag 31 | useEffect(() => { 32 | if (active) { 33 | setTried(true); 34 | } 35 | }, [active]); 36 | 37 | return tried; 38 | } 39 | 40 | export default useEagerConnect; 41 | -------------------------------------------------------------------------------- /src/hooks/useInactiveListener.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'; 3 | 4 | import { injected } from '../connectors'; 5 | 6 | /** 7 | * Use for network and injected - logs user in 8 | * and out after checking what network theyre on 9 | */ 10 | function useInactiveListener(suppress = false) { 11 | const { active, error, activate } = useWeb3ReactCore(); // specifically using useWeb3React because of what this hook does 12 | 13 | useEffect(() => { 14 | const { ethereum } = window; 15 | 16 | if (ethereum && ethereum.on && !active && !error && !suppress) { 17 | const handleChainChanged = () => { 18 | // eat errors 19 | activate(injected, undefined, true).catch(error => { 20 | console.error('Failed to activate after chain changed', error); 21 | }); 22 | }; 23 | 24 | const handleAccountsChanged = accounts => { 25 | if (accounts.length > 0) { 26 | // eat errors 27 | activate(injected, undefined, true).catch(error => { 28 | console.error('Failed to activate after accounts changed', error); 29 | }); 30 | } 31 | }; 32 | 33 | ethereum.on('chainChanged', handleChainChanged); 34 | ethereum.on('accountsChanged', handleAccountsChanged); 35 | 36 | return () => { 37 | if (ethereum.removeListener) { 38 | ethereum.removeListener('chainChanged', handleChainChanged); 39 | ethereum.removeListener('accountsChanged', handleAccountsChanged); 40 | } 41 | }; 42 | } 43 | return undefined; 44 | }, [active, error, suppress, activate]); 45 | } 46 | 47 | export default useInactiveListener; 48 | -------------------------------------------------------------------------------- /src/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | // modified from https://usehooks.com/usePrevious/ 4 | export default function usePrevious(value) { 5 | // The ref object is a generic container whose current property is mutable ... 6 | // ... and can hold any value, similar to an instance property on a class 7 | const ref = useRef(); 8 | 9 | // Store current value in ref 10 | useEffect(() => { 11 | ref.current = value; 12 | }, [value]); // Only re-run if value changes 13 | 14 | // Return previous value (happens before update in useEffect above) 15 | return ref.current; 16 | } 17 | -------------------------------------------------------------------------------- /src/hooks/useTokens.js: -------------------------------------------------------------------------------- 1 | import { ChainId } from '@sushiswap/sdk'; 2 | 3 | // import iconFTM from 'assets/imgs/ftm.png'; 4 | import iconWFTM from 'assets/imgs/wftm.png'; 5 | import iconUSDT from 'assets/imgs/usdt.png'; 6 | import iconUSDC from 'assets/imgs/usdc.png'; 7 | import iconDAI from 'assets/imgs/dai.png'; 8 | 9 | // eslint-disable-next-line no-undef 10 | const isMainnet = process.env.REACT_APP_ENV === 'MAINNET'; 11 | 12 | const Tokens = { 13 | [ChainId.FANTOM]: [ 14 | // { 15 | // address: '', 16 | // name: 'Fantom', 17 | // symbol: 'FTM', 18 | // decimals: 18, 19 | // icon: iconFTM, 20 | // }, 21 | { 22 | address: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', 23 | name: 'Wrapped Fantom', 24 | symbol: 'WFTM', 25 | decimals: 18, 26 | icon: iconWFTM, 27 | }, 28 | { 29 | address: '0x049d68029688eabf473097a2fc38ef61633a3c7a', 30 | name: 'Tether USD', 31 | symbol: 'fUSDT', 32 | decimals: 6, 33 | icon: iconUSDT, 34 | }, 35 | { 36 | address: '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75', 37 | name: 'USD Coin', 38 | symbol: 'USDC', 39 | decimals: 6, 40 | icon: iconUSDC, 41 | }, 42 | { 43 | address: '0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E', 44 | name: 'Dai Stablecoin', 45 | symbol: 'DAI', 46 | decimals: 18, 47 | icon: iconDAI, 48 | }, 49 | ], 50 | [ChainId.FANTOM_TESTNET]: [ 51 | // { 52 | // address: '', 53 | // name: 'Fantom', 54 | // symbol: 'FTM', 55 | // decimals: 18, 56 | // icon: iconFTM, 57 | // }, 58 | { 59 | address: '0xf1277d1ed8ad466beddf92ef448a132661956621', 60 | name: 'Wrapped Fantom', 61 | symbol: 'WFTM', 62 | decimals: 18, 63 | icon: iconWFTM, 64 | }, 65 | ], 66 | }; 67 | 68 | export default function useTokens() { 69 | const chain = isMainnet ? ChainId.FANTOM : ChainId.FANTOM_TESTNET; 70 | 71 | const tokens = Tokens[chain]; 72 | 73 | const getTokenByAddress = addr => { 74 | const address = 75 | !addr || 76 | addr === '0x0000000000000000000000000000000000000000' || 77 | addr === 'ftm' 78 | ? '' 79 | : addr; 80 | return (tokens || []).find( 81 | tk => tk.address.toLowerCase() === address.toLowerCase() 82 | ); 83 | }; 84 | 85 | return { getTokenByAddress, tokens }; 86 | } 87 | -------------------------------------------------------------------------------- /src/hooks/useWindowDimensions.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | function getWindowDimensions() { 4 | const { innerWidth: width, innerHeight: height } = window; 5 | return { 6 | width, 7 | height, 8 | }; 9 | } 10 | 11 | export default function useWindowDimensions() { 12 | const [windowDimensions, setWindowDimensions] = useState( 13 | getWindowDimensions() 14 | ); 15 | 16 | useEffect(() => { 17 | function handleResize() { 18 | setWindowDimensions(getWindowDimensions()); 19 | } 20 | 21 | window.addEventListener('resize', handleResize); 22 | return () => window.removeEventListener('resize', handleResize); 23 | }, []); 24 | 25 | return windowDimensions; 26 | } 27 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | .MuiAppBar-colorPrimary { 2 | color: #fff; 3 | background-color: #007bff !important; 4 | } 5 | 6 | .css-gr2aqs { 7 | background-color: #007bff !important; 8 | } 9 | 10 | body { 11 | margin: 0 !important; 12 | overflow: auto; 13 | } 14 | .slick-slide { 15 | outline: none; 16 | width: unset !important; 17 | /* width: 360px !important; */ 18 | } 19 | 20 | body * { 21 | font-family: 'proxima-nova' !important; 22 | } 23 | 24 | /* width */ 25 | ::-webkit-scrollbar { 26 | width: 6px; 27 | height: 6px; 28 | } 29 | 30 | /* Track */ 31 | ::-webkit-scrollbar-track { 32 | background: #f1f1f1; 33 | border-radius: 6px; 34 | } 35 | 36 | /* Handle */ 37 | ::-webkit-scrollbar-thumb { 38 | background: #888; 39 | border-radius: 6px; 40 | } 41 | 42 | /* Handle on hover */ 43 | ::-webkit-scrollbar-thumb:hover { 44 | background: #555; 45 | } 46 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'; 5 | import './index.css'; 6 | 7 | import App from 'components/app'; 8 | import Web3ReactManager from 'components/Web3ReactManager'; 9 | import { store } from '../src/stores/reduxStore'; 10 | import { NetworkContextName } from './constants'; 11 | import getLibrary from './utils/getLibrary'; 12 | 13 | const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | , 25 | document.getElementById('root') 26 | ); 27 | -------------------------------------------------------------------------------- /src/pages/NotificationSetting/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | height: 100vh; 4 | padding-top: 80px; 5 | padding-bottom: 48px; 6 | box-sizing: border-box; 7 | overflow-y: auto; 8 | position: relative; 9 | } 10 | 11 | .inner { 12 | width: 100%; 13 | padding: 54px 80px; 14 | box-sizing: border-box; 15 | } 16 | 17 | .title { 18 | font-weight: 700; 19 | font-size: 32px; 20 | line-height: 39px; 21 | letter-spacing: -0.02em; 22 | color: #121223; 23 | } 24 | 25 | .body { 26 | margin-top: 50px; 27 | display: flex; 28 | } 29 | 30 | .group { 31 | width: 448px; 32 | flex: 0 0 448px; 33 | margin-right: 120px; 34 | color: #121223; 35 | 36 | &:last-child { 37 | margin-right: 0; 38 | margin-bottom: 0; 39 | } 40 | } 41 | 42 | .groupOptions { 43 | display: flex; 44 | flex-direction: column; 45 | border: 1px solid #EAEAF1; 46 | border-radius: 10px; 47 | margin-top: 19px; 48 | } 49 | 50 | .optionPanel { 51 | padding: 24px 12px; 52 | margin: 0; 53 | border-bottom: 1px solid #EAEAF1; 54 | align-items: flex-start; 55 | 56 | &:last-child { 57 | border-bottom: 0; 58 | } 59 | } 60 | 61 | .option { 62 | margin-left: 5px; 63 | } 64 | 65 | .optionTitle { 66 | font-size: 14px; 67 | line-height: 17px; 68 | font-weight: 700; 69 | } 70 | 71 | .optionDesc { 72 | font-size: 12px; 73 | line-height: 140%; 74 | font-weight: 400; 75 | color: #A2A2AD; 76 | margin-top: 7px; 77 | } 78 | 79 | .groupTitle { 80 | font-size: 18px; 81 | font-weight: 700; 82 | } 83 | 84 | .buttonsWrapper { 85 | margin-top: 40px; 86 | display: flex; 87 | } 88 | 89 | .createButton { 90 | width: 168px; 91 | height: 48px; 92 | border-radius: 8px; 93 | background-color: #1969FF !important; 94 | color: #FFF; 95 | font-weight: 700; 96 | font-size: 18px; 97 | cursor: pointer; 98 | display: flex; 99 | align-items: center; 100 | justify-content: center; 101 | 102 | &:hover { 103 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 104 | } 105 | } 106 | 107 | .disabled { 108 | cursor: not-allowed; 109 | box-shadow: none !important; 110 | opacity: 0.7; 111 | } 112 | 113 | @media only screen and (max-width: 1181px) { 114 | .body { 115 | flex-direction: column; 116 | } 117 | 118 | .group { 119 | width: 100%; 120 | max-width: 800px; 121 | margin-right: 0; 122 | margin-bottom: 40px; 123 | } 124 | } 125 | 126 | @media only screen and (max-width: 768px) { 127 | .inner { 128 | margin: 0 auto; 129 | padding: 0 40px; 130 | } 131 | } 132 | 133 | @media only screen and (max-width: 768px) { 134 | .container { 135 | padding-top: 155px; 136 | } 137 | } 138 | 139 | @media only screen and (max-width: 600px) { 140 | .inner { 141 | margin: 0; 142 | padding: 0 30px; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/pages/explorepage/Body/ExploreBody/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FInfiniteLoader from '../../../../components/InfiniteLoader'; 3 | import './styles.css'; 4 | 5 | const ExploreAllNFTs = () => { 6 | return ; 7 | }; 8 | 9 | export default ExploreAllNFTs; 10 | -------------------------------------------------------------------------------- /src/pages/explorepage/Body/ExploreBody/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/pages/explorepage/Body/ExploreBody/styles.css -------------------------------------------------------------------------------- /src/pages/explorepage/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-top: 80px; 3 | height: 100vh; 4 | box-sizing: border-box; 5 | display: flex; 6 | overflow-y: auto; 7 | } 8 | 9 | .sidebar { 10 | flex: 0 0 300px; 11 | display: flex; 12 | flex-direction: column; 13 | border-right: 1px solid #D9E1EE; 14 | box-sizing: border-box; 15 | overflow-y: auto; 16 | overflow-x: hidden; 17 | transition: flex-basis ease 300ms; 18 | } 19 | 20 | .collapsed { 21 | flex: 0 0 64px; 22 | 23 | .sidebarHeader { 24 | padding: 0; 25 | justify-content: center; 26 | } 27 | 28 | .iconCollapse { 29 | transform: rotateZ(180deg); 30 | } 31 | 32 | .filterList { 33 | display: none; 34 | } 35 | } 36 | 37 | .sidebarHeader { 38 | height: 88px; 39 | flex: 0 0 88px; 40 | display: flex; 41 | padding: 0 32px 0 24px; 42 | align-items: center; 43 | justify-content: space-between; 44 | } 45 | 46 | .sidebarTitle { 47 | font-size: 20px; 48 | font-weight: 700; 49 | color: #3D3D3D; 50 | } 51 | 52 | .iconCollapse { 53 | cursor: pointer; 54 | transform: rotateZ(0deg); 55 | transition: transform ease 300ms; 56 | } 57 | 58 | .profileWrapper { 59 | display: flex; 60 | flex-direction: column; 61 | align-items: center; 62 | margin-bottom: 20px; 63 | } 64 | 65 | .avatar { 66 | width: 100px !important; 67 | height: 100px !important; 68 | border-radius: 50px; 69 | } 70 | 71 | .username { 72 | margin-top: 8px; 73 | font-size: 24px; 74 | font-weight: 600; 75 | color: #333; 76 | } 77 | 78 | .address { 79 | margin-top: 8px; 80 | font-size: 18px; 81 | font-weight: 500; 82 | color: #333; 83 | } 84 | 85 | .bio { 86 | margin-top: 8px; 87 | font-size: 16px; 88 | font-weight: 400; 89 | color: #555; 90 | text-align: center; 91 | } 92 | 93 | .body { 94 | margin-left: 20px; 95 | flex-grow: 1; 96 | display: flex; 97 | flex-direction: column; 98 | height: 100%; 99 | overflow-y: auto; 100 | } 101 | 102 | .exploreAll { 103 | height: fit-content; 104 | max-height: 92%; 105 | overflow-y: scroll; 106 | margin-left: -40px; 107 | padding-left: 40px; 108 | padding-right: 10px; 109 | padding-bottom: 20px; 110 | display: flex; 111 | flex-wrap: wrap; 112 | } 113 | 114 | .filterHeader { 115 | display: flex; 116 | align-items: center; 117 | justify-content: space-between; 118 | box-sizing: border-box; 119 | margin-bottom: 10px; 120 | padding: 10px 0; 121 | } 122 | 123 | @media only screen and (max-width: 768px) { 124 | .container { 125 | padding-top: 133px; 126 | } 127 | 128 | .sidebar:not(.collapsed) { 129 | flex: 0 0 260px; 130 | } 131 | } 132 | 133 | @media only screen and (max-width: 600px) { 134 | .container { 135 | flex-direction: column; 136 | 137 | /* width */ 138 | &::-webkit-scrollbar { 139 | width: 0; 140 | } 141 | } 142 | 143 | .sidebar { 144 | flex-basis: unset; 145 | flex-grow: 1; 146 | } 147 | 148 | .collapsed { 149 | flex-basis: unset; 150 | flex-grow: 1; 151 | 152 | .filterList { 153 | display: block; 154 | } 155 | } 156 | 157 | .sidebarHeader { 158 | display: none; 159 | } 160 | 161 | .filterHeader { 162 | padding-left: 24px; 163 | padding-right: 24px; 164 | } 165 | 166 | .body { 167 | margin-left: 0; 168 | overflow-y: visible; 169 | } 170 | 171 | .exploreAll { 172 | padding-right: 0; 173 | overflow-y: visible; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/reducers/auth.reducers.js: -------------------------------------------------------------------------------- 1 | import { AuthConstants } from '../constants/auth.constants'; 2 | 3 | export function Auth( 4 | state = { 5 | fetching: false, 6 | user: {}, 7 | }, 8 | action 9 | ) { 10 | switch (action.type) { 11 | case AuthConstants.PROFILE_GET_START: { 12 | return { 13 | ...state, 14 | fetching: true, 15 | }; 16 | } 17 | case AuthConstants.PROFILE_GET_SUCCESS: { 18 | return { 19 | ...state, 20 | fetching: false, 21 | user: action.payload, 22 | }; 23 | } 24 | case AuthConstants.PROFILE_GET_FAILED: { 25 | return { 26 | ...state, 27 | fetching: false, 28 | user: {}, 29 | }; 30 | } 31 | case AuthConstants.SIGN_OUT: { 32 | return { 33 | ...state, 34 | user: {}, 35 | }; 36 | } 37 | 38 | default: { 39 | return state; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/reducers/collections.reducers.js: -------------------------------------------------------------------------------- 1 | import CollectionsConstants from '../constants/collections.constants'; 2 | 3 | const initialState = { 4 | collections: [], 5 | collectionsLoading: false, 6 | }; 7 | 8 | export function Collections(state = initialState, action) { 9 | switch (action.type) { 10 | case CollectionsConstants.FETCH_COLLECTIONS_START: { 11 | return { 12 | ...state, 13 | collectionsLoading: true, 14 | }; 15 | } 16 | case CollectionsConstants.FETCH_COLLECTIONS_SUCCESS: { 17 | return { 18 | collections: action.collections, 19 | collectionsLoading: false, 20 | }; 21 | } 22 | case CollectionsConstants.FETCH_COLLECTIONS_FAILED: { 23 | return { 24 | collections: [], 25 | collectionsLoading: false, 26 | }; 27 | } 28 | default: { 29 | return state; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/reducers/connectwallet.reducers.js: -------------------------------------------------------------------------------- 1 | import { WalletConnectConstants } from '../constants/walletconnect.constants'; 2 | 3 | export function ConnectWallet( 4 | state = { authToken: null, isModerator: false }, 5 | action 6 | ) { 7 | switch (action.type) { 8 | case WalletConnectConstants.WALLETCONNECTED: { 9 | return { 10 | ...state, 11 | authToken: action.token, 12 | isModerator: action.isModerator, 13 | }; 14 | } 15 | case WalletConnectConstants.WALLETDISCONNECTED: { 16 | return { 17 | ...state, 18 | authToken: null, 19 | isModerator: false, 20 | }; 21 | } 22 | default: { 23 | return state; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/reducers/filter.reducers.js: -------------------------------------------------------------------------------- 1 | import FilterConstants from '../constants/filter.constants'; 2 | 3 | const initialState = { 4 | statusBuyNow: false, 5 | statusHasBids: false, 6 | statusHasOffers: false, 7 | statusOnAuction: false, 8 | collections: [], 9 | category: null, 10 | groupType: 'all', 11 | sortBy: 'viewed', 12 | }; 13 | 14 | export function Filter(state = initialState, action) { 15 | switch (action.type) { 16 | case FilterConstants.UPDATE_STATUS_FILTER: { 17 | const newState = { ...state }; 18 | newState[action.field] = action.selected; 19 | return newState; 20 | } 21 | case FilterConstants.UPDATE_COLLECTIONS_FILTER: { 22 | return { 23 | ...state, 24 | collections: action.collections, 25 | }; 26 | } 27 | case FilterConstants.UPDATE_CATEGORIES_FILTER: { 28 | return { 29 | ...state, 30 | category: action.category, 31 | }; 32 | } 33 | case FilterConstants.UPDATE_GROUP_TYPE_FILTER: { 34 | return { 35 | ...state, 36 | groupType: action.groupType, 37 | }; 38 | } 39 | case FilterConstants.UPDATE_SORT_BY_FILTER: { 40 | return { 41 | ...state, 42 | sortBy: action.sortBy, 43 | }; 44 | } 45 | default: { 46 | return state; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/reducers/header.reducers.js: -------------------------------------------------------------------------------- 1 | import HeaderConstants from '../constants/header.constants'; 2 | /* 3 | store headerbar related options, like toggle search bar status, 4 | */ 5 | 6 | export function HeaderOptions(state = { isShown: false }, action) { 7 | switch (action.type) { 8 | case HeaderConstants.TOGGLESEARCHBAR: { 9 | return { 10 | ...state, 11 | isShown: action.toggle, 12 | }; 13 | } 14 | default: { 15 | return state; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import { Auth } from './auth.reducers'; 4 | import { ConnectWallet } from './connectwallet.reducers'; 5 | import { HeaderOptions } from './header.reducers'; 6 | import { Modal } from './modal.reducers'; 7 | import { Filter } from './filter.reducers'; 8 | import { Collections } from './collections.reducers'; 9 | import { Tokens } from './tokens.reducers'; 10 | import { Price } from './price.reducers'; 11 | 12 | const rootReducer = combineReducers({ 13 | Auth, 14 | ConnectWallet, 15 | HeaderOptions, 16 | Modal, 17 | Filter, 18 | Collections, 19 | Tokens, 20 | Price, 21 | }); 22 | 23 | export default rootReducer; 24 | -------------------------------------------------------------------------------- /src/reducers/modal.reducers.js: -------------------------------------------------------------------------------- 1 | import ModalConstants from '../constants/modal.constants'; 2 | 3 | const initialState = { 4 | accountModalVisible: false, 5 | wftmModalVisible: false, 6 | connectWalletModalVisible: false, 7 | }; 8 | 9 | export function Modal(state = initialState, action) { 10 | switch (action.type) { 11 | case ModalConstants.SHOW_ACCOUNT_MODAL: 12 | return { 13 | ...state, 14 | accountModalVisible: true, 15 | }; 16 | case ModalConstants.HIDE_ACCOUNT_MODAL: 17 | return { 18 | ...state, 19 | accountModalVisible: false, 20 | }; 21 | case ModalConstants.SHOW_WFTM_MODAL: 22 | return { 23 | ...state, 24 | wftmModalVisible: true, 25 | }; 26 | case ModalConstants.HIDE_WFTM_MODAL: 27 | return { 28 | ...state, 29 | wftmModalVisible: false, 30 | }; 31 | case ModalConstants.SHOW_CONNECT_WALLET_MODAL: 32 | return { 33 | ...state, 34 | connectWalletModalVisible: true, 35 | }; 36 | case ModalConstants.HIDE_CONNECT_WALLET_MODAL: 37 | return { 38 | ...state, 39 | connectWalletModalVisible: false, 40 | }; 41 | default: { 42 | return state; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/reducers/noti.reducers.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fantom-foundation/Artion-Client/130e6674ad309b9fe073ce44b6f5b07207b229ed/src/reducers/noti.reducers.js -------------------------------------------------------------------------------- /src/reducers/price.reducers.js: -------------------------------------------------------------------------------- 1 | import { PriceConstants } from '../constants/price.constants'; 2 | 3 | export function Price( 4 | state = { 5 | price: 0, 6 | }, 7 | action 8 | ) { 9 | switch (action.type) { 10 | case PriceConstants.UPDATE_PRICE: { 11 | return { 12 | price: action.price, 13 | }; 14 | } 15 | default: { 16 | return state; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/reducers/tokens.reducers.js: -------------------------------------------------------------------------------- 1 | import TokenConstants from '../constants/tokens.constants'; 2 | import FilterConstants from '../constants/filter.constants'; 3 | 4 | const initialState = { 5 | upFetching: false, 6 | downFetching: false, 7 | count: null, 8 | tokens: [], 9 | from: 0, 10 | to: 0, 11 | }; 12 | 13 | export function Tokens(state = initialState, action) { 14 | switch (action.type) { 15 | case FilterConstants.UPDATE_STATUS_FILTER: 16 | case FilterConstants.UPDATE_COLLECTIONS_FILTER: 17 | case FilterConstants.UPDATE_CATEGORIES_FILTER: 18 | case FilterConstants.UPDATE_GROUP_TYPE_FILTER: 19 | case FilterConstants.UPDATE_SORT_BY_FILTER: 20 | case TokenConstants.RESET_TOKENS: 21 | return initialState; 22 | case TokenConstants.UPDATE_TOKENS: 23 | return { 24 | ...state, 25 | tokens: action.payload.tokens, 26 | }; 27 | case TokenConstants.FETCHING_START: 28 | if (action.payload.direction < 0) { 29 | return { 30 | ...state, 31 | upFetching: true, 32 | }; 33 | } 34 | return { 35 | ...state, 36 | downFetching: true, 37 | }; 38 | case TokenConstants.FETCHING_SUCCESS: 39 | return { 40 | ...state, 41 | upFetching: false, 42 | downFetching: false, 43 | ...action.payload, 44 | }; 45 | case TokenConstants.FETCHING_FAILED: 46 | return { 47 | ...state, 48 | upFetching: false, 49 | downFetching: false, 50 | }; 51 | default: { 52 | return state; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/stores/reduxStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import thunkMiddleware from 'redux-thunk'; 3 | import { createLogger } from 'redux-logger'; 4 | 5 | import rootReducer from '../reducers/index'; 6 | 7 | const loggerMiddleware = createLogger(); 8 | 9 | export const store = createStore( 10 | rootReducer, 11 | applyMiddleware(thunkMiddleware, loggerMiddleware) 12 | ); 13 | -------------------------------------------------------------------------------- /src/utils/erc721.js: -------------------------------------------------------------------------------- 1 | const getTokenURI = async (tkId, fnft_sc) => { 2 | let tokenId = await fnft_sc.tokenURI(tkId); 3 | return tokenId; 4 | }; 5 | 6 | const ERC721Module = { 7 | getTokenURI, 8 | }; 9 | 10 | export default ERC721Module; 11 | -------------------------------------------------------------------------------- /src/utils/getLibrary.js: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from '@ethersproject/providers'; 2 | 3 | export default function getLibrary(provider) { 4 | const library = new Web3Provider( 5 | provider, 6 | typeof provider.chainId === 'number' 7 | ? provider.chainId 8 | : typeof provider.chainId === 'string' 9 | ? parseInt(provider.chainId) 10 | : 'any' 11 | ); 12 | library.pollingInterval = 15_000; 13 | library.detectNetwork(); 14 | return library; 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { getAddress } from '@ethersproject/address'; 3 | 4 | import { Categories } from 'constants/filter.constants'; 5 | import { IPFSUris } from 'constants/ipfs.constants'; 6 | import MetamaskErrors from 'constants/errors'; 7 | 8 | export function isAddress(value) { 9 | try { 10 | return getAddress(value); 11 | } catch { 12 | return false; 13 | } 14 | } 15 | 16 | function isValidCode(code) { 17 | return code in MetamaskErrors ? true : false; 18 | } 19 | 20 | export function shortenAddress(address, chars = 4) { 21 | if (!address) return ''; 22 | 23 | const parsed = isAddress(address); 24 | if (!parsed) { 25 | throw Error(`Invalid 'address' parameter '${address}'.`); 26 | } 27 | return `${parsed.substring(0, chars + 2)}...${parsed.substring(42 - chars)}`; 28 | } 29 | 30 | export const getHigherGWEI = async () => { 31 | const provider = new ethers.providers.Web3Provider(window.ethereum); 32 | const price = (await provider.getGasPrice()) * 2; 33 | 34 | return price; 35 | }; 36 | 37 | export const getRandomIPFS = (tokenURI, justURL = false) => { 38 | let random = Math.floor(Math.random() * IPFSUris.length); 39 | 40 | if (justURL) { 41 | return `${IPFSUris[random]}`; 42 | } 43 | 44 | if ( 45 | tokenURI.includes('gateway.pinata.cloud') || 46 | tokenURI.includes('cloudflare') || 47 | tokenURI.includes('ipfs.io') || 48 | tokenURI.includes('ipfs.infura.io') 49 | ) { 50 | return `${IPFSUris[random]}${tokenURI.split('ipfs/')[1]}`; 51 | } else if (tokenURI.includes('ipfs://')) { 52 | return `${IPFSUris[random]}${tokenURI.split('ipfs://')[1]}`; 53 | } 54 | 55 | return tokenURI; 56 | }; 57 | 58 | export const formatNumber = num => { 59 | if (isNaN(num) || num === null) return ''; 60 | let parts = num.toString().split('.'); 61 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); 62 | return parts.join('.'); 63 | }; 64 | 65 | export const formatCategory = category => { 66 | return Categories.find(item => item.id === category).label; 67 | }; 68 | 69 | export const formatError = error => { 70 | if (error.data) { 71 | if (isValidCode(error.data.code)) { 72 | return MetamaskErrors[String(error.data.code)]; 73 | } else { 74 | return error.data.message; 75 | } 76 | } else { 77 | if (error.message) { 78 | let message = error.message; 79 | let startIndex = message.indexOf('data'); 80 | 81 | if (startIndex < 0) { 82 | if (isValidCode(error.code)) { 83 | return MetamaskErrors[String(error.code)]; 84 | } 85 | } 86 | 87 | let code = String(message.substr(startIndex + 14, 6)); 88 | 89 | if (isValidCode(code)) { 90 | return MetamaskErrors[code]; 91 | } 92 | } 93 | } 94 | 95 | return 'Error!'; 96 | }; 97 | 98 | const intlFormat = num => { 99 | return new Intl.NumberFormat().format(Math.round(num * 10) / 10); 100 | }; 101 | 102 | export const formatFollowers = num => { 103 | if (num >= 1000000) return intlFormat(num / 1000000) + 'M'; 104 | if (num >= 1000) return intlFormat(num / 1000) + 'k'; 105 | return intlFormat(num); 106 | }; 107 | 108 | export const calculateGasMargin = value => { 109 | return value 110 | .mul(ethers.BigNumber.from(10000).add(ethers.BigNumber.from(1000))) 111 | .div(ethers.BigNumber.from(10000)); 112 | }; 113 | -------------------------------------------------------------------------------- /src/utils/ipfs.js: -------------------------------------------------------------------------------- 1 | const { default: axios } = require('axios'); 2 | 3 | const readMetadataFromIPFS = async path => { 4 | let fileContent = await axios.get(path); 5 | let status = fileContent.status; 6 | if (status != 200) return ''; 7 | fileContent = fileContent.data; 8 | return fileContent; 9 | }; 10 | 11 | const IPFSHandler = { 12 | readMetadataFromIPFS, 13 | }; 14 | 15 | export default IPFSHandler; 16 | -------------------------------------------------------------------------------- /src/utils/sc.interaction.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { useWeb3React } from '@web3-react/core'; 3 | import { ethers } from 'ethers'; 4 | 5 | const useContract = () => { 6 | const { chainId } = useWeb3React(); 7 | 8 | const loadContract = useCallback( 9 | async (address, abi) => { 10 | const provider = new ethers.providers.Web3Provider(window.ethereum); 11 | const signer = provider.getSigner(); 12 | return new ethers.Contract(address, abi, signer); 13 | }, 14 | [chainId] 15 | ); 16 | 17 | const getAccountBalance = useCallback( 18 | async address => { 19 | const provider = new ethers.providers.Web3Provider(window.ethereum); 20 | let balance = await provider.getBalance(address); 21 | balance = ethers.utils.formatEther(balance); 22 | return balance; 23 | }, 24 | [chainId] 25 | ); 26 | 27 | return { loadContract, getAccountBalance }; 28 | }; 29 | 30 | export default useContract; 31 | -------------------------------------------------------------------------------- /src/utils/toast/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import toast from 'react-hot-toast'; 3 | 4 | import iconInfo from 'assets/svgs/info.svg'; 5 | import iconSuccess from 'assets/svgs/success.svg'; 6 | import iconError from 'assets/svgs/error.svg'; 7 | import iconWarning from 'assets/svgs/warning.svg'; 8 | 9 | import styles from './styles.module.scss'; 10 | 11 | const icons = { 12 | info: iconInfo, 13 | success: iconSuccess, 14 | error: iconError, 15 | warning: iconWarning, 16 | }; 17 | 18 | export default (type, title, body = '', onClick = () => {}) => { 19 | return toast( 20 | () => ( 21 |
22 |
23 | {type} 24 | {title} 25 |
26 | {body.length > 0 &&
{body}
} 27 |
28 | ), 29 | { 30 | duration: 5000, 31 | className: styles.toast, 32 | } 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/utils/toast/styles.module.scss: -------------------------------------------------------------------------------- 1 | .toast { 2 | cursor: pointer; 3 | padding: 0; 4 | border-radius: 10px; 5 | background-color: #FFF; 6 | box-shadow: 0 4px 40px rgba(0, 0, 0, .1); 7 | } 8 | 9 | .toastInner { 10 | margin: -4px -10px; 11 | padding: 24px; 12 | } 13 | 14 | .header { 15 | display: flex; 16 | align-items: center; 17 | 18 | .icon { 19 | width: 24px; 20 | height: 24px; 21 | object-fit: contain; 22 | margin-right: 14px; 23 | } 24 | 25 | span { 26 | font-weight: 700; 27 | font-size: 20px; 28 | line-height: 24.36px; 29 | color: #3D3D3D; 30 | white-space: pre-line; 31 | } 32 | } 33 | 34 | .body { 35 | margin-top: 12px; 36 | font-weight: 400; 37 | font-size: 16px; 38 | line-height: 25.6px; 39 | color: #3D3D3D; 40 | white-space: pre-line; 41 | } -------------------------------------------------------------------------------- /src/utils/userAgent.js: -------------------------------------------------------------------------------- 1 | import UAParser from 'ua-parser-js'; 2 | 3 | const USER_AGENT_PARSED = new UAParser().getResult(); 4 | 5 | export const isDesktop = (() => { 6 | const userAgentOsName = USER_AGENT_PARSED.os.name || []; 7 | const isWindows = userAgentOsName.indexOf('Windows') !== -1; 8 | const isMac = userAgentOsName.indexOf('Mac') !== -1; 9 | return isWindows || isMac; 10 | })(); 11 | 12 | export const isMobile = (() => { 13 | const isMobileDevice = USER_AGENT_PARSED.device.type === 'mobile'; 14 | return isMobileDevice || !isDesktop; 15 | })(); 16 | -------------------------------------------------------------------------------- /src/utils/wallet.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | 3 | const checkBalance = async address => { 4 | const provider = new ethers.providers.Web3Provider(window.ethereum); 5 | let balance = await provider.getBalance(address); 6 | balance = ethers.utils.formatEther(balance); 7 | return balance; 8 | }; 9 | const WalletUtils = { 10 | checkBalance, 11 | }; 12 | 13 | export default WalletUtils; 14 | --------------------------------------------------------------------------------