Compare commits

...

29 Commits

Author SHA1 Message Date
fmtabbara 77d7fede76 update build script 2024-07-18 15:55:12 +01:00
fmtabbara f6929e365b refactor: Remove unused imports in nodemap page component 2024-07-18 15:27:53 +01:00
fmtabbara f98911f904 temp fix for ci 2024-07-18 15:04:20 +01:00
fmtabbara c1be720e49 fix linting 2024-07-18 14:41:20 +01:00
fmtabbara b4a719dbab fix next js explorer 2024-07-18 12:44:54 +01:00
fmtabbara ce99ccea44 fix pnpm audit recommendations 2024-07-18 12:37:30 +01:00
fmtabbara 595a76ee50 fix next explorer build 2024-07-18 12:37:30 +01:00
fmtabbara dda7c09ec8 fix wallet linting 2024-07-18 12:37:30 +01:00
fmtabbara c27713c678 fix wallet build + storybook 2024-07-18 12:37:30 +01:00
fmtabbara b8125d735e fix docs ci 2024-07-18 12:37:30 +01:00
fmtabbara 96617469e9 fix storybook build 2024-07-18 12:37:30 +01:00
fmtabbara 10ade80da0 fix storybook for react components lib 2024-07-18 12:37:28 +01:00
fmtabbara 6c8d5c129d fix up linting 2024-07-18 12:37:06 +01:00
fmtabbara 4c01ee0150 fix up CI 2024-07-18 12:37:06 +01:00
fmtabbara 9c85d51e34 fix multi package scripts 2024-07-18 12:37:06 +01:00
fmtabbara 30dc257ba7 ci updates 2024-07-18 12:37:06 +01:00
fmtabbara 1878720a31 wallet updates 2024-07-18 12:37:06 +01:00
fmtabbara ae0d9f3179 package updates 2024-07-18 12:37:06 +01:00
fmtabbara 1688eb9477 use components from react-components lib 2024-07-18 12:37:06 +01:00
fmtabbara 3c1939953c new wallet build using vite 2024-07-18 12:37:06 +01:00
fmtabbara 7de43540b9 bundle react components library with vite 2024-07-18 12:37:04 +01:00
fmtabbara 979244f9da update wallet deps 2024-07-18 12:34:39 +01:00
fmtabbara 9e555ebd8a fix types package build 2024-07-18 12:34:39 +01:00
fmtabbara 579ed7bb6e update sdk build scripts 2024-07-18 12:34:39 +01:00
fmtabbara 1655933678 remove lerna 2024-07-18 12:34:39 +01:00
fmtabbara cd2978a8b0 finish next-explorer build 2024-07-18 12:34:39 +01:00
fmtabbara 6fcdbc15ce update ts CI 2024-07-18 12:34:39 +01:00
fmtabbara ceee2d4466 yarn -> pnpm 2024-07-18 12:34:37 +01:00
fmtabbara 8ee3ec2f4e replace yarn with pnpm 2024-07-18 12:32:50 +01:00
544 changed files with 80638 additions and 45690 deletions
+58 -54
View File
@@ -10,57 +10,61 @@ jobs:
build:
runs-on: ubuntu-20.04-16-core
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Build
run: yarn && yarn build && yarn build:ci:storybook
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/storybook/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (example)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/example/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}-example
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: ts-packages
NYM_PROJECT_NAME: "ts-packages"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Build
run: pnpm i && pnpm run build && pnpm run build:ci:storybook
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/storybook/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (example)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/example/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}-example
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: ts-packages
NYM_PROJECT_NAME: "ts-packages"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+11 -10
View File
@@ -21,8 +21,12 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -35,23 +39,20 @@ jobs:
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
version: "116"
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install
run: yarn
go-version: "1.20"
- name: Build packages
run: yarn build:ci
run: pnpm run build:ci
- name: Lint
run: yarn lint
run: pnpm run lint
- name: Typecheck with tsc
run: yarn tsc
run: pnpm run tsc
- name: Matrix - Node Install
run: npm install
+80 -76
View File
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
push:
paths:
- 'explorer/**'
- "explorer/**"
defaults:
run:
@@ -14,78 +14,82 @@ jobs:
build:
runs-on: custom-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
continue-on-error: true
- name: Build shared packages
run: cd .. && yarn && yarn build
- name: Set environment from the example
run: cp .env.prod .env
# - run: yarn test
# continue-on-error: true
- run: yarn && yarn build
continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
continue-on-error: true
- name: Build shared packages
run: cd .. && yarn && yarn build
- name: Set environment from the example
run: cp .env.prod .env
# - run: yarn test
# continue-on-error: true
- run: yarn && yarn build
continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
+56 -53
View File
@@ -3,72 +3,75 @@ name: ci-nym-wallet-storybook
on:
pull_request:
paths:
- 'nym-wallet/**'
- "nym-wallet/**"
jobs:
build:
runs-on: custom-linux
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build dependencies
run: yarn && yarn build
- name: Build dependencies
run: pnpm build
- name: Build storybook
run: yarn storybook:build
working-directory: ./nym-wallet
- name: Build storybook
run: pnpm run storybook:build
working-directory: ./wallet
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-wallet/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-wallet/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
NYM_PROJECT_NAME: "nym-wallet"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "wallet-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
NYM_PROJECT_NAME: "nym-wallet"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "wallet-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+15 -11
View File
@@ -17,18 +17,23 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18.17
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: "1.20"
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -36,16 +41,15 @@ jobs:
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
version: "116"
- name: Build branch WASM packages
run: make sdk-wasm-build
- name: Install
run: yarn
- name: Build
run: yarn docs:prod:build
- name: Deploy branch to CI www (docs)
run: pnpm docs:prod:build
- name: Deploy branch to CI www (docs)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
@@ -57,7 +61,7 @@ jobs:
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/sdk-ts-docs-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
run: pnpm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
@@ -75,4 +79,4 @@ jobs:
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -61,12 +61,12 @@ jobs:
- name: Create env file
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
fileName: ".env"
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
run: cd .. && pnpm i --network-timeout 100000
- name: Install app dependencies and build it
env:
@@ -80,7 +80,7 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build
run: pnpm i && pnpm build
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -112,7 +112,7 @@ jobs:
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
EXCLUDE: "/dist/, /node_modules/"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
Generated
+7 -24
View File
@@ -2221,7 +2221,7 @@ dependencies = [
"atomic 0.6.0",
"pear",
"serde",
"toml 0.8.14",
"toml 0.8.15",
"uncased",
"version_check",
]
@@ -4999,23 +4999,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "nym-network-statistics"
version = "1.1.34"
dependencies = [
"dirs 4.0.0",
"log",
"nym-bin-common",
"nym-statistics-common",
"nym-task",
"pretty_env_logger",
"rocket",
"serde",
"sqlx",
"thiserror",
"tokio",
]
[[package]]
name = "nym-node"
version = "1.1.4"
@@ -5054,7 +5037,7 @@ dependencies = [
"sysinfo 0.30.12",
"thiserror",
"tokio",
"toml 0.8.14",
"toml 0.8.15",
"tracing",
"url",
"zeroize",
@@ -8643,14 +8626,14 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.14",
"toml_edit 0.22.16",
]
[[package]]
@@ -8677,9 +8660,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.14"
version = "0.22.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
dependencies = [
"indexmap 2.2.6",
"serde",
+7 -7
View File
@@ -107,12 +107,12 @@ sdk-wasm-build:
$(MAKE) -C wasm/zknym-lib
#$(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
# run this from npm/pnpm to ensure tools are in the path, e.g. pnpm build:sdk from root of repo
sdk-typescript-build:
npx lerna run --scope @nymproject/sdk build --stream
npx lerna run --scope @nymproject/mix-fetch build --stream
npx lerna run --scope @nymproject/node-tester build --stream
yarn --cwd sdk/typescript/codegen/contract-clients build
pnpm run --filter @nymproject/sdk --stream build
pnpm run --filter @nymproject/mix-fetch --stream build
pnpm run --filter @nymproject/node-tester --stream build
pnpm --cwd sdk/typescript/codegen/contract-clients build
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
@@ -170,10 +170,10 @@ build-nym-mixnode:
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
pnpm types:lint:fix
run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
cd nym-api/tests/functional_test && pnpm test:qa
# Build debian package, and update PPA
deb-mixnode: build-nym-mixnode
+4 -1
View File
@@ -1,3 +1,6 @@
{
"extends": ["next/core-web-vitals"]
"extends": ["next/core-web-vitals"],
"rules": {
"react-hooks/exhaustive-deps": "off"
}
}
+6 -6
View File
@@ -1,11 +1,11 @@
import React from 'react'
import { Navbar } from './components/Nav/Navbar'
import { Providers } from './providers'
import React from "react";
import { Navbar } from "./components/Nav/Navbar";
import { Providers } from "./providers";
const App = ({ children }: { children: React.ReactNode }) => (
const App = ({ children }: { children: any }) => (
<Providers>
<Navbar>{children}</Navbar>
</Providers>
)
);
export { App }
export { App };
@@ -1,9 +1,9 @@
import { Typography } from '@mui/material';
import * as React from 'react';
import { Typography } from "@mui/material";
import * as React from "react";
export const ComponentError: FCWithChildren<{ text: string }> = ({ text }) => (
export const ComponentError = ({ text }: { text: string }) => (
<Typography
sx={{ marginTop: 2, color: 'primary.main', fontSize: 10 }}
sx={{ marginTop: 2, color: "primary.main", fontSize: 10 }}
variant="body1"
data-testid="delegation-total-amount"
>
+16 -15
View File
@@ -1,16 +1,17 @@
import { Card, CardHeader, CardContent, Typography } from '@mui/material'
import React, { ReactEventHandler } from 'react'
import React, { ReactEventHandler } from "react";
import { Card, CardHeader, CardContent, Typography } from "@mui/material";
type ContentCardProps = {
title?: React.ReactNode
subtitle?: string
Icon?: React.ReactNode
Action?: React.ReactNode
errorMsg?: string
onClick?: ReactEventHandler
}
title?: React.ReactNode;
subtitle?: string;
Icon?: React.ReactNode;
Action?: React.ReactNode;
errorMsg?: string;
children: React.ReactNode;
onClick?: ReactEventHandler;
};
export const ContentCard: FCWithChildren<ContentCardProps> = ({
export const ContentCard = ({
title,
Icon,
Action,
@@ -18,11 +19,11 @@ export const ContentCard: FCWithChildren<ContentCardProps> = ({
errorMsg,
children,
onClick,
}) => (
<Card onClick={onClick} sx={{ height: '100%' }}>
}: ContentCardProps) => (
<Card onClick={onClick} sx={{ height: "100%" }}>
{title && (
<CardHeader
title={title || ''}
title={title || ""}
avatar={Icon}
action={Action}
subheader={subtitle}
@@ -30,9 +31,9 @@ export const ContentCard: FCWithChildren<ContentCardProps> = ({
)}
{children && <CardContent>{children}</CardContent>}
{errorMsg && (
<Typography variant="body2" sx={{ color: 'danger', padding: 2 }}>
<Typography variant="body2" sx={{ color: "danger", padding: 2 }}>
{errorMsg}
</Typography>
)}
</Card>
)
);
@@ -1,13 +1,14 @@
import * as React from 'react'
import { Box, Typography } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import * as React from "react";
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { InfoOutlined } from "@mui/icons-material";
export const CustomColumnHeading: FCWithChildren<{
headingTitle: string
tooltipInfo?: string
headingTitle: string;
tooltipInfo?: string;
}> = ({ headingTitle, tooltipInfo }) => {
const theme = useTheme()
const theme = useTheme();
return (
<Box alignItems="center" display="flex">
@@ -20,11 +21,12 @@ export const CustomColumnHeading: FCWithChildren<{
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
maxWidth={230}
arrow
TooltipIcon={<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />}
/>
)}
<Typography variant="body2" fontWeight={600} data-testid={headingTitle}>
{headingTitle}
</Typography>
</Box>
)
}
);
};
@@ -1,35 +1,35 @@
'use client'
"use client";
import React, { useState } from 'react'
import { Box, SxProps } from '@mui/material'
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'
import { CurrencyDenom, DecCoin } from '@nymproject/types'
import { useWalletContext } from '@/app/context/wallet'
import { urls } from '@/app/utils'
import { useDelegationsContext } from '@/app/context/delegations'
import { validateAmount } from '@/app/utils/currency'
import { SimpleModal } from './SimpleModal'
import { ModalListItem } from './ModalListItem'
import { DelegationModalProps } from './DelegationModal'
import React, { useState } from "react";
import { Box, SxProps } from "@mui/material";
import { IdentityKeyFormField } from "@nymproject/react";
import { CurrencyFormField } from "@nymproject/react";
import { CurrencyDenom, DecCoin } from "@nymproject/types";
import { useWalletContext } from "@/app/context/wallet";
import { urls } from "@/app/utils";
import { useDelegationsContext } from "@/app/context/delegations";
import { validateAmount } from "@/app/utils/currency";
import { SimpleModal } from "./SimpleModal";
import { ModalListItem } from "./ModalListItem";
import { DelegationModalProps } from "./DelegationModal";
const MIN_AMOUNT_TO_DELEGATE = 10
const MIN_AMOUNT_TO_DELEGATE = 10;
type Props = {
mixId: number
identityKey: string
header?: string
buttonText?: string
rewardInterval?: string
estimatedReward?: number
profitMarginPercentage?: string | null
nodeUptimePercentage?: number | null
denom: CurrencyDenom
sx?: SxProps
backdropProps?: object
onClose: () => void
onOk?: (delegationModalProps: DelegationModalProps) => void
}
mixId: number;
identityKey: string;
header?: string;
buttonText?: string;
rewardInterval?: string;
estimatedReward?: number;
profitMarginPercentage?: string | null;
nodeUptimePercentage?: number | null;
denom: CurrencyDenom;
sx?: SxProps;
backdropProps?: object;
onClose: () => void;
onOk?: (delegationModalProps: DelegationModalProps) => void;
};
export const DelegateModal = ({
mixId,
@@ -40,106 +40,106 @@ export const DelegateModal = ({
sx,
}: Props) => {
const [amount, setAmount] = useState<DecCoin | undefined>({
amount: '10',
denom: 'nym',
})
const [isValidated, setValidated] = useState<boolean>(false)
const [errorAmount, setErrorAmount] = useState<string | undefined>()
amount: "10",
denom: "nym",
});
const [isValidated, setValidated] = useState<boolean>(false);
const [errorAmount, setErrorAmount] = useState<string | undefined>();
const { address, balance } = useWalletContext()
const { handleDelegate } = useDelegationsContext()
const { address, balance } = useWalletContext();
const { handleDelegate } = useDelegationsContext();
const validate = async () => {
let newValidatedValue = true
let errorAmountMessage
let newValidatedValue = true;
let errorAmountMessage;
if (amount && !(await validateAmount(amount.amount, '0'))) {
newValidatedValue = false
errorAmountMessage = 'Please enter a valid amount'
if (amount && !(await validateAmount(amount.amount, "0"))) {
newValidatedValue = false;
errorAmountMessage = "Please enter a valid amount";
}
if (amount && +amount.amount < MIN_AMOUNT_TO_DELEGATE) {
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`
newValidatedValue = false
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`;
newValidatedValue = false;
}
if (!amount?.amount.length) {
newValidatedValue = false
newValidatedValue = false;
}
if (amount && balance.data && +balance.data - +amount.amount <= 0) {
errorAmountMessage = 'Not enough funds'
newValidatedValue = false
errorAmountMessage = "Not enough funds";
newValidatedValue = false;
}
setErrorAmount(errorAmountMessage)
setValidated(newValidatedValue)
}
setErrorAmount(errorAmountMessage);
setValidated(newValidatedValue);
};
const delegateToMixnode = async ({
delegationMixId,
delegationAmount,
}: {
delegationMixId: number
delegationAmount: string
delegationMixId: number;
delegationAmount: string;
}) => {
try {
const tx = await handleDelegate(delegationMixId, delegationAmount)
return tx
const tx = await handleDelegate(delegationMixId, delegationAmount);
return tx;
} catch (e) {
console.error('Failed to delegate to mixnode', e)
throw e
console.error("Failed to delegate to mixnode", e);
throw e;
}
}
};
const handleConfirm = async () => {
if (mixId && amount && onOk) {
onOk({
status: 'loading',
})
status: "loading",
});
try {
if (!address) {
throw new Error('Please connect your wallet')
throw new Error("Please connect your wallet");
}
const tx = await delegateToMixnode({
delegationMixId: mixId,
delegationAmount: amount.amount,
})
});
if (!tx) {
throw new Error('Failed to delegate')
throw new Error("Failed to delegate");
}
onOk({
status: 'success',
message: 'Delegation can take up to one hour to process',
status: "success",
message: "Delegation can take up to one hour to process",
transactions: [
{
url: `${urls('MAINNET').blockExplorer}/transaction/${
url: `${urls("MAINNET").blockExplorer}/transaction/${
tx.transactionHash
}`,
hash: tx.transactionHash,
},
],
})
});
} catch (e) {
console.error('Failed to delegate', e)
console.error("Failed to delegate", e);
onOk({
status: 'error',
status: "error",
message: (e as Error).message,
})
});
}
}
}
};
const handleAmountChanged = (newAmount: DecCoin) => {
setAmount(newAmount)
}
setAmount(newAmount);
};
React.useEffect(() => {
validate()
}, [amount, identityKey, mixId])
validate();
}, [amount, identityKey, mixId]);
return (
<SimpleModal
@@ -170,7 +170,7 @@ export const DelegateModal = ({
fullWidth
autoFocus
label="Amount"
initialValue={amount?.amount || '10'}
initialValue={amount?.amount || "10"}
onChanged={handleAmountChanged}
denom={denom}
validationError={errorAmount}
@@ -187,5 +187,5 @@ export const DelegateModal = ({
<ModalListItem label="Est. fee for this transaction will be calculated in your connected wallet" />
</SimpleModal>
)
}
);
};
@@ -1,26 +1,26 @@
import React from 'react'
import { Typography, SxProps, Stack } from '@mui/material'
import { Link } from '@nymproject/react/link/Link'
import { LoadingModal } from './LoadingModal'
import { ConfirmationModal } from './ConfirmationModal'
import { ErrorModal } from './ErrorModal'
import React from "react";
import { Typography, SxProps, Stack } from "@mui/material";
import { Link } from "@nymproject/react";
import { LoadingModal } from "./LoadingModal";
import { ConfirmationModal } from "./ConfirmationModal";
import { ErrorModal } from "./ErrorModal";
export type DelegationModalProps = {
status: 'loading' | 'success' | 'error' | 'info'
message?: string
status: "loading" | "success" | "error" | "info";
message?: string;
transactions?: {
url: string
hash: string
}[]
}
url: string;
hash: string;
}[];
};
export const DelegationModal: FCWithChildren<
DelegationModalProps & {
open: boolean
onClose: () => void
sx?: SxProps
backdropProps?: object
children?: React.ReactNode
open: boolean;
onClose: () => void;
sx?: SxProps;
backdropProps?: object;
children?: React.ReactNode;
}
> = ({
status,
@@ -32,18 +32,18 @@ export const DelegationModal: FCWithChildren<
sx,
backdropProps,
}) => {
if (status === 'loading')
return <LoadingModal sx={sx} backdropProps={backdropProps} />
if (status === "loading")
return <LoadingModal sx={sx} backdropProps={backdropProps} />;
if (status === 'error') {
if (status === "error") {
return (
<ErrorModal message={message} sx={sx} open={open} onClose={onClose}>
{children}
</ErrorModal>
)
);
}
if (status === 'info') {
if (status === "info") {
return (
<ConfirmationModal
open={open}
@@ -53,7 +53,7 @@ export const DelegationModal: FCWithChildren<
>
<Typography>{message}</Typography>
</ConfirmationModal>
)
);
}
return (
@@ -91,5 +91,5 @@ export const DelegationModal: FCWithChildren<
)}
</Stack>
</ConfirmationModal>
)
}
);
};
+39 -39
View File
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Link,
Paper,
@@ -9,49 +9,49 @@ import {
TableHead,
TableRow,
TableCellProps,
} from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Box } from '@mui/system'
import { unymToNym } from '@/app/utils/currency'
import { GatewayEnrichedRowType } from './Gateways/Gateways'
import { MixnodeRowType } from './MixNodes'
import { StakeSaturationProgressBar } from './MixNodes/Economics/StakeSaturationProgressBar'
} from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { CopyToClipboard } from "@nymproject/react";
import { Box } from "@mui/system";
import { unymToNym } from "@/app/utils/currency";
import { GatewayEnrichedRowType } from "./Gateways/Gateways";
import { MixnodeRowType } from "./MixNodes";
import { StakeSaturationProgressBar } from "./MixNodes/Economics/StakeSaturationProgressBar";
export type ColumnsType = {
field: string
title: string
headerAlign?: TableCellProps['align']
width?: string | number
tooltipInfo?: string
}
field: string;
title: string;
headerAlign?: TableCellProps["align"];
width?: string | number;
tooltipInfo?: string;
};
export interface UniversalTableProps<T = any> {
tableName: string
columnsData: ColumnsType[]
rows: T[]
tableName: string;
columnsData: ColumnsType[];
rows: T[];
}
function formatCellValues(val: string | number, field: string) {
if (field === 'identity_key' && typeof val === 'string') {
if (field === "identity_key" && typeof val === "string") {
return (
<Box display="flex" justifyContent="flex-end">
<CopyToClipboard
sx={{ mr: 1, mt: 0.5, fontSize: '18px' }}
sx={{ mr: 1, mt: 0.5, fontSize: "18px" }}
value={val}
tooltip={`Copy identity key ${val} to clipboard`}
/>
<span>{val}</span>
</Box>
)
);
}
if (field === 'bond') {
return unymToNym(val, 6)
if (field === "bond") {
return unymToNym(val, 6);
}
if (field === 'owner') {
if (field === "owner") {
return (
<Link
underline="none"
@@ -61,22 +61,22 @@ function formatCellValues(val: string | number, field: string) {
>
{val}
</Link>
)
);
}
if (field === 'stake_saturation') {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />
if (field === "stake_saturation") {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />;
}
return val
return val;
}
export const DetailTable: FCWithChildren<{
tableName: string
columnsData: ColumnsType[]
rows: MixnodeRowType[] | GatewayEnrichedRowType[]
tableName: string;
columnsData: ColumnsType[];
rows: MixnodeRowType[] | GatewayEnrichedRowType[];
}> = ({ tableName, columnsData, rows }: UniversalTableProps) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 1080 }} aria-label={tableName}>
@@ -87,9 +87,9 @@ export const DetailTable: FCWithChildren<{
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Tooltip
title={tooltipInfo}
id={field}
@@ -115,7 +115,7 @@ export const DetailTable: FCWithChildren<{
{rows.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((data, index) => (
<TableCell
@@ -128,7 +128,7 @@ export const DetailTable: FCWithChildren<{
width: 200,
fontSize: 14,
}}
data-testid={`${data.title.replace(/ /g, '-')}-value`}
data-testid={`${data.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(
eachRow[columnsData[index].field],
@@ -141,5 +141,5 @@ export const DetailTable: FCWithChildren<{
</TableBody>
</Table>
</TableContainer>
)
}
);
};
+24 -23
View File
@@ -1,23 +1,24 @@
import React from 'react'
import Box from '@mui/material/Box'
import MuiLink from '@mui/material/Link'
import Typography from '@mui/material/Typography'
import { useIsMobile } from '../hooks/useIsMobile'
import { NymVpnIcon } from '../icons/NymVpn'
import { Socials } from './Socials'
import Link from 'next/link'
import React from "react";
import Box from "@mui/material/Box";
import MuiLink from "@mui/material/Link";
import Typography from "@mui/material/Typography";
import { useIsMobile } from "../hooks/useIsMobile";
import { NymVpnIcon } from "../icons/NymVpn";
import { Socials } from "./Socials";
import Link from "next/link";
export const Footer: FCWithChildren = () => {
const isMobile = useIsMobile()
const isMobile = useIsMobile();
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: '100%',
height: 'auto',
display: "flex",
flexDirection: "column",
justifyContent: "center",
width: "100%",
height: "auto",
bgcolor: "background.default",
mt: 3,
pt: 3,
pb: 3,
@@ -25,11 +26,11 @@ export const Footer: FCWithChildren = () => {
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
width: 'auto',
justifyContent: 'center',
alignItems: 'center',
display: "flex",
flexDirection: "row",
width: "auto",
justifyContent: "center",
alignItems: "center",
mb: 2,
}}
>
@@ -45,12 +46,12 @@ export const Footer: FCWithChildren = () => {
<Typography
sx={{
fontSize: 12,
textAlign: isMobile ? 'center' : 'end',
color: 'nym.muted.onDarkBg',
textAlign: isMobile ? "center" : "end",
color: "nym.muted.onDarkBg",
}}
>
© {new Date().getFullYear()} Nym Technologies SA, all rights reserved
</Typography>
</Box>
)
}
);
};
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Paper,
Table,
@@ -8,26 +8,26 @@ import {
TableHead,
TableRow,
Typography,
} from '@mui/material'
import { Box } from '@mui/system'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from './types'
import { UniversalTableProps } from '@/app/components/DetailTable'
import { textColour } from '@/app/utils'
} from "@mui/material";
import { Box } from "@mui/system";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from "./types";
import { UniversalTableProps } from "@/app/components/DetailTable";
import { textColour } from "@/app/utils";
const formatCellValues = (value: EconomicsRowsType, field: string) => (
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
<Box sx={{ display: "flex", alignItems: "center" }} id="field">
<Typography sx={{ mr: 1, fontWeight: "600", fontSize: "12px" }} id={field}>
{value.value}
</Typography>
</Box>
)
);
export const DelegatorsInfoTable: FCWithChildren<
UniversalTableProps<EconomicsInfoRowWithIndex>
> = ({ tableName, columnsData, rows }) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
@@ -39,7 +39,7 @@ export const DelegatorsInfoTable: FCWithChildren<
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Tooltip
title={tooltipInfo}
@@ -65,27 +65,27 @@ export const DelegatorsInfoTable: FCWithChildren<
{rows?.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((_, index: number) => {
const { field } = columnsData[index]
const value: EconomicsRowsType = (eachRow as any)[field]
const { field } = columnsData[index];
const value: EconomicsRowsType = (eachRow as any)[field];
return (
<TableCell
key={_.title}
sx={{
color: textColour(value, field, theme),
}}
data-testid={`${_.title.replace(/ /g, '-')}-value`}
data-testid={`${_.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(value, columnsData[index].field)}
</TableCell>
)
);
})}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
);
};
+122 -121
View File
@@ -1,93 +1,94 @@
'use client'
"use client";
import * as React from 'react'
import { ExpandLess, ExpandMore, Menu } from '@mui/icons-material'
import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles'
import { Link as MuiLink } from '@mui/material'
import Button from '@mui/material/Button'
import Box from '@mui/material/Box'
import ListItem from '@mui/material/ListItem'
import MuiDrawer from '@mui/material/Drawer'
import AppBar from '@mui/material/AppBar'
import Toolbar from '@mui/material/Toolbar'
import Typography from '@mui/material/Typography'
import List from '@mui/material/List'
import IconButton from '@mui/material/IconButton'
import ListItemButton from '@mui/material/ListItemButton'
import ListItemIcon from '@mui/material/ListItemIcon'
import ListItemText from '@mui/material/ListItemText'
import { NYM_WEBSITE } from '@/app/api/constants'
import { useMainContext } from '@/app/context/main'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { NavOptionType, originalNavOptions } from '@/app/context/nav'
import { DarkLightSwitchDesktop } from '@/app/components/Switch'
import { Footer } from '@/app/components/Footer'
import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet'
import { usePathname, useRouter } from 'next/navigation'
import * as React from "react";
import { Menu } from "@mui/icons-material";
import { CSSObject, styled, Theme, useTheme } from "@mui/material/styles";
import { Link as MuiLink } from "@mui/material";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import ListItem from "@mui/material/ListItem";
import MuiDrawer from "@mui/material/Drawer";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import List from "@mui/material/List";
import IconButton from "@mui/material/IconButton";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import { NYM_WEBSITE } from "@/app/api/constants";
import { useMainContext } from "@/app/context/main";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { NavOptionType, originalNavOptions } from "@/app/context/nav";
import { DarkLightSwitchDesktop } from "@/app/components/Switch";
import { Footer } from "@/app/components/Footer";
import { ConnectKeplrWallet } from "@/app/components/Wallet/ConnectKeplrWallet";
import { usePathname, useRouter } from "next/navigation";
import { NymLogo } from "@nymproject/react";
const drawerWidth = 255
const bannerHeight = 80
const drawerWidth = 255;
const bannerHeight = 80;
const openedMixin = (theme: Theme): CSSObject => ({
width: drawerWidth,
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: 'hidden',
})
overflowX: "hidden",
});
const closedMixin = (theme: Theme): CSSObject => ({
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
overflowX: 'hidden',
overflowX: "hidden",
width: `calc(${theme.spacing(7)} + 1px)`,
})
});
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: theme.spacing(0, 1),
height: 64,
}))
}));
const Drawer = styled(MuiDrawer, {
shouldForwardProp: (prop) => prop !== 'open',
shouldForwardProp: (prop) => prop !== "open",
})(({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
whiteSpace: "nowrap",
boxSizing: "border-box",
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
"& .MuiDrawer-paper": openedMixin(theme),
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
"& .MuiDrawer-paper": closedMixin(theme),
}),
}))
}));
type ExpandableButtonType = {
title: string
url: string
isActive?: boolean
Icon?: React.ReactNode
nested?: NavOptionType[]
isChild?: boolean
isMobile: boolean
drawIsTempOpen: boolean
drawIsFixed: boolean
isExternalLink?: boolean
openDrawer: () => void
closeDrawer?: () => void
fixDrawerClose?: () => void
}
title: string;
url: string;
isActive?: boolean;
Icon?: React.ReactNode;
nested?: NavOptionType[];
isChild?: boolean;
isMobile: boolean;
drawIsTempOpen: boolean;
drawIsFixed: boolean;
isExternalLink?: boolean;
openDrawer: () => void;
closeDrawer?: () => void;
fixDrawerClose?: () => void;
};
export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
export const ExpandableButton = ({
title,
url,
drawIsTempOpen,
@@ -100,34 +101,34 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
openDrawer,
closeDrawer,
fixDrawerClose,
}) => {
const { palette } = useTheme()
const pathname = usePathname()
const router = useRouter()
}: ExpandableButtonType) => {
const { palette } = useTheme();
const pathname = usePathname();
const router = useRouter();
const handleClick = () => {
if (title === 'Network Components') {
return undefined
if (title === "Network Components") {
return undefined;
}
if (isExternalLink) {
window.open(url, '_blank')
window.open(url, "_blank");
return undefined
return undefined;
}
if (!isExternalLink) {
router.push(url, {})
router.push(url, {});
}
if (closeDrawer) {
closeDrawer()
closeDrawer();
}
}
};
const selectedStyle = {
background: palette.nym.networkExplorer.nav.selected.main,
borderRight: `3px solid ${palette.nym.highlight}`,
}
};
return (
<>
@@ -135,12 +136,12 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
disablePadding
disableGutters
sx={{
borderBottom: isChild ? 'none' : '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: isChild ? "none" : "1px solid rgba(255, 255, 255, 0.1)",
...(pathname === url
? selectedStyle
: {
background: palette.nym.networkExplorer.nav.background,
borderRight: 'none',
borderRight: "none",
}),
}}
>
@@ -151,10 +152,10 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
pb: 2,
background: isChild
? palette.nym.networkExplorer.nav.selected.nested
: 'none',
: "none",
}}
>
<ListItemIcon sx={{ minWidth: '39px' }}>{Icon}</ListItemIcon>
<ListItemIcon sx={{ minWidth: "39px" }}>{Icon}</ListItemIcon>
<ListItemText
primary={title}
sx={{
@@ -179,52 +180,52 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
/>
))}
</>
)
}
);
};
export const Nav: FCWithChildren = ({ children }) => {
const { environment } = useMainContext()
const [drawerIsOpen, setDrawerToOpen] = React.useState(false)
const [fixedOpen, setFixedOpen] = React.useState(false)
export const Nav = ({ children }: { children: React.ReactNode }) => {
const { environment } = useMainContext();
const [drawerIsOpen, setDrawerToOpen] = React.useState(false);
const [fixedOpen, setFixedOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const theme = useTheme()
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const theme = useTheme();
const explorerName = environment
? `${environment} Explorer`
: 'Mainnet Explorer'
: "Mainnet Explorer";
const switchNetworkText =
environment === 'mainnet' ? 'Switch to Testnet' : 'Switch to Mainnet'
environment === "mainnet" ? "Switch to Testnet" : "Switch to Mainnet";
const switchNetworkLink =
environment === 'mainnet'
? 'https://sandbox-explorer.nymtech.net'
: 'https://explorer.nymtech.net'
environment === "mainnet"
? "https://sandbox-explorer.nymtech.net"
: "https://explorer.nymtech.net";
const fixDrawerOpen = () => {
setFixedOpen(true)
setDrawerToOpen(true)
}
setFixedOpen(true);
setDrawerToOpen(true);
};
const fixDrawerClose = () => {
setFixedOpen(false)
setDrawerToOpen(false)
}
setFixedOpen(false);
setDrawerToOpen(false);
};
const tempDrawerOpen = () => {
if (!fixedOpen) {
setDrawerToOpen(true)
setDrawerToOpen(true);
}
}
};
const tempDrawerClose = () => {
if (!fixedOpen) {
setDrawerToOpen(false)
setDrawerToOpen(false);
}
}
};
return (
<Box sx={{ display: 'flex' }}>
<Box sx={{ display: "flex" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
@@ -234,28 +235,28 @@ export const Nav: FCWithChildren = ({ children }) => {
<Toolbar
disableGutters
sx={{
display: 'flex',
justifyContent: 'space-between',
display: "flex",
justifyContent: "space-between",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
ml: 0.5,
}}
>
<IconButton component="a" href={NYM_WEBSITE} target="_blank">
{/* <NymLogo /> */}
<NymLogo width={25} />
</IconButton>
<Typography
variant="h6"
noWrap
sx={{
color: theme.palette.nym.networkExplorer.nav.text,
fontSize: '18px',
fontSize: "18px",
fontWeight: 600,
}}
>
@@ -274,7 +275,7 @@ export const Nav: FCWithChildren = ({ children }) => {
href={switchNetworkLink}
sx={{
borderRadius: 2,
textTransform: 'none',
textTransform: "none",
width: 150,
ml: 4,
fontSize: 14,
@@ -288,19 +289,19 @@ export const Nav: FCWithChildren = ({ children }) => {
<Box
sx={{
mr: 2,
alignItems: 'center',
display: 'flex',
alignItems: "center",
display: "flex",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
width: 'auto',
display: "flex",
flexDirection: "row",
width: "auto",
pr: 0,
pl: 2,
justifyContent: 'flex-end',
alignItems: 'center',
justifyContent: "flex-end",
alignItems: "center",
}}
>
<Box sx={{ mr: 1 }}>
@@ -324,10 +325,10 @@ export const Nav: FCWithChildren = ({ children }) => {
>
<DrawerHeader
sx={{
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
justifyContent: 'flex-start',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
justifyContent: "flex-start",
paddingLeft: 0,
display: 'none',
display: "none",
}}
>
<IconButton
@@ -363,11 +364,11 @@ export const Nav: FCWithChildren = ({ children }) => {
</Drawer>
<Box
style={{ width: `calc(100% - ${drawerWidth}px` }}
sx={{ py: 5, px: 6, mt: 7 }}
sx={{ py: 5, px: 6, mt: 7, bgcolor: "background.default" }}
>
{children}
<Footer />
</Box>
</Box>
)
}
);
};
@@ -1,7 +1,7 @@
'use client'
"use client";
import * as React from 'react'
import { useTheme } from '@mui/material/styles'
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import {
AppBar,
Box,
@@ -12,61 +12,58 @@ import {
ListItemButton,
ListItemIcon,
Toolbar,
} from '@mui/material'
import { Menu } from '@mui/icons-material'
import { MaintenanceBanner } from '@nymproject/react/banners/MaintenanceBanner'
import { useIsMobile } from '@/app/hooks/useIsMobile'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { Footer } from '../Footer'
import { ExpandableButton } from './DesktopNav'
import { ConnectKeplrWallet } from '../Wallet/ConnectKeplrWallet'
import { NetworkTitle } from '../NetworkTitle'
import { originalNavOptions } from '@/app/context/nav'
} from "@mui/material";
import { Menu } from "@mui/icons-material";
import { useIsMobile } from "@/app/hooks/useIsMobile";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { Footer } from "../Footer";
import { ExpandableButton } from "./DesktopNav";
import { ConnectKeplrWallet } from "../Wallet/ConnectKeplrWallet";
import { NetworkTitle } from "../NetworkTitle";
import { originalNavOptions } from "@/app/context/nav";
export const MobileNav: FCWithChildren = ({ children }) => {
const theme = useTheme()
const [drawerOpen, setDrawerOpen] = React.useState(false)
export const MobileNav: FCWithChildren<{ children: React.ReactNode }> = ({
children,
}) => {
const theme = useTheme();
const [drawerOpen, setDrawerOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const isSmallMobile = useIsMobile(400)
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const isSmallMobile = useIsMobile(400);
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen)
}
setDrawerOpen(!drawerOpen);
};
const openDrawer = () => {
setDrawerOpen(true)
}
setDrawerOpen(true);
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
borderRadius: 0,
}}
>
<MaintenanceBanner
open={openMaintenance}
onClick={() => setOpenMaintenance(false)}
/>
<Toolbar
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<IconButton onClick={toggleDrawer}>
<Menu sx={{ color: 'primary.contrastText' }} />
<Menu sx={{ color: "primary.contrastText" }} />
</IconButton>
{!isSmallMobile && <NetworkTitle />}
</Box>
@@ -91,7 +88,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
sx={{
height: 64,
background: theme.palette.nym.networkExplorer.nav.background,
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
}}
>
<ListItemButton
@@ -100,8 +97,8 @@ export const MobileNav: FCWithChildren = ({ children }) => {
pt: 2,
pb: 2,
background: theme.palette.nym.networkExplorer.nav.background,
display: 'flex',
justifyContent: 'flex-start',
display: "flex",
justifyContent: "flex-start",
}}
>
<ListItemIcon>
@@ -126,11 +123,10 @@ export const MobileNav: FCWithChildren = ({ children }) => {
</List>
</Box>
</Drawer>
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
<Box sx={{ width: "100%", p: 4, mt: 7, bgcolor: "background.default" }}>
{children}
<Footer />
</Box>
</Box>
)
}
);
};
+3 -2
View File
@@ -1,11 +1,12 @@
import * as React from 'react';
import { Typography } from '@mui/material';
import * as React from "react";
import { Typography } from "@mui/material";
export const Title: FCWithChildren<{ text: string }> = ({ text }) => (
<Typography
variant="h5"
sx={{
fontWeight: 600,
color: "text.primary",
}}
data-testid={text}
>
+35 -31
View File
@@ -1,38 +1,42 @@
'use client'
"use client";
import * as React from 'react'
import { scaleLinear } from 'd3-scale'
import * as React from "react";
import { scaleLinear } from "d3-scale";
import {
ComposableMap,
Geographies,
Geography,
Marker,
ZoomableGroup,
} from 'react-simple-maps'
import ReactTooltip from 'react-tooltip'
import { CircularProgress } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { ApiState, CountryDataResponse } from '../typeDefs/explorer-api'
import MAP_TOPOJSON from '../assets/world-110m.json'
} from "react-simple-maps";
import { Tooltip as ReactTooltip } from "react-tooltip";
import { CircularProgress } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { ApiState, CountryDataResponse } from "../typeDefs/explorer-api";
import MAP_TOPOJSON from "../assets/world-110m.json";
import "react-tooltip/dist/react-tooltip.css";
type MapProps = {
userLocation?: [number, number]
countryData?: ApiState<CountryDataResponse>
loading: boolean
}
userLocation?: [number, number];
countryData?: ApiState<CountryDataResponse>;
loading: boolean;
};
export const WorldMap: FCWithChildren<MapProps> = ({
countryData,
userLocation,
loading,
}) => {
const { palette } = useTheme()
const { palette } = useTheme();
// tooltip needs fixing
const colorScale = React.useMemo(() => {
if (countryData?.data) {
const heighestNumberOfNodes = Math.max(
...Object.values(countryData.data).map((country) => country.nodes)
)
);
return scaleLinear<string, string>()
.domain([
0,
@@ -42,17 +46,17 @@ export const WorldMap: FCWithChildren<MapProps> = ({
heighestNumberOfNodes,
])
.range(palette.nym.networkExplorer.map.fills)
.unknown(palette.nym.networkExplorer.map.fills[0])
.unknown(palette.nym.networkExplorer.map.fills[0]);
}
return () => palette.nym.networkExplorer.map.fills[0]
}, [countryData, palette])
return () => palette.nym.networkExplorer.map.fills[0];
}, [countryData, palette]);
const [tooltipContent, setTooltipContent] = React.useState<string | null>(
null
)
);
if (loading) {
return <CircularProgress />
return <CircularProgress />;
}
return (
@@ -61,8 +65,8 @@ export const WorldMap: FCWithChildren<MapProps> = ({
data-tip=""
style={{
backgroundColor: palette.nym.networkExplorer.background.tertiary,
width: '100%',
height: 'auto',
width: "100%",
height: "auto",
}}
viewBox="0, 50, 800, 350"
projection="geoMercator"
@@ -75,7 +79,7 @@ export const WorldMap: FCWithChildren<MapProps> = ({
<Geographies geography={MAP_TOPOJSON}>
{({ geographies }) =>
geographies.map((geo) => {
const d = (countryData?.data || {})[geo.properties.ISO_A3]
const d = (countryData?.data || {})[geo.properties.ISO_A3];
return (
<Geography
key={geo.rsmKey}
@@ -84,25 +88,25 @@ export const WorldMap: FCWithChildren<MapProps> = ({
stroke={palette.nym.networkExplorer.map.stroke}
strokeWidth={0.2}
onMouseEnter={() => {
const { NAME_LONG } = geo.properties
const { NAME_LONG } = geo.properties;
if (!userLocation) {
setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`)
setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`);
}
}}
onMouseLeave={() => {
setTooltipContent('')
setTooltipContent("");
}}
style={{
hover:
!userLocation && countryData
? {
fill: palette.nym.highlight,
outline: 'white',
outline: "white",
}
: undefined,
}}
/>
)
);
})
}
</Geographies>
@@ -123,7 +127,7 @@ export const WorldMap: FCWithChildren<MapProps> = ({
)}
</ZoomableGroup>
</ComposableMap>
<ReactTooltip>{tooltipContent}</ReactTooltip>
<ReactTooltip id="tooltip">{tooltipContent}</ReactTooltip>
</>
)
}
);
};
+95 -93
View File
@@ -1,7 +1,7 @@
'use client'
"use client";
import * as React from 'react'
import { PaletteMode } from '@mui/material'
import * as React from "react";
import { PaletteMode } from "@mui/material";
import {
ApiState,
BlockResponse,
@@ -13,110 +13,112 @@ import {
ValidatorsResponse,
Environment,
DirectoryServiceProvider,
} from '@/app/typeDefs/explorer-api'
import { EnumFilterKey } from '@/app/typeDefs/filters'
import { Api, getEnvironment } from '@/app/api'
import { toPercentIntegerString } from '@/app/utils'
import { NavOptionType, originalNavOptions } from './nav'
} from "@/app/typeDefs/explorer-api";
import { EnumFilterKey } from "@/app/typeDefs/filters";
import { Api, getEnvironment } from "@/app/api";
import { toPercentIntegerString } from "@/app/utils";
import { NavOptionType, originalNavOptions } from "./nav";
interface StateData {
summaryOverview?: ApiState<SummaryOverviewResponse>
block?: ApiState<BlockResponse>
countryData?: ApiState<CountryDataResponse>
gateways?: ApiState<GatewayResponse>
globalError?: string | undefined
mixnodes?: ApiState<MixNodeResponse>
mode: PaletteMode
validators?: ApiState<ValidatorsResponse>
environment?: Environment
serviceProviders?: ApiState<DirectoryServiceProvider[]>
summaryOverview?: ApiState<SummaryOverviewResponse>;
block?: ApiState<BlockResponse>;
countryData?: ApiState<CountryDataResponse>;
gateways?: ApiState<GatewayResponse>;
globalError?: string | undefined;
mixnodes?: ApiState<MixNodeResponse>;
mode: PaletteMode;
validators?: ApiState<ValidatorsResponse>;
environment?: Environment;
serviceProviders?: ApiState<DirectoryServiceProvider[]>;
}
interface StateApi {
fetchMixnodes: (
status?: MixnodeStatus
) => Promise<MixNodeResponse | undefined>
filterMixnodes: (filters: any, status: any) => void
toggleMode: () => void
) => Promise<MixNodeResponse | undefined>;
filterMixnodes: (filters: any, status: any) => void;
toggleMode: () => void;
}
type State = StateData & StateApi
type State = StateData & StateApi;
export const MainContext = React.createContext<State>({
mode: 'dark',
mode: "dark",
toggleMode: () => undefined,
filterMixnodes: () => null,
fetchMixnodes: () => Promise.resolve(undefined),
})
});
export const useMainContext = (): React.ContextType<typeof MainContext> =>
React.useContext<State>(MainContext)
React.useContext<State>(MainContext);
export const MainContextProvider: FCWithChildren = ({ children }) => {
export const MainContextProvider: FCWithChildren<{
children: React.ReactNode;
}> = ({ children }) => {
// network explorer environment
const [environment, setEnvironment] = React.useState<Environment>()
const [environment, setEnvironment] = React.useState<Environment>();
// light/dark mode
const [mode, setMode] = React.useState<PaletteMode>('dark')
const [mode, setMode] = React.useState<PaletteMode>("dark");
// global / banner error messaging
const [globalError] = React.useState<string>()
const [globalError] = React.useState<string>();
// various APIs for Overview page
const [summaryOverview, setSummaryOverview] =
React.useState<ApiState<SummaryOverviewResponse>>()
const [mixnodes, setMixnodes] = React.useState<ApiState<MixNodeResponse>>()
const [gateways, setGateways] = React.useState<ApiState<GatewayResponse>>()
React.useState<ApiState<SummaryOverviewResponse>>();
const [mixnodes, setMixnodes] = React.useState<ApiState<MixNodeResponse>>();
const [gateways, setGateways] = React.useState<ApiState<GatewayResponse>>();
const [validators, setValidators] =
React.useState<ApiState<ValidatorsResponse>>()
const [block, setBlock] = React.useState<ApiState<BlockResponse>>()
React.useState<ApiState<ValidatorsResponse>>();
const [block, setBlock] = React.useState<ApiState<BlockResponse>>();
const [countryData, setCountryData] =
React.useState<ApiState<CountryDataResponse>>()
React.useState<ApiState<CountryDataResponse>>();
const [serviceProviders, setServiceProviders] =
React.useState<ApiState<DirectoryServiceProvider[]>>()
React.useState<ApiState<DirectoryServiceProvider[]>>();
const toggleMode = () => setMode((m) => (m !== 'light' ? 'light' : 'dark'))
const toggleMode = () => setMode((m) => (m !== "light" ? "light" : "dark"));
const fetchOverviewSummary = async () => {
try {
const data = await Api.fetchOverviewSummary()
setSummaryOverview({ data, isLoading: false })
const data = await Api.fetchOverviewSummary();
setSummaryOverview({ data, isLoading: false });
} catch (error) {
setSummaryOverview({
error:
error instanceof Error
? error
: new Error('Overview summary api fail'),
: new Error("Overview summary api fail"),
isLoading: false,
})
});
}
}
};
const fetchMixnodes = async (status?: MixnodeStatus) => {
let data
setMixnodes((d) => ({ ...d, isLoading: true }))
let data;
setMixnodes((d) => ({ ...d, isLoading: true }));
try {
data = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
setMixnodes({ data, isLoading: false })
: await Api.fetchMixnodes();
setMixnodes({ data, isLoading: false });
} catch (error) {
setMixnodes({
error: error instanceof Error ? error : new Error('Mixnode api fail'),
error: error instanceof Error ? error : new Error("Mixnode api fail"),
isLoading: false,
})
});
}
return data
}
return data;
};
const filterMixnodes = async (
filters: { [key in EnumFilterKey]: number[] },
status?: MixnodeStatus
) => {
setMixnodes((d) => ({ ...d, isLoading: true }))
setMixnodes((d) => ({ ...d, isLoading: true }));
const mxns = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
: await Api.fetchMixnodes();
const filtered = mxns?.filter(
(m) =>
@@ -126,101 +128,101 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
m.stake_saturation <= filters.stakeSaturation[1] &&
m.avg_uptime >= filters.routingScore[0] &&
m.avg_uptime <= filters.routingScore[1]
)
);
setMixnodes({ data: filtered, isLoading: false })
}
setMixnodes({ data: filtered, isLoading: false });
};
const fetchGateways = async () => {
setGateways((d) => ({ ...d, isLoading: true }))
setGateways((d) => ({ ...d, isLoading: true }));
try {
const data = await Api.fetchGateways()
setGateways({ data, isLoading: false })
const data = await Api.fetchGateways();
setGateways({ data, isLoading: false });
} catch (error) {
setGateways({
error: error instanceof Error ? error : new Error('Gateways api fail'),
error: error instanceof Error ? error : new Error("Gateways api fail"),
isLoading: false,
})
});
}
}
};
const fetchValidators = async () => {
try {
const data = await Api.fetchValidators()
setValidators({ data, isLoading: false })
const data = await Api.fetchValidators();
setValidators({ data, isLoading: false });
} catch (error) {
setValidators({
error:
error instanceof Error ? error : new Error('Validators api fail'),
error instanceof Error ? error : new Error("Validators api fail"),
isLoading: false,
})
});
}
}
};
const fetchBlock = async () => {
try {
const data = await Api.fetchBlock()
setBlock({ data, isLoading: false })
const data = await Api.fetchBlock();
setBlock({ data, isLoading: false });
} catch (error) {
setBlock({
error: error instanceof Error ? error : new Error('Block api fail'),
error: error instanceof Error ? error : new Error("Block api fail"),
isLoading: false,
})
});
}
}
};
const fetchCountryData = async () => {
setCountryData({ data: undefined, isLoading: true })
setCountryData({ data: undefined, isLoading: true });
try {
const res = await Api.fetchCountryData()
setCountryData({ data: res, isLoading: false })
const res = await Api.fetchCountryData();
setCountryData({ data: res, isLoading: false });
} catch (error) {
setCountryData({
error:
error instanceof Error ? error : new Error('Country Data api fail'),
error instanceof Error ? error : new Error("Country Data api fail"),
isLoading: false,
})
});
}
}
};
const fetchServiceProviders = async () => {
setServiceProviders({ data: undefined, isLoading: true })
setServiceProviders({ data: undefined, isLoading: true });
try {
const res = await Api.fetchServiceProviders()
const res = await Api.fetchServiceProviders();
const resWithRoutingScorePercentage = res.map((item) => ({
...item,
routing_score: item.routing_score
? `${toPercentIntegerString(item.routing_score.toString())}%`
: item.routing_score,
}))
}));
setServiceProviders({
data: resWithRoutingScorePercentage,
isLoading: false,
})
});
} catch (error) {
setServiceProviders({
error:
error instanceof Error
? error
: new Error('Service provider api fail'),
: new Error("Service provider api fail"),
isLoading: false,
})
});
}
}
};
React.useEffect(() => {
if (environment === 'mainnet') {
fetchServiceProviders()
if (environment === "mainnet") {
fetchServiceProviders();
}
}, [environment])
}, [environment]);
React.useEffect(() => {
setEnvironment(getEnvironment())
setEnvironment(getEnvironment());
Promise.all([
fetchOverviewSummary(),
fetchGateways(),
fetchValidators(),
fetchBlock(),
fetchCountryData(),
])
}, [])
]);
}, []);
const state = React.useMemo<State>(
() => ({
@@ -250,7 +252,7 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
validators,
serviceProviders,
]
)
);
return <MainContext.Provider value={state}>{children}</MainContext.Provider>
}
return <MainContext.Provider value={state}>{children}</MainContext.Provider>;
};
@@ -1,8 +1,8 @@
import * as React from 'react'
import { FallbackProps } from 'react-error-boundary'
import { Alert, AlertTitle, Container } from '@mui/material'
import { NymThemeProvider } from '@nymproject/mui-theme'
import { NymLogo } from '@nymproject/react/logo/NymLogo'
import * as React from "react";
import { FallbackProps } from "react-error-boundary";
import { Alert, AlertTitle, Container } from "@mui/material";
import { NymThemeProvider } from "@nymproject/mui-theme";
import { NymLogo } from "@nymproject/react";
export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
error,
@@ -15,7 +15,7 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
<AlertTitle>{error.name}</AlertTitle>
{error.message}
</Alert>
{process.env.NODE_ENV === 'development' && (
{process.env.NODE_ENV === "development" && (
<Alert severity="info" sx={{ mt: 2 }} data-testid="stack-trace">
<AlertTitle>Stack trace</AlertTitle>
{error.stack}
@@ -23,4 +23,4 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
)}
</Container>
</NymThemeProvider>
)
);
+9 -9
View File
@@ -1,21 +1,21 @@
import type { Metadata } from 'next'
import '@interchain-ui/react/styles'
import { App } from './App'
import type { Metadata } from "next";
import "@interchain-ui/react/styles";
import { App } from "./App";
export const metadata: Metadata = {
title: 'Nym Network Explorer',
}
title: "Nym Network Explorer",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<App>{children}</App>
</body>
</html>
)
);
}
@@ -1,106 +1,107 @@
'use client'
"use client";
import React, { useMemo } from 'react'
import { Box, Card, Grid, Stack } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import React, { useMemo } from "react";
import { Box, Card, Grid, Stack } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import {
MRT_ColumnDef,
MaterialReactTable,
useMaterialReactTable,
} from 'material-react-table'
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Tooltip as InfoTooltip } from '@nymproject/react/tooltip/Tooltip'
import { diff, gte, rcompare } from 'semver'
import { useMainContext } from '@/app/context/main'
import { TableToolbar } from '@/app/components/TableToolbar'
import { CustomColumnHeading } from '@/app/components/CustomColumnHeading'
import { Title } from '@/app/components/Title'
import { unymToNym } from '@/app/utils/currency'
import { Tooltip } from '@/app/components/Tooltip'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
import { splice } from '@/app/utils'
} from "material-react-table";
import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
import { CopyToClipboard } from "@nymproject/react";
import { Tooltip as InfoTooltip } from "@nymproject/react";
import { diff, gte, rcompare } from "semver";
import { useMainContext } from "@/app/context/main";
import { TableToolbar } from "@/app/components/TableToolbar";
import { CustomColumnHeading } from "@/app/components/CustomColumnHeading";
import { Title } from "@/app/components/Title";
import { unymToNym } from "@/app/utils/currency";
import { Tooltip } from "@/app/components/Tooltip";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import { splice } from "@/app/utils";
import {
VersionDisplaySelector,
VersionSelectOptions,
} from '@/app/components/Gateways/VersionDisplaySelector'
import StyledLink from '@/app/components/StyledLink'
} from "@/app/components/Gateways/VersionDisplaySelector";
import StyledLink from "@/app/components/StyledLink";
import {
GatewayRowType,
gatewayToGridRow,
} from '@/app/components/Gateways/Gateways'
} from "@/app/components/Gateways/Gateways";
import { InfoOutlined } from "@mui/icons-material";
const PageGateways = () => {
const { gateways } = useMainContext()
const { gateways } = useMainContext();
const [versionFilter, setVersionFilter] =
React.useState<VersionSelectOptions>(VersionSelectOptions.all)
React.useState<VersionSelectOptions>(VersionSelectOptions.all);
const theme = useTheme()
const theme = useTheme();
const highestVersion = React.useMemo(() => {
if (gateways?.data) {
const versions = gateways.data.reduce(
(a: string[], b) => [...a, b.gateway.version],
[]
)
const [lastestVersion] = versions.sort(rcompare)
return lastestVersion
);
const [lastestVersion] = versions.sort(rcompare);
return lastestVersion;
}
// fallback value
return '2.0.0'
}, [gateways])
return "2.0.0";
}, [gateways]);
const filterByLatestVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'patch' || versionDiff === null
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "patch" || versionDiff === null;
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filterByOlderVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'major' || versionDiff === 'minor'
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "major" || versionDiff === "minor";
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filteredByVersion = React.useMemo(() => {
switch (versionFilter) {
case VersionSelectOptions.latestVersion:
return filterByLatestVersions
return filterByLatestVersions;
case VersionSelectOptions.olderVersions:
return filterByOlderVersions
return filterByOlderVersions;
case VersionSelectOptions.all:
return gateways?.data || []
return gateways?.data || [];
default:
return []
return [];
}
}, [versionFilter, gateways])
}, [versionFilter, gateways]);
const data = useMemo(() => {
return gatewayToGridRow(filteredByVersion || [])
}, [filteredByVersion])
return gatewayToGridRow(filteredByVersion || []);
}, [filteredByVersion]);
const columns = useMemo<MRT_ColumnDef<GatewayRowType>[]>(() => {
return [
{
id: 'gateway-data',
header: 'Gatewsay Data',
id: "gateway-data",
header: "Gatewsay Data",
columns: [
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -113,13 +114,13 @@ const PageGateways = () => {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'node_performance',
header: 'Node Performance',
accessorKey: 'node_performance',
id: "node_performance",
header: "Node Performance",
accessorKey: "node_performance",
size: 200,
Header: () => {
return (
@@ -134,10 +135,13 @@ const PageGateways = () => {
}
maxWidth={230}
arrow
TooltipIcon={
<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />
}
/>
<CustomColumnHeading headingTitle="Routing Score" />
</Box>
)
);
},
Cell: ({ row }) => {
return (
@@ -148,13 +152,13 @@ const PageGateways = () => {
>
{`${row.original.node_performance}%`}
</StyledLink>
)
);
},
},
{
id: 'version',
header: 'Version',
accessorKey: 'version',
id: "version",
header: "Version",
accessorKey: "version",
size: 150,
Cell: ({ row }) => {
return (
@@ -165,18 +169,18 @@ const PageGateways = () => {
>
{row.original.version}
</StyledLink>
)
);
},
},
{
id: 'location',
header: 'Location',
accessorKey: 'location',
id: "location",
header: "Location",
accessorKey: "location",
size: 150,
Cell: ({ row }) => {
return (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip
@@ -185,22 +189,22 @@ const PageGateways = () => {
>
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{row.original.location}
</Box>
</Tooltip>
</Box>
)
);
},
},
{
id: 'host',
header: 'IP:Port',
accessorKey: 'host',
id: "host",
header: "IP:Port",
accessorKey: "host",
size: 150,
Cell: ({ row }) => {
return (
@@ -211,13 +215,13 @@ const PageGateways = () => {
>
{row.original.host}
</StyledLink>
)
);
},
},
{
id: 'owner',
header: 'Owner',
accessorKey: 'owner',
id: "owner",
header: "Owner",
accessorKey: "owner",
size: 150,
Cell: ({ row }) => {
return (
@@ -229,18 +233,18 @@ const PageGateways = () => {
>
{splice(7, 29, row.original.owner)}
</StyledLink>
)
);
},
},
],
},
]
}, [])
];
}, []);
const _columns: GridColDef[] = [
{
field: 'node_performance',
align: 'center',
field: "node_performance",
align: "center",
renderHeader: () => (
<>
<InfoTooltip
@@ -251,14 +255,15 @@ const PageGateways = () => {
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
maxWidth={230}
arrow
TooltipIcon={<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />}
/>
<CustomColumnHeading headingTitle="Routing Score" />
</>
),
width: 120,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -269,13 +274,13 @@ const PageGateways = () => {
),
},
{
field: 'version',
align: 'center',
field: "version",
align: "center",
renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
width: 150,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -285,28 +290,28 @@ const PageGateways = () => {
</StyledLink>
),
sortComparator: (a, b) => {
if (gte(a, b)) return 1
return -1
if (gte(a, b)) return 1;
return -1;
},
},
{
field: 'location',
field: "location",
renderHeader: () => <CustomColumnHeading headingTitle="Location" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip text={params.value} id="gateway-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{params.value}
@@ -316,12 +321,12 @@ const PageGateways = () => {
),
},
{
field: 'host',
field: "host",
renderHeader: () => <CustomColumnHeading headingTitle="IP:Port" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -332,13 +337,13 @@ const PageGateways = () => {
),
},
{
field: 'owner',
headerName: 'Owner',
field: "owner",
headerName: "Owner",
renderHeader: () => <CustomColumnHeading headingTitle="Owner" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`${NYM_BIG_DIPPER}/account/${params.value}`}
@@ -350,13 +355,13 @@ const PageGateways = () => {
),
},
{
field: 'bond',
field: "bond",
width: 150,
disableColumnMenu: true,
type: 'number',
type: "number",
renderHeader: () => <CustomColumnHeading headingTitle="Bond" />,
headerClassName: 'MuiDataGrid-header-override',
headerAlign: 'left',
headerClassName: "MuiDataGrid-header-override",
headerAlign: "left",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -366,12 +371,12 @@ const PageGateways = () => {
</StyledLink>
),
},
]
];
const table = useMaterialReactTable({
columns,
data,
})
});
return (
<>
@@ -383,7 +388,7 @@ const PageGateways = () => {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -399,7 +404,7 @@ const PageGateways = () => {
</Grid>
</Grid>
</>
)
}
);
};
export default PageGateways
export default PageGateways;
@@ -1,13 +1,13 @@
'use client'
"use client";
import React, { useCallback, useMemo } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import React, { useCallback, useMemo } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef,
} from 'material-react-table'
import { Grid, Card, Button, Box, Stack } from '@mui/material'
} from "material-react-table";
import { Grid, Card, Button, Box, Stack } from "@mui/material";
import {
CustomColumnHeading,
DelegateIconButton,
@@ -21,81 +21,81 @@ import {
Title,
Tooltip,
mixnodeToGridRow,
} from '@/app/components'
import { DelegationsProvider } from '@/app/context/delegations'
import { useWalletContext } from '@/app/context/wallet'
import { useGetMixNodeStatusColor, useIsMobile } from '@/app/hooks'
import { useMainContext } from '@/app/context/main'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { splice } from '@/app/utils'
import { currencyToString } from '@/app/utils/currency'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
} from "@/app/components";
import { DelegationsProvider } from "@/app/context/delegations";
import { useWalletContext } from "@/app/context/wallet";
import { useGetMixNodeStatusColor, useIsMobile } from "@/app/hooks";
import { useMainContext } from "@/app/context/main";
import { CopyToClipboard } from "@nymproject/react";
import { splice } from "@/app/utils";
import { currencyToString } from "@/app/utils/currency";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import {
MixnodeStatusWithAll,
toMixnodeStatus,
} from '@/app/typeDefs/explorer-api'
} from "@/app/typeDefs/explorer-api";
export default function MixnodesPage() {
const isMobile = useIsMobile()
const { isWalletConnected } = useWalletContext()
const { mixnodes, fetchMixnodes } = useMainContext()
const router = useRouter()
const isMobile = useIsMobile();
const { isWalletConnected } = useWalletContext();
const { mixnodes, fetchMixnodes } = useMainContext();
const router = useRouter();
const [itemSelectedForDelegation, setItemSelectedForDelegation] =
React.useState<{
mixId: number
identityKey: string
}>()
mixId: number;
identityKey: string;
}>();
const [confirmationModalProps, setConfirmationModalProps] = React.useState<
DelegationModalProps | undefined
>()
>();
const search = useSearchParams()
const status = search.get('status') as MixnodeStatusWithAll
const search = useSearchParams();
const status = search.get("status") as MixnodeStatusWithAll;
React.useEffect(() => {
// when the status changes, get the mixnodes
fetchMixnodes(toMixnodeStatus(status))
}, [status])
fetchMixnodes(toMixnodeStatus(status));
}, [status]);
const handleMixnodeStatusChanged = (newStatus?: MixnodeStatusWithAll) => {
router.push(
newStatus && newStatus !== 'all'
newStatus && newStatus !== "all"
? `/network-components/mixnodes?status=${newStatus}`
: '/network-components/mixnodes'
)
}
: "/network-components/mixnodes"
);
};
const handleOnDelegate = useCallback(
({ identityKey, mixId }: { identityKey: string; mixId: number }) => {
if (!isWalletConnected) {
setConfirmationModalProps({
status: 'info',
message: 'Please connect your wallet to delegate',
})
status: "info",
message: "Please connect your wallet to delegate",
});
} else {
setItemSelectedForDelegation({ identityKey, mixId })
setItemSelectedForDelegation({ identityKey, mixId });
}
},
[isWalletConnected]
)
);
const handleNewDelegation = (delegationModalProps: DelegationModalProps) => {
setItemSelectedForDelegation(undefined)
setConfirmationModalProps(delegationModalProps)
}
setItemSelectedForDelegation(undefined);
setConfirmationModalProps(delegationModalProps);
};
const columns = useMemo<MRT_ColumnDef<MixnodeRowType>[]>(() => {
return [
{
id: 'mixnode-data',
header: 'Mixnode Data',
id: "mixnode-data",
header: "Mixnode Data",
columns: [
{
id: 'delegate',
accessorKey: 'delegate',
id: "delegate",
accessorKey: "delegate",
size: isMobile ? 50 : 150,
header: '',
header: "",
grow: false,
Cell: ({ row }) => (
<DelegateIconButton
@@ -113,15 +113,15 @@ export default function MixnodesPage() {
Filter: () => null,
},
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -134,13 +134,13 @@ export default function MixnodesPage() {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'bond',
header: 'Stake',
accessorKey: 'bond',
id: "bond",
header: "Stake",
accessorKey: "bond",
Cell: ({ row }) => (
<StyledLink
to={`/network-components/mixnodes/${row.original.mix_id}`}
@@ -151,9 +151,9 @@ export default function MixnodesPage() {
),
},
{
id: 'stake_saturation',
header: 'Stake Saturation',
accessorKey: 'stake_saturation',
id: "stake_saturation",
header: "Stake Saturation",
accessorKey: "stake_saturation",
size: 225,
Header() {
return (
@@ -161,7 +161,7 @@ export default function MixnodesPage() {
headingTitle="Stake Saturation"
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 940k NYMs, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set."
/>
)
);
},
Cell: ({ row }) => (
<StyledLink
@@ -171,9 +171,9 @@ export default function MixnodesPage() {
),
},
{
id: 'pledge_amount',
header: 'Bond',
accessorKey: 'pledge_amount',
id: "pledge_amount",
header: "Bond",
accessorKey: "pledge_amount",
size: 185,
Header: () => (
<CustomColumnHeading
@@ -193,9 +193,9 @@ export default function MixnodesPage() {
),
},
{
id: 'profit_percentage',
accessorKey: 'profit_percentage',
header: 'Profit Margin',
id: "profit_percentage",
accessorKey: "profit_percentage",
header: "Profit Margin",
size: 145,
Header: () => (
<CustomColumnHeading
@@ -211,10 +211,10 @@ export default function MixnodesPage() {
),
},
{
id: 'operating_cost',
accessorKey: 'operating_cost',
id: "operating_cost",
accessorKey: "operating_cost",
size: 220,
header: 'Operating Cost',
header: "Operating Cost",
disableColumnMenu: true,
Header: () => (
<CustomColumnHeading
@@ -230,10 +230,10 @@ export default function MixnodesPage() {
),
},
{
id: 'node_performance',
accessorKey: 'node_performance',
id: "node_performance",
accessorKey: "node_performance",
size: 200,
header: 'Routing Score',
header: "Routing Score",
Header: () => (
<CustomColumnHeading
headingTitle="Routing Score"
@@ -248,10 +248,10 @@ export default function MixnodesPage() {
),
},
{
id: 'owner',
accessorKey: 'owner',
id: "owner",
accessorKey: "owner",
size: 150,
header: 'Owner',
header: "Owner",
Header: () => <CustomColumnHeading headingTitle="Owner" />,
Cell: ({ row }) => (
<StyledLink
@@ -265,19 +265,19 @@ export default function MixnodesPage() {
),
},
{
id: 'location',
accessorKey: 'location',
header: 'Location',
id: "location",
accessorKey: "location",
header: "Location",
maxSize: 150,
Header: () => <CustomColumnHeading headingTitle="Location" />,
Cell: ({ row }) => (
<Tooltip text={row.original.location} id="mixnode-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
cursor: 'pointer',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
cursor: "pointer",
color: useGetMixNodeStatusColor(row.original.status),
}}
>
@@ -287,9 +287,9 @@ export default function MixnodesPage() {
),
},
{
id: 'host',
accessorKey: 'host',
header: 'Host',
id: "host",
accessorKey: "host",
header: "Host",
size: 130,
Header: () => <CustomColumnHeading headingTitle="Host" />,
Cell: ({ row }) => (
@@ -303,12 +303,12 @@ export default function MixnodesPage() {
},
],
},
]
}, [handleOnDelegate, isMobile])
];
}, [handleOnDelegate, isMobile]);
const data = useMemo(() => {
return mixnodeToGridRow(mixnodes?.data)
}, [mixnodes?.data])
return mixnodeToGridRow(mixnodes?.data);
}, [mixnodes?.data]);
const table = useMaterialReactTable({
columns,
@@ -317,11 +317,11 @@ export default function MixnodesPage() {
state: {
isLoading: mixnodes?.isLoading,
},
layoutMode: 'grid-no-grow',
layoutMode: "grid-no-grow",
initialState: {
columnPinning: { left: ['delegate'] },
columnPinning: { left: ["delegate"] },
},
})
});
return (
<DelegationsProvider>
@@ -333,7 +333,7 @@ export default function MixnodesPage() {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -350,7 +350,7 @@ export default function MixnodesPage() {
fullWidth
size="large"
variant="outlined"
onClick={() => router.push('/delegations')}
onClick={() => router.push("/delegations")}
>
Delegations
</Button>
@@ -364,7 +364,7 @@ export default function MixnodesPage() {
{itemSelectedForDelegation && (
<DelegateModal
onClose={() => {
setItemSelectedForDelegation(undefined)
setItemSelectedForDelegation(undefined);
}}
header="Delegate"
buttonText="Delegate stake"
@@ -382,19 +382,19 @@ export default function MixnodesPage() {
{...confirmationModalProps}
open={Boolean(confirmationModalProps)}
onClose={async () => {
setConfirmationModalProps(undefined)
if (confirmationModalProps.status === 'success') {
router.push('/delegations')
setConfirmationModalProps(undefined);
if (confirmationModalProps.status === "success") {
router.push("/delegations");
}
}}
sx={{
width: {
xs: '90%',
xs: "90%",
sm: 600,
},
}}
/>
)}
</DelegationsProvider>
)
);
}
@@ -1,6 +1,6 @@
'use client'
"use client";
import React, { useMemo } from 'react'
import React, { useMemo } from "react";
import {
Box,
Button,
@@ -9,29 +9,28 @@ import {
Grid,
ListItem,
Menu,
Typography,
} from '@mui/material'
import { TableToolbar } from '@/app/components/TableToolbar'
import { Title } from '@/app/components/Title'
import { useMainContext } from '@/app/context/main'
import { CustomColumnHeading } from '@/app/components/CustomColumnHeading'
} from "@mui/material";
import { TableToolbar } from "@/app/components/TableToolbar";
import { Title } from "@/app/components/Title";
import { useMainContext } from "@/app/context/main";
import { CustomColumnHeading } from "@/app/components/CustomColumnHeading";
import {
MRT_ColumnDef,
MaterialReactTable,
useMaterialReactTable,
} from 'material-react-table'
import { DirectoryServiceProvider } from '@/app/typeDefs/explorer-api'
} from "material-react-table";
import { DirectoryServiceProvider } from "@/app/typeDefs/explorer-api";
const SupportedApps = () => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
const open = Boolean(anchorEl)
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null)
}
const anchorRef = React.useRef<HTMLButtonElement>(null)
setAnchorEl(null);
};
const anchorRef = React.useRef<HTMLButtonElement>(null);
return (
<FormControl size="small">
@@ -41,7 +40,7 @@ const SupportedApps = () => {
size="large"
variant="outlined"
color="inherit"
sx={{ mr: 2, textTransform: 'capitalize' }}
sx={{ mr: 2, textTransform: "capitalize" }}
>
Supported Apps
</Button>
@@ -52,67 +51,67 @@ const SupportedApps = () => {
<ListItem>Blockstream Green</ListItem>
</Menu>
</FormControl>
)
}
);
};
const ServiceProviders = () => {
const { serviceProviders } = useMainContext()
const { serviceProviders } = useMainContext();
const columns = useMemo<MRT_ColumnDef<DirectoryServiceProvider>[]>(() => {
return [
{
id: 'service-providers-data',
header: 'Service Providers Data',
id: "service-providers-data",
header: "Service Providers Data",
columns: [
{
id: 'address',
accessorKey: 'address',
header: 'Client ID',
id: "address",
accessorKey: "address",
header: "Client ID",
size: 450,
},
{
id: 'service_type-type',
accessorKey: 'service_type',
header: 'Type',
id: "service_type-type",
accessorKey: "service_type",
header: "Type",
size: 100,
},
{
id: 'routing_score-score',
accessorKey: 'routing_score',
header: 'Routing score',
id: "routing_score-score",
accessorKey: "routing_score",
header: "Routing score",
Header() {
return (
<CustomColumnHeading
headingTitle="Routing score"
tooltipInfo="Routing score is only displayed for the service providers that had a successful ping within the last two hours"
/>
)
);
},
Cell({ row }) {
return row.original.routing_score || '-'
return row.original.routing_score || "-";
},
},
],
},
]
}, [])
];
}, []);
const table = useMaterialReactTable({
columns,
data: serviceProviders?.data || [],
layoutMode: 'grid',
layoutMode: "grid",
state: {
isLoading: serviceProviders?.isLoading,
},
initialState: {
sorting: [
{
id: 'routing_score',
id: "routing_score",
desc: true,
},
],
},
})
});
return (
<>
@@ -134,7 +133,7 @@ const ServiceProviders = () => {
</Grid>
</Grid>
</>
)
}
);
};
export default ServiceProviders
export default ServiceProviders;
-4
View File
@@ -2,12 +2,8 @@
import React, { useMemo } from 'react'
import {
Alert,
Box,
CircularProgress,
Grid,
SelectChangeEvent,
Typography,
} from '@mui/material'
import { ContentCard } from '@/app/components/ContentCard'
import { TableToolbar } from '@/app/components/TableToolbar'
+8 -8
View File
@@ -1,8 +1,8 @@
import React from 'react'
import CosmosKitProvider from '@/app/context/cosmos-kit'
import { WalletProvider } from '@/app/context/wallet'
import { NetworkExplorerThemeProvider } from '@/app/theme'
import { MainContextProvider } from '@/app/context/main'
import React from "react";
import CosmosKitProvider from "@/app/context/cosmos-kit";
import { WalletProvider } from "@/app/context/wallet";
import { NetworkExplorerThemeProvider } from "@/app/theme";
import { MainContextProvider } from "@/app/context/main";
const Providers = ({ children }: { children: React.ReactNode }) => {
return (
@@ -13,7 +13,7 @@ const Providers = ({ children }: { children: React.ReactNode }) => {
</CosmosKitProvider>
</NetworkExplorerThemeProvider>
</MainContextProvider>
)
}
);
};
export { Providers }
export { Providers };
+12 -8
View File
@@ -1,15 +1,19 @@
'use client'
"use client";
import * as React from 'react'
import { NymNetworkExplorerThemeProvider } from '@nymproject/mui-theme'
import { useMainContext } from '../context/main'
import * as React from "react";
import { NymNetworkExplorerThemeProvider } from "@nymproject/mui-theme";
import { useMainContext } from "../context/main";
export const NetworkExplorerThemeProvider: FCWithChildren = ({ children }) => {
const { mode } = useMainContext()
export const NetworkExplorerThemeProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { mode } = useMainContext();
return (
<NymNetworkExplorerThemeProvider mode={mode}>
{children}
</NymNetworkExplorerThemeProvider>
)
}
);
};
+1 -1
View File
@@ -1 +1 @@
declare type FCWithChildren<P = {}> = React.FC<React.PropsWithChildren<P>>;
declare type FCWithChildren<P = object> = React.FC<React.PropsWithChildren<P>>;
-1
View File
@@ -1 +0,0 @@
declare module 'react-tooltip';
+56 -37
View File
@@ -1,19 +1,21 @@
/* eslint-disable camelcase */
import { MutableRefObject } from 'react';
import { Theme } from '@mui/material/styles';
import { registerLocale, getName } from 'i18n-iso-countries';
import Big from 'big.js';
import { CountryData } from '@/app/typeDefs/explorer-api';
import { EconomicsRowsType } from '@/app/components/MixNodes/Economics/types';
import { Network } from '../typeDefs/network';
import { MutableRefObject } from "react";
import { Theme } from "@mui/material/styles";
import { registerLocale, getName } from "i18n-iso-countries";
import Big from "big.js";
import { CountryData } from "@/app/typeDefs/explorer-api";
import { EconomicsRowsType } from "@/app/components/MixNodes/Economics/types";
import { Network } from "../typeDefs/network";
registerLocale(require('i18n-iso-countries/langs/en.json'));
registerLocale(require("i18n-iso-countries/langs/en.json"));
export function formatNumber(num: number): string {
return new Intl.NumberFormat().format(num);
}
export function scrollToRef(ref: MutableRefObject<HTMLDivElement | undefined>): void {
export function scrollToRef(
ref: MutableRefObject<HTMLDivElement | undefined>
): void {
if (ref?.current) ref.current.scrollIntoView();
}
@@ -25,13 +27,15 @@ export type CountryDataRowType = {
percentage: string;
};
export function countryDataToGridRow(countriesData: CountryData[]): CountryDataRowType[] {
export function countryDataToGridRow(
countriesData: CountryData[]
): CountryDataRowType[] {
const totalNodes = countriesData.reduce((acc, obj) => acc + obj.nodes, 0);
const formatted = countriesData.map((each: CountryData, index: number) => {
const updatedCountryRecord: CountryDataRowType = {
...each,
id: index,
countryName: getName(each.ISO3, 'en', { select: 'alias' }),
countryName: getName(each.ISO3, "en", { select: "alias" }) || "",
percentage: ((each.nodes * 100) / totalNodes).toFixed(1),
};
return updatedCountryRecord;
@@ -41,16 +45,21 @@ export function countryDataToGridRow(countriesData: CountryData[]): CountryDataR
return sorted;
}
export const splice = (start: number, deleteCount: number, address?: string): string => {
export const splice = (
start: number,
deleteCount: number,
address?: string
): string => {
if (address) {
const array = address.split('');
array.splice(start, deleteCount, '...');
return array.join('');
const array = address.split("");
array.splice(start, deleteCount, "...");
return array.join("");
}
return '';
return "";
};
export const trimAddress = (address = '', trimBy = 6) => `${address.slice(0, trimBy)}...${address.slice(-trimBy)}`;
export const trimAddress = (address = "", trimBy = 6) =>
`${address.slice(0, trimBy)}...${address.slice(-trimBy)}`;
/**
* Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100).
@@ -58,27 +67,33 @@ export const trimAddress = (address = '', trimBy = 6) => `${address.slice(0, tri
* @param value - the percentage to convert
* @returns A stringified integer
*/
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
export const toPercentInteger = (value: string) => Math.round(Number(value) * 100);
export const toPercentIntegerString = (value: string) =>
Math.round(Number(value) * 100).toString();
export const toPercentInteger = (value: string) =>
Math.round(Number(value) * 100);
export const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
export const textColour = (
value: EconomicsRowsType,
field: string,
theme: Theme
) => {
const progressBarValue = value?.progressBarValue || 0;
const fieldValue = value.value;
if (progressBarValue > 100) {
return theme.palette.warning.main;
}
if (field === 'selectionChance') {
if (field === "selectionChance") {
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
switch (fieldValue) {
case 'High':
case 'VeryHigh':
case "High":
case "VeryHigh":
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
case 'Good':
case 'Moderate':
case "Good":
case "Moderate":
return theme.palette.nym.networkExplorer.selectionChance.moderate;
case 'Low':
case 'VeryLow':
case "Low":
case "VeryLow":
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
default:
return theme.palette.nym.wallet.fee;
@@ -101,8 +116,12 @@ export const isLessThan = (a: number, b: number) => a < b;
*
*/
export const isBalanceEnough = (fee: string, tx: string = '0', balance: string = '0') => {
console.log('balance', balance, fee, tx);
export const isBalanceEnough = (
fee: string,
tx: string = "0",
balance: string = "0"
) => {
console.log("balance", balance, fee, tx);
try {
return Big(balance).gte(Big(fee).plus(Big(tx)));
} catch (e) {
@@ -112,13 +131,13 @@ export const isBalanceEnough = (fee: string, tx: string = '0', balance: string =
};
export const urls = (networkName?: Network) =>
networkName === 'MAINNET'
networkName === "MAINNET"
? {
mixnetExplorer: 'https://mixnet.explorers.guru/',
blockExplorer: 'https://blocks.nymtech.net',
networkExplorer: 'https://explorer.nymtech.net',
}
mixnetExplorer: "https://mixnet.explorers.guru/",
blockExplorer: "https://blocks.nymtech.net",
networkExplorer: "https://explorer.nymtech.net",
}
: {
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
+37 -6
View File
@@ -4,25 +4,56 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"build:prod": "yarn --cwd .. build && next build",
"build:prod": "pnpm i && pnpm -C .. build && next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nymproject/react": "^1.0.0",
"@nymproject/nym-validator-client": "0.18.0",
"@chain-registry/types": "^0.45.9",
"@cosmjs/cosmwasm-stargate": "^0.29.5",
"@cosmos-kit/core": "^2.13.1",
"@cosmos-kit/keplr-extension": "^2.12.2",
"@cosmos-kit/react": "^2.17.2",
"@mui/base": "5.0.0-beta.40",
"@mui/icons-material": "^5.2.0",
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/system": "^5.15.20",
"@mui/x-data-grid": "7.1.1",
"@mui/x-date-pickers": "7.1.1",
"@nymproject/contract-clients": "1.2.4-rc.1",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@storybook/react": "^6.5.15",
"@types/d3-scale": "^4.0.8",
"big.js": "^6.2.1",
"chain-registry": "^1.63.12",
"d3-scale": "^4.0.2",
"date-fns": "^2.28.0",
"i18n-iso-countries": "^7.11.2",
"lodash": "^4.17.21",
"material-react-table": "^2.12.1",
"next": "14.1.4",
"react": "^18",
"react-dom": "^18",
"react-error-boundary": "^4.0.13",
"material-react-table": "^2.12.1",
"@mui/x-date-pickers": "7.1.1",
"@mui/x-data-grid": "7.1.1"
"react-google-charts": "^4.0.1",
"react-identicons": "^1.2.5",
"react-simple-maps": "^3.0.0",
"react-tooltip": "^5.27.0",
"semver": "^6.3.1",
"use-clipboard-copy": "^0.2.0"
},
"devDependencies": {
"@types/big.js": "^6.1.6",
"@types/lodash": "^4.17.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-simple-maps": "^3.0.4",
"@types/semver": "^7.3.8",
"eslint": "^8",
"eslint-config-next": "14.1.4",
"typescript": "^5"
+4 -4
View File
@@ -40,11 +40,11 @@
"@mui/system": "^5.0.1",
"@mui/x-data-grid": "^5.0.0-beta.5",
"@nymproject/contract-clients": "1.2.4-rc.1",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/mui-theme": "workspace: ^1.0.0",
"@nymproject/node-tester": "^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@tauri-apps/api": "^1.5.1",
"big.js": "^6.2.1",
"bs58": "^5.0.0",
@@ -68,7 +68,7 @@
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/addon-actions": "^6.5.8",
"@storybook/addon-essentials": "^6.5.8",
-3
View File
@@ -1,3 +0,0 @@
{
"version": "independent"
}
+1 -1
View File
@@ -36,7 +36,7 @@
"zod": "^3.21.4"
},
"devDependencies": {
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1",
+16 -15
View File
@@ -5,20 +5,20 @@
"main": "index.js",
"scripts": {
"build": "run-s webpack:prod tauri:build",
"dev": "run-p tauri:dev webpack:dev",
"dev": "pnpm run-p tauri:dev webpack:dev",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prebuild": "yarn --cwd .. build",
"prestorybook": "yarn --cwd .. build",
"prewebpack:dev": "yarn --cwd .. build",
"prebuild": "pnpm -C .. build",
"prestorybook": "pnpm -C .. build",
"prewebpack:dev": "pnpm -C .. run build",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook",
"tauri:build": "yarn tauri build",
"tauri:dev": "yarn tauri dev",
"tauri:build": "pnpm tauri build",
"tauri:dev": "pnpm tauri dev",
"tsc": "tsc --noEmit true",
"tsc:watch": "tsc --noEmit true --watch",
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
"webpack:prod": "yarn webpack --progress --config webpack.prod.js"
"webpack:dev": "pnpm webpack serve --config webpack.dev.js",
"webpack:prod": "pnpm webpack --progress --config webpack.prod.js"
},
"dependencies": {
"@emotion/react": "^11.7.0",
@@ -28,10 +28,11 @@
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/utils": "^5.7.0",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/node-tester": "^1.2.3",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/node-tester": "1.2.4-rc.1",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@nymproject/webpack": "workspace:^1.0.0",
"@storybook/react": "^6.5.15",
"@tauri-apps/api": "^1.2.0",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
@@ -42,7 +43,6 @@
"joi": "^17.11.0",
"lodash": "^4.17.21",
"notistack": "^2.0.3",
"npm-run-all": "^4.1.5",
"qrcode.react": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -63,7 +63,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/react": "^6.5.15",
"@svgr/webpack": "^6.1.1",
@@ -73,6 +73,7 @@
"@types/big.js": "^6.1.6",
"@types/bs58": "^4.0.1",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.17.6",
"@types/node": "^16.7.13",
"@types/qrcode.react": "^1.0.2",
"@types/react": "^18.0.26",
@@ -125,4 +126,4 @@
"webpack-merge": "^5.8.0"
},
"private": false
}
}
@@ -1,10 +0,0 @@
import * as React from 'react';
import { ComponentMeta } from '@storybook/react';
import { Playground } from '@nymproject/react/playground/Playground';
export default {
title: 'Playground',
component: Playground,
} as ComponentMeta<typeof Playground>;
export const AllControls = () => <Playground />;
+37 -34
View File
@@ -3,55 +3,58 @@
"version": "1.0.0",
"private": true,
"license": "Apache 2.0",
"workspaces": [
"dist/wasm/**",
"dist/node/**",
"dist/ts/**",
"sdk/typescript/packages/mui-theme",
"sdk/typescript/packages/react-components",
"sdk/typescript/packages/validator-client",
"ts-packages/*",
"nym-wallet",
"explorer",
"explorer-nextjs",
"types",
"clients/validator"
],
"scripts": {
"nuke": "npx rimraf **/node_modules node_modules",
"scrub": "npx rimraf **/dist dist",
"clean": "lerna run clean",
"build": "run-s build:types build:packages",
"build": "pnpm build:types && pnpm build:packages",
"build:wasm": "make sdk-wasm-build",
"build:types": "lerna run --scope @nymproject/types build --stream",
"build:types": "pnpm run --filter @nymproject/types --stream build",
"build:packages": "run-s build:packages:theme build:packages:react",
"build:packages:theme": "lerna run --scope @nymproject/mui-theme build",
"build:packages:react": "lerna run --scope @nymproject/react build",
"build:react-example": "lerna run --scope @nymproject/react-webpack-with-theme-example build --stream",
"build:playground": "lerna run --scope @nymproject/react storybook:build --stream",
"build:ci:storybook": "yarn build && yarn dev:on && run-p build:react-example build:playground && yarn build:ci:storybook:collect-artifacts",
"build:packages:theme": "pnpm run --filter @nymproject/mui-theme build",
"build:packages:react": "pnpm run --filter @nymproject/react build",
"build:react-example": "pnpm run --filter @nymproject/react-webpack-with-theme-example --stream build ",
"build:playground": "pnpm run --filter @nymproject/react --stream storybook:build",
"build:ci:storybook": "pnpm run build && pnpm run dev:on && run-p build:react-example build:playground && pnpm build:ci:storybook:collect-artifacts",
"build:ci:storybook:collect-artifacts": "mkdir -p ts-packages/dist && mv sdk/typescript/packages/react-components/storybook-static ts-packages/dist/storybook && mv sdk/typescript/examples/react/mui-theme/dist ts-packages/dist/example",
"prebuild:ci": "yarn dev:on && yarn",
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
"postbuild:ci": "yarn dev:off",
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/contract-clients,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
"docs:prod:build": "run-s docs:prod:build:ws",
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
"prebuild:ci": "pnpm run dev:on",
"build:ci": "pnpm dlx npm-run-all build:types build:packages build:wasm build:ci:sdk",
"postbuild:ci": "pnpm run dev:off",
"build:ci:sdk": "pnpm run --filter '{@nymproject/sdk,@nymproject/node-tester,@nymproject/contract-clients,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' --stream build",
"docs:prod:build": "pnpm docs:prod:build:ws",
"docs:prod:build:ws": "pnpm -r docs:prod:build",
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
"sdk:publish": "./sdk/typescript/scripts/publish.sh",
"lint": "lerna run lint --stream",
"lint:fix": "lerna run lint:fix --stream",
"tsc": "lerna run tsc --stream",
"types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app",
"lint": "pnpm -r run --stream lint",
"lint:fix": "pnpm -r run --stream lint:fix",
"tsc": "pnpm -r run --stream tsc",
"types:lint:fix": "pnpm run lint:fix --filter @nymproject/types --filter @nymproject/nym-wallet-app",
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
"dev:on": "node sdk/typescript/scripts/dev-mode-add.mjs",
"dev:off": "node sdk/typescript/scripts/dev-mode-remove.mjs"
},
"devDependencies": {
"lerna": "^7.3.0",
"npm-run-all": "^4.1.5",
"@npmcli/node-gyp": "^3.0.0",
"js-yaml": "^4.1.0",
"node-gyp": "^9.3.1",
"npm-run-all": "^4.1.5",
"tslog": "3.3.3"
},
"pnpm": {
"overrides": {
"d3-color@<3.1.0": ">=3.1.0",
"trim@<0.0.3": ">=0.0.3",
"sharp@<0.30.5": ">=0.30.5",
"postcss@<8.4.31": ">=8.4.31",
"sharp@<0.32.6": ">=0.32.6",
"trim-newlines@<3.0.1": ">=3.0.1",
"semver@>=7.0.0 <7.5.2": ">=7.5.2",
"xml2js@<0.5.0": ">=0.5.0",
"webpack-dev-middleware@<=5.3.3": ">=5.3.4",
"@babel/traverse@<7.23.2": ">=7.23.2",
"ws@>=8.0.0 <8.17.1": ">=8.17.1",
"axios@>=0.8.1 <0.28.0": ">=0.28.0",
"protobufjs@>=6.10.0 <6.11.4": ">=6.11.4",
"braces@<3.0.3": ">=3.0.3"
}
}
}
+39228
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
packages:
- clients/validator
- dist/wasm/**
- dist/node/**
- dist/ts/**
- explorer-nextjs
- sdk/typescript/packages/react-components
- sdk/typescript/packages/validator-client
- sdk/typescript/packages/mui-theme
- sdk/typescript/packages/**
- sdk/typescript/examples/**
- sdk/typescript/codegen/**
- ts-packages/**
- wallet
@@ -15,7 +15,7 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/contract-clients/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\""
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\""
},
"devDependencies": {
"@cosmwasm/ts-codegen": "^0.35.3",
@@ -26,5 +26,9 @@
"typescript": "^4.6.2"
},
"private": false,
"types": "./dist/index.d.ts"
}
"types": "./dist/index.d.ts",
"dependencies": {
"@cosmjs/amino": "^0.32.4",
"@cosmjs/cosmwasm-stargate": "^0.29.5"
}
}
@@ -7,7 +7,7 @@ set -o pipefail
rm -rf ../../../../dist/ts/docs/tsdoc/nymproject/contract-clients || true
# run the build
yarn docs:generate:prod
pnpm docs:generate:prod
# move the output outside of the yarn/npm workspaces
mkdir -p ../../../../dist/ts/docs/tsdoc/nymproject
@@ -6,7 +6,7 @@
"scripts": {
"build": "npx parcel build",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint": "echo not implemented",
"lint:fix": "eslint src --fix",
"start": "npx parcel",
"test": "jest",
@@ -41,4 +41,4 @@
"private": false,
"browserslist": "> 0.5%, last 2 versions, not dead",
"source": "src/index.html"
}
}
@@ -7,8 +7,7 @@
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"lint": "echo not implemented",
"start": "webpack serve --progress --port 3000",
"test": "jest",
"test:watch": "jest --watch",
@@ -62,4 +61,4 @@
"webpack-merge": "^5.8.0"
},
"private": false
}
}
@@ -4,7 +4,7 @@
"description": "An example project that uses WASM, React, Webpack, Typescript and the Nym theme + components library",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"build": "echo not implemented",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
@@ -78,7 +78,7 @@
"typescript": "^4.6.2",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^4.8.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.5.0",
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
@@ -90,4 +90,4 @@
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
}
@@ -1,5 +1,6 @@
import * as React from 'react';
import { createNymMixnetClient, IWebWorkerEvents, MimeTypes, NymClientConfig, NymMixnetClient } from '@nymproject/sdk';
export interface BinaryMessageHeaders {
filename: string;
mimeType: string;
@@ -1,5 +1,11 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { App } from './App';
ReactDOM.render(<App />, document.getElementById('app'));
const element = document.getElementById('app');
if (!element) {
throw new Error('Element not found');
}
createRoot(element).render(<App />);
@@ -4,14 +4,14 @@
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@mui/icons-material": "^5.5.0",
"@mui/lab": "^5.0.0-alpha.72",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/react": "^1.0.0"
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/react": "workspace:^1.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -19,15 +19,16 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/webpack": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@nymproject/webpack": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.26",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.3.0",
@@ -1,8 +1,7 @@
import * as React from 'react';
import { Box, Container, Grid, Typography } from '@mui/material';
import { NymLogo } from '@nymproject/react/logo/NymLogo';
import { Playground } from '@nymproject/react/playground/Playground';
import { useIsMounted } from '@nymproject/react/hooks/useIsMounted';
import { NymLogo } from '@nymproject/react';
import { useIsMounted } from '@nymproject/react';
import { NymThemeProvider } from '@nymproject/mui-theme';
import { useTheme } from '@mui/material/styles';
import { ThemeToggle } from './ThemeToggle';
@@ -65,8 +64,6 @@ export const Content: FCWithChildren = () => {
))}
</Grid>
</Box>
<h1>Component playground</h1>
<Playground />
</Container>
);
};
@@ -1,13 +0,0 @@
import React, { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { App } from '../App';
describe('App', () => {
beforeEach(() => {
render(<App />);
});
it('should render without exploding', () => {
const { container } = render(<App />);
expect(container.firstChild).toBeInTheDocument();
});
});
File diff suppressed because it is too large Load Diff
@@ -37,7 +37,7 @@
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint": "echo not implemented",
"lint:fix": "eslint src --fix"
}
}
@@ -19,11 +19,11 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-fetch --experimental-vm-modules node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
@@ -42,6 +42,7 @@
"@rollup/plugin-wasm": "^6.1.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
File diff suppressed because it is too large Load Diff
@@ -24,18 +24,19 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prebuild": "node scripts/showDependencyLocation.cjs",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/mix-fetch-wasm": ">=1.2.4-rc.2 || ^1",
"comlink": "^4.3.1"
"comlink": "^4.3.1",
"ws": "^8.18.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -43,7 +44,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: ^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0",
@@ -54,6 +55,7 @@
"@rollup/plugin-wasm": "^6.1.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
@@ -11,7 +11,7 @@
"@mui/styles": "^5.2.2"
},
"devDependencies": {
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: jnshhbwshsbsjZ hb ^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
@@ -20,7 +20,7 @@
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
@@ -34,7 +34,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: ^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0",
@@ -17,11 +17,11 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"tsc": "tsc --noEmit true"
},
"dependencies": {
@@ -40,6 +40,7 @@
"@rollup/plugin-url": "^8.0.1",
"@rollup/plugin-wasm": "^6.1.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,21 @@
{
"parserOptions": {
"project": "./tsconfig.eslint.json"
"root": true,
"env": {
"browser": true,
"es2020": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"],
"ignorePatterns": ["dist", ".eslintrc.cjs"],
"parser": "@typescript-eslint/parser",
"plugins": ["react-refresh"],
"rules": {
"react-refresh/only-export-components": [
"warn",
{
"allowConstantExport": true
}
],
"@typescript-eslint/no-explicit-any": "off",
"react-hooks/exhaustive-deps": "off"
}
}
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -1,40 +1,25 @@
/* eslint-disable no-param-reassign */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
export const framework = {
name: '@storybook/react-vite',
options: {},
};
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: '@storybook/react',
core: {
builder: 'webpack5',
},
// webpackFinal: async (config, { configType }) => {
// // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// // You can change the configuration based on that.
// // 'PRODUCTION' is used when building the static version of storybook.
webpackFinal: async (config) => {
config.module.rules.forEach((rule) => {
// look for SVG import rule and replace
// NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
if (rule.test?.toString().includes('svg')) {
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
}
});
export const docs = {
autodocs: true,
};
// handle asset loading with this
config.module.rules.unshift({
test: /\.svg(\?.*)?$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
});
export const typescript = {
reactDocgen: 'react-docgen-typescript',
};
config.resolve.extensions = ['.tsx', '.ts', '.js'];
config.resolve.plugins = [new TsconfigPathsPlugin()];
const config = {
framework: '@storybook/react-vite',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
// Return the altered config
return config;
},
features: {
emotionAlias: false,
docs: {},
typescript: {
reactDocgen: 'react-docgen-typescript',
},
};
export default config;
@@ -1,58 +1,30 @@
# Nym Shared React Components
# React + TypeScript + Vite
This package contains shared React components that are used in other Nym projects.
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
It uses the following packages:
Currently, two official plugins are available:
- [shared MUI theme](../mui-theme/README.md)
- [webpack config](../webpack/README.md)
- [MUI](https://https://mui.com/)
- Typescript
- React
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Building
## Expanding the ESLint configuration
```
yarn
yarn build
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```
## Development
Run watch mode with:
```
yarn watch
```
Or you can run Storybook with:
```
yarn storybook
```
Or you can run the [example project](../react-webpack-with-theme-example/README.md) in dev mode and this package in watch mode, and test results in the example project's dev server output.
## Playground
There are [playground components](./src/playground/index.tsx) that are intended to be used during development to see the effects are changes to the MUI theme or shared components at a glance.
They are available in Storybook from [src/stories/Playground.stories.tsx](./src/stories/Playground.stories.tsx).
> ️ **Tip**: use the playground to make sure components stay consistent and you don't break other components while making changes
## Shared assets
This project uses [shared asset files](../../assets/README.md) such as favicons and logos.
> ️ **Tip**: use to keep your project consistent with Nym's branding and so that it automatically receives changes when the shared assets change. Please try to avoid duplicating the files in the shared assets directory.
## Publishing
This package is not published to NPM ... yet.
## TODO
- ugprade to React 18
- upgrade Storybook
- upgrade MUI
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nym React Components</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
@@ -1,12 +1,11 @@
import * as React from 'react';
import { ChangeEvent } from 'react';
import { InputAdornment, TextField } from '@mui/material';
import { InputAdornment, TextField, SxProps } from '@mui/material';
import { TextFieldProps } from '@mui/material/TextField/TextField';
import { validateWalletAddress } from '@nymproject/types';
import DoneIcon from '@mui/icons-material/Done';
import { SxProps } from '@mui/system';
export const WalletAddressFormField: FCWithChildren<{
export type WalletAddressFormFieldProps = {
showTickOnValid?: boolean;
fullWidth?: boolean;
required?: boolean;
@@ -20,7 +19,9 @@ export const WalletAddressFormField: FCWithChildren<{
textFieldProps?: TextFieldProps;
errorText?: string;
sx?: SxProps;
}> = ({
};
export const WalletAddressFormField = ({
required,
fullWidth,
placeholder,
@@ -33,7 +34,7 @@ export const WalletAddressFormField: FCWithChildren<{
onValidate,
textFieldProps,
showTickOnValid = true,
}) => {
}: WalletAddressFormFieldProps) => {
const [value, setValue] = React.useState<string | undefined>(initialValue);
const [validationError, setValidationError] = React.useState<string | undefined>();
@@ -1,14 +1,13 @@
import * as React from 'react';
import { Box, Collapse, Alert, IconButton, Typography, Divider } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { SxProps } from '@mui/system';
import { SxProps } from '@mui/material';
export interface BannerProps {
export type BannerProps = {
open: boolean;
onClick: () => void;
height?: number;
sx?: SxProps;
}
};
export const MaintenanceBanner = (props: BannerProps) => {
const { open, onClick, height, sx } = props;
@@ -1,22 +1,21 @@
import React from 'react';
import { Box, Typography, Tooltip } from '@mui/material';
import { CopyToClipboard } from '../clipboard/CopyToClipboard';
const AddressTooltip: FCWithChildren<{ visible?: boolean; address?: string }> = ({ visible, address, children }) => {
type AddressTooltipProps = { visible?: boolean; address?: string; children: React.ReactNode };
const AddressTooltip = ({ visible, address, children }: AddressTooltipProps) => {
if (!visible || !address) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
return (
<Tooltip title={address} arrow>
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
<>{children}</>
</Tooltip>
);
};
type ClientAddressProps = {
export type ClientAddressProps = {
address: string;
withLabel?: boolean;
withCopy?: boolean;
@@ -24,7 +23,7 @@ type ClientAddressProps = {
showEntireAddress?: boolean;
};
export const ClientAddressDisplay = ({
export const ClientAddress = ({
withLabel,
withCopy,
smallIcons,
@@ -48,5 +47,3 @@ export const ClientAddressDisplay = ({
{withCopy && <CopyToClipboard smallIcons={smallIcons} value={address} />}
</Box>
);
export const ClientAddress = ({ ...props }: ClientAddressProps) => <ClientAddressDisplay {...props} />;
@@ -4,13 +4,15 @@ import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import DoneIcon from '@mui/icons-material/Done';
import { useClipboard } from 'use-clipboard-copy';
export const CopyToClipboard: FCWithChildren<{
export type CopyToClipboardProps = {
value: string;
tooltip?: React.ReactNode;
onCopy?: (value: string) => void;
smallIcons?: boolean;
sx?: SxProps;
}> = ({ value, tooltip, onCopy, smallIcons, sx }) => {
};
export const CopyToClipboard = ({ value, tooltip, onCopy, smallIcons, sx }: CopyToClipboardProps) => {
const copy = useClipboard();
const [showConfirmation, setShowConfirmation] = React.useState<boolean>(false);
const handleCopy = (e: React.MouseEvent<SVGSVGElement>) => {
@@ -1,13 +1,14 @@
import * as React from 'react';
import { PaletteMode, useTheme } from '@mui/material';
import TokenLight from '@assets/token/token-light.svg';
import TokenDark from '@assets/token/token-dark.svg';
import TokenLight from '@assets/token/token-light.svg?react';
import TokenDark from '@assets/token/token-dark.svg?react';
export const CoinMark: FCWithChildren<{
export type CoinMarkProps = {
mode?: PaletteMode;
width?: number | string;
height?: number | string;
}> = ({ mode, ...props }) => {
};
export const CoinMark = ({ mode, ...props }: CoinMarkProps) => {
const theme = useTheme();
const modeWithTheme = mode || theme.palette.mode;
if (modeWithTheme === 'light') {
@@ -0,0 +1,13 @@
import { useTheme } from '@mui/material';
import TokenLight from '@assets/token/token-light-testnet.svg?react';
import TokenDark from '@assets/token/token-dark-testnet.svg?react';
import { CoinMarkProps } from './CoinMark';
export const CoinMarkTestnet = ({ mode, ...props }: CoinMarkProps) => {
const theme = useTheme();
const modeWithTheme = mode || theme.palette.mode;
if (modeWithTheme === 'light') {
return <TokenLight {...props} />;
}
return <TokenDark {...props} />;
};
@@ -1,16 +1,23 @@
import * as React from 'react';
import type { DecCoin } from '@nymproject/types';
import { Stack, SxProps } from '@mui/material';
import { CurrencyWithCoinMark } from './CurrencyWithCoinMark';
import { CURRENCY_AMOUNT_SPACING, CurrencyAmount } from './CurrencyAmount';
export const Currency: FCWithChildren<{
export type CurrencyProps = {
majorAmount?: DecCoin;
showDenom?: boolean;
showCoinMark?: boolean;
coinMarkPrefix?: boolean;
sx?: SxProps;
}> = ({ majorAmount, sx, showDenom = true, showCoinMark = false, coinMarkPrefix = false }) => {
};
export const Currency = ({
majorAmount,
sx,
showDenom = true,
showCoinMark = false,
coinMarkPrefix = false,
}: CurrencyProps) => {
if (!majorAmount || !majorAmount.amount) {
return (
<Stack direction="row" sx={sx}>
@@ -1,13 +1,11 @@
/* eslint-disable react/no-array-index-key */
import * as React from 'react';
import type { DecCoin } from '@nymproject/types';
import { Stack, SxProps, Typography } from '@mui/material';
export const CURRENCY_AMOUNT_SPACING = 0.35;
const toReverseChunks = (value: String, size: number = 3): Array<string> => {
const toReverseChunks = (value: string, size: number = 3): Array<string> => {
const reversed = value.split('').reverse();
const chunks: Array<Array<String>> = [];
const chunks: Array<Array<string>> = [];
let chunksIndex = 0;
reversed.forEach((char, index) => {
if (index > 0 && index % size === 0) {
@@ -21,8 +19,8 @@ const toReverseChunks = (value: String, size: number = 3): Array<string> => {
return chunks.map((chars) => chars.reverse().join('')).reverse();
};
const toChunks = (value: String, size: number = 3): Array<string> => {
const chunks: Array<Array<String>> = [];
const toChunks = (value: string, size: number = 3): Array<string> => {
const chunks: Array<Array<string>> = [];
let chunksIndex = 0;
value.split('').forEach((char, index) => {
if (index > 0 && index % size === 0) {
@@ -36,11 +34,13 @@ const toChunks = (value: String, size: number = 3): Array<string> => {
return chunks.map((chars) => chars.join(''));
};
export const CurrencyAmountString: FCWithChildren<{
export type CurrencyAmountStringProps = {
majorAmount?: string;
showSeparators?: boolean;
sx?: SxProps;
}> = ({ majorAmount, sx, showSeparators = true }) => {
};
export const CurrencyAmountString = ({ majorAmount, sx, showSeparators = true }: CurrencyAmountStringProps) => {
if (!majorAmount) {
return (
<Stack direction="row" sx={sx}>
@@ -93,8 +93,12 @@ export const CurrencyAmountString: FCWithChildren<{
);
};
export const CurrencyAmount: FCWithChildren<{
export type CurrencyAmountProps = {
majorAmount?: DecCoin;
showSeparators?: boolean;
sx?: SxProps;
}> = ({ majorAmount, ...props }) => <CurrencyAmountString majorAmount={majorAmount?.amount} {...props} />;
};
export const CurrencyAmount = ({ majorAmount, ...props }: CurrencyAmountProps) => (
<CurrencyAmountString majorAmount={majorAmount?.amount} {...props} />
);
@@ -1,14 +1,13 @@
import * as React from 'react';
import { ChangeEvent } from 'react';
import { InputAdornment, TextField } from '@mui/material';
import { SxProps } from '@mui/system';
import { InputAdornment, TextField, SxProps } from '@mui/material';
import { CurrencyDenom, DecCoin } from '@nymproject/types';
import { CoinMark } from '../coins/CoinMark';
const MAX_VALUE = 1_000_000_000_000_000;
const MIN_VALUE = 0.000001;
export const CurrencyFormField: FCWithChildren<{
export type CurrencyFormFieldProps = {
autoFocus?: boolean;
required?: boolean;
fullWidth?: boolean;
@@ -22,7 +21,9 @@ export const CurrencyFormField: FCWithChildren<{
onChanged?: (newValue: DecCoin) => void;
onValidate?: (newValue: string | undefined, isValid: boolean, error?: string) => void;
sx?: SxProps;
}> = ({
};
export const CurrencyFormField = ({
autoFocus,
required,
placeholder,
@@ -36,8 +37,8 @@ export const CurrencyFormField: FCWithChildren<{
sx,
showCoinMark = true,
denom = 'nym',
}) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}: CurrencyFormFieldProps) => {
const [value, setValue] = React.useState<string | undefined>(initialValue);
const [validationError, setValidationError] = React.useState<string | undefined>(validationErrorProp);
@@ -1,4 +1,3 @@
import * as React from 'react';
import { DecCoin } from '@nymproject/types';
import { Stack, SxProps } from '@mui/material';
import { useTheme } from '@mui/material/styles';
@@ -6,13 +5,21 @@ import { CoinMark } from '../coins/CoinMark';
import { CoinMarkTestnet } from '../coins/CoinMarkTestnet';
import { CurrencyAmount } from './CurrencyAmount';
export const CurrencyWithCoinMark: FCWithChildren<{
export type CurrencyWithCoinMarkProps = {
majorAmount?: DecCoin;
fontSize?: number;
prefix?: boolean;
showSeparators?: boolean;
sx?: SxProps;
}> = ({ majorAmount, fontSize, prefix, showSeparators, sx }) => {
};
export const CurrencyWithCoinMark = ({
majorAmount,
fontSize,
prefix,
showSeparators,
sx,
}: CurrencyWithCoinMarkProps) => {
const theme = useTheme();
const size = fontSize || theme.typography.htmlFontSize;
if (!majorAmount) {
@@ -0,0 +1,22 @@
export * from './account/WalletAddressFormField';
export * from './banners/MaintenanceBanner';
export { ClientAddress } from './client-address/ClientAddress';
export { CopyToClipboard } from './clipboard/CopyToClipboard';
export { CoinMark } from './coins/CoinMark';
export { CoinMarkTestnet } from './coins/CoinMarkTestnet';
export { Currency } from './currency/Currency';
export { CurrencyAmount } from './currency/CurrencyAmount';
export { CurrencyFormField } from './currency/CurrencyFormField';
export { CurrencyWithCoinMark } from './currency/CurrencyWithCoinMark';
export { Link } from './link/Link';
export { NymIcon } from './logo/NymIcon';
export { NymLogo } from './logo/NymLogo';
export { NymLogoBW } from './logo/NymLogoBW';
export { NymWordmark } from './logo/NymWordmark';
export { IdentityKeyFormField } from './mixnodes/IdentityKeyFormField';
export { NetworkSelector } from './networks/NetworkSelector';
export { PasswordStrength } from './password-strength/PasswordStrength';
export { MnemonicInput } from './textfields/Mnemonic';
export { PasswordInput } from './textfields/Password';
export { Tooltip } from './tooltip/Tooltip';
export { Error } from './warnings/Error';
@@ -1,14 +1,13 @@
import * as React from 'react';
import { Box, Typography, Link as MUILink, LinkProps as MUILinkProps } from '@mui/material';
import { OpenInNew } from '@mui/icons-material';
export interface LinkProps {
export type LinkProps = {
text?: string;
icon?: React.ReactNode;
noIcon?: boolean;
fontWeight?: number | string;
fontSize?: number | string;
}
};
export const Link = (props: MUILinkProps & LinkProps) => {
const { text, icon, underline, noIcon, children, fontWeight, fontSize } = props;
@@ -1,4 +1,4 @@
export interface LogoProps {
export type LogoProps = {
height?: number | string;
width?: number | string;
}
};
@@ -0,0 +1,5 @@
/// <reference types="vite-plugin-svgr/client" />
import Logo from '@assets/logo/logo-circle-small.svg?react';
import { LogoProps } from './LogoProps';
export const NymIcon = ({ height, width }: LogoProps) => <Logo height={height} width={width} />;
@@ -0,0 +1,4 @@
import Logo from '@assets/logo/logo-circle.svg?react';
import { LogoProps } from './LogoProps';
export const NymLogo = ({ height, width }: LogoProps) => <Logo height={height} width={width} />;
@@ -1,5 +1,4 @@
import * as React from 'react';
import Logo from '@assets/logo/logo-bw.svg';
import Logo from '@assets/logo/logo-bw.svg?react';
import { LogoProps } from './LogoProps';
export const NymLogoBW = ({ height, width }: LogoProps) => <Logo height={height} width={width} />;
@@ -1,9 +1,10 @@
import * as React from 'react';
import Wordmark from '@assets/logo/logo-wordmark.svg';
import Wordmark from '@assets/logo/logo-wordmark.svg?react';
import { useTheme } from '@mui/material';
import { LogoProps } from './LogoProps';
export const NymWordmark: FCWithChildren<LogoProps & { fill?: string }> = ({ height, width, fill }) => {
type NymWordmarkProps = LogoProps & { fill?: string };
export const NymWordmark = ({ height, width, fill }: NymWordmarkProps) => {
const theme = useTheme();
return <Wordmark height={height} width={width} fill={fill || theme.palette.text.primary} />;
};
@@ -1,12 +1,11 @@
import * as React from 'react';
import { ChangeEvent } from 'react';
import { InputAdornment, TextField } from '@mui/material';
import { InputAdornment, TextField, SxProps } from '@mui/material';
import { TextFieldProps } from '@mui/material/TextField/TextField';
import { validateKey } from '@nymproject/types';
import DoneIcon from '@mui/icons-material/Done';
import { SxProps } from '@mui/system';
export const IdentityKeyFormField: FCWithChildren<{
export type IdentityKeyFormFieldProps = {
showTickOnValid?: boolean;
fullWidth?: boolean;
required?: boolean;
@@ -23,7 +22,9 @@ export const IdentityKeyFormField: FCWithChildren<{
sx?: SxProps;
disabled?: boolean;
autoFocus?: boolean;
}> = ({
};
export const IdentityKeyFormField = ({
required,
fullWidth,
placeholder,
@@ -39,7 +40,7 @@ export const IdentityKeyFormField: FCWithChildren<{
size,
disabled,
autoFocus,
}) => {
}: IdentityKeyFormFieldProps) => {
const [value, setValue] = React.useState<string | undefined>(initialValue);
const [validationError, setValidationError] = React.useState<string | undefined>();
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { Button, List, ListItem, ListItemIcon, ListItemText, ListSubheader, Popover } from '@mui/material';
import { ArrowDropDown, CheckSharp } from '@mui/icons-material';
@@ -11,21 +11,21 @@ const networks: { networkName: Network; name: string }[] = [
{ networkName: 'QA', name: 'QA' },
];
const NetworkItem: FCWithChildren<{ title: string; isSelected: boolean; onSelect: () => void }> = ({
title,
isSelected,
onSelect,
}) => (
type NetworkItemProps = { title: string; isSelected: boolean; onSelect: () => void };
const NetworkItem = ({ title, isSelected, onSelect }: NetworkItemProps) => (
<ListItem button onClick={onSelect}>
<ListItemIcon>{isSelected && <CheckSharp color="success" />}</ListItemIcon>
<ListItemText>{title}</ListItemText>
</ListItem>
);
export const NetworkSelector: FCWithChildren<{
export type NetworkSelectorProps = {
network?: Network;
onSwitchNetwork?: (newNetwork: Network) => void;
}> = ({ network, onSwitchNetwork }) => {
};
export const NetworkSelector = ({ network, onSwitchNetwork }: NetworkSelectorProps) => {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
@@ -1,15 +1,13 @@
/* eslint-disable no-nested-ternary */
import React from 'react';
import zxcvbn, { ZXCVBNScore } from 'zxcvbn';
import { LockOutlined } from '@mui/icons-material';
import { LinearProgress, Stack, Typography, Box } from '@mui/material';
const colorMap = {
4: 'success' as 'success',
3: 'success' as 'success',
2: 'warning' as 'warning',
1: 'error' as 'error',
0: 'error' as 'error',
4: 'success' as const,
3: 'success' as const,
2: 'warning' as const,
1: 'error' as const,
0: 'error' as const,
};
const getText = (score: ZXCVBNScore) => {
@@ -61,15 +59,13 @@ const getPasswordStrength = (score: ZXCVBNScore) => {
}
};
export const PasswordStrength = ({
password,
withWarnings,
handleIsSafePassword,
}: {
export type PasswordStrengthProps = {
password: string;
withWarnings?: boolean;
handleIsSafePassword: (isSafe: boolean) => void;
}) => {
};
export const PasswordStrength = ({ password, withWarnings, handleIsSafePassword }: PasswordStrengthProps) => {
const result = zxcvbn(password);
handleIsSafePassword(result.score > 1);
@@ -1,14 +1,16 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { Stack, TextField } from '@mui/material';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import { Error } from '../warnings/Error';
export const MnemonicInput: FCWithChildren<{
export type MnemonicInputProps = {
mnemonic: string;
error?: string;
onUpdateMnemonic: (mnemonic: string) => void;
}> = ({ mnemonic, error, onUpdateMnemonic }) => {
};
export const MnemonicInput = ({ mnemonic, error, onUpdateMnemonic }: MnemonicInputProps) => {
const [showMnemonic, setShowMnemonic] = useState(false);
return (
<Stack spacing={2}>
@@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { Error } from '../warnings/Error';
export const PasswordInput: FCWithChildren<{
export type PasswordInputProps = {
password: string;
error?: string;
label?: string;
@@ -11,7 +11,17 @@ export const PasswordInput: FCWithChildren<{
autoFocus?: boolean;
disabled?: boolean;
onUpdatePassword: (password: string) => void;
}> = ({ password, label, placeholder, error, autoFocus, disabled, onUpdatePassword }) => {
};
export const PasswordInput = ({
password,
label,
placeholder,
error,
autoFocus,
disabled,
onUpdatePassword,
}: PasswordInputProps) => {
const [showPassword, setShowPassword] = useState(false);
return (
@@ -0,0 +1,2 @@
export * from './Mnemonic';
export * from './Password';
@@ -1,8 +1,7 @@
import * as React from 'react';
import { IconButton, Tooltip as MUITooltip } from '@mui/material';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
export interface CustomTooltipProps {
export type CustomTooltipProps = {
title: string;
arrow?: boolean;
id: string;
@@ -22,7 +21,8 @@ export interface CustomTooltipProps {
textColor?: string;
bgColor?: string;
maxWidth?: number;
}
TooltipIcon?: React.ReactElement;
};
const TooltipInfoIcon: React.ReactElement<any, any> = (
<IconButton
@@ -44,7 +44,7 @@ const TooltipInfoIcon: React.ReactElement<any, any> = (
);
export const Tooltip = (props: CustomTooltipProps) => {
const { title, arrow, id, placement, textColor, bgColor, maxWidth } = props;
const { title, arrow, id, placement, textColor, bgColor, maxWidth, TooltipIcon } = props;
return (
<MUITooltip
title={title}
@@ -64,7 +64,7 @@ export const Tooltip = (props: CustomTooltipProps) => {
},
}}
>
{TooltipInfoIcon}
{TooltipIcon || TooltipInfoIcon}
</MUITooltip>
);
};
@@ -0,0 +1 @@
export * from './Tooltip';
@@ -1,4 +1,3 @@
import React from 'react';
import { Alert } from '@mui/material';
export const Error = ({ message }: { message: string }) => (
@@ -0,0 +1 @@
export { useIsMounted } from './useIsMounted';
@@ -0,0 +1,2 @@
export * from './components';
export * from './hooks';
@@ -1,95 +1,61 @@
{
"name": "@nymproject/react",
"private": true,
"version": "1.0.0",
"license": "Apache-2.0",
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./*": "./dist/components/*",
"./hooks/*": "./dist/hooks/*",
"./playground/*": "./dist/playground/*"
"type": "module",
"files": [
"dist"
],
"module": "./dist/index.js",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"storybook:build": "storybook build"
},
"typesVersions": {
"*": {
"*": [
"dist/components/*"
],
"hooks/*": [
"dist/hooks/*"
],
"playground/*": [
"dist/playground/*"
]
}
},
"peerDependencies": {
"dependencies": {
"@cosmjs/math": "^0.27.1",
"@mui/icons-material": ">= 5",
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": ">= 5",
"@mui/styles": ">= 5",
"@mui/system": ">= 5",
"@nymproject/mui-theme": "1",
"@mui/x-tree-view": "^7.10.0",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/types": "1",
"base58": "4",
"@nymproject/types": "workspace:1",
"bech32": "^1.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"bs58": "4",
"flat": "^5.0.2",
"use-clipboard-copy": "^0.2.0",
"zxcvbn": "^4.4.2"
},
"dependencies": {
"@mui/x-tree-view": "^7.10.0",
"flat": "^5.0.2",
"use-clipboard-copy": "^0.2.0"
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@storybook/addon-actions": "^6.5.8",
"@storybook/addon-essentials": "^6.5.8",
"@storybook/addon-interactions": "^6.5.8",
"@storybook/addon-links": "^6.5.8",
"@storybook/builder-webpack5": "^6.5.8",
"@storybook/manager-webpack5": "^6.5.8",
"@storybook/react": "^6.5.15",
"@storybook/testing-library": "^0.0.9",
"@svgr/webpack": "^6.1.1",
"@types/flat": "^5.0.2",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.2.3",
"babel-plugin-root-import": "^5.1.0",
"eslint": "^8.10.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-root-import": "^1.0.4",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^26.1.1",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-storybook": "^0.5.12",
"jest": "^27.1.0",
"prettier": "^2.8.7",
"rimraf": "^3.0.2",
"ts-jest": "^27.0.5",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.2"
},
"scripts": {
"clean": "rimraf dist",
"tsc": "tsc --noEmit true",
"build": "tsc --noEmit false",
"watch": "tsc --noEmit false -w",
"lint": "eslint src .storybook",
"lint:fix": "eslint src .storybook --fix",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
},
"sideEffects": false
"@storybook/react": "^8.1.11",
"@storybook/react-vite": "^8.1.11",
"@types/flat": "^5.0.5",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/zxcvbn": "^4.4.4",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"storybook": "^8.1.11",
"typescript": "^5.2.2",
"vite": "^5.3.1",
"vite-plugin-dts": "^3.9.1",
"vite-plugin-svgr": "^4.2.0",
"vite-tsconfig-paths": "^4.3.2"
}
}
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
@@ -0,0 +1,20 @@
import React from 'react';
import { CoinMark, CoinMarkTestnet, NymLogo, NymLogoBW, NymWordmark } from '../dist/index';
import { NymThemeProvider } from '@nymproject/mui-theme';
import { Stack } from '@mui/system';
function App() {
return (
<NymThemeProvider mode="light">
<Stack spacing={2} direction="row" justifyContent="center">
<CoinMarkTestnet height={199} width={199} />
<NymLogoBW height={199} width={199} />
<NymLogo height={199} width={199} />
<NymWordmark height={199} width={199} />
<CoinMark height={199} width={199} />
</Stack>
</NymThemeProvider>
);
}
export default App;

Some files were not shown because too many files have changed in this diff Show More