mirror of
https://github.com/apotdevin/thunderhub.git
synced 2026-08-13 12:33:08 +02:00
feat: big cleanup and refactor (#644)
* feat: big cleanup and refactor * chore: fix config * chore: security stuff * chore: dep bumps * chore: more version bumps * chore: move to lucide icons * chore: change slider * chore: more stuff * chore: move component to shadcn * chore: remove numeral * chore: remove night theme * chore: move to sse * chore: cleanup deps * feat!: remove lnmarkets integration * feat!: remove rebalancing flow * chore: move to svg progress bar * chore: new toast lib * chore!: remove accounting report tool * chore: dependency updates * chore: donate button changes * chore: cleanup file * chore: cleanup * ci: build changes * fix: routing
This commit is contained in:
parent
b2ca712ef1
commit
d94e7c341c
354 changed files with 14258 additions and 19884 deletions
|
|
@ -4,17 +4,18 @@
|
|||
*.md
|
||||
!README*.md
|
||||
/node_modules
|
||||
/.next
|
||||
src/client/.next
|
||||
/dist
|
||||
/docs
|
||||
/.github
|
||||
.vscode
|
||||
CHANGELOG.md
|
||||
coverage
|
||||
test
|
||||
src/client/dist
|
||||
|
||||
# all env files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.production.local
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
node_modules
|
||||
.next
|
||||
src/client/.next
|
||||
|
||||
**/*.generated.tsx
|
||||
src/graphql/types.ts
|
||||
26
.eslintrc.js
26
.eslintrc.js
|
|
@ -1,26 +0,0 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
},
|
||||
};
|
||||
55
.github/workflows/docker-release.yml
vendored
Normal file
55
.github/workflows/docker-release.yml
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
name: Build and push Docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build and push standard image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
apotdevin/thunderhub:${{ steps.version.outputs.version }}
|
||||
apotdevin/thunderhub:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build and push base-path image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: BASE_PATH=/thub
|
||||
tags: |
|
||||
apotdevin/thunderhub:base-${{ steps.version.outputs.version }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,8 +1,7 @@
|
|||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
/.next
|
||||
src/client/.next
|
||||
src/client/dist
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
|
@ -48,3 +47,6 @@ config.yaml
|
|||
# For Dev
|
||||
local_data
|
||||
docker-compose.dev.yml
|
||||
|
||||
# LLM
|
||||
.claude
|
||||
|
|
@ -1,4 +1 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run lint-staged
|
||||
|
|
|
|||
2
.nvmrc
2
.nvmrc
|
|
@ -1 +1 @@
|
|||
v18.18.2
|
||||
v24.13.1
|
||||
35
Dockerfile
35
Dockerfile
|
|
@ -1,11 +1,13 @@
|
|||
FROM node:24.13.1-alpine AS base
|
||||
|
||||
# ---------------
|
||||
# Install Dependencies
|
||||
# ---------------
|
||||
FROM node:18.18.2-alpine as deps
|
||||
FROM base AS deps
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies neccesary for node-gyp on node alpine
|
||||
# Install dependencies necessary for node-gyp on node alpine
|
||||
RUN apk add --update --no-cache \
|
||||
libc6-compat \
|
||||
python3 \
|
||||
|
|
@ -19,7 +21,7 @@ RUN npm ci
|
|||
# ---------------
|
||||
# Build App
|
||||
# ---------------
|
||||
FROM deps as build
|
||||
FROM deps AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
@ -28,41 +30,36 @@ ARG BASE_PATH=""
|
|||
ENV BASE_PATH=${BASE_PATH}
|
||||
ARG NODE_ENV="production"
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Build the NestJS and NextJS application
|
||||
# Build the NestJS and Vite application
|
||||
COPY . .
|
||||
RUN npm run build:nest
|
||||
RUN npm run build:next
|
||||
RUN npm run build:nest && npm run build:client
|
||||
|
||||
# Remove non production necessary modules
|
||||
RUN npm prune --production
|
||||
RUN npm prune --omit=dev
|
||||
|
||||
# ---------------
|
||||
# Release App
|
||||
# ---------------
|
||||
FROM node:18.18.2-alpine as final
|
||||
FROM base AS final
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Set env variables
|
||||
ARG BASE_PATH=""
|
||||
ENV BASE_PATH=${BASE_PATH}
|
||||
ARG NODE_ENV="production"
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
# Copy build artifacts
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=build /app/node_modules/ ./node_modules
|
||||
|
||||
# Copy NextJS files
|
||||
COPY --from=build /app/src/client/public ./src/client/public
|
||||
COPY --from=build /app/src/client/next.config.js ./src/client/
|
||||
COPY --from=build /app/src/client/.next/ ./src/client/.next
|
||||
|
||||
# Copy NestJS files
|
||||
COPY --from=build /app/src/client/dist/ ./src/client/dist
|
||||
COPY --from=build /app/dist/ ./dist
|
||||
|
||||
# Run as non-root user
|
||||
USER node
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "npm", "run", "start:prod" ]
|
||||
|
|
|
|||
21
components.json
Normal file
21
components.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/client/src/styles/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
71
eslint.config.js
Normal file
71
eslint.config.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
const {
|
||||
defineConfig,
|
||||
globalIgnores,
|
||||
} = require("eslint/config");
|
||||
|
||||
const tsParser = require("@typescript-eslint/parser");
|
||||
const typescriptEslintEslintPlugin = require("@typescript-eslint/eslint-plugin");
|
||||
const globals = require("globals");
|
||||
const js = require("@eslint/js");
|
||||
|
||||
const {
|
||||
FlatCompat,
|
||||
} = require("@eslint/eslintrc");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
module.exports = defineConfig([{
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
sourceType: "module",
|
||||
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
},
|
||||
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
},
|
||||
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslintEslintPlugin,
|
||||
},
|
||||
|
||||
extends: compat.extends(
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier",
|
||||
),
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
},
|
||||
}, {
|
||||
files: ["src/client/**/*.ts", "src/client/**/*.tsx"],
|
||||
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: "src/client/tsconfig.json",
|
||||
},
|
||||
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
},
|
||||
}, globalIgnores([
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
"**/*.generated.tsx",
|
||||
])]);
|
||||
22592
package-lock.json
generated
22592
package-lock.json
generated
File diff suppressed because it is too large
Load diff
229
package.json
229
package.json
|
|
@ -4,22 +4,23 @@
|
|||
"description": "Lightning Node Manager",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist && rimraf .next",
|
||||
"build": "npm run build:nest && npm run build:next",
|
||||
"prebuild": "rimraf dist && rimraf src/client/dist",
|
||||
"build": "npm run build:nest && npm run build:client",
|
||||
"build:nest": "nest build",
|
||||
"build:next": "cd src/client && next build",
|
||||
"build:client": "cd src/client && npx vite build",
|
||||
"build:all": "sh ./scripts/buildMultiArchImage.sh",
|
||||
"build:all:test": "sh ./scripts/buildMultiArchImage.sh --test",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "cross-env NODE_ENV=production nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:dev": "concurrently \"nest start --watch\" \"cd src/client && npx vite\"",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod:env": "NODE_ENV=production node dist/main",
|
||||
"release": "standard-version",
|
||||
"release:test": "standard-version --dry-run",
|
||||
"release:minor": "standard-version --release-as minor",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\"",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.{ts,tsx}\" --fix",
|
||||
"lint:check": "eslint \"{src,apps,libs,test}/**/*.{ts,tsx}\"",
|
||||
"lint-staged": "lint-staged",
|
||||
"generate": "graphql-codegen --config codegen.yml",
|
||||
"test": "jest --passWithNoTests",
|
||||
|
|
@ -31,146 +32,130 @@
|
|||
"update": "sh ./scripts/updateToLatest.sh",
|
||||
"build:image": "docker build --pull --rm -f Dockerfile -t thunderhub:testing '.'",
|
||||
"build:image:base": "docker build --pull --rm -f Dockerfile --build-arg BASE_PATH=/thub -t thunderhub:testing .",
|
||||
"prepare": "husky install"
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.8.8",
|
||||
"@fullerstack/nax-ipware": "^0.10.0",
|
||||
"@nestjs/apollo": "^12.0.11",
|
||||
"@nestjs/common": "^10.2.10",
|
||||
"@nestjs/config": "^3.1.1",
|
||||
"@nestjs/core": "^10.2.10",
|
||||
"@nestjs/graphql": "^12.0.11",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/passport": "^10.0.2",
|
||||
"@nestjs/platform-express": "^10.2.10",
|
||||
"@nestjs/platform-socket.io": "^10.2.10",
|
||||
"@nestjs/schedule": "^4.0.0",
|
||||
"@nestjs/throttler": "^5.0.1",
|
||||
"@nestjs/websockets": "^10.2.10",
|
||||
"@tanstack/react-table": "^8.10.7",
|
||||
"@apollo/server": "^5.4.0",
|
||||
"@as-integrations/express5": "^1.1.2",
|
||||
"@nestjs/apollo": "^13.2.4",
|
||||
"@nestjs/common": "^11.1.14",
|
||||
"@nestjs/config": "^4.0.3",
|
||||
"@nestjs/core": "^11.1.14",
|
||||
"@nestjs/graphql": "^13.2.4",
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.1.14",
|
||||
"@nestjs/schedule": "^6.1.1",
|
||||
"@nestjs/serve-static": "^5.0.4",
|
||||
"@nestjs/throttler": "^6.5.0",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@vulpemventures/secp256k1-zkp": "^3.2.1",
|
||||
"apollo-server-express": "^3.13.0",
|
||||
"async": "^3.2.6",
|
||||
"balanceofsatoshis": "^17.5.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"bech32": "^2.0.0",
|
||||
"big.js": "^6.2.1",
|
||||
"big.js": "^7.0.1",
|
||||
"bip32": "^4.0.0",
|
||||
"bip39": "^3.1.0",
|
||||
"bitcoinjs-lib": "^6.1.5",
|
||||
"boltz-core": "^2.1.1",
|
||||
"cookie": "^0.6.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"d3-array": "^3.2.4",
|
||||
"bitcoinjs-lib": "^6.1.7",
|
||||
"boltz-core": "^3.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cookie": "^1.1.1",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"dataloader": "^2.2.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"echarts": "^5.5.0",
|
||||
"echarts-for-react": "^3.0.2",
|
||||
"ecpair": "^2.0.1",
|
||||
"graphql": "^16.8.1",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"dataloader": "^2.2.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"echarts": "^6.0.0",
|
||||
"echarts-for-react": "^3.0.6",
|
||||
"ecpair": "^3.0.1",
|
||||
"graphql": "^16.12.0",
|
||||
"helmet": "^8.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lightning": "^10.1.3",
|
||||
"lodash": "^4.17.21",
|
||||
"nest-winston": "^1.9.4",
|
||||
"next": "^13.5.5",
|
||||
"node-fetch": "^3.3.2",
|
||||
"numeral": "^2.0.6",
|
||||
"otplib": "^12.0.1",
|
||||
"passport": "^0.6.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"lightning": "^11.0.1",
|
||||
"lodash": "^4.17.23",
|
||||
"lucide-react": "^0.574.0",
|
||||
"nest-winston": "^1.10.2",
|
||||
"otplib": "^13.3.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-grid-layout": "^1.4.4",
|
||||
"react-is": "^18.2.0",
|
||||
"react-select": "^5.8.0",
|
||||
"react-slider": "^2.0.6",
|
||||
"react-spinners": "^0.13.8",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-tooltip": "^5.24.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^5.0.5",
|
||||
"rxjs": "^7.5.6",
|
||||
"secp256k1": "^5.0.0",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"socks-proxy-agent": "^8.0.1",
|
||||
"styled-components": "^6.1.1",
|
||||
"styled-react-modal": "^3.0.1",
|
||||
"react-grid-layout": "^2.2.2",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-router-dom": "^7.13.0",
|
||||
"react-tooltip": "^5.30.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.2",
|
||||
"secp256k1": "^5.0.1",
|
||||
"socks-proxy-agent": "^8.0.5",
|
||||
"styled-components": "^6.3.9",
|
||||
"styled-theming": "^2.2.0",
|
||||
"stylis": "^4.3.0",
|
||||
"tiny-secp256k1": "^2.2.3",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.11.0"
|
||||
"tailwind-merge": "^3.4.1",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"tiny-secp256k1": "^2.2.4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"uuid": "^13.0.0",
|
||||
"winston": "^3.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^5.0.0",
|
||||
"@graphql-codegen/fragment-matcher": "^5.0.0",
|
||||
"@graphql-codegen/introspection": "^4.0.0",
|
||||
"@graphql-codegen/near-operation-file-preset": "^3.0.0",
|
||||
"@graphql-codegen/typescript": "^4.0.1",
|
||||
"@graphql-codegen/typescript-operations": "^4.0.1",
|
||||
"@graphql-codegen/typescript-react-apollo": "^4.1.0",
|
||||
"@graphql-codegen/typescript-resolvers": "^4.0.1",
|
||||
"@nestjs/cli": "^10.2.1",
|
||||
"@nestjs/schematics": "^10.0.3",
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@graphql-codegen/cli": "^6.1.1",
|
||||
"@graphql-codegen/fragment-matcher": "^6.0.0",
|
||||
"@graphql-codegen/introspection": "^5.0.0",
|
||||
"@graphql-codegen/near-operation-file-preset": "^4.0.0",
|
||||
"@graphql-codegen/typescript": "^5.0.7",
|
||||
"@graphql-codegen/typescript-operations": "^5.0.7",
|
||||
"@graphql-codegen/typescript-react-apollo": "^4.4.0",
|
||||
"@graphql-codegen/typescript-resolvers": "^5.1.5",
|
||||
"@nestjs/cli": "^11.0.16",
|
||||
"@nestjs/schematics": "^11.0.9",
|
||||
"@types/async": "^3.2.25",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/bcryptjs": "^3.0.0",
|
||||
"@types/big.js": "^6.2.2",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/cron": "^2.4.0",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@types/d3-array": "^3.2.1",
|
||||
"@types/cookie": "^1.0.0",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jest": "^29.5.10",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^20.10.1",
|
||||
"@types/numeral": "^2.0.5",
|
||||
"@types/qrcode.react": "^1.0.5",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@types/react-qr-reader": "^2.1.7",
|
||||
"@types/react-slider": "^1.3.5",
|
||||
"@types/secp256k1": "^4.0.6",
|
||||
"@types/styled-react-modal": "^1.2.5",
|
||||
"@types/styled-theming": "^2.2.8",
|
||||
"@types/supertest": "^2.0.16",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||
"@typescript-eslint/parser": "^6.13.1",
|
||||
"apollo-server": "^3.13.0",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/lodash": "^4.17.23",
|
||||
"@types/node": "^25.2.3",
|
||||
"@types/qrcode.react": "^3.0.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-grid-layout": "^2.1.0",
|
||||
"@types/secp256k1": "^4.0.7",
|
||||
"@types/styled-theming": "^2.2.9",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
||||
"@typescript-eslint/parser": "^8.56.0",
|
||||
"@vitejs/plugin-react": "^5.1.4",
|
||||
"babel-plugin-styled-components": "^2.1.4",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-next": "^14.0.3",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.1.0",
|
||||
"prettier": "^3.1.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"concurrently": "^9.2.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"globals": "^17.3.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^30.2.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"prettier": "^3.8.1",
|
||||
"rimraf": "^6.1.3",
|
||||
"standard-version": "^9.5.0",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-jest": "^29.4.6",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.3.2",
|
||||
"ws": "^8.14.2"
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
|
|
|
|||
71
schema.gql
71
schema.gql
|
|
@ -65,28 +65,11 @@ type Balances {
|
|||
onchain: OnChainBalance!
|
||||
}
|
||||
|
||||
type BaseInvoice {
|
||||
id: String!
|
||||
request: String!
|
||||
}
|
||||
|
||||
type BaseNode {
|
||||
_id: String
|
||||
name: String
|
||||
public_key: String!
|
||||
socket: String!
|
||||
}
|
||||
|
||||
type BaseNodeInfo {
|
||||
alias: String!
|
||||
public_key: String!
|
||||
}
|
||||
|
||||
type BasePoints {
|
||||
alias: String!
|
||||
amount: Float!
|
||||
}
|
||||
|
||||
type BitcoinFee {
|
||||
fast: Float!
|
||||
halfHour: Float!
|
||||
|
|
@ -116,37 +99,6 @@ type BoltzSwapTransaction {
|
|||
id: String
|
||||
}
|
||||
|
||||
type BosDecrease {
|
||||
decreased_inbound_on: String!
|
||||
liquidity_inbound: String!
|
||||
liquidity_inbound_opening: String
|
||||
liquidity_inbound_pending: String
|
||||
liquidity_outbound: String!
|
||||
liquidity_outbound_opening: String
|
||||
liquidity_outbound_pending: String
|
||||
}
|
||||
|
||||
type BosIncrease {
|
||||
increased_inbound_on: String!
|
||||
liquidity_inbound: String!
|
||||
liquidity_inbound_opening: String
|
||||
liquidity_inbound_pending: String
|
||||
liquidity_outbound: String!
|
||||
liquidity_outbound_opening: String
|
||||
liquidity_outbound_pending: String
|
||||
}
|
||||
|
||||
type BosRebalanceResult {
|
||||
decrease: BosDecrease
|
||||
increase: BosIncrease
|
||||
result: BosResult
|
||||
}
|
||||
|
||||
type BosResult {
|
||||
rebalance_fees_spent: String!
|
||||
rebalanced: String!
|
||||
}
|
||||
|
||||
type ChainAddressSend {
|
||||
confirmationCount: Float!
|
||||
id: String!
|
||||
|
|
@ -450,15 +402,6 @@ type LightningNodeSocialInfo {
|
|||
socials: NodeSocial
|
||||
}
|
||||
|
||||
type LnMarketsUserInfo {
|
||||
account_type: String
|
||||
balance: String
|
||||
last_ip: String
|
||||
linkingpublickey: String
|
||||
uid: String
|
||||
username: String
|
||||
}
|
||||
|
||||
union LnUrlRequest = ChannelRequest | PayRequest | WithdrawRequest
|
||||
|
||||
type Message {
|
||||
|
|
@ -478,23 +421,16 @@ type MessageType {
|
|||
|
||||
type Mutation {
|
||||
addPeer(isTemporary: Boolean, publicKey: String, socket: String, url: String): Boolean!
|
||||
bosRebalance(avoid: [String!], in_through: String, max_fee: Float, max_fee_rate: Float, max_rebalance: Float, node: String, out_inbound: Float, out_through: String, timeout_minutes: Float): BosRebalanceResult!
|
||||
claimBoltzTransaction(destination: String!, fee: Float!, id: String!, lockupAddress: String!, preimage: String!, privateKey: String!, redeem: String!): String!
|
||||
closeChannel(forceClose: Boolean, id: String!, targetConfirmations: Float, tokensPerVByte: Float): OpenOrCloseChannel!
|
||||
createAddress(type: String! = "p2tr"): String!
|
||||
createBaseInvoice(amount: Float!): BaseInvoice!
|
||||
createBoltzReverseSwap(address: String, amount: Float!): CreateBoltzReverseSwapType!
|
||||
createInvoice(amount: Float!, description: String, includePrivate: Boolean, secondsUntil: Float): CreateInvoice!
|
||||
createMacaroon(permissions: NetworkInfoInput!): CreateMacaroon!
|
||||
createThunderPoints(alias: String!, id: String!, public_key: String!, uris: [String!]!): Boolean!
|
||||
fetchLnUrl(url: String!): LnUrlRequest!
|
||||
getAuthToken(cookie: String): Boolean!
|
||||
getSessionToken(id: String!, password: String!, token: String): String!
|
||||
keysend(destination: String, tokens: Float!): PayInvoice!
|
||||
lnMarketsDeposit(amount: Float!): Boolean!
|
||||
lnMarketsLogin: AuthResponse!
|
||||
lnMarketsLogout: Boolean!
|
||||
lnMarketsWithdraw(amount: Float!): Boolean!
|
||||
lnUrlAuth(url: String!): AuthResponse!
|
||||
lnUrlChannel(callback: String!, k1: String!, uri: String!): String!
|
||||
lnUrlPay(amount: Float!, callback: String!, comment: String): PaySuccess!
|
||||
|
|
@ -740,13 +676,9 @@ type Policy {
|
|||
type Query {
|
||||
decodeRequest(request: String!): DecodeInvoice!
|
||||
getAccount: ServerAccount!
|
||||
getAccountingReport(category: String, currency: String, fiat: String, month: String, year: String): String!
|
||||
getAmbossLoginToken(redirect_url: String): String!
|
||||
getAmbossUser: AmbossUser
|
||||
getBackups: String!
|
||||
getBaseCanConnect: Boolean!
|
||||
getBaseNodes: [BaseNode!]!
|
||||
getBasePoints: [BasePoints!]!
|
||||
getBitcoinFees: BitcoinFee!
|
||||
getBitcoinPrice: String!
|
||||
getBoltzInfo: BoltzInfoType!
|
||||
|
|
@ -765,9 +697,6 @@ type Query {
|
|||
getLatestVersion: String!
|
||||
getLightningAddressInfo(address: String!): PayRequest!
|
||||
getLiquidityPerUsd: String!
|
||||
getLnMarketsStatus: String!
|
||||
getLnMarketsUrl: String!
|
||||
getLnMarketsUserInfo: LnMarketsUserInfo!
|
||||
getMessages(initialize: Boolean): GetMessages!
|
||||
getNetworkInfo: NetworkInfo!
|
||||
getNode(publicKey: String!, withoutChannels: Boolean): Node!
|
||||
|
|
|
|||
|
|
@ -4,6 +4,43 @@ trap "exit" INT
|
|||
|
||||
REPO=apotdevin/thunderhub
|
||||
BASE=base
|
||||
TEST_MODE=false
|
||||
|
||||
# Parse flags
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--test) TEST_MODE=true; shift;;
|
||||
*) echo "Unknown option: $1"; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$TEST_MODE" = true ]; then
|
||||
echo
|
||||
echo "------------------------------------------"
|
||||
echo "Test build (local, no push, no branch switch)"
|
||||
echo "------------------------------------------"
|
||||
echo
|
||||
|
||||
docker buildx create --name mybuilder --driver-opt network=host --use 2>/dev/null || true
|
||||
|
||||
START=`date +%s`
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag $REPO:test \
|
||||
--file ./Dockerfile \
|
||||
./
|
||||
|
||||
END=`date +%s`
|
||||
RUNTIME=$((END-START))
|
||||
|
||||
echo
|
||||
echo "------------------------------------------"
|
||||
echo "DONE - Test build took $RUNTIME seconds"
|
||||
echo "------------------------------------------"
|
||||
echo
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo
|
||||
|
|
@ -26,7 +63,7 @@ echo "Do you want to build images for version" $VERSION "?"
|
|||
select yn in "Yes" "No" "Specify"; do
|
||||
case $yn in
|
||||
Yes ) break;;
|
||||
Specify) NOT_LATEST=true; break;;
|
||||
Specify) NOT_LATEST=true; break;;
|
||||
No ) exit;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -36,16 +73,19 @@ read -p "Enter the version you want to build: " VERSION
|
|||
git checkout $VERSION || exit
|
||||
fi
|
||||
|
||||
# docker buildx create --use
|
||||
|
||||
START=`date +%s`
|
||||
|
||||
docker buildx create --name mybuilder --use
|
||||
docker buildx install
|
||||
docker buildx create --name mybuilder --driver-opt network=host --use 2>/dev/null || true
|
||||
|
||||
docker build \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||
LATEST_TAGS=""
|
||||
if [ "$NOT_LATEST" = false ]; then
|
||||
LATEST_TAGS="--tag $REPO:latest"
|
||||
fi
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag $REPO:$VERSION \
|
||||
$LATEST_TAGS \
|
||||
--file ./Dockerfile \
|
||||
--push ./
|
||||
|
||||
|
|
@ -57,9 +97,9 @@ echo "Building basepath multiarch image for" $REPO
|
|||
echo
|
||||
echo
|
||||
|
||||
docker build \
|
||||
docker buildx build \
|
||||
--build-arg BASE_PATH='/thub' \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag $REPO:$BASE-$VERSION \
|
||||
--file ./Dockerfile \
|
||||
--push ./
|
||||
|
|
@ -81,4 +121,4 @@ echo
|
|||
echo "multiarch took" $RUNTIME "seconds"
|
||||
echo "multiarch base took" $RUNTIME1 "seconds"
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ else
|
|||
echo "Installing dependencies..."
|
||||
npm install --quiet
|
||||
|
||||
# build nextjs
|
||||
# build app
|
||||
echo "Building application..."
|
||||
npm run build
|
||||
|
||||
# remove useless deps
|
||||
echo "Removing unneccesary modules..."
|
||||
npm prune --production
|
||||
# remove dev deps
|
||||
echo "Removing unnecessary modules..."
|
||||
npm prune --omit=dev
|
||||
|
||||
TAG=$(git tag | sort -V | tail -1)
|
||||
echo "Updated to version" $TAG
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'next/core-web-vitals',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
// '@next/next/no-html-link-for-pages': ['error', '/src/client/pages/'],
|
||||
},
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint @typescript-eslint/no-var-requires: 0 */
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
ApolloClient,
|
||||
|
|
@ -8,23 +6,18 @@ import {
|
|||
InMemoryCache,
|
||||
NormalizedCacheObject,
|
||||
} from '@apollo/client';
|
||||
import getConfig from 'next/config';
|
||||
import { config } from '../src/config/thunderhubConfig';
|
||||
import possibleTypes from '../src/graphql/fragmentTypes.json';
|
||||
import { onError } from '@apollo/client/link/error';
|
||||
import { setContext } from '@apollo/client/link/context';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { apiUrl: uri } = publicRuntimeConfig;
|
||||
|
||||
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined;
|
||||
|
||||
export type ResolverContext = {
|
||||
req?: IncomingMessage;
|
||||
res?: ServerResponse;
|
||||
};
|
||||
|
||||
function createApolloClient(authToken: string) {
|
||||
const httpLink = createHttpLink({ uri, credentials: 'include' });
|
||||
const httpLink = createHttpLink({
|
||||
uri: config.apiUrl,
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
const authLink = setContext((_, { headers }) => {
|
||||
return {
|
||||
|
|
@ -51,7 +44,7 @@ function createApolloClient(authToken: string) {
|
|||
|
||||
return new ApolloClient({
|
||||
credentials: 'same-origin',
|
||||
ssrMode: typeof window === 'undefined',
|
||||
ssrMode: false,
|
||||
link,
|
||||
cache: new InMemoryCache({
|
||||
...possibleTypes,
|
||||
|
|
@ -98,7 +91,6 @@ export function initializeApollo(
|
|||
const existingCache = _apolloClient.extract();
|
||||
_apolloClient.cache.restore({ ...existingCache, ...initialState });
|
||||
}
|
||||
if (typeof window === 'undefined') return _apolloClient;
|
||||
if (!apolloClient) apolloClient = _apolloClient;
|
||||
|
||||
return _apolloClient;
|
||||
|
|
|
|||
17
src/client/index.html
Normal file
17
src/client/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Manage and monitor your lightning network node right inside your browser"
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
5
src/client/next-env.d.ts
vendored
5
src/client/next-env.d.ts
vendored
|
|
@ -1,5 +0,0 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config({ path: path.resolve(process.cwd(), '.env.local') });
|
||||
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
|
||||
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
poweredByHeader: false,
|
||||
basePath: process.env.BASE_PATH || '',
|
||||
transpilePackages: ['echarts', 'zrender'],
|
||||
compiler: {
|
||||
styledComponents: true,
|
||||
},
|
||||
publicRuntimeConfig: {
|
||||
mempoolUrl: process.env.MEMPOOL_URL || 'https://mempool.space',
|
||||
disable2FA: process.env.DISABLE_TWOFA === 'true',
|
||||
apiUrl: `${process.env.BASE_PATH || ''}/graphql`,
|
||||
basePath: process.env.BASE_PATH || '',
|
||||
npmVersion: process.env.npm_package_version || '0.0.0',
|
||||
defaultTheme: process.env.THEME || 'dark',
|
||||
defaultCurrency: process.env.CURRENCY || 'sat',
|
||||
fetchPrices: process.env.FETCH_PRICES === 'false' ? false : true,
|
||||
fetchFees: process.env.FETCH_FEES === 'false' ? false : true,
|
||||
disableLinks: process.env.DISABLE_LINKS === 'true',
|
||||
disableLnMarkets: process.env.DISABLE_LNMARKETS === 'true',
|
||||
noVersionCheck: process.env.NO_VERSION_CHECK === 'true',
|
||||
logoutUrl: process.env.LOGOUT_URL || '',
|
||||
},
|
||||
};
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
import { FC, ReactNode, useEffect } from 'react';
|
||||
import { StyleSheetManager, ThemeProvider } from 'styled-components';
|
||||
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
|
||||
import { AppProps } from 'next/app';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { useApollo } from '../config/client';
|
||||
import { BaseProvider } from '../src/context/BaseContext';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
import { Header } from '../src/layouts/header/Header';
|
||||
import { Footer } from '../src/layouts/footer/Footer';
|
||||
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { useListener } from '../src/hooks/UseListener';
|
||||
import { SocketProvider } from '../src/context/SocketContext';
|
||||
import { useRouter } from 'next/router';
|
||||
import getConfig from 'next/config';
|
||||
import Head from 'next/head';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
|
||||
import 'react-toastify/dist/ReactToastify.min.css';
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import 'react-resizable/css/styles.css';
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { logoutUrl } = publicRuntimeConfig;
|
||||
|
||||
function shouldForwardProp(propName: string, target: any) {
|
||||
if (typeof target === 'string') {
|
||||
// For HTML elements, forward the prop if it is a valid HTML attribute
|
||||
return isPropValid(propName);
|
||||
}
|
||||
// For other elements, forward all props
|
||||
return true;
|
||||
}
|
||||
|
||||
const NotAuthenticated: React.FC = () => {
|
||||
const { push } = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
push(logoutUrl || '/login');
|
||||
}, [push]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const Listener: FC<{ isRoot: boolean }> = ({ isRoot }) => {
|
||||
useListener(isRoot);
|
||||
return null;
|
||||
};
|
||||
|
||||
const Wrapper: React.FC<{ authenticated: boolean; children?: ReactNode }> = ({
|
||||
children,
|
||||
authenticated,
|
||||
}) => {
|
||||
const { theme } = useConfigState();
|
||||
const { pathname } = useRouter();
|
||||
|
||||
const isRoot = pathname === '/login' || pathname === '/sso';
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={{ mode: isRoot ? 'light' : theme }}>
|
||||
<ModalProvider backgroundComponent={BaseModalBackground}>
|
||||
<GlobalStyles />
|
||||
<PageWrapper>
|
||||
<HeaderBodyWrapper>
|
||||
<Header />
|
||||
<Listener isRoot={isRoot} />
|
||||
{authenticated ? children : <NotAuthenticated />}
|
||||
</HeaderBodyWrapper>
|
||||
<Footer />
|
||||
<ToastContainer theme={theme === 'light' ? 'light' : 'dark'} />
|
||||
</PageWrapper>
|
||||
</ModalProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const {
|
||||
initialApolloState,
|
||||
initialConfig,
|
||||
hasToken,
|
||||
authToken,
|
||||
authenticated,
|
||||
} = pageProps;
|
||||
|
||||
const apolloClient = useApollo(authToken, initialApolloState);
|
||||
|
||||
return (
|
||||
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ConfigProvider initialConfig={initialConfig}>
|
||||
<BaseProvider initialHasToken={hasToken}>
|
||||
<SocketProvider authToken={authToken}>
|
||||
<ContextProvider>
|
||||
<Wrapper authenticated={authenticated}>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</SocketProvider>
|
||||
</BaseProvider>
|
||||
</ConfigProvider>
|
||||
</ApolloProvider>
|
||||
</StyleSheetManager>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import Document, {
|
||||
DocumentContext,
|
||||
Html,
|
||||
Head,
|
||||
Main,
|
||||
NextScript,
|
||||
} from 'next/document';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
// eslint-disable-next-line react/display-name
|
||||
enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<meta
|
||||
name="description"
|
||||
content="Manage and monitor your lightning network node right inside your browser"
|
||||
key="description"
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import Error from 'next/error';
|
||||
|
||||
function Page({ statusCode }: any) {
|
||||
return <Error statusCode={statusCode}></Error>;
|
||||
}
|
||||
|
||||
Page.getInitialProps = ({ res, err }: any) => {
|
||||
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
|
||||
return { statusCode };
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../../src/components/gridWrapper/GridWrapper';
|
||||
import { SingleLine } from '../../src/components/generic/Styled';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../../src/utils/ssr';
|
||||
import { AmbossLoginButton } from '../../src/views/amboss/LoginButton';
|
||||
import { Backups } from '../../src/views/amboss/Backups';
|
||||
import { SectionTitle, Text } from '../../src/components/typography/Styled';
|
||||
import { Healthchecks } from '../../src/views/amboss/Healthchecks';
|
||||
import { Balances } from '../../src/views/amboss/Balances';
|
||||
import { Billboard } from '../../src/views/amboss/Billboard';
|
||||
|
||||
const AmbossView = () => (
|
||||
<>
|
||||
<SingleLine>
|
||||
<SectionTitle style={{ margin: '0', color: '#ff0080', fontWeight: 900 }}>
|
||||
AMBOSS
|
||||
</SectionTitle>
|
||||
<AmbossLoginButton />
|
||||
</SingleLine>
|
||||
<Text>
|
||||
Amboss offers different integration options that can help you monitor your
|
||||
node, store backups and get historical graphs about your balances.
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<AmbossView />
|
||||
<Backups />
|
||||
<Healthchecks />
|
||||
<Balances />
|
||||
<Billboard />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { ChainTransactions } from '../src/views/chain/transactions/ChainTransactions';
|
||||
import { ChainUtxos } from '../src/views/chain/utxos/ChainUtxos';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
} from '../src/components/generic/Styled';
|
||||
|
||||
const ChainView = () => {
|
||||
return (
|
||||
<>
|
||||
<CardWithTitle>
|
||||
<SubTitle>Chain Utxos</SubTitle>
|
||||
<Card mobileNoBackground={true}>
|
||||
<ChainUtxos />
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
<CardWithTitle>
|
||||
<SubTitle>Chain Transactions</SubTitle>
|
||||
<Card mobileNoBackground={true}>
|
||||
<ChainTransactions />
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<ChainView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { SimpleWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const S = {
|
||||
wrapper: styled.div`
|
||||
position: relative;
|
||||
`,
|
||||
};
|
||||
|
||||
const LoadingComp = () => <LoadingCard noCard={true} loadingHeight={'90vh'} />;
|
||||
|
||||
const Dashboard = dynamic(() => import('../src/views/dashboard'), {
|
||||
ssr: false,
|
||||
loading: LoadingComp,
|
||||
});
|
||||
|
||||
const Wrapped = () => {
|
||||
return (
|
||||
<SimpleWrapper>
|
||||
<S.wrapper>
|
||||
<Dashboard />
|
||||
</S.wrapper>
|
||||
</SimpleWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { Version } from '../src/components/version/Version';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { MempoolReport } from '../src/views/home/reports/mempool';
|
||||
import { LiquidityGraph } from '../src/views/home/reports/liquidReport/LiquidityGraph';
|
||||
import { AccountButtons } from '../src/views/home/account/AccountButtons';
|
||||
import { AccountInfo } from '../src/views/home/account/AccountInfo';
|
||||
import { QuickActions } from '../src/views/home/quickActions/QuickActions';
|
||||
import { FlowBox } from '../src/views/home/reports/flow';
|
||||
import { ForwardBox } from '../src/views/home/reports/forwardReport';
|
||||
import { ConnectCard } from '../src/views/home/connect/Connect';
|
||||
import { Liquidity } from '../src/views/home/liquidity/Liquidity';
|
||||
|
||||
const HomeView = () => (
|
||||
<>
|
||||
<Version />
|
||||
<AccountInfo />
|
||||
<AccountButtons />
|
||||
<ConnectCard />
|
||||
<Liquidity />
|
||||
<QuickActions />
|
||||
<FlowBox />
|
||||
<LiquidityGraph />
|
||||
<ForwardBox />
|
||||
<MempoolReport />
|
||||
</>
|
||||
);
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<HomeView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { useGetBasePointsQuery } from '../src/graphql/queries/__generated__/getBasePoints.generated';
|
||||
import { NodeCard } from '../src/views/leaderboard/NodeCard';
|
||||
import { SupportBar } from '../src/views/home/quickActions/donate/DonateContent';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
Card,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
|
||||
const LeaderboardView = () => {
|
||||
const { loading, data } = useGetBasePointsQuery({ ssr: false });
|
||||
|
||||
const renderBoard = () => {
|
||||
if (loading || !data?.getBasePoints) {
|
||||
return <LoadingCard title={'Supporters'} />;
|
||||
}
|
||||
if (!data.getBasePoints.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Supporters</SubTitle>
|
||||
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||
{data.getBasePoints.map((node, index: number) => (
|
||||
<React.Fragment key={index}>
|
||||
<NodeCard node={node} index={index + 1} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<SupportBar />
|
||||
</Card>
|
||||
{renderBoard()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<LeaderboardView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { UserInfo } from '../src/views/lnmarkets/UserInfo';
|
||||
import { Title } from '../src/components/typography/Styled';
|
||||
import { GoToLnMarkets } from '../src/views/lnmarkets/GoToLnMarkets';
|
||||
import { DepositWithdraw } from '../src/views/lnmarkets/DepositWithdraw';
|
||||
import { useGetLnMarketsStatusQuery } from '../src/graphql/queries/__generated__/getLnMarketsStatus.generated';
|
||||
import {
|
||||
useLnMarketsLoginMutation,
|
||||
useLnMarketsLogoutMutation,
|
||||
} from '../src/graphql/mutations/__generated__/lnMarkets.generated';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
|
||||
import { useConfigDispatch } from '../src/context/ConfigContext';
|
||||
import { SingleLine } from '../src/components/generic/Styled';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
width: auto;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SettingsLine = styled(SingleLine)`
|
||||
margin: 8px 0;
|
||||
`;
|
||||
|
||||
const LnMarketsView = () => {
|
||||
const dispatch = useConfigDispatch();
|
||||
|
||||
const { data: statusData } = useGetLnMarketsStatusQuery({
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
const [login, { data, loading }] = useLnMarketsLoginMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
refetchQueries: ['GetLnMarketsStatus'],
|
||||
});
|
||||
|
||||
const [logout, { loading: logoutLoading }] = useLnMarketsLogoutMutation({
|
||||
onCompleted: () => {
|
||||
toast.success('Logged out');
|
||||
dispatch({ type: 'change', lnMarketsAuth: false });
|
||||
},
|
||||
refetchQueries: ['GetLnMarketsStatus'],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.lnMarketsLogin?.status === 'OK') {
|
||||
dispatch({ type: 'change', lnMarketsAuth: true });
|
||||
toast.success('Logged In');
|
||||
}
|
||||
}, [data, dispatch]);
|
||||
|
||||
const Content = () => {
|
||||
if (statusData?.getLnMarketsStatus === 'out') {
|
||||
return (
|
||||
<ColorButton
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
withMargin={'8px 0 0 '}
|
||||
fullWidth={true}
|
||||
onClick={login}
|
||||
>
|
||||
Login
|
||||
</ColorButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserInfo />
|
||||
<DepositWithdraw />
|
||||
<GoToLnMarkets />
|
||||
<ColorButton
|
||||
loading={logoutLoading}
|
||||
disabled={logoutLoading}
|
||||
withMargin={'8px 0 0 '}
|
||||
fullWidth={true}
|
||||
onClick={logout}
|
||||
>
|
||||
Logout
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>LnMarkets</Title>
|
||||
<Content />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<LnMarketsView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { Spacer } from '../src/components/spacer/Spacer';
|
||||
import { ThunderStorm } from '../src/views/homepage/HomePage.styled';
|
||||
import { appendBasePath } from '../src/utils/basePath';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { TopSection } from '../src/views/homepage/Top';
|
||||
import { Accounts } from '../src/views/homepage/Accounts';
|
||||
|
||||
const ContextApp = () => (
|
||||
<>
|
||||
<ThunderStorm alt={''} src={appendBasePath('/static/thunderstorm.webp')} />
|
||||
<TopSection />
|
||||
<Accounts />
|
||||
<Spacer />
|
||||
</>
|
||||
);
|
||||
|
||||
export default ContextApp;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, true);
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SingleLine,
|
||||
SubTitle,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { AdvancedBalance } from '../src/views/balance/AdvancedBalance';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { HelpCircle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import { chartColors } from '../src/styles/Themes';
|
||||
|
||||
const Button = styled.a`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const BalanceView = () => (
|
||||
<CardWithTitle>
|
||||
<SingleLine>
|
||||
<SubTitle>Rebalance</SubTitle>
|
||||
<Button
|
||||
href={'https://apotdevin.com/blog/thunderhub-rebalance'}
|
||||
target={'__blank'}
|
||||
>
|
||||
<HelpCircle size={18} color={chartColors.orange} />
|
||||
</Button>
|
||||
</SingleLine>
|
||||
<AdvancedBalance />
|
||||
</CardWithTitle>
|
||||
);
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<BalanceView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../../src/utils/ssr';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { LoadingCard } from '../../src/components/loading/LoadingCard';
|
||||
|
||||
const LoadingComp = () => <LoadingCard noCard={true} loadingHeight={'30vh'} />;
|
||||
|
||||
const Dashboard = dynamic(() => import('../../src/views/settings/DashPanel'), {
|
||||
ssr: false,
|
||||
loading: LoadingComp,
|
||||
});
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper noNavigation={true}>
|
||||
<Dashboard />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GridWrapper } from '../../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../../src/utils/ssr';
|
||||
import { DashboardSettings } from '../../src/views/settings/Dashboard';
|
||||
import { SingleLine } from '../../src/components/generic/Styled';
|
||||
import { InterfaceSettings } from '../../src/views/settings/Interface';
|
||||
import { DangerView } from '../../src/views/settings/Danger';
|
||||
import { ChatSettings } from '../../src/views/settings/Chat';
|
||||
import { PrivacySettings } from '../../src/views/settings/Privacy';
|
||||
import { Security } from '../../src/views/settings/Security';
|
||||
import { NetworkInfo } from '../../src/views/home/networkInfo/NetworkInfo';
|
||||
import { NotificationSettings } from '../../src/views/settings/Notifications';
|
||||
import { AmbossSettings } from '../../src/views/settings/Amboss';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
width: auto;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SettingsLine = styled(SingleLine)`
|
||||
margin: 8px 0;
|
||||
`;
|
||||
|
||||
const SettingsView = () => {
|
||||
return (
|
||||
<>
|
||||
<InterfaceSettings />
|
||||
<NotificationSettings />
|
||||
<AmbossSettings />
|
||||
<Security />
|
||||
<DashboardSettings />
|
||||
<PrivacySettings />
|
||||
<ChatSettings />
|
||||
<DangerView />
|
||||
<NetworkInfo />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<SettingsView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { useCheckAuthToken } from '../src/hooks/UseCheckAuthToken';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
|
||||
const Wrapped = () => {
|
||||
useCheckAuthToken();
|
||||
|
||||
return <LoadingCard noCard={true} loadingHeight={'80vh'} />;
|
||||
};
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, true);
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { VolumeStats } from '../src/views/stats/FlowStats';
|
||||
import { TimeStats } from '../src/views/stats/TimeStats';
|
||||
import { FeeStats } from '../src/views/stats/FeeStats';
|
||||
import { StatResume } from '../src/views/stats/StatResume';
|
||||
import { StatsProvider } from '../src/views/stats/context';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { SingleLine } from '../src/components/generic/Styled';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
width: auto;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SettingsLine = styled(SingleLine)`
|
||||
margin: 8px 0;
|
||||
`;
|
||||
|
||||
const StatsView = () => {
|
||||
return (
|
||||
<>
|
||||
<StatResume />
|
||||
<VolumeStats />
|
||||
<TimeStats />
|
||||
<FeeStats />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<StatsProvider>
|
||||
<StatsView />
|
||||
</StatsProvider>
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { SwapView } from '../src/views/swap';
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<SwapView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from '../src/components/gridWrapper/GridWrapper';
|
||||
import { Bakery } from '../src/views/tools/bakery/Bakery';
|
||||
import { Accounting } from '../src/views/tools/accounting/Accounting';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from '../src/utils/ssr';
|
||||
import { BackupsView } from '../src/views/tools/backups/Backups';
|
||||
import { MessagesView } from '../src/views/tools/messages/Messages';
|
||||
import { WalletVersion } from '../src/views/tools/WalletVersion';
|
||||
|
||||
const ToolsView = () => (
|
||||
<>
|
||||
<Accounting />
|
||||
<BackupsView />
|
||||
<MessagesView />
|
||||
<Bakery />
|
||||
<WalletVersion />
|
||||
</>
|
||||
);
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<ToolsView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
178
src/client/src/App.tsx
Normal file
178
src/client/src/App.tsx
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { FC, ReactNode, useEffect, lazy, Suspense } from 'react';
|
||||
import { StyleSheetManager, ThemeProvider } from 'styled-components';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { Routes, Route, useNavigate, useLocation } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import { useApollo } from '../config/client';
|
||||
import { ContextProvider } from './context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from './context/ConfigContext';
|
||||
import { GlobalStyles } from './styles/GlobalStyle';
|
||||
import { Header } from './layouts/header/Header';
|
||||
import { Footer } from './layouts/footer/Footer';
|
||||
import { PageWrapper, HeaderBodyWrapper } from './layouts/Layout.styled';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { useListener } from './hooks/UseListener';
|
||||
import { SseProvider } from './context/SseContext';
|
||||
import { config } from './config/thunderhubConfig';
|
||||
import { LoadingCard } from './components/loading/LoadingCard';
|
||||
import { useGetNodeInfoQuery } from './graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import 'react-resizable/css/styles.css';
|
||||
|
||||
// Page imports
|
||||
import HomePage from './pages/HomePage';
|
||||
import LoginPage from './pages/LoginPage';
|
||||
import SsoPage from './pages/SsoPage';
|
||||
import ChannelsPage from './pages/ChannelsPage';
|
||||
import ChannelDetailPage from './pages/ChannelDetailPage';
|
||||
import PeersPage from './pages/PeersPage';
|
||||
import TransactionsPage from './pages/TransactionsPage';
|
||||
import ForwardsPage from './pages/ForwardsPage';
|
||||
import ChainPage from './pages/ChainPage';
|
||||
import ToolsPage from './pages/ToolsPage';
|
||||
import StatsPage from './pages/StatsPage';
|
||||
import SwapPage from './pages/SwapPage';
|
||||
import ChatPage from './pages/ChatPage';
|
||||
import SettingsPage from './pages/SettingsPage';
|
||||
import AmbossPage from './pages/AmbossPage';
|
||||
|
||||
const DashboardPage = lazy(() => import('./pages/DashboardPage'));
|
||||
const SettingsDashboardPage = lazy(
|
||||
() => import('./pages/SettingsDashboardPage')
|
||||
);
|
||||
|
||||
const LoadingComp = () => <LoadingCard noCard={true} loadingHeight={'90vh'} />;
|
||||
const LoadingCompSmall = () => (
|
||||
<LoadingCard noCard={true} loadingHeight={'30vh'} />
|
||||
);
|
||||
|
||||
const S = {
|
||||
wrapper: styled.div`
|
||||
position: relative;
|
||||
`,
|
||||
};
|
||||
|
||||
function shouldForwardProp(propName: string, target: any) {
|
||||
if (typeof target === 'string') {
|
||||
return isPropValid(propName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const NotAuthenticated: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
navigate(config.logoutUrl || '/login');
|
||||
}, [navigate]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const Listener: FC<{ isRoot: boolean }> = ({ isRoot }) => {
|
||||
useListener(isRoot);
|
||||
return null;
|
||||
};
|
||||
|
||||
const Wrapper: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const { theme } = useConfigState();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle('dark', theme === 'dark');
|
||||
}, [theme]);
|
||||
|
||||
const { data, loading, error } = useGetNodeInfoQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
skip: pathname === '/login' || pathname === '/sso',
|
||||
});
|
||||
|
||||
const isRoot = pathname === '/login' || pathname === '/sso';
|
||||
const authenticated = !isRoot && !loading && !error && !!data?.getNodeInfo;
|
||||
const checking = !isRoot && loading;
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={{ mode: isRoot ? 'light' : theme }}>
|
||||
<GlobalStyles />
|
||||
<PageWrapper>
|
||||
<HeaderBodyWrapper>
|
||||
<Header />
|
||||
<Listener isRoot={isRoot} />
|
||||
{checking ? (
|
||||
<LoadingCard noCard={true} loadingHeight={'80vh'} />
|
||||
) : isRoot || authenticated ? (
|
||||
children
|
||||
) : (
|
||||
<NotAuthenticated />
|
||||
)}
|
||||
</HeaderBodyWrapper>
|
||||
<Footer />
|
||||
<Toaster position="top-right" />
|
||||
</PageWrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const AuthenticatedRoutes = () => (
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route
|
||||
path="/dashboard"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComp />}>
|
||||
<S.wrapper>
|
||||
<DashboardPage />
|
||||
</S.wrapper>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path="/channels" element={<ChannelsPage />} />
|
||||
<Route path="/channels/:slug" element={<ChannelDetailPage />} />
|
||||
<Route path="/peers" element={<PeersPage />} />
|
||||
<Route path="/transactions" element={<TransactionsPage />} />
|
||||
<Route path="/forwards" element={<ForwardsPage />} />
|
||||
<Route path="/chain" element={<ChainPage />} />
|
||||
<Route path="/tools" element={<ToolsPage />} />
|
||||
<Route path="/stats" element={<StatsPage />} />
|
||||
<Route path="/swap" element={<SwapPage />} />
|
||||
<Route path="/chat" element={<ChatPage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
<Route
|
||||
path="/settings/dashboard"
|
||||
element={
|
||||
<Suspense fallback={<LoadingCompSmall />}>
|
||||
<SettingsDashboardPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route path="/amboss" element={<AmbossPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/sso" element={<SsoPage />} />
|
||||
<Route path="*" element={<HomePage />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
export default function App() {
|
||||
const themeCookie = Cookies.get('theme') || config.defaultTheme;
|
||||
|
||||
const apolloClient = useApollo('', null);
|
||||
|
||||
return (
|
||||
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<ConfigProvider initialConfig={{ theme: themeCookie }}>
|
||||
<SseProvider>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<AuthenticatedRoutes />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</SseProvider>
|
||||
</ConfigProvider>
|
||||
</ApolloProvider>
|
||||
</StyleSheetManager>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useGetBitcoinFeesQuery } from '../../../src/graphql/queries/__generated__/getBitcoinFees.generated';
|
||||
import { useGetBitcoinFeesQuery } from '@/graphql/queries/__generated__/getBitcoinFees.generated';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const BitcoinFees: React.FC = () => {
|
||||
const { fetchFees } = useConfigState();
|
||||
|
||||
const { stopPolling, error } = useGetBitcoinFeesQuery({
|
||||
ssr: false,
|
||||
skip: !fetchFees,
|
||||
fetchPolicy: 'network-only',
|
||||
pollInterval: 60000,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useGetBitcoinPriceQuery } from '../../../src/graphql/queries/__generated__/getBitcoinPrice.generated';
|
||||
import { useGetBitcoinPriceQuery } from '@/graphql/queries/__generated__/getBitcoinPrice.generated';
|
||||
import { usePriceDispatch } from '../../context/PriceContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
|
|
@ -7,7 +7,6 @@ export const BitcoinPrice: React.FC = () => {
|
|||
const { fetchPrices } = useConfigState();
|
||||
const setPrices = usePriceDispatch();
|
||||
const { loading, data, stopPolling } = useGetBitcoinPriceQuery({
|
||||
ssr: false,
|
||||
skip: !fetchPrices,
|
||||
fetchPolicy: 'network-only',
|
||||
onError: () => {
|
||||
|
|
@ -28,7 +27,7 @@ export const BitcoinPrice: React.FC = () => {
|
|||
try {
|
||||
const prices = JSON.parse(data.getBitcoinPrice);
|
||||
setPrices({ type: 'fetched', state: { prices } });
|
||||
} catch (error: any) {
|
||||
} catch {
|
||||
setPrices({ type: 'dontShow' });
|
||||
stopPolling();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { burgerColor } from '../../styles/Themes';
|
||||
import { NodeInfo } from '../../layouts/navigation/nodeInfo/NodeInfo';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { ChevronRight } from 'react-feather';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { ThemeSet } from 'styled-theming';
|
||||
import {
|
||||
textColor,
|
||||
|
|
@ -134,7 +134,7 @@ export interface ColorButtonProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const ColorButton: React.FC<ColorButtonProps> = ({
|
||||
export const ColorButton: FC<ColorButtonProps> = ({
|
||||
loading,
|
||||
color,
|
||||
disabled,
|
||||
|
|
@ -174,7 +174,11 @@ export const ColorButton: React.FC<ColorButtonProps> = ({
|
|||
mobileFullWidth={mobileFullWidth}
|
||||
buttonWidth={width}
|
||||
>
|
||||
<ScaleLoader height={16} color={themeColors.blue2} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={16}
|
||||
style={{ color: themeColors.blue2 }}
|
||||
/>
|
||||
</DisabledButton>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import {
|
||||
multiSelectColor,
|
||||
|
|
@ -6,7 +6,7 @@ import {
|
|||
multiButtonColor,
|
||||
themeColors,
|
||||
} from '../../../styles/Themes';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
interface StyledSingleProps {
|
||||
selected?: boolean;
|
||||
|
|
@ -44,7 +44,7 @@ interface SingleButtonProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const SingleButton: React.FC<SingleButtonProps> = ({
|
||||
export const SingleButton: FC<SingleButtonProps> = ({
|
||||
children,
|
||||
disabled,
|
||||
selected,
|
||||
|
|
@ -59,7 +59,7 @@ export const SingleButton: React.FC<SingleButtonProps> = ({
|
|||
buttonColor={color}
|
||||
withPadding={withPadding}
|
||||
onClick={() => {
|
||||
onClick && onClick();
|
||||
if (onClick) onClick();
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
@ -90,7 +90,7 @@ interface MultiButtonProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const MultiButton: React.FC<MultiButtonProps> = ({
|
||||
export const MultiButton: FC<MultiButtonProps> = ({
|
||||
children,
|
||||
margin,
|
||||
loading,
|
||||
|
|
@ -100,7 +100,11 @@ export const MultiButton: React.FC<MultiButtonProps> = ({
|
|||
<MultiBackground margin={margin}>
|
||||
{loading ? (
|
||||
<div style={{ width, textAlign: 'center' }}>
|
||||
<ScaleLoader height={21} color={themeColors.blue3} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={21}
|
||||
style={{ color: themeColors.blue3 }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import * as echarts from 'echarts/core';
|
|||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import ReactEChartsCore from 'echarts-for-react/lib/core';
|
||||
import { ThemeContext } from 'styled-components';
|
||||
import numeral from 'numeral';
|
||||
import { timeFormat, timeParse } from 'd3-time-format';
|
||||
import { formatSats } from '../../utils/helpers';
|
||||
import { COMMON_CHART_STYLES } from './common';
|
||||
|
|
@ -110,8 +109,10 @@ export const BarChart = ({
|
|||
axisTick: { show: true },
|
||||
axisLabel: {
|
||||
formatter: function (value: number) {
|
||||
const format = value < 1000 ? '0a' : '0.0a';
|
||||
return numeral(value).format(format);
|
||||
return value.toLocaleString('en-US', {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: value < 1000 ? 0 : 1,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useGetForwardsQuery } from '../../graphql/queries/__generated__/getForwards.generated';
|
||||
import { toast } from 'react-toastify';
|
||||
import toast from 'react-hot-toast';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import ReactEChartsCore from 'echarts-for-react/lib/core';
|
||||
import * as echarts from 'echarts/core';
|
||||
|
|
@ -25,7 +25,6 @@ const getMaxHeight = (arr: number[], rounding?: number): number => {
|
|||
export const ChannelCart = ({ channelId, days }: ChannelCartProps) => {
|
||||
const themeContext = useContext(ThemeContext);
|
||||
const { data } = useGetForwardsQuery({
|
||||
ssr: false,
|
||||
variables: { days: days },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
import * as React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGetMessagesQuery } from '../../../src/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useAccount } from '../../../src/hooks/UseAccount';
|
||||
import { FC, useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useGetMessagesQuery } from '@/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useAccount } from '@/hooks/UseAccount';
|
||||
import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const ChatFetcher: React.FC = () => {
|
||||
export const ChatFetcher: FC = () => {
|
||||
const newChatToastId = 'newChatToastId';
|
||||
|
||||
const { chatPollingSpeed } = useConfigState();
|
||||
|
||||
const account = useAccount();
|
||||
const { pathname } = useRouter();
|
||||
const { pathname } = useLocation();
|
||||
const { lastChat, chats, sentChats, initialized } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
|
||||
const noChatsAvailable = chats.length <= 0 && sentChats.length <= 0;
|
||||
|
||||
const { data, loading, error } = useGetMessagesQuery({
|
||||
ssr: false,
|
||||
skip: initialized || noChatsAvailable || !account,
|
||||
pollInterval: chatPollingSpeed,
|
||||
fetchPolicy: 'network-only',
|
||||
|
|
@ -28,7 +27,7 @@ export const ChatFetcher: React.FC = () => {
|
|||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (data && data.getMessages?.messages) {
|
||||
const messages = [...data.getMessages.messages];
|
||||
let index = -1;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useGetMessagesLazyQuery } from '../../../src/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useAccount } from '../../../src/hooks/UseAccount';
|
||||
import { FC, useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useGetMessagesLazyQuery } from '@/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useAccount } from '@/hooks/UseAccount';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
|
||||
export const ChatInit: React.FC = () => {
|
||||
export const ChatInit: FC = () => {
|
||||
const dispatch = useChatDispatch();
|
||||
|
||||
const [
|
||||
|
|
@ -18,7 +18,7 @@ export const ChatInit: React.FC = () => {
|
|||
|
||||
const account = useAccount();
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
const storageChats =
|
||||
localStorage.getItem(`${account.id}-sentChats`) || '';
|
||||
|
|
@ -34,7 +34,7 @@ export const ChatInit: React.FC = () => {
|
|||
sender,
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch {
|
||||
localStorage.removeItem('sentChats');
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ export const ChatInit: React.FC = () => {
|
|||
}
|
||||
}, [dispatch, getMessages, account]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!initLoading && !initError && initData && initData.getMessages) {
|
||||
const { messages } = initData.getMessages;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
colorButtonBackground,
|
||||
|
|
@ -42,7 +42,7 @@ type CheckboxProps = {
|
|||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const Checkbox: React.FC<CheckboxProps> = ({
|
||||
export const Checkbox: FC<CheckboxProps> = ({
|
||||
children,
|
||||
checked,
|
||||
onChange,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ChevronRight } from 'react-feather';
|
||||
import { useUpdateFeesMutation } from '../../../../src/graphql/mutations/__generated__/updateFees.generated';
|
||||
import { Input } from '../../../../src/components/input';
|
||||
import { InputWithDeco } from '../../../../src/components/input/InputWithDeco';
|
||||
import { ColorButton } from '../../../../src/components/buttons/colorButton/ColorButton';
|
||||
import { getErrorContent } from '../../../../src/utils/error';
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { useUpdateFeesMutation } from '@/graphql/mutations/__generated__/updateFees.generated';
|
||||
import { Input } from '@/components/input';
|
||||
import { InputWithDeco } from '@/components/input/InputWithDeco';
|
||||
import { ColorButton } from '@/components/buttons/colorButton/ColorButton';
|
||||
import { getErrorContent } from '@/utils/error';
|
||||
import { RightAlign } from '../../generic/Styled';
|
||||
|
||||
type DetailsChangeProps = {
|
||||
|
|
@ -27,10 +27,12 @@ export const DetailsChange = ({ callback }: DetailsChangeProps) => {
|
|||
setCLTV(0);
|
||||
setMax(0);
|
||||
setMin(0);
|
||||
data.updateFees
|
||||
? toast.success('Channel Details Updated')
|
||||
: toast.error('Error updating fees');
|
||||
callback && callback();
|
||||
if (data.updateFees) {
|
||||
toast.success('Channel Details Updated');
|
||||
} else {
|
||||
toast.error('Error updating fees');
|
||||
}
|
||||
if (callback) callback();
|
||||
},
|
||||
refetchQueries: ['GetChannels', 'ChannelFees'],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import React from 'react';
|
||||
|
||||
interface EmojiProps {
|
||||
symbol: string;
|
||||
label?: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
isFuture,
|
||||
format,
|
||||
|
|
@ -6,10 +5,9 @@ import {
|
|||
differenceInCalendarDays,
|
||||
isToday,
|
||||
} from 'date-fns';
|
||||
import { X, Copy } from 'react-feather';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { toast } from 'react-toastify';
|
||||
import getConfig from 'next/config';
|
||||
import { X, Copy } from 'lucide-react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { config } from '../../config/thunderhubConfig';
|
||||
import {
|
||||
SmallLink,
|
||||
DarkSubTitle,
|
||||
|
|
@ -19,9 +17,6 @@ import {
|
|||
} from './Styled';
|
||||
import { StatusDot, DetailLine } from './CardGeneric';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { disableLinks, mempoolUrl } = publicRuntimeConfig;
|
||||
|
||||
export const shorten = (text: string, length?: number): string => {
|
||||
if (!text) return '';
|
||||
const amount = length || 6;
|
||||
|
|
@ -49,62 +44,64 @@ export const addEllipsis = (
|
|||
};
|
||||
|
||||
export const copyLink = (text: string) => (
|
||||
<CopyToClipboard text={text} onCopy={() => toast.success('Copied')}>
|
||||
<CopyIcon>
|
||||
<Copy size={12} />
|
||||
</CopyIcon>
|
||||
</CopyToClipboard>
|
||||
<CopyIcon
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(text).then(() => toast.success('Copied'))
|
||||
}
|
||||
>
|
||||
<Copy size={12} />
|
||||
</CopyIcon>
|
||||
);
|
||||
|
||||
export const getAddressLink = (transaction: string | null | undefined) => {
|
||||
if (!transaction) return null;
|
||||
if (disableLinks) {
|
||||
if (config.disableLinks) {
|
||||
return (
|
||||
<>
|
||||
<span className="flex items-center">
|
||||
{shorten(transaction)}
|
||||
{copyLink(transaction)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const link = `${mempoolUrl}/address/${transaction}`;
|
||||
const link = `${config.mempoolUrl}/address/${transaction}`;
|
||||
return (
|
||||
<>
|
||||
<span className="flex items-center">
|
||||
<SmallLink href={link} target="_blank">
|
||||
{shorten(transaction)}
|
||||
</SmallLink>
|
||||
{copyLink(transaction)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const getTransactionLink = (transaction: string | null | undefined) => {
|
||||
if (!transaction) return null;
|
||||
if (disableLinks) {
|
||||
if (config.disableLinks) {
|
||||
return (
|
||||
<>
|
||||
<span className="flex items-center">
|
||||
{shorten(transaction)}
|
||||
{copyLink(transaction)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const link = `${mempoolUrl}/tx/${transaction}`;
|
||||
const link = `${config.mempoolUrl}/tx/${transaction}`;
|
||||
return (
|
||||
<>
|
||||
<span className="flex items-center">
|
||||
<SmallLink href={link} target="_blank">
|
||||
{shorten(transaction)}
|
||||
</SmallLink>
|
||||
{copyLink(transaction)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const getWithCopy = (text: string | null | undefined) => {
|
||||
if (!text) return null;
|
||||
return (
|
||||
<>
|
||||
<span className="flex items-center">
|
||||
{shorten(text)}
|
||||
{copyLink(text)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -118,8 +115,8 @@ export const getNodeLink = (
|
|||
const link = `https://amboss.space/node/${publicKey}`;
|
||||
const text = alias ? alias : shorten(publicKey);
|
||||
return (
|
||||
<>
|
||||
{disableLinks ? (
|
||||
<span className="flex items-center">
|
||||
{config.disableLinks ? (
|
||||
text
|
||||
) : (
|
||||
<SmallLink href={link} target="_blank">
|
||||
|
|
@ -127,15 +124,15 @@ export const getNodeLink = (
|
|||
</SmallLink>
|
||||
)}
|
||||
{copyLink(publicKey)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const getChannelLink = (id: string) => {
|
||||
const link = `https://amboss.space/edge/${id}`;
|
||||
return (
|
||||
<>
|
||||
{disableLinks ? (
|
||||
<span className="flex items-center">
|
||||
{config.disableLinks ? (
|
||||
id
|
||||
) : (
|
||||
<SmallLink href={link} target="_blank">
|
||||
|
|
@ -143,7 +140,7 @@ export const getChannelLink = (id: string) => {
|
|||
</SmallLink>
|
||||
)}
|
||||
{copyLink(id)}
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { BitcoinFees } from '../../../src/components/bitcoinInfo/BitcoinFees';
|
||||
import { BitcoinPrice } from '../../../src/components/bitcoinInfo/BitcoinPrice';
|
||||
import { BitcoinFees } from '@/components/bitcoinInfo/BitcoinFees';
|
||||
import { BitcoinPrice } from '@/components/bitcoinInfo/BitcoinPrice';
|
||||
import { mediaWidths } from '../../styles/Themes';
|
||||
import { Section } from '../section/Section';
|
||||
import { Navigation } from '../../layouts/navigation/Navigation';
|
||||
|
|
@ -32,7 +32,7 @@ const ContentStyle = styled.div`
|
|||
grid-area: content;
|
||||
`;
|
||||
|
||||
export const GridWrapper: React.FC<
|
||||
export const GridWrapper: FC<
|
||||
GridProps & { centerContent?: boolean; children?: ReactNode }
|
||||
> = ({ children, centerContent = true, noNavigation }) => (
|
||||
<Section padding={'16px 16px 32px'}>
|
||||
|
|
@ -51,7 +51,7 @@ export const GridWrapper: React.FC<
|
|||
</Section>
|
||||
);
|
||||
|
||||
export const SimpleWrapper: React.FC<GridProps> = ({ children }) => (
|
||||
export const SimpleWrapper: FC<GridProps> = ({ children }) => (
|
||||
<Section padding={'16px'}>
|
||||
<BitcoinPrice />
|
||||
<BitcoinFees />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { FC, KeyboardEvent, ReactNode } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { unSelectedNavButton, mediaWidths } from '../../../src/styles/Themes';
|
||||
import { unSelectedNavButton, mediaWidths } from '@/styles/Themes';
|
||||
import { SingleLine } from '../generic/Styled';
|
||||
import { Price } from '../price/Price';
|
||||
import { Input } from '.';
|
||||
|
|
@ -48,12 +48,12 @@ type InputWithDecoProps = {
|
|||
inputType?: string;
|
||||
inputCallback?: (value: string) => void;
|
||||
blurCallback?: (value: string) => void;
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
||||
onEnter?: () => void;
|
||||
children?: React.ReactNode;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const InputWithDeco: React.FC<InputWithDecoProps> = ({
|
||||
export const InputWithDeco: FC<InputWithDecoProps> = ({
|
||||
title,
|
||||
value,
|
||||
amount,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import { ChangeEvent, KeyboardEvent } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { ThemeSet } from 'styled-theming';
|
||||
import {
|
||||
|
|
@ -83,9 +83,9 @@ interface InputCompProps {
|
|||
mobileFullWidth?: boolean;
|
||||
maxWidth?: string;
|
||||
autoFocus?: boolean;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onBlur?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
onBlur?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
||||
onEnter?: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ export const Input = ({
|
|||
if (onEnter && e.key === 'Enter') {
|
||||
onEnter();
|
||||
} else {
|
||||
onKeyDown && onKeyDown(e);
|
||||
if (onKeyDown) onKeyDown(e);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { ThemeSet } from 'styled-theming';
|
||||
import RouterLink from 'next/link';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { textColor, linkHighlight } from '../../styles/Themes';
|
||||
|
||||
interface StyledProps {
|
||||
|
|
@ -11,10 +11,38 @@ interface StyledProps {
|
|||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
const StyledSpan = styled.span<StyledProps>`
|
||||
cursor: pointer;
|
||||
color: ${({ fontColor, inheritColor }) =>
|
||||
inheritColor ? 'inherit' : (fontColor ?? textColor)};
|
||||
text-decoration: none;
|
||||
${({ fullWidth }: StyledProps) =>
|
||||
fullWidth &&
|
||||
css`
|
||||
width: 100%;
|
||||
`};
|
||||
|
||||
:hover {
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
${({ underline }: StyledProps) => underline ?? linkHighlight} 0%,
|
||||
${({ underline }: StyledProps) => underline ?? linkHighlight} 100%
|
||||
);
|
||||
background-position: 0 100%;
|
||||
background-size: 2px 2px;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
`;
|
||||
|
||||
const NoStylingSpan = styled.span`
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
`;
|
||||
|
||||
const StyledLink = styled.a<StyledProps>`
|
||||
cursor: pointer;
|
||||
color: ${({ fontColor, inheritColor }) =>
|
||||
inheritColor ? 'inherit' : fontColor ?? textColor};
|
||||
inheritColor ? 'inherit' : (fontColor ?? textColor)};
|
||||
text-decoration: none;
|
||||
${({ fullWidth }: StyledProps) =>
|
||||
fullWidth &&
|
||||
|
|
@ -51,7 +79,7 @@ interface LinkProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const Link: React.FC<LinkProps> = ({
|
||||
export const Link: FC<LinkProps> = ({
|
||||
children,
|
||||
href,
|
||||
to,
|
||||
|
|
@ -81,9 +109,10 @@ export const Link: React.FC<LinkProps> = ({
|
|||
}
|
||||
|
||||
if (to) {
|
||||
const CorrectSpan = noStyling ? NoStylingSpan : StyledSpan;
|
||||
return (
|
||||
<RouterLink href={to} passHref legacyBehavior>
|
||||
<CorrectLink {...props}>{children}</CorrectLink>
|
||||
<RouterLink to={to} style={{ textDecoration: 'none' }}>
|
||||
<CorrectSpan {...props}>{children}</CorrectSpan>
|
||||
</RouterLink>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import styled from 'styled-components';
|
||||
import { CardWithTitle, CardTitle, SubTitle, Card } from '../generic/Styled';
|
||||
import { themeColors } from '../../styles/Themes';
|
||||
|
|
@ -34,7 +33,11 @@ export const LoadingCard = ({
|
|||
if (noCard) {
|
||||
return (
|
||||
<Loading loadingHeight={loadingHeight}>
|
||||
<ScaleLoader height={20} color={loadingColor} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={20}
|
||||
style={{ color: loadingColor }}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
}
|
||||
|
|
@ -43,7 +46,11 @@ export const LoadingCard = ({
|
|||
return (
|
||||
<Card>
|
||||
<Loading loadingHeight={loadingHeight}>
|
||||
<ScaleLoader height={20} color={loadingColor} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={20}
|
||||
style={{ color: loadingColor }}
|
||||
/>
|
||||
</Loading>
|
||||
</Card>
|
||||
);
|
||||
|
|
@ -56,7 +63,11 @@ export const LoadingCard = ({
|
|||
</CardTitle>
|
||||
<Card>
|
||||
<Loading loadingHeight={loadingHeight}>
|
||||
<ScaleLoader height={20} color={loadingColor} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={20}
|
||||
style={{ color: loadingColor }}
|
||||
/>
|
||||
</Loading>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { progressBackground } from '../../styles/Themes';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
import React, { FC, ReactNode, useEffect } from 'react';
|
||||
import { LogOut } from 'react-feather';
|
||||
import { useLogoutMutation } from '../../../src/graphql/mutations/__generated__/logout.generated';
|
||||
import { FC, ReactNode, useEffect } from 'react';
|
||||
import { LogOut } from 'lucide-react';
|
||||
import { useLogoutMutation } from '@/graphql/mutations/__generated__/logout.generated';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { HeaderNavButton } from '../../../src/layouts/header/Header.styled';
|
||||
import { HeaderNavButton } from '@/layouts/header/Header.styled';
|
||||
import styled from 'styled-components';
|
||||
import { themeColors } from '../../../src/styles/Themes';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import getConfig from 'next/config';
|
||||
import { themeColors } from '@/styles/Themes';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { config } from '../../config/thunderhubConfig';
|
||||
import { safeRedirect } from '../../utils/url';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { logoutUrl, basePath } = publicRuntimeConfig;
|
||||
|
||||
const Logout = styled.button`
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
|
@ -39,14 +37,21 @@ export const LogoutWrapper: FC<{ children?: ReactNode }> = ({ children }) => {
|
|||
dispatchChat({ type: 'disconnected' });
|
||||
client.clearStore();
|
||||
|
||||
window.location.href = logoutUrl || `${basePath}/login`;
|
||||
safeRedirect(
|
||||
config.logoutUrl || `${config.basePath}/login`,
|
||||
`${config.basePath}/login`
|
||||
);
|
||||
}
|
||||
}, [data, dispatchChat, client]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<LogoutWrapperStyled>
|
||||
<ScaleLoader height={14} width={1} color={themeColors.blue3} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={14}
|
||||
style={{ color: themeColors.blue3 }}
|
||||
/>
|
||||
</LogoutWrapperStyled>
|
||||
);
|
||||
}
|
||||
|
|
@ -72,7 +77,10 @@ export const LogoutButton = () => {
|
|||
dispatchChat({ type: 'disconnected' });
|
||||
client.clearStore();
|
||||
|
||||
window.location.href = logoutUrl || `${basePath}/login`;
|
||||
safeRedirect(
|
||||
config.logoutUrl || `${config.basePath}/login`,
|
||||
`${config.basePath}/login`
|
||||
);
|
||||
}
|
||||
}, [data, dispatchChat, client]);
|
||||
|
||||
|
|
@ -80,7 +88,11 @@ export const LogoutButton = () => {
|
|||
return (
|
||||
<Logout>
|
||||
<HeaderNavButton>
|
||||
<ScaleLoader height={14} width={1} color={themeColors.blue3} />
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={14}
|
||||
style={{ color: themeColors.blue3 }}
|
||||
/>
|
||||
</HeaderNavButton>
|
||||
</Logout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
import ReactModal from 'styled-react-modal';
|
||||
import { cardColor, mediaWidths, themeColors } from '../../styles/Themes';
|
||||
import { ReactNode } from 'react';
|
||||
import { Dialog, DialogContent } from '../ui/dialog';
|
||||
|
||||
interface ModalProps {
|
||||
children: ReactNode;
|
||||
|
|
@ -10,55 +8,30 @@ interface ModalProps {
|
|||
closeCallback: () => void;
|
||||
}
|
||||
|
||||
const generalCSS = css`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateY(-50%) translateX(-50%);
|
||||
background-color: ${cardColor};
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
border: 1px solid ${themeColors.grey8};
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
/* top: 100%; */
|
||||
border-radius: 0px;
|
||||
/* transform: translateY(-100%) translateX(-50%); */
|
||||
width: 100%;
|
||||
min-width: 325px;
|
||||
max-height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyleModal = ReactModal.styled`
|
||||
${generalCSS}
|
||||
min-width: 578px;
|
||||
`;
|
||||
|
||||
const StyleModalSmall = ReactModal.styled`
|
||||
${generalCSS}
|
||||
background-color: ${themeColors.white};
|
||||
`;
|
||||
|
||||
const Modal = ({
|
||||
children,
|
||||
isOpen,
|
||||
noMinWidth = false,
|
||||
closeCallback,
|
||||
}: ModalProps) => {
|
||||
const Styled = noMinWidth ? StyleModalSmall : StyleModal;
|
||||
|
||||
return (
|
||||
<Styled
|
||||
isOpen={isOpen}
|
||||
onBackgroundClick={closeCallback}
|
||||
onEscapeKeydown={closeCallback}
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={open => {
|
||||
if (!open) closeCallback();
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Styled>
|
||||
<DialogContent
|
||||
showCloseButton={false}
|
||||
className={
|
||||
noMinWidth
|
||||
? 'max-h-[80vh] overflow-y-auto'
|
||||
: 'max-h-[80vh] overflow-y-auto sm:max-w-[578px]'
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useUpdateFeesMutation } from '../../../../src/graphql/mutations/__generated__/updateFees.generated';
|
||||
import { getErrorContent } from '../../../../src/utils/error';
|
||||
import { InputWithDeco } from '../../../../src/components/input/InputWithDeco';
|
||||
import { ColorButton } from '../../../../src/components/buttons/colorButton/ColorButton';
|
||||
import { Input } from '../../../../src/components/input';
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useUpdateFeesMutation } from '@/graphql/mutations/__generated__/updateFees.generated';
|
||||
import { getErrorContent } from '@/utils/error';
|
||||
import { InputWithDeco } from '@/components/input/InputWithDeco';
|
||||
import { ColorButton } from '@/components/buttons/colorButton/ColorButton';
|
||||
import { Input } from '@/components/input';
|
||||
import { SingleLine, SubTitle, Sub4Title } from '../../generic/Styled';
|
||||
|
||||
type ChangeDetailsType = {
|
||||
|
|
@ -55,9 +55,11 @@ export const ChangeDetails = ({
|
|||
toast.error(getErrorContent(error));
|
||||
},
|
||||
onCompleted: data => {
|
||||
data.updateFees
|
||||
? toast.success('Channel policy updated')
|
||||
: toast.error('Error updating channel policy');
|
||||
if (data.updateFees) {
|
||||
toast.success('Channel policy updated');
|
||||
} else {
|
||||
toast.error('Error updating channel policy');
|
||||
}
|
||||
},
|
||||
refetchQueries: ['GetChannels', 'ChannelFees'],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useState } from 'react';
|
||||
import { AlertTriangle } from 'react-feather';
|
||||
import { useState } from 'react';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import styled from 'styled-components';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useCloseChannelMutation } from '../../../../src/graphql/mutations/__generated__/closeChannel.generated';
|
||||
import { useBitcoinFees } from '../../../../src/hooks/UseBitcoinFees';
|
||||
import { useConfigState } from '../../../../src/context/ConfigContext';
|
||||
import { renderLine } from '../../../../src/components/generic/helpers';
|
||||
import { InputWithDeco } from '../../../../src/components/input/InputWithDeco';
|
||||
import { chartColors } from '../../../../src/styles/Themes';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useCloseChannelMutation } from '@/graphql/mutations/__generated__/closeChannel.generated';
|
||||
import { useBitcoinFees } from '@/hooks/UseBitcoinFees';
|
||||
import { useConfigState } from '@/context/ConfigContext';
|
||||
import { renderLine } from '@/components/generic/helpers';
|
||||
import { InputWithDeco } from '@/components/input/InputWithDeco';
|
||||
import { chartColors } from '@/styles/Themes';
|
||||
import {
|
||||
Separation,
|
||||
SingleLine,
|
||||
|
|
@ -56,7 +56,7 @@ export const CloseChannel = ({
|
|||
const [closeChannel, { loading }] = useCloseChannelMutation({
|
||||
onCompleted: () => {
|
||||
toast.success('Channel Closed');
|
||||
() => setIsConfirmed(false);
|
||||
setIsConfirmed(false);
|
||||
callback?.();
|
||||
},
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { AlertTriangle } from 'react-feather';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import styled from 'styled-components';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRemovePeerMutation } from '../../../../src/graphql/mutations/__generated__/removePeer.generated';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useRemovePeerMutation } from '@/graphql/mutations/__generated__/removePeer.generated';
|
||||
import { SubTitle } from '../../generic/Styled';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import styled from 'styled-components';
|
||||
import { chartColors, mediaWidths } from '../../../src/styles/Themes';
|
||||
import { chartColors, mediaWidths } from '@/styles/Themes';
|
||||
|
||||
export const BetaNotification = styled.div`
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { getValue } from '../../utils/helpers';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, Fragment, ReactNode } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { ThemeSet } from 'styled-theming';
|
||||
import { backgroundColor, mediaWidths } from '../../styles/Themes';
|
||||
|
|
@ -47,14 +47,14 @@ type SectionProps = {
|
|||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const Section: React.FC<SectionProps> = ({
|
||||
export const Section: FC<SectionProps> = ({
|
||||
fixedWidth = false,
|
||||
children,
|
||||
color,
|
||||
textColor,
|
||||
padding,
|
||||
}) => {
|
||||
const Fixed = fixedWidth ? FixedWidth : React.Fragment;
|
||||
const Fixed = fixedWidth ? FixedWidth : Fragment;
|
||||
|
||||
return (
|
||||
<FullWidth padding={padding} sectionColor={color} textColor={textColor}>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import styled from 'styled-components';
|
||||
import { mediaWidths, themeColors } from '../../../src/styles/Themes';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { mediaWidths, themeColors } from '@/styles/Themes';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { SingleLine } from '../generic/Styled';
|
||||
import { Select, SelectWithValue, ValueProp } from '.';
|
||||
import { FC, ReactNode } from 'react';
|
||||
|
|
@ -33,7 +33,6 @@ const InputLine = styled(SingleLine)`
|
|||
type InputWithDecoProps = {
|
||||
title: string;
|
||||
options: ValueProp[];
|
||||
isMulti?: boolean;
|
||||
noInput?: boolean;
|
||||
loading?: boolean;
|
||||
maxWidth?: string;
|
||||
|
|
@ -46,7 +45,6 @@ export const SelectWithDeco: FC<InputWithDecoProps> = ({
|
|||
title,
|
||||
noInput,
|
||||
options,
|
||||
isMulti,
|
||||
loading,
|
||||
maxWidth,
|
||||
callback,
|
||||
|
|
@ -54,11 +52,16 @@ export const SelectWithDeco: FC<InputWithDecoProps> = ({
|
|||
const renderContent = () => {
|
||||
switch (true) {
|
||||
case loading:
|
||||
return <ScaleLoader height={20} color={themeColors.blue3} />;
|
||||
return (
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={20}
|
||||
style={{ color: themeColors.blue3 }}
|
||||
/>
|
||||
);
|
||||
case !noInput:
|
||||
return (
|
||||
<Select
|
||||
isMulti={isMulti}
|
||||
maxWidth={maxWidth || '500px'}
|
||||
options={options}
|
||||
callback={callback}
|
||||
|
|
@ -83,7 +86,6 @@ type InputWithDecoAndValueProps = {
|
|||
title: string;
|
||||
value: ValueProp | undefined;
|
||||
options: ValueProp[];
|
||||
isMulti?: boolean;
|
||||
noInput?: boolean;
|
||||
loading?: boolean;
|
||||
callback: (value: ValueProp[]) => void;
|
||||
|
|
@ -95,7 +97,6 @@ export const SelectWithDecoAndValue: React.FC<InputWithDecoAndValueProps> = ({
|
|||
title,
|
||||
noInput,
|
||||
options,
|
||||
isMulti,
|
||||
loading,
|
||||
callback,
|
||||
value,
|
||||
|
|
@ -103,11 +104,16 @@ export const SelectWithDecoAndValue: React.FC<InputWithDecoAndValueProps> = ({
|
|||
const renderContent = () => {
|
||||
switch (true) {
|
||||
case loading:
|
||||
return <ScaleLoader height={20} color={themeColors.blue3} />;
|
||||
return (
|
||||
<Loader2
|
||||
className="animate-spin"
|
||||
size={20}
|
||||
style={{ color: themeColors.blue3 }}
|
||||
/>
|
||||
);
|
||||
case !noInput:
|
||||
return (
|
||||
<SelectWithValue
|
||||
isMulti={isMulti}
|
||||
maxWidth={'500px'}
|
||||
options={options}
|
||||
callback={callback}
|
||||
|
|
|
|||
|
|
@ -1,97 +1,4 @@
|
|||
import React, { useId } from 'react';
|
||||
import ReactSelect from 'react-select';
|
||||
import styled, { css } from 'styled-components';
|
||||
import {
|
||||
inputBackgroundColor,
|
||||
textColor,
|
||||
inputBorderColor,
|
||||
themeColors,
|
||||
selectColors,
|
||||
} from '../../styles/Themes';
|
||||
|
||||
type WrapperProps = {
|
||||
maxWidth?: string;
|
||||
minWidth?: string;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.div<WrapperProps>`
|
||||
${({ maxWidth }) =>
|
||||
maxWidth &&
|
||||
css`
|
||||
max-width: ${maxWidth};
|
||||
`}
|
||||
${({ minWidth }) =>
|
||||
minWidth &&
|
||||
css`
|
||||
min-width: ${minWidth};
|
||||
`}
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
`;
|
||||
|
||||
const StyledSelect = styled(ReactSelect)`
|
||||
& .Select__control {
|
||||
cursor: pointer;
|
||||
background-color: ${inputBackgroundColor};
|
||||
border: 1px solid ${inputBorderColor};
|
||||
font-size: 14px;
|
||||
|
||||
& .Select__control--is-focused {
|
||||
border: 1px solid ${themeColors.blue2};
|
||||
}
|
||||
|
||||
& .Select__single-value {
|
||||
color: ${textColor};
|
||||
}
|
||||
}
|
||||
|
||||
& .Select__menu {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
|
||||
& .Select__option {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .Select__option--is-selected {
|
||||
background-color: ${themeColors.blue2};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSmallSelect = styled(ReactSelect)`
|
||||
& .Select__control {
|
||||
cursor: pointer;
|
||||
background-color: ${selectColors.smallBackground};
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
|
||||
& .Select__control--is-focused {
|
||||
border: 1px solid ${themeColors.blue2};
|
||||
}
|
||||
|
||||
& .Select__single-value {
|
||||
color: ${textColor};
|
||||
}
|
||||
|
||||
& .Select__dropdown-indicator {
|
||||
padding: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
& .Select__menu {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
|
||||
& .Select__option {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .Select__option--is-selected {
|
||||
background-color: ${themeColors.blue2};
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { NativeSelect, NativeSelectOption } from '../ui/native-select';
|
||||
|
||||
export type ValueProp = {
|
||||
value: string | number;
|
||||
|
|
@ -100,41 +7,34 @@ export type ValueProp = {
|
|||
|
||||
type SelectProps = {
|
||||
options: ValueProp[];
|
||||
isMulti?: boolean;
|
||||
maxWidth?: string;
|
||||
callback: (value: ValueProp[]) => void;
|
||||
};
|
||||
|
||||
export const Select = ({
|
||||
isMulti,
|
||||
options,
|
||||
maxWidth,
|
||||
callback,
|
||||
}: SelectProps) => {
|
||||
const handleChange: any = (value: ValueProp | ValueProp[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
callback(value);
|
||||
} else {
|
||||
callback([value]);
|
||||
}
|
||||
};
|
||||
export const Select = ({ options, maxWidth, callback }: SelectProps) => {
|
||||
return (
|
||||
<StyledWrapper maxWidth={maxWidth} fullWidth={true}>
|
||||
<StyledSelect
|
||||
instanceId={useId()}
|
||||
isMulti={isMulti}
|
||||
classNamePrefix={'Select'}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
<div style={{ maxWidth: maxWidth || undefined, width: '100%' }}>
|
||||
<NativeSelect
|
||||
defaultValue=""
|
||||
onChange={e => {
|
||||
const option = options.find(o => String(o.value) === e.target.value);
|
||||
if (option) callback([option]);
|
||||
}}
|
||||
>
|
||||
<NativeSelectOption value="">Select...</NativeSelectOption>
|
||||
{options.map(opt => (
|
||||
<NativeSelectOption key={String(opt.value)} value={String(opt.value)}>
|
||||
{String(opt.label)}
|
||||
</NativeSelectOption>
|
||||
))}
|
||||
</NativeSelect>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type SelectWithValueProps = {
|
||||
options: ValueProp[];
|
||||
value: ValueProp | undefined;
|
||||
isMulti?: boolean;
|
||||
maxWidth?: string;
|
||||
minWidth?: string;
|
||||
isClearable?: boolean;
|
||||
|
|
@ -142,7 +42,6 @@ type SelectWithValueProps = {
|
|||
};
|
||||
|
||||
export const SelectWithValue = ({
|
||||
isMulti,
|
||||
options,
|
||||
maxWidth,
|
||||
minWidth,
|
||||
|
|
@ -150,54 +49,64 @@ export const SelectWithValue = ({
|
|||
value,
|
||||
isClearable = true,
|
||||
}: SelectWithValueProps) => {
|
||||
const handleChange: any = (value: ValueProp | ValueProp[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
callback(value);
|
||||
} else {
|
||||
callback([value]);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<StyledWrapper maxWidth={maxWidth} minWidth={minWidth} fullWidth={false}>
|
||||
<StyledSelect
|
||||
instanceId={useId()}
|
||||
isMulti={isMulti}
|
||||
classNamePrefix={'Select'}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
value={value || null}
|
||||
isClearable={isClearable}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
<div style={{ maxWidth, minWidth, width: maxWidth ? undefined : 'auto' }}>
|
||||
<NativeSelect
|
||||
value={value ? String(value.value) : ''}
|
||||
onChange={e => {
|
||||
const selectedValue = e.target.value;
|
||||
if (!selectedValue) {
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
const option = options.find(o => String(o.value) === selectedValue);
|
||||
if (option) callback([option]);
|
||||
}}
|
||||
>
|
||||
{(isClearable || !value) && (
|
||||
<NativeSelectOption value="">Select...</NativeSelectOption>
|
||||
)}
|
||||
{options.map(opt => (
|
||||
<NativeSelectOption key={String(opt.value)} value={String(opt.value)}>
|
||||
{String(opt.label)}
|
||||
</NativeSelectOption>
|
||||
))}
|
||||
</NativeSelect>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SmallSelectWithValue = ({
|
||||
isMulti,
|
||||
options,
|
||||
maxWidth,
|
||||
callback,
|
||||
value,
|
||||
isClearable = true,
|
||||
}: SelectWithValueProps) => {
|
||||
const handleChange: any = (value: ValueProp | ValueProp[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
callback(value);
|
||||
} else {
|
||||
callback([value]);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<StyledWrapper maxWidth={maxWidth} fullWidth={true}>
|
||||
<StyledSmallSelect
|
||||
instanceId={useId()}
|
||||
isMulti={isMulti}
|
||||
classNamePrefix={'Select'}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
value={value || null}
|
||||
isClearable={isClearable}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
<div style={{ maxWidth, width: '100%' }}>
|
||||
<NativeSelect
|
||||
size="sm"
|
||||
value={value ? String(value.value) : ''}
|
||||
onChange={e => {
|
||||
const selectedValue = e.target.value;
|
||||
if (!selectedValue) {
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
const option = options.find(o => String(o.value) === selectedValue);
|
||||
if (option) callback([option]);
|
||||
}}
|
||||
>
|
||||
{(isClearable || !value) && (
|
||||
<NativeSelectOption value="">Select...</NativeSelectOption>
|
||||
)}
|
||||
{options.map(opt => (
|
||||
<NativeSelectOption key={String(opt.value)} value={String(opt.value)}>
|
||||
{String(opt.label)}
|
||||
</NativeSelectOption>
|
||||
))}
|
||||
</NativeSelect>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import React from 'react';
|
||||
import { shorten } from '../../../../src/components/generic/helpers';
|
||||
import { useGetChannelsWithPeersQuery } from '../../../../src/graphql/queries/__generated__/getChannels.generated';
|
||||
import { shorten } from '@/components/generic/helpers';
|
||||
import { useGetChannelsWithPeersQuery } from '@/graphql/queries/__generated__/getChannels.generated';
|
||||
import { SelectWithDeco } from '../SelectWithDeco';
|
||||
import { Channel } from '../../../graphql/types';
|
||||
import { ValueProp } from '..';
|
||||
|
||||
type ChannelSelectProps = {
|
||||
title: string;
|
||||
isMulti?: boolean;
|
||||
maxWidth?: string;
|
||||
callback: (peer: Channel[]) => void;
|
||||
};
|
||||
|
||||
export const ChannelSelect = ({
|
||||
title,
|
||||
isMulti,
|
||||
maxWidth,
|
||||
callback,
|
||||
}: ChannelSelectProps) => {
|
||||
|
|
@ -59,7 +56,6 @@ export const ChannelSelect = ({
|
|||
|
||||
return (
|
||||
<SelectWithDeco
|
||||
isMulti={isMulti}
|
||||
loading={loading}
|
||||
title={title}
|
||||
options={options}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
import React from 'react';
|
||||
import { useGetPeersQuery } from '../../../../src/graphql/queries/__generated__/getPeers.generated';
|
||||
import { shorten } from '../../../../src/components/generic/helpers';
|
||||
import { Peer } from '../../../../src/graphql/types';
|
||||
import { useGetPeersQuery } from '@/graphql/queries/__generated__/getPeers.generated';
|
||||
import { shorten } from '@/components/generic/helpers';
|
||||
import { Peer } from '@/graphql/types';
|
||||
import { SelectWithDeco } from '../SelectWithDeco';
|
||||
import { ValueProp } from '..';
|
||||
|
||||
type PeerSelectProps = {
|
||||
title: string;
|
||||
isMulti?: boolean;
|
||||
callback: (peer: Peer[]) => void;
|
||||
};
|
||||
|
||||
export const PeerSelect = ({ title, isMulti, callback }: PeerSelectProps) => {
|
||||
export const PeerSelect = ({ title, callback }: PeerSelectProps) => {
|
||||
const { data, loading } = useGetPeersQuery();
|
||||
|
||||
const peers = data?.getPeers || [];
|
||||
|
|
@ -55,7 +53,6 @@ export const PeerSelect = ({ title, isMulti, callback }: PeerSelectProps) => {
|
|||
|
||||
return (
|
||||
<SelectWithDeco
|
||||
isMulti={isMulti}
|
||||
loading={loading}
|
||||
title={title}
|
||||
options={options}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,4 @@
|
|||
import ReactSlider from 'react-slider';
|
||||
import {
|
||||
sliderBackgroundColor,
|
||||
sliderThumbColor,
|
||||
themeColors,
|
||||
} from '../../../src/styles/Themes';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledSlider = styled(ReactSlider)`
|
||||
max-width: 440px;
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
`;
|
||||
|
||||
const StyledThumb = styled.div`
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: ${sliderThumbColor};
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
cursor: grab;
|
||||
`;
|
||||
|
||||
const Thumb = (props: any) => <StyledThumb {...props} />;
|
||||
|
||||
const StyledTrack = styled.div<{ index: number }>`
|
||||
height: 8px;
|
||||
background: ${({ index }) =>
|
||||
index === 1 ? sliderBackgroundColor : themeColors.blue2};
|
||||
border-radius: 8px;
|
||||
`;
|
||||
|
||||
const Track = (props: any, state: any) => (
|
||||
<StyledTrack {...props} index={state.index} />
|
||||
);
|
||||
import { Slider as ShadcnSlider } from '@/components/ui/slider';
|
||||
|
||||
type SliderProps = {
|
||||
value: number;
|
||||
|
|
@ -46,13 +9,12 @@ type SliderProps = {
|
|||
|
||||
export const Slider = ({ value, max, min, onChange }: SliderProps) => {
|
||||
return (
|
||||
<StyledSlider
|
||||
value={value}
|
||||
<ShadcnSlider
|
||||
className="max-w-[440px]"
|
||||
value={[value]}
|
||||
max={max}
|
||||
min={min}
|
||||
renderTrack={Track}
|
||||
renderThumb={Thumb}
|
||||
onChange={value => value && typeof value === 'number' && onChange(value)}
|
||||
onValueChange={values => onChange(values[0])}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { mediaWidths } from '../../../src/styles/Themes';
|
||||
import { mediaWidths } from '@/styles/Themes';
|
||||
|
||||
const StyledSpacer = styled.div`
|
||||
height: 32px;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
SortingState,
|
||||
VisibilityState,
|
||||
} from '@tanstack/react-table';
|
||||
import { Settings, X } from 'react-feather';
|
||||
import { Settings, X } from 'lucide-react';
|
||||
import { separationColor } from '../../styles/Themes';
|
||||
import { ColorButton } from '../buttons/colorButton/ColorButton';
|
||||
import { ColumnConfigurations } from './ColumnConfigurations';
|
||||
|
|
|
|||
64
src/client/src/components/ui/button.tsx
Normal file
64
src/client/src/components/ui/button.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { ComponentProps } from 'react';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
||||
outline:
|
||||
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost:
|
||||
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
||||
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
|
||||
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||
icon: 'size-9',
|
||||
'icon-xs': "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
||||
'icon-sm': 'size-8',
|
||||
'icon-lg': 'size-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
asChild = false,
|
||||
...props
|
||||
}: ComponentProps<'button'> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean;
|
||||
}) {
|
||||
const Comp = asChild ? Slot.Root : 'button';
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
data-variant={variant}
|
||||
data-size={size}
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants };
|
||||
154
src/client/src/components/ui/dialog.tsx
Normal file
154
src/client/src/components/ui/dialog.tsx
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
import { ComponentProps } from 'react';
|
||||
import { XIcon } from 'lucide-react';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
function Dialog({ ...props }: ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogFooter({
|
||||
className,
|
||||
showCloseButton = false,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<'div'> & {
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close asChild>
|
||||
<Button variant="outline">Close</Button>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn('text-lg leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
};
|
||||
53
src/client/src/components/ui/native-select.tsx
Normal file
53
src/client/src/components/ui/native-select.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { ComponentProps } from 'react';
|
||||
import { ChevronDownIcon } from 'lucide-react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function NativeSelect({
|
||||
className,
|
||||
size = 'default',
|
||||
...props
|
||||
}: Omit<ComponentProps<'select'>, 'size'> & { size?: 'sm' | 'default' }) {
|
||||
return (
|
||||
<div
|
||||
className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
|
||||
data-slot="native-select-wrapper"
|
||||
>
|
||||
<select
|
||||
data-slot="native-select"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<ChevronDownIcon
|
||||
className="text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none"
|
||||
aria-hidden="true"
|
||||
data-slot="native-select-icon"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NativeSelectOption({ ...props }: ComponentProps<'option'>) {
|
||||
return <option data-slot="native-select-option" {...props} />;
|
||||
}
|
||||
|
||||
function NativeSelectOptGroup({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<'optgroup'>) {
|
||||
return (
|
||||
<optgroup
|
||||
data-slot="native-select-optgroup"
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
|
||||
61
src/client/src/components/ui/slider.tsx
Normal file
61
src/client/src/components/ui/slider.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { ComponentProps, useMemo } from 'react';
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Slider({
|
||||
className,
|
||||
defaultValue,
|
||||
value,
|
||||
min = 0,
|
||||
max = 100,
|
||||
...props
|
||||
}: ComponentProps<typeof SliderPrimitive.Root>) {
|
||||
const _values = useMemo(
|
||||
() =>
|
||||
Array.isArray(value)
|
||||
? value
|
||||
: Array.isArray(defaultValue)
|
||||
? defaultValue
|
||||
: [min, max],
|
||||
[value, defaultValue, min, max]
|
||||
);
|
||||
|
||||
return (
|
||||
<SliderPrimitive.Root
|
||||
data-slot="slider"
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
className={cn(
|
||||
'relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track
|
||||
data-slot="slider-track"
|
||||
className={cn(
|
||||
'bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5'
|
||||
)}
|
||||
>
|
||||
<SliderPrimitive.Range
|
||||
data-slot="slider-range"
|
||||
className={cn(
|
||||
'bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full'
|
||||
)}
|
||||
/>
|
||||
</SliderPrimitive.Track>
|
||||
{Array.from({ length: _values.length }, (_, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
data-slot="slider-thumb"
|
||||
key={index}
|
||||
className="border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||
/>
|
||||
))}
|
||||
</SliderPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export { Slider };
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { useGetLatestVersionQuery } from '../../../src/graphql/queries/__generated__/getLatestVersion.generated';
|
||||
import getConfig from 'next/config';
|
||||
import { useGetLatestVersionQuery } from '@/graphql/queries/__generated__/getLatestVersion.generated';
|
||||
import { config } from '../../config/thunderhubConfig';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from '../link/Link';
|
||||
|
||||
|
|
@ -17,10 +16,9 @@ const VersionBox = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { npmVersion, noVersionCheck } = publicRuntimeConfig;
|
||||
|
||||
export const Version = () => {
|
||||
const { npmVersion, noVersionCheck } = config;
|
||||
|
||||
const { data, loading, error } = useGetLatestVersionQuery({
|
||||
skip: noVersionCheck,
|
||||
});
|
||||
|
|
@ -34,7 +32,7 @@ export const Version = () => {
|
|||
}
|
||||
|
||||
const githubVersion = data.getLatestVersion.replace('v', '');
|
||||
const version = githubVersion.split('.');
|
||||
const version = githubVersion.split('.').map(Number);
|
||||
const localVersion = npmVersion.split('.').map(Number);
|
||||
|
||||
const newVersionAvailable =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { mediaWidths } from '../../styles/Themes';
|
||||
|
||||
|
|
@ -20,10 +20,7 @@ interface ViewSwitchProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const ViewSwitch: React.FC<ViewSwitchProps> = ({
|
||||
hideMobile,
|
||||
children,
|
||||
}) => {
|
||||
export const ViewSwitch: FC<ViewSwitchProps> = ({ hideMobile, children }) => {
|
||||
return hideMobile ? (
|
||||
<HideMobile>{children}</HideMobile>
|
||||
) : (
|
||||
|
|
|
|||
33
src/client/src/config/thunderhubConfig.ts
Normal file
33
src/client/src/config/thunderhubConfig.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
interface ThunderhubConfig {
|
||||
apiUrl: string;
|
||||
basePath: string;
|
||||
mempoolUrl: string;
|
||||
defaultTheme: string;
|
||||
defaultCurrency: string;
|
||||
fetchPrices: boolean;
|
||||
fetchFees: boolean;
|
||||
disableLinks: boolean;
|
||||
noVersionCheck: boolean;
|
||||
logoutUrl: string;
|
||||
disable2FA: boolean;
|
||||
npmVersion: string;
|
||||
}
|
||||
|
||||
export const config: ThunderhubConfig = {
|
||||
apiUrl: '/graphql',
|
||||
basePath: '',
|
||||
mempoolUrl: 'https://mempool.space',
|
||||
defaultTheme: 'dark',
|
||||
defaultCurrency: 'sat',
|
||||
fetchPrices: true,
|
||||
fetchFees: true,
|
||||
disableLinks: false,
|
||||
noVersionCheck: false,
|
||||
logoutUrl: '',
|
||||
disable2FA: false,
|
||||
npmVersion: '0.0.1',
|
||||
};
|
||||
|
||||
export function initConfig(data: Partial<ThunderhubConfig>): void {
|
||||
Object.assign(config, data);
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import React, { ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
|
||||
type State = {
|
||||
hasToken: boolean;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'change';
|
||||
hasToken: boolean;
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
export const StateContext = createContext<State | undefined>(undefined);
|
||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'change':
|
||||
return { hasToken: action.hasToken };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const BaseProvider: React.FC<{
|
||||
initialHasToken: boolean;
|
||||
children?: ReactNode;
|
||||
}> = ({ children, initialHasToken = false }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, {
|
||||
hasToken: initialHasToken,
|
||||
});
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useBaseState = () => {
|
||||
const context = useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useBaseState must be used within a BaseProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const useBaseDispatch = () => {
|
||||
const context = useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useBaseDispatch must be used within a BaseProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { BaseProvider, useBaseState, useBaseDispatch };
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
import { Message } from '../../src/graphql/types';
|
||||
import { FC, ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
import { Message } from '@/graphql/types';
|
||||
|
||||
export interface SentChatProps extends Message {
|
||||
isSent?: boolean;
|
||||
|
|
@ -92,7 +92,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
}
|
||||
};
|
||||
|
||||
const ChatProvider: React.FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const ChatProvider: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import React, {
|
||||
import {
|
||||
FC,
|
||||
createContext,
|
||||
useContext,
|
||||
useReducer,
|
||||
useEffect,
|
||||
ReactNode,
|
||||
} from 'react';
|
||||
import getConfig from 'next/config';
|
||||
import Cookies from 'js-cookie';
|
||||
import { omit } from 'lodash';
|
||||
import { config } from '../config/thunderhubConfig';
|
||||
|
||||
const themeTypes = ['dark', 'light', 'night'];
|
||||
const themeTypes = ['dark', 'light'];
|
||||
const currencyTypes = ['sat', 'btc', 'fiat'];
|
||||
|
||||
export type channelBarStyleTypes =
|
||||
|
|
@ -39,7 +40,6 @@ export type maxSatValueType = 'auto' | 1000000 | 5000000 | 10000000 | 16777215;
|
|||
type State = {
|
||||
currency: string;
|
||||
theme: string;
|
||||
lnMarketsAuth: boolean;
|
||||
sidebar: boolean;
|
||||
fetchFees: boolean;
|
||||
fetchPrices: boolean;
|
||||
|
|
@ -67,7 +67,6 @@ type ActionType =
|
|||
type: 'change' | 'initChange';
|
||||
currency?: string;
|
||||
theme?: string;
|
||||
lnMarketsAuth?: boolean;
|
||||
sidebar?: boolean;
|
||||
fetchFees?: boolean;
|
||||
fetchPrices?: boolean;
|
||||
|
|
@ -91,33 +90,27 @@ type Dispatch = (action: ActionType) => void;
|
|||
const StateContext = createContext<State | undefined>(undefined);
|
||||
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const {
|
||||
defaultTheme: defT,
|
||||
defaultCurrency: defC,
|
||||
fetchPrices,
|
||||
fetchFees,
|
||||
} = publicRuntimeConfig;
|
||||
|
||||
const initialState: State = {
|
||||
currency: currencyTypes.indexOf(defC) > -1 ? defC : 'sat',
|
||||
theme: themeTypes.indexOf(defT) > -1 ? defT : 'dark',
|
||||
lnMarketsAuth: false,
|
||||
sidebar: true,
|
||||
fetchFees,
|
||||
fetchPrices,
|
||||
displayValues: true,
|
||||
hideFee: false,
|
||||
hideNonVerified: false,
|
||||
maxFee: 20,
|
||||
chatPollingSpeed: 1000,
|
||||
channelBarStyle: 'normal',
|
||||
channelBarType: 'balance',
|
||||
channelSort: 'none',
|
||||
sortDirection: 'decrease',
|
||||
extraColumns: 'none',
|
||||
maxSatValue: 'auto',
|
||||
useSatWord: false,
|
||||
const getInitialState = (): State => {
|
||||
const { defaultTheme: defT, defaultCurrency: defC } = config;
|
||||
return {
|
||||
currency: currencyTypes.indexOf(defC) > -1 ? defC : 'sat',
|
||||
theme: themeTypes.indexOf(defT) > -1 ? defT : 'dark',
|
||||
sidebar: true,
|
||||
fetchFees: config.fetchFees,
|
||||
fetchPrices: config.fetchPrices,
|
||||
displayValues: true,
|
||||
hideFee: false,
|
||||
hideNonVerified: false,
|
||||
maxFee: 20,
|
||||
chatPollingSpeed: 1000,
|
||||
channelBarStyle: 'normal',
|
||||
channelBarType: 'balance',
|
||||
channelSort: 'none',
|
||||
sortDirection: 'decrease',
|
||||
extraColumns: 'none',
|
||||
maxSatValue: 'auto',
|
||||
useSatWord: false,
|
||||
};
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
|
|
@ -134,10 +127,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
...state,
|
||||
...settings,
|
||||
};
|
||||
localStorage.setItem(
|
||||
'config',
|
||||
JSON.stringify(omit(newState, 'theme', 'lnMarketsAuth'))
|
||||
);
|
||||
localStorage.setItem('config', JSON.stringify(omit(newState, 'theme')));
|
||||
return newState;
|
||||
}
|
||||
case 'themeChange': {
|
||||
|
|
@ -158,12 +148,12 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
}
|
||||
};
|
||||
|
||||
const ConfigProvider: React.FC<ConfigInitProps> = ({
|
||||
const ConfigProvider: FC<ConfigInitProps> = ({
|
||||
children,
|
||||
initialConfig = { theme: 'dark' },
|
||||
}) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, {
|
||||
...initialState,
|
||||
...getInitialState(),
|
||||
theme:
|
||||
themeTypes.indexOf(initialConfig.theme) > -1
|
||||
? initialConfig.theme
|
||||
|
|
|
|||
|
|
@ -1,19 +1,14 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import { PriceProvider } from './PriceContext';
|
||||
import { ChatProvider } from './ChatContext';
|
||||
import { RebalanceProvider } from './RebalanceContext';
|
||||
import { DashProvider } from './DashContext';
|
||||
import { NotificationProvider } from './NotificationContext';
|
||||
|
||||
export const ContextProvider: React.FC<{ children?: ReactNode }> = ({
|
||||
children,
|
||||
}) => (
|
||||
export const ContextProvider: FC<{ children?: ReactNode }> = ({ children }) => (
|
||||
<NotificationProvider>
|
||||
<DashProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<RebalanceProvider>{children}</RebalanceProvider>
|
||||
</ChatProvider>
|
||||
<ChatProvider>{children}</ChatProvider>
|
||||
</PriceProvider>
|
||||
</DashProvider>
|
||||
</NotificationProvider>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
import { FC, ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
|
||||
type State = {
|
||||
modalType: string;
|
||||
|
|
@ -23,7 +23,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
}
|
||||
};
|
||||
|
||||
const DashProvider: React.FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const DashProvider: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, {
|
||||
modalType: '',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, {
|
||||
import {
|
||||
FC,
|
||||
createContext,
|
||||
useContext,
|
||||
useReducer,
|
||||
|
|
@ -63,9 +64,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
}
|
||||
};
|
||||
|
||||
const NotificationProvider: React.FC<{ children?: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const NotificationProvider: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, {
|
||||
import {
|
||||
FC,
|
||||
createContext,
|
||||
useContext,
|
||||
useReducer,
|
||||
|
|
@ -63,7 +64,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
}
|
||||
};
|
||||
|
||||
const PriceProvider: React.FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const PriceProvider: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
import React, { ReactNode, createContext, useContext, useReducer } from 'react';
|
||||
import { Channel } from '../../src/graphql/types';
|
||||
|
||||
type State = {
|
||||
inChannel: Channel | null;
|
||||
outChannel: Channel | null;
|
||||
};
|
||||
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'setIn';
|
||||
channel: Channel | null;
|
||||
}
|
||||
| {
|
||||
type: 'setOut';
|
||||
channel: Channel | null;
|
||||
}
|
||||
| {
|
||||
type: 'clear';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
export const StateContext = createContext<State | undefined>(undefined);
|
||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState: State = {
|
||||
inChannel: null,
|
||||
outChannel: null,
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'setIn':
|
||||
return { ...state, inChannel: action.channel };
|
||||
case 'setOut':
|
||||
return { ...state, outChannel: action.channel };
|
||||
case 'clear':
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const RebalanceProvider: React.FC<{ children?: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useRebalanceState = () => {
|
||||
const context = useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
'useRebalanceState must be used within a RebalanceProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const useRebalanceDispatch = () => {
|
||||
const context = useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
'useRebalanceDispatch must be used within a RebalanceProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { RebalanceProvider, useRebalanceState, useRebalanceDispatch };
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
import React, { FC, ReactNode, useCallback, useRef, useState } from 'react';
|
||||
import io from 'socket.io-client';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { basePath } = publicRuntimeConfig;
|
||||
|
||||
type Connection = {
|
||||
socket: Socket | undefined;
|
||||
cleanup: () => void;
|
||||
};
|
||||
|
||||
type CreateConnection = () => Connection;
|
||||
|
||||
type Status = 'connecting' | 'connected' | 'disconnected';
|
||||
|
||||
type Context = {
|
||||
createConnection: CreateConnection;
|
||||
getConnection: () => Socket | undefined;
|
||||
getLastMessage: (forEvent: string) => any;
|
||||
setLastMessage: (forEvent: string, message: any) => void;
|
||||
registerSharedListener: (forEvent: string) => void;
|
||||
getError: () => any;
|
||||
setError: (error: any) => void;
|
||||
getStatus: () => Status;
|
||||
};
|
||||
|
||||
const SocketContext = React.createContext<Context | undefined>(undefined);
|
||||
|
||||
const SocketProvider: FC<{ authToken?: string; children?: ReactNode }> = ({
|
||||
children,
|
||||
authToken,
|
||||
}) => {
|
||||
const sockets = useRef<Socket | undefined>(undefined);
|
||||
|
||||
const [status, setStatus] = useState<Status>('disconnected');
|
||||
const [error, setError] = useState<any>(undefined);
|
||||
|
||||
const [lastMessages, setLastMessages] = useState<Record<string, any>>({});
|
||||
|
||||
const createConnection = useCallback(() => {
|
||||
const cleanup = () => {
|
||||
sockets.current?.disconnect();
|
||||
};
|
||||
|
||||
// Early return if the user has no authToken cookie
|
||||
if (!authToken) {
|
||||
return { socket: undefined, cleanup };
|
||||
}
|
||||
|
||||
if (sockets.current) {
|
||||
sockets.current.connect();
|
||||
return { socket: sockets.current, cleanup };
|
||||
}
|
||||
|
||||
const handleConnect = () => setStatus('connected');
|
||||
const handleDisconnect = () => setStatus('disconnected');
|
||||
|
||||
const socket = io({
|
||||
...(basePath ? { path: `${basePath}/socket.io` } : {}),
|
||||
reconnectionAttempts: 5,
|
||||
});
|
||||
|
||||
sockets.current = socket;
|
||||
|
||||
socket.on('error', (error: any) => setError(error));
|
||||
socket.on('connect', handleConnect);
|
||||
socket.on('disconnect', handleDisconnect);
|
||||
|
||||
return { socket, cleanup };
|
||||
}, [authToken]);
|
||||
|
||||
const getLastMessage = (forEvent = '') => lastMessages[forEvent];
|
||||
const setLastMessage = (forEvent: string, message: any) =>
|
||||
setLastMessages(state => ({
|
||||
...state,
|
||||
[forEvent]: message,
|
||||
}));
|
||||
|
||||
const getConnection = () => sockets.current;
|
||||
const getStatus = () => status;
|
||||
const getError = () => error;
|
||||
|
||||
const registerSharedListener = (forEvent = '') => {
|
||||
if (!sockets.current) return;
|
||||
if (sockets.current.hasListeners(forEvent)) return;
|
||||
|
||||
sockets.current.on(forEvent, (message: any) => {
|
||||
setLastMessages(state => ({
|
||||
...state,
|
||||
[forEvent]: message,
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SocketContext.Provider
|
||||
value={{
|
||||
createConnection,
|
||||
getConnection,
|
||||
getLastMessage,
|
||||
setLastMessage,
|
||||
getError,
|
||||
setError,
|
||||
getStatus,
|
||||
registerSharedListener,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { SocketProvider, SocketContext };
|
||||
99
src/client/src/context/SseContext.tsx
Normal file
99
src/client/src/context/SseContext.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import {
|
||||
createContext,
|
||||
FC,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { config } from '../config/thunderhubConfig';
|
||||
|
||||
type Status = 'connecting' | 'connected' | 'disconnected';
|
||||
|
||||
type Connection = {
|
||||
cleanup: () => void;
|
||||
};
|
||||
|
||||
type Context = {
|
||||
connect: () => Connection;
|
||||
getLastMessage: (forEvent: string) => any;
|
||||
getError: () => any;
|
||||
getStatus: () => Status;
|
||||
};
|
||||
|
||||
const SseContext = createContext<Context | undefined>(undefined);
|
||||
|
||||
const SseProvider: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||
const eventSource = useRef<EventSource | undefined>(undefined);
|
||||
|
||||
const [status, setStatus] = useState<Status>('disconnected');
|
||||
const [error, setError] = useState<any>(undefined);
|
||||
const [lastMessages, setLastMessages] = useState<Record<string, any>>({});
|
||||
|
||||
const connect = useCallback(() => {
|
||||
const cleanup = () => {
|
||||
eventSource.current?.close();
|
||||
setStatus('disconnected');
|
||||
};
|
||||
|
||||
if (
|
||||
eventSource.current &&
|
||||
eventSource.current.readyState !== EventSource.CLOSED
|
||||
) {
|
||||
return { cleanup };
|
||||
}
|
||||
|
||||
setStatus('connecting');
|
||||
|
||||
const url = `${config.basePath || ''}/api/sse/events`;
|
||||
const es = new EventSource(url, { withCredentials: true });
|
||||
|
||||
eventSource.current = es;
|
||||
|
||||
es.onopen = () => setStatus('connected');
|
||||
|
||||
es.onmessage = (event: MessageEvent) => {
|
||||
if (!event.data) return;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(event.data);
|
||||
const { event: eventName, data } = parsed;
|
||||
|
||||
if (eventName && data) {
|
||||
setLastMessages(state => ({
|
||||
...state,
|
||||
[eventName]: data,
|
||||
}));
|
||||
}
|
||||
} catch {
|
||||
// heartbeat or invalid data, ignore
|
||||
}
|
||||
};
|
||||
|
||||
es.onerror = () => {
|
||||
setError('SSE connection error');
|
||||
setStatus('disconnected');
|
||||
};
|
||||
|
||||
return { cleanup };
|
||||
}, []);
|
||||
|
||||
const getLastMessage = (forEvent = '') => lastMessages[forEvent];
|
||||
const getStatus = () => status;
|
||||
const getError = () => error;
|
||||
|
||||
return (
|
||||
<SseContext.Provider
|
||||
value={{
|
||||
connect,
|
||||
getLastMessage,
|
||||
getError,
|
||||
getStatus,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SseContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { SseProvider, SseContext };
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type BosRebalanceMutationVariables = Types.Exact<{
|
||||
avoid?: Types.InputMaybe<
|
||||
Array<Types.Scalars['String']['input']> | Types.Scalars['String']['input']
|
||||
>;
|
||||
in_through?: Types.InputMaybe<Types.Scalars['String']['input']>;
|
||||
max_fee?: Types.InputMaybe<Types.Scalars['Float']['input']>;
|
||||
max_fee_rate?: Types.InputMaybe<Types.Scalars['Float']['input']>;
|
||||
max_rebalance?: Types.InputMaybe<Types.Scalars['Float']['input']>;
|
||||
timeout_minutes?: Types.InputMaybe<Types.Scalars['Float']['input']>;
|
||||
node?: Types.InputMaybe<Types.Scalars['String']['input']>;
|
||||
out_through?: Types.InputMaybe<Types.Scalars['String']['input']>;
|
||||
out_inbound?: Types.InputMaybe<Types.Scalars['Float']['input']>;
|
||||
}>;
|
||||
|
||||
export type BosRebalanceMutation = {
|
||||
__typename?: 'Mutation';
|
||||
bosRebalance: {
|
||||
__typename?: 'BosRebalanceResult';
|
||||
increase?: {
|
||||
__typename?: 'BosIncrease';
|
||||
increased_inbound_on: string;
|
||||
liquidity_inbound: string;
|
||||
liquidity_inbound_opening?: string | null;
|
||||
liquidity_inbound_pending?: string | null;
|
||||
liquidity_outbound: string;
|
||||
liquidity_outbound_opening?: string | null;
|
||||
liquidity_outbound_pending?: string | null;
|
||||
} | null;
|
||||
decrease?: {
|
||||
__typename?: 'BosDecrease';
|
||||
decreased_inbound_on: string;
|
||||
liquidity_inbound: string;
|
||||
liquidity_inbound_opening?: string | null;
|
||||
liquidity_inbound_pending?: string | null;
|
||||
liquidity_outbound: string;
|
||||
liquidity_outbound_opening?: string | null;
|
||||
liquidity_outbound_pending?: string | null;
|
||||
} | null;
|
||||
result?: {
|
||||
__typename?: 'BosResult';
|
||||
rebalanced: string;
|
||||
rebalance_fees_spent: string;
|
||||
} | null;
|
||||
};
|
||||
};
|
||||
|
||||
export const BosRebalanceDocument = gql`
|
||||
mutation BosRebalance(
|
||||
$avoid: [String!]
|
||||
$in_through: String
|
||||
$max_fee: Float
|
||||
$max_fee_rate: Float
|
||||
$max_rebalance: Float
|
||||
$timeout_minutes: Float
|
||||
$node: String
|
||||
$out_through: String
|
||||
$out_inbound: Float
|
||||
) {
|
||||
bosRebalance(
|
||||
avoid: $avoid
|
||||
in_through: $in_through
|
||||
max_fee: $max_fee
|
||||
max_fee_rate: $max_fee_rate
|
||||
max_rebalance: $max_rebalance
|
||||
timeout_minutes: $timeout_minutes
|
||||
node: $node
|
||||
out_through: $out_through
|
||||
out_inbound: $out_inbound
|
||||
) {
|
||||
increase {
|
||||
increased_inbound_on
|
||||
liquidity_inbound
|
||||
liquidity_inbound_opening
|
||||
liquidity_inbound_pending
|
||||
liquidity_outbound
|
||||
liquidity_outbound_opening
|
||||
liquidity_outbound_pending
|
||||
}
|
||||
decrease {
|
||||
decreased_inbound_on
|
||||
liquidity_inbound
|
||||
liquidity_inbound_opening
|
||||
liquidity_inbound_pending
|
||||
liquidity_outbound
|
||||
liquidity_outbound_opening
|
||||
liquidity_outbound_pending
|
||||
}
|
||||
result {
|
||||
rebalanced
|
||||
rebalance_fees_spent
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type BosRebalanceMutationFn = Apollo.MutationFunction<
|
||||
BosRebalanceMutation,
|
||||
BosRebalanceMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useBosRebalanceMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useBosRebalanceMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useBosRebalanceMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [bosRebalanceMutation, { data, loading, error }] = useBosRebalanceMutation({
|
||||
* variables: {
|
||||
* avoid: // value for 'avoid'
|
||||
* in_through: // value for 'in_through'
|
||||
* max_fee: // value for 'max_fee'
|
||||
* max_fee_rate: // value for 'max_fee_rate'
|
||||
* max_rebalance: // value for 'max_rebalance'
|
||||
* timeout_minutes: // value for 'timeout_minutes'
|
||||
* node: // value for 'node'
|
||||
* out_through: // value for 'out_through'
|
||||
* out_inbound: // value for 'out_inbound'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useBosRebalanceMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
BosRebalanceMutation,
|
||||
BosRebalanceMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
BosRebalanceMutation,
|
||||
BosRebalanceMutationVariables
|
||||
>(BosRebalanceDocument, options);
|
||||
}
|
||||
export type BosRebalanceMutationHookResult = ReturnType<
|
||||
typeof useBosRebalanceMutation
|
||||
>;
|
||||
export type BosRebalanceMutationResult =
|
||||
Apollo.MutationResult<BosRebalanceMutation>;
|
||||
export type BosRebalanceMutationOptions = Apollo.BaseMutationOptions<
|
||||
BosRebalanceMutation,
|
||||
BosRebalanceMutationVariables
|
||||
>;
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type CreateBaseInvoiceMutationVariables = Types.Exact<{
|
||||
amount: Types.Scalars['Float']['input'];
|
||||
}>;
|
||||
|
||||
export type CreateBaseInvoiceMutation = {
|
||||
__typename?: 'Mutation';
|
||||
createBaseInvoice: {
|
||||
__typename?: 'BaseInvoice';
|
||||
request: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const CreateBaseInvoiceDocument = gql`
|
||||
mutation CreateBaseInvoice($amount: Float!) {
|
||||
createBaseInvoice(amount: $amount) {
|
||||
request
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateBaseInvoiceMutationFn = Apollo.MutationFunction<
|
||||
CreateBaseInvoiceMutation,
|
||||
CreateBaseInvoiceMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useCreateBaseInvoiceMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateBaseInvoiceMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateBaseInvoiceMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createBaseInvoiceMutation, { data, loading, error }] = useCreateBaseInvoiceMutation({
|
||||
* variables: {
|
||||
* amount: // value for 'amount'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateBaseInvoiceMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
CreateBaseInvoiceMutation,
|
||||
CreateBaseInvoiceMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
CreateBaseInvoiceMutation,
|
||||
CreateBaseInvoiceMutationVariables
|
||||
>(CreateBaseInvoiceDocument, options);
|
||||
}
|
||||
export type CreateBaseInvoiceMutationHookResult = ReturnType<
|
||||
typeof useCreateBaseInvoiceMutation
|
||||
>;
|
||||
export type CreateBaseInvoiceMutationResult =
|
||||
Apollo.MutationResult<CreateBaseInvoiceMutation>;
|
||||
export type CreateBaseInvoiceMutationOptions = Apollo.BaseMutationOptions<
|
||||
CreateBaseInvoiceMutation,
|
||||
CreateBaseInvoiceMutationVariables
|
||||
>;
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type CreateThunderPointsMutationVariables = Types.Exact<{
|
||||
id: Types.Scalars['String']['input'];
|
||||
alias: Types.Scalars['String']['input'];
|
||||
uris:
|
||||
| Array<Types.Scalars['String']['input']>
|
||||
| Types.Scalars['String']['input'];
|
||||
public_key: Types.Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
export type CreateThunderPointsMutation = {
|
||||
__typename?: 'Mutation';
|
||||
createThunderPoints: boolean;
|
||||
};
|
||||
|
||||
export const CreateThunderPointsDocument = gql`
|
||||
mutation CreateThunderPoints(
|
||||
$id: String!
|
||||
$alias: String!
|
||||
$uris: [String!]!
|
||||
$public_key: String!
|
||||
) {
|
||||
createThunderPoints(
|
||||
id: $id
|
||||
alias: $alias
|
||||
uris: $uris
|
||||
public_key: $public_key
|
||||
)
|
||||
}
|
||||
`;
|
||||
export type CreateThunderPointsMutationFn = Apollo.MutationFunction<
|
||||
CreateThunderPointsMutation,
|
||||
CreateThunderPointsMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useCreateThunderPointsMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateThunderPointsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateThunderPointsMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createThunderPointsMutation, { data, loading, error }] = useCreateThunderPointsMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* alias: // value for 'alias'
|
||||
* uris: // value for 'uris'
|
||||
* public_key: // value for 'public_key'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateThunderPointsMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
CreateThunderPointsMutation,
|
||||
CreateThunderPointsMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
CreateThunderPointsMutation,
|
||||
CreateThunderPointsMutationVariables
|
||||
>(CreateThunderPointsDocument, options);
|
||||
}
|
||||
export type CreateThunderPointsMutationHookResult = ReturnType<
|
||||
typeof useCreateThunderPointsMutation
|
||||
>;
|
||||
export type CreateThunderPointsMutationResult =
|
||||
Apollo.MutationResult<CreateThunderPointsMutation>;
|
||||
export type CreateThunderPointsMutationOptions = Apollo.BaseMutationOptions<
|
||||
CreateThunderPointsMutation,
|
||||
CreateThunderPointsMutationVariables
|
||||
>;
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type LnMarketsLoginMutationVariables = Types.Exact<{
|
||||
[key: string]: never;
|
||||
}>;
|
||||
|
||||
export type LnMarketsLoginMutation = {
|
||||
__typename?: 'Mutation';
|
||||
lnMarketsLogin: {
|
||||
__typename?: 'AuthResponse';
|
||||
status: string;
|
||||
message: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LnMarketsWithdrawMutationVariables = Types.Exact<{
|
||||
amount: Types.Scalars['Float']['input'];
|
||||
}>;
|
||||
|
||||
export type LnMarketsWithdrawMutation = {
|
||||
__typename?: 'Mutation';
|
||||
lnMarketsWithdraw: boolean;
|
||||
};
|
||||
|
||||
export type LnMarketsDepositMutationVariables = Types.Exact<{
|
||||
amount: Types.Scalars['Float']['input'];
|
||||
}>;
|
||||
|
||||
export type LnMarketsDepositMutation = {
|
||||
__typename?: 'Mutation';
|
||||
lnMarketsDeposit: boolean;
|
||||
};
|
||||
|
||||
export type LnMarketsLogoutMutationVariables = Types.Exact<{
|
||||
[key: string]: never;
|
||||
}>;
|
||||
|
||||
export type LnMarketsLogoutMutation = {
|
||||
__typename?: 'Mutation';
|
||||
lnMarketsLogout: boolean;
|
||||
};
|
||||
|
||||
export const LnMarketsLoginDocument = gql`
|
||||
mutation LnMarketsLogin {
|
||||
lnMarketsLogin {
|
||||
status
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type LnMarketsLoginMutationFn = Apollo.MutationFunction<
|
||||
LnMarketsLoginMutation,
|
||||
LnMarketsLoginMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useLnMarketsLoginMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useLnMarketsLoginMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useLnMarketsLoginMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [lnMarketsLoginMutation, { data, loading, error }] = useLnMarketsLoginMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useLnMarketsLoginMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
LnMarketsLoginMutation,
|
||||
LnMarketsLoginMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
LnMarketsLoginMutation,
|
||||
LnMarketsLoginMutationVariables
|
||||
>(LnMarketsLoginDocument, options);
|
||||
}
|
||||
export type LnMarketsLoginMutationHookResult = ReturnType<
|
||||
typeof useLnMarketsLoginMutation
|
||||
>;
|
||||
export type LnMarketsLoginMutationResult =
|
||||
Apollo.MutationResult<LnMarketsLoginMutation>;
|
||||
export type LnMarketsLoginMutationOptions = Apollo.BaseMutationOptions<
|
||||
LnMarketsLoginMutation,
|
||||
LnMarketsLoginMutationVariables
|
||||
>;
|
||||
export const LnMarketsWithdrawDocument = gql`
|
||||
mutation LnMarketsWithdraw($amount: Float!) {
|
||||
lnMarketsWithdraw(amount: $amount)
|
||||
}
|
||||
`;
|
||||
export type LnMarketsWithdrawMutationFn = Apollo.MutationFunction<
|
||||
LnMarketsWithdrawMutation,
|
||||
LnMarketsWithdrawMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useLnMarketsWithdrawMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useLnMarketsWithdrawMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useLnMarketsWithdrawMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [lnMarketsWithdrawMutation, { data, loading, error }] = useLnMarketsWithdrawMutation({
|
||||
* variables: {
|
||||
* amount: // value for 'amount'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useLnMarketsWithdrawMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
LnMarketsWithdrawMutation,
|
||||
LnMarketsWithdrawMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
LnMarketsWithdrawMutation,
|
||||
LnMarketsWithdrawMutationVariables
|
||||
>(LnMarketsWithdrawDocument, options);
|
||||
}
|
||||
export type LnMarketsWithdrawMutationHookResult = ReturnType<
|
||||
typeof useLnMarketsWithdrawMutation
|
||||
>;
|
||||
export type LnMarketsWithdrawMutationResult =
|
||||
Apollo.MutationResult<LnMarketsWithdrawMutation>;
|
||||
export type LnMarketsWithdrawMutationOptions = Apollo.BaseMutationOptions<
|
||||
LnMarketsWithdrawMutation,
|
||||
LnMarketsWithdrawMutationVariables
|
||||
>;
|
||||
export const LnMarketsDepositDocument = gql`
|
||||
mutation LnMarketsDeposit($amount: Float!) {
|
||||
lnMarketsDeposit(amount: $amount)
|
||||
}
|
||||
`;
|
||||
export type LnMarketsDepositMutationFn = Apollo.MutationFunction<
|
||||
LnMarketsDepositMutation,
|
||||
LnMarketsDepositMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useLnMarketsDepositMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useLnMarketsDepositMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useLnMarketsDepositMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [lnMarketsDepositMutation, { data, loading, error }] = useLnMarketsDepositMutation({
|
||||
* variables: {
|
||||
* amount: // value for 'amount'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useLnMarketsDepositMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
LnMarketsDepositMutation,
|
||||
LnMarketsDepositMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
LnMarketsDepositMutation,
|
||||
LnMarketsDepositMutationVariables
|
||||
>(LnMarketsDepositDocument, options);
|
||||
}
|
||||
export type LnMarketsDepositMutationHookResult = ReturnType<
|
||||
typeof useLnMarketsDepositMutation
|
||||
>;
|
||||
export type LnMarketsDepositMutationResult =
|
||||
Apollo.MutationResult<LnMarketsDepositMutation>;
|
||||
export type LnMarketsDepositMutationOptions = Apollo.BaseMutationOptions<
|
||||
LnMarketsDepositMutation,
|
||||
LnMarketsDepositMutationVariables
|
||||
>;
|
||||
export const LnMarketsLogoutDocument = gql`
|
||||
mutation LnMarketsLogout {
|
||||
lnMarketsLogout
|
||||
}
|
||||
`;
|
||||
export type LnMarketsLogoutMutationFn = Apollo.MutationFunction<
|
||||
LnMarketsLogoutMutation,
|
||||
LnMarketsLogoutMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useLnMarketsLogoutMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useLnMarketsLogoutMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useLnMarketsLogoutMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [lnMarketsLogoutMutation, { data, loading, error }] = useLnMarketsLogoutMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useLnMarketsLogoutMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
LnMarketsLogoutMutation,
|
||||
LnMarketsLogoutMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
LnMarketsLogoutMutation,
|
||||
LnMarketsLogoutMutationVariables
|
||||
>(LnMarketsLogoutDocument, options);
|
||||
}
|
||||
export type LnMarketsLogoutMutationHookResult = ReturnType<
|
||||
typeof useLnMarketsLogoutMutation
|
||||
>;
|
||||
export type LnMarketsLogoutMutationResult =
|
||||
Apollo.MutationResult<LnMarketsLogoutMutation>;
|
||||
export type LnMarketsLogoutMutationOptions = Apollo.BaseMutationOptions<
|
||||
LnMarketsLogoutMutation,
|
||||
LnMarketsLogoutMutationVariables
|
||||
>;
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const BOS_REBALANCE = gql`
|
||||
mutation BosRebalance(
|
||||
$avoid: [String!]
|
||||
$in_through: String
|
||||
$max_fee: Float
|
||||
$max_fee_rate: Float
|
||||
$max_rebalance: Float
|
||||
$timeout_minutes: Float
|
||||
$node: String
|
||||
$out_through: String
|
||||
$out_inbound: Float
|
||||
) {
|
||||
bosRebalance(
|
||||
avoid: $avoid
|
||||
in_through: $in_through
|
||||
max_fee: $max_fee
|
||||
max_fee_rate: $max_fee_rate
|
||||
max_rebalance: $max_rebalance
|
||||
timeout_minutes: $timeout_minutes
|
||||
node: $node
|
||||
out_through: $out_through
|
||||
out_inbound: $out_inbound
|
||||
) {
|
||||
increase {
|
||||
increased_inbound_on
|
||||
liquidity_inbound
|
||||
liquidity_inbound_opening
|
||||
liquidity_inbound_pending
|
||||
liquidity_outbound
|
||||
liquidity_outbound_opening
|
||||
liquidity_outbound_pending
|
||||
}
|
||||
decrease {
|
||||
decreased_inbound_on
|
||||
liquidity_inbound
|
||||
liquidity_inbound_opening
|
||||
liquidity_inbound_pending
|
||||
liquidity_outbound
|
||||
liquidity_outbound_opening
|
||||
liquidity_outbound_pending
|
||||
}
|
||||
result {
|
||||
rebalanced
|
||||
rebalance_fees_spent
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_BASE_INVOICE = gql`
|
||||
mutation CreateBaseInvoice($amount: Float!) {
|
||||
createBaseInvoice(amount: $amount) {
|
||||
request
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_THUNDER_POINTS = gql`
|
||||
mutation CreateThunderPoints(
|
||||
$id: String!
|
||||
$alias: String!
|
||||
$uris: [String!]!
|
||||
$public_key: String!
|
||||
) {
|
||||
createThunderPoints(
|
||||
id: $id
|
||||
alias: $alias
|
||||
uris: $uris
|
||||
public_key: $public_key
|
||||
)
|
||||
}
|
||||
`;
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const LN_MARKETS_LOGIN = gql`
|
||||
mutation LnMarketsLogin {
|
||||
lnMarketsLogin {
|
||||
status
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const LN_MARKETS_WITHDRAW = gql`
|
||||
mutation LnMarketsWithdraw($amount: Float!) {
|
||||
lnMarketsWithdraw(amount: $amount)
|
||||
}
|
||||
`;
|
||||
|
||||
export const LN_MARKETS_DEPOSIT = gql`
|
||||
mutation LnMarketsDeposit($amount: Float!) {
|
||||
lnMarketsDeposit(amount: $amount)
|
||||
}
|
||||
`;
|
||||
|
||||
export const LN_MARKETS_LOGOUT = gql`
|
||||
mutation LnMarketsLogout {
|
||||
lnMarketsLogout
|
||||
}
|
||||
`;
|
||||
|
|
@ -83,7 +83,11 @@ export function useDecodeRequestQuery(
|
|||
baseOptions: Apollo.QueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
> &
|
||||
(
|
||||
| { variables: DecodeRequestQueryVariables; skip?: boolean }
|
||||
| { skip: boolean }
|
||||
)
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<DecodeRequestQuery, DecodeRequestQueryVariables>(
|
||||
|
|
@ -103,13 +107,39 @@ export function useDecodeRequestLazyQuery(
|
|||
options
|
||||
);
|
||||
}
|
||||
// @ts-ignore
|
||||
export function useDecodeRequestSuspenseQuery(
|
||||
baseOptions?: Apollo.SuspenseQueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
): Apollo.UseSuspenseQueryResult<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>;
|
||||
export function useDecodeRequestSuspenseQuery(
|
||||
baseOptions?:
|
||||
| Apollo.SkipToken
|
||||
| Apollo.SuspenseQueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
): Apollo.UseSuspenseQueryResult<
|
||||
DecodeRequestQuery | undefined,
|
||||
DecodeRequestQueryVariables
|
||||
>;
|
||||
export function useDecodeRequestSuspenseQuery(
|
||||
baseOptions?:
|
||||
| Apollo.SkipToken
|
||||
| Apollo.SuspenseQueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
const options =
|
||||
baseOptions === Apollo.skipToken
|
||||
? baseOptions
|
||||
: { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useSuspenseQuery<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
|
|
|
|||
|
|
@ -68,13 +68,30 @@ export function useGetAccountLazyQuery(
|
|||
options
|
||||
);
|
||||
}
|
||||
// @ts-ignore
|
||||
export function useGetAccountSuspenseQuery(
|
||||
baseOptions?: Apollo.SuspenseQueryHookOptions<
|
||||
GetAccountQuery,
|
||||
GetAccountQueryVariables
|
||||
>
|
||||
): Apollo.UseSuspenseQueryResult<GetAccountQuery, GetAccountQueryVariables>;
|
||||
export function useGetAccountSuspenseQuery(
|
||||
baseOptions?:
|
||||
| Apollo.SkipToken
|
||||
| Apollo.SuspenseQueryHookOptions<GetAccountQuery, GetAccountQueryVariables>
|
||||
): Apollo.UseSuspenseQueryResult<
|
||||
GetAccountQuery | undefined,
|
||||
GetAccountQueryVariables
|
||||
>;
|
||||
export function useGetAccountSuspenseQuery(
|
||||
baseOptions?:
|
||||
| Apollo.SkipToken
|
||||
| Apollo.SuspenseQueryHookOptions<GetAccountQuery, GetAccountQueryVariables>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
const options =
|
||||
baseOptions === Apollo.skipToken
|
||||
? baseOptions
|
||||
: { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useSuspenseQuery<GetAccountQuery, GetAccountQueryVariables>(
|
||||
GetAccountDocument,
|
||||
options
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue