Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77d7fede76 | |||
| f6929e365b | |||
| f98911f904 | |||
| c1be720e49 | |||
| b4a719dbab | |||
| ce99ccea44 | |||
| 595a76ee50 | |||
| dda7c09ec8 | |||
| c27713c678 | |||
| b8125d735e | |||
| 96617469e9 | |||
| 10ade80da0 | |||
| 6c8d5c129d | |||
| 4c01ee0150 | |||
| 9c85d51e34 | |||
| 30dc257ba7 | |||
| 1878720a31 | |||
| ae0d9f3179 | |||
| 1688eb9477 | |||
| 3c1939953c | |||
| 7de43540b9 | |||
| 979244f9da | |||
| 9e555ebd8a | |||
| 579ed7bb6e | |||
| 1655933678 | |||
| cd2978a8b0 | |||
| 6fcdbc15ce | |||
| ceee2d4466 | |||
| 8ee3ec2f4e |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals"]
|
||||
"extends": ["next/core-web-vitals"],
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
declare type FCWithChildren<P = {}> = React.FC<React.PropsWithChildren<P>>;
|
||||
declare type FCWithChildren<P = object> = React.FC<React.PropsWithChildren<P>>;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
declare module 'react-tooltip';
|
||||
@@ -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`,
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"version": "independent"
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+39228
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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>
|
||||
+6
-5
@@ -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>();
|
||||
|
||||
+3
-4
@@ -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;
|
||||
+5
-8
@@ -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
-2
@@ -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>) => {
|
||||
+6
-5
@@ -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} />;
|
||||
};
|
||||
+10
-3
@@ -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}>
|
||||
+14
-10
@@ -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} />
|
||||
);
|
||||
+7
-6
@@ -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);
|
||||
|
||||
+10
-3
@@ -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';
|
||||
+2
-3
@@ -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;
|
||||
+2
-2
@@ -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
-2
@@ -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} />;
|
||||
+4
-3
@@ -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} />;
|
||||
};
|
||||
+6
-5
@@ -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>();
|
||||
|
||||
+8
-8
@@ -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>) => {
|
||||
+9
-13
@@ -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);
|
||||
+5
-3
@@ -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}>
|
||||
+13
-3
@@ -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';
|
||||
+5
-5
@@ -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
@@ -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
Reference in New Issue
Block a user