Switch from cra to vite

This commit is contained in:
Gilbert 2024-04-11 16:40:22 -05:00
parent cc8f9527da
commit f8d706233e
36 changed files with 5163 additions and 6582 deletions

1
.node-version Normal file
View File

@ -0,0 +1 @@
v20.12.1

View File

@ -0,0 +1 @@
v20.12.1

View File

@ -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"
} }
} }

View File

@ -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 =

View File

@ -1 +0,0 @@
build

View 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 },
],
},
}

View File

@ -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"
}
}
}

View File

@ -21,4 +21,4 @@
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*

View File

@ -0,0 +1 @@
v20.12.1

View File

@ -1,3 +0,0 @@
# artifacts
build
coverage

View File

@ -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 cant go back!**
If you arent 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 youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt 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/).

View 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>

View File

@ -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

View File

@ -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"
} }
} }

View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -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 />,
}, },
]); ]);

View File

@ -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,
}, },
]; ];

View File

@ -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}

View 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>
);
};

View File

@ -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",
}); });
}; };

View File

@ -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}

View File

@ -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);

View File

@ -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,
}); });
}} }}

View File

@ -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({

View File

@ -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 });

View 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>
);
};

View 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,
});

View 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;
}

View File

@ -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" }]
} }

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}

View 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",
],
},
});

View File

@ -1,2 +0,0 @@
export * from './src/client';
export * from './src/types';

View File

@ -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"
} }
} }

View File

@ -0,0 +1,2 @@
export * from "./client";
export * from "./types";

View 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."

10876
yarn.lock

File diff suppressed because it is too large Load Diff