forked from cerc-io/snowballtools-base
Switch from cra to vite
This commit is contained in:
parent
cc8f9527da
commit
f8d706233e
1
.node-version
Normal file
1
.node-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
v20.12.1
|
1
packages/backend/.node-version
Normal file
1
packages/backend/.node-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
v20.12.1
|
@ -35,7 +35,6 @@
|
|||||||
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
|
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
|
||||||
"clean": "rm -rf ./dist",
|
"clean": "rm -rf ./dist",
|
||||||
"build": "yarn clean && tsc && yarn copy-assets",
|
"build": "yarn clean && tsc && yarn copy-assets",
|
||||||
"lint": "eslint .",
|
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check .",
|
"format:check": "prettier --check .",
|
||||||
"test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts",
|
"test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts",
|
||||||
@ -46,20 +45,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express-session": "^1.17.10",
|
"@types/express-session": "^1.17.10",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
|
||||||
"@typescript-eslint/parser": "^6.18.1",
|
|
||||||
"better-sqlite3": "^9.2.2",
|
"better-sqlite3": "^9.2.2",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-config-semistandard": "^15.0.1",
|
|
||||||
"eslint-config-standard": "^16.0.3",
|
|
||||||
"eslint-plugin-import": "^2.27.5",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"workspace": "^0.0.1-preview.1"
|
"workspace": "^0.0.1-preview.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
REACT_APP_SERVER_URL = 'http://localhost:8000'
|
|
||||||
|
|
||||||
REACT_APP_GITHUB_CLIENT_ID =
|
|
||||||
REACT_APP_GITHUB_PWA_TEMPLATE_REPO =
|
|
||||||
REACT_APP_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO =
|
|
||||||
|
|
||||||
REACT_APP_WALLET_CONNECT_ID =
|
|
@ -1 +0,0 @@
|
|||||||
build
|
|
18
packages/frontend/.eslintrc.cjs
Normal file
18
packages/frontend/.eslintrc.cjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"jsx": true
|
|
||||||
},
|
|
||||||
"ecmaVersion": 13,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es2021": true
|
|
||||||
},
|
|
||||||
"plugins": ["react", "@typescript-eslint"],
|
|
||||||
"extends": [
|
|
||||||
"plugin:react/recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:prettier/recommended"
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
packages/frontend/.gitignore
vendored
2
packages/frontend/.gitignore
vendored
@ -21,4 +21,4 @@
|
|||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
1
packages/frontend/.node-version
Normal file
1
packages/frontend/.node-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
v20.12.1
|
@ -1,3 +0,0 @@
|
|||||||
# artifacts
|
|
||||||
build
|
|
||||||
coverage
|
|
@ -1,46 +1,30 @@
|
|||||||
# Getting Started with Create React App
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app), using [typescript-tailwindcss-eslint-prettier](https://github.com/cufarvid/cra-templates) template.
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
## Available Scripts
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
In the project directory, you can run:
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
### `yarn start`
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
- Configure the top-level `parserOptions` property like this:
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `yarn test`
|
```js
|
||||||
|
export default {
|
||||||
|
// other rules...
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||||
### `yarn build`
|
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `yarn eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
||||||
|
36
packages/frontend/index.html
Normal file
36
packages/frontend/index.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="description" content="snowball tools dashboard" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
<meta name="msapplication-TileColor" content="#2d89ef" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<title>Snowball</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,7 +0,0 @@
|
|||||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="500" height="500" fill="#0F86F5"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M191.873 125.126C224.893 126.765 250.458 150.121 274.042 172.995C297.925 196.158 323.089 221.108 324.868 254.114C326.718 288.42 308.902 321.108 283.281 344.355C258.67 366.687 225.288 373.859 191.873 374.788C157.228 375.752 119.038 374.394 95.1648 349.588C71.6207 325.125 74.6696 287.843 75.7341 254.114C76.7518 221.865 79.2961 188.525 101.009 164.41C123.845 139.047 157.543 123.423 191.873 125.126Z" fill="#4BA4F7"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M229.373 125.126C262.393 126.765 287.958 150.121 311.542 172.995C335.425 196.158 360.589 221.108 362.368 254.114C364.218 288.42 346.402 321.108 320.781 344.355C296.17 366.687 262.788 373.859 229.373 374.788C194.728 375.752 156.538 374.394 132.665 349.588C109.121 325.125 112.17 287.843 113.234 254.114C114.252 221.865 116.796 188.525 138.509 164.41C161.345 139.047 195.043 123.423 229.373 125.126Z" fill="#8AC4FA"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M266.873 125.126C299.893 126.765 325.458 150.121 349.042 172.995C372.925 196.158 398.089 221.108 399.868 254.114C401.718 288.42 383.902 321.108 358.281 344.355C333.67 366.687 300.288 373.859 266.873 374.788C232.228 375.752 194.038 374.394 170.165 349.588C146.621 325.125 149.67 287.843 150.734 254.114C151.752 221.865 154.296 188.525 176.009 164.41C198.845 139.047 232.543 123.423 266.873 125.126Z" fill="#CAE4FD"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M304.373 125.126C337.393 126.765 362.958 150.121 386.542 172.995C410.425 196.158 435.589 221.108 437.368 254.114C439.218 288.42 421.402 321.108 395.781 344.355C371.17 366.687 337.788 373.859 304.373 374.788C269.728 375.752 231.538 374.394 207.665 349.588C184.121 325.125 187.17 287.843 188.234 254.114C189.252 221.865 191.796 188.525 213.509 164.41C236.345 139.047 270.043 123.423 304.373 125.126Z" fill="white"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.0 KiB |
@ -1,7 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.1.8",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port 3000",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/jetbrains-mono": "^5.0.19",
|
"@fontsource-variable/jetbrains-mono": "^5.0.19",
|
||||||
"@fontsource/inter": "^5.0.16",
|
"@fontsource/inter": "^5.0.16",
|
||||||
@ -15,6 +21,13 @@
|
|||||||
"@radix-ui/react-tabs": "^1.0.4",
|
"@radix-ui/react-tabs": "^1.0.4",
|
||||||
"@radix-ui/react-toast": "^1.1.5",
|
"@radix-ui/react-toast": "^1.1.5",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
|
"@snowballtools/auth": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/auth",
|
||||||
|
"@snowballtools/auth-lit": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/auth-lit",
|
||||||
|
"@snowballtools/js-sdk": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/js-sdk",
|
||||||
|
"@snowballtools/link-lit-alchemy-light": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/link-lit-alchemy-light",
|
||||||
|
"@snowballtools/smartwallet-alchemy-light": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/smartwallet-alchemy-light",
|
||||||
|
"@snowballtools/types": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/types",
|
||||||
|
"@snowballtools/utils": "/Users/rabbit-m2/p/snowball/snowball-ts-sdk/packages/utils",
|
||||||
"@tanstack/react-query": "^5.22.2",
|
"@tanstack/react-query": "^5.22.2",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
@ -23,6 +36,7 @@
|
|||||||
"@types/node": "^16.18.68",
|
"@types/node": "^16.18.68",
|
||||||
"@types/react": "^18.2.42",
|
"@types/react": "^18.2.42",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
|
"@walletconnect/ethereum-provider": "^2.12.2",
|
||||||
"@web3modal/siwe": "^4.0.5",
|
"@web3modal/siwe": "^4.0.5",
|
||||||
"@web3modal/wagmi": "^4.0.5",
|
"@web3modal/wagmi": "^4.0.5",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
@ -30,7 +44,6 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"downshift": "^8.3.2",
|
"downshift": "^8.3.2",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
|
||||||
"framer-motion": "^11.0.8",
|
"framer-motion": "^11.0.8",
|
||||||
"gql-client": "^1.0.0",
|
"gql-client": "^1.0.0",
|
||||||
"lottie-react": "^2.4.0",
|
"lottie-react": "^2.4.0",
|
||||||
@ -46,54 +59,24 @@
|
|||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-oauth-popup": "^1.0.5",
|
"react-oauth-popup": "^1.0.5",
|
||||||
"react-router-dom": "^6.20.1",
|
"react-router-dom": "^6.20.1",
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"react-timer-hook": "^3.0.7",
|
"react-timer-hook": "^3.0.7",
|
||||||
"siwe": "^2.1.4",
|
"siwe": "^2.1.4",
|
||||||
"tailwind-variants": "^0.2.0",
|
"tailwind-variants": "^0.2.0",
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"usehooks-ts": "^2.15.1",
|
"usehooks-ts": "^2.15.1",
|
||||||
"vertical-stepper-nav": "^1.0.2",
|
|
||||||
"viem": "^2.7.11",
|
"viem": "^2.7.11",
|
||||||
"wagmi": "^2.5.7",
|
"wagmi": "^2.5.7",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"start": "react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test": "react-scripts test",
|
|
||||||
"eject": "react-scripts eject",
|
|
||||||
"format": "prettier --write .",
|
|
||||||
"format:check": "prettier --check .",
|
|
||||||
"lint": "eslint ."
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
|
||||||
"@types/luxon": "^3.3.7",
|
"@types/luxon": "^3.3.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
"@types/react": "^18.2.66",
|
||||||
"@typescript-eslint/parser": "^6.13.2",
|
"@types/react-dom": "^18.2.22",
|
||||||
"eslint": "^8.55.0",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint-plugin-prettier": "^5.0.1",
|
"postcss": "^8.4.38",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.3",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"vite": "^5.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
6
packages/frontend/postcss.config.js
Normal file
6
packages/frontend/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
@ -1,51 +1,51 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
|
|
||||||
import Projects from './pages/org-slug';
|
import Projects from "./pages/org-slug";
|
||||||
import Settings from './pages/org-slug/Settings';
|
import Settings from "./pages/org-slug/Settings";
|
||||||
import {
|
import {
|
||||||
projectsRoutesWithSearch,
|
projectsRoutesWithSearch,
|
||||||
projectsRoutesWithoutSearch,
|
projectsRoutesWithoutSearch,
|
||||||
} from './pages/org-slug/projects/routes';
|
} from "./pages/org-slug/projects/routes";
|
||||||
import ProjectSearchLayout from './layouts/ProjectSearch';
|
import ProjectSearchLayout from "./layouts/ProjectSearch";
|
||||||
import Index from './pages';
|
import Index from "./pages";
|
||||||
import Login from './pages/Login';
|
import Login from "./pages/Login";
|
||||||
import { DashboardLayout } from 'pages/org-slug/layout';
|
import { DashboardLayout } from "./pages/org-slug/layout";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: ':orgSlug',
|
path: ":orgSlug",
|
||||||
element: <DashboardLayout />,
|
element: <DashboardLayout />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
element: <ProjectSearchLayout />,
|
element: <ProjectSearchLayout />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: "",
|
||||||
element: <Projects />,
|
element: <Projects />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'projects',
|
path: "projects",
|
||||||
children: projectsRoutesWithSearch,
|
children: projectsRoutesWithSearch,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'settings',
|
path: "settings",
|
||||||
element: <Settings />,
|
element: <Settings />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'projects',
|
path: "projects",
|
||||||
children: projectsRoutesWithoutSearch,
|
children: projectsRoutesWithoutSearch,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: "/",
|
||||||
element: <Index />,
|
element: <Index />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: "/login",
|
||||||
element: <Login />,
|
element: <Login />,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: "1",
|
||||||
name: 'Progressive Web App (PWA)',
|
name: "Progressive Web App (PWA)",
|
||||||
icon: 'pwa',
|
icon: "pwa",
|
||||||
repoFullName: `${process.env.REACT_APP_GITHUB_PWA_TEMPLATE_REPO}`,
|
repoFullName: `${import.meta.env.VITE_GITHUB_PWA_TEMPLATE_REPO}`,
|
||||||
isComingSoon: false,
|
isComingSoon: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: "2",
|
||||||
name: 'Image Upload PWA',
|
name: "Image Upload PWA",
|
||||||
icon: 'pwa',
|
icon: "pwa",
|
||||||
repoFullName: `${process.env.REACT_APP_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
|
repoFullName: `${import.meta.env.VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
|
||||||
isComingSoon: false,
|
isComingSoon: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: "3",
|
||||||
name: 'Kotlin',
|
name: "Kotlin",
|
||||||
icon: 'kotlin',
|
icon: "kotlin",
|
||||||
repoFullName: '',
|
repoFullName: "",
|
||||||
isComingSoon: true,
|
isComingSoon: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: "4",
|
||||||
name: 'React Native',
|
name: "React Native",
|
||||||
icon: 'react-native',
|
icon: "react-native",
|
||||||
repoFullName: '',
|
repoFullName: "",
|
||||||
isComingSoon: true,
|
isComingSoon: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: "5",
|
||||||
name: 'Swift',
|
name: "Swift",
|
||||||
icon: 'swift',
|
icon: "swift",
|
||||||
repoFullName: '',
|
repoFullName: "",
|
||||||
isComingSoon: true,
|
isComingSoon: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { StepperNav } from 'vertical-stepper-nav';
|
import { StepperNav } from "./VerticalStepper";
|
||||||
|
|
||||||
const COLOR_COMPLETED = '#059669';
|
const COLOR_COMPLETED = "#059669";
|
||||||
const COLOR_ACTIVE = '#CFE6FC';
|
const COLOR_ACTIVE = "#CFE6FC";
|
||||||
const COLOR_NOT_STARTED = '#F1F5F9';
|
const COLOR_NOT_STARTED = "#F1F5F9";
|
||||||
|
|
||||||
interface StepperValue {
|
interface StepperValue {
|
||||||
step: number;
|
step: number;
|
||||||
@ -25,8 +25,8 @@ const Stepper = ({ activeStep, stepperValues }: StepperProps) => {
|
|||||||
<div
|
<div
|
||||||
className={`text-sm ${
|
className={`text-sm ${
|
||||||
activeStep === stepperValue.step
|
activeStep === stepperValue.step
|
||||||
? 'text-black font-semibold'
|
? "text-black font-semibold"
|
||||||
: 'text-gray-600'
|
: "text-gray-600"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{stepperValue.label}
|
{stepperValue.label}
|
||||||
|
121
packages/frontend/src/components/VerticalStepper.tsx
Normal file
121
packages/frontend/src/components/VerticalStepper.tsx
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as CSS from 'csstype';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Nav
|
||||||
|
//
|
||||||
|
export interface IStepDescription {
|
||||||
|
stepContent: () => JSX.Element;
|
||||||
|
stepStateColor?: string;
|
||||||
|
stepStatusCircleSize?: number;
|
||||||
|
onClickHandler?: () => void | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStepperNavProps {
|
||||||
|
steps: IStepDescription[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StepperNav = (props: IStepperNavProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<nav>
|
||||||
|
{props.steps.map(
|
||||||
|
(
|
||||||
|
{ stepContent, stepStateColor, onClickHandler, stepStatusCircleSize },
|
||||||
|
index,
|
||||||
|
) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Step
|
||||||
|
stepContent={stepContent}
|
||||||
|
statusColor={stepStateColor}
|
||||||
|
onClickHandler={onClickHandler}
|
||||||
|
statusCircleSize={stepStatusCircleSize}
|
||||||
|
/>
|
||||||
|
{index !== props.steps.length - 1 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingLeft: `${(stepStatusCircleSize ?? 16) / 2 + 1}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Separator />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Separator
|
||||||
|
//
|
||||||
|
const separatorStyles = {
|
||||||
|
height: '5vh',
|
||||||
|
width: 2,
|
||||||
|
border: '1px solid #E1E1E1',
|
||||||
|
background: '#E1E1E1',
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ISeparator {
|
||||||
|
height?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Separator = ({ height }: ISeparator): JSX.Element => {
|
||||||
|
return <div style={{ ...separatorStyles, height: height ?? '5vh' }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Step
|
||||||
|
//
|
||||||
|
export interface IStep {
|
||||||
|
stepContent: () => JSX.Element;
|
||||||
|
statusColor?: string;
|
||||||
|
statusCircleSize?: number;
|
||||||
|
onClickHandler?: (
|
||||||
|
event?: React.MouseEvent<HTMLDivElement>,
|
||||||
|
) => void | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonContainerStyles: CSS.Properties = {
|
||||||
|
display: 'inline-flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '12px',
|
||||||
|
padding: '2px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Step = ({
|
||||||
|
stepContent,
|
||||||
|
statusColor,
|
||||||
|
statusCircleSize,
|
||||||
|
onClickHandler,
|
||||||
|
}: IStep): JSX.Element => {
|
||||||
|
const circleStyles = {
|
||||||
|
borderRadius: statusCircleSize ?? 16,
|
||||||
|
width: statusCircleSize ?? 16,
|
||||||
|
height: statusCircleSize ?? 16,
|
||||||
|
border: '2px solid #E1E1E1',
|
||||||
|
background: statusColor ?? 'white',
|
||||||
|
};
|
||||||
|
|
||||||
|
const keyDownHandler = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (event.keyCode === 13 || event.keyCode === 32) {
|
||||||
|
onClickHandler?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={onClickHandler}
|
||||||
|
onKeyDown={keyDownHandler}
|
||||||
|
role="button"
|
||||||
|
style={{ ...buttonContainerStyles }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div style={circleStyles} />
|
||||||
|
</div>
|
||||||
|
<div style={{ paddingBottom: 2 }}>{stepContent()}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,23 +1,23 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import OauthPopup from 'react-oauth-popup';
|
import OauthPopup from "react-oauth-popup";
|
||||||
|
|
||||||
import { useGQLClient } from '../../../context/GQLClientContext';
|
import { useGQLClient } from "../../../context/GQLClientContext";
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from "../../shared/Button";
|
||||||
import {
|
import {
|
||||||
GitIcon,
|
GitIcon,
|
||||||
EllipsesIcon,
|
EllipsesIcon,
|
||||||
SnowballIcon,
|
SnowballIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
GitTeaIcon,
|
GitTeaIcon,
|
||||||
} from 'components/shared/CustomIcon';
|
} from "../../shared/CustomIcon";
|
||||||
import { useToast } from 'components/shared/Toast';
|
import { useToast } from "../../shared/Toast";
|
||||||
import { IconWithFrame } from 'components/shared/IconWithFrame';
|
import { IconWithFrame } from "../../shared/IconWithFrame";
|
||||||
import { Heading } from 'components/shared/Heading';
|
import { Heading } from "../../shared/Heading";
|
||||||
import { MockConnectGitCard } from './MockConnectGitCard';
|
import { MockConnectGitCard } from "./MockConnectGitCard";
|
||||||
|
|
||||||
const SCOPES = 'repo user';
|
const SCOPES = "repo user";
|
||||||
const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${
|
const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${
|
||||||
process.env.REACT_APP_GITHUB_CLIENT_ID
|
import.meta.env.VITE_GITHUB_CLIENT_ID
|
||||||
}&scope=${encodeURIComponent(SCOPES)}`;
|
}&scope=${encodeURIComponent(SCOPES)}`;
|
||||||
|
|
||||||
interface ConnectAccountInterface {
|
interface ConnectAccountInterface {
|
||||||
@ -39,9 +39,9 @@ const ConnectAccount: React.FC<ConnectAccountInterface> = ({
|
|||||||
onToken(token);
|
onToken(token);
|
||||||
toast({
|
toast({
|
||||||
onDismiss: dismiss,
|
onDismiss: dismiss,
|
||||||
id: 'connected-to-github',
|
id: "connected-to-github",
|
||||||
title: 'The Git account is connected.',
|
title: "The Git account is connected.",
|
||||||
variant: 'success',
|
variant: "success",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
import { Tabs } from 'components/shared/Tabs';
|
import { Tabs } from "components/shared/Tabs";
|
||||||
|
|
||||||
const ConnectAccountTabPanel: React.FC = () => {
|
const ConnectAccountTabPanel: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
@ -11,8 +11,8 @@ const ConnectAccountTabPanel: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
{[
|
{[
|
||||||
{ title: 'Import a repository' },
|
{ title: "Import a repository" },
|
||||||
{ title: 'Start with a template' },
|
{ title: "Start with a template" },
|
||||||
].map(({ title }, index) => (
|
].map(({ title }, index) => (
|
||||||
<Tabs.Trigger value={title} key={index}>
|
<Tabs.Trigger value={title} key={index}>
|
||||||
{title}
|
{title}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from "react";
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import { DeployStep, DeployStatus } from './DeployStep';
|
import { DeployStep, DeployStatus } from "./DeployStep";
|
||||||
import { Stopwatch, setStopWatchOffset } from 'components/StopWatch';
|
import { Stopwatch, setStopWatchOffset } from "../../StopWatch";
|
||||||
import { Heading } from 'components/shared/Heading';
|
import { Heading } from "../../shared/Heading";
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from "../../shared/Button";
|
||||||
import { ClockOutlineIcon, WarningIcon } from 'components/shared/CustomIcon';
|
import { ClockOutlineIcon, WarningIcon } from "../../shared/CustomIcon";
|
||||||
import { CancelDeploymentDialog } from 'components/projects/Dialog/CancelDeploymentDialog';
|
import { CancelDeploymentDialog } from "../../projects/Dialog/CancelDeploymentDialog";
|
||||||
|
|
||||||
const TIMEOUT_DURATION = 5000;
|
const TIMEOUT_DURATION = 5000;
|
||||||
const Deploy = () => {
|
const Deploy = () => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const projectId = searchParams.get('projectId');
|
const projectId = searchParams.get("projectId");
|
||||||
|
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
const handleOpen = () => setOpen(!open);
|
const handleOpen = () => setOpen(!open);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { Collapse } from '@material-tailwind/react';
|
import { Collapse } from "@material-tailwind/react";
|
||||||
|
|
||||||
import { Stopwatch, setStopWatchOffset } from '../../StopWatch';
|
import { Stopwatch, setStopWatchOffset } from "../../StopWatch";
|
||||||
import FormatMillisecond from '../../FormatMilliSecond';
|
import FormatMillisecond from "../../FormatMilliSecond";
|
||||||
import processLogs from '../../../assets/process-logs.json';
|
import processLogs from "../../../assets/process-logs.json";
|
||||||
import { cn } from 'utils/classnames';
|
import { cn } from "utils/classnames";
|
||||||
import {
|
import {
|
||||||
CheckRoundFilledIcon,
|
CheckRoundFilledIcon,
|
||||||
ClockOutlineIcon,
|
ClockOutlineIcon,
|
||||||
@ -13,15 +13,15 @@ import {
|
|||||||
LoaderIcon,
|
LoaderIcon,
|
||||||
MinusCircleIcon,
|
MinusCircleIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
} from 'components/shared/CustomIcon';
|
} from "components/shared/CustomIcon";
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from "components/shared/Button";
|
||||||
import { useToast } from 'components/shared/Toast';
|
import { useToast } from "components/shared/Toast";
|
||||||
import { useIntersectionObserver } from 'usehooks-ts';
|
import { useIntersectionObserver } from "usehooks-ts";
|
||||||
|
|
||||||
enum DeployStatus {
|
enum DeployStatus {
|
||||||
PROCESSING = 'progress',
|
PROCESSING = "progress",
|
||||||
COMPLETE = 'complete',
|
COMPLETE = "complete",
|
||||||
NOT_STARTED = 'notStarted',
|
NOT_STARTED = "notStarted",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeployStepsProps {
|
interface DeployStepsProps {
|
||||||
@ -52,8 +52,8 @@ const DeployStep = ({
|
|||||||
{/* Collapisble trigger */}
|
{/* Collapisble trigger */}
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex justify-between w-full py-5 gap-2',
|
"flex justify-between w-full py-5 gap-2",
|
||||||
disableCollapse && 'cursor-auto',
|
disableCollapse && "cursor-auto",
|
||||||
)}
|
)}
|
||||||
tabIndex={disableCollapse ? -1 : undefined}
|
tabIndex={disableCollapse ? -1 : undefined}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -62,7 +62,7 @@ const DeployStep = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={cn('grow flex items-center gap-3')}>
|
<div className={cn("grow flex items-center gap-3")}>
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<div className="w-6 h-6 grid place-content-center">
|
<div className="w-6 h-6 grid place-content-center">
|
||||||
{status === DeployStatus.NOT_STARTED && (
|
{status === DeployStatus.NOT_STARTED && (
|
||||||
@ -84,8 +84,8 @@ const DeployStep = ({
|
|||||||
{/* Title */}
|
{/* Title */}
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-left text-sm md:text-base',
|
"text-left text-sm md:text-base",
|
||||||
status === DeployStatus.PROCESSING && 'text-elements-link',
|
status === DeployStatus.PROCESSING && "text-elements-link",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
@ -107,7 +107,7 @@ const DeployStep = ({
|
|||||||
size={15}
|
size={15}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<FormatMillisecond time={Number(processTime)} />{' '}
|
<FormatMillisecond time={Number(processTime)} />{" "}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
@ -133,15 +133,15 @@ const DeployStep = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Copy log button */}
|
{/* Copy log button */}
|
||||||
<div className={cn('sticky bottom-4 left-1/2 flex justify-center')}>
|
<div className={cn("sticky bottom-4 left-1/2 flex justify-center")}>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigator.clipboard.writeText(processLogs.join('\n'));
|
navigator.clipboard.writeText(processLogs.join("\n"));
|
||||||
toast({
|
toast({
|
||||||
title: 'Logs copied',
|
title: "Logs copied",
|
||||||
variant: 'success',
|
variant: "success",
|
||||||
id: 'logs',
|
id: "logs",
|
||||||
onDismiss: dismiss,
|
onDismiss: dismiss,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -1,83 +1,83 @@
|
|||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from "react";
|
||||||
import { SiweMessage } from 'siwe';
|
import { SiweMessage } from "siwe";
|
||||||
import { WagmiProvider } from 'wagmi';
|
import { WagmiProvider } from "wagmi";
|
||||||
import { arbitrum, mainnet } from 'wagmi/chains';
|
import { arbitrum, mainnet } from "wagmi/chains";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
|
|
||||||
import { createWeb3Modal } from '@web3modal/wagmi/react';
|
import { createWeb3Modal } from "@web3modal/wagmi/react";
|
||||||
import { defaultWagmiConfig } from '@web3modal/wagmi/react/config';
|
import { defaultWagmiConfig } from "@web3modal/wagmi/react/config";
|
||||||
import { createSIWEConfig } from '@web3modal/siwe';
|
import { createSIWEConfig } from "@web3modal/siwe";
|
||||||
import type {
|
import type {
|
||||||
SIWECreateMessageArgs,
|
SIWECreateMessageArgs,
|
||||||
SIWEVerifyMessageArgs,
|
SIWEVerifyMessageArgs,
|
||||||
} from '@web3modal/core';
|
} from "@web3modal/core";
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: process.env.REACT_APP_SERVER_URL,
|
baseURL: import.meta.env.VITE_SERVER_URL,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
'Access-Control-Allow-Origin': '*',
|
"Access-Control-Allow-Origin": "*",
|
||||||
},
|
},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const metadata = {
|
const metadata = {
|
||||||
name: 'Snowball Tools',
|
name: "Snowball Tools",
|
||||||
description: 'Snowball Tools Dashboard',
|
description: "Snowball Tools Dashboard",
|
||||||
url: window.location.origin,
|
url: window.location.origin,
|
||||||
icons: [
|
icons: [
|
||||||
'https://raw.githubusercontent.com/snowball-tools/mediakit/main/assets/logo.svg',
|
"https://raw.githubusercontent.com/snowball-tools/mediakit/main/assets/logo.svg",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const chains = [mainnet, arbitrum] as const;
|
const chains = [mainnet, arbitrum] as const;
|
||||||
const config = defaultWagmiConfig({
|
const config = defaultWagmiConfig({
|
||||||
chains,
|
chains,
|
||||||
projectId: process.env.REACT_APP_WALLET_CONNECT_ID,
|
projectId: import.meta.env.VITE_WALLET_CONNECT_ID,
|
||||||
metadata,
|
metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
const siweConfig = createSIWEConfig({
|
const siweConfig = createSIWEConfig({
|
||||||
createMessage: ({ nonce, address, chainId }: SIWECreateMessageArgs) =>
|
createMessage: ({ nonce, address, chainId }: SIWECreateMessageArgs) =>
|
||||||
new SiweMessage({
|
new SiweMessage({
|
||||||
version: '1',
|
version: "1",
|
||||||
domain: window.location.host,
|
domain: window.location.host,
|
||||||
uri: window.location.origin,
|
uri: window.location.origin,
|
||||||
address,
|
address,
|
||||||
chainId,
|
chainId,
|
||||||
nonce,
|
nonce,
|
||||||
// Human-readable ASCII assertion that the user will sign, and it must not contain `\n`.
|
// Human-readable ASCII assertion that the user will sign, and it must not contain `\n`.
|
||||||
statement: 'Sign in With Ethereum.',
|
statement: "Sign in With Ethereum.",
|
||||||
}).prepareMessage(),
|
}).prepareMessage(),
|
||||||
getNonce: async () => {
|
getNonce: async () => {
|
||||||
const nonce = (await axiosInstance.get('/auth/nonce')).data;
|
const nonce = (await axiosInstance.get("/auth/nonce")).data;
|
||||||
if (!nonce) {
|
if (!nonce) {
|
||||||
throw new Error('Failed to get nonce!');
|
throw new Error("Failed to get nonce!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return nonce;
|
return nonce;
|
||||||
},
|
},
|
||||||
getSession: async () => {
|
getSession: async () => {
|
||||||
try {
|
try {
|
||||||
const session = (await axiosInstance.get('/auth/session')).data;
|
const session = (await axiosInstance.get("/auth/session")).data;
|
||||||
const { address, chainId } = session;
|
const { address, chainId } = session;
|
||||||
|
|
||||||
return { address, chainId };
|
return { address, chainId };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (window.location.pathname !== '/login') {
|
if (window.location.pathname !== "/login") {
|
||||||
window.location.href = '/login';
|
window.location.href = "/login";
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Failed to get session!');
|
throw new Error("Failed to get session!");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
verifyMessage: async ({ message, signature }: SIWEVerifyMessageArgs) => {
|
verifyMessage: async ({ message, signature }: SIWEVerifyMessageArgs) => {
|
||||||
try {
|
try {
|
||||||
const { success } = (
|
const { success } = (
|
||||||
await axiosInstance.post('/auth/validate', {
|
await axiosInstance.post("/auth/validate", {
|
||||||
message,
|
message,
|
||||||
signature,
|
signature,
|
||||||
})
|
})
|
||||||
@ -90,28 +90,28 @@ const siweConfig = createSIWEConfig({
|
|||||||
},
|
},
|
||||||
signOut: async () => {
|
signOut: async () => {
|
||||||
try {
|
try {
|
||||||
const { success } = (await axiosInstance.post('/auth/logout')).data;
|
const { success } = (await axiosInstance.post("/auth/logout")).data;
|
||||||
return success;
|
return success;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSignOut: () => {
|
onSignOut: () => {
|
||||||
window.location.href = '/login';
|
window.location.href = "/login";
|
||||||
},
|
},
|
||||||
onSignIn: () => {
|
onSignIn: () => {
|
||||||
window.location.href = '/';
|
window.location.href = "/";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!process.env.REACT_APP_WALLET_CONNECT_ID) {
|
if (!import.meta.env.VITE_WALLET_CONNECT_ID) {
|
||||||
throw new Error('Error: REACT_APP_WALLET_CONNECT_ID env config is not set');
|
throw new Error("Error: REACT_APP_WALLET_CONNECT_ID env config is not set");
|
||||||
}
|
}
|
||||||
|
|
||||||
createWeb3Modal({
|
createWeb3Modal({
|
||||||
siweConfig,
|
siweConfig,
|
||||||
wagmiConfig: config,
|
wagmiConfig: config,
|
||||||
projectId: process.env.REACT_APP_WALLET_CONNECT_ID,
|
projectId: import.meta.env.VITE_WALLET_CONNECT_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function Web3ModalProvider({
|
export default function Web3ModalProvider({
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import assert from 'assert';
|
import assert from "assert";
|
||||||
import { GQLClient } from 'gql-client';
|
import { GQLClient } from "gql-client";
|
||||||
|
|
||||||
import { ThemeProvider } from '@material-tailwind/react';
|
import { ThemeProvider } from "@material-tailwind/react";
|
||||||
|
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import '@fontsource/inter';
|
import "@fontsource/inter";
|
||||||
import '@fontsource-variable/jetbrains-mono';
|
import "@fontsource-variable/jetbrains-mono";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import { GQLClientProvider } from './context/GQLClientContext';
|
import { GQLClientProvider } from "./context/GQLClientContext";
|
||||||
import { SERVER_GQL_PATH } from './constants';
|
import { SERVER_GQL_PATH } from "./constants";
|
||||||
import { Toaster } from 'components/shared/Toast';
|
import { Toaster } from "components/shared/Toast";
|
||||||
import Web3ModalProvider from './context/Web3ModalProvider';
|
import Web3ModalProvider from "./context/Web3ModalProvider";
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement,
|
document.getElementById("root") as HTMLElement,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
process.env.REACT_APP_SERVER_URL,
|
import.meta.env.VITE_SERVER_URL,
|
||||||
'REACT_APP_SERVER_URL is not set in env',
|
"REACT_APP_SERVER_URL is not set in env",
|
||||||
);
|
);
|
||||||
const gqlEndpoint = `${process.env.REACT_APP_SERVER_URL}/${SERVER_GQL_PATH}`;
|
const gqlEndpoint = `${import.meta.env.VITE_SERVER_URL}/${SERVER_GQL_PATH}`;
|
||||||
|
|
||||||
const gqlClient = new GQLClient({ gqlEndpoint });
|
const gqlClient = new GQLClient({ gqlEndpoint });
|
||||||
|
|
||||||
|
18
packages/frontend/src/pages/SnowballLogin.tsx
Normal file
18
packages/frontend/src/pages/SnowballLogin.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useSnowball } from 'utils/use-snowball';
|
||||||
|
|
||||||
|
export const SnowballLogin = () => {
|
||||||
|
const snowball = useSnowball();
|
||||||
|
console.log(snowball);
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-screen bg-snowball-900 snow">
|
||||||
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
<img
|
||||||
|
src="/logo.svg"
|
||||||
|
alt="snowball logo"
|
||||||
|
className="w-32 h-32 rounded-full mb-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
19
packages/frontend/src/utils/snowball.ts
Normal file
19
packages/frontend/src/utils/snowball.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { LitGoogleAuth } from "@snowballtools/auth-lit";
|
||||||
|
import { Snowball, SnowballChain } from "@snowballtools/js-sdk";
|
||||||
|
// import { LinkLitAlchemyLight } from '@snowballtools/link-lit-alchemy-light';
|
||||||
|
|
||||||
|
export const DOMAIN = import.meta.env.VITE_DOMAIN || "localhost";
|
||||||
|
export const ORIGIN =
|
||||||
|
import.meta.env.VITE_VERCEL_ENV === "production"
|
||||||
|
? `https://${DOMAIN}`
|
||||||
|
: `http://${DOMAIN}:3000`;
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
|
||||||
|
export const snowball = Snowball.withAuth(
|
||||||
|
LitGoogleAuth.configure({
|
||||||
|
litReplayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
|
||||||
|
}),
|
||||||
|
).create({
|
||||||
|
initialChain: SnowballChain.sepolia,
|
||||||
|
});
|
13
packages/frontend/src/utils/use-snowball.ts
Normal file
13
packages/frontend/src/utils/use-snowball.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { snowball } from './snowball';
|
||||||
|
|
||||||
|
export function useSnowball() {
|
||||||
|
const [state, setState] = useState(100);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Subscribe and directly return the unsubscribe function
|
||||||
|
return snowball.subscribe(() => setState(state + 1));
|
||||||
|
}, [snowball]);
|
||||||
|
|
||||||
|
return snowball;
|
||||||
|
}
|
@ -1,21 +1,32 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "ES2020",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"useDefineForClassFields": true,
|
||||||
"allowJs": true,
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
/* Bundler mode */
|
||||||
"strict": true,
|
"moduleResolution": "bundler",
|
||||||
"forceConsistentCasingInFileNames": true,
|
"allowImportingTsExtensions": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"baseUrl": "src"
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
"paths": {
|
||||||
|
"utils/*": ["./src/utils/*"],
|
||||||
|
"assets/*": ["./src/assets/*"],
|
||||||
|
"context/*": ["./src/context/*"],
|
||||||
|
"components/*": ["./src/components/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
11
packages/frontend/tsconfig.node.json
Normal file
11
packages/frontend/tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
29
packages/frontend/vite.config.ts
Normal file
29
packages/frontend/vite.config.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
utils: "/src/utils",
|
||||||
|
assets: "/src/assets",
|
||||||
|
context: "/src/context",
|
||||||
|
components: "/src/components",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
"process.env": "import.meta.env",
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: [
|
||||||
|
// "@snowballtools/types",
|
||||||
|
// "@snowballtools/utils",
|
||||||
|
// "@snowballtools/auth",
|
||||||
|
// "@snowballtools/auth-lit",
|
||||||
|
// "@snowballtools/smartwallet-alchemy-light",
|
||||||
|
// "@snowballtools/link-lit-alchemy-light",
|
||||||
|
// "@snowballtools/js-sdk",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
@ -1,2 +0,0 @@
|
|||||||
export * from './src/client';
|
|
||||||
export * from './src/types';
|
|
@ -2,25 +2,16 @@
|
|||||||
"name": "gql-client",
|
"name": "gql-client",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
"module": "./dist/index.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"build": "npx tsup src/index.ts --dts --format esm,cjs --sourcemap"
|
||||||
"build": "tsc"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.11.4",
|
"@types/node": "^20.11.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
"tsup": "^8.0.2",
|
||||||
"@typescript-eslint/parser": "^6.19.0",
|
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-config-semistandard": "^17.0.0",
|
|
||||||
"eslint-config-standard": "^17.1.0",
|
|
||||||
"eslint-plugin-import": "^2.29.1",
|
|
||||||
"eslint-plugin-n": "^16.6.2",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.8.9"
|
"@apollo/client": "^3.8.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
2
packages/gql-client/src/index.ts
Normal file
2
packages/gql-client/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./client";
|
||||||
|
export * from "./types";
|
67
scripts/yarn-file-for-local-dev.sh
Executable file
67
scripts/yarn-file-for-local-dev.sh
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export ENV_FILE=packages/frontend/.env
|
||||||
|
|
||||||
|
# Load the .env file
|
||||||
|
if [ ! -f $ENV_FILE ]; then
|
||||||
|
echo "$ENV_FILE file not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
source $ENV_FILE
|
||||||
|
|
||||||
|
# Check if the LOCAL_SNOWBALL_SDK_DIR variable is set
|
||||||
|
if [ -z "$LOCAL_SNOWBALL_SDK_DIR" ]; then
|
||||||
|
echo "LOCAL_SNOWBALL_SDK_DIR is not set in the .env file."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define the list of package names, each on its own line
|
||||||
|
packages=(
|
||||||
|
"types"
|
||||||
|
"utils"
|
||||||
|
"auth"
|
||||||
|
"auth-lit"
|
||||||
|
"smartwallet-alchemy-light"
|
||||||
|
"link-lit-alchemy-light"
|
||||||
|
"js-sdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for the --reset flag
|
||||||
|
RESET=false
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ $arg == "--reset" ]]; then
|
||||||
|
RESET=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If --reset flag is provided, remove a specific package first
|
||||||
|
if [ "$RESET" = true ]; then
|
||||||
|
# Build the remove command
|
||||||
|
cmd="yarn workspace frontend remove"
|
||||||
|
|
||||||
|
# Append each package path to the command
|
||||||
|
for pkg in "${packages[@]}"; do
|
||||||
|
cmd+=" @snowballtools/$pkg"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Removing packages..."
|
||||||
|
echo "Executing: $cmd"
|
||||||
|
eval $cmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Build the add command
|
||||||
|
cmd="yarn workspace frontend add"
|
||||||
|
|
||||||
|
# Append each package path to the command
|
||||||
|
for pkg in "${packages[@]}"; do
|
||||||
|
cmd+=" $LOCAL_SNOWBALL_SDK_DIR/packages/$pkg"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Adding packages..."
|
||||||
|
echo "Executing: $cmd"
|
||||||
|
eval $cmd
|
||||||
|
|
||||||
|
echo "SDK packages locally installed."
|
Loading…
Reference in New Issue
Block a user