From 0b578f83f7f4cceb67e577f5bda49e7b8c117c90 Mon Sep 17 00:00:00 2001 From: sergey filyanin Date: Mon, 19 Feb 2024 12:43:21 +0100 Subject: [PATCH 01/17] Release battleship (#253) Co-authored-by: root --- .../workflows/Release-DeployBattleship.yml | 111 +++++++++++++++++ .github/workflows/Release-escrow.yml | 110 +++++++++++++++++ .../workflows/Release-galactic-express.yml | 109 +++++++++++++++++ .github/workflows/Release-game-of-chance.yml | 110 +++++++++++++++++ .github/workflows/Release-nft-marketplace.yml | 113 ++++++++++++++++++ .../workflows/Release-non-fungible-token.yml | 112 +++++++++++++++++ .github/workflows/Release-racing-game.yml | 110 +++++++++++++++++ .github/workflows/Release-simple-nft.yml | 113 ++++++++++++++++++ .github/workflows/Release-supply-chain.yml | 110 +++++++++++++++++ .github/workflows/Release-syndote.yml | 110 +++++++++++++++++ .../workflows/Release-tamagochi-battle.yml | 111 +++++++++++++++++ .github/workflows/Release-tamagotchi.yml | 109 +++++++++++++++++ .github/workflows/Release-tequila-train.yml | 110 +++++++++++++++++ .github/workflows/Release-tictac.yml | 110 +++++++++++++++++ .github/workflows/Release-varaman.yml | 110 +++++++++++++++++ .github/workflows/Release-varatube.yml | 112 +++++++++++++++++ .github/workflows/Release-w3bstreaming.yml | 113 ++++++++++++++++++ .github/workflows/Release-w3bstreaming_be.yml | 111 +++++++++++++++++ ...attleship.yml => STG-DeployBattleship.yml} | 10 +- .../workflows/{escrow.yml => STG-escrow.yml} | 10 +- ...c-express.yml => STG-galactic-express.yml} | 10 +- ...e-of-chance.yml => STG-game-of-chance.yml} | 10 +- ...arketplace.yml => STG-nft-marketplace.yml} | 10 +- ...e-token.yml => STG-non-fungible-token.yml} | 10 +- .../{racing-game.yml => STG-racing-game.yml} | 10 +- .../{simple-nft.yml => STG-simple-nft.yml} | 10 +- ...{supply-chain.yml => STG-supply-chain.yml} | 10 +- .../{syndote.yml => STG-syndote.yml} | 10 +- ...hi-battle.yml => STG-tamagochi-battle.yml} | 10 +- .../{tamagotchi.yml => STG-tamagotchi.yml} | 8 +- ...equila-train.yml => STG-tequila-train.yml} | 10 +- .../workflows/{tictac.yml => STG-tictac.yml} | 10 +- .../{varaman.yml => STG-varaman.yml} | 10 +- .../{varatube.yml => STG-varatube.yml} | 10 +- ...{w3bstreaming.yml => STG-w3bstreaming.yml} | 10 +- ...reaming_be.yml => STG-w3bstreaming_be.yml} | 10 +- 36 files changed, 2083 insertions(+), 89 deletions(-) create mode 100644 .github/workflows/Release-DeployBattleship.yml create mode 100644 .github/workflows/Release-escrow.yml create mode 100644 .github/workflows/Release-galactic-express.yml create mode 100644 .github/workflows/Release-game-of-chance.yml create mode 100644 .github/workflows/Release-nft-marketplace.yml create mode 100644 .github/workflows/Release-non-fungible-token.yml create mode 100644 .github/workflows/Release-racing-game.yml create mode 100644 .github/workflows/Release-simple-nft.yml create mode 100644 .github/workflows/Release-supply-chain.yml create mode 100644 .github/workflows/Release-syndote.yml create mode 100644 .github/workflows/Release-tamagochi-battle.yml create mode 100644 .github/workflows/Release-tamagotchi.yml create mode 100644 .github/workflows/Release-tequila-train.yml create mode 100644 .github/workflows/Release-tictac.yml create mode 100644 .github/workflows/Release-varaman.yml create mode 100644 .github/workflows/Release-varatube.yml create mode 100644 .github/workflows/Release-w3bstreaming.yml create mode 100644 .github/workflows/Release-w3bstreaming_be.yml rename .github/workflows/{DeployBattleship.yml => STG-DeployBattleship.yml} (89%) rename .github/workflows/{escrow.yml => STG-escrow.yml} (89%) rename .github/workflows/{galactic-express.yml => STG-galactic-express.yml} (88%) rename .github/workflows/{game-of-chance.yml => STG-game-of-chance.yml} (89%) rename .github/workflows/{nft-marketplace.yml => STG-nft-marketplace.yml} (90%) rename .github/workflows/{non-fungible-token.yml => STG-non-fungible-token.yml} (89%) rename .github/workflows/{racing-game.yml => STG-racing-game.yml} (89%) rename .github/workflows/{simple-nft.yml => STG-simple-nft.yml} (89%) rename .github/workflows/{supply-chain.yml => STG-supply-chain.yml} (89%) rename .github/workflows/{syndote.yml => STG-syndote.yml} (89%) rename .github/workflows/{tamagochi-battle.yml => STG-tamagochi-battle.yml} (89%) rename .github/workflows/{tamagotchi.yml => STG-tamagotchi.yml} (90%) rename .github/workflows/{tequila-train.yml => STG-tequila-train.yml} (89%) rename .github/workflows/{tictac.yml => STG-tictac.yml} (89%) rename .github/workflows/{varaman.yml => STG-varaman.yml} (89%) rename .github/workflows/{varatube.yml => STG-varatube.yml} (89%) rename .github/workflows/{w3bstreaming.yml => STG-w3bstreaming.yml} (89%) rename .github/workflows/{w3bstreaming_be.yml => STG-w3bstreaming_be.yml} (89%) diff --git a/.github/workflows/Release-DeployBattleship.yml b/.github/workflows/Release-DeployBattleship.yml new file mode 100644 index 000000000..ae683b87b --- /dev/null +++ b/.github/workflows/Release-DeployBattleship.yml @@ -0,0 +1,111 @@ +name: Release - Deploy BattleShips + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: battleship + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/battleship/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + VITE_NODE_ADDRESS=${{ secrets.VITE_NODE_ADDRESS }} + VITE_CONTRACT_ADDRESS=${{ secrets.VITE_CONTRACT_ADDRESS_BATTLE }} + VITE_SENTRY_DSN=${{ secrets.VITE_SENTRY_DSN_BATTLE }} + VITE_GASLESS_BACKEND_ADDRESS=${{ secrets.VITE_GASLESS_BACKEND_ADDRESS }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-escrow.yml b/.github/workflows/Release-escrow.yml new file mode 100644 index 000000000..4619fdf96 --- /dev/null +++ b/.github/workflows/Release-escrow.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Escrow + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: escrow + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/escrow/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CODE_ADDRESS=${{ secrets.REACT_APP_CODE_ADDRESS_ESCROW }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_ESCROW }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-galactic-express.yml b/.github/workflows/Release-galactic-express.yml new file mode 100644 index 000000000..cce54023b --- /dev/null +++ b/.github/workflows/Release-galactic-express.yml @@ -0,0 +1,109 @@ +name: Release - Deploy Galactic Express + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: galactic-express + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/galactic-express/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_GALEX }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-game-of-chance.yml b/.github/workflows/Release-game-of-chance.yml new file mode 100644 index 000000000..ce6628b2f --- /dev/null +++ b/.github/workflows/Release-game-of-chance.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Game Of Chance + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: game-chance + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/game-of-chance/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_GAME_OF_CHANCE }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_GAME_OF_CHANCE }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-nft-marketplace.yml b/.github/workflows/Release-nft-marketplace.yml new file mode 100644 index 000000000..019c38b2b --- /dev/null +++ b/.github/workflows/Release-nft-marketplace.yml @@ -0,0 +1,113 @@ +name: Release - Deploy Marketplace + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: marketplace + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/nft-marketplace/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_IPFS_GATEWAY_ADDRESS=${{ secrets.REACT_APP_IPFS_GATEWAY_ADDRESS }} + REACT_APP_IPFS_ADDRESS=${{ secrets.REACT_APP_IPFS_ADDRESS }} + REACT_APP_NFT_CONTRACT_ADDRESS=${{ secrets.REACT_APP_NFT_CONTRACT_ADDRESS }} + REACT_APP_MARKETPLACE_CONTRACT_ADDRESS=${{ secrets.REACT_APP_MARKETPLACE_CONTRACT_ADDRESS }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_NFT_MARKETPLACE }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-non-fungible-token.yml b/.github/workflows/Release-non-fungible-token.yml new file mode 100644 index 000000000..a16af8cdd --- /dev/null +++ b/.github/workflows/Release-non-fungible-token.yml @@ -0,0 +1,112 @@ +name: Release - Deploy Non Fungible Token + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: non-fungible-token + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/non-fungible-token/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_IPFS_ADDRESS=${{ secrets.REACT_APP_IPFS_ADDRESS }} + REACT_APP_IPFS_GATEWAY_ADDRESS=${{ secrets.REACT_APP_IPFS_GATEWAY_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_NON_FUNGIBLE_TOKEN }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_NON_FUNGIBLE_TOKEN }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-racing-game.yml b/.github/workflows/Release-racing-game.yml new file mode 100644 index 000000000..d37722027 --- /dev/null +++ b/.github/workflows/Release-racing-game.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Racing Game + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: dapps-racing + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/racing-car-game/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_RACING }} + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_RACING }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-simple-nft.yml b/.github/workflows/Release-simple-nft.yml new file mode 100644 index 000000000..f216111e6 --- /dev/null +++ b/.github/workflows/Release-simple-nft.yml @@ -0,0 +1,113 @@ +name: Release - Deploy simple-nft + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: simple-nft + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/nft-master/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_IPFS_GATEWAY_ADDRESS=${{ secrets.REACT_APP_IPFS_GATEWAY_ADDRESS }} + REACT_APP_IPFS_ADDRESS=${{ secrets.REACT_APP_IPFS_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_NFT }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_NFT }} + REACT_APP_NFT_EXPLORER_URL=${{ secrets.REACT_APP_NFT_EXPLORER_URL }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-supply-chain.yml b/.github/workflows/Release-supply-chain.yml new file mode 100644 index 000000000..61e7c894d --- /dev/null +++ b/.github/workflows/Release-supply-chain.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Supply Chain + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: supply-chain + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/supply-chain/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CODE_ADDRESS=${{ secrets.REACT_APP_CODE_ADDRESS_SUPPLY_CHAIN }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_SUPPLY_CHAIN }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-syndote.yml b/.github/workflows/Release-syndote.yml new file mode 100644 index 000000000..5e342e582 --- /dev/null +++ b/.github/workflows/Release-syndote.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Syndote + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: syndote + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/syndote/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_SYNDOTE }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_SYNDOTE }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-tamagochi-battle.yml b/.github/workflows/Release-tamagochi-battle.yml new file mode 100644 index 000000000..7ee1814c8 --- /dev/null +++ b/.github/workflows/Release-tamagochi-battle.yml @@ -0,0 +1,111 @@ +name: Release - Deploy Tamagotchi Battle + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: tamagotchi-battle + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/tamagotchi-battle/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_TAMAGOCHI_BATTLE }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_TAMAGOCHI_BATTLE }} + REACT_APP_GASLESS_BACKEND_ADDRESS=${{ secrets.REACT_APP_GASLESS_BACKEND_ADDRESS }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-tamagotchi.yml b/.github/workflows/Release-tamagotchi.yml new file mode 100644 index 000000000..b6ed329a7 --- /dev/null +++ b/.github/workflows/Release-tamagotchi.yml @@ -0,0 +1,109 @@ +name: Release - Deploy Tamagotchi + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: tamagotchi + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/tamagotchi/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + VITE_NODE_ADDRESS=${{ secrets.VITE_NODE_ADDRESS }} + VITE_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_TAMAGOTCHI }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-tequila-train.yml b/.github/workflows/Release-tequila-train.yml new file mode 100644 index 000000000..9687b7d04 --- /dev/null +++ b/.github/workflows/Release-tequila-train.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Tequila Train + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: tequila-train + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/tequila-train/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_TEQUILA_TRAIN }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_TEQUILA_TRAIN }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-tictac.yml b/.github/workflows/Release-tictac.yml new file mode 100644 index 000000000..87a45f9ca --- /dev/null +++ b/.github/workflows/Release-tictac.yml @@ -0,0 +1,110 @@ +name: Release - Deploy TicTac + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: tic-tac-toe + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/tic-tac-toe/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + VITE_NODE_ADDRESS=${{ secrets.VITE_NODE_ADDRESS }} + VITE_CONTRACT_ADDRESS=${{ secrets.VITE_CONTRACT_ADDRESS_TIC_TAC }} + VITE_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_TIC_TAC }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-varaman.yml b/.github/workflows/Release-varaman.yml new file mode 100644 index 000000000..233bde099 --- /dev/null +++ b/.github/workflows/Release-varaman.yml @@ -0,0 +1,110 @@ +name: Release - Deploy Vara Man + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: vara-man + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/vara-man/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + VITE_NODE_ADDRESS=${{ secrets.VITE_NODE_ADDRESS }} + VITE_CONTRACT_ADDRESS=${{ secrets.VITE_CONTRACT_ADDRESS_VARA_MAN }} + VITE_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_VARA_MAN }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-varatube.yml b/.github/workflows/Release-varatube.yml new file mode 100644 index 000000000..ebf7b305d --- /dev/null +++ b/.github/workflows/Release-varatube.yml @@ -0,0 +1,112 @@ +name: Release - Deploy Varatube + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: varatube + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/varatube/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_IPFS_GATEWAY_ADDRESS=${{ secrets.REACT_APP_IPFS_GATEWAY_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_VARATUBE }} + REACT_APP_FT_CONTRACT_ADDRESS=${{ secrets.REACT_APP_FT_CONTRACT_ADDRESS_VARATUBE }} + REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_VARATUBE }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-w3bstreaming.yml b/.github/workflows/Release-w3bstreaming.yml new file mode 100644 index 000000000..d97b87e67 --- /dev/null +++ b/.github/workflows/Release-w3bstreaming.yml @@ -0,0 +1,113 @@ +name: Release - Deploy W3bstreaming FE + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: w3bstreaming + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: frontend/apps/w3bstreaming/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_W3BSTEAMING }} + REACT_APP_IPFS_GATEWAY_ADDRESS=${{ secrets.REACT_APP_IPFS_GATEWAY_ADDRESS }} + REACT_APP_IPFS_ADDRESS=${{ secrets.REACT_APP_IPFS_ADDRESS }} + REACT_APP_SIGNALING_SERVER=${{ secrets.REACT_APP_SIGNALING_SERVER_W3BSTEAMING }} + REACT_APP_BACKEND_SERVER=${{ secrets.REACT_APP_BACKEND_SERVER_W3BSTEAMING }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/Release-w3bstreaming_be.yml b/.github/workflows/Release-w3bstreaming_be.yml new file mode 100644 index 000000000..35eac027c --- /dev/null +++ b/.github/workflows/Release-w3bstreaming_be.yml @@ -0,0 +1,111 @@ +name: Release - Deploy W3bstreaming backend + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ENVIRONMENT: prod + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} + KUBECTL_VERSION: 'v1.22.17' + KUBE_NAMESPACE: gear-dapps + KUBE_DEPLOYMENT_PREFIX: w3bstreaming-backend + REGISTRY: ghcr.io/${{ github.repository }} + +jobs: + prepair: + runs-on: ubuntu-latest + outputs: + image_name: ${{ steps.image.outputs.image_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get branch + id: branch + run: | + branch_name=${GITHUB_REF#refs/heads/} + echo "branch_name=$branch_name" >> $GITHUB_ENV + + - name: Get short SHA + id: sha + run: | + sha_short=$(git rev-parse --short HEAD) + echo "sha_short=$sha_short" >> $GITHUB_ENV + + - name: Set IMAGE_NAME + id: image + run: | + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} + echo "image_name=$image_name" >> $GITHUB_OUTPUT + + build-and-push-image: + needs: [prepair] + runs-on: ubuntu-latest + environment: prod + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the github container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + file: backend/w3bstreaming/Dockerfile + push: true + tags: ${{ needs.prepair.outputs.image_name }} + build-args: | + REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + WS_ADRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + PATH_TO_META=${{ secrets.PATH_TO_META }} + PROGRAM_ID=${{ secrets.REACT_APP_CONTRACT_ADDRESS_W3BSTEAMING }} + + deploy-to-k8s: + needs: [prepair, build-and-push-image] + runs-on: ubuntu-latest + environment: prod + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update deployment image + uses: kodermax/kubectl-aws-eks@main + with: + args: | + set image deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + ${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }}=${{ needs.prepair.outputs.image_name }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Restart deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout restart deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + -n ${{ env.KUBE_NAMESPACE }} + + - name: Check deployment + uses: kodermax/kubectl-aws-eks@main + with: + args: | + rollout status deployment/${{ env.KUBE_DEPLOYMENT_PREFIX }}-${{ env.ENVIRONMENT }} \ + --timeout=240s \ + -n ${{ env.KUBE_NAMESPACE }} diff --git a/.github/workflows/DeployBattleship.yml b/.github/workflows/STG-DeployBattleship.yml similarity index 89% rename from .github/workflows/DeployBattleship.yml rename to .github/workflows/STG-DeployBattleship.yml index f934ed9c0..a4d1f9480 100644 --- a/.github/workflows/DeployBattleship.yml +++ b/.github/workflows/STG-DeployBattleship.yml @@ -1,9 +1,9 @@ -name: Build and Deploy BattleShips +name: Staging - Deploy BattleShips on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/battleship/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -83,7 +83,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/escrow.yml b/.github/workflows/STG-escrow.yml similarity index 89% rename from .github/workflows/escrow.yml rename to .github/workflows/STG-escrow.yml index d77af4f3e..bf552f408 100644 --- a/.github/workflows/escrow.yml +++ b/.github/workflows/STG-escrow.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Escrow +name: Staging - Deploy Escrow on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/escrow/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/galactic-express.yml b/.github/workflows/STG-galactic-express.yml similarity index 88% rename from .github/workflows/galactic-express.yml rename to .github/workflows/STG-galactic-express.yml index 6e66ee67b..61ecd052e 100644 --- a/.github/workflows/galactic-express.yml +++ b/.github/workflows/STG-galactic-express.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Galactic Express +name: Staging - Deploy Galactic Express on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/galactic-express/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -81,7 +81,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/game-of-chance.yml b/.github/workflows/STG-game-of-chance.yml similarity index 89% rename from .github/workflows/game-of-chance.yml rename to .github/workflows/STG-game-of-chance.yml index 1aa9ac424..0df7e701d 100644 --- a/.github/workflows/game-of-chance.yml +++ b/.github/workflows/STG-game-of-chance.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Game Of Chance +name: Staging - Deploy Game Of Chance on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/game-of-chance/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/nft-marketplace.yml b/.github/workflows/STG-nft-marketplace.yml similarity index 90% rename from .github/workflows/nft-marketplace.yml rename to .github/workflows/STG-nft-marketplace.yml index f62904cd9..f74a365ac 100644 --- a/.github/workflows/nft-marketplace.yml +++ b/.github/workflows/STG-nft-marketplace.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Marketplace +name: Staging - Deploy Marketplace on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/nft-marketplace/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -85,7 +85,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/non-fungible-token.yml b/.github/workflows/STG-non-fungible-token.yml similarity index 89% rename from .github/workflows/non-fungible-token.yml rename to .github/workflows/STG-non-fungible-token.yml index 3956e3204..5e5af2445 100644 --- a/.github/workflows/non-fungible-token.yml +++ b/.github/workflows/STG-non-fungible-token.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Non Fungible Token +name: Staging - Deploy Non Fungible Token on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/non-fungible-token/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -84,7 +84,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/racing-game.yml b/.github/workflows/STG-racing-game.yml similarity index 89% rename from .github/workflows/racing-game.yml rename to .github/workflows/STG-racing-game.yml index acb2c47fc..1309473f6 100644 --- a/.github/workflows/racing-game.yml +++ b/.github/workflows/STG-racing-game.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Racing Game +name: Staging - Deploy Racing Game on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/racing-car-game/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/simple-nft.yml b/.github/workflows/STG-simple-nft.yml similarity index 89% rename from .github/workflows/simple-nft.yml rename to .github/workflows/STG-simple-nft.yml index 9f75ebb05..1a910265d 100644 --- a/.github/workflows/simple-nft.yml +++ b/.github/workflows/STG-simple-nft.yml @@ -1,9 +1,9 @@ -name: Build and Deploy simple-nft +name: Staging - Deploy simple-nft on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/nft-master/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -85,7 +85,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/STG-supply-chain.yml similarity index 89% rename from .github/workflows/supply-chain.yml rename to .github/workflows/STG-supply-chain.yml index 0e9d537da..4a8b8678e 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/STG-supply-chain.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Supply Chain +name: Staging - Deploy Supply Chain on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/supply-chain/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/syndote.yml b/.github/workflows/STG-syndote.yml similarity index 89% rename from .github/workflows/syndote.yml rename to .github/workflows/STG-syndote.yml index d5f982e93..8cc16841b 100644 --- a/.github/workflows/syndote.yml +++ b/.github/workflows/STG-syndote.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Syndote +name: Staging - Deploy Syndote on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/syndote/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/tamagochi-battle.yml b/.github/workflows/STG-tamagochi-battle.yml similarity index 89% rename from .github/workflows/tamagochi-battle.yml rename to .github/workflows/STG-tamagochi-battle.yml index c7d8ccbd7..6439af23d 100644 --- a/.github/workflows/tamagochi-battle.yml +++ b/.github/workflows/STG-tamagochi-battle.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Tamagotchi Battle +name: Staging - Deploy Tamagotchi Battle on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/tamagotchi-battle/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -83,7 +83,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/tamagotchi.yml b/.github/workflows/STG-tamagotchi.yml similarity index 90% rename from .github/workflows/tamagotchi.yml rename to .github/workflows/STG-tamagotchi.yml index e8a527db2..1e59c6d32 100644 --- a/.github/workflows/tamagotchi.yml +++ b/.github/workflows/STG-tamagotchi.yml @@ -1,4 +1,4 @@ -name: Build and Deploy Tamagotchi +name: Staging - Deploy Tamagotchi on: workflow_dispatch: @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -81,7 +81,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/tequila-train.yml b/.github/workflows/STG-tequila-train.yml similarity index 89% rename from .github/workflows/tequila-train.yml rename to .github/workflows/STG-tequila-train.yml index e6dadf405..fadbd4599 100644 --- a/.github/workflows/tequila-train.yml +++ b/.github/workflows/STG-tequila-train.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Tequila Train +name: Staging - Deploy Tequila Train on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/tequila-train/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/tictac.yml b/.github/workflows/STG-tictac.yml similarity index 89% rename from .github/workflows/tictac.yml rename to .github/workflows/STG-tictac.yml index c0e958b39..df0dbd631 100644 --- a/.github/workflows/tictac.yml +++ b/.github/workflows/STG-tictac.yml @@ -1,9 +1,9 @@ -name: Build and Deploy TicTac +name: Staging - Deploy TicTac on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/tic-tac-toe/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/varaman.yml b/.github/workflows/STG-varaman.yml similarity index 89% rename from .github/workflows/varaman.yml rename to .github/workflows/STG-varaman.yml index 3fa2b50e6..08a8e6a66 100644 --- a/.github/workflows/varaman.yml +++ b/.github/workflows/STG-varaman.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Vara Man +name: Staging - Deploy Vara Man on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/vara-man/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/varatube.yml b/.github/workflows/STG-varatube.yml similarity index 89% rename from .github/workflows/varatube.yml rename to .github/workflows/STG-varatube.yml index c39b8741f..caf842b24 100644 --- a/.github/workflows/varatube.yml +++ b/.github/workflows/STG-varatube.yml @@ -1,9 +1,9 @@ -name: Build and Deploy Varatube +name: Staging - Deploy Varatube on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/varatube/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -84,7 +84,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/w3bstreaming.yml b/.github/workflows/STG-w3bstreaming.yml similarity index 89% rename from .github/workflows/w3bstreaming.yml rename to .github/workflows/STG-w3bstreaming.yml index 3d4377bdc..9e473848d 100644 --- a/.github/workflows/w3bstreaming.yml +++ b/.github/workflows/STG-w3bstreaming.yml @@ -1,9 +1,9 @@ -name: Build and Deploy W3bstreaming +name: Staging - Deploy W3bstreaming FE on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - frontend/apps/w3bstreaming/** - frontend/packages/** @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -53,7 +53,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -85,7 +85,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/w3bstreaming_be.yml b/.github/workflows/STG-w3bstreaming_be.yml similarity index 89% rename from .github/workflows/w3bstreaming_be.yml rename to .github/workflows/STG-w3bstreaming_be.yml index eed0c6fc4..4f215c48c 100644 --- a/.github/workflows/w3bstreaming_be.yml +++ b/.github/workflows/STG-w3bstreaming_be.yml @@ -1,9 +1,9 @@ -name: Build and Deploy W3bstreaming backend +name: Staging - Deploy W3bstreaming backend on: workflow_dispatch: push: - branches: ['master', 'main', 'staging'] + branches: ['master', 'main'] paths: - backend/w3bstreaming/** @@ -12,7 +12,7 @@ concurrency: cancel-in-progress: true env: - ENVIRONMENT: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + ENVIRONMENT: stg AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_REGION }} @@ -52,7 +52,7 @@ jobs: build-and-push-image: needs: [prepair] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg permissions: contents: read packages: write @@ -82,7 +82,7 @@ jobs: deploy-to-k8s: needs: [prepair, build-and-push-image] runs-on: ubuntu-latest - environment: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && 'prod' || 'stg' }} + environment: stg steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 From f37eea32ff2d51f89df1dc7befea33273d4575a7 Mon Sep 17 00:00:00 2001 From: Nikita Yutanov Date: Mon, 19 Feb 2024 18:06:59 +0300 Subject: [PATCH 02/17] Release 1.1.0 (#254) Co-authored-by: Yuriy <43875549+faizov@users.noreply.github.com> Co-authored-by: YauheniDraichykau <64776571+YauheniDraichykau@users.noreply.github.com> Co-authored-by: Yauheni Co-authored-by: Dmitriy <128726599+Magorsky@users.noreply.github.com> Co-authored-by: magorsky Co-authored-by: sergey filyanin Co-authored-by: yuriy Co-authored-by: Yauheni Draichykau --- frontend/apps/battleship/package.json | 20 +- .../apps/battleship/src/app/hocs/index.tsx | 18 +- .../components/game-process/game-process.tsx | 18 +- .../ship-arrangement/ship-arrangement.tsx | 18 +- .../battleship/src/features/game/utils.ts | 8 - .../battleship/src/features/wallet/hooks.ts | 68 +- frontend/apps/escrow/package.json | 8 +- frontend/apps/galactic-express/package.json | 10 +- frontend/apps/galactic-express/src/App.scss | 2 +- frontend/apps/galactic-express/src/App.tsx | 1 + .../src/assets/images/icons/tvara-coin.svg | 19 + .../src/assets/images/icons/vara-coin.svg | 11 + .../src/assets/meta/galactic_express_meta.txt | 2 +- frontend/apps/galactic-express/src/atoms.ts | 8 +- .../layout/TextField/TextField.interfaces.ts | 9 + .../layout/TextField/TextField.module.scss | 114 +++ .../components/layout/TextField/TextField.tsx | 49 ++ .../src/components/layout/TextField/index.ts | 1 + .../layout/container/Container.module.scss | 1 - .../layout/header/Header.module.scss | 83 +- .../src/components/layout/header/Header.tsx | 56 +- .../components/layout/modal/Modal.module.scss | 64 ++ .../src/components/layout/modal/Modal.tsx | 69 ++ .../layout/modal/Modal.variants.tsx | 39 + .../src/components/layout/modal/index.ts | 3 + frontend/apps/galactic-express/src/consts.ts | 3 + .../session/assets/ic-user-small-24.svg | 3 + .../CancelGameButton.module.scss | 16 + .../cancel-game-button/CancelGameButton.tsx | 70 ++ .../components/cancel-game-button/index.ts | 3 + .../session/components/form/Form.module.scss | 1 - .../features/session/components/form/Form.tsx | 47 +- .../GameFoundModal.module.scss | 52 ++ .../game-found-modal/GameFoundModal.tsx | 113 +++ .../components/game-found-modal/index.ts | 3 + .../GameNotFoundModal.module.scss | 21 + .../GameNotFoundModal.tsx | 27 + .../components/game-not-found-modal/index.ts | 3 + .../ParticipantsTable.module.scss | 18 +- .../participants-table/ParticipantsTable.tsx | 55 +- .../session/components/radar/Radar.tsx | 11 +- .../SessionPassedInfo.module.scss | 11 + .../session-passed-info/SessionPassedInfo.tsx | 21 + .../components/session-passed-info/index.ts | 3 + .../session/components/session/Session.tsx | 13 +- .../components/start/Start.module.scss | 1 + .../session/components/start/Start.tsx | 67 +- .../components/table/Table.module.scss | 16 +- .../session/components/table/Table.tsx | 5 +- .../components/win-status/WinStatus.tsx | 34 +- .../src/features/session/consts.ts | 3 +- .../src/features/session/hooks.ts | 28 +- .../src/features/session/types.ts | 58 +- .../EnterContractAddress.tsx | 94 --- ...ss.module.scss => RequestGame.module.scss} | 33 +- .../enter-contract-address/RequestGame.tsx | 274 +++++++ .../enter-contract-address/index.ts | 4 +- .../game-intro/GameIntro.module.scss | 15 +- .../components/game-intro/GameIntro.tsx | 25 +- .../components/welcome/Welcome.module.scss | 2 +- .../welcome/components/welcome/Welcome.tsx | 6 +- .../galactic-express/src/pages/home/Home.tsx | 45 +- frontend/apps/game-of-chance/package.json | 8 +- frontend/apps/nft-marketplace/package.json | 8 +- frontend/apps/nft-master/package.json | 8 +- .../src/components/ui/alert/alert.module.scss | 4 +- frontend/apps/non-fungible-token/package.json | 8 +- frontend/apps/racing-car-game/package.json | 8 +- frontend/apps/supply-chain/package.json | 8 +- frontend/apps/syndote/package.json | 8 +- frontend/apps/tamagotchi-battle/package.json | 23 +- .../apps/tamagotchi-battle/src/app/consts.ts | 2 + .../tamagotchi-battle/src/app/hocs/index.tsx | 19 +- .../battle-round-players.tsx | 27 +- .../battle-wait-admin/battle-wait-admin.tsx | 19 +- .../create-tamagotchi-form.tsx | 21 +- .../new-game-button/new-game-button.tsx | 19 +- .../src/features/battle/hooks/use-battle.ts | 6 +- .../battle/utils/init-gasless-transactions.ts | 8 - .../src/features/wallet/hooks.ts | 51 -- frontend/apps/tamagotchi/package.json | 8 +- frontend/apps/tequila-train/package.json | 42 +- .../tequila-train/public/sprites/icons.svg | 760 +++++++++++------- frontend/apps/tequila-train/src/App.tsx | 2 + frontend/apps/tequila-train/src/app/consts.ts | 7 + .../tequila-train/src/app/context/ctx-app.tsx | 3 + .../src/app/context/ctx-game.tsx | 24 +- .../apps/tequila-train/src/app/hocs/index.tsx | 14 +- .../tequila-train/src/app/hooks/use-game.ts | 131 +-- .../src/app/hooks/use-metadata.ts | 33 +- .../src/app/hooks/use-ref-dimensions.ts | 2 +- .../apps/tequila-train/src/app/types/game.ts | 65 +- .../apps/tequila-train/src/app/types/index.ts | 35 +- .../apps/tequila-train/src/app/utils/index.ts | 40 +- .../src/assets/images/cactus.png | Bin 0 -> 444320 bytes .../tequila-train/src/assets/images/index.ts | 1 + .../src/assets/meta/tequila_state.meta.wasm | Bin 130467 -> 0 bytes .../src/assets/meta/tequila_train.meta.txt | 2 +- .../src/assets/meta/tequila_train.opt.wasm | Bin 0 -> 106284 bytes .../src/assets/styles/_mixins.scss | 90 +++ .../src/assets/styles/_resets.scss | 45 ++ .../src/assets/styles/_variables.scss | 44 + .../src/assets/styles/global.scss | 14 + .../common/domino-zone/domino-zone.tsx | 12 +- .../common/player-train/player-train.tsx | 19 +- .../layout/header/account/account.tsx | 36 - .../components/layout/header/account/index.ts | 1 - .../layout/header/header.module.scss | 48 ++ .../src/components/layout/header/header.tsx | 87 +- .../layout/header/logo/logo.module.scss | 43 + .../components/layout/header/logo/logo.tsx | 33 +- .../src/components/popups/popup-container.tsx | 80 +- .../popups/winner-popup/winner-popup.tsx | 81 +- .../canceled-modal/canceled-modal.tsx | 27 + .../game-section/canceled-modal/index.ts | 1 + .../finished-modal/finished-modal.tsx | 12 + .../game-section/finished-modal/index.ts | 1 + .../sections/game-section/game-section.tsx | 63 +- .../sections/game-section/mock/mock-data.json | 553 +++++++++++++ .../game-section/mock/mock-game-section.tsx | 117 +++ .../src/components/sections/index.ts | 15 + .../sections/login-section/login-section.tsx | 39 +- .../player-card-section.tsx | 19 +- .../player-cons-section.tsx | 142 +++- .../player-track-section.tsx | 62 +- .../sections/player-track-section/timer.tsx | 31 + .../modal/Modal.module.scss | 52 ++ .../registration-section/modal/Modal.tsx | 44 + .../registration-section/modal/index.ts | 3 + .../modal/modal.variants.ts | 39 + .../registration-form.tsx | 11 +- .../registration-section.tsx | 195 ++++- .../sections/start-section/create-game.tsx | 78 ++ .../sections/start-section/find-game.tsx | 132 +++ .../sections/start-section/index.ts | 1 + .../start-section/start-secrtion.module.scss | 21 + .../sections/start-section/start-section.tsx | 48 ++ .../components/ui/Button/Button.interfaces.ts | 9 + .../components/ui/Button/Button.module.scss | 76 ++ .../src/components/ui/Button/Button.tsx | 49 ++ .../src/components/ui/Button/index.ts | 1 + .../src/components/ui/alert/alert.module.scss | 100 +++ .../src/components/ui/alert/alert.tsx | 23 + .../src/components/ui/alert/alert.types.ts | 20 + .../src/components/ui/alert/index.ts | 2 + .../tequila-train/src/components/ui/index.ts | 1 + .../src/components/ui/modal/Modal.module.scss | 51 ++ .../src/components/ui/modal/Modal.tsx | 66 ++ .../src/components/ui/modal/index.ts | 3 + .../src/components/ui/modal/modal.variants.ts | 39 + .../src/components/ui/sprite.tsx | 15 + .../src/components/ui/text-gradient/index.ts | 3 + .../text-gradient/text-gradient.module.scss | 8 + .../ui/text-gradient/text-gradient.tsx | 6 + frontend/apps/tequila-train/src/index.css | 64 +- .../apps/tequila-train/src/pages/home.tsx | 58 +- .../apps/tequila-train/tailwind.config.js | 13 - frontend/apps/tic-tac-toe/package.json | 18 +- frontend/apps/tic-tac-toe/src/app.tsx | 1 + .../layout/header/header.module.scss | 2 +- .../src/components/layout/header/header.tsx | 32 +- .../layout/header/mobile-menu/index.ts | 3 - .../header/mobile-menu/mobile-menu-dialog.tsx | 98 --- .../mobile-menu/mobile-menu.module.scss | 164 ---- .../layout/header/mobile-menu/mobile-menu.tsx | 18 - .../src/components/ui/balance/Balance.tsx | 13 - .../src/components/ui/balance/index.ts | 4 +- .../tic-tac-toe/src/components/ui/dialogs.ts | 19 - .../src/features/wallet/components/index.ts | 3 - .../wallet/components/wallet-icon/index.ts | 3 - .../components/wallet-icon/polkadot-icon.tsx | 25 - .../wallet/components/wallet-icon/types.ts | 11 - .../wallet/components/wallet-icon/utils.tsx | 171 ---- .../components/wallet-icon/wallet-icon.tsx | 11 - .../wallet-item/WalletItem.module.scss | 18 - .../components/wallet-item/WalletItem.tsx | 18 - .../wallet/components/wallet-item/index.ts | 3 - .../wallet-modal/WalletModal.module.scss | 227 ------ .../components/wallet-modal/WalletModal.tsx | 218 ----- .../wallet/components/wallet-modal/index.ts | 2 - .../components/wallet/Wallet.module.scss | 27 - .../wallet/components/wallet/Wallet.tsx | 41 - .../wallet/components/wallet/index.ts | 1 - .../tic-tac-toe/src/features/wallet/index.ts | 1 - frontend/apps/tic-tac-toe/src/pages/home.tsx | 2 +- frontend/apps/tic-tac-toe/vite.config.ts | 33 +- frontend/apps/vara-man/package.json | 6 +- frontend/apps/varatube/package.json | 12 +- frontend/apps/varatube/src/App.tsx | 7 +- .../layout/header/Header.module.scss | 24 +- .../src/components/layout/header/Header.tsx | 42 +- .../PurchaseSubscriptionApproveModal.tsx | 6 +- .../PurchaseSubscriptionModal.tsx | 6 +- frontend/apps/varatube/src/hocs/index.tsx | 3 +- frontend/apps/varatube/src/hooks/index.ts | 149 +--- .../src/pages/subscription/Subscription.tsx | 76 +- .../apps/w3bstreaming/config-overrides.js | 17 + frontend/apps/w3bstreaming/craco.config.js | 23 - frontend/apps/w3bstreaming/package.json | 29 +- .../src/components/BurgerMenu/BurgerMenu.tsx | 4 +- .../src/components/Header/Header.module.scss | 26 +- .../src/components/Header/Header.tsx | 92 +-- .../w3bstreaming/src/features/Auth/hooks.ts | 85 -- .../StreamTeaser/StreamTeaser.module.scss | 2 +- .../WalletInfo/WalletInfo.interfaces.ts | 7 - .../WalletInfo/WalletInfo.module.scss | 92 --- .../components/WalletInfo/WalletInfo.tsx | 74 -- .../Wallet/components/WalletInfo/index.ts | 1 - .../WalletItem/WalletItem.interfaces.ts | 4 - .../WalletItem/WalletItem.module.scss | 19 - .../components/WalletItem/WalletItem.tsx | 14 - .../Wallet/components/WalletItem/index.ts | 1 - .../WalletModal/WalletModal.interface.ts | 5 - .../WalletModal/WalletModal.module.scss | 266 ------ .../components/WalletModal/WalletModal.tsx | 214 ----- .../Wallet/components/WalletModal/index.ts | 1 - .../Wallet/components/account-icon.tsx | 14 - .../src/features/Wallet/components/index.ts | 3 - .../src/features/Wallet/consts.ts | 28 - .../w3bstreaming/src/features/Wallet/hooks.ts | 40 +- .../IntrodutionInfo/IntrodutionInfo.tsx | 3 +- .../packages/gasless-transactions/README.md | 32 +- .../gasless-transactions/package.json | 7 +- .../gasless-transactions/src/atoms.ts | 5 - .../src/context/consts.ts | 4 + .../src/context/index.tsx | 115 +++ .../gasless-transactions/src/context/types.ts | 6 + .../gasless-transactions/src/hooks/index.ts | 1 - .../src/hooks/use-fetch-voucher.ts | 107 --- .../gasless-transactions/src/index.ts | 7 +- frontend/packages/hooks/package.json | 11 +- frontend/packages/hooks/src/consts.ts | 1 + frontend/packages/hooks/src/hooks/index.ts | 4 +- .../hooks/src/hooks/use-calculate-gas.ts | 32 + .../hooks/src/hooks/use-check-balance.tsx | 45 ++ frontend/packages/hooks/src/index.ts | 5 +- .../hooks/src/providers/balance-provider.tsx | 24 + .../packages/hooks/src/providers/index.ts | 3 + frontend/packages/hooks/vite.config.ts | 2 +- .../signless-transactions/package.json | 5 +- .../create-session-modal.tsx | 9 +- .../signless-params-list/signless-params.tsx | 2 +- .../signless-transactions.tsx | 17 +- .../src/context/consts.ts | 3 + .../src/context/index.tsx | 25 +- .../src/context/types.ts | 4 +- .../src/hooks/use-batch-sign-and-send.ts | 4 +- .../src/hooks/use-create-session.ts | 87 +- .../hooks/use-get-extrinsic-failed-error.ts | 4 +- .../src/hooks/use-signless-send-message.ts | 10 +- .../signless-transactions/src/utils.ts | 32 +- .../signless-transactions/vite.config.ts | 3 +- frontend/packages/ui/package.json | 3 +- .../src/components/header/assets/discord.svg | 3 + .../src/components/header/assets/github.svg | 3 + .../src/components/header/assets/medium.svg | 3 + .../src/components/header/assets/twitter.svg | 3 + .../ui/src/components/header/assets/user.svg | 3 + .../ui/src/components/header/consts.ts | 13 + .../src/components/header/header.module.css | 19 + .../ui/src/components/header/header.tsx | 26 + .../ui/src/components/header/index.ts | 3 + frontend/packages/ui/src/components/index.ts | 3 +- .../menu-handler/menu-handler.module.css | 13 +- .../components/menu-handler/menu-handler.tsx | 11 +- .../menu-options/menu-options.module.css | 3 +- .../components/mobile-menu/mobile-menu.tsx | 36 +- .../features/wallet-new/assets/tvara-coin.svg | 19 + .../features/wallet-new/assets/vara-coin.svg | 11 + .../src/features/wallet-new/assets/vara.svg | 19 - .../vara-balance/vara-balance.module.css | 2 +- .../components/vara-balance/vara-balance.tsx | 23 +- .../wallet-modal/wallet-modal.module.css | 1 + .../wallet-new/components/wallet/index.ts | 5 +- .../wallet-new/components/wallet/wallet.tsx | 8 +- .../ui/src/features/wallet-new/hooks/index.ts | 3 +- .../hooks/use-free-account-balance.ts | 26 - frontend/packages/ui/src/index.ts | 4 +- .../templates/gear-app-starter/package.json | 8 +- frontend/yarn.lock | 748 ++++------------- 280 files changed, 5814 insertions(+), 4679 deletions(-) create mode 100644 frontend/apps/galactic-express/src/assets/images/icons/tvara-coin.svg create mode 100644 frontend/apps/galactic-express/src/assets/images/icons/vara-coin.svg create mode 100644 frontend/apps/galactic-express/src/components/layout/TextField/TextField.interfaces.ts create mode 100644 frontend/apps/galactic-express/src/components/layout/TextField/TextField.module.scss create mode 100644 frontend/apps/galactic-express/src/components/layout/TextField/TextField.tsx create mode 100644 frontend/apps/galactic-express/src/components/layout/TextField/index.ts create mode 100644 frontend/apps/galactic-express/src/components/layout/modal/Modal.module.scss create mode 100644 frontend/apps/galactic-express/src/components/layout/modal/Modal.tsx create mode 100644 frontend/apps/galactic-express/src/components/layout/modal/Modal.variants.tsx create mode 100644 frontend/apps/galactic-express/src/components/layout/modal/index.ts create mode 100644 frontend/apps/galactic-express/src/features/session/assets/ic-user-small-24.svg create mode 100644 frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss create mode 100644 frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/cancel-game-button/index.ts create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.module.scss create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-found-modal/index.ts create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts create mode 100644 frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.module.scss create mode 100644 frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/session-passed-info/index.ts delete mode 100644 frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/EnterContractAddress.tsx rename frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/{EnterContractAddress.module.scss => RequestGame.module.scss} (52%) create mode 100644 frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx delete mode 100644 frontend/apps/tamagotchi-battle/src/features/battle/utils/init-gasless-transactions.ts delete mode 100644 frontend/apps/tamagotchi-battle/src/features/wallet/hooks.ts create mode 100644 frontend/apps/tequila-train/src/assets/images/cactus.png create mode 100644 frontend/apps/tequila-train/src/assets/images/index.ts delete mode 100644 frontend/apps/tequila-train/src/assets/meta/tequila_state.meta.wasm create mode 100644 frontend/apps/tequila-train/src/assets/meta/tequila_train.opt.wasm create mode 100644 frontend/apps/tequila-train/src/assets/styles/_mixins.scss create mode 100644 frontend/apps/tequila-train/src/assets/styles/_resets.scss create mode 100644 frontend/apps/tequila-train/src/assets/styles/_variables.scss create mode 100644 frontend/apps/tequila-train/src/assets/styles/global.scss delete mode 100644 frontend/apps/tequila-train/src/components/layout/header/account/account.tsx delete mode 100644 frontend/apps/tequila-train/src/components/layout/header/account/index.ts create mode 100644 frontend/apps/tequila-train/src/components/layout/header/header.module.scss create mode 100644 frontend/apps/tequila-train/src/components/layout/header/logo/logo.module.scss create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/canceled-modal/canceled-modal.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/canceled-modal/index.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/finished-modal/finished-modal.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/finished-modal/index.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/mock/mock-data.json create mode 100644 frontend/apps/tequila-train/src/components/sections/game-section/mock/mock-game-section.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/index.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/player-track-section/timer.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.module.scss create mode 100644 frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/registration-section/modal/index.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/registration-section/modal/modal.variants.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/start-section/create-game.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/start-section/find-game.tsx create mode 100644 frontend/apps/tequila-train/src/components/sections/start-section/index.ts create mode 100644 frontend/apps/tequila-train/src/components/sections/start-section/start-secrtion.module.scss create mode 100644 frontend/apps/tequila-train/src/components/sections/start-section/start-section.tsx create mode 100644 frontend/apps/tequila-train/src/components/ui/Button/Button.interfaces.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/Button/Button.module.scss create mode 100644 frontend/apps/tequila-train/src/components/ui/Button/Button.tsx create mode 100644 frontend/apps/tequila-train/src/components/ui/Button/index.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/alert/alert.module.scss create mode 100644 frontend/apps/tequila-train/src/components/ui/alert/alert.tsx create mode 100644 frontend/apps/tequila-train/src/components/ui/alert/alert.types.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/alert/index.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/index.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/modal/Modal.module.scss create mode 100644 frontend/apps/tequila-train/src/components/ui/modal/Modal.tsx create mode 100644 frontend/apps/tequila-train/src/components/ui/modal/index.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/modal/modal.variants.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/sprite.tsx create mode 100644 frontend/apps/tequila-train/src/components/ui/text-gradient/index.ts create mode 100644 frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.module.scss create mode 100644 frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu-dialog.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.module.scss delete mode 100644 frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/components/ui/dialogs.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/polkadot-icon.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/types.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/utils.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/wallet-icon.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.module.scss delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.module.scss delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.module.scss delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.tsx delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/index.ts delete mode 100644 frontend/apps/tic-tac-toe/src/features/wallet/index.ts create mode 100644 frontend/apps/w3bstreaming/config-overrides.js delete mode 100644 frontend/apps/w3bstreaming/craco.config.js delete mode 100644 frontend/apps/w3bstreaming/src/features/Auth/hooks.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.interfaces.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.module.scss delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.tsx delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/index.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletItem/WalletItem.interfaces.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletItem/WalletItem.module.scss delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletItem/WalletItem.tsx delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletItem/index.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/WalletModal.interface.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/WalletModal.module.scss delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/WalletModal.tsx delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/index.ts delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/account-icon.tsx delete mode 100644 frontend/apps/w3bstreaming/src/features/Wallet/components/index.ts delete mode 100644 frontend/packages/gasless-transactions/src/atoms.ts create mode 100644 frontend/packages/gasless-transactions/src/context/consts.ts create mode 100644 frontend/packages/gasless-transactions/src/context/index.tsx create mode 100644 frontend/packages/gasless-transactions/src/context/types.ts delete mode 100644 frontend/packages/gasless-transactions/src/hooks/index.ts delete mode 100644 frontend/packages/gasless-transactions/src/hooks/use-fetch-voucher.ts create mode 100644 frontend/packages/hooks/src/consts.ts create mode 100644 frontend/packages/hooks/src/hooks/use-calculate-gas.ts create mode 100644 frontend/packages/hooks/src/hooks/use-check-balance.tsx create mode 100644 frontend/packages/hooks/src/providers/balance-provider.tsx create mode 100644 frontend/packages/hooks/src/providers/index.ts create mode 100644 frontend/packages/ui/src/components/header/assets/discord.svg create mode 100644 frontend/packages/ui/src/components/header/assets/github.svg create mode 100644 frontend/packages/ui/src/components/header/assets/medium.svg create mode 100644 frontend/packages/ui/src/components/header/assets/twitter.svg create mode 100644 frontend/packages/ui/src/components/header/assets/user.svg create mode 100644 frontend/packages/ui/src/components/header/consts.ts create mode 100644 frontend/packages/ui/src/components/header/header.module.css create mode 100644 frontend/packages/ui/src/components/header/header.tsx create mode 100644 frontend/packages/ui/src/components/header/index.ts create mode 100644 frontend/packages/ui/src/features/wallet-new/assets/tvara-coin.svg create mode 100644 frontend/packages/ui/src/features/wallet-new/assets/vara-coin.svg delete mode 100644 frontend/packages/ui/src/features/wallet-new/assets/vara.svg delete mode 100644 frontend/packages/ui/src/features/wallet-new/hooks/use-free-account-balance.ts diff --git a/frontend/apps/battleship/package.json b/frontend/apps/battleship/package.json index f01ab9872..4b678e895 100644 --- a/frontend/apps/battleship/package.json +++ b/frontend/apps/battleship/package.json @@ -14,20 +14,23 @@ "@dapps-frontend/hooks": "workspace:*", "@dapps-frontend/signless-transactions": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@gear-js/vara-ui": "0.0.6", "@mantine/form": "6.0.15", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", - "@polkadot/react-identicon": "3.5.1", - "@polkadot/types": "10.10.1", + "@polkadot/react-identicon": "3.1.4", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@radix-ui/react-dropdown-menu": "2.0.5", "@radix-ui/react-scroll-area": "1.0.4", "@radix-ui/react-select": "1.2.2", "@tanstack/react-table": "8.9.3", + "@types/node": "18.16.19", + "@types/react": "18.2.33", + "@types/react-dom": "18.2.14", "buffer": "6.0.3", "class-variance-authority": "0.6.1", "clsx": "1.2.1", @@ -39,12 +42,10 @@ "react-dom": "18.2.0", "react-hook-form": "7.48.2", "react-router-dom": "6.10.0", - "react-transition-group": "4.4.5" + "react-transition-group": "4.4.5", + "sass": "1.62.0" }, "devDependencies": { - "@types/node": "18.16.19", - "@types/react": "18.2.33", - "@types/react-dom": "18.2.14", "@vitejs/plugin-react-swc": "3.3.2", "autoprefixer": "10.4.14", "eslint": "8.43.0", @@ -52,7 +53,6 @@ "node-stdlib-browser": "1.2.0", "postcss": "8.4.24", "prettier": "2.8.8", - "sass": "1.62.0", "tailwindcss": "3.3.2", "typescript": "4.9.5", "vite": "4.3.9", diff --git a/frontend/apps/battleship/src/app/hocs/index.tsx b/frontend/apps/battleship/src/app/hocs/index.tsx index 2503a395a..b23d178c8 100644 --- a/frontend/apps/battleship/src/app/hocs/index.tsx +++ b/frontend/apps/battleship/src/app/hocs/index.tsx @@ -8,6 +8,7 @@ import { ComponentType } from 'react'; import { BrowserRouter } from 'react-router-dom'; import { SignlessTransactionsProvider as SharedSignlessTransactionsProvider } from '@dapps-frontend/signless-transactions'; +import { GaslessTransactionsProvider as SharedGaslessTransactionsProvider } from '@dapps-frontend/gasless-transactions'; import metaTxt from '@/features/game/assets/meta/battleship.meta.txt'; import { ADDRESS } from '@/app/consts'; @@ -25,6 +26,14 @@ function AlertProvider({ children }: ProviderProps) { ); } +function GaslessTransactionsProvider({ children }: ProviderProps) { + return ( + + {children} + + ); +} + function SignlessTransactionsProvider({ children }: ProviderProps) { return ( @@ -33,7 +42,14 @@ function SignlessTransactionsProvider({ children }: ProviderProps) { ); } -const providers = [BrowserRouter, ApiProvider, AccountProvider, AlertProvider, SignlessTransactionsProvider]; +const providers = [ + BrowserRouter, + ApiProvider, + AccountProvider, + AlertProvider, + GaslessTransactionsProvider, + SignlessTransactionsProvider, +]; function withProviders(Component: ComponentType) { return () => providers.reduceRight((children, Provider) => {children}, ); diff --git a/frontend/apps/battleship/src/features/game/components/game-process/game-process.tsx b/frontend/apps/battleship/src/features/game/components/game-process/game-process.tsx index 96c7a0b69..0b995d657 100644 --- a/frontend/apps/battleship/src/features/game/components/game-process/game-process.tsx +++ b/frontend/apps/battleship/src/features/game/components/game-process/game-process.tsx @@ -1,16 +1,18 @@ import { useEffect, useState } from 'react'; +import { useSignlessTransactions } from '@dapps-frontend/signless-transactions'; import { Text } from '@/components/ui/text'; import { GameEndModal, Map } from '@/features/game'; import styles from './GameProcess.module.scss'; import { MapEnemy } from '../map'; import { useGame, useGameMessage, usePending } from '../../hooks'; -import { getFormattedTime, useFetchVoucher } from '../../utils'; +import { getFormattedTime } from '../../utils'; import { Loader } from '@/components'; -import { useCheckBalance } from '@/features/wallet/hooks'; +import { useCheckBalance } from '@dapps-frontend/hooks'; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; export default function GameProcess() { - const { isVoucher, isLoading } = useFetchVoucher(); - + const { voucherId, isLoadingVoucher } = useGaslessTransactions(); + const { pairVoucherId } = useSignlessTransactions(); const [playerShips, setPlayerShips] = useState([]); const [enemiesShips, setEnemiesShips] = useState([]); const [elapsedTime, setElapsedTime] = useState(''); @@ -20,7 +22,7 @@ export default function GameProcess() { const { gameState } = useGame(); const { setPending } = usePending(); const message = useGameMessage(); - const { checkBalance } = useCheckBalance(isVoucher); + const { checkBalance } = useCheckBalance({ signlessPairVoucherId: pairVoucherId, gaslessVoucherId: voucherId }); const [isOpenEndModal, setIsOpenEndModal] = useState(false); const openEndModal = () => setIsOpenEndModal(true); @@ -70,7 +72,7 @@ export default function GameProcess() { const onClickCell = async (indexCell: number) => { const gasLimit = 120000000000; - if (!isLoading) { + if (!isLoadingVoucher) { setDisabledCell(true); checkBalance(gasLimit, () => @@ -82,7 +84,7 @@ export default function GameProcess() { } }, gasLimit, - withVoucher: isVoucher, + voucherId, onSuccess: () => { setPending(false); }, @@ -158,7 +160,7 @@ export default function GameProcess() { sizeBlock={68} onClickCell={onClickCell} shipStatusArray={enemiesShips} - isDisabledCell={isDisabledCell || isLoading} + isDisabledCell={isDisabledCell || isLoadingVoucher} /> diff --git a/frontend/apps/battleship/src/features/game/components/ship-arrangement/ship-arrangement.tsx b/frontend/apps/battleship/src/features/game/components/ship-arrangement/ship-arrangement.tsx index de3de1cb0..155e2d9fe 100644 --- a/frontend/apps/battleship/src/features/game/components/ship-arrangement/ship-arrangement.tsx +++ b/frontend/apps/battleship/src/features/game/components/ship-arrangement/ship-arrangement.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useSignlessTransactions } from '@dapps-frontend/signless-transactions'; import { Button } from '@gear-js/vara-ui'; import { Heading } from '@/components/ui/heading'; import { TextGradient } from '@/components/ui/text-gradient'; @@ -7,15 +8,16 @@ import { Map } from '../'; import styles from './ShipArrangement.module.scss'; import { useGameMessage, usePending } from '../../hooks'; import { generateShipsField } from './shipGenerator'; -import { convertShipsToField, useFetchVoucher } from '../../utils'; -import { useCheckBalance } from '@/features/wallet/hooks'; +import { convertShipsToField } from '../../utils'; +import { useCheckBalance } from '@dapps-frontend/hooks'; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; export default function ShipArrangement() { - const { isVoucher, isLoading } = useFetchVoucher(); - + const { voucherId, isLoadingVoucher } = useGaslessTransactions(); + const { pairVoucherId } = useSignlessTransactions(); const message = useGameMessage(); const { setPending } = usePending(); - const { checkBalance } = useCheckBalance(isVoucher); + const { checkBalance } = useCheckBalance({ signlessPairVoucherId: pairVoucherId, gaslessVoucherId: voucherId }); const [shipLayout, setShipLayout] = useState([]); const [shipsField, setShipsField] = useState([]); @@ -36,7 +38,7 @@ export default function ShipArrangement() { const onGameStart = async () => { const gasLimit = 120000000000; - if (!isLoading) { + if (!isLoadingVoucher) { setPending(true); checkBalance(gasLimit, () => @@ -46,7 +48,7 @@ export default function ShipArrangement() { ships: shipsField, }, }, - withVoucher: isVoucher, + voucherId, gasLimit, }), ); @@ -68,7 +70,7 @@ export default function ShipArrangement() {
); diff --git a/frontend/apps/battleship/src/features/game/utils.ts b/frontend/apps/battleship/src/features/game/utils.ts index ad7744346..7cfba1656 100644 --- a/frontend/apps/battleship/src/features/game/utils.ts +++ b/frontend/apps/battleship/src/features/game/utils.ts @@ -1,6 +1,3 @@ -import { ADDRESS } from '@/app/consts'; -import { initGasslessTransactions } from '@dapps-frontend/gasless-transactions'; - type ShipLayout = ('Empty' | 'Ship')[]; export const getShipLayout = (shipStatusArray: string[]): number[][] => { @@ -43,8 +40,3 @@ export const getFormattedTime = (time: number) => { return formattedTime; }; - -export const { useFetchVoucher } = initGasslessTransactions({ - programId: ADDRESS.GAME, - backendAddress: ADDRESS.BACK, -}); diff --git a/frontend/apps/battleship/src/features/wallet/hooks.ts b/frontend/apps/battleship/src/features/wallet/hooks.ts index 97844163e..72e6bc65a 100644 --- a/frontend/apps/battleship/src/features/wallet/hooks.ts +++ b/frontend/apps/battleship/src/features/wallet/hooks.ts @@ -1,25 +1,9 @@ -import { CreateType, decodeAddress } from '@gear-js/api'; -import { - useAccount, - useAlert, - useApi, - useBalance, - useBalanceFormat, - useVoucherBalance, - withoutCommas, -} from '@gear-js/react-hooks'; -import { formatBalance, stringShorten } from '@polkadot/util'; +import { CreateType } from '@gear-js/api'; +import { useAccount, useApi, useBalance } from '@gear-js/react-hooks'; +import { formatBalance } from '@polkadot/util'; import { useEffect, useState } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { useSignlessTransactions } from '@dapps-frontend/signless-transactions'; -import { ADDRESS } from '@/app/consts'; -import { - AVAILABLE_BALANCE, - IS_AVAILABLE_BALANCE_READY, - VOUCHER_MIN_LIMIT, - WALLET, - WALLET_ID_LOCAL_STORAGE_KEY, -} from './consts'; +import { AVAILABLE_BALANCE, IS_AVAILABLE_BALANCE_READY, WALLET, WALLET_ID_LOCAL_STORAGE_KEY } from './consts'; import { SystemAccount, WalletId } from './types'; function useWalletSync() { @@ -118,46 +102,4 @@ function useAccountAvailableBalanceSync() { }, [account, api, isAccountReady, isApiReady, isReady, balance]); } -function useCheckBalance(isVoucher: boolean) { - const { api } = useApi(); - - const { account } = useAccount(); - const { pair } = useSignlessTransactions(); - const accountAddress = pair ? decodeAddress(pair.address) : account?.decodedAddress; - const { voucherBalance } = useVoucherBalance(ADDRESS.GAME, accountAddress); - - const { availableBalance } = useAccountAvailableBalance(); - const { getChainBalanceValue } = useBalanceFormat(); - const { getFormattedBalanceValue } = useBalanceFormat(); - const alert = useAlert(); - - const checkBalance = (limit: number, callback: () => void, onError?: () => void) => { - const chainBalance = Number(getChainBalanceValue(Number(withoutCommas(availableBalance?.value || ''))).toFixed()); - const valuePerGas = Number(withoutCommas(api!.valuePerGas!.toHuman())); - const chainEDeposit = Number( - getChainBalanceValue(Number(withoutCommas(availableBalance?.existentialDeposit || ''))).toFixed(), - ); - - const chainEDepositWithLimit = chainEDeposit + limit * valuePerGas; - - if ( - isVoucher && !!voucherBalance - ? getFormattedBalanceValue(voucherBalance.toString()).toFixed() < VOUCHER_MIN_LIMIT - : !chainBalance || chainBalance < chainEDepositWithLimit - ) { - alert.error(`Low balance on ${stringShorten(account?.decodedAddress || '', 8)}`); - - if (onError) { - onError(); - } - - return; - } - - callback(); - }; - - return { checkBalance }; -} - -export { useWalletSync, useWallet, useAccountAvailableBalance, useAccountAvailableBalanceSync, useCheckBalance }; +export { useWalletSync, useWallet, useAccountAvailableBalance, useAccountAvailableBalanceSync }; diff --git a/frontend/apps/escrow/package.json b/frontend/apps/escrow/package.json index 168e20b1a..2a6c142c4 100644 --- a/frontend/apps/escrow/package.json +++ b/frontend/apps/escrow/package.json @@ -5,14 +5,14 @@ "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@mantine/form": "4.2.12", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/react-identicon": "3.1.4", - "@polkadot/types": "10.10.1", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@polkadot/wasm-crypto": "7.2.2", "@testing-library/jest-dom": "5.16.4", diff --git a/frontend/apps/galactic-express/package.json b/frontend/apps/galactic-express/package.json index f16528a2b..f5ab2b188 100644 --- a/frontend/apps/galactic-express/package.json +++ b/frontend/apps/galactic-express/package.json @@ -5,15 +5,15 @@ "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@headlessui/react": "^1.7.17", "@mantine/form": "6.0.8", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/react-identicon": "3.5.1", - "@polkadot/types": "10.10.1", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@polkadot/wasm-crypto": "7.2.2", "@radix-ui/react-scroll-area": "1.0.4", @@ -22,7 +22,7 @@ "@testing-library/user-event": "13.5.0", "assert": "2.0.0", "buffer": "6.0.3", - "clsx": "^1.2.1", + "clsx": "2.1.0", "framer-motion": "^10.16.4", "jotai": "^2.2.2", "react": "18.2.0", diff --git a/frontend/apps/galactic-express/src/App.scss b/frontend/apps/galactic-express/src/App.scss index d19ae4fef..6cf608110 100644 --- a/frontend/apps/galactic-express/src/App.scss +++ b/frontend/apps/galactic-express/src/App.scss @@ -9,7 +9,7 @@ display: flex; flex-direction: column; box-sizing: content-box; - font-family: 'Kanit', 'Poppins', sans-serif; + font-family: 'Anuphan', 'Poppins', sans-serif; } main { diff --git a/frontend/apps/galactic-express/src/App.tsx b/frontend/apps/galactic-express/src/App.tsx index d4800ae53..70ad15025 100644 --- a/frontend/apps/galactic-express/src/App.tsx +++ b/frontend/apps/galactic-express/src/App.tsx @@ -3,6 +3,7 @@ import { Footer } from '@dapps-frontend/ui'; import { Routing } from 'pages'; import { Header, ApiLoader } from 'components'; import { withProviders } from 'hocs'; +import '@gear-js/vara-ui/dist/style.css'; import 'App.scss'; function Component() { diff --git a/frontend/apps/galactic-express/src/assets/images/icons/tvara-coin.svg b/frontend/apps/galactic-express/src/assets/images/icons/tvara-coin.svg new file mode 100644 index 000000000..ec6036faa --- /dev/null +++ b/frontend/apps/galactic-express/src/assets/images/icons/tvara-coin.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/apps/galactic-express/src/assets/images/icons/vara-coin.svg b/frontend/apps/galactic-express/src/assets/images/icons/vara-coin.svg new file mode 100644 index 000000000..0d0467d2f --- /dev/null +++ b/frontend/apps/galactic-express/src/assets/images/icons/vara-coin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/apps/galactic-express/src/assets/meta/galactic_express_meta.txt b/frontend/apps/galactic-express/src/assets/meta/galactic_express_meta.txt index cae89fe7a..1f3960521 100644 --- a/frontend/apps/galactic-express/src/assets/meta/galactic_express_meta.txt +++ b/frontend/apps/galactic-express/src/assets/meta/galactic_express_meta.txt @@ -1 +1 @@ -0002000001000000000105000000010a00000000000000000118000000a11a70000418526573756c740804540104044501080108084f6b040004000000000c457272040008000001000004000004000008084c67616c61637469635f657870726573735f696f144572726f72000120485374617465556e696e6974616c69617a656400000024477374644572726f7204000c0118537472696e670001003041636365737344656e6965640002003053657373696f6e456e646564000300544675656c4f725061796c6f61644f7665726c6f61640004002c53657373696f6e46756c6c000500544e6f74456e6f7567685061727469636970616e74730006002454784d616e61676572040010015c5472616e73616374696f6e4d616e616765724572726f72000700000c0000050200100c20676561725f6c69622874785f6d616e616765725c5472616e73616374696f6e4d616e616765724572726f7200010c4c5472616e73616374696f6e4e6f74466f756e64000000404d69736d617463686564547844617461000100204f766572666c6f770002000014084c67616c61637469635f657870726573735f696f18416374696f6e0001102c4368616e676541646d696e040018011c4163746f724964000000404372656174654e657753657373696f6e000100205265676973746572040024012c5061727469636970616e7400020024537461727447616d65040024012c5061727469636970616e74000300001810106773746418636f6d6d6f6e287072696d6974697665731c4163746f724964000004001c01205b75383b2033325d00001c00000320000000200020000005030024084c67616c61637469635f657870726573735f696f2c5061727469636970616e74000008012c6675656c5f616d6f756e7420010875380001387061796c6f61645f616d6f756e7420010875380000280418526573756c74080454012c044501080108084f6b04002c000000000c45727204000800000100002c084c67616c61637469635f657870726573735f696f144576656e740001103041646d696e4368616e676564080018011c4163746f724964000018011c4163746f724964000000284e657753657373696f6e040030011c53657373696f6e0001002852656769737465726564080018011c4163746f724964000024012c5061727469636970616e740002003047616d6546696e6973686564040040011c526573756c74730003000030084c67616c61637469635f657870726573735f696f1c53657373696f6e000010012873657373696f6e5f696434011075313238000120616c74697475646538010c75313600011c776561746865723c011c576561746865720001187265776172643401107531323800003400000507003800000504003c084c67616c61637469635f657870726573735f696f1c5765617468657200011814436c65617200000018436c6f756479000100145261696e790002001853746f726d790003001c5468756e6465720004001c546f726e61646f0005000040084c67616c61637469635f657870726573735f696f1c526573756c747300000801147475726e734401645665633c5665633c284163746f7249642c205475726e293e3e00012072616e6b696e67735801505665633c284163746f7249642c2075313238293e0000440000024800480000024c004c0000040818500050084c67616c61637469635f657870726573735f696f105475726e00010814416c6976650801246675656c5f6c65667420010875380001387061796c6f61645f616d6f756e7420010875380000002444657374726f796564040054012848616c74526561736f6e0001000054084c67616c61637469635f657870726573735f696f2848616c74526561736f6e0001183c5061796c6f61644f7665726c6f6164000000304675656c4f7665726c6f61640001004453657061726174696f6e4661696c7572650002004441737465726f6964436f6c6c6973696f6e000300304675656c53686f727461676500040034456e67696e654661696c75726500050000580000025c005c0000040818340060084c67616c61637469635f657870726573735f696f145374617465000018011461646d696e18011c4163746f72496400011c73657373696f6e30011c53657373696f6e00014069735f73657373696f6e5f656e646564640110626f6f6c0001307061727469636970616e747368016c5665633c284163746f7249642c205061727469636970616e74293e0001147475726e734401645665633c5665633c284163746f7249642c205475726e293e3e00012072616e6b696e67735801505665633c284163746f7249642c2075313238293e0000640000050000680000026c006c00000408182400 \ No newline at end of file +0002000001000000000105000000010a000000000000000119000000011a00000035268c000418526573756c740804540104044501080108084f6b040004000000000c457272040008000001000004000004000008084c67616c61637469635f657870726573735f696f144572726f72000138485374617465556e696e6974616c69617a656400000024477374644572726f7204000c0118537472696e670001003053657373696f6e456e646564000200544675656c4f725061796c6f61644f7665726c6f61640003002c53657373696f6e46756c6c000400544e6f74456e6f7567685061727469636970616e74730005002454784d616e61676572040010015c5472616e73616374696f6e4d616e616765724572726f72000600284e6f5375636847616d650007002057726f6e67426964000800304e6f53756368506c6179657200090030556e72656769737465726564000a0044416c726561647952656769737465726564000b00505365766572616c526567697374726174696f6e73000c002c4e6f74466f7241646d696e000d00000c0000050200100c20676561725f6c69622874785f6d616e616765725c5472616e73616374696f6e4d616e616765724572726f7200010c4c5472616e73616374696f6e4e6f74466f756e64000000404d69736d617463686564547844617461000100204f766572666c6f770002000014084c67616c61637469635f657870726573735f696f18416374696f6e00011c404372656174654e657753657373696f6e0401106e616d650c0118537472696e6700000020526567697374657208011c63726561746f7218011c4163746f72496400012c7061727469636970616e7424012c5061727469636970616e740001004843616e63656c526567697374726174696f6e0002003044656c657465506c61796572040124706c617965725f696418011c4163746f7249640003002843616e63656c47616d65000400244c6561766547616d6500050024537461727447616d6508012c6675656c5f616d6f756e7420010875380001387061796c6f61645f616d6f756e742001087538000600001810106773746418636f6d6d6f6e287072696d6974697665731c4163746f724964000004001c01205b75383b2033325d00001c00000320000000200020000005030024084c67616c61637469635f657870726573735f696f2c5061727469636970616e740000100108696418011c4163746f7249640001106e616d650c0118537472696e6700012c6675656c5f616d6f756e7420010875380001387061796c6f61645f616d6f756e7420010875380000280418526573756c74080454012c044501080108084f6b04002c000000000c45727204000800000100002c084c67616c61637469635f657870726573735f696f144576656e740001203041646d696e4368616e676564080018011c4163746f724964000018011c4163746f724964000000284e657753657373696f6e100120616c74697475646530010c75313600011c7765617468657234011c576561746865720001187265776172643801107531323800010c626964380110753132380001002852656769737465726564080018011c4163746f724964000024012c5061727469636970616e740002004843616e63656c526567697374726174696f6e00030034506c6179657244656c65746564040124706c617965725f696418011c4163746f7249640004003047616d6543616e63656c65640005003047616d6546696e697368656404003c011c526573756c74730006002047616d654c6566740007000030000005040034084c67616c61637469635f657870726573735f696f1c5765617468657200011814436c65617200000018436c6f756479000100145261696e790002001853746f726d790003001c5468756e6465720004001c546f726e61646f000500003800000507003c084c67616c61637469635f657870726573735f696f1c526573756c747300000c01147475726e734001645665633c5665633c284163746f7249642c205475726e293e3e00012072616e6b696e67735401505665633c284163746f7249642c2075313238293e0001307061727469636970616e74735c016c5665633c284163746f7249642c205061727469636970616e74293e00004000000244004400000248004800000408184c004c084c67616c61637469635f657870726573735f696f105475726e00010814416c6976650801246675656c5f6c65667420010875380001387061796c6f61645f616d6f756e7420010875380000002444657374726f796564040050012848616c74526561736f6e0001000050084c67616c61637469635f657870726573735f696f2848616c74526561736f6e0001183c5061796c6f61644f7665726c6f6164000000304675656c4f7665726c6f61640001004453657061726174696f6e4661696c7572650002004441737465726f6964436f6c6c6973696f6e000300304675656c53686f727461676500040034456e67696e654661696c7572650005000054000002580058000004081838005c0000026000600000040818240064084c67616c61637469635f657870726573735f696f28537461746551756572790001080c416c6c0000001c47657447616d65040124706c617965725f696418011c4163746f7249640001000068084c67616c61637469635f657870726573735f696f2853746174655265706c790001080c416c6c04006c011453746174650000001047616d6504008801444f7074696f6e3c47616d6553746174653e000100006c084c67616c61637469635f657870726573735f696f145374617465000008011467616d65737001645665633c284163746f7249642c2047616d655374617465293e000144706c617965725f746f5f67616d655f696480015c5665633c284163746f7249642c204163746f724964293e0000700000027400740000040818780078084c67616c61637469635f657870726573735f696f2447616d65537461746500001c011461646d696e18011c4163746f72496400012861646d696e5f6e616d650c0118537472696e67000120616c74697475646530010c75313600011c7765617468657234011c576561746865720001187265776172643801107531323800011473746167657c01285374616765537461746500010c6269643801107531323800007c084c67616c61637469635f657870726573735f696f285374616765537461746500010830526567697374726174696f6e04005c016c5665633c284163746f7249642c205061727469636970616e74293e0000001c526573756c747304003c011c526573756c74730001000080000002840084000004081818008804184f7074696f6e04045401780108104e6f6e6500000010536f6d650400780000010000 \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/atoms.ts b/frontend/apps/galactic-express/src/atoms.ts index 5e744549d..1207612d5 100644 --- a/frontend/apps/galactic-express/src/atoms.ts +++ b/frontend/apps/galactic-express/src/atoms.ts @@ -1,5 +1,9 @@ import { atom } from 'jotai'; -export const CURRENT_CONTRACT_ADDRESS_ATOM = atom(''); +export const CURRENT_GAME_ATOM = atom(''); -export const IS_CONTRACT_ADDRESS_INITIALIZED_ATOM = atom(false); +export const PLAYER_NAME_ATOM = atom(null); + +export const PLAYER_INITIAL_STATUS_ATOM = atom<'Finished' | 'Registered' | null>(null); + +export const IS_LOADING = atom(false); diff --git a/frontend/apps/galactic-express/src/components/layout/TextField/TextField.interfaces.ts b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.interfaces.ts new file mode 100644 index 000000000..3cee591b9 --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.interfaces.ts @@ -0,0 +1,9 @@ +import { InputHTMLAttributes } from 'react'; + +export interface TextFieldProps extends Omit, 'size'> { + size?: 'large' | 'medium' | 'small'; + label?: string; + variant?: 'default' | 'active'; + icon?: React.ReactNode; + theme?: 'default' | 'dark'; +} diff --git a/frontend/apps/galactic-express/src/components/layout/TextField/TextField.module.scss b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.module.scss new file mode 100644 index 000000000..0ca49e04d --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.module.scss @@ -0,0 +1,114 @@ + .inputContainer { + position: relative; + + display: flex; + align-items: center; + border: 1px solid rgba(255, 255, 255, 0.23); + border-radius: 4px; + } + + .darkInputContainer { + border: 1px solid rgba(0, 0, 0, 0.25); + } + + .input { + height: 55px; + width: 100%; + padding-left: 16px; + border: none; + box-sizing: border-box; + + background: transparent; + color: #ffffff; + + &::placeholder { + color: rgba(255, 255, 255, 0.5); + } + } + + .darkInputColors { + color: #000000; + + &::placeholder { + color: rgba(0, 0, 0, 0.6); + } + } + + .label { + position: absolute; + top: 0; + bottom: 0; + left: 16px; + display: flex; + align-items: center; + pointer-events: none; + } + + .input, .label .text { + font-family: 'Anuphan'; + font-size: 16px; + } + + .input:focus + .label .text { + font-size: 12px; + transform: translate(0, -50%); + // background-color: white; + padding-left: 4px; + padding-right: 4px; + color: #199680; + background: #000; + } + + .label .text { + transition: all 0.15s ease-out; + } + + .input:focus { + border: none; + outline: none; + } + + .input:focus + .label .text, :not(.input[value=""]) + .label .text { + color: #313635; + font-size: 12px; + transform: translate(0, -156%); + padding-left: 4px; + padding-right: 4px; + + background: #000; + } + + .input:focus + .label .text { + color: #199680; + } + + .darkInputColors:focus + .label .text, :not(.input[value=""]) + .label .text { + background: #fff; + } + + .activeLabel { + height: 22px; + font-size: 12px; + transform: translate(0, -50%); + padding-left: 4px; + padding-right: 4px; + background: #000; + position: absolute; + top: 0; + bottom: 0; + left: 16px; + display: flex; + align-items: center; + pointer-events: none; + color: rgba(255, 255, 255, 0.7);; + } + + + .darkLabel { + background: #fff; + color: #000; + } + + .iconContainer { + margin-left: 18px; + } \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/components/layout/TextField/TextField.tsx b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.tsx new file mode 100644 index 000000000..9a30efaf2 --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/TextField/TextField.tsx @@ -0,0 +1,49 @@ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable jsx-a11y/label-has-associated-control */ +import clsx from 'clsx'; +import styles from './TextField.module.scss'; +import { TextFieldProps } from './TextField.interfaces'; + +function TextField({ + label, + size, + placeholder, + value, + icon, + theme = 'default', + variant = 'default', + ...props +}: TextFieldProps) { + return ( +
+ {icon &&
{icon}
} + + +
+ ); +} + +export { TextField }; diff --git a/frontend/apps/galactic-express/src/components/layout/TextField/index.ts b/frontend/apps/galactic-express/src/components/layout/TextField/index.ts new file mode 100644 index 000000000..665fa3cb5 --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/TextField/index.ts @@ -0,0 +1 @@ +export * from './TextField'; diff --git a/frontend/apps/galactic-express/src/components/layout/container/Container.module.scss b/frontend/apps/galactic-express/src/components/layout/container/Container.module.scss index b0ae7fcbb..daa27759c 100644 --- a/frontend/apps/galactic-express/src/components/layout/container/Container.module.scss +++ b/frontend/apps/galactic-express/src/components/layout/container/Container.module.scss @@ -1,4 +1,3 @@ .container { - max-width: 560px; padding-bottom: 25px; } diff --git a/frontend/apps/galactic-express/src/components/layout/header/Header.module.scss b/frontend/apps/galactic-express/src/components/layout/header/Header.module.scss index 6f33ed993..f60cc2478 100644 --- a/frontend/apps/galactic-express/src/components/layout/header/Header.module.scss +++ b/frontend/apps/galactic-express/src/components/layout/header/Header.module.scss @@ -17,88 +17,17 @@ margin: auto; } -.menu { - list-style: none; - display: flex; -} - -.wallet-info { - display: flex; - align-items: center; -} - -.balance { - display: flex; - align-items: center; - padding: 12px 0; - margin-right: 20px; - - &-coin-image { - margin-right: 10px; - } - - &-value { - font-size: 20px; - font-weight: 700; - letter-spacing: -2%; - } - - &-currency-name { - margin: 8px 0 0 4px; - font-size: 10px; - font-weight: 400; - color: #ffffff; - opacity: 0.6; - text-transform: uppercase; - } +.walletBalance { + color: #ffffff; } -.description { - height: 50px; - min-width: 128px; - max-width: 200px; - padding: 0 10px; - display: flex; - justify-content: center; - align-items: center; - text-transform: capitalize; - border: none; - outline: none; - background: #ffffff12; - cursor: pointer; - - &-icon { - min-width: 16px; - min-height: 16px; - margin-right: 10px; - background: wheat; - border-radius: 50px; - } - - &-name { - font-size: 16px; - font-weight: 700; - letter-spacing: 3%; - color: #ffffff; +.menuIcon { + & svg path { + fill: #ffffff; + stroke: #ffffff; } } -.blur-background { - position: fixed; - top: 0; - left: 0; - background: rgba(39, 44, 63, 0.11); - backdrop-filter: blur(16px); - z-index: 4; - width: 100%; - height: 100%; - display: none; -} - -.score { - padding-right: 60px; -} - .vara-logo { margin-right: 22px; } diff --git a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx index 10f7686b2..0e8be75ee 100644 --- a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx +++ b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx @@ -1,51 +1,31 @@ -import { useAccount } from '@gear-js/react-hooks'; -import { useLocation, Link } from 'react-router-dom'; -import { Wallet } from '@dapps-frontend/ui'; +import { Link } from 'react-router-dom'; +import { Header as CommonHeader, MenuHandler } from '@dapps-frontend/ui'; import { ReactComponent as GalexSVG } from 'assets/images/logo.svg'; import { ReactComponent as VaraSVG } from 'assets/images/logo-vara.svg'; import { cx } from 'utils'; import styles from './Header.module.scss'; -interface HeaderProps { - menu?: Record; -} - -function Header({ menu }: HeaderProps) { - const location = useLocation(); - const { account } = useAccount(); - +function Header() { return ( -
-
+ - - {account && ( - - )} - - -
-
+ } + menu={ + + } + className={{ header: styles.header, content: styles.container }} + /> ); } diff --git a/frontend/apps/galactic-express/src/components/layout/modal/Modal.module.scss b/frontend/apps/galactic-express/src/components/layout/modal/Modal.module.scss new file mode 100644 index 000000000..251a1d69b --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/modal/Modal.module.scss @@ -0,0 +1,64 @@ +.modal { + display: flex; + justify-content: center; + align-items: center; + padding: 0; + background: none; + + &::backdrop { + background-color: rgba(0, 0, 0, 0.2); + } +} + +.wrapper { + position: fixed; + width: 100%; + padding: 30px 32px; + border-radius: 4px; + background-color: #fff; + max-width: 670px; + margin-left: auto; + margin-right: auto; + + &.top { + background-color: #fff; + border-radius: 0; + } +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; +} + +.title { + font-size: 24px; + font-weight: 700; + line-height: 120%; + letter-spacing: 0.02em; + text-transform: capitalize; + color: #222424; +} + +.modal-close { + position: relative; + bottom: 2px; + left: 5px; + margin-left: auto; + transition: color 350ms ease; + + svg path { + fill: #000 + } + + &:hover { + color: #777777; + } +} + +.scroll { + max-height: 85vh; + padding-right: 20px; +} diff --git a/frontend/apps/galactic-express/src/components/layout/modal/Modal.tsx b/frontend/apps/galactic-express/src/components/layout/modal/Modal.tsx new file mode 100644 index 000000000..b57becfd8 --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/modal/Modal.tsx @@ -0,0 +1,69 @@ +import { MouseEvent, useEffect, useRef } from 'react'; +import clsx from 'clsx'; +import { motion } from 'framer-motion'; +import { ReactComponent as CrossIcon } from 'assets/images/icons/cross-icon.svg'; +import { variantsOverlay, variantsPanel } from 'components/layout/modal/Modal.variants'; +import { Button } from '@gear-js/vara-ui'; + +import styles from './Modal.module.scss'; + +type Props = React.PropsWithChildren & { + heading: string; + className?: { + header?: string; + }; + onClose: () => void; +}; + +export function Modal({ heading, children, onClose, className }: Props) { + const ref = useRef(null); + + const disableScroll = () => document.body.classList.add('modal-open'); + const enableScroll = () => document.body.classList.remove('modal-open'); + + const open = () => { + ref.current?.showModal(); + disableScroll(); + }; + + const close = () => { + ref.current?.close(); + enableScroll(); + }; + + useEffect(() => { + open(); + + return () => close(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleClick = ({ target }: MouseEvent) => { + const isBackdropClick = target === ref.current; + + if (isBackdropClick) onClose(); + }; + + return ( + + +
+

{heading}

+ + +
+ + {children} +
+
+ ); +} diff --git a/frontend/apps/galactic-express/src/components/layout/modal/Modal.variants.tsx b/frontend/apps/galactic-express/src/components/layout/modal/Modal.variants.tsx new file mode 100644 index 000000000..c56cf20fd --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/modal/Modal.variants.tsx @@ -0,0 +1,39 @@ +import { Variants } from 'framer-motion'; + +export const variantsOverlay: Variants = { + closed: { + opacity: 0, + transition: { + // delay: 0.15, + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + opacity: 1, + transition: { + duration: 0.2, + ease: 'easeOut', + }, + }, +}; +export const variantsPanel: Variants = { + closed: { + y: 'var(--y-closed, 0)', + opacity: 'var(--opacity-closed)', + scale: 'var(--scale-closed, 1)', + transition: { + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + y: 'var(--y-open, 0)', + opacity: 'var(--opacity-open)', + scale: 'var(--scale-open, 1)', + transition: { + // delay: 0.15, + duration: 0.2, + }, + }, +}; diff --git a/frontend/apps/galactic-express/src/components/layout/modal/index.ts b/frontend/apps/galactic-express/src/components/layout/modal/index.ts new file mode 100644 index 000000000..67fcb47bf --- /dev/null +++ b/frontend/apps/galactic-express/src/components/layout/modal/index.ts @@ -0,0 +1,3 @@ +import { Modal } from './Modal'; + +export { Modal }; diff --git a/frontend/apps/galactic-express/src/consts.ts b/frontend/apps/galactic-express/src/consts.ts index 3cb9daee9..c4aa84927 100644 --- a/frontend/apps/galactic-express/src/consts.ts +++ b/frontend/apps/galactic-express/src/consts.ts @@ -1,5 +1,8 @@ +import { HexString } from '@gear-js/api'; + const ADDRESS = { NODE: process.env.REACT_APP_NODE_ADDRESS as string, + CONTRACT: process.env.REACT_APP_CONTRACT_ADDRESS as HexString, }; const LOCAL_STORAGE = { diff --git a/frontend/apps/galactic-express/src/features/session/assets/ic-user-small-24.svg b/frontend/apps/galactic-express/src/features/session/assets/ic-user-small-24.svg new file mode 100644 index 000000000..701be7aff --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/assets/ic-user-small-24.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss new file mode 100644 index 000000000..b356287cb --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss @@ -0,0 +1,16 @@ + + +.buttonWrapper { + position: absolute; + top: 0; + left: 20px; +} +.button.button.button { + background: #F24A4A12; + color: #EB5757; + + & svg path { + fill: #EB5757; + stroke: #EB5757; + } +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx new file mode 100644 index 000000000..dca312d48 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx @@ -0,0 +1,70 @@ +import { ReactComponent as CrossIconSVG } from 'assets/images/icons/cross-icon.svg'; +import { useAtom } from 'jotai'; +import { Button } from '@gear-js/vara-ui'; +import { useAccount } from '@gear-js/react-hooks'; +import { useLaunchMessage } from 'features/session/hooks'; +import { Participant } from 'features/session/types'; +import { IS_LOADING } from 'atoms'; +import styles from './CancelGameButton.module.scss'; + +type Props = { + isAdmin: boolean; + userAddress: string; + participants: Participant[]; +}; + +function CancelGameButton({ isAdmin, participants, userAddress }: Props) { + const { meta: isMeta, message: sendMessage } = useLaunchMessage(); + const [isLoading, setIsLoading] = useAtom(IS_LOADING); + const { account } = useAccount(); + + const isRegistered = account?.decodedAddress + ? participants.map((participant) => participant[0]).includes(account.decodedAddress) + : false; + + const onError = () => { + setIsLoading(false); + }; + + const onInBlock = () => { + setIsLoading(false); + }; + + const handleClick = () => { + setIsLoading(true); + if (isAdmin) { + sendMessage({ + payload: { + CancelGame: null, + }, + onError, + onInBlock, + }); + } + + if (!isAdmin && isRegistered) { + sendMessage({ + payload: { + CancelRegistration: null, + }, + onError, + onInBlock, + }); + } + }; + + return isRegistered || isAdmin ? ( +
+
+ ) : null; +} + +export { CancelGameButton }; diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/index.ts b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/index.ts new file mode 100644 index 000000000..72ad1d16a --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/index.ts @@ -0,0 +1,3 @@ +import { CancelGameButton } from './CancelGameButton'; + +export { CancelGameButton }; diff --git a/frontend/apps/galactic-express/src/features/session/components/form/Form.module.scss b/frontend/apps/galactic-express/src/features/session/components/form/Form.module.scss index cc90afd06..0dac60792 100644 --- a/frontend/apps/galactic-express/src/features/session/components/form/Form.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/form/Form.module.scss @@ -31,7 +31,6 @@ > *:first-child { padding-right: 28px; - border-right: 1px solid rgba(#2d2c34, 0.3); } > *:last-child { diff --git a/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx b/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx index 49163e14f..cfbd97216 100644 --- a/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx @@ -1,8 +1,9 @@ -import { useAtomValue } from 'jotai'; -import { Input, Button } from '@gear-js/ui'; +import { useAtomValue, useSetAtom, useAtom } from 'jotai'; +import { CURRENT_GAME_ATOM, IS_LOADING, PLAYER_NAME_ATOM } from 'atoms'; +import { useAccount, withoutCommas } from '@gear-js/react-hooks'; +import { Button } from '@gear-js/ui'; import { useForm } from '@mantine/form'; import { Card } from 'components'; -import { CURRENT_CONTRACT_ADDRESS_ATOM } from 'atoms'; import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; import { ReactComponent as RocketSVG } from '../../assets/rocket.svg'; import { INITIAL_VALUES, VALIDATE, WEATHERS } from '../../consts'; @@ -13,26 +14,27 @@ import styles from './Form.module.scss'; type Props = { weather: string; - defaultDeposit: string; + bid: string | undefined; isAdmin: boolean; setRegistrationStatus: Dispatch< SetStateAction<'registration' | 'success' | 'error' | 'NotEnoughParticipants' | 'MaximumPlayersReached'> >; }; -function Form({ weather, defaultDeposit, isAdmin, setRegistrationStatus }: Props) { - const currentContractAddress = useAtomValue(CURRENT_CONTRACT_ADDRESS_ATOM); - const [isLoading, setIsLoading] = useState(false); +function Form({ weather, bid, isAdmin, setRegistrationStatus }: Props) { + const { account } = useAccount(); + const [isLoading, setIsLoading] = useAtom(IS_LOADING); + const setCurrentGame = useSetAtom(CURRENT_GAME_ATOM); const { values, getInputProps, onSubmit, setFieldValue } = useForm({ - initialValues: { deposit: defaultDeposit, ...INITIAL_VALUES }, + initialValues: { ...INITIAL_VALUES }, validate: VALIDATE, }); + const playerName = useAtomValue(PLAYER_NAME_ATOM); + const currentGameAddress = useAtomValue(CURRENT_GAME_ATOM); const { fuel, payload } = values; - const { meta, message: sendMessage } = useLaunchMessage(currentContractAddress); - - const isFirstPlayer = defaultDeposit === '0'; + const { meta, message: sendMessage } = useLaunchMessage(); const handleNumberInputChange = ({ target }: ChangeEvent) => { const value = +target.value; @@ -51,12 +53,19 @@ function Form({ weather, defaultDeposit, isAdmin, setRegistrationStatus }: Props }); const handleSubmit = () => { - if (!isAdmin && meta) { + if (!isAdmin && meta && account?.decodedAddress) { setIsLoading(true); sendMessage({ - payload: { Register: { fuel_amount: fuel, payload_amount: payload } }, + payload: { + Register: { + creator: currentGameAddress, + participant: { fuel_amount: fuel, payload_amount: payload, name: playerName, id: account.decodedAddress }, + }, + }, + value: Number(withoutCommas(bid || '')), onSuccess: () => { setRegistrationStatus('success'); + setCurrentGame(''); setIsLoading(false); }, onError: () => { @@ -72,18 +81,6 @@ function Form({ weather, defaultDeposit, isAdmin, setRegistrationStatus }: Props return (
- -

Mission Deposit

- -
-

Calculation Block

diff --git a/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.module.scss b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.module.scss new file mode 100644 index 000000000..8a04c9934 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.module.scss @@ -0,0 +1,52 @@ +.modal { + min-width: 670px; +} + +.container { + display: flex; + flex-direction: column; + gap: 28px; +} + +.info { + width: 100%; + padding: 16px; + display: grid; + grid-template-rows: auto; + background: #F7F9FA; + gap: 13px; +} + +.item { + display: grid; + grid-template-columns: repeat(2, 1fr); + + .itemName { + font-size: 14px; + font-weight: 400; + } + + .itemValue { + font-size: 14px; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; + } +} + +.form { + display: flex; + flex-direction: column; + gap: 23px; +} + +.inputs { + display: flex; + align-items: flex-end; + gap: 16px; +} + +.button { + width: 100%; +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.tsx b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.tsx new file mode 100644 index 000000000..8db1073ea --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/GameFoundModal.tsx @@ -0,0 +1,113 @@ +import { useState } from 'react'; +import { cx } from 'utils'; +import { Modal } from 'components/layout/modal'; +import { ReactComponent as VaraSVG } from 'assets/images/icons/vara-coin.svg'; +import { ReactComponent as TVaraSVG } from 'assets/images/icons/tvara-coin.svg'; +import { useAccountDeriveBalancesAll, useApi, useBalanceFormat } from '@gear-js/react-hooks'; +import { TextField } from 'components/layout/TextField'; +import { Button } from '@gear-js/vara-ui'; +import { isNotEmpty, useForm } from '@mantine/form'; +import { ReactComponent as UserSVG } from '../../assets/ic-user-small-24.svg'; +import styles from './GameFoundModal.module.scss'; + +type Props = { + entryFee: number | string; + players: number; + gasAmount: number | string; + onSubmit: (values: JoinModalFormValues) => void; + onClose: () => void; +}; + +export type JoinModalFormValues = { + name: string; +}; + +function GameFoundModal({ entryFee, players, gasAmount, onSubmit, onClose }: Props) { + const { isApiReady } = useApi(); + const [isLoading, setIsLoading] = useState(false); + const { getFormattedBalance } = useBalanceFormat(); + const balances = useAccountDeriveBalancesAll(); + const balance = + isApiReady && balances?.freeBalance ? getFormattedBalance(balances.freeBalance.toString()) : undefined; + + const VaraSvg = balance?.unit?.toLowerCase() === 'vara' ? : ; + const items = [ + { + name: 'Entry fee', + value: ( + <> + {VaraSvg} {entryFee} VARA + + ), + }, + { + name: 'Players already joined the game', + value: ( + <> + {players} / 4 + + ), + }, + { + name: 'Required gas amount ', + value: ( + <> + {VaraSvg} {gasAmount} VARA + + ), + }, + ]; + + const joinForm = useForm({ + initialValues: { + name: '', + }, + validate: { + name: isNotEmpty(`Name shouldn't be empty`), + }, + }); + + const { errors: joinErrors, getInputProps: getJoinInputProps, onSubmit: onJoinSubmit } = joinForm; + + const handleJoinSession = (values: JoinModalFormValues) => { + onSubmit(values); + }; + + return ( + +
+

+ To proceed, review the parameters of the gaming session and click the “Join” button. If applicable, you will + need to pay the entry fee and required amount of gas immediately after clicking the “Join” button. After the + end of the game, any unused gas will be refunded. +

+
+ {items.map((item) => ( +
+ {item.name} + {item.value} +
+ ))} +
+ +
+ + {joinErrors.name} +
+
+
+ +
+
+ ); +} + +export { GameFoundModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-found-modal/index.ts b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/index.ts new file mode 100644 index 000000000..5367f057e --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-found-modal/index.ts @@ -0,0 +1,3 @@ +import { GameFoundModal } from './GameFoundModal'; + +export { GameFoundModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss new file mode 100644 index 000000000..2d55a1dbc --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss @@ -0,0 +1,21 @@ +.modal { + min-width: 670px; +} + +.container { + display: flex; + flex-direction: column; + gap: 28px; +} + +.text { + font-size: 14px; +} + +.button { + width: 45%; +} + +.modalHeader { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx new file mode 100644 index 000000000..673805351 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx @@ -0,0 +1,27 @@ +import { Modal } from 'components/layout/modal'; +import { Button } from '@gear-js/vara-ui'; +import styles from './GameNotFoundModal.module.scss'; + +type Props = { + onClose: () => void; +}; + +export type JoinModalFormValues = { + name: string; +}; + +function GameNotFoundModal({ onClose }: Props) { + return ( + +
+

+ Please check the entered address. It's possible the game has been canceled or does not exist. +

+ +
+
+ ); +} + +export { GameNotFoundModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts new file mode 100644 index 000000000..36b74b523 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts @@ -0,0 +1,3 @@ +import { GameNotFoundModal } from './GameNotFoundModal'; + +export { GameNotFoundModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.module.scss b/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.module.scss index 15bfc8323..cfa872b91 100644 --- a/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.module.scss @@ -26,7 +26,11 @@ } &playerAddress { - min-width: 345px; + min-width: 145px; + } + + &playerName { + min-width: 200px } &Action { @@ -35,6 +39,10 @@ } } +.removeTd { + width: 152px; +} + .yourAddressSpan { color: #2bd071; } @@ -50,3 +58,11 @@ color: #8c8b90; padding-bottom: 8px; } + +.removeButton { + font-family: 'Anuphan'; + font-size: 12px; + font-weight: 400; + color: #00FFC4; + line-height: 20px; +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.tsx b/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.tsx index 2679a64c8..9452bf493 100644 --- a/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/participants-table/ParticipantsTable.tsx @@ -1,19 +1,25 @@ import { Fragment } from 'react'; import { cx } from 'utils'; import { shortenString } from 'features/session/utils'; +import { Button } from '@gear-js/vara-ui'; +import { useLaunchMessage } from 'features/session/hooks'; import styles from './ParticipantsTable.module.scss'; interface TableData { id: string; playerAddress: string; + playerName: string; } type Props = { data: TableData[]; userAddress: string; + isUserAdmin: boolean; }; -function ParticipantsTable({ data, userAddress }: Props) { +function ParticipantsTable({ data, userAddress, isUserAdmin }: Props) { + const { meta: isMeta, message: sendMessage } = useLaunchMessage(); + const isYourAddress = (address: string) => address === userAddress; const modifiedData: TableData[] = [ @@ -21,22 +27,35 @@ function ParticipantsTable({ data, userAddress }: Props) { ...data.filter((item) => !isYourAddress(item.playerAddress)), ]; + const handleDeletePlayer = (playerId: string) => { + sendMessage({ + payload: { + DeletePlayer: { + playerId, + }, + }, + }); + }; + return ( {modifiedData && ( <> - {modifiedData[0] && - Object.keys(modifiedData[0]).map( - (cellName: string) => - cellName !== 'id' && ( - - - - - ), - )} + {modifiedData[0] && ( + <> + + {Object.keys(modifiedData[0]).map( + (cellName: string) => + cellName !== 'id' && ( + + + + ), + )} + + )} @@ -44,17 +63,17 @@ function ParticipantsTable({ data, userAddress }: Props) { + {Object.keys(row).map( (cellName) => cellName !== 'id' && ( - + )} ))} diff --git a/frontend/apps/galactic-express/src/features/session/components/radar/Radar.tsx b/frontend/apps/galactic-express/src/features/session/components/radar/Radar.tsx index ce6ae59f9..cfc8d9bcd 100644 --- a/frontend/apps/galactic-express/src/features/session/components/radar/Radar.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/radar/Radar.tsx @@ -1,7 +1,7 @@ import { PLAYER_COLORS } from 'features/session/consts'; import { CSSProperties } from 'react'; import cropEarthSrc from '../../assets/earth-crop.gif'; -import { Event, Rank } from '../../types'; +import { Event, RankWithName } from '../../types'; import styles from './Radar.module.scss'; import { WinStatus } from '../win-status'; @@ -11,10 +11,11 @@ type Props = { roundsCount: number; isWinner: boolean; userRank: string; - winners: Rank[]; + winners: RankWithName[]; + admin: string | undefined; }; -function Radar({ currentEvents, currentRound, roundsCount, isWinner, userRank, winners }: Props) { +function Radar({ currentEvents, currentRound, roundsCount, isWinner, userRank, winners, admin }: Props) { const defineHeightIndex = (current: number, firstDead: number) => { if (firstDead !== -1) { if (current < firstDead) { @@ -46,9 +47,9 @@ function Radar({ currentEvents, currentRound, roundsCount, isWinner, userRank, w return (
{isWinner ? ( - + ) : ( - + )}
{getPlayers()}
diff --git a/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.module.scss b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.module.scss new file mode 100644 index 000000000..9bfa5c711 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.module.scss @@ -0,0 +1,11 @@ +.sessionPassedInfoWrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 40px; + +} + +.button { + width: 200px; +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.tsx b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.tsx new file mode 100644 index 000000000..efc009c5b --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/SessionPassedInfo.tsx @@ -0,0 +1,21 @@ +import { useSetAtom } from 'jotai'; +import { Button } from '@gear-js/vara-ui'; +import { CURRENT_GAME_ATOM } from 'atoms'; +import styles from './SessionPassedInfo.module.scss'; + +function SessionPassedInfo() { + const setCurrentGame = useSetAtom(CURRENT_GAME_ATOM); + + const handleClick = () => { + setCurrentGame(''); + }; + + return ( +
+
The session has passed. You are not participating in this one
+
+ ); +} + +export { SessionPassedInfo }; diff --git a/frontend/apps/galactic-express/src/features/session/components/session-passed-info/index.ts b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/index.ts new file mode 100644 index 000000000..e9f84d9db --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/session-passed-info/index.ts @@ -0,0 +1,3 @@ +import { SessionPassedInfo } from './SessionPassedInfo'; + +export { SessionPassedInfo }; diff --git a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx index 5cb6080a7..277df6217 100644 --- a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx @@ -6,7 +6,7 @@ import { Container } from 'components'; import { ReactComponent as LeftDoubleArrowSVG } from '../../assets/left-double-arrow.svg'; import { ReactComponent as LeftArrowSVG } from '../../assets/left-arrow.svg'; import { PLAYER_COLORS } from '../../consts'; -import { Event, Rank, Session as SessionType, Turns, Participant, TurnParticipant } from '../../types'; +import { Event, Rank, Session as SessionType, Turns, Participant, TurnParticipant, RankWithName } from '../../types'; import { Traits } from '../traits'; import { Radar } from '../radar'; import { Table } from '../table'; @@ -18,9 +18,10 @@ type Props = { rankings: Rank[]; userId?: HexString; participants: Participant[]; + admin: string | undefined; }; -function Session({ session, turns, rankings, userId, participants }: Props) { +function Session({ session, turns, rankings, userId, participants, admin }: Props) { const { altitude, weather, reward, sessionId: id } = session; const roundsCount = turns.length; @@ -61,6 +62,7 @@ function Session({ session, turns, rankings, userId, participants }: Props) { return { participant: participantInfo[0], + name: participants.find((part) => part[0] === participantInfo[0])?.[1].name, deadRound: !isAlive, firstDeadRound, fuelLeft: defineFuelLeftFormat(isAlive, participantInfo[1]?.Alive?.fuelLeft), @@ -106,7 +108,9 @@ function Session({ session, turns, rankings, userId, participants }: Props) { const sortedRanks = sortRanks(); const highestRank = sortedRanks?.[0]?.[1]; - const winners = sortedRanks.filter((item) => item[1] === highestRank); + const winners = sortedRanks + .filter((item) => item[1] === highestRank) + .map((item) => [...item, participants.find((part) => part[0] === item[0])?.[1].name || '']) as RankWithName[]; return { isUserWinner: winners.map((item) => item[0]).includes(userId || '0x'), @@ -119,7 +123,7 @@ function Session({ session, turns, rankings, userId, participants }: Props) {
-

Session #{id}

+

Session

); diff --git a/frontend/apps/galactic-express/src/features/session/components/start/Start.module.scss b/frontend/apps/galactic-express/src/features/session/components/start/Start.module.scss index c7a4decbf..9cec8d171 100644 --- a/frontend/apps/galactic-express/src/features/session/components/start/Start.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/start/Start.module.scss @@ -82,6 +82,7 @@ .imageWrapper { @include flexCenter; flex: 1; + position: relative; .image { max-width: 345px; diff --git a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx index 0b9eef738..b4d853a82 100644 --- a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx @@ -1,15 +1,15 @@ import { useEffect, useState } from 'react'; import clsx from 'clsx'; -import { UserMessageSent, encodeAddress } from '@gear-js/api'; +import { HexString, UserMessageSent, encodeAddress } from '@gear-js/api'; import { Button } from '@gear-js/ui'; -import { useAtomValue, useSetAtom } from 'jotai'; -import { CURRENT_CONTRACT_ADDRESS_ATOM, IS_CONTRACT_ADDRESS_INITIALIZED_ATOM } from 'atoms'; +import { useSetAtom } from 'jotai'; +import { CURRENT_GAME_ATOM } from 'atoms'; +import { ADDRESS } from 'consts'; import { Bytes } from '@polkadot/types'; -import { useAccount, useApi, withoutCommas } from '@gear-js/react-hooks'; +import { useAccount, useApi } from '@gear-js/react-hooks'; import { UnsubscribePromise } from '@polkadot/api/types'; import src from 'assets/images/earth.gif'; import { Container } from 'components'; - import { Participant, Session } from '../../types'; import { Traits } from '../traits'; import { Form } from '../form'; @@ -19,25 +19,27 @@ import { ParticipantsTable } from '../participants-table'; import { SuccessfullyRegisteredInfo } from '../successfully-registered-info'; import { Warning } from '../warning'; +import { CancelGameButton } from '../cancel-game-button/CancelGameButton'; type Props = { participants: Participant[]; session: Session; isUserAdmin: boolean; userAddress: string; + adminAddress: HexString | undefined; + adminName: string; + bid: string | undefined; }; type DecodedReply = { Err: string; }; -function Start({ participants, session, isUserAdmin, userAddress }: Props) { +function Start({ participants, session, isUserAdmin, userAddress, adminAddress, bid, adminName }: Props) { const { api } = useApi(); const { account } = useAccount(); const { decodedAddress } = account || {}; - const currentContractAddress = useAtomValue(CURRENT_CONTRACT_ADDRESS_ATOM); - const setCurrentContractAddress = useSetAtom(CURRENT_CONTRACT_ADDRESS_ATOM); - const setIsContractAddressInitialized = useSetAtom(IS_CONTRACT_ADDRESS_INITIALIZED_ATOM); + const setCurrentGame = useSetAtom(CURRENT_GAME_ATOM); const { altitude, weather, reward, sessionId } = session; const playersCount = participants?.length ? participants.length + 1 : 1; const isRegistered = decodedAddress ? !!participants.some((participant) => participant[0] === decodedAddress) : false; @@ -62,15 +64,14 @@ function Start({ participants, session, isUserAdmin, userAddress }: Props) { }; const handleGoBack = () => { - setCurrentContractAddress(''); - setIsContractAddressInitialized(false); + setCurrentGame(''); }; const handleEvents = ({ data }: UserMessageSent) => { const { message } = data; const { destination, source, payload } = message; const isOwner = destination.toHex() === account?.decodedAddress; - const isEscrowProgram = source.toHex() === currentContractAddress; + const isEscrowProgram = source.toHex() === ADDRESS.CONTRACT; if (isOwner && isEscrowProgram) { const reply = getDecodedReply(payload); @@ -118,23 +119,23 @@ function Start({ participants, session, isUserAdmin, userAddress }: Props) {
- {isUserAdmin && ( - item[0] !== decodedAddress) - .map((item) => ({ - id: item[0], - playerAddress: encodeAddress(item[0]), - })), - ]} - userAddress={userAddress} - /> - )} + ({ + id: item[0], + playerAddress: encodeAddress(item[0]), + playerName: item[1].name, + })), + ]} + userAddress={userAddress} + isUserAdmin={isUserAdmin} + /> +
@@ -159,18 +160,14 @@ function Start({ participants, session, isUserAdmin, userAddress }: Props) { )} {((isUserAdmin && registrationStatus !== 'NotEnoughParticipants') || (!isUserAdmin && !isRegistered && registrationStatus === 'registration')) && ( -
+ )}
+
diff --git a/frontend/apps/galactic-express/src/features/session/components/table/Table.module.scss b/frontend/apps/galactic-express/src/features/session/components/table/Table.module.scss index 24168d0c5..ea285a293 100644 --- a/frontend/apps/galactic-express/src/features/session/components/table/Table.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/table/Table.module.scss @@ -6,7 +6,7 @@ $borderRadius: 8px; .table { display: grid; - grid-template-columns: 1fr repeat(4, max-content); + grid-template-columns: 1fr repeat(5, max-content); text-align: center; div { @@ -25,34 +25,34 @@ $borderRadius: 8px; background-color: rgba(255, 255, 255, 0.04); // first row - &:nth-child(n + 5):nth-child(-n + 10) { + &:nth-child(n + 6):nth-child(-n + 12) { border-top: $tableBorder; } // last row - &:nth-last-child(-n + 5) { + &:nth-last-child(-n + 6) { border-bottom: $tableBorder; } // not last column - &:not(:nth-child(5n)) { + &:not(:nth-child(6n)) { border-right: $cellBorder; } // not last row - &:not(:nth-last-child(-n + 5)) { + &:not(:nth-last-child(-n + 6)) { border-bottom: $cellBorder; } - &:nth-child(6) { + &:nth-child(7) { border-top-left-radius: $borderRadius; } - &:nth-child(10) { + &:nth-child(12) { border-top-right-radius: $borderRadius; } - &:nth-last-child(5) { + &:nth-last-child(6) { border-bottom-left-radius: $borderRadius; } diff --git a/frontend/apps/galactic-express/src/features/session/components/table/Table.tsx b/frontend/apps/galactic-express/src/features/session/components/table/Table.tsx index e47333869..9f7f56dc1 100644 --- a/frontend/apps/galactic-express/src/features/session/components/table/Table.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/table/Table.tsx @@ -21,16 +21,17 @@ function Table({ data, userId }: Props) { )); const getBody = () => - data?.map(({ participant, deadRound, fuelLeft, lastAltitude, payload }, index) => ( + data?.map(({ participant, name, deadRound, fuelLeft, lastAltitude, payload }, index) => (
{shortenString(participant, 4)}{' '} - {userId === participant ? (You) : ''} + {userId === participant ? (You) : ''}
+
{name}
{deadRound ? : }
{fuelLeft}
{lastAltitude}
diff --git a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx index 6dcdb80f3..58ac9c26c 100644 --- a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx @@ -1,27 +1,34 @@ import { useAtomValue } from 'jotai'; -import { CURRENT_CONTRACT_ADDRESS_ATOM } from 'atoms'; import { cx } from 'utils'; import { useAccount } from '@gear-js/react-hooks'; import { Button } from '@gear-js/ui'; -import { useNewSessionMessage } from 'features/session/hooks'; +import { useLaunchMessage } from 'features/session/hooks'; import { shortenString } from 'features/session/utils'; -import { Rank } from 'features/session/types'; +import { Rank, RankWithName } from 'features/session/types'; import styles from './WinStatus.module.scss'; type Props = { type: 'win' | 'lose'; userRank: string; - winners: Rank[]; + winners: RankWithName[]; + admin: string | undefined; }; -function WinStatus({ type, userRank, winners }: Props) { - const contractAddress = useAtomValue(CURRENT_CONTRACT_ADDRESS_ATOM); - const { meta, message: sendNewSessionMessage } = useNewSessionMessage(contractAddress); +function WinStatus({ type, userRank, winners, admin }: Props) { + const { meta, message: sendNewSessionMessage } = useLaunchMessage(); const { account } = useAccount(); + const isAdmin = admin === account?.decodedAddress; + const handleCreateNewSession = () => { - if (meta) { - sendNewSessionMessage({ payload: { CreateNewSession: null } }); + if (!meta) { + return; + } + + if (isAdmin) { + sendNewSessionMessage({ payload: { CancelGame: null } }); + } else { + sendNewSessionMessage({ payload: { LeaveGame: null } }); } }; @@ -39,13 +46,18 @@ function WinStatus({ type, userRank, winners }: Props) {
    {winners.map((item) => (
  • - {shortenString(item[0], 6)} + {item[2] || shortenString(item[0], 6)}
  • ))}
)} - )} @@ -132,13 +139,13 @@ export const BattleRoundPlayers = () => { buttonStyles.button, )} onClick={onAttack} - disabled={isPending || !isAllowed || isLoading}> + disabled={isPending || !isAllowed || isLoadingVoucher}> Attack diff --git a/frontend/apps/tamagotchi-battle/src/features/battle/components/battle-wait-admin/battle-wait-admin.tsx b/frontend/apps/tamagotchi-battle/src/features/battle/components/battle-wait-admin/battle-wait-admin.tsx index 7b11e8c08..33f2aae0d 100644 --- a/frontend/apps/tamagotchi-battle/src/features/battle/components/battle-wait-admin/battle-wait-admin.tsx +++ b/frontend/apps/tamagotchi-battle/src/features/battle/components/battle-wait-admin/battle-wait-admin.tsx @@ -3,16 +3,17 @@ import { SpriteIcon } from 'components/ui/sprite-icon'; import { useBattle } from '../../context'; import { useBattleMessage } from '../../hooks'; import { cn, gasLimitToNumber } from 'app/utils'; -import { useFetchVoucher } from 'features/battle/utils/init-gasless-transactions'; -import { useCheckBalance } from 'features/wallet/hooks'; +import { useCheckBalance } from '@dapps-frontend/hooks'; import { useApi } from '@gear-js/react-hooks'; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; +import { GAS_LIMIT } from 'app/consts'; export const BattleWaitAdmin = () => { const { api } = useApi(); const { players, isPending, setIsPending } = useBattle(); const handleMessage = useBattleMessage(); - const { isVoucher, isLoading } = useFetchVoucher(); - const { checkBalance } = useCheckBalance(isVoucher); + const { voucherId, isLoadingVoucher } = useGaslessTransactions(); + const { checkBalance } = useCheckBalance({ gaslessVoucherId: voucherId }); const handler = async () => { const payload = { StartBattle: null }; @@ -24,7 +25,13 @@ export const BattleWaitAdmin = () => { checkBalance( gasLimitToNumber(api?.blockGasLimit), () => { - handleMessage({ payload, onSuccess, onError, withVoucher: isVoucher }); + handleMessage({ + payload, + onSuccess, + onError, + voucherId, + gasLimit: GAS_LIMIT, + }); }, onError, ); @@ -47,7 +54,7 @@ export const BattleWaitAdmin = () => { buttonStyles.button, )} onClick={handler} - disabled={isPending || players.length < 2 || isLoading}> + disabled={isPending || players.length < 2 || isLoadingVoucher}> Start Battle diff --git a/frontend/apps/tamagotchi-battle/src/features/battle/components/create-tamagotchi-form/create-tamagotchi-form.tsx b/frontend/apps/tamagotchi-battle/src/features/battle/components/create-tamagotchi-form/create-tamagotchi-form.tsx index 8df7505af..0cfe71729 100644 --- a/frontend/apps/tamagotchi-battle/src/features/battle/components/create-tamagotchi-form/create-tamagotchi-form.tsx +++ b/frontend/apps/tamagotchi-battle/src/features/battle/components/create-tamagotchi-form/create-tamagotchi-form.tsx @@ -4,10 +4,11 @@ import { gasLimitToNumber, hexRequired } from 'app/utils'; import { useBattle } from '../../context'; import { useNavigate } from 'react-router-dom'; import { HexString } from '@polkadot/util/types'; -import { useFetchVoucher } from 'features/battle/utils/init-gasless-transactions'; -import { useCheckBalance } from 'features/wallet/hooks'; +import { useCheckBalance } from '@dapps-frontend/hooks'; import { useBattleMessage } from 'features/battle/hooks/use-battle'; import { useApi } from '@gear-js/react-hooks'; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; +import { GAS_LIMIT } from 'app/consts'; const createTamagotchiInitial = { programId: '' as HexString, @@ -22,9 +23,9 @@ const validate: Record = { export const CreateTamagotchiForm = () => { const { battle, isPending } = useBattle(); const handleMessage = useBattleMessage(); - const { isVoucher, isLoading } = useFetchVoucher(); + const { voucherId, isLoadingVoucher } = useGaslessTransactions(); const { api } = useApi(); - const { checkBalance } = useCheckBalance(isVoucher); + const { checkBalance } = useCheckBalance({ gaslessVoucherId: voucherId }); const navigate = useNavigate(); const form = useForm({ initialValues: createTamagotchiInitial, @@ -46,7 +47,13 @@ export const CreateTamagotchiForm = () => { checkBalance( gasLimitToNumber(api?.blockGasLimit), () => { - handleMessage({ payload, onSuccess, onError, withVoucher: isVoucher }); + handleMessage({ + payload, + onSuccess, + onError, + voucherId, + gasLimit: GAS_LIMIT, + }); }, onError, ); @@ -62,7 +69,9 @@ export const CreateTamagotchiForm = () => { text="Create Tamagotchi" color="primary" type="submit" - disabled={Object.keys(errors).length > 0 || isPending || battle?.state !== 'Registration' || isLoading} + disabled={ + Object.keys(errors).length > 0 || isPending || battle?.state !== 'Registration' || isLoadingVoucher + } /> diff --git a/frontend/apps/tamagotchi-battle/src/features/battle/components/new-game-button/new-game-button.tsx b/frontend/apps/tamagotchi-battle/src/features/battle/components/new-game-button/new-game-button.tsx index c0dd8b26c..ea4b705a7 100644 --- a/frontend/apps/tamagotchi-battle/src/features/battle/components/new-game-button/new-game-button.tsx +++ b/frontend/apps/tamagotchi-battle/src/features/battle/components/new-game-button/new-game-button.tsx @@ -1,16 +1,17 @@ import { useBattle } from 'features/battle/context'; -import { useFetchVoucher } from 'features/battle/utils/init-gasless-transactions'; -import { useCheckBalance } from 'features/wallet/hooks'; +import { useCheckBalance } from '@dapps-frontend/hooks'; import { useBattleMessage } from 'features/battle/hooks'; import { Button } from '@gear-js/ui'; import { useApi } from '@gear-js/react-hooks'; import { gasLimitToNumber } from 'app/utils'; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; +import { GAS_LIMIT } from 'app/consts'; export const NewGameButton = () => { const { api } = useApi(); const { isPending, setIsPending } = useBattle(); - const { isVoucher, isLoading } = useFetchVoucher(); - const { checkBalance } = useCheckBalance(isVoucher); + const { voucherId, isLoadingVoucher } = useGaslessTransactions(); + const { checkBalance } = useCheckBalance({ gaslessVoucherId: voucherId }); const handleMessage = useBattleMessage(); const onSuccess = () => setIsPending(false); @@ -24,11 +25,17 @@ export const NewGameButton = () => { checkBalance( gasLimitToNumber(api?.blockGasLimit), () => { - handleMessage({ payload, onSuccess, onError, withVoucher: isVoucher }); + handleMessage({ + payload, + onSuccess, + onError, + voucherId, + gasLimit: GAS_LIMIT, + }); }, onError, ); }; - return - - - ) : ( - +
+
diff --git a/frontend/apps/tequila-train/src/components/sections/game-section/canceled-modal/canceled-modal.tsx b/frontend/apps/tequila-train/src/components/sections/game-section/canceled-modal/canceled-modal.tsx new file mode 100644 index 000000000..10f83979e --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/game-section/canceled-modal/canceled-modal.tsx @@ -0,0 +1,27 @@ + +import { Button } from '@gear-js/vara-ui'; + +import { useApp, useGame } from 'app/context'; + +import { Modal } from 'components/ui/modal'; + +export function CanceledSection() { + const { setPreviousGame } = useGame(); + const { setOpenEmptyPopup } = useApp(); + + const onLeaveGame = () => { + setPreviousGame(null) + setOpenEmptyPopup(false) + } + + return ( + { }}> +

+ Game administrator has ended the game. All spent VARA tokens + for the entry fee will be refunded. +

+ + - {open && } +
+
+
+
+ image +
+
+
+

Welcome to Tequila Train

+

+ Connect your wallet to start +

+ +
+ +
+
+
); }; diff --git a/frontend/apps/tequila-train/src/components/sections/player-card-section/player-card-section.tsx b/frontend/apps/tequila-train/src/components/sections/player-card-section/player-card-section.tsx index 8d69a8549..c250de413 100644 --- a/frontend/apps/tequila-train/src/components/sections/player-card-section/player-card-section.tsx +++ b/frontend/apps/tequila-train/src/components/sections/player-card-section/player-card-section.tsx @@ -2,6 +2,8 @@ import { Icon } from '../../ui/icon'; import clsx from 'clsx'; import { getBgColors } from 'app/utils'; import { useGame } from '../../../app/context'; +import { useEffect, useState } from 'react'; +import { playerNames } from 'app/consts'; type Props = { index: number; @@ -9,7 +11,16 @@ type Props = { }; export const PlayerCardSection = ({ index, active }: Props) => { - const { gameWasm: wasm } = useGame(); + const { game } = useGame(); + const [countHand, setCountHand] = useState(0) + + useEffect(() => { + if (game) { + const onHandsCount = Object.values(game?.gameState?.tileToPlayer).filter((player) => Number(player) === index).length + setCountHand(onHandsCount) + } + }, [game, index]) + return (
{active && ( @@ -28,7 +39,7 @@ export const PlayerCardSection = ({ index, active }: Props) => {
)}
- {wasm?.players[index][1]} + {`Señor ${playerNames[index]}`}
{ )}>
- {wasm?.playersTiles[index].length} + {countHand} On hands
@@ -55,7 +66,7 @@ export const PlayerCardSection = ({ index, active }: Props) => { )}>
- {wasm?.shotCounters[index]} + {game?.gameState?.shots[index]} Number of shots diff --git a/frontend/apps/tequila-train/src/components/sections/player-cons-section/player-cons-section.tsx b/frontend/apps/tequila-train/src/components/sections/player-cons-section/player-cons-section.tsx index 585403a0a..5b137b17c 100644 --- a/frontend/apps/tequila-train/src/components/sections/player-cons-section/player-cons-section.tsx +++ b/frontend/apps/tequila-train/src/components/sections/player-cons-section/player-cons-section.tsx @@ -2,12 +2,16 @@ import { useApp, useGame } from 'app/context'; import { useGameMessage } from 'app/hooks/use-game'; import { PlayerDomino } from '../../common/player-domino'; import { DominoTileType } from 'app/types/game'; -import { cn, getTileId } from 'app/utils'; +import { cn, findTile, getTileId } from 'app/utils'; import { useEffect, useState } from 'react'; +import { Icon } from 'components/ui/icon'; +import { useAccount } from '@gear-js/react-hooks'; export const PlayerConsSection = () => { - const { setIsPending, isPending, setOpenEmptyPopup } = useApp(); - const { game, gameWasm: wasm, setSelectedDomino, selectedDomino, setPlayerChoice, playerChoice } = useGame(); + const { account } = useAccount(); + const { setIsPending, isPending } = useApp(); + const { game, setSelectedDomino, selectedDomino, setPlayerChoice, playerChoice } = useGame(); + const [playersTiles, setPlayersTiles] = useState([]) const handleMessage = useGameMessage(); const [turnPending, setTurnPending] = useState(false); const [passPending, setPassPending] = useState(false); @@ -19,6 +23,22 @@ export const PlayerConsSection = () => { }; }, []); + useEffect(() => { + setSelectedDomino(undefined); + }, [game]); + + useEffect(() => { + if (game) { + const playersTiles = Object.entries(game.gameState.tileToPlayer) + .filter(([key, value]) => value === game.gameState.currentPlayer) + .map(([key, value]) => findTile(key, game?.gameState?.tiles)) + .filter(tile => tile !== null) as DominoTileType[]; + + setPlayersTiles(playersTiles) + } + + }, [game]) + const onSuccess = () => { setTurnPending(false); setPassPending(false); @@ -31,25 +51,23 @@ export const PlayerConsSection = () => { }; const onSelect = ([i, tile]: [number, DominoTileType]) => { - if (selectedDomino) { - selectedDomino[0] !== i ? setSelectedDomino([i, tile]) : setSelectedDomino(undefined); - } else { - setSelectedDomino([i, tile]); - } + let newPlayerChoice; - if (game?.gameState) { - if (playerChoice) { - playerChoice.tile !== tile - ? setPlayerChoice({ ...playerChoice, tile, tile_id: getTileId(tile, game.gameState?.tiles).toString() }) - : setPlayerChoice({ - ...playerChoice, - tile: undefined, - tile_id: undefined, - }); + if (game) { + if (selectedDomino) { + if (selectedDomino[0] !== i) { + setSelectedDomino([i, tile]); + newPlayerChoice = { ...playerChoice, tile, tile_id: getTileId(tile, game.gameState?.tiles).toString() }; + } else { + setSelectedDomino(undefined); + newPlayerChoice = { ...playerChoice, tile: undefined, tile_id: undefined }; + } } else { - setPlayerChoice({ tile, tile_id: getTileId(tile, game.gameState?.tiles).toString() }); + setSelectedDomino([i, tile]); + newPlayerChoice = { tile, tile_id: getTileId(tile, game.gameState?.tiles).toString() }; } } + setPlayerChoice(newPlayerChoice); }; const onTurn = () => { @@ -59,24 +77,72 @@ export const PlayerConsSection = () => { if (+track_id >= 0 && +tile_id >= 0) { setIsPending((prev) => !prev); setTurnPending(true); - handleMessage({ payload: { Place: { tile_id, track_id, remove_train } }, onSuccess, onError }); + handleMessage({ payload: { Place: { creator: game?.admin, tile_id, track_id, remove_train } }, onSuccess, onError }); } - } else { - setOpenEmptyPopup(true); } }; const onPass = () => { setIsPending((prev) => !prev); setPassPending(true); - handleMessage({ payload: { Skip: null }, onSuccess, onError }); + handleMessage({ payload: { Skip: { creator: game?.admin } }, onSuccess, onError }); + }; + + const isDisabledShot = () => { + if (playerChoice) { + const { tile_id, track_id } = playerChoice; + + const validTrack = track_id === game?.gameState.currentPlayer; + const validChoice = tile_id !== undefined && track_id !== undefined && +track_id >= 0 && +tile_id >= 0; + const isCurrentPlayer = account?.decodedAddress === game?.gameState?.players[+game?.gameState.currentPlayer].id; + const tracks = game?.gameState.tracks[+game.gameState.currentPlayer]; + const isTracksValid = tracks?.hasTrain && tracks.tiles.length > 0; + + return isPending || !validChoice || !validTrack || !isCurrentPlayer || !isTracksValid + } else { + return true + } + } + + const isDisabledTurn = () => { + if (playerChoice) { + const { tile_id, track_id } = playerChoice; + + return isPending || !tile_id || !track_id + } else { + return true + } + } + + const onShot = () => { + if (playerChoice) { + const { tile_id, track_id } = playerChoice; + + if (track_id === game?.gameState.currentPlayer) { + setPlayerChoice((prev) => ({ ...prev, remove_train: true })); + setIsPending((prev) => !prev); + setTurnPending(true); + handleMessage({ + payload: { + Place: { + creator: game?.admin, + tile_id, + track_id, + remove_train: true + } + }, + onSuccess, + onError + }); + } + } }; return (
- {wasm && - wasm.playersTiles[+wasm.currentPlayer].map((tile, i) => ( + {playersTiles && + playersTiles.map((tile, i) => ( { ))}
+
+ + +
+ -
diff --git a/frontend/apps/tequila-train/src/components/sections/player-track-section/player-track-section.tsx b/frontend/apps/tequila-train/src/components/sections/player-track-section/player-track-section.tsx index d38ad771c..51349db38 100644 --- a/frontend/apps/tequila-train/src/components/sections/player-track-section/player-track-section.tsx +++ b/frontend/apps/tequila-train/src/components/sections/player-track-section/player-track-section.tsx @@ -1,15 +1,15 @@ import { Icon } from '../../ui/icon'; import clsx from 'clsx'; -import { getBgColors, isPartialSubset } from 'app/utils'; +import { findTile, getBgColors, isPartialSubset } from 'app/utils'; import { DominoItem } from '../../common/domino-item'; import { DominoZone } from '../../common/domino-zone'; import { DominoTileType } from 'app/types/game'; import { useEffect, useRef, useState } from 'react'; import { useApp, useGame } from '../../../app/context'; -import { PlayerTrain } from '../../common/player-train'; -import { useAccount } from '@gear-js/react-hooks'; import { useRefDimensions } from '../../../app/hooks/use-ref-dimensions'; import { TooltipWrapper } from '@gear-js/ui'; +import { playerNames } from 'app/consts'; +import Timer from './timer'; type Props = { index: number; @@ -23,9 +23,8 @@ const SPACING = 2; const CARD_WIDTH = 72; export const PlayerTrackSection = ({ index, train, isUserTrain, active, tiles }: Props) => { - const { account } = useAccount(); const { isAllowed } = useApp(); - const { gameWasm: wasm, playerChoice } = useGame(); + const { game, playerChoice } = useGame(); const [isDisabled, setIsDisabled] = useState(false); const ref = useRef(null); const [w] = useRefDimensions(ref); @@ -43,30 +42,37 @@ export const PlayerTrackSection = ({ index, train, isUserTrain, active, tiles }: }, [w, tiles]); const checkIsActiveDominoReverse = () => { - if (playerChoice?.tile && tiles && wasm) { - const lastTile = tiles.length > 0 ? tiles[tiles.length - 1] : wasm.startTile; + if (playerChoice?.tile && tiles && game) { + const lastTile = tiles.length > 0 ? tiles[tiles.length - 1] : game.gameState.startTile; return lastTile[1] === playerChoice.tile[0] ? false : lastTile[1] === playerChoice.tile[1]; } else return false; }; const checkIsRowDominoReverse = (tile: DominoTileType, i: number, tiles: DominoTileType[]) => { - if (wasm) { - const lastTile = tiles.length > 0 ? (i > 0 ? tiles[i - 1] : false) : wasm.startTile; - // console.log({ tile, lastTile }); + + if (game) { + const lastTile = tiles.length > 0 ? (i > 0 ? tiles[i - 1] : false) : game.gameState.startTile; return lastTile ? (lastTile[1] === tile[0] ? false : lastTile[1] === tile[1]) : false; } else return false; }; useEffect(() => { - if (playerChoice?.tile && tiles && wasm && !train && (active || isUserTrain)) { - setIsDisabled( - !isPartialSubset([tiles.length > 0 ? tiles[tiles.length - 1][1] : wasm.startTile[1]], playerChoice.tile), - ); + if (playerChoice?.tile && tiles && game && !train && (active || isUserTrain)) { + + const stateStartTile = game.gameState.startTile + const startTile = stateStartTile && findTile(stateStartTile, game.gameState.tiles) + + if (startTile) { + setIsDisabled( + !isPartialSubset([tiles.length > 0 ? tiles[tiles.length - 1][1] : startTile[0]], playerChoice.tile), + ); + } } else { setIsDisabled(false); } - }, [active, isUserTrain, playerChoice, tiles, train, wasm]); + + }, [active, isUserTrain, playerChoice, tiles, train, game]); return (
)} {isUserTrain && - (account?.decodedAddress === wasm?.players[index][0] && !isDisabled && playerChoice?.tile_id !== undefined ? ( - - ) : ( - - ))} + }

- {train ? 'Tequila Train' : `${wasm?.players[index][1]}`} + {train ? 'Tequila Train' : `Señor ${playerNames[index]}`} +

+ {active && + + }
diff --git a/frontend/apps/tequila-train/src/components/sections/player-track-section/timer.tsx b/frontend/apps/tequila-train/src/components/sections/player-track-section/timer.tsx new file mode 100644 index 000000000..70051764e --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/player-track-section/timer.tsx @@ -0,0 +1,31 @@ +import { useGame } from 'app/context'; +import React, { useEffect, useState } from 'react' + +const Timer = () => { + const { timer } = useGame(); + const [seconds, setSeconds] = useState(timer); + + useEffect(() => { + setSeconds(timer); + }, [timer]); + + useEffect(() => { + + const interval = setInterval(() => { + setSeconds((prevSeconds) => { + if (prevSeconds > 0) return prevSeconds - 1; + clearInterval(interval); + return 0; + }); + }, 1000); + + + return () => clearInterval(interval); + }, [seconds]); + + return ( +

{seconds.toFixed(0)}

+ ) +} + +export default Timer diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.module.scss b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.module.scss new file mode 100644 index 000000000..c6253ea05 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.module.scss @@ -0,0 +1,52 @@ +.modal { + overflow: hidden; + color: transparent; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 0; + background: none; + outline: none; + + &::backdrop { + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(10px); + } +} + +.wrapper { + width: 100%; + // max-width: 800px; + padding: 30px 90px; + border-radius: 4px; + // background-color: #f6f8f8; +} + +// .header { +// display: flex; +// align-items: center; +// justify-content: space-between; +// margin-bottom: 24px; +// } + +// .title { +// font-size: 18px; +// font-weight: 700; +// line-height: 120%; +// letter-spacing: 0.02em; +// text-transform: capitalize; +// color: #222424; +// } + +// .modal-close { +// position: relative; +// bottom: 2px; +// left: 5px; +// margin-left: auto; +// transition: color 350ms ease; + +// &:hover { +// color: #777777; +// } +// } diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.tsx b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.tsx new file mode 100644 index 000000000..d8f612f6e --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/Modal.tsx @@ -0,0 +1,44 @@ +import { useEffect, useRef } from 'react'; +import { motion } from 'framer-motion'; +import styles from './Modal.module.scss'; +import { variantsOverlay, variantsPanel } from 'components/ui/modal/modal.variants'; +import type { BaseComponentProps } from 'app/types'; + +export function Modal({ children }: BaseComponentProps) { + const ref = useRef(null); + + const disableScroll = () => document.body.classList.add('modal-open'); + const enableScroll = () => document.body.classList.remove('modal-open'); + + const open = () => { + ref.current?.showModal(); + disableScroll(); + }; + + const close = () => { + ref.current?.close(); + enableScroll(); + }; + + useEffect(() => { + open(); + + return () => close(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + + return ( + + + {children} + + + ); +} diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/modal/index.ts b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/index.ts new file mode 100644 index 000000000..67fcb47bf --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/index.ts @@ -0,0 +1,3 @@ +import { Modal } from './Modal'; + +export { Modal }; diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/modal/modal.variants.ts b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/modal.variants.ts new file mode 100644 index 000000000..c56cf20fd --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/modal/modal.variants.ts @@ -0,0 +1,39 @@ +import { Variants } from 'framer-motion'; + +export const variantsOverlay: Variants = { + closed: { + opacity: 0, + transition: { + // delay: 0.15, + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + opacity: 1, + transition: { + duration: 0.2, + ease: 'easeOut', + }, + }, +}; +export const variantsPanel: Variants = { + closed: { + y: 'var(--y-closed, 0)', + opacity: 'var(--opacity-closed)', + scale: 'var(--scale-closed, 1)', + transition: { + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + y: 'var(--y-open, 0)', + opacity: 'var(--opacity-open)', + scale: 'var(--scale-open, 1)', + transition: { + // delay: 0.15, + duration: 0.2, + }, + }, +}; diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/registration-form.tsx b/frontend/apps/tequila-train/src/components/sections/registration-section/registration-form.tsx index 6f05bc206..dd9006821 100644 --- a/frontend/apps/tequila-train/src/components/sections/registration-section/registration-form.tsx +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/registration-form.tsx @@ -4,7 +4,6 @@ import { stringRequired } from '../../../app/utils'; import { useGameMessage } from '../../../app/hooks/use-game'; import { useAccount } from '@gear-js/react-hooks'; import { useApp } from '../../../app/context'; -import clsx from 'clsx'; const initialValues = { name: '', @@ -51,16 +50,10 @@ export function RegistrationForm() { className="[&_label]:text-sm [&_label]:font-normal" autoComplete="name" {...getInputProps('name')} + required + />
-
- -
); } diff --git a/frontend/apps/tequila-train/src/components/sections/registration-section/registration-section.tsx b/frontend/apps/tequila-train/src/components/sections/registration-section/registration-section.tsx index 05dbefdd4..efd36da9c 100644 --- a/frontend/apps/tequila-train/src/components/sections/registration-section/registration-section.tsx +++ b/frontend/apps/tequila-train/src/components/sections/registration-section/registration-section.tsx @@ -1,35 +1,178 @@ -import { RegistrationForm } from './registration-form'; -import { useGame } from '../../../app/context'; + +import { useAccount, useAlert, useApi } from '@gear-js/react-hooks'; +import { useGameMessage } from 'app/hooks/use-game'; +import { Button } from '@gear-js/vara-ui'; + +import { cn, copyToClipboard, shortenString } from 'app/utils'; + +import { useApp, useGame } from 'app/context'; +import { HexString, encodeAddress } from '@gear-js/api'; + +import { Icon } from 'components/ui/icon'; +import { Modal } from './modal'; + +import { MockGameSection } from '../game-section/mock/mock-game-section'; +import { CanceledSection } from '../game-section/canceled-modal'; export function RegistrationSection() { - const { game } = useGame(); + const { api } = useApi(); + const { account } = useAccount(); + const { game, isAdmin } = useGame(); + const { setIsPending, setIsUserCancelled } = useApp(); + const handleMessage = useGameMessage(); + const alert = useAlert(); + + const onSuccess = () => { + setIsPending(false); + }; + const onError = () => { + setIsPending(false); + }; + + const onStartGame = () => { + handleMessage({ + payload: { StartGame: null }, + onSuccess, + onError, + }); + } + + const onCancelGame = () => { + if (isAdmin) { + handleMessage({ + payload: { CancelGame: null }, + onSuccess, + onError, + }); + } else { + setIsUserCancelled(true); + handleMessage({ + payload: { CancelRegistration: { creator: game?.admin } }, + onSuccess, + onError, + }); + } + } + + const onDeletePlayer = (player: HexString) => { + handleMessage({ + payload: { DeletePlayer: { player_id: player } }, + onSuccess, + onError, + }); + } + + const onCopy = () => { + if (account) { + copyToClipboard(account.decodedAddress, alert); + } + }; + + const reversedArrayPlayers = [...(game?.initialPlayers ?? [])]; + const disableButton = !Boolean(game && game.initialPlayers?.length >= 2) + + const [decimals] = api?.registry.chainDecimals ?? [12]; + const bid = parseFloat(game?.bid.replace(/,/g, '') || "0") / 10 ** decimals return ( -
-
-
-
- image -
-
-
-

Registration...

-

- Players ({game?.players.length || 0}/{game?.maybeLimit || 8}). Waiting for other players...{' '} -

- -
- +
+ + + +
+
+
+
+ image +
+
+
+

Registration...

+

+ Players ({game?.initialPlayers.length || 0}/8). Waiting for other players...{' '} +

+ +
+ {isAdmin && +
+
+
+
+

Entry fee

+
+ + {bid} VARA +
+
+ +
+

Players already joined the game

+
+ {game?.initialPlayers.length} + /8 +
+
+ +
+

Your game address + ({account && shortenString(account.address, 4)}) +

+
+ + Copy +
+
+
+
+ +
+ {reversedArrayPlayers.map((player, i) => { + return ( +
+

+ {account && shortenString(encodeAddress(player), 4)} + {i === 0 && (you)} +

+ + {i !== 0 && + } +
+ ) + })} +
+ +
+ } + +
+ {isAdmin && +
+
+
-
+
); } diff --git a/frontend/apps/tequila-train/src/components/sections/start-section/create-game.tsx b/frontend/apps/tequila-train/src/components/sections/start-section/create-game.tsx new file mode 100644 index 000000000..46ac9cfcc --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/start-section/create-game.tsx @@ -0,0 +1,78 @@ +import { Button, Input } from '@gear-js/vara-ui'; + +import styles from './start-secrtion.module.scss' +import { Sprite } from 'components/ui/sprite'; +import { useForm } from '@mantine/form'; +import { useApp } from 'app/context'; +import { useGameMessage } from 'app/hooks/use-game'; +import { numberRequired } from 'app/utils'; +import { useApi } from '@gear-js/react-hooks'; + +const initialValues = { + bid: 0, +}; + +const validate: Record = { + bid: numberRequired, +}; + +export const CreateGame = ({ closeCreateGame }: { closeCreateGame: () => void }) => { + const { api } = useApi(); + + const { setIsPending, isPending } = useApp(); + const form = useForm({ + initialValues, + validate, + validateInputOnChange: true, + }); + const { getInputProps, errors, reset } = form; + + const handleMessage = useGameMessage(); + const onSuccess = () => { + setIsPending(false); + reset(); + }; + const onError = () => { + setIsPending(false); + }; + + const handleSubmit = form.onSubmit((values) => { + const [decimals] = api?.registry.chainDecimals ?? [12]; + setIsPending(true); + handleMessage({ + payload: { CreateGame: null }, value: (values.bid * 10 ** decimals).toString() || "0", + onSuccess, + onError, + }); + }); + + return ( +
+
+
+

Create new game

+

+ Set the entry fee. After creating the game, share your unique game ID (which is your wallet address) so players can join. +

+
+
+ } + {...getInputProps('bid')} + required + /> +
+ +
+
+ +
+
+
+ ); +}; diff --git a/frontend/apps/tequila-train/src/components/sections/start-section/find-game.tsx b/frontend/apps/tequila-train/src/components/sections/start-section/find-game.tsx new file mode 100644 index 000000000..f40e679f0 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/start-section/find-game.tsx @@ -0,0 +1,132 @@ +import { Button, Input } from '@gear-js/vara-ui'; +import { useState } from 'react'; +import { useApi } from '@gear-js/react-hooks'; + +import styles from './start-secrtion.module.scss' +import { useGame, useApp } from 'app/context'; +import { useGameMessage } from 'app/hooks/use-game'; +import { useForm } from '@mantine/form'; +import { stringRequired } from 'app/utils'; +import { Modal } from 'components/ui/modal'; +import { Icon } from 'components/ui/icon'; +import { GameType } from 'app/types/game'; + +const initialValues = { + creator: '', +}; + +const validate: Record = { + creator: stringRequired, +}; + +export const FindGame = ({ closeFindGame }: { closeFindGame: () => void }) => { + const { api } = useApi() + const [isOpenModal, setOpenModal] = useState(false) + const [findGame, setFindGame] = useState(null) + const { state, setPreviousGame } = useGame(); + const { setIsPending, isPending } = useApp(); + const handleMessage = useGameMessage(); + const [isNotFound, setIsNotFound] = useState(false) + + const form = useForm({ + initialValues, + validate, + validateInputOnChange: true, + }); + const { getInputProps } = form; + + const onSuccess = () => { + setIsPending(false); + }; + const onError = () => { + setIsPending(false); + }; + + const handleSubmit = form.onSubmit((values) => { + const [decimals] = api?.registry.chainDecimals ?? [12]; + + if (findGame) { + setIsPending(true); + handleMessage({ + payload: { Register: { creator: values.creator } }, + value: parseFloat(findGame.bid) * 10 ** decimals, + onSuccess, + onError, + }); + } + }); + + const onFindGame = () => { + setPreviousGame(null) + const findGame = state?.games.find(game => game[0] === form.values.creator) + if (findGame) { + setFindGame(findGame[1] as GameType) + setOpenModal(true) + } else { + setIsNotFound(true) + } + } + + const [decimals] = api?.registry.chainDecimals ?? [12]; + const bid = parseFloat(findGame?.bid.replace(/,/g, '') || "0") / 10 ** decimals + + return ( +
+

Welcome to Tequila Train

+

+ To join the game, specify the address received from the game administrator. +

+
+ +
+ +
+
+ + {isOpenModal && + setOpenModal(false)}> +

To proceed, review the parameters of the gaming session and click the “Join” button. If applicable, you will need to pay the entry fee immediately after clicking the “Join” button.

+
+
+
+

Entry fee

+
+ + {bid} VARA +
+
+ +
+

Players already joined the game

+
+ + {findGame?.initialPlayers.length} + /8
+
+
+
+
+
+
+ +
+ } + + {isNotFound && + setIsNotFound(false)}> +

Please check the entered address. It's possible the game has been canceled or does not exist.

+
+ ); +}; diff --git a/frontend/apps/tequila-train/src/components/sections/start-section/index.ts b/frontend/apps/tequila-train/src/components/sections/start-section/index.ts new file mode 100644 index 000000000..16f4ce2f6 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/start-section/index.ts @@ -0,0 +1 @@ +export { StartSection } from './start-section'; diff --git a/frontend/apps/tequila-train/src/components/sections/start-section/start-secrtion.module.scss b/frontend/apps/tequila-train/src/components/sections/start-section/start-secrtion.module.scss new file mode 100644 index 000000000..72d130176 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/start-section/start-secrtion.module.scss @@ -0,0 +1,21 @@ +.wallet { + display: flex; + align-items: center; + gap: 48px; +} + +.accountButton { + display: block; + + @media screen and (max-width: 767px) { + display: none; + } +} + +.connectButton.connectButton { + font-size: 16px; + + @media screen and (max-width: 767px) { + height: 44px; + } +} diff --git a/frontend/apps/tequila-train/src/components/sections/start-section/start-section.tsx b/frontend/apps/tequila-train/src/components/sections/start-section/start-section.tsx new file mode 100644 index 000000000..384fbab90 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/sections/start-section/start-section.tsx @@ -0,0 +1,48 @@ +import { Button } from '@gear-js/vara-ui'; +import { useState } from 'react'; + +import styles from './start-secrtion.module.scss' +import { CreateGame } from './create-game'; +import { FindGame } from './find-game'; + +export const StartSection = () => { + const [isFindGame, setIsFindGame] = useState(false) + const [isCreateGame, setIsCreateGame] = useState(false) + + if (isCreateGame) { + return setIsCreateGame(false)} /> + } + + return ( +
+
+
+
+ image +
+
+ {!isFindGame && +
+

Welcome to Tequila Train

+

+ To begin, choose whether you want to join an existing game or become an administrator and create a new game. +

+ +
+
+
} + + {isFindGame && setIsFindGame(false)} />} +
+
+ ); +}; diff --git a/frontend/apps/tequila-train/src/components/ui/Button/Button.interfaces.ts b/frontend/apps/tequila-train/src/components/ui/Button/Button.interfaces.ts new file mode 100644 index 000000000..5d3b42592 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/Button/Button.interfaces.ts @@ -0,0 +1,9 @@ +import { ButtonHTMLAttributes } from 'react'; + +export interface ButtonProps extends ButtonHTMLAttributes { + label?: string; + variant: 'primary' | 'outline' | 'icon' | 'text'; + size?: 'large' | 'medium' | 'small'; + icon?: string; + isLoading?: boolean; +} diff --git a/frontend/apps/tequila-train/src/components/ui/Button/Button.module.scss b/frontend/apps/tequila-train/src/components/ui/Button/Button.module.scss new file mode 100644 index 000000000..de6ec5f24 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/Button/Button.module.scss @@ -0,0 +1,76 @@ +// // @use '~assets/styles/variables' as *; +// // @use '~assets/styles/mixins' as *; + +// .button { +// display: flex; +// align-items: center; +// justify-content: center; +// gap: 10px; +// outline: none; +// box-shadow: none; +// border-radius: 2px; +// font-size: 16px; +// font-weight: 600; +// letter-spacing: 0.5px; +// cursor: pointer; + +// &:disabled { +// opacity: 0.5; +// } +// } + +// .variant { +// &-primary { +// border: none; +// background: theme-var($green-special-bright); +// color: theme-var($text-primary); +// } + +// &-outline { +// border: 2px solid theme-var($button-border); +// background: transparent; +// color: theme-var($text-primary); +// } + +// &-icon { +// border: none; +// background: transparent; +// color: theme-var($text-primary); +// } + +// &-text { +// border: none; +// background: transparent; +// color: theme-var($text-primary); +// } +// } + +// .size { +// &-small { +// height: 30px; +// width: 150px; +// } + +// &-medium { +// height: 40px; +// width: 160px; +// } + +// &-large { +// height: 50px; +// width: 180px; +// } +// } + +// .loader { +// animation: spin 1s linear infinite; + +// @keyframes spin { +// from { +// transform: rotate(0deg); +// } +// to { +// transform: rotate(360deg); +// } +// } +// } diff --git a/frontend/apps/tequila-train/src/components/ui/Button/Button.tsx b/frontend/apps/tequila-train/src/components/ui/Button/Button.tsx new file mode 100644 index 000000000..489d54afb --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/Button/Button.tsx @@ -0,0 +1,49 @@ +import { cx } from 'app/utils'; +import styles from './Button.module.scss'; +import { ButtonProps } from './Button.interfaces'; + +function Button({ + variant, + icon, + label = '', + type = 'button', + size = 'medium', + className, + isLoading, + disabled, + ...props +}: ButtonProps) { + return ( + + ); +} + +export { Button }; diff --git a/frontend/apps/tequila-train/src/components/ui/Button/index.ts b/frontend/apps/tequila-train/src/components/ui/Button/index.ts new file mode 100644 index 000000000..8b166a86e --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/Button/index.ts @@ -0,0 +1 @@ +export * from './Button'; diff --git a/frontend/apps/tequila-train/src/components/ui/alert/alert.module.scss b/frontend/apps/tequila-train/src/components/ui/alert/alert.module.scss new file mode 100644 index 000000000..03e4f92c9 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/alert/alert.module.scss @@ -0,0 +1,100 @@ +.root { + position: fixed; + right: 20px; + bottom: 20px; + z-index: 20; + width: calc(100vw - 32px); + max-width: 480px; + +} + +.alert { + position: relative; + display: grid; + grid-gap: 8px; + padding: 24px 48px 24px 24px; + color: #000; + background-color: #fff; + border-radius: 4px; + box-shadow: 0 10px 20px 0 rgba(91, 91, 91, 0.15); +} + +.header { + display: flex; + align-items: center; + font-size: 16px; + font-weight: 500; + line-height: 20px; + text-transform: capitalize; + + &::before { + content: ''; + display: inline-block; + width: 20px; + height: 20px; + margin-right: 10px; + background-size: cover; + background-repeat: no-repeat; + } + + &.info { + &::before { + background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_180_426)'%3E%3Cpath d='M16.6716 0.0294991H3.35792C2.9058 -0.0350076 2.44488 0.00661504 2.01163 0.151072C1.57839 0.295529 1.18472 0.538855 0.861786 0.861786C0.538855 1.18472 0.295529 1.57839 0.151072 2.01163C0.00661504 2.44488 -0.0350076 2.9058 0.0294991 3.35792V20L3.35792 16.6716H16.6716C17.1237 16.7361 17.5846 16.6945 18.0179 16.55C18.4511 16.4056 18.8448 16.1622 19.1677 15.8393C19.4906 15.5164 19.734 15.1227 19.8784 14.6895C20.0229 14.2562 20.0645 13.7953 20 13.3432V3.35792C20.0645 2.9058 20.0229 2.44488 19.8784 2.01163C19.734 1.57839 19.4906 1.18472 19.1677 0.861786C18.8448 0.538855 18.4511 0.295529 18.0179 0.151072C17.5846 0.00661504 17.1237 -0.0350076 16.6716 0.0294991ZM10.8469 11.679C10.8469 11.8996 10.7592 12.1113 10.6031 12.2673C10.4471 12.4234 10.2354 12.5111 10.0147 12.5111C9.79406 12.5111 9.58241 12.4234 9.42636 12.2673C9.27031 12.1113 9.18265 11.8996 9.18265 11.679V8.35054C9.18265 8.12985 9.27031 7.9182 9.42636 7.76216C9.58241 7.60611 9.79406 7.51844 10.0147 7.51844C10.2354 7.51844 10.4471 7.60611 10.6031 7.76216C10.7592 7.9182 10.8469 8.12985 10.8469 8.35054V11.679ZM10.0369 6.1316C9.74269 6.13307 9.45991 6.01759 9.2508 5.81056C9.04169 5.60354 8.92339 5.32192 8.92192 5.02767C8.92045 4.73342 9.03593 4.45064 9.24295 4.24153C9.44998 4.03242 9.73159 3.91412 10.0258 3.91265H10.0369C10.3312 3.91265 10.6134 4.02954 10.8215 4.23761C11.0295 4.44568 11.1464 4.72787 11.1464 5.02212C11.1464 5.31637 11.0295 5.59857 10.8215 5.80664C10.6134 6.01471 10.3312 6.1316 10.0369 6.1316Z' fill='black'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_180_426'%3E%3Crect width='20' height='20' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"); + } + } + + &.success { + color: #0ed3a3; + + &::before { + background-image: url("data:image/svg+xml,%3Csvg width='17' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.5.5a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm-.96 11.465L3.728 8.152l.904-.904 2.908 2.907 4.827-4.827.905.904-5.732 5.733Z' fill='%230ed3a3'/%3E%3C/svg%3E"); + } + } + + &.error { + color: #ff0101; + + &::before { + background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_180_8)'%3E%3Cpath d='M19.351 8.39432L11.5666 0.609894C11.1482 0.191345 10.5917 -0.0390625 10 -0.0390625C9.40827 -0.0390625 8.85178 0.191345 8.43338 0.609894L0.648956 8.39432C0.23056 8.81271 0 9.36905 0 9.96094C0 10.5528 0.23056 11.1092 0.648956 11.5276L8.43338 19.312C8.85178 19.7305 9.40827 19.9609 10 19.9609C10.5917 19.9609 11.1482 19.7305 11.5666 19.312L19.3509 11.5276C19.7694 11.1092 20 10.5528 20 9.96094C20 9.36905 19.7694 8.81271 19.351 8.39432ZM10.6012 13.9815C10.5992 14.0002 10.5965 14.0193 10.5925 14.0381C10.5887 14.0569 10.584 14.0756 10.5785 14.0941C10.573 14.1121 10.5664 14.1304 10.5589 14.148C10.5519 14.1655 10.5434 14.1832 10.5347 14.2C10.5258 14.2168 10.5156 14.2332 10.5051 14.2493C10.4944 14.2653 10.4831 14.2805 10.471 14.2954C10.4588 14.3102 10.4456 14.3246 10.4323 14.3384C10.4187 14.3517 10.4042 14.3649 10.3893 14.377C10.3745 14.3892 10.3592 14.4005 10.3432 14.411C10.3271 14.4215 10.3107 14.4318 10.2939 14.4408C10.2771 14.4493 10.2596 14.4576 10.2419 14.465C10.2243 14.4724 10.206 14.4791 10.188 14.4846C10.1695 14.4901 10.1509 14.4946 10.1321 14.4986C10.1134 14.5021 10.0941 14.5053 10.0754 14.5071C10.0563 14.5091 10.0368 14.5099 10.0175 14.5099C9.99848 14.5099 9.97925 14.5091 9.96002 14.5071C9.94095 14.5053 9.92188 14.5021 9.90296 14.4986C9.88434 14.4946 9.86557 14.4901 9.84757 14.4846C9.8291 14.4791 9.81079 14.4724 9.79324 14.465C9.77554 14.4576 9.7583 14.4493 9.74167 14.4408C9.72473 14.4318 9.70795 14.4215 9.69193 14.411C9.67636 14.4005 9.66065 14.3892 9.64584 14.377C9.63089 14.3649 9.61655 14.3517 9.60312 14.3384C9.58954 14.3246 9.57657 14.3102 9.56406 14.2954C9.55231 14.2805 9.54071 14.2653 9.53003 14.2493C9.5195 14.2332 9.50974 14.2168 9.50073 14.2C9.49188 14.1832 9.48364 14.1655 9.47617 14.148C9.46915 14.1304 9.46243 14.1121 9.45694 14.0941C9.45114 14.0756 9.44641 14.0569 9.4429 14.0381C9.43909 14.0193 9.43634 14.0002 9.43436 13.9815C9.43237 13.9623 9.43131 13.9427 9.43131 13.9236C9.43131 13.9044 9.43237 13.8853 9.43436 13.8661C9.43634 13.847 9.43909 13.8278 9.4429 13.8091C9.44641 13.7903 9.45114 13.7715 9.45694 13.7537C9.46243 13.7352 9.46915 13.7172 9.47617 13.6992C9.48364 13.6816 9.49173 13.6644 9.50073 13.6476C9.50974 13.6308 9.5195 13.614 9.53003 13.5985C9.54071 13.5825 9.55231 13.5667 9.56406 13.5519C9.57657 13.537 9.58954 13.5226 9.60312 13.5089C9.61655 13.4956 9.63089 13.4827 9.64584 13.4702C9.66065 13.4584 9.67621 13.4468 9.69193 13.4361C9.70795 13.4256 9.72473 13.4158 9.74167 13.4068C9.75845 13.3978 9.77554 13.3897 9.79324 13.3823C9.81079 13.3748 9.8291 13.3685 9.84757 13.363C9.86557 13.3572 9.88419 13.3525 9.90296 13.349C9.92172 13.3452 9.94095 13.342 9.96002 13.3405C9.99802 13.3365 10.0371 13.3365 10.0754 13.3405C10.0941 13.342 10.1134 13.3452 10.1321 13.349C10.1508 13.3525 10.1695 13.3572 10.188 13.363C10.206 13.3685 10.2243 13.3748 10.2419 13.3823C10.2596 13.3897 10.2771 13.3978 10.2939 13.4068C10.3107 13.4158 10.3271 13.4256 10.3432 13.4361C10.3592 13.4468 10.3745 13.4584 10.3893 13.4702C10.4042 13.4827 10.4187 13.4956 10.4323 13.5089C10.4456 13.5226 10.459 13.537 10.471 13.5519C10.4831 13.5667 10.4944 13.5825 10.5051 13.5985C10.5156 13.614 10.5257 13.6308 10.5347 13.6476C10.5434 13.6644 10.5519 13.6816 10.5589 13.6992C10.5664 13.7172 10.573 13.7352 10.5785 13.7537C10.584 13.7715 10.5887 13.7903 10.5925 13.8091C10.5965 13.8278 10.5992 13.847 10.6012 13.8661C10.6032 13.8853 10.6039 13.9044 10.6039 13.9236C10.6039 13.9427 10.6032 13.9623 10.6012 13.9815ZM10.6039 12.0865C10.6039 12.4103 10.3415 12.6729 10.0177 12.6729C9.69391 12.6729 9.43131 12.4103 9.43131 12.0865V6.00464C9.43131 5.68085 9.69391 5.41824 10.0177 5.41824C10.3415 5.41824 10.6039 5.68085 10.6039 6.00464V12.0865Z' fill='%23FF0101'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_180_8'%3E%3Crect width='20' height='20' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A"); + } + } + + &.loading { + color: #0e8ed3; + + &::before { + background-image: url("data:image/svg+xml,%3Csvg width='30' height='30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15 2.5c3.393 0 6.474 1.361 8.729 3.564l1.418-1.418a.498.498 0 0 1 .834.216l2 7a.5.5 0 0 1-.618.618l-7-1.999a.5.5 0 0 1-.216-.835L21.6 8.192A9.446 9.446 0 0 0 15 5.5c-5.239 0-9.5 4.261-9.5 9.5 0 5.238 4.261 9.5 9.5 9.5 5.238 0 9.5-4.262 9.5-9.5 0-.248-.018-.492-.038-.736l3.035.875C27.422 21.968 21.846 27.5 15 27.5 8.107 27.5 2.5 21.892 2.5 15 2.5 8.107 8.107 2.5 15 2.5Z' fill='%230e8ed3'/%3E%3C/svg%3E"); + animation: rotating 1s linear infinite; + } + } +} + +.button { + position: absolute; + top: 18px; + right: 18px; + margin-left: auto; + transition: color 350ms ease; + + &:hover { + color: #777777; + } +} + +.body { + overflow: hidden; + text-overflow: ellipsis; + padding-top: 4px; + padding-bottom: 6px; + font-size: 14px; + line-height: 20px; +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/frontend/apps/tequila-train/src/components/ui/alert/alert.tsx b/frontend/apps/tequila-train/src/components/ui/alert/alert.tsx new file mode 100644 index 000000000..be5a6751a --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/alert/alert.tsx @@ -0,0 +1,23 @@ +import clsx from 'clsx'; +import { AlertProps } from './alert.types'; +import styles from './alert.module.scss'; +import { Button } from '../Button'; +import { Icon } from '../icon'; + +export function Alert({ alert, close }: AlertProps) { + const { content, options } = alert; + const { type, title, style, isClosed } = options; + + return ( +
+
{title || type}
+
{content}
+ {isClosed && ( + + )} +
+ ); +} +export { styles as alertStyles }; diff --git a/frontend/apps/tequila-train/src/components/ui/alert/alert.types.ts b/frontend/apps/tequila-train/src/components/ui/alert/alert.types.ts new file mode 100644 index 000000000..2e5f9d150 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/alert/alert.types.ts @@ -0,0 +1,20 @@ +import { CSSProperties, ReactNode } from 'react'; + +type Options = { + type: 'info' | 'error' | 'loading' | 'success'; + style?: CSSProperties; + title?: string; + timeout?: number; + isClosed?: boolean; +}; + +type Alert = { + id: string; + content: ReactNode; + options: Options; +}; + +export type AlertProps = { + alert: Alert; + close: () => void; +}; diff --git a/frontend/apps/tequila-train/src/components/ui/alert/index.ts b/frontend/apps/tequila-train/src/components/ui/alert/index.ts new file mode 100644 index 000000000..d1c70adb7 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/alert/index.ts @@ -0,0 +1,2 @@ +export { Alert, alertStyles } from './alert'; +export type { AlertProps } from './alert.types'; diff --git a/frontend/apps/tequila-train/src/components/ui/index.ts b/frontend/apps/tequila-train/src/components/ui/index.ts new file mode 100644 index 000000000..8b166a86e --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/index.ts @@ -0,0 +1 @@ +export * from './Button'; diff --git a/frontend/apps/tequila-train/src/components/ui/modal/Modal.module.scss b/frontend/apps/tequila-train/src/components/ui/modal/Modal.module.scss new file mode 100644 index 000000000..b4973b0ab --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/modal/Modal.module.scss @@ -0,0 +1,51 @@ +.modal { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 0; + background: none; + outline: none; + border: none; + + &::backdrop { + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(10px); + } +} + +.wrapper { + width: 100%; + max-width: 700px; + padding: 30px 32px; + border-radius: 4px; + background-color: #ffffff; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; +} + +.title { + font-size: 32px; + font-weight: 700; + line-height: 120%; + letter-spacing: 0.02em; + text-transform: capitalize; + color: #222424; +} + +.modal-close { + position: relative; + bottom: 2px; + left: 5px; + margin-left: auto; + transition: color 350ms ease; + + &:hover { + color: #777777; + } +} diff --git a/frontend/apps/tequila-train/src/components/ui/modal/Modal.tsx b/frontend/apps/tequila-train/src/components/ui/modal/Modal.tsx new file mode 100644 index 000000000..2ba035255 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/modal/Modal.tsx @@ -0,0 +1,66 @@ +import { MouseEvent, useEffect, useRef } from 'react'; +import { motion } from 'framer-motion'; +import styles from './Modal.module.scss'; +import { variantsOverlay, variantsPanel } from 'components/ui/modal/modal.variants'; +import { Button } from 'components/ui/Button'; +import { Sprite } from 'components/ui/sprite'; +import type { BaseComponentProps } from 'app/types'; +import { Icon } from '../icon'; + +type Props = BaseComponentProps & { + heading: string; + onClose: () => void; +}; + +export function Modal({ heading, children, onClose }: Props) { + const ref = useRef(null); + + const disableScroll = () => document.body.classList.add('modal-open'); + const enableScroll = () => document.body.classList.remove('modal-open'); + + const open = () => { + ref.current?.showModal(); + disableScroll(); + }; + + const close = () => { + ref.current?.close(); + enableScroll(); + }; + + useEffect(() => { + open(); + + return () => close(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleClick = ({ target }: MouseEvent) => { + const isBackdropClick = target === ref.current; + + if (isBackdropClick) onClose(); + }; + + return ( + + +
+

{heading}

+ + +
+ + {children} +
+
+ ); +} diff --git a/frontend/apps/tequila-train/src/components/ui/modal/index.ts b/frontend/apps/tequila-train/src/components/ui/modal/index.ts new file mode 100644 index 000000000..67fcb47bf --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/modal/index.ts @@ -0,0 +1,3 @@ +import { Modal } from './Modal'; + +export { Modal }; diff --git a/frontend/apps/tequila-train/src/components/ui/modal/modal.variants.ts b/frontend/apps/tequila-train/src/components/ui/modal/modal.variants.ts new file mode 100644 index 000000000..c56cf20fd --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/modal/modal.variants.ts @@ -0,0 +1,39 @@ +import { Variants } from 'framer-motion'; + +export const variantsOverlay: Variants = { + closed: { + opacity: 0, + transition: { + // delay: 0.15, + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + opacity: 1, + transition: { + duration: 0.2, + ease: 'easeOut', + }, + }, +}; +export const variantsPanel: Variants = { + closed: { + y: 'var(--y-closed, 0)', + opacity: 'var(--opacity-closed)', + scale: 'var(--scale-closed, 1)', + transition: { + duration: 0.3, + ease: 'easeIn', + }, + }, + open: { + y: 'var(--y-open, 0)', + opacity: 'var(--opacity-open)', + scale: 'var(--scale-open, 1)', + transition: { + // delay: 0.15, + duration: 0.2, + }, + }, +}; diff --git a/frontend/apps/tequila-train/src/components/ui/sprite.tsx b/frontend/apps/tequila-train/src/components/ui/sprite.tsx new file mode 100644 index 000000000..7ca020dee --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/sprite.tsx @@ -0,0 +1,15 @@ +import { FC, SVGProps } from 'react'; + +type IconProps = SVGProps & { + name: string; + section?: string; + size?: number; +}; + +export const Sprite: FC = ({ name, className, section = 'icons', size, ...props }) => { + return ( + + + + ); +}; diff --git a/frontend/apps/tequila-train/src/components/ui/text-gradient/index.ts b/frontend/apps/tequila-train/src/components/ui/text-gradient/index.ts new file mode 100644 index 000000000..46799f9c5 --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/text-gradient/index.ts @@ -0,0 +1,3 @@ +import { TextGradient } from './text-gradient'; + +export { TextGradient }; diff --git a/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.module.scss b/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.module.scss new file mode 100644 index 000000000..f88ef6adb --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.module.scss @@ -0,0 +1,8 @@ +.gradient { + --color-from: #2b2b2b; + --color-to: #00ffc4; + + background: linear-gradient(90deg, var(--color-from) 33.39%, var(--color-to) 77.42%); + background-clip: text; + -webkit-text-fill-color: transparent; +} diff --git a/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.tsx b/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.tsx new file mode 100644 index 000000000..2737b185f --- /dev/null +++ b/frontend/apps/tequila-train/src/components/ui/text-gradient/text-gradient.tsx @@ -0,0 +1,6 @@ +import styles from './text-gradient.module.scss'; +import clsx from 'clsx'; + +export function TextGradient({ children, className }: React.PropsWithChildren & { className?: string }) { + return {children}; +} diff --git a/frontend/apps/tequila-train/src/index.css b/frontend/apps/tequila-train/src/index.css index 4f2e4d59c..1e332f754 100644 --- a/frontend/apps/tequila-train/src/index.css +++ b/frontend/apps/tequila-train/src/index.css @@ -11,9 +11,9 @@ --color-light: 209 209 209; } -#alert-root { - color: white; -} +/* #alert-root { + z-index: ; +} */ .test::before { background: linear-gradient(270.95deg, rgba(255, 255, 255, 0) 85.3%, #ffffff 102.87%); @@ -21,7 +21,7 @@ } body.body { - background-color: #ebf1ee; + background-color: #f7f9fa; color: #4f4f4f; } @@ -37,51 +37,19 @@ body.body { @layer components { .typo-h1 { - @apply font-kanit text-[72px] leading-[86px] font-medium; + @apply text-[72px] leading-[86px] font-medium; } .typo-h2 { - @apply font-kanit text-[40px] leading-[48px] font-bold tracking-[0.04em]; + @apply text-[40px] leading-[48px] font-bold tracking-[0.04em]; } .btn { @apply inline-flex justify-center items-center text-center py-2.5 px-8 text-[14px] font-semibold leading-5 tracking-[0.08em] rounded-full whitespace-nowrap cursor-pointer select-none; } - .btn--primary { - @apply text-white bg-primary enabled:hover:bg-[#57b53f] enabled:active:bg-[#3f9829] transition-colors; - } - - .btn--primary-light { - @apply text-white bg-primary bg-opacity-50 enabled:hover:bg-[#57b53f] enabled:active:bg-[#3f9829] transition-colors; - } - - .btn--primary-outline { - @apply text-white bg-primary bg-opacity-10 enabled:hover:bg-opacity-20 enabled:active:bg-opacity-30 border border-primary border-opacity-20 transition-colors; - } - - .btn--gray { - @apply text-black bg-secondary-500 enabled:hover:bg-secondary-600 enabled:active:bg-secondary-700 transition-colors; - } - .btn--ghost { - @apply text-white bg-white/10 enabled:hover:bg-white/30 enabled:active:bg-white/50 transition-colors; - } - - .btn--white { - @apply text-black bg-white enabled:hover:bg-[#DEDEDE] enabled:active:bg-neutral-300 transition-colors; - } - - .btn--black { - @apply text-white bg-dark-500 enabled:hover:bg-dark-400 enabled:active:bg-opacity-80 transition-colors; - } - - .btn--error { - @apply text-white bg-red-500 enabled:hover:bg-red-600 enabled:active:bg-red-700 transition-colors; - } - - .btn--blue { - @apply text-white bg-blue-600 enabled:hover:bg-blue-700 enabled:active:bg-blue-800 transition-colors; + @apply text-white bg-white/10 enabled: hover:bg-white/30 enabled:active:bg-white/50 transition-colors; } .btn[disabled] { @@ -91,6 +59,8 @@ body.body { .btn--loading { @apply opacity-90 gap-2 before:w-4 before:h-4 before:border-2 before:border-neutral-500 before:rounded-full before:border-l-transparent before:border-t-transparent before:animate-spin; } + + } .ellipse { @@ -107,15 +77,13 @@ body.body { } .text-transparent-primary { - background: radial-gradient( - 136.03% 142.06% at 47.37% 6.07%, - #52d88c 0%, - #4dc383 7.98%, - #42946e 27.89%, - #3a725f 45.54%, - #355e56 60.07%, - #335653 69.7% - ); + background: radial-gradient(136.03% 142.06% at 47.37% 6.07%, + #52d88c 0%, + #4dc383 7.98%, + #42946e 27.89%, + #3a725f 45.54%, + #355e56 60.07%, + #335653 69.7%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; diff --git a/frontend/apps/tequila-train/src/pages/home.tsx b/frontend/apps/tequila-train/src/pages/home.tsx index dedad753e..b5710db14 100644 --- a/frontend/apps/tequila-train/src/pages/home.tsx +++ b/frontend/apps/tequila-train/src/pages/home.tsx @@ -1,34 +1,50 @@ import { useAccount } from '@gear-js/react-hooks'; -import { LoginSection } from 'components/sections/login-section'; -import { GameSection } from '../components/sections/game-section'; -import { useInitGame, useWasmState } from '../app/hooks/use-game'; -import { useGame } from '../app/context'; -import { RegistrationSection } from '../components/sections/registration-section'; -import { Loader } from '../components/loaders/loader'; -import { cn } from '../app/utils'; -// import { useEffect } from 'react'; +import { useApp, useGame } from 'app/context'; +import { cn } from 'app/utils'; +import { LoginSection, GameSection, StartSection, RegistrationSection, CanceledSection } from 'components/sections'; +import { useInitGame } from 'app/hooks/use-game'; +import { useEffect } from 'react'; export const Home = () => { useInitGame(); - useWasmState(); const { account } = useAccount(); - const { game, gameWasm } = useGame(); + const { game, previousGame, setPreviousGame } = useGame(); + const { setOpenEmptyPopup, openEmptyPopup, isUserCancelled, setIsUserCancelled } = useApp() - // useEffect(() => { - // console.log({ game, gameWasm }); - // }, [game, gameWasm]); + useEffect(() => { + const isAdmin = previousGame?.admin === account?.decodedAddress; + + if (game) { + setPreviousGame(game); + } + else if (previousGame) { + if (!isAdmin && !isUserCancelled && !previousGame.state.Winners) { + setOpenEmptyPopup(true) + } + setIsUserCancelled(false) + setPreviousGame(null) + } + }, [game]); + + const renderSection = () => { + if (!account) { + return + } + + if (game?.isStarted || previousGame?.isStarted) { + return ; + } else if (game?.state && 'Registration') { + return ; + } else { + return ; + } + }; return (
- {account ? ( - game && <>{game.isStarted ? gameWasm ? : : } - ) : ( -
-

Connect your account to start the game

- -
- )} + {renderSection()} + {openEmptyPopup && }
); }; diff --git a/frontend/apps/tequila-train/tailwind.config.js b/frontend/apps/tequila-train/tailwind.config.js index efd0174c2..5e50c9fde 100644 --- a/frontend/apps/tequila-train/tailwind.config.js +++ b/frontend/apps/tequila-train/tailwind.config.js @@ -35,19 +35,6 @@ module.exports = { '100%': { opacity: 1 }, }, }, - colors: { - current: 'currentColor', - secondary: 'rgb(var(--color-secondary) / )', - primary: 'rgb(var(--color-primary) / )', - error: 'rgb(var(--color-error) / )', - 'dark-500': 'rgb(var(--color-dark-500) / )', - 'dark-400': 'rgb(var(--color-dark-400) / )', - light: 'rgb(var(--color-light) / )', - }, - fontFamily: { - kanit: ['Kanit', ...defaultTheme.fontFamily.sans], - poppins: ['Poppins', ...defaultTheme.fontFamily.sans], - }, fontSize: { xxs: ['10px', '18px'], xs: ['12px', '16px'], diff --git a/frontend/apps/tic-tac-toe/package.json b/frontend/apps/tic-tac-toe/package.json index 022623306..27bd1e56b 100644 --- a/frontend/apps/tic-tac-toe/package.json +++ b/frontend/apps/tic-tac-toe/package.json @@ -11,16 +11,19 @@ "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@headlessui/react": "1.7.17", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", - "@polkadot/types": "10.10.1", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2", "@radix-ui/react-dialog": "1.0.4", "@radix-ui/react-scroll-area": "1.0.4", + "@types/node": "18.16.19", + "@types/react": "18.2.33", + "@types/react-dom": "18.2.14", + "assert": "2.0.0", "buffer": "6.0.3", "class-variance-authority": "0.6.1", "clsx": "1.2.1", @@ -32,13 +35,11 @@ "react-dom": "18.2.0", "react-router-dom": "6.10.0", "react-transition-group": "4.4.5", + "sass": "1.62.0", "socket.io-client": "4.7.2" }, "devDependencies": { "@types/lodash.isequal": "4.5.6", - "@types/node": "18.16.19", - "@types/react": "18.2.33", - "@types/react-dom": "18.2.14", "@vitejs/plugin-react-swc": "3.3.2", "autoprefixer": "10.4.15", "eslint": "8.48.0", @@ -47,7 +48,6 @@ "postcss": "8.4.29", "prettier": "3.0.3", "rollup-plugin-visualizer": "5.9.2", - "sass": "1.58.3", "tailwindcss": "3.3.3", "typescript": "4.9.5", "vite": "4.4.9", diff --git a/frontend/apps/tic-tac-toe/src/app.tsx b/frontend/apps/tic-tac-toe/src/app.tsx index 0caee3db0..7ab060707 100644 --- a/frontend/apps/tic-tac-toe/src/app.tsx +++ b/frontend/apps/tic-tac-toe/src/app.tsx @@ -5,6 +5,7 @@ import meta from '@/features/tic-tac-toe/assets/meta/tic_tac_toe.meta.txt'; import { Routing } from '@/pages'; import { Loader, LoadingError, MainLayout } from '@/components'; import { useProgramMetadata } from './app/hooks'; +import '@gear-js/vara-ui/dist/style.css'; function Component() { const metadata = useProgramMetadata(meta); diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/header.module.scss b/frontend/apps/tic-tac-toe/src/components/layout/header/header.module.scss index 73f9d2a24..c65a61531 100644 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/header.module.scss +++ b/frontend/apps/tic-tac-toe/src/components/layout/header/header.module.scss @@ -1,7 +1,7 @@ .header { position: relative; z-index: 40; - padding: 20px 0; + padding: 20px; &__container { display: flex; diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/header.tsx b/frontend/apps/tic-tac-toe/src/components/layout/header/header.tsx index 5f2531a3a..e316fa1b9 100644 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/header.tsx +++ b/frontend/apps/tic-tac-toe/src/components/layout/header/header.tsx @@ -1,37 +1,19 @@ import { Logo } from './logo'; import styles from './header.module.scss'; -import { Container } from '@/components/ui/container'; +import { Header as CommonHeader, MenuHandler } from '@dapps-frontend/ui'; import clsx from 'clsx'; import { useAccount } from '@gear-js/react-hooks'; -import { MobileMenu } from '@/components/layout/header/mobile-menu'; -import { Wallet } from '@/features/wallet'; -import { Suspense } from 'react'; -import { Loader } from '@/components/loaders'; -import { VaraBalance } from '@/components/ui/balance'; -import { useAccountAvailableBalance } from '@/features/account-available-balance/hooks'; export function Header() { const { account } = useAccount(); - const { availableBalance: balance } = useAccountAvailableBalance(); return ( -
- + -
- {!!account && ( - <> - }> - - - - - )} -
-
- -
-
-
+ } + className={{ header: styles.header, content: styles.header__container }} + menu={} + /> ); } diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/index.ts b/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/index.ts deleted file mode 100644 index e876aead9..000000000 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { MobileMenu } from './mobile-menu'; -export { MobileMenuDialog } from './mobile-menu-dialog'; -export type { MobileMenuDialogProps } from './mobile-menu-dialog'; diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu-dialog.tsx b/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu-dialog.tsx deleted file mode 100644 index da72404d4..000000000 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu-dialog.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { Button } from '@/components/ui/button'; -import styles from './mobile-menu.module.scss'; -import { useState } from 'react'; -import { ADDRESS } from '@/app/consts'; -import { useAccount, useApi } from '@gear-js/react-hooks'; -import { WalletIcon } from '@/features/wallet'; -import { AnimatePresence, motion } from 'framer-motion'; -import { variantsOverlay, variantsPanel } from '@/components/ui/modal/modal.variants'; -import { Dialog } from '@headlessui/react'; -import { useAuth } from '@/features/auth'; -import { DialogsLibrary } from '@/components/ui/dialogs'; -import { Sprite } from '@/components/ui/sprite'; -import { useGame } from '@/features/tic-tac-toe/hooks'; - -export type MobileMenuDialogProps = { - onClose?(): void; - open: boolean; - setOpen(value: boolean): void; -}; - -export function MobileMenuDialog({ setOpen, open }: MobileMenuDialogProps) { - const { api } = useApi(); - const { account } = useAccount(); - const { signOut } = useAuth(); - const { resetGame } = useGame(); - - const handleLogoutButtonClick = () => { - signOut(); - setOpen(false); - resetGame(); - }; - - return ( - - {open && ( - - - -
-
- -
-
- -
-
-

{api?.runtimeVersion.specName.toHuman()}

-

{ADDRESS.NODE}

-
-
-
-
- -
-

{account?.meta.name}

-
-
-
- setOpen(false)} /> - -
- - - -
-
-
-
- )} -
- ); -} - -function SwitchAccount({ onClose }: { onClose(): void }) { - const [openWallet, setOpenWallet] = useState(false); - return ( - <> - - - - ); -} diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.module.scss b/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.module.scss deleted file mode 100644 index b86833825..000000000 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.module.scss +++ /dev/null @@ -1,164 +0,0 @@ -.wrapper { - display: none; - - @media screen and (max-width: 767px) { - display: flex; - } -} - -.toggle { - padding: 0; - border: none; -} - -.modal { - position: relative; - z-index: 39; - - &__backdrop { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - - @media screen and (min-width: 768px) { - background-color: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(10px); - } - } - - &__wrapper { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow-y: hidden; - } - - &__container { - display: flex; - min-height: 100%; - align-items: flex-start; - justify-content: center; - - @media screen and (min-width: 768px) { - align-items: center; - } - } - - &__content { - --opacity-closed: 0%; - --opacity-open: 100%; - - position: relative; - width: 100%; - max-width: 400px; - padding-top: 88px; - padding-bottom: 16px; - background: #ffffff; - - > * + * { - margin-top: 8px; - } - - hr { - border-bottom: 1px solid #72707d33; - } - - @media screen and (min-width: 768px) { - --scale-closed: 90%; - --scale-open: 100%; - } - - @media screen and (max-width: 767px) { - --y-closed: -32px; - --y-open: 0px; - - padding: 80px 16px 16px; - max-width: 100%; - } - } - - &__header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 24px; - } - - &__title { - font-size: 18px; - font-weight: 700; - line-height: 120%; - letter-spacing: 0.02em; - text-transform: capitalize; - color: #222424; - } - - &__close { - position: absolute; - top: 20px; - right: 16px; - z-index: 10; - opacity: 50%; - transition: color 350ms ease; - background: red; - - &:hover { - color: #777777; - } - } -} - -.actions { - display: flex; - - > * + * { - margin-left: 16px; - } - - > button { - --btn-radius: 4px; - - width: 100%; - padding: 5px 0; - font-size: 16px; - } -} - -.item { - display: grid; - grid-template-columns: auto 1fr; - align-items: center; - grid-gap: 12px; - padding: 4px 9px 8px; - - &__icon { - display: inline-flex; - justify-content: center; - align-items: center; - width: 32px; - height: 32px; - background-color: #00ffc4; - border-radius: 50%; - } - - &__text { - } - - &__title { - font-size: 16px; - font-weight: 600; - line-height: 18px; - text-transform: capitalize; - } - - &__helper { - font-size: 11px; - line-height: 12px; - letter-spacing: 0.15px; - } -} diff --git a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.tsx b/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.tsx deleted file mode 100644 index b5c4184a6..000000000 --- a/frontend/apps/tic-tac-toe/src/components/layout/header/mobile-menu/mobile-menu.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Button } from '@/components/ui/button'; -import styles from './mobile-menu.module.scss'; -import { useState } from 'react'; -import { DialogsLibrary } from '@/components/ui/dialogs'; -import { Sprite } from '@/components/ui/sprite'; - -export function MobileMenu() { - const [open, setOpen] = useState(false); - - return ( -
- - -
- ); -} diff --git a/frontend/apps/tic-tac-toe/src/components/ui/balance/Balance.tsx b/frontend/apps/tic-tac-toe/src/components/ui/balance/Balance.tsx index be3e326bf..56890085f 100644 --- a/frontend/apps/tic-tac-toe/src/components/ui/balance/Balance.tsx +++ b/frontend/apps/tic-tac-toe/src/components/ui/balance/Balance.tsx @@ -25,19 +25,6 @@ export function Balance({ icon, value, decimal, unit, className }: Props) { ); } -export function VaraBalance({ value, unit, className }: HOCProps) { - const v = value.split('.'); - return ( - - ); -} - export function PointsBalance({ value, unit = 'PPV', className }: HOCProps) { return ; } diff --git a/frontend/apps/tic-tac-toe/src/components/ui/balance/index.ts b/frontend/apps/tic-tac-toe/src/components/ui/balance/index.ts index de4e50b78..9e1fccf25 100644 --- a/frontend/apps/tic-tac-toe/src/components/ui/balance/index.ts +++ b/frontend/apps/tic-tac-toe/src/components/ui/balance/index.ts @@ -1,3 +1,3 @@ -import { VaraBalance, PointsBalance } from './Balance'; +import { PointsBalance } from './Balance'; -export { VaraBalance, PointsBalance }; +export { PointsBalance }; diff --git a/frontend/apps/tic-tac-toe/src/components/ui/dialogs.ts b/frontend/apps/tic-tac-toe/src/components/ui/dialogs.ts deleted file mode 100644 index b5d84f735..000000000 --- a/frontend/apps/tic-tac-toe/src/components/ui/dialogs.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { WalletModalProps } from '@/features/wallet/components/wallet-modal'; -import { MobileMenuDialogProps } from '@/components/layout/header/mobile-menu'; -import { ComponentType, lazy } from 'react'; - -export interface IDialogsLibrary { - WalletModal: WalletModalProps; - MobileMenuDialog: MobileMenuDialogProps; -} - -export const DialogsLibrary: Record = { - WalletModal: lazy>(() => - import('@/features/wallet/components/wallet-modal').then(({ WalletModal }) => ({ default: WalletModal })), - ), - MobileMenuDialog: lazy>(() => - import('@/components/layout/header/mobile-menu').then(({ MobileMenuDialog }) => ({ - default: MobileMenuDialog, - })), - ), -}; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/index.ts deleted file mode 100644 index c3fd479f1..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Wallet } from './wallet'; -export { WalletModal } from './wallet-modal'; -export { WalletIcon } from './wallet-icon'; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/index.ts deleted file mode 100644 index 4eaa5154b..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { WalletIcon } from './wallet-icon'; -export { PolkadotIcon } from './polkadot-icon'; -export type { PolkadotIconProps } from './polkadot-icon'; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/polkadot-icon.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/polkadot-icon.tsx deleted file mode 100644 index 7f5990772..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/polkadot-icon.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { CSSProperties, useMemo } from 'react'; -import { polkadotIcon } from './utils'; -import type { Circle } from './types'; - -function renderCircle({ cx, cy, fill, r }: Circle, key: number) { - return ; -} - -export type PolkadotIconProps = { - address: string; - className?: string; - isAlternative?: boolean; - size?: number; - style?: CSSProperties; -}; - -export function PolkadotIcon({ address, className = '', isAlternative = false, size, style = {} }: PolkadotIconProps) { - const circles = useMemo(() => polkadotIcon(address, { isAlternative }), [address, isAlternative]); - - return ( - - {circles.map(renderCircle)} - - ); -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/types.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/types.ts deleted file mode 100644 index 51fc28297..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface Circle { - cx: number; - cy: number; - fill: string; - r: number; -} - -export interface Options { - isAlternative?: boolean; - size?: number; -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/utils.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/utils.tsx deleted file mode 100644 index 3b4f3ddc4..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/utils.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { blake2AsU8a, decodeAddress } from '@polkadot/util-crypto'; -import { Circle, Options } from './types'; - -interface Scheme { - freq: number; - colors: readonly number[]; -} - -const S = 64; -const C = S / 2; -const Z = (S / 64) * 5; - -const SCHEMES: readonly Scheme[] = [ - /* target */ { - colors: [0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 1], - freq: 1, - }, - /* cube */ { - colors: [0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 5], - freq: 20, - }, - /* quazar */ { - colors: [1, 2, 3, 1, 2, 4, 5, 5, 4, 1, 2, 3, 1, 2, 4, 5, 5, 4, 0], - freq: 16, - }, - /* flower */ { - colors: [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 3], - freq: 32, - }, - /* cyclic */ { - colors: [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6], - freq: 32, - }, - /* vmirror */ { - colors: [0, 1, 2, 3, 4, 5, 3, 4, 2, 0, 1, 6, 7, 8, 9, 7, 8, 6, 10], - freq: 128, - }, - /* hmirror */ { - colors: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 8, 6, 7, 5, 3, 4, 2, 11], - freq: 128, - }, -]; - -const SCHEMES_TOTAL = SCHEMES.map((s): number => s.freq).reduce((a, b): number => a + b); - -const OUTER_CIRCLE: Circle = { - cx: C, - cy: C, - fill: '#eeeeed', - r: C, -}; - -let zeroHash: Uint8Array = new Uint8Array(); - -function getRotation(isSixPoint: boolean): { - r: number; - ro2: number; - r3o4: number; - ro4: number; - rroot3o2: number; - rroot3o4: number; -} { - const r = isSixPoint ? (C / 8) * 5 : (C / 4) * 3; - const rroot3o2 = (r * Math.sqrt(3)) / 2; - const ro2 = r / 2; - const rroot3o4 = (r * Math.sqrt(3)) / 4; - const ro4 = r / 4; - const r3o4 = (r * 3) / 4; - - return { r, r3o4, ro2, ro4, rroot3o2, rroot3o4 }; -} - -function getCircleXY(isSixPoint = false): [number, number][] { - const { r, r3o4, ro2, ro4, rroot3o2, rroot3o4 } = getRotation(isSixPoint); - - return [ - [C, C - r], - [C, C - ro2], - [C - rroot3o4, C - r3o4], - [C - rroot3o2, C - ro2], - [C - rroot3o4, C - ro4], - [C - rroot3o2, C], - [C - rroot3o2, C + ro2], - [C - rroot3o4, C + ro4], - [C - rroot3o4, C + r3o4], - [C, C + r], - [C, C + ro2], - [C + rroot3o4, C + r3o4], - [C + rroot3o2, C + ro2], - [C + rroot3o4, C + ro4], - [C + rroot3o2, C], - [C + rroot3o2, C - ro2], - [C + rroot3o4, C - ro4], - [C + rroot3o4, C - r3o4], - [C, C], - ]; -} - -function findScheme(d: number): Scheme { - let cum = 0; - const schema = SCHEMES.find((schema): boolean => { - cum += schema.freq; - - return d < cum; - }); - - if (!schema) { - throw new Error('Unable to find schema'); - } - - return schema; -} - -function addressToId(address: string): Uint8Array { - if (!zeroHash.length) { - zeroHash = blake2AsU8a(new Uint8Array(32), 512); - } - - return blake2AsU8a(decodeAddress(address), 512).map((x, i) => (x + 256 - zeroHash[i]) % 256); -} - -function getColors(address: string): string[] { - const id = addressToId(address); - const d = Math.floor((id[30] + id[31] * 256) % SCHEMES_TOTAL); - const rot = (id[28] % 6) * 3; - const sat = (Math.floor((id[29] * 70) / 256 + 26) % 80) + 30; - const scheme = findScheme(d); - const palette = Array.from(id).map((x, i): string => { - const b = (x + (i % 28) * 58) % 256; - - if (b === 0) { - return '#444'; - } else if (b === 255) { - return 'transparent'; - } - - const h = Math.floor(((b % 64) * 360) / 64); - const l = [53, 15, 35, 75][Math.floor(b / 64)]; - - return `hsl(${h}, ${sat}%, ${l}%)`; - }); - - return scheme.colors.map((_, i): string => palette[scheme.colors[i < 18 ? (i + rot) % 18 : 18]]); -} - -/** - * @description Generates an array of the circles that make up an identicon - */ -export function polkadotIcon(address: string, { isAlternative }: Options): Circle[] { - const xy = getCircleXY(isAlternative); - let colors: string[]; - - try { - // in some cases, e.g. RN where crypto may not be initialized, chaos can - // happen when hashing, in these cases we just fill with a placeholder - colors = getColors(address); - } catch { - colors = new Array(xy.length).fill('#ddd'); - } - - return [OUTER_CIRCLE].concat( - xy.map( - ([cx, cy], index): Circle => ({ - cx, - cy, - fill: colors[index], - r: Z, - }), - ), - ); -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/wallet-icon.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/wallet-icon.tsx deleted file mode 100644 index a77827bee..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-icon/wallet-icon.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { BaseComponentProps } from '@/app/types'; -import { PolkadotIcon, PolkadotIconProps } from './polkadot-icon'; - -type Props = BaseComponentProps & - Omit & { - address?: string; - }; - -export function WalletIcon({ children, className, size = 20, address, ...rest }: Props) { - return <>{address && }; -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.module.scss b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.module.scss deleted file mode 100644 index 17160330b..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.module.scss +++ /dev/null @@ -1,18 +0,0 @@ -@use '@/utils' as *; - -.wallet { - display: flex; - align-items: center; - font-weight: 700; - font-size: 14px; - line-height: 120%; - letter-spacing: 0.02em; - color: #000; -} - -.icon { - margin-right: 8px; - background-color: #fff; - border-radius: 50%; - border: 1px solid #ebeeee; -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.tsx deleted file mode 100644 index f4c50ab2b..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/WalletItem.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import styles from './WalletItem.module.scss'; -import { SVGComponent } from '@/app/types'; - -type Props = { - icon: SVGComponent; - name: string; -}; - -function WalletItem({ icon: Icon, name }: Props) { - return ( - - - {name} - - ); -} - -export { WalletItem }; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/index.ts deleted file mode 100644 index 761d04caf..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-item/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { WalletItem } from './WalletItem'; - -export { WalletItem }; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.module.scss b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.module.scss deleted file mode 100644 index 0cb877dde..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.module.scss +++ /dev/null @@ -1,227 +0,0 @@ -@use '@/utils' as *; - -.content { - flex: 1 1 100%; - max-height: 430px; -} - -.list { - display: grid; - grid-gap: 15px; - width: 100%; - - &--scroll { - padding-right: 20px; - } - - .walletButton { - width: 100%; - padding-right: 20px; - padding-left: 24px; - justify-content: space-between; - - &:hover { - & .statusAccounts { - color: #00b387; - } - } - } -} - -.status { - display: block; - text-align: right; - - &Text { - display: block; - font-weight: 600; - font-size: 12px; - line-height: 12px; - color: #a2a2a2; - } - - &Accounts { - display: block; - font-weight: 700; - font-size: 11px; - line-height: 10px; - letter-spacing: 0.02em; - color: #02f8bf; - transition: color 250ms ease; - } -} - -.account { - @include gap(10px, right); - display: flex; - align-items: center; - - &Button { - @include gap(12px, right); - justify-content: center; - display: inline-grid; - grid-template-columns: auto auto; - width: 100%; - padding-right: 24px; - padding-left: 24px; - font-weight: 600; - font-size: 16px; - - span { - display: block; - max-width: 22ch; - white-space: nowrap; - @include textOverflow; - } - } - - &Icon { - pointer-events: none; - } -} - -.footer { - margin-top: 22px; - display: flex; - justify-content: space-between; - - .walletButton { - @include gap(8px, right); - display: flex; - align-items: center; - background-color: transparent; - transition: all 0.3s; - - > svg { - color: #acaeae; - } - - &:hover { - opacity: 0.5; - } - } -} - -.changeText { - font-size: 12px; - font-weight: 700; - letter-spacing: 0.02em; - color: #00b387; -} - -.textButton { - display: inline-flex; - justify-content: center; - align-items: center; - font-size: 14px; - transition: opacity 300ms ease; - @include gap(10px, right); - - &:hover { - opacity: 50%; - } -} - -.modal { - position: relative; - z-index: 50; - - &__backdrop { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - - @media screen and (min-width: 768px) { - background-color: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(10px); - } - } - - &__wrapper { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 1; - overflow-y: hidden; - } - - &__container { - display: flex; - min-height: 100%; - align-items: flex-end; - justify-content: center; - - @media screen and (min-width: 768px) { - align-items: center; - } - } - - &__content { - --opacity-closed: 0%; - --opacity-open: 100%; - - position: relative; - width: 100%; - background-color: #f6f8f8; - - @media screen and (min-width: 768px) { - --scale-closed: 90%; - --scale-open: 100%; - - max-width: 400px; - padding: 30px 32px; - border-radius: 4px; - } - - @media screen and (max-width: 767px) { - --y-closed: 32px; - --y-open: 0px; - - padding: 32px 16px 32px; - } - } - - &__header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 24px; - } - - &__title { - font-size: 18px; - font-weight: 700; - line-height: 120%; - letter-spacing: 0.02em; - text-transform: capitalize; - color: #222424; - } - - &__close { - position: relative; - bottom: 2px; - left: 5px; - margin-left: auto; - transition: color 350ms ease; - - &:hover { - color: #777777; - } - } -} - -.external { - color: #00b387; - font-weight: 500; - text-decoration: underline; - text-underline-offset: 2px; - - &:hover { - text-decoration: none; - } -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.tsx deleted file mode 100644 index a65f830b4..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/WalletModal.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import { decodeAddress } from '@gear-js/api'; -import { useAccount, useAlert } from '@gear-js/react-hooks'; -import { Button } from '@/components/ui/button'; -import { WALLETS, Wallets } from '../../consts'; -import { useWallet } from '../../hooks'; -import { WalletItem } from '../wallet-item'; -import styles from './WalletModal.module.scss'; -import { copyToClipboard, isMobileDevice } from '@/app/utils'; -import { useGame } from '@/features/tic-tac-toe/hooks'; -import { useAuth } from '@/features/auth'; -import { ScrollArea } from '@/components/ui/scroll-area/scroll-area'; -import { WalletIcon } from '../wallet-icon'; -import { AnimatePresence, motion } from 'framer-motion'; -import { Dialog } from '@headlessui/react'; -import { variantsOverlay, variantsPanel } from '@/components/ui/modal/modal.variants'; -import clsx from 'clsx'; -import { Sprite } from '@/components/ui/sprite'; -import { ArrayElement } from '@/app/types'; - -export type WalletModalProps = { - open: boolean; - setOpen(value: boolean): void; - onClose?(): void; -}; - -export function WalletModal({ onClose, open, setOpen }: WalletModalProps) { - const alert = useAlert(); - const { extensions, account, accounts } = useAccount(); - const { resetGame, clearGame } = useGame(); - const { signIn, signOut } = useAuth(); - const { wallet, walletAccounts, setWalletId, resetWalletId, getWalletAccounts } = useWallet(); - - const sortWallets = (wallets: Wallets): Wallets => { - const [accountsWallets, subwallet, noAccountsWallets] = wallets.reduce( - (acc: [Wallets, ArrayElement | null, Wallets], item) => { - const id = item[0]; - if (id === 'subwallet-js') { - acc[1] = item; - return acc; - } - - if (getWalletAccounts(id)?.length) { - acc[0].push(item); - return acc; - } - - acc[2].push(item); - return acc; - }, - [[], null, []], - ); - - const sortedAccountsWallets = accountsWallets.sort(([idA], [idB]) => - getWalletAccounts(idA)!.length > getWalletAccounts(idB)!.length ? 1 : -1, - ); - - return subwallet - ? [ - ...(getWalletAccounts(subwallet[0])?.length - ? [subwallet, ...sortedAccountsWallets] - : [...sortedAccountsWallets, subwallet]), - ...noAccountsWallets, - ] - : [...sortedAccountsWallets, ...noAccountsWallets]; - }; - - const getWallets = () => - sortWallets(WALLETS).map(([id, { SVG, name }]) => { - const isEnabled = extensions?.some((extension) => extension.name === id); - const status = isEnabled ? 'Enabled' : 'Disabled'; - - const accountsCount = getWalletAccounts(id)?.length; - const accountsStatus = `${accountsCount} ${accountsCount === 1 ? 'account' : 'accounts'}`; - - const onClick = () => setWalletId(id); - - return ( -
  • - -
  • - ); - }); - - const getAccounts = () => - walletAccounts?.map((_account) => { - const { address, meta } = _account; - - const isActive = address === account?.address; - - const handleClick = async () => { - clearGame(); - await signIn(_account); - setOpen(false); - onClose && onClose(); - }; - - const handleCopyClick = async () => { - const decodedAddress = decodeAddress(address); - await copyToClipboard({ value: decodedAddress, alert }); - setOpen(false); - onClose && onClose(); - }; - - return ( -
  • -
    - - - -
    -
  • - ); - }); - - const handleLogoutButtonClick = () => { - signOut(); - setOpen(false); - resetGame(); - onClose && onClose(); - }; - - const isScrollable = (walletAccounts?.length || 0) > 6; - - return ( - - {open && ( - - - -
    - - )} - - ); -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/index.ts deleted file mode 100644 index 25e1e380e..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet-modal/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { WalletModal } from './WalletModal'; -export type { WalletModalProps } from './WalletModal'; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.module.scss b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.module.scss deleted file mode 100644 index fc336be11..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -@use '@/utils' as *; - -.wrapper { - display: flex; - align-items: center; - @include gap(20px, right); -} - -.button { - @media screen and (max-width: 767px) { - padding-right: 30px; - padding-left: 30px; - font-size: 18px; - } - - span { - display: block; - max-width: 15ch; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.icon { - pointer-events: none; -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.tsx b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.tsx deleted file mode 100644 index 20af08795..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/Wallet.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useState } from 'react'; -import { useAccount } from '@gear-js/react-hooks'; -import { Button } from '@/components/ui/button'; -import styles from './Wallet.module.scss'; -import { WalletIcon } from '../wallet-icon'; -import { DialogsLibrary } from '@/components/ui/dialogs'; -import clsx from 'clsx'; -import { useAccountAvailableBalance } from '@/features/account-available-balance/hooks'; -import { useIsAppReady } from '@/app/hooks/use-is-app-ready'; -import { useInitGame } from '@/features/tic-tac-toe/hooks'; -import { VaraBalance } from '@/components/ui/balance'; -import type { BaseComponentProps } from '@/app/types'; - -export function Wallet({ className }: BaseComponentProps) { - const [open, setOpen] = useState(false); - - const { account } = useAccount(); - const { isAppReady } = useIsAppReady(); - const { availableBalance: balance } = useAccountAvailableBalance(); - const { isGameReady } = useInitGame(); - - const isSigned = isAppReady && !!account && isGameReady; - - return ( - <> -
    - {isSigned && } - -
    - - - - ); -} diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/index.ts deleted file mode 100644 index a3db3b1b4..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/components/wallet/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Wallet } from './Wallet'; diff --git a/frontend/apps/tic-tac-toe/src/features/wallet/index.ts b/frontend/apps/tic-tac-toe/src/features/wallet/index.ts deleted file mode 100644 index 13d01100c..000000000 --- a/frontend/apps/tic-tac-toe/src/features/wallet/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Wallet, WalletModal, WalletIcon } from './components'; diff --git a/frontend/apps/tic-tac-toe/src/pages/home.tsx b/frontend/apps/tic-tac-toe/src/pages/home.tsx index 90f4c2ae7..a183f5486 100644 --- a/frontend/apps/tic-tac-toe/src/pages/home.tsx +++ b/frontend/apps/tic-tac-toe/src/pages/home.tsx @@ -1,7 +1,7 @@ import { useAccount } from '@gear-js/react-hooks'; import { useGame } from '@/features/tic-tac-toe/hooks'; import { Game, Welcome } from '@/features/tic-tac-toe'; -import { Wallet } from '@/features/wallet'; +import { WalletNew as Wallet } from '@dapps-frontend/ui'; import { GameStartButton } from '@/features/tic-tac-toe/components/game-start-button'; import metaTxt from '@/features/tic-tac-toe/assets/meta/tic_tac_toe.meta.txt'; import { useProgramMetadata } from '@/app/hooks'; diff --git a/frontend/apps/tic-tac-toe/vite.config.ts b/frontend/apps/tic-tac-toe/vite.config.ts index be7176022..0274a083f 100644 --- a/frontend/apps/tic-tac-toe/vite.config.ts +++ b/frontend/apps/tic-tac-toe/vite.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, splitVendorChunkPlugin } from 'vite'; +import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react-swc'; import path from 'path'; import nodePolyfills from 'vite-plugin-node-stdlib-browser'; @@ -24,37 +24,8 @@ export default defineConfig(({ mode }) => { build: { outDir: 'build', sourcemap: true, - rollupOptions: { - output: { - manualChunks(id) { - if ( - id.includes('@polkadot') || - id.includes('@substrate') || - id.includes('@scure') || - id.includes('@noble') - ) { - return 'polkadot'; - } - if (id.includes('@open-ish') || id.includes('tslib')) { - return '@open-ish'; - } - if (id.includes('react-router') || id.includes('@remix-run')) { - return '@react-router'; - } - if (id.includes('@headlessui') || id.includes('@radix-ui') || id.includes('framer-motion')) { - return 'app-ui'; - } - if (id.includes('@gear-js') || id.includes('react-transition-group')) { - return '@gear-js'; - } - if (id.includes('@sentry')) { - return '@sentry'; - } - }, - }, - }, }, - plugins: [splitVendorChunkPlugin(), svgr(), react(), nodePolyfills(), eslint()], + plugins: [svgr(), react(), nodePolyfills(), eslint()], assetsInclude: ['**/*.wasm?inline', '**/*.txt?inline'], }; }); diff --git a/frontend/apps/vara-man/package.json b/frontend/apps/vara-man/package.json index be0ea991d..cef6cc3bf 100644 --- a/frontend/apps/vara-man/package.json +++ b/frontend/apps/vara-man/package.json @@ -13,12 +13,12 @@ "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@headlessui/react": "1.7.14", "@mantine/form": "6.0.10", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/react-identicon": "3.5.1", "@radix-ui/react-scroll-area": "1.0.3", diff --git a/frontend/apps/varatube/package.json b/frontend/apps/varatube/package.json index bca1e0868..647031dbf 100644 --- a/frontend/apps/varatube/package.json +++ b/frontend/apps/varatube/package.json @@ -4,16 +4,16 @@ "private": true, "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", - "@dapps-frontend/signless-transactions": "workspace:*", + "@dapps-frontend/hooks": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@mantine/form": "4.2.12", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/react-identicon": "3.1.4", - "@polkadot/types": "10.10.1", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@polkadot/wasm-crypto": "7.2.2", "@testing-library/jest-dom": "5.16.4", @@ -35,7 +35,7 @@ "react-transition-group": "4.4.5", "sass": "1.62.0", "simplebar-react": "3.2.1", - "typescript": "4.9.5", + "typescript": "5.0.2", "web-vitals": "3.3.1" }, "scripts": { diff --git a/frontend/apps/varatube/src/App.tsx b/frontend/apps/varatube/src/App.tsx index 79d84784e..a379bd6dd 100644 --- a/frontend/apps/varatube/src/App.tsx +++ b/frontend/apps/varatube/src/App.tsx @@ -1,20 +1,21 @@ -import { useApi, useAccount } from '@gear-js/react-hooks'; +import { useApi, useAccount, useDeriveBalancesAll, useBalanceFormat } from '@gear-js/react-hooks'; import { Footer } from '@dapps-frontend/ui'; import { Routing } from 'pages'; import { Header, ApiLoader } from 'components'; import { withProviders } from 'hocs'; import { useProgramState } from 'hooks/api'; -import { useAccountAvailableBalanceSync } from 'hooks'; import 'simplebar-react/dist/simplebar.min.css'; import 'App.scss'; import '@gear-js/vara-ui/dist/style.css'; function Component() { + const { account } = useAccount(); const { isApiReady } = useApi(); const { isAccountReady } = useAccount(); const { isSubscriptionsStateRead } = useProgramState(); + const { getFormattedBalanceValue } = useBalanceFormat(); - useAccountAvailableBalanceSync(); + const balances = useDeriveBalancesAll(account?.decodedAddress); const isAppReady = isApiReady && isAccountReady && isSubscriptionsStateRead; diff --git a/frontend/apps/varatube/src/components/layout/header/Header.module.scss b/frontend/apps/varatube/src/components/layout/header/Header.module.scss index 75edb169a..16745ca16 100644 --- a/frontend/apps/varatube/src/components/layout/header/Header.module.scss +++ b/frontend/apps/varatube/src/components/layout/header/Header.module.scss @@ -1,12 +1,6 @@ @use '@gear-js/ui/variables' as *; .header { - width: 100%; - display: flex; - flex-direction: column; - gap: 30px; - z-index: 9; - @media screen and (max-width: 767px) { background: #1A1A1F; position: relative; @@ -14,13 +8,6 @@ } .content { - margin: auto; - width: 100%; - max-width: 1200px; - display: flex; - justify-content: space-between; - align-items: center; - @media screen and (max-width: 767px) { padding: 0 26px; } @@ -59,3 +46,14 @@ } } } + +.balance { + color: #fff; +} + +.menuIcon { + svg path { + fill: #fff; + stroke: #fff; + } +} \ No newline at end of file diff --git a/frontend/apps/varatube/src/components/layout/header/Header.tsx b/frontend/apps/varatube/src/components/layout/header/Header.tsx index 8ec74fd26..9df06d458 100644 --- a/frontend/apps/varatube/src/components/layout/header/Header.tsx +++ b/frontend/apps/varatube/src/components/layout/header/Header.tsx @@ -1,7 +1,7 @@ import { buttonStyles } from '@gear-js/ui'; import clsx from 'clsx'; import { Link } from 'react-router-dom'; -import { MenuHandler } from '@dapps-frontend/ui'; +import { MenuHandler, Header as CommonHeader } from '@dapps-frontend/ui'; import logo from 'assets/images/logo.png'; import { useFTBalance } from 'hooks/api'; import styles from './Header.module.scss'; @@ -10,12 +10,32 @@ function Header() { const tokens = useFTBalance(); return ( -
    -
    + logo - + } + menu={ + + } + className={{ + header: styles.header, + content: styles.content, + }}> + <> @@ -27,18 +47,8 @@ function Header() { Tokens: {tokens}

    )} - - -
    -
    + + ); } diff --git a/frontend/apps/varatube/src/components/modals/purchase-subscription-approve-modal/PurchaseSubscriptionApproveModal.tsx b/frontend/apps/varatube/src/components/modals/purchase-subscription-approve-modal/PurchaseSubscriptionApproveModal.tsx index da244c858..6cfa5bdb1 100644 --- a/frontend/apps/varatube/src/components/modals/purchase-subscription-approve-modal/PurchaseSubscriptionApproveModal.tsx +++ b/frontend/apps/varatube/src/components/modals/purchase-subscription-approve-modal/PurchaseSubscriptionApproveModal.tsx @@ -1,14 +1,14 @@ import { Button, Modal } from '@gear-js/ui'; import styles from './PurchaseSubscriptionApproveModal.module.scss'; -type Props = { amount: string; close: () => void; onSubmit: () => void }; +type Props = { disabledSubmitButton: boolean; amount: string; close: () => void; onSubmit: () => void }; -function PurchaseSubscriptionApproveModal({ amount, close, onSubmit }: Props) { +function PurchaseSubscriptionApproveModal({ disabledSubmitButton, amount, close, onSubmit }: Props) { return (
    You're going to transfer {amount} Tokens -
    ); diff --git a/frontend/apps/varatube/src/components/modals/purchase-subscription-modal/PurchaseSubscriptionModal.tsx b/frontend/apps/varatube/src/components/modals/purchase-subscription-modal/PurchaseSubscriptionModal.tsx index fdfba30b7..3e732e21e 100644 --- a/frontend/apps/varatube/src/components/modals/purchase-subscription-modal/PurchaseSubscriptionModal.tsx +++ b/frontend/apps/varatube/src/components/modals/purchase-subscription-modal/PurchaseSubscriptionModal.tsx @@ -7,7 +7,7 @@ import { periods } from 'consts'; const initialValues = { isRenewal: true, period: periods[0].value }; -type Props = { close: () => void; onSubmit: (values: typeof initialValues) => void }; +type Props = { disabledSubmitButton: boolean; close: () => void; onSubmit: (values: typeof initialValues) => void }; const useForm = (input: UseFormInput>) => { const form = useMantineForm(input); @@ -29,7 +29,7 @@ const useForm = (input: UseFormInput>) => { return { ...form, getCheckboxProps, getRadioProps }; }; -function PurchaseSubscriptionModal({ close, onSubmit }: Props) { +function PurchaseSubscriptionModal({ disabledSubmitButton, close, onSubmit }: Props) { const form = useForm({ initialValues }); const { getInputProps, getCheckboxProps } = form; @@ -43,7 +43,7 @@ function PurchaseSubscriptionModal({ close, onSubmit }: Props) { By confirming your subscription, you hereby authorize VaraTube Inc. to charge your wallet for the amount of tokens for this and future payments.

    -
    -
    @@ -206,10 +227,17 @@ function Subscription() { )} - {isModalOpen && } + {isModalOpen && ( + + )} {isApproveModalOpen && valuesToTransfer && ( diff --git a/frontend/apps/w3bstreaming/config-overrides.js b/frontend/apps/w3bstreaming/config-overrides.js new file mode 100644 index 000000000..9af1f529e --- /dev/null +++ b/frontend/apps/w3bstreaming/config-overrides.js @@ -0,0 +1,17 @@ +const webpack = require('webpack'); +const path = require(`path`); + +const SRC = `src`; + +module.exports = (config) => { + config.plugins.push(new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] })); + config.resolve = { + ...config.resolve, + alias: { + ...config.resolve.alias, + '@': path.resolve(__dirname, `${SRC}`), + '@ui': path.resolve(__dirname, `${SRC}/ui`), + }, + }; + return config; +}; diff --git a/frontend/apps/w3bstreaming/craco.config.js b/frontend/apps/w3bstreaming/craco.config.js deleted file mode 100644 index a490801db..000000000 --- a/frontend/apps/w3bstreaming/craco.config.js +++ /dev/null @@ -1,23 +0,0 @@ -const webpack = require('webpack'); -const path = require(`path`); - -const SRC = `src`; - -module.exports = { - webpack: { - alias: { - '@': path.resolve(__dirname, `${SRC}`), - '@ui': path.resolve(__dirname, `${SRC}/ui`), - }, - plugins: { - add: [ - new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - }), - ], - }, - }, - devServer: { - port: 3000, - }, -}; diff --git a/frontend/apps/w3bstreaming/package.json b/frontend/apps/w3bstreaming/package.json index 92a36b404..ccd3a8ec1 100644 --- a/frontend/apps/w3bstreaming/package.json +++ b/frontend/apps/w3bstreaming/package.json @@ -3,12 +3,7 @@ "version": "0.1.0", "private": true, "devDependencies": { - "@craco/craco": "7.1.0", "@types/lodash.merge": "4.6.7", - "@types/node": "18.17.15", - "@types/react": "18.2.24", - "@types/react-datepicker": "4.11.2", - "@types/react-dom": "18.2.8", "@typescript-eslint/parser": "6.7.3", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "17.0.0", @@ -18,23 +13,22 @@ "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-react": "7.32.2", "eslint-plugin-react-hooks": "4.6.0", - "types": "*", - "typescript": "4.9.5" + "react-app-rewired": "2.2.1" }, "dependencies": { "@dapps-frontend/error-tracking": "workspace:*", "@dapps-frontend/ui": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@gear-js/vara-ui": "0.0.6", "@headlessui/react": "1.7.17", "@mantine/form": "6.0.15", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/extension-inject": "0.46.5", - "@polkadot/react-identicon": "3.5.1", - "@polkadot/types": "10.10.1", + "@polkadot/react-identicon": "3.1.4", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@polkadot/util-crypto": "12.3.2", "@polkadot/wasm-crypto": "7.2.2", @@ -42,7 +36,11 @@ "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "13.4.0", "@testing-library/user-event": "13.5.0", - "assert": "2.1.0", + "@types/node": "18.17.15", + "@types/react": "18.2.33", + "@types/react-datepicker": "4.11.2", + "@types/react-dom": "18.2.14", + "assert": "2.0.0", "axios": "1.4.0", "babel-polyfill": "6.26.0", "buffer": "6.0.3", @@ -65,12 +63,13 @@ "sass": "1.62.0", "socket.io": "4.6.1", "socket.io-client": "4.6.1", + "typescript": "4.9.5", "video.js": "8.3.0", "web-vitals": "2.1.4" }, "scripts": { - "start": "yarn build:packages && craco start", - "build": "yarn build:packages && craco build" + "start": "yarn build:packages && react-app-rewired start", + "build": "yarn build:packages && react-app-rewired build" }, "eslintConfig": { "extends": "./.eslintrc.js" diff --git a/frontend/apps/w3bstreaming/src/components/BurgerMenu/BurgerMenu.tsx b/frontend/apps/w3bstreaming/src/components/BurgerMenu/BurgerMenu.tsx index 1b7eb1b7d..7104ee26d 100644 --- a/frontend/apps/w3bstreaming/src/components/BurgerMenu/BurgerMenu.tsx +++ b/frontend/apps/w3bstreaming/src/components/BurgerMenu/BurgerMenu.tsx @@ -1,7 +1,7 @@ import { useNavigate } from 'react-router-dom'; import { useAccount } from '@gear-js/react-hooks'; import { cx } from '@/utils'; -import { WalletInfo } from '@/features/Wallet/components'; +// import { WalletInfo } from '@/features/Wallet/components'; import { routes } from '@/App.routes'; import { Button } from '@/ui'; import closeMenuIcon from '@/assets/icons/cross-icon.svg'; @@ -27,7 +27,7 @@ function BurgerMenu({ burgerMenuHandler }: BurgerMenuProps) {
    - + {/* */}
    {account && (
    diff --git a/frontend/apps/w3bstreaming/src/components/Header/Header.module.scss b/frontend/apps/w3bstreaming/src/components/Header/Header.module.scss index 200f91208..ca6738c21 100644 --- a/frontend/apps/w3bstreaming/src/components/Header/Header.module.scss +++ b/frontend/apps/w3bstreaming/src/components/Header/Header.module.scss @@ -2,20 +2,12 @@ @use '~styles/mixins' as *; .header { - height: 100px; - width: 100%; box-sizing: border-box; background: theme-var($background-secondary); } -.container { - height: 100%; +.content { padding: 20px 0; - max-width: 1200px; - display: flex; - justify-content: space-between; - align-items: center; - margin: auto; @include lg { padding-left: 10px; @@ -45,19 +37,3 @@ color: theme-var($green-special) } } - -.blur-background { - position: fixed; - top: 0; - left: 0; - background: rgba(39, 44, 63, 0.11); - backdrop-filter: blur(16px); - z-index: 4; - width: 100%; - height: 100%; - display: none; - - @include lg { - display: block - } -} \ No newline at end of file diff --git a/frontend/apps/w3bstreaming/src/components/Header/Header.tsx b/frontend/apps/w3bstreaming/src/components/Header/Header.tsx index d5bbcad22..e83c44f21 100644 --- a/frontend/apps/w3bstreaming/src/components/Header/Header.tsx +++ b/frontend/apps/w3bstreaming/src/components/Header/Header.tsx @@ -1,80 +1,50 @@ -import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; -import { Button, Link } from '@ui'; +import { Link } from '@ui'; import { useAccount } from '@gear-js/react-hooks'; -import { WalletModal, WalletInfo } from '@/features/Wallet/components'; +import { MenuHandler, Header as CommonHeader } from '@dapps-frontend/ui'; import { cx } from '@/utils'; import styles from './Header.module.scss'; import logo from '@/assets/icons/logo.svg'; import { HeaderProps } from './Header.interfaces'; import { useMediaQuery } from '@/hooks'; -import menuIcon from '@/assets/icons/burger-menu-icon.svg'; -import { BurgerMenu } from '../BurgerMenu/BurgerMenu'; function Header({ menu }: HeaderProps) { const location = useLocation(); const { account } = useAccount(); - const [isWalletModalOpen, setIsWalletModalOpen] = useState(false); const isMobile = useMediaQuery(600); - const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const burgerMenuHandler = () => { - setIsMobileMenuOpen(false); - }; - - useEffect(() => { - if (isMobileMenuOpen && !isMobile) { - burgerMenuHandler(); - } - }, [isMobile, isMobileMenuOpen]); - - const handleCloseWalletModal = () => { - setIsWalletModalOpen(false); - }; return ( - <> -
    -
    - - - - {account && !isMobile && ( -
    -
    - {isMobileMenuOpen && ( - <> -
    - - + return ( + +

    + {item} +

    + + ); + })} + )} - - - + ); } diff --git a/frontend/apps/w3bstreaming/src/features/Auth/hooks.ts b/frontend/apps/w3bstreaming/src/features/Auth/hooks.ts deleted file mode 100644 index 93cb079ff..000000000 --- a/frontend/apps/w3bstreaming/src/features/Auth/hooks.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { useEffect } from 'react'; -import { useSearchParams } from 'react-router-dom'; -import { useAtom } from 'jotai'; -import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; -import { Account, useAccount } from '@gear-js/react-hooks'; -import { useWallet } from '../Wallet/hooks'; -import { IS_AUTH_READY_ATOM, USER_ADDRESS_ATOM } from './atoms'; -import { fetchAuth } from './utils'; -import { CB_UUID_KEY } from './consts'; -import { AuthResponse } from './types'; - -export function useAuth() { - const [isAuthReady, setIsAuthReady] = useAtom(IS_AUTH_READY_ATOM); - const [userAddress, setIsUserAddress] = useAtom(USER_ADDRESS_ATOM); - const [query, setQuery] = useSearchParams(); - - const { login, logout, account } = useAccount(); - const { resetWalletId } = useWallet(); - - const resetSearchQuery = () => { - query.delete('uuid'); - - setQuery(query); - }; - - const signOut = () => { - logout(); - resetWalletId(); - localStorage.removeItem(CB_UUID_KEY); - }; - - const auth = async () => { - const uuid = query.get('uuid'); - const cbUuid = localStorage.getItem(CB_UUID_KEY); - - if (query.size && uuid) { - localStorage.setItem(CB_UUID_KEY, uuid); - } - setIsAuthReady(false); - if (account) { - try { - const res = await fetchAuth('api/user/auth', 'POST', { - coinbaseUID: uuid || cbUuid, - substrate: account.decodedAddress, - }); - - if (res?.success) { - setIsUserAddress(res.content.user.address); - } - - if (!res?.success) { - setIsUserAddress(null); - } - - resetSearchQuery(); - } catch (err) { - console.log(err); - } - } - setIsAuthReady(true); - }; - - const signIn = async (_account: InjectedAccountWithMeta) => { - await login(_account); - }; - - return { signIn, signOut, auth, isAuthReady, userAddress }; -} - -function useAuthSync() { - const { isAccountReady, account } = useAccount(); - const { auth } = useAuth(); - - useEffect(() => { - if (!isAccountReady) { - return; - } - - auth(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isAccountReady, account?.decodedAddress]); -} - -export { useAuthSync }; diff --git a/frontend/apps/w3bstreaming/src/features/StreamTeasers/components/StreamTeaser/StreamTeaser.module.scss b/frontend/apps/w3bstreaming/src/features/StreamTeasers/components/StreamTeaser/StreamTeaser.module.scss index cd703198a..d64a53a7a 100644 --- a/frontend/apps/w3bstreaming/src/features/StreamTeasers/components/StreamTeaser/StreamTeaser.module.scss +++ b/frontend/apps/w3bstreaming/src/features/StreamTeasers/components/StreamTeaser/StreamTeaser.module.scss @@ -131,7 +131,7 @@ margin-bottom: 10px; font-size: 22px; font-weight: 600; - line-height: 22px; + line-height: 26px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.interfaces.ts b/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.interfaces.ts deleted file mode 100644 index 96a36fdb9..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.interfaces.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Account } from '@gear-js/react-hooks/dist/esm/types'; - -export type WalletInfoProps = { - account?: Account; - withoutBalance?: boolean; - buttonClassName?: string; -}; diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.module.scss b/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.module.scss deleted file mode 100644 index c9617a2eb..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.module.scss +++ /dev/null @@ -1,92 +0,0 @@ -@use '~styles/variables' as *; -@use '~styles/mixins' as *; - -.wallet-info { - display: flex; - align-items: center; -} - -.balance { - display: flex; - align-items: baseline; - padding: 12px 0; - margin-right: 20px; - - &-coin-image { - align-self: center; - margin-right: 10px; - width: 24px; - height: 24px; - } - - &-value { - font-size: 20px; - font-weight: 700; - letter-spacing: -2%; - } - - &-decimals { - margin-left: 1px; - font-size: 10px; - font-weight: 500; - line-height: 1.4; - color: theme-var($text-primary); - text-transform: uppercase; - opacity: 60%; - } - - &-currency-name { - margin-left: 4px; - font-size: 10px; - font-weight: 500; - line-height: 1.4; - color: theme-var($text-primary); - text-transform: uppercase; - opacity: 60%; - } -} - -.description { - height: 50px; - min-width: 128px; - max-width: 200px; - padding: 0 10px; - display: flex; - justify-content: center; - align-items: center; - text-transform: capitalize; - border: none; - outline: none; - background: theme-var($background-tertiary); - cursor: pointer; - - &-icon { - min-width: 16px; - min-height: 16px; - margin-right: 10px; - background: theme-var($background-primary); - border-radius: 50px; - } - - &-name { - font-size: 16px; - font-weight: 700; - letter-spacing: 0.03em; - color: theme-var($text-secondary); - } -} - -.score { - padding-right: 60px; - - @include md { - padding-right: 20px; - } -} - -.connect-btn { - width: 200px; - font-size: 18px; - font-weight: 700; - letter-spacing: 0.03em; -} diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.tsx b/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.tsx deleted file mode 100644 index e7e042954..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletInfo/WalletInfo.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useState } from 'react'; -import Identicon from '@polkadot/react-identicon'; -import { useAtom } from 'jotai'; -import { cx } from '@/utils'; -import { ADDRESS } from '@/consts'; -import { CONTRACT_ADDRESS_ATOM } from '@/atoms'; -import varaCoin from '@/assets/icons/vara-coin.svg'; -import tVaraCoin from '@/assets/icons/tvara-coin.svg'; -import { WalletInfoProps } from './WalletInfo.interfaces'; -import { Button } from '@/ui'; -import { WalletModal } from '../WalletModal'; -import styles from './WalletInfo.module.scss'; -import { useAccountAvailableBalance } from '../../hooks'; - -function WalletInfo({ account, withoutBalance, buttonClassName }: WalletInfoProps) { - const address = useAtom(CONTRACT_ADDRESS_ATOM); - const { availableBalance: balance, isAvailableBalanceReady } = useAccountAvailableBalance(); - const [isWalletModalOpen, setIsWalletModalOpen] = useState(false); - - const handleCloseWalletModal = () => { - setIsWalletModalOpen(false); - }; - - const handleOpenWalletModal = () => { - setIsWalletModalOpen(true); - }; - - const balanceValue = (balance?.value || '0').split('.'); - const balanceAmount = balanceValue[0].replaceAll(/,|\s/g, ' '); - const balanceDecimals = balanceValue[1]; - - return ( - <> - {account && isAvailableBalanceReady ? ( -
    - {!withoutBalance && ( -
    - vara coin -
    {balanceAmount}
    - {balanceDecimals &&
    {`.${balanceDecimals}`}
    } -
    {balance?.unit}
    -
    - )} - -
    - ) : ( - - - ); - }); - - const getAccounts = () => - walletAccounts?.map((_account) => { - const { address, meta } = _account; - - const isActive = address === account?.address; - - const handleClick = async () => { - await signIn(_account); - setOpen(false); - onClose(); - }; - - const handleCopyClick = async () => { - const decodedAddress = decodeAddress(address); - await copyToClipboard({ value: decodedAddress, alert }); - setOpen(false); - onClose(); - }; - - return ( -
  • -
    - - - -
    -
  • - ); - }); - - const handleLogoutButtonClick = () => { - signOut(); - setOpen(false); - onClose(); - }; - - const isScrollable = (walletAccounts?.length || 0) > 6; - - return ( - - {open && ( - - - -
    -
    - -
    - - Wallet connection - - -
    - {accounts?.length ? ( - -
      - {getAccounts() || getWallets()} -
    -
    - ) : ( - <> - {isMobileDevice ? ( -

    - To use this application on the mobile devices, open this page inside the compatible wallets like - SubWallet or Nova. -

    - ) : ( -

    - A compatible wallet was not found or is disabled. Install it following the{' '} - - instructions - - . -

    - )} - - )} - - {wallet && ( -
    - - - {account && ( - - )} -
    - )} -
    -
    -
    -
    - )} -
    - ); -} - -export { WalletModal }; diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/index.ts b/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/index.ts deleted file mode 100644 index 9bab74d75..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/WalletModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './WalletModal'; diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/account-icon.tsx b/frontend/apps/w3bstreaming/src/features/Wallet/components/account-icon.tsx deleted file mode 100644 index 652f2928e..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/account-icon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { lazy, Suspense } from 'react'; -import { IdentityProps } from '@polkadot/react-identicon/types'; - -const Identicon = lazy(() => import('@polkadot/react-identicon')); - -type AccountIconProps = any; - -export function AccountIcon({ children, className, size = 20, theme = 'polkadot', ...rest }: AccountIconProps) { - return ( - }> - - - ); -} diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/components/index.ts b/frontend/apps/w3bstreaming/src/features/Wallet/components/index.ts deleted file mode 100644 index c3a6cdea7..000000000 --- a/frontend/apps/w3bstreaming/src/features/Wallet/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './WalletModal'; -export * from './WalletItem'; -export * from './WalletInfo'; diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/consts.ts b/frontend/apps/w3bstreaming/src/features/Wallet/consts.ts index 89c02a3fd..6f32bee2b 100644 --- a/frontend/apps/w3bstreaming/src/features/Wallet/consts.ts +++ b/frontend/apps/w3bstreaming/src/features/Wallet/consts.ts @@ -1,32 +1,4 @@ import { atom } from 'jotai'; -import EnkryptSVG from '@/assets/icons/enkrypt-icon.svg'; -import PolkadotSVG from '@/assets/icons/polkadot-js-icon.svg'; -import SubWalletSVG from '@/assets/icons/sub-wallet-icon.svg'; -import TalismanSVG from '@/assets/icons/talisman-icon.svg'; -import NovaSVG from '@/assets/icons/nova.svg'; -import { WalletValue } from './types'; - -export const WALLET_ID_LOCAL_STORAGE_KEY = 'wallet'; - -export const isNovaWallet = !!window?.walletExtension?.isNovaWallet; - -export const WALLET = isNovaWallet - ? { - 'polkadot-js': { name: 'Nova Wallet', SVG: NovaSVG }, - 'subwallet-js': { name: 'SubWallet', SVG: SubWalletSVG }, - } - : { - 'polkadot-js': { name: 'Polkadot JS', SVG: PolkadotSVG }, - 'subwallet-js': { name: 'SubWallet', SVG: SubWalletSVG }, - talisman: { name: 'Talisman', SVG: TalismanSVG }, - enkrypt: { name: 'Enkrypt', SVG: EnkryptSVG }, - }; - -export type WalletId = keyof typeof WALLET; - -export type Wallets = [WalletId, WalletValue][]; - -export const WALLETS = Object.entries(WALLET) as Wallets; export const IS_AVAILABLE_BALANCE_READY = atom(false); export const AVAILABLE_BALANCE = atom( diff --git a/frontend/apps/w3bstreaming/src/features/Wallet/hooks.ts b/frontend/apps/w3bstreaming/src/features/Wallet/hooks.ts index 7a64cb180..90a41f8a2 100644 --- a/frontend/apps/w3bstreaming/src/features/Wallet/hooks.ts +++ b/frontend/apps/w3bstreaming/src/features/Wallet/hooks.ts @@ -4,8 +4,7 @@ import { useAtomValue, useSetAtom } from 'jotai'; import { CreateType } from '@gear-js/api'; import { formatBalance } from '@polkadot/util'; import { useAlert, useAccount, useApi, useBalance } from '@gear-js/react-hooks'; -import { LOCAL_STORAGE } from '@/consts'; -import { AVAILABLE_BALANCE, IS_AVAILABLE_BALANCE_READY, WALLET, WALLET_ID_LOCAL_STORAGE_KEY, WalletId } from './consts'; +import { AVAILABLE_BALANCE, IS_AVAILABLE_BALANCE_READY } from './consts'; import { SystemAccount } from './types'; function useWasmMetadata(source: RequestInfo | URL) { @@ -25,37 +24,6 @@ function useWasmMetadata(source: RequestInfo | URL) { return { buffer: data }; } -function useWallet() { - const { accounts } = useAccount(); - - const [walletId, setWalletId] = useState(localStorage[LOCAL_STORAGE.WALLET]); - - const resetWalletId = () => setWalletId(undefined); - - const getWalletAccounts = (id: WalletId) => accounts?.filter(({ meta }) => meta.source === id) || []; - - const saveWallet = () => walletId && localStorage.setItem(LOCAL_STORAGE.WALLET, walletId); - - const removeWallet = () => localStorage.removeItem(LOCAL_STORAGE.WALLET); - - const wallet = walletId && WALLET[walletId]; - const walletAccounts = walletId && getWalletAccounts(walletId); - - return { wallet, walletAccounts, setWalletId, resetWalletId, getWalletAccounts, saveWallet, removeWallet }; -} - -function useWalletSync() { - const { account, isAccountReady } = useAccount(); - const { address } = account || {}; - - useEffect(() => { - if (!isAccountReady) return; - if (!account) return localStorage.removeItem(WALLET_ID_LOCAL_STORAGE_KEY); - - localStorage.setItem(WALLET_ID_LOCAL_STORAGE_KEY, account.meta.source); - }, [isAccountReady, address, account]); -} - export function useAccountAvailableBalance() { const isAvailableBalanceReady = useAtomValue(IS_AVAILABLE_BALANCE_READY); const availableBalance = useAtomValue(AVAILABLE_BALANCE); @@ -66,7 +34,7 @@ export function useAccountAvailableBalance() { export function useAccountAvailableBalanceSync() { const { isAccountReady, account } = useAccount(); const { api, isApiReady } = useApi(); - const { balance, isBalanceReady } = useBalance(account?.decodedAddress); + const { balance } = useBalance(account?.decodedAddress); const isReady = useAtomValue(IS_AVAILABLE_BALANCE_READY); const setIsReady = useSetAtom(IS_AVAILABLE_BALANCE_READY); @@ -114,7 +82,7 @@ export function useAccountAvailableBalanceSync() { } else { setIsReady(true); } - }, [account, api, isAccountReady, isApiReady, isReady, balance, isBalanceReady, setAvailableBalance, setIsReady]); + }, [account, api, isAccountReady, isApiReady, isReady, balance]); } -export { useWalletSync, useWallet, useWasmMetadata }; +export { useWasmMetadata }; diff --git a/frontend/apps/w3bstreaming/src/pages/MainPage/components/IntrodutionInfo/IntrodutionInfo.tsx b/frontend/apps/w3bstreaming/src/pages/MainPage/components/IntrodutionInfo/IntrodutionInfo.tsx index 8d6f705b4..9817f2a3a 100644 --- a/frontend/apps/w3bstreaming/src/pages/MainPage/components/IntrodutionInfo/IntrodutionInfo.tsx +++ b/frontend/apps/w3bstreaming/src/pages/MainPage/components/IntrodutionInfo/IntrodutionInfo.tsx @@ -5,7 +5,6 @@ import mainFrame from '@/assets/icons/main-page-frame.png'; import animImg from '@/assets/icons/main-page-wara-anim.png'; import courtain from '@/assets/icons/courtain.png'; import { cx } from '@/utils'; -import { WalletModal } from '@/features/Wallet/components/WalletModal'; function IntrodutionInfo() { const [isWalletModalOpen, setIsWalletModalOpen] = useState(false); @@ -36,7 +35,7 @@ function IntrodutionInfo() { courtain some stuff
    - + {/* */}
    ); } diff --git a/frontend/packages/gasless-transactions/README.md b/frontend/packages/gasless-transactions/README.md index be2034690..6beac8f2f 100644 --- a/frontend/packages/gasless-transactions/README.md +++ b/frontend/packages/gasless-transactions/README.md @@ -10,33 +10,35 @@ yarn add @dapps-frontend/gasless-transactions ## Use -Import initGaslessTransactions function from @dapps-frontend/gasless-transactions in your utils and execute it to get requred tools for using gasless transactions. You should pass required arguments in it: +Import GaslessTransactionsProvider from @dapps-frontend/gasless-transactions in your index.tsx and wrap your application. You should pass required arguments in it: ```jsx -import { initGasslessTransactions } from '@dapps-frontend/gasless-transactions'; +import { GaslessTransactionsProvider } from '@dapps-frontend/gasless-transactions'; -export const gaslessTransactions = initGasslessTransactions({ - programId: // Contract address - backendAddress: // Address of the backend managing gasless transactions handling - voucherLimit?: // OPTIONAL. A limit when voucher balance needs to be replenished. voucherLimit is 18 by default -}); + + + ``` -An object returned from `initGasslessTransactions` contains a set of tools for handling gasless transactrions. +The package provides `useGaslessTransactions` hook which returns a context with all required properties. -### useFetchVoucher +### useGaslessTransactions -This hook creates a voucher for current account and automaticly updates its balance when it becomes lower than `voucherLimit`. +This hook currently returns two properties: ```jsx -const { useFetchVoucher } = gaslessTransactions; +import { useGaslessTransactions } from '@dapps-frontend/gasless-transactions'; -const { isVoucher, isLoading, updateBalance } = useFetchVoucher(); +const { voucherId, isLoadingVoucher } = useGaslessTransactions(); ``` -`isVoucher` is the boolean variable which shows if a voucher does exist for this account or not +`voucherId` - id of a created voucher for current account -`isLoading` is true if a voucher is creating or updating the balance in this moment +`isLoadingVoucher` - a boolean value indicating whether the voucher is being created/updated at the moment -`updateBalance` allows to manually update voucher balance if needed +You can use voucher id to get all required details via methods provided with @gear-js/api diff --git a/frontend/packages/gasless-transactions/package.json b/frontend/packages/gasless-transactions/package.json index 0afbbf262..ce77466a1 100644 --- a/frontend/packages/gasless-transactions/package.json +++ b/frontend/packages/gasless-transactions/package.json @@ -11,8 +11,8 @@ }, "peerDependencies": { "@dapps-frontend/signless-transactions": "workspace:*", - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.2", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "react": "18.2.0", "react-dom": "18.2.0" }, @@ -30,9 +30,6 @@ "vite": "4.4.5", "vite-plugin-dts": "3.5.1" }, - "dependencies": { - "jotai": "2.6.0" - }, "files": [ "dist" ], diff --git a/frontend/packages/gasless-transactions/src/atoms.ts b/frontend/packages/gasless-transactions/src/atoms.ts deleted file mode 100644 index 4ec6914b1..000000000 --- a/frontend/packages/gasless-transactions/src/atoms.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { atom } from 'jotai'; - -export const IS_CREATING_VOUCHER_ATOM = atom(false); - -export const IS_UPDATING_VOUCHER_ATOM = atom(false); diff --git a/frontend/packages/gasless-transactions/src/context/consts.ts b/frontend/packages/gasless-transactions/src/context/consts.ts new file mode 100644 index 000000000..d3a2dc4fe --- /dev/null +++ b/frontend/packages/gasless-transactions/src/context/consts.ts @@ -0,0 +1,4 @@ +export const DEFAULT_VALUES = { + voucherId: undefined, + isLoadingVoucher: false, +}; diff --git a/frontend/packages/gasless-transactions/src/context/index.tsx b/frontend/packages/gasless-transactions/src/context/index.tsx new file mode 100644 index 000000000..be1ecbf9d --- /dev/null +++ b/frontend/packages/gasless-transactions/src/context/index.tsx @@ -0,0 +1,115 @@ +import { ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'; +import { Value } from './types'; +import { DEFAULT_VALUES } from './consts'; +import { useAccount, useBalance, useBalanceFormat } from '@gear-js/react-hooks'; +import { HexString } from '@gear-js/api'; + +const GaslessTransactionsContext = createContext(DEFAULT_VALUES); +const { Provider } = GaslessTransactionsContext; + +type Props = { + programId: HexString; + backendAddress: string; + voucherLimit: number; + children: ReactNode; +}; + +function GaslessTransactionsProvider({ backendAddress, programId, voucherLimit, children }: Props) { + const { account } = useAccount(); + const { getFormattedBalanceValue } = useBalanceFormat(); + const [voucherId, setVoucherId] = useState(undefined); + const [isRequestingVoucher, setIsRequestingVoucher] = useState(false); + const [isUpdatingVoucher, setIsUpdatingVoucher] = useState(false); + + const { balance } = useBalance(voucherId || account?.decodedAddress); + + const requestVoucher = async () => { + try { + const response = await fetch(`${backendAddress}api/voucher/request`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ account: account?.address, program: programId }), + }); + + const data = await response.json(); + + if (data?.error) { + console.log(`Voucher is not fetched - ${data.error}`); + } + + return data.voucherId; + } catch (error: any) { + console.log('Error when fetching voucher'); + console.log(error); + + return undefined; + } + }; + + const fetchVoucherId = async () => { + try { + setIsRequestingVoucher(true); + const createdVoucherId = await requestVoucher(); + + if (createdVoucherId) { + setVoucherId(createdVoucherId); + } + + setIsRequestingVoucher(false); + } catch (error) { + setIsRequestingVoucher(false); + } + }; + + const updateBalance = useCallback(async () => { + const formattedBalance = balance && getFormattedBalanceValue(balance.toString()).toFixed(); + const isBalanceLow = Number(formattedBalance) < voucherLimit; + + if (isBalanceLow && voucherId) { + setIsUpdatingVoucher(true); + + try { + const createdVoucherId = await requestVoucher(); + + if (createdVoucherId) { + setVoucherId(createdVoucherId); + } + + setIsUpdatingVoucher(false); + } catch (error) { + console.log('error'); + } + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [balance]); + + useEffect(() => { + if (account?.address) { + fetchVoucherId(); + } + }, [account?.address]); + + useEffect(() => { + setVoucherId(undefined); + }, [account?.address]); + + useEffect(() => { + if (voucherId) { + updateBalance(); + } + }, [updateBalance, voucherId]); + + const value = { + voucherId, + isLoadingVoucher: isRequestingVoucher || isUpdatingVoucher, + }; + + return {children}; +} + +const useGaslessTransactions = () => useContext(GaslessTransactionsContext); + +export { GaslessTransactionsProvider, useGaslessTransactions }; diff --git a/frontend/packages/gasless-transactions/src/context/types.ts b/frontend/packages/gasless-transactions/src/context/types.ts new file mode 100644 index 000000000..45053ecf8 --- /dev/null +++ b/frontend/packages/gasless-transactions/src/context/types.ts @@ -0,0 +1,6 @@ +import { HexString } from '@gear-js/api'; + +export type Value = { + voucherId: HexString | undefined; + isLoadingVoucher: boolean; +}; diff --git a/frontend/packages/gasless-transactions/src/hooks/index.ts b/frontend/packages/gasless-transactions/src/hooks/index.ts deleted file mode 100644 index 72021094e..000000000 --- a/frontend/packages/gasless-transactions/src/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './use-fetch-voucher'; diff --git a/frontend/packages/gasless-transactions/src/hooks/use-fetch-voucher.ts b/frontend/packages/gasless-transactions/src/hooks/use-fetch-voucher.ts deleted file mode 100644 index 72108cf2f..000000000 --- a/frontend/packages/gasless-transactions/src/hooks/use-fetch-voucher.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { decodeAddress } from '@gear-js/api'; -import { useBalanceFormat, useAccount, useVoucher } from '@gear-js/react-hooks'; - -import { useSignlessTransactions } from '@dapps-frontend/signless-transactions'; - -import { useState, useEffect, useMemo, useCallback } from 'react'; -import { useAtom } from 'jotai'; - -import { IS_CREATING_VOUCHER_ATOM, IS_UPDATING_VOUCHER_ATOM } from '../atoms'; -import { UseFetchVoucherProps } from '../types'; - -export function useFetchVoucher({ programId, backendAddress, voucherLimit = 18 }: UseFetchVoucherProps) { - const { pair } = useSignlessTransactions(); - const { account } = useAccount(); - - const accountAddress = pair ? decodeAddress(pair.address) : account?.decodedAddress; - const { isVoucherExists, voucherBalance } = useVoucher(programId, accountAddress); - - const { getFormattedBalanceValue } = useBalanceFormat(); - - const [voucher, setVoucher] = useState(false); - const [isCreating, setIsCreating] = useAtom(IS_CREATING_VOUCHER_ATOM); - const [isUpdating, setIsUpdating] = useAtom(IS_UPDATING_VOUCHER_ATOM); - - const createVoucher = async () => { - try { - const response = await fetch(backendAddress, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ account: accountAddress }), - }); - - if (response.status === 200) { - return true; - } - } catch (error) { - console.error('error creating voucher: ', error); - } - - return false; - }; - - useEffect(() => { - if (accountAddress && isVoucherExists !== undefined && backendAddress) { - const fetchData = async () => { - try { - setIsCreating(true); - const availableBack = await fetch(backendAddress); - - if (availableBack?.status === 200) { - if (isVoucherExists) { - setVoucher(true); - } else { - const createdVoucher = await createVoucher(); - if (createdVoucher) { - setVoucher(true); - } - } - } - setIsCreating(false); - } catch (error) { - setIsCreating(false); - } - }; - - fetchData(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [accountAddress, isVoucherExists, backendAddress]); - - const updateBalance = useCallback(async () => { - const formattedBalance = voucherBalance && getFormattedBalanceValue(voucherBalance.toString()).toFixed(); - const isBalanceLow = formattedBalance < voucherLimit; - - if (isBalanceLow) { - setIsUpdating(true); - - const createdVoucher = await createVoucher(); - - if (createdVoucher) { - setVoucher(true); - } - - setIsUpdating(false); - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [voucherBalance]); - - useEffect(() => { - setVoucher(false); - }, [accountAddress]); - - useEffect(() => { - if (voucher) { - updateBalance(); - } - }, [updateBalance, voucher]); - - const isVoucher = useMemo(() => voucher, [voucher]); - - const isLoading = isCreating || isUpdating; - - return { isVoucher, isLoading, updateBalance }; -} diff --git a/frontend/packages/gasless-transactions/src/index.ts b/frontend/packages/gasless-transactions/src/index.ts index 1b56574fd..c7aacd0d2 100644 --- a/frontend/packages/gasless-transactions/src/index.ts +++ b/frontend/packages/gasless-transactions/src/index.ts @@ -1,6 +1,3 @@ -import { useFetchVoucher as useFetchVoucherHook } from './hooks'; -import { InitGasslessTransactions } from './types'; +import { GaslessTransactionsProvider, useGaslessTransactions } from './context'; -export const initGasslessTransactions = ({ programId, backendAddress, voucherLimit }: InitGasslessTransactions) => ({ - useFetchVoucher: () => useFetchVoucherHook({ programId, backendAddress, voucherLimit }), -}); +export { GaslessTransactionsProvider, useGaslessTransactions }; diff --git a/frontend/packages/hooks/package.json b/frontend/packages/hooks/package.json index 015c77583..a58d462c3 100644 --- a/frontend/packages/hooks/package.json +++ b/frontend/packages/hooks/package.json @@ -10,8 +10,10 @@ "preview": "vite preview" }, "peerDependencies": { - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.3", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", + "@polkadot/types": "10.10.1", + "@polkadot/util": "12.3.2", "react": "18.2.0", "react-dom": "18.2.0" }, @@ -32,5 +34,8 @@ "dist" ], "main": "dist/hooks.js", - "types": "dist/index.d.ts" + "types": "dist/index.d.ts", + "dependencies": { + "@polkadot/extension-inject": "0.46.5" + } } diff --git a/frontend/packages/hooks/src/consts.ts b/frontend/packages/hooks/src/consts.ts new file mode 100644 index 000000000..d84473f9e --- /dev/null +++ b/frontend/packages/hooks/src/consts.ts @@ -0,0 +1 @@ +export const VOUCHER_MIN_LIMIT = 18; diff --git a/frontend/packages/hooks/src/hooks/index.ts b/frontend/packages/hooks/src/hooks/index.ts index 749ac7a73..b7c101107 100644 --- a/frontend/packages/hooks/src/hooks/index.ts +++ b/frontend/packages/hooks/src/hooks/index.ts @@ -1,4 +1,6 @@ import { useCountdown } from './use-countdown'; import { useProgramMetadata } from './use-program-metadata'; +import { useHandleCalculateGas } from './use-calculate-gas'; +import { useCheckBalance } from './use-check-balance'; -export { useCountdown, useProgramMetadata }; +export { useCountdown, useProgramMetadata, useHandleCalculateGas, useCheckBalance }; diff --git a/frontend/packages/hooks/src/hooks/use-calculate-gas.ts b/frontend/packages/hooks/src/hooks/use-calculate-gas.ts new file mode 100644 index 000000000..1ef9d4b1d --- /dev/null +++ b/frontend/packages/hooks/src/hooks/use-calculate-gas.ts @@ -0,0 +1,32 @@ +import { GasInfo, HexString, ProgramMetadata } from '@gear-js/api'; +import { + useAlert, + useHandleCalculateGas as useCalculateGasNative, + withoutCommas, + useDeriveBalancesAll, + useAccount, + useApi, +} from '@gear-js/react-hooks'; +import { AnyJson, AnyNumber } from '@polkadot/types/types'; + +const useHandleCalculateGas = (address: HexString, meta: ProgramMetadata | undefined) => { + const { api } = useApi(); + const { account } = useAccount(); + const balances = useDeriveBalancesAll(account?.decodedAddress); + const calculateGasNative = useCalculateGasNative(address, meta); + + const alert = useAlert(); + + return (initPayload: AnyJson, value?: AnyNumber | undefined): Promise => { + const balance = Number(withoutCommas(balances?.freeBalance.toString() || '')); + const existentialDeposit = Number(withoutCommas(api?.existentialDeposit.toString() || '')); + + if (!balance || balance < existentialDeposit) { + alert.error(`Low balance when calculating gas`); + } + + return calculateGasNative(initPayload, value); + }; +}; + +export { useHandleCalculateGas }; diff --git a/frontend/packages/hooks/src/hooks/use-check-balance.tsx b/frontend/packages/hooks/src/hooks/use-check-balance.tsx new file mode 100644 index 000000000..6efb88dfe --- /dev/null +++ b/frontend/packages/hooks/src/hooks/use-check-balance.tsx @@ -0,0 +1,45 @@ +import { useAccount, useAlert, useApi, useBalance, useBalanceFormat, withoutCommas } from '@gear-js/react-hooks'; +import { stringShorten } from '@polkadot/util'; + +type Props = { + gaslessVoucherId?: `0x${string}`; + signlessPairVoucherId?: string; +}; + +function useCheckBalance(args?: Props) { + const { signlessPairVoucherId, gaslessVoucherId } = args || {}; + const { api } = useApi(); + const { account } = useAccount(); + const voucherAddress = signlessPairVoucherId || account?.decodedAddress; + + const { balance } = useBalance(gaslessVoucherId || voucherAddress); + const { getFormattedBalanceValue, getFormattedGasValue } = useBalanceFormat(); + const alert = useAlert(); + + const checkBalance = (limit: number, callback: () => void, onError?: () => void) => { + const chainBalance = Number(getFormattedBalanceValue(Number(withoutCommas(balance?.toString() || ''))).toFixed()); + const valuePerGas = Number(withoutCommas(getFormattedGasValue(api!.valuePerGas!.toHuman()).toFixed())); + const chainEDeposit = Number( + getFormattedBalanceValue(Number(withoutCommas(api?.existentialDeposit.toString() || ''))).toFixed(), + ); + const gasLimit = Number(getFormattedGasValue(limit).toFixed()); + + const chainEDepositWithLimit = chainEDeposit + gasLimit * valuePerGas; + + if (chainBalance < chainEDepositWithLimit) { + alert.error(`Low balance on ${stringShorten(account?.decodedAddress || '', 8)}`); + + if (onError) { + onError(); + } + + return; + } + + callback(); + }; + + return { checkBalance }; +} + +export { useCheckBalance }; diff --git a/frontend/packages/hooks/src/index.ts b/frontend/packages/hooks/src/index.ts index 197bbd187..27c031a52 100644 --- a/frontend/packages/hooks/src/index.ts +++ b/frontend/packages/hooks/src/index.ts @@ -1,3 +1,4 @@ -import { useCountdown, useProgramMetadata } from './hooks'; +import { useCountdown, useProgramMetadata, useHandleCalculateGas, useCheckBalance } from './hooks'; +import { AvailableBalanceProvider } from './providers'; -export { useCountdown, useProgramMetadata }; +export { useCountdown, useProgramMetadata, useHandleCalculateGas, useCheckBalance, AvailableBalanceProvider }; diff --git a/frontend/packages/hooks/src/providers/balance-provider.tsx b/frontend/packages/hooks/src/providers/balance-provider.tsx new file mode 100644 index 000000000..78c59c9af --- /dev/null +++ b/frontend/packages/hooks/src/providers/balance-provider.tsx @@ -0,0 +1,24 @@ +import { Dispatch, PropsWithChildren, SetStateAction, createContext, useState } from 'react'; + +type AvailableBalance = undefined | { value: string; unit: string; existentialDeposit: string }; + +export type BalanceContextProps = { + isAvailableBalanceReady: boolean; + availableBalance: AvailableBalance; + setIsAvailableBalanceReady: Dispatch>; + setAvailableBalance: Dispatch>; +}; + +export const BalanceContext = createContext>({}); + +export function BalanceProvider({ children }: PropsWithChildren) { + const [isAvailableBalanceReady, setIsAvailableBalanceReady] = useState(false); + const [availableBalance, setAvailableBalance] = useState(undefined); + + return ( + + {children} + + ); +} diff --git a/frontend/packages/hooks/src/providers/index.ts b/frontend/packages/hooks/src/providers/index.ts new file mode 100644 index 000000000..798d23969 --- /dev/null +++ b/frontend/packages/hooks/src/providers/index.ts @@ -0,0 +1,3 @@ +import { BalanceProvider as AvailableBalanceProvider } from './balance-provider'; + +export { AvailableBalanceProvider }; diff --git a/frontend/packages/hooks/vite.config.ts b/frontend/packages/hooks/vite.config.ts index 30dbd6c4b..5acb70e2f 100644 --- a/frontend/packages/hooks/vite.config.ts +++ b/frontend/packages/hooks/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ formats: ['es'], }, rollupOptions: { - external: ['react', 'react-dom', '@gear-js/api', '@gear-js/react-hooks'], + external: ['react', 'react-dom', '@gear-js/api', '@gear-js/react-hooks', '@dapps-frontend/signless-transactions'], output: { globals: { react: 'React', 'react-dom': 'ReactDOM' }, }, diff --git a/frontend/packages/signless-transactions/package.json b/frontend/packages/signless-transactions/package.json index 6ec6f5119..60d9e5023 100644 --- a/frontend/packages/signless-transactions/package.json +++ b/frontend/packages/signless-transactions/package.json @@ -10,8 +10,8 @@ "preview": "vite preview" }, "peerDependencies": { - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.3", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "react": "18.2.0", "react-dom": "18.2.0" }, @@ -24,6 +24,7 @@ "react-hook-form": "7.49.0" }, "devDependencies": { + "@types/node": "18.17.15", "@types/react": "18.2.33", "@types/react-dom": "18.2.14", "@typescript-eslint/eslint-plugin": "6.0.0", diff --git a/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx b/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx index 4ab49e171..5cf6fd009 100644 --- a/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx +++ b/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx @@ -28,7 +28,7 @@ function CreateSessionModal({ close }: Props) { const { register, handleSubmit, formState } = useForm({ defaultValues: DEFAULT_VALUES }); const { errors } = formState; - const { savePair, storagePair, voucherBalance, createSession } = useSignlessTransactions(); + const { savePair, storagePair, voucherBalance, createSession, updateSession } = useSignlessTransactions(); // eslint-disable-next-line react-hooks/exhaustive-deps const [pair, setPair] = useState(storagePair); @@ -71,7 +71,7 @@ function CreateSessionModal({ close }: Props) { const key = decodeAddress(pair.address); const allowedActions = ACTIONS; - const onSuccess = () => { + const onSuccess = async () => { if (storagePair) { openEnableModal(); } else { @@ -82,6 +82,11 @@ function CreateSessionModal({ close }: Props) { const onFinally = () => setIsLoading(false); + if (storagePair) { + updateSession({ duration, key, allowedActions }, issueVoucherValue, { onSuccess, onFinally }); + return; + } + createSession({ duration, key, allowedActions }, issueVoucherValue, { onSuccess, onFinally }); }; diff --git a/frontend/packages/signless-transactions/src/components/signless-params-list/signless-params.tsx b/frontend/packages/signless-transactions/src/components/signless-params-list/signless-params.tsx index 8c7ca8e76..cb4126e6f 100644 --- a/frontend/packages/signless-transactions/src/components/signless-params-list/signless-params.tsx +++ b/frontend/packages/signless-transactions/src/components/signless-params-list/signless-params.tsx @@ -11,7 +11,7 @@ function SignlessParams({ params }: Props) { return (
      {params.map((param) => ( -
    • +
    • {param.heading}

      {param.value}

      diff --git a/frontend/packages/signless-transactions/src/components/signless-transactions/signless-transactions.tsx b/frontend/packages/signless-transactions/src/components/signless-transactions/signless-transactions.tsx index ad4c9b596..d57e367ce 100644 --- a/frontend/packages/signless-transactions/src/components/signless-transactions/signless-transactions.tsx +++ b/frontend/packages/signless-transactions/src/components/signless-transactions/signless-transactions.tsx @@ -14,9 +14,10 @@ import { AccountPair } from '../account-pair'; function SignlessTransactions() { const { account } = useAccount(); - const { pair, session, isSessionReady, voucherBalance, storagePair, deleteSession } = useSignlessTransactions(); - + const { pair, session, isSessionReady, voucherBalance, storagePair, deletePair, deleteSession } = + useSignlessTransactions(); const [modal, setModal] = useState(''); + const [isLoading, setIsLoading] = useState(false); const openCreateModal = () => setModal('create'); const openEnableModal = () => setModal('enable'); const closeModal = () => setModal(''); @@ -26,6 +27,15 @@ function SignlessTransactions() { const { getFormattedBalance } = useBalanceFormat(); const sessionBalance = voucherBalance ? getFormattedBalance(voucherBalance) : undefined; + const handleDeleteSession = async () => { + if (session) { + setIsLoading(true); + await deleteSession(session.key, pair); + deletePair(); + setIsLoading(false); + } + }; + return account && isSessionReady ? (
      {session ? ( @@ -77,7 +87,8 @@ function SignlessTransactions() { text="Log Out" color="light" className={styles.closeButton} - onClick={deleteSession} + isLoading={isLoading} + onClick={handleDeleteSession} />
      diff --git a/frontend/packages/signless-transactions/src/context/consts.ts b/frontend/packages/signless-transactions/src/context/consts.ts index 0e73b64b7..9496d48d9 100644 --- a/frontend/packages/signless-transactions/src/context/consts.ts +++ b/frontend/packages/signless-transactions/src/context/consts.ts @@ -8,9 +8,12 @@ const DEFAULT_VALUES = { unlockPair: () => {}, session: undefined, isSessionReady: false, + isVoucherExists: false, voucherBalance: 0, createSession: () => {}, deleteSession: () => {}, + updateSession: () => {}, + pairVoucherId: undefined, }; export { SIGNLESS_STORAGE_KEY, DEFAULT_VALUES }; diff --git a/frontend/packages/signless-transactions/src/context/index.tsx b/frontend/packages/signless-transactions/src/context/index.tsx index c2af6dec7..c0edb7865 100644 --- a/frontend/packages/signless-transactions/src/context/index.tsx +++ b/frontend/packages/signless-transactions/src/context/index.tsx @@ -1,5 +1,5 @@ -import { GearKeyring, HexString, decodeAddress, generateVoucherId } from '@gear-js/api'; -import { useAccount, useBalance } from '@gear-js/react-hooks'; +import { GearKeyring, HexString, decodeAddress } from '@gear-js/api'; +import { useAccount, useBalance, useVouchers } from '@gear-js/react-hooks'; import { KeyringPair, KeyringPair$Json } from '@polkadot/keyring/types'; import { ReactNode, createContext, useContext, useEffect, useState } from 'react'; @@ -20,12 +20,26 @@ type Props = { }; function useVoucherBalance(programId: HexString, address: string | undefined) { - const voucherId = address ? generateVoucherId(decodeAddress(address), programId) : undefined; + const decodedAddress = address ? decodeAddress(address) : ''; + + const { vouchers } = useVouchers(decodedAddress, programId); + + const voucherId = Object.keys(vouchers || {})[0]; const { balance } = useBalance(voucherId); return balance ? balance.toNumber() : 0; } +function useVoucherId(programId: HexString, address: string | undefined) { + const decodedAddress = address ? decodeAddress(address) : ''; + + const { vouchers } = useVouchers(decodedAddress, programId); + + const voucherId = Object.keys(vouchers || {})[0]; + + return voucherId; +} + function SignlessTransactionsProvider({ metadataSource, programId, children }: Props) { const metadata = useProgramMetadata(metadataSource); @@ -37,7 +51,8 @@ function SignlessTransactionsProvider({ metadataSource, programId, children }: P const getStorage = () => JSON.parse(localStorage[SIGNLESS_STORAGE_KEY] || '{}') as Storage; const storagePair = account ? getStorage()[account.address] : undefined; - const { createSession, deleteSession } = useCreateSession(programId, metadata); + const { createSession, deleteSession, updateSession } = useCreateSession(programId, metadata); + const pairVoucherId = useVoucherId(programId, pair?.address) as `0x${string}`; const voucherBalance = useVoucherBalance(programId, storagePair?.address); const unlockPair = (password: string) => { @@ -85,6 +100,8 @@ function SignlessTransactionsProvider({ metadataSource, programId, children }: P voucherBalance, createSession, deleteSession, + updateSession, + pairVoucherId, }; return {children}; diff --git a/frontend/packages/signless-transactions/src/context/types.ts b/frontend/packages/signless-transactions/src/context/types.ts index a4fe9c49c..8552856a4 100644 --- a/frontend/packages/signless-transactions/src/context/types.ts +++ b/frontend/packages/signless-transactions/src/context/types.ts @@ -25,7 +25,9 @@ type Value = { isSessionReady: boolean; voucherBalance: number; createSession: (...args: Parameters['createSession']>) => void; - deleteSession: () => void; + deleteSession: (...args: Parameters['deleteSession']>) => void; + updateSession: (...args: Parameters['updateSession']>) => void; + pairVoucherId: `0x${string}` | undefined; }; export type { State, Session, Storage, Value }; diff --git a/frontend/packages/signless-transactions/src/hooks/use-batch-sign-and-send.ts b/frontend/packages/signless-transactions/src/hooks/use-batch-sign-and-send.ts index 94ff30f37..dd77367f7 100644 --- a/frontend/packages/signless-transactions/src/hooks/use-batch-sign-and-send.ts +++ b/frontend/packages/signless-transactions/src/hooks/use-batch-sign-and-send.ts @@ -4,11 +4,13 @@ import { web3FromSource } from '@polkadot/extension-dapp'; import { ISubmittableResult } from '@polkadot/types/types'; import { useGetExtrinsicFailedError } from './use-get-extrinsic-failed-error'; +import { KeyringPair } from '@polkadot/keyring/types'; type Options = Partial<{ onSuccess: () => void; onError: (error: string) => void; onFinally: () => void; + pair?: KeyringPair; }>; function useBatchSignAndSend(type?: 'all' | 'force') { @@ -66,7 +68,7 @@ function useBatchSignAndSend(type?: 'all' | 'force') { const batch = getBatch(); const statusCallback = (result: ISubmittableResult) => handleStatus(result, options); - web3FromSource(meta.source) + await web3FromSource(meta.source) .then(({ signer }) => batch(txs).signAndSend(address, { signer }, statusCallback)) .catch(({ message }: Error) => { const { onError = () => {}, onFinally = () => {} } = options; diff --git a/frontend/packages/signless-transactions/src/hooks/use-create-session.ts b/frontend/packages/signless-transactions/src/hooks/use-create-session.ts index 3a41019cc..e9354fe26 100644 --- a/frontend/packages/signless-transactions/src/hooks/use-create-session.ts +++ b/frontend/packages/signless-transactions/src/hooks/use-create-session.ts @@ -1,8 +1,9 @@ import { HexString, ProgramMetadata } from '@gear-js/api'; -import { useAlert, useApi } from '@gear-js/react-hooks'; +import { useAccount, useAlert, useApi, useBalanceFormat } from '@gear-js/react-hooks'; import { AnyJson } from '@polkadot/types/types'; - import { useBatchSignAndSend } from './use-batch-sign-and-send'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { sendTransaction } from '@/utils'; type Session = { key: HexString; @@ -18,27 +19,46 @@ type Options = { function useCreateSession(programId: HexString, metadata: ProgramMetadata | undefined) { const { api, isApiReady } = useApi(); const alert = useAlert(); + const { account } = useAccount(); const { batchSignAndSend } = useBatchSignAndSend('all'); + const { getFormattedBalance } = useBalanceFormat(); const onError = (message: string) => alert.error(message); const getMessage = (payload: AnyJson) => { const destination = programId; // TODO: replace with calculation after release fix - const gasLimit = 10000000000; + const gasLimit = 250000000000; return { destination, payload, gasLimit }; }; - const deleteSession = async () => { + const deleteSession = async (key: HexString, pair?: KeyringPair) => { if (!isApiReady) throw new Error('API is not initialized'); if (!metadata) throw new Error('Metadata not found'); const message = getMessage({ DeleteSessionFromAccount: null }); const extrinsic = api.message.send(message, metadata); - // const voucher = api.voucher.revoke(session.key, programId); - const txs = [extrinsic]; + const vouchersForAccount = await api.voucher.getAllForAccount(key, programId); + + const accountVoucherId = Object.keys(vouchersForAccount)[0]; + + const details = await api?.voucher.getDetails(key, accountVoucherId as `0x${string}`); + const finilizedBlockHash = await api?.blocks.getFinalizedHead(); + const currentBlockNumber = await api.blocks.getBlockNumber(finilizedBlockHash.toHex()); + + const isExpired = currentBlockNumber.toNumber() > details.expiry; + + if (!isExpired && pair) { + const declineExtrrinsic = api.voucher.call(accountVoucherId, { DeclineVoucher: null }); + + await sendTransaction(declineExtrrinsic, pair, ['VoucherDeclined']); + } + + const revokeExtrrinsic = api.voucher.revoke(key, accountVoucherId); + + const txs = [extrinsic, revokeExtrrinsic]; batchSignAndSend(txs, { onError }); }; @@ -46,11 +66,15 @@ function useCreateSession(programId: HexString, metadata: ProgramMetadata | unde const createSession = async (session: Session, voucherValue: number, _options: Options) => { if (!isApiReady) throw new Error('API is not initialized'); if (!metadata) throw new Error('Metadata not found'); + if (!account) throw new Error('Account not found'); const message = getMessage({ CreateSession: session }); const extrinsic = api.message.send(message, metadata); - const voucher = api.voucher.issue(session.key, programId, voucherValue); + + const minDuration = api.voucher.minDuration; + + const voucher = await api.voucher.issue(session.key, voucherValue, minDuration, [programId], true); const txs = [extrinsic, voucher.extrinsic]; const options = { ..._options, onError }; @@ -58,7 +82,54 @@ function useCreateSession(programId: HexString, metadata: ProgramMetadata | unde batchSignAndSend(txs, options); }; - return { createSession, deleteSession }; + const updateSession = async (session: Session, voucherValue: number, _options: Options) => { + if (!isApiReady) throw new Error('API is not initialized'); + if (!metadata) throw new Error('Metadata not found'); + if (!account) throw new Error('Account not found'); + + const updateVoucher = async (accountVoucherId: string) => { + const details = await api?.voucher.getDetails(session.key, accountVoucherId as `0x${string}`); + + const finilizedBlockHash = await api?.blocks.getFinalizedHead(); + const currentBlockNumber = await api.blocks.getBlockNumber(finilizedBlockHash.toHex()); + + const isNeedProlongDuration = currentBlockNumber.toNumber() > details.expiry; + + if (voucherValue || isNeedProlongDuration) { + const minDuration = api.voucher.minDuration; + + const voucherExtrinsic = await api.voucher.update(session.key, accountVoucherId, { + balanceTopUp: voucherValue + ? Number(getFormattedBalance(balance.toNumber()).value) + Number(getFormattedBalance(voucherValue).value) + : undefined, + prolongDuration: isNeedProlongDuration ? minDuration : undefined, + }); + + return voucherExtrinsic; + } + + return null; + }; + + const message = getMessage({ CreateSession: session }); + + const extrinsic = await api.message.send(message, metadata); + + const vouchersForAccount = await api.voucher.getAllForAccount(session.key, programId); + + const accountVoucherId = Object.keys(vouchersForAccount)[0]; + + const balance = await api.balance.findOut(accountVoucherId); + + const updatedVoucherExtrinsic = await updateVoucher(accountVoucherId); + + const txs = updatedVoucherExtrinsic ? [extrinsic, updatedVoucherExtrinsic] : [extrinsic]; + const options = { ..._options, onError }; + + batchSignAndSend(txs, options); + }; + + return { createSession, updateSession, deleteSession }; } export { useCreateSession }; diff --git a/frontend/packages/signless-transactions/src/hooks/use-get-extrinsic-failed-error.ts b/frontend/packages/signless-transactions/src/hooks/use-get-extrinsic-failed-error.ts index 1bd8322e6..962161670 100644 --- a/frontend/packages/signless-transactions/src/hooks/use-get-extrinsic-failed-error.ts +++ b/frontend/packages/signless-transactions/src/hooks/use-get-extrinsic-failed-error.ts @@ -7,9 +7,9 @@ function useGetExtrinsicFailedError() { const getExtrinsicFailedError = (event: Event) => { if (!isApiReady) throw new Error('API is not initialized'); - const { section, method, docs } = api.getExtrinsicFailedError(event); + const { name, method, docs } = api.getExtrinsicFailedError(event); - return `${section}.${method}: ${docs.join(' ')}`; + return `${name}.${method}: ${docs}`; }; return { getExtrinsicFailedError }; diff --git a/frontend/packages/signless-transactions/src/hooks/use-signless-send-message.ts b/frontend/packages/signless-transactions/src/hooks/use-signless-send-message.ts index 1862b0997..b0319127c 100644 --- a/frontend/packages/signless-transactions/src/hooks/use-signless-send-message.ts +++ b/frontend/packages/signless-transactions/src/hooks/use-signless-send-message.ts @@ -26,15 +26,15 @@ function useSignlessSendMessage( options?: UseSendMessageOptions, ) { const { account } = useAccount(); - const { pair } = useSignlessTransactions(); + const { pair, pairVoucherId } = useSignlessTransactions(); const sendMessage = useSendMessage(destination, metadata, { ...options, pair }); const sendSignlessMessage = (args: SendSignlessMessageOptions) => { const sessionForAccount = pair ? account?.decodedAddress : null; const payload = getSinglessPayload(args.payload, sessionForAccount); - const withVoucher = !!pair || args.withVoucher; // to not overrider gasless transactions + const voucherId = pairVoucherId ? pairVoucherId : args.voucherId; // to not overrider gasless transactions - sendMessage({ ...args, payload, withVoucher }); + sendMessage({ ...args, payload, voucherId }); }; return sendSignlessMessage; @@ -52,9 +52,9 @@ function useSignlessSendMessageHandler( const sendSignlessMessage = (args: Omit) => { const sessionForAccount = pair ? account?.decodedAddress : null; const payload = getSinglessPayload(args.payload, sessionForAccount); - const withVoucher = !!pair || args.withVoucher; // to not overrider gasless transactions + const voucherId = pair ? (pair?.address as `0x${string}`) : args.voucherId; // to not overrider gasless transactions - sendMessage({ ...args, payload, withVoucher }); + sendMessage({ ...args, payload, voucherId }); }; return sendSignlessMessage; diff --git a/frontend/packages/signless-transactions/src/utils.ts b/frontend/packages/signless-transactions/src/utils.ts index a77cb7edb..c96eb6787 100644 --- a/frontend/packages/signless-transactions/src/utils.ts +++ b/frontend/packages/signless-transactions/src/utils.ts @@ -1,6 +1,8 @@ -import { decodeAddress } from '@gear-js/api'; +import { GearTransaction, IGearEvent, IGearVoucherEvent, decodeAddress } from '@gear-js/api'; import { AlertContainerFactory } from '@gear-js/react-hooks'; import { encodeAddress } from '@polkadot/keyring'; +import { SubmittableExtrinsic } from '@polkadot/api/types'; +import { KeyringPair } from '@polkadot/keyring/types'; const MULTIPLIER = { MS: 1000, @@ -9,6 +11,34 @@ const MULTIPLIER = { HOURS: 24, }; +export async function sendTransaction( + submitted: GearTransaction | SubmittableExtrinsic<'promise'>, + account: KeyringPair, + methods: E[], +): Promise { + const result: any = new Array(methods.length); + return new Promise((resolve, reject) => { + submitted + .signAndSend(account, ({ events, status }) => { + events.forEach(({ event }) => { + const { method, data } = event; + if (methods.includes(method as E) && status.isInBlock) { + result[methods.indexOf(method as E)] = data; + } else if (method === 'ExtrinsicFailed') { + reject(data.toString()); + } + }); + if (status.isInBlock) { + resolve([...result, status.asInBlock.toHex()]); + } + }) + .catch((err) => { + console.log(err); + reject(err.message); + }); + }); +} + const getVaraAddress = (value: string) => { const VARA_SS58_FORMAT = 137; const decodedAddress = decodeAddress(value); diff --git a/frontend/packages/signless-transactions/vite.config.ts b/frontend/packages/signless-transactions/vite.config.ts index b069b7f04..f91251355 100644 --- a/frontend/packages/signless-transactions/vite.config.ts +++ b/frontend/packages/signless-transactions/vite.config.ts @@ -7,7 +7,7 @@ import dts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), svgr(), dts()], - + resolve: { alias: { '@': '/src' } }, build: { lib: { entry: resolve(__dirname, 'src/index.ts'), @@ -22,7 +22,6 @@ export default defineConfig({ '@polkadot/extension-dapp', '@polkadot/wasm-crypto', ], - resolve: { alias: { '@': '/src' } }, output: { globals: { react: 'React', 'react-dom': 'ReactDOM' }, intro: 'import "./style.css";', diff --git a/frontend/packages/ui/package.json b/frontend/packages/ui/package.json index 9cc77ac5a..0a5e43e54 100644 --- a/frontend/packages/ui/package.json +++ b/frontend/packages/ui/package.json @@ -10,7 +10,8 @@ "preview": "vite preview" }, "peerDependencies": { - "@gear-js/react-hooks": "0.9.2", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "react": "18.2.0", "react-dom": "18.2.0" }, diff --git a/frontend/packages/ui/src/components/header/assets/discord.svg b/frontend/packages/ui/src/components/header/assets/discord.svg new file mode 100644 index 000000000..bccb429ea --- /dev/null +++ b/frontend/packages/ui/src/components/header/assets/discord.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/ui/src/components/header/assets/github.svg b/frontend/packages/ui/src/components/header/assets/github.svg new file mode 100644 index 000000000..cd3cdbdb3 --- /dev/null +++ b/frontend/packages/ui/src/components/header/assets/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/ui/src/components/header/assets/medium.svg b/frontend/packages/ui/src/components/header/assets/medium.svg new file mode 100644 index 000000000..decd4b34d --- /dev/null +++ b/frontend/packages/ui/src/components/header/assets/medium.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/ui/src/components/header/assets/twitter.svg b/frontend/packages/ui/src/components/header/assets/twitter.svg new file mode 100644 index 000000000..906411342 --- /dev/null +++ b/frontend/packages/ui/src/components/header/assets/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/ui/src/components/header/assets/user.svg b/frontend/packages/ui/src/components/header/assets/user.svg new file mode 100644 index 000000000..90980cfc7 --- /dev/null +++ b/frontend/packages/ui/src/components/header/assets/user.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/ui/src/components/header/consts.ts b/frontend/packages/ui/src/components/header/consts.ts new file mode 100644 index 000000000..d0d18f6a3 --- /dev/null +++ b/frontend/packages/ui/src/components/header/consts.ts @@ -0,0 +1,13 @@ +import { ReactComponent as TwitterSVG } from './assets/twitter.svg'; +import { ReactComponent as GithubSVG } from './assets/github.svg'; +import { ReactComponent as DiscordSVG } from './assets/discord.svg'; +import { ReactComponent as MediumSVG } from './assets/medium.svg'; + +const SOCIALS = [ + { href: 'https://twitter.com/VaraNetwork', SVG: TwitterSVG }, + { href: 'https://github.com/gear-foundation', SVG: GithubSVG }, + { href: 'https://discord.gg/x8ZeSy6S6K', SVG: DiscordSVG }, + { href: 'https://medium.com/@VaraNetwork', SVG: MediumSVG }, +]; + +export { SOCIALS }; diff --git a/frontend/packages/ui/src/components/header/header.module.css b/frontend/packages/ui/src/components/header/header.module.css new file mode 100644 index 000000000..3e9ad6037 --- /dev/null +++ b/frontend/packages/ui/src/components/header/header.module.css @@ -0,0 +1,19 @@ +.header { + width: 100%; + display: flex; + gap: 30px; +} + +.content { + height: 100%; + width: 100%; + max-width: 1200px; + display: flex; + justify-content: space-between; + align-items: center; + margin: auto; + + @media screen and (max-width: 767px) { + padding: 0 26px; + } +} \ No newline at end of file diff --git a/frontend/packages/ui/src/components/header/header.tsx b/frontend/packages/ui/src/components/header/header.tsx new file mode 100644 index 000000000..e62b08d66 --- /dev/null +++ b/frontend/packages/ui/src/components/header/header.tsx @@ -0,0 +1,26 @@ +import { clsx } from 'clsx'; +import styles from './header.module.css'; +import { PropsWithChildren } from 'react'; + +type Props = { + logo: JSX.Element; + className?: { + header?: string; + content?: string; + }; + menu: JSX.Element; +} & PropsWithChildren; + +function Header({ logo, className, menu, children }: Props) { + return ( +
      +
      + {logo} + {children} + <>{menu} +
      +
      + ); +} + +export { Header }; diff --git a/frontend/packages/ui/src/components/header/index.ts b/frontend/packages/ui/src/components/header/index.ts new file mode 100644 index 000000000..a2d2b9a04 --- /dev/null +++ b/frontend/packages/ui/src/components/header/index.ts @@ -0,0 +1,3 @@ +import { Header } from './header'; + +export { Header }; diff --git a/frontend/packages/ui/src/components/index.ts b/frontend/packages/ui/src/components/index.ts index a755078e4..5e702996d 100644 --- a/frontend/packages/ui/src/components/index.ts +++ b/frontend/packages/ui/src/components/index.ts @@ -1,8 +1,9 @@ import { Footer } from './footer'; +import { Header } from './header'; import { Container } from './container'; import { StartDisclaimer } from './start-disclaimer'; import { MenuOptions } from './menu-options'; import { MobileMenu } from './mobile-menu'; import { MenuHandler } from './menu-handler'; -export { Footer, Container, StartDisclaimer, MenuOptions, MobileMenu, MenuHandler }; +export { Footer, Header, Container, StartDisclaimer, MenuOptions, MobileMenu, MenuHandler }; diff --git a/frontend/packages/ui/src/components/menu-handler/menu-handler.module.css b/frontend/packages/ui/src/components/menu-handler/menu-handler.module.css index 0f33d8196..c9c83ffef 100644 --- a/frontend/packages/ui/src/components/menu-handler/menu-handler.module.css +++ b/frontend/packages/ui/src/components/menu-handler/menu-handler.module.css @@ -6,8 +6,8 @@ .burger { & path { - fill: #fff; - stroke: #fff; + fill: #000; + stroke: #000; } } @@ -26,6 +26,7 @@ border-radius: 4px; box-shadow: 0px 10px 20px 0px #5B5B5B26; display: block; + z-index: 1; @media screen and (max-width: 767px) { display: none; @@ -58,10 +59,10 @@ width: 100%; padding: 16px; display: none; + z-index: 1; + background: #fff; @media screen and (max-width: 767px) { - display: block; - } - - + display: block; + } } \ No newline at end of file diff --git a/frontend/packages/ui/src/components/menu-handler/menu-handler.tsx b/frontend/packages/ui/src/components/menu-handler/menu-handler.tsx index 2c61d6afd..c724c4830 100644 --- a/frontend/packages/ui/src/components/menu-handler/menu-handler.tsx +++ b/frontend/packages/ui/src/components/menu-handler/menu-handler.tsx @@ -11,6 +11,7 @@ import { MobileMenuClassNameProps } from '../mobile-menu'; import { useClickOutside } from '@/utils'; import clsx from 'clsx'; import { useAccount } from '@gear-js/react-hooks'; +import { WalletClassNameProps } from '@/features/wallet-new/components/wallet'; type Props = { customItems?: { @@ -21,8 +22,10 @@ type Props = { container?: string; dropdown?: string; mobileMenuWrapper?: string; + icon?: string; menuOptions?: MenuOptionsClassNameProps; mobileMenu?: MobileMenuClassNameProps; + wallet?: WalletClassNameProps; }; }; @@ -42,8 +45,13 @@ export function MenuHandler({ customItems, className }: Props) { return (
      - +
      + {account && ( <>
      @@ -54,6 +62,7 @@ export function MenuHandler({ customItems, className }: Props) { ? () => : () => } + className={clsx(className?.icon)} onClick={isMenuOpen ? closeMenu : openMenu} /> {isMenuOpen && ( diff --git a/frontend/packages/ui/src/components/menu-options/menu-options.module.css b/frontend/packages/ui/src/components/menu-options/menu-options.module.css index f6ad31b99..654b2ccea 100644 --- a/frontend/packages/ui/src/components/menu-options/menu-options.module.css +++ b/frontend/packages/ui/src/components/menu-options/menu-options.module.css @@ -22,8 +22,9 @@ .disconnectButton.disconnectButton { display: flex; + justify-content: flex-start; align-items: center; - gap: 2px; + gap: 12px; background: transparent; margin: 0; padding: 0; diff --git a/frontend/packages/ui/src/components/mobile-menu/mobile-menu.tsx b/frontend/packages/ui/src/components/mobile-menu/mobile-menu.tsx index 5068fb38d..f5bd213c6 100644 --- a/frontend/packages/ui/src/components/mobile-menu/mobile-menu.tsx +++ b/frontend/packages/ui/src/components/mobile-menu/mobile-menu.tsx @@ -4,7 +4,6 @@ import { motion } from 'framer-motion'; import { Button } from '@gear-js/vara-ui'; import { useAccount } from '@gear-js/react-hooks'; import { useWallet } from '@/features/wallet-new/hooks'; - import styles from './mobile-menu.module.css'; import clsx from 'clsx'; @@ -57,23 +56,26 @@ export function MobileMenu({ children, className, onClose, walletModalHandler }: }; return ( - -
      - {children} -
      -
        {getAccounts()}
      -
      + <> + +
      + {children} +
      +
        {getAccounts()}
      +
      -
      -
      -
      -
      + +
      + ); } diff --git a/frontend/packages/ui/src/features/wallet-new/assets/tvara-coin.svg b/frontend/packages/ui/src/features/wallet-new/assets/tvara-coin.svg new file mode 100644 index 000000000..ec6036faa --- /dev/null +++ b/frontend/packages/ui/src/features/wallet-new/assets/tvara-coin.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/packages/ui/src/features/wallet-new/assets/vara-coin.svg b/frontend/packages/ui/src/features/wallet-new/assets/vara-coin.svg new file mode 100644 index 000000000..0d0467d2f --- /dev/null +++ b/frontend/packages/ui/src/features/wallet-new/assets/vara-coin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/packages/ui/src/features/wallet-new/assets/vara.svg b/frontend/packages/ui/src/features/wallet-new/assets/vara.svg deleted file mode 100644 index 32bafa6ed..000000000 --- a/frontend/packages/ui/src/features/wallet-new/assets/vara.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.module.css b/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.module.css index 5d54aa1dc..8d33f0674 100644 --- a/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.module.css +++ b/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.module.css @@ -2,12 +2,12 @@ display: flex; align-items: center; gap: 10px; + color: black; .text { display: flex; align-items: flex-end; gap: 4px; - color: #fff; .value { font-size: 20px; diff --git a/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.tsx b/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.tsx index 67a1c69c8..2acdac9af 100644 --- a/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.tsx +++ b/frontend/packages/ui/src/features/wallet-new/components/vara-balance/vara-balance.tsx @@ -1,19 +1,26 @@ -import { useAccount, useApi, useBalanceFormat } from '@gear-js/react-hooks'; -import { ReactComponent as VaraSVG } from '../../assets/vara.svg'; -import { useFreeAccountBalance } from '../../hooks'; +import clsx from 'clsx'; +import { useAccount, useApi, useBalanceFormat, useDeriveBalancesAll } from '@gear-js/react-hooks'; +import { ReactComponent as VaraSVG } from '../../assets/vara-coin.svg'; +import { ReactComponent as TVaraSVG } from '../../assets/tvara-coin.svg'; import styles from './vara-balance.module.css'; -function VaraBalance() { +type Props = { + className?: string; +}; + +function VaraBalance({ className }: Props) { + const { account } = useAccount(); const { isApiReady } = useApi(); const { isAccountReady } = useAccount(); const { getFormattedBalance } = useBalanceFormat(); - const { freeAccountBalance } = useFreeAccountBalance(); - const balance = isApiReady && freeAccountBalance ? getFormattedBalance(freeAccountBalance) : undefined; + const balances = useDeriveBalancesAll(account?.decodedAddress); + const balance = + isApiReady && balances?.freeBalance ? getFormattedBalance(balances.freeBalance.toString()) : undefined; return isAccountReady && balance ? ( -
      - +
      + {balance.unit?.toLowerCase() === 'vara' ? : }

      {balance.value} diff --git a/frontend/packages/ui/src/features/wallet-new/components/wallet-modal/wallet-modal.module.css b/frontend/packages/ui/src/features/wallet-new/components/wallet-modal/wallet-modal.module.css index e26253964..e5b2c9d0f 100644 --- a/frontend/packages/ui/src/features/wallet-new/components/wallet-modal/wallet-modal.module.css +++ b/frontend/packages/ui/src/features/wallet-new/components/wallet-modal/wallet-modal.module.css @@ -223,6 +223,7 @@ width: 100%; max-width: 400px; background-color: #f6f8f8; + color: #000; @media screen and (min-width: 768px) { --scale-closed: 90%; diff --git a/frontend/packages/ui/src/features/wallet-new/components/wallet/index.ts b/frontend/packages/ui/src/features/wallet-new/components/wallet/index.ts index c7f5f4752..b425ac5d2 100644 --- a/frontend/packages/ui/src/features/wallet-new/components/wallet/index.ts +++ b/frontend/packages/ui/src/features/wallet-new/components/wallet/index.ts @@ -1,3 +1,2 @@ -import { Wallet } from './wallet'; - -export { Wallet }; +export { Wallet } from './wallet'; +export type { ClassNameProps as WalletClassNameProps } from './wallet'; diff --git a/frontend/packages/ui/src/features/wallet-new/components/wallet/wallet.tsx b/frontend/packages/ui/src/features/wallet-new/components/wallet/wallet.tsx index 7cb21eba0..2c8edc4f2 100644 --- a/frontend/packages/ui/src/features/wallet-new/components/wallet/wallet.tsx +++ b/frontend/packages/ui/src/features/wallet-new/components/wallet/wallet.tsx @@ -6,12 +6,16 @@ import { WalletModal } from '../wallet-modal'; import styles from './wallet.module.css'; import { VaraBalance } from '../vara-balance'; +export type ClassNameProps = { + balance?: string; +}; type Props = { isWalletModalOpen?: boolean; walletModalHandler?: (bool: boolean) => void; + className?: ClassNameProps; }; -function Wallet({ isWalletModalOpen, walletModalHandler }: Props) { +function Wallet({ isWalletModalOpen, walletModalHandler, className }: Props) { const { account, isAccountReady } = useAccount(); const [isModalOpen, setIsModalOpen] = useState(isWalletModalOpen || false); @@ -27,7 +31,7 @@ function Wallet({ isWalletModalOpen, walletModalHandler }: Props) { return isAccountReady ? ( <>

      - + {account ? (
      diff --git a/frontend/packages/ui/src/features/wallet-new/hooks/index.ts b/frontend/packages/ui/src/features/wallet-new/hooks/index.ts index fda2e7ba1..eab5ace13 100644 --- a/frontend/packages/ui/src/features/wallet-new/hooks/index.ts +++ b/frontend/packages/ui/src/features/wallet-new/hooks/index.ts @@ -1,4 +1,3 @@ import { useWallet } from './use-wallet'; -import { useFreeAccountBalance } from './use-free-account-balance'; -export { useWallet, useFreeAccountBalance }; +export { useWallet }; diff --git a/frontend/packages/ui/src/features/wallet-new/hooks/use-free-account-balance.ts b/frontend/packages/ui/src/features/wallet-new/hooks/use-free-account-balance.ts deleted file mode 100644 index da580152b..000000000 --- a/frontend/packages/ui/src/features/wallet-new/hooks/use-free-account-balance.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useAccount, useApi } from '@gear-js/react-hooks'; -import { useEffect, useState } from 'react'; - -function useFreeAccountBalance() { - const { api, isApiReady } = useApi(); - const { account } = useAccount(); - const { address } = account || {}; - - const [freeAccountBalance, setFreeAccountBalance] = useState(''); - - useEffect(() => { - if (!isApiReady || !address) return; - - const unsub = api.derive.balances.all(address, (result) => setFreeAccountBalance(result.freeBalance.toString())); - - return () => { - setFreeAccountBalance(''); - unsub.then((unsubCallback) => unsubCallback()); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isApiReady, address]); - - return { freeAccountBalance }; -} - -export { useFreeAccountBalance }; diff --git a/frontend/packages/ui/src/index.ts b/frontend/packages/ui/src/index.ts index 4f7f88e08..ec0df280e 100644 --- a/frontend/packages/ui/src/index.ts +++ b/frontend/packages/ui/src/index.ts @@ -1,4 +1,4 @@ -import { Footer, Container, StartDisclaimer, MenuOptions, MobileMenu, MenuHandler } from './components'; +import { Footer, Header, Container, StartDisclaimer, MenuOptions, MobileMenu, MenuHandler } from './components'; import { WalletNew, Wallet } from './features'; -export { Footer, Container, StartDisclaimer, MenuOptions, WalletNew, Wallet, MobileMenu, MenuHandler }; +export { Footer, Header, Container, StartDisclaimer, MenuOptions, WalletNew, Wallet, MobileMenu, MenuHandler }; diff --git a/frontend/templates/gear-app-starter/package.json b/frontend/templates/gear-app-starter/package.json index 208f87b3e..a92fe09cc 100644 --- a/frontend/templates/gear-app-starter/package.json +++ b/frontend/templates/gear-app-starter/package.json @@ -3,16 +3,16 @@ "version": "0.1.0", "private": true, "dependencies": { - "@gear-js/api": "0.35.2", - "@gear-js/react-hooks": "0.9.4", + "@gear-js/api": "0.36.5", + "@gear-js/react-hooks": "0.10.2", "@gear-js/ui": "0.5.21", "@headlessui/react": "^1.7.17", "@mantine/form": "^5.10.5", - "@polkadot/api": "10.10.1", + "@polkadot/api": "10.11.2", "@polkadot/extension-dapp": "0.46.5", "@polkadot/extension-inject": "0.46.5", "@polkadot/react-identicon": "3.5.1", - "@polkadot/types": "10.10.1", + "@polkadot/types": "10.11.2", "@polkadot/util": "12.3.2", "@polkadot/util-crypto": "12.3.2", "@polkadot/wasm-crypto": "7.2.2", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index a4d492271..d6580b350 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1943,14 +1943,13 @@ __metadata: eslint: "npm:8.45.0" eslint-plugin-react-hooks: "npm:4.6.0" eslint-plugin-react-refresh: "npm:0.4.3" - jotai: "npm:2.6.0" typescript: "npm:5.0.2" vite: "npm:4.4.5" vite-plugin-dts: "npm:3.5.1" peerDependencies: "@dapps-frontend/signless-transactions": "workspace:*" - "@gear-js/api": 0.35.2 - "@gear-js/react-hooks": 0.9.2 + "@gear-js/api": 0.36.5 + "@gear-js/react-hooks": 0.10.2 react: 18.2.0 react-dom: 18.2.0 languageName: unknown @@ -1960,6 +1959,7 @@ __metadata: version: 0.0.0-use.local resolution: "@dapps-frontend/hooks@workspace:packages/hooks" dependencies: + "@polkadot/extension-inject": "npm:0.46.5" "@types/react": "npm:18.2.33" "@types/react-dom": "npm:18.2.14" "@typescript-eslint/eslint-plugin": "npm:6.0.0" @@ -1972,8 +1972,10 @@ __metadata: vite: "npm:4.4.5" vite-plugin-dts: "npm:3.5.1" peerDependencies: - "@gear-js/api": 0.35.2 - "@gear-js/react-hooks": 0.9.3 + "@gear-js/api": 0.36.5 + "@gear-js/react-hooks": 0.10.2 + "@polkadot/types": 10.10.1 + "@polkadot/util": 12.3.2 react: 18.2.0 react-dom: 18.2.0 languageName: unknown @@ -1987,6 +1989,7 @@ __metadata: "@gear-js/vara-ui": "npm:0.0.6" "@polkadot/keyring": "npm:12.6.1" "@polkadot/react-identicon": "npm:3.6.4" + "@types/node": "npm:18.17.15" "@types/react": "npm:18.2.33" "@types/react-dom": "npm:18.2.14" "@typescript-eslint/eslint-plugin": "npm:6.0.0" @@ -2002,8 +2005,8 @@ __metadata: vite-plugin-dts: "npm:3.5.1" vite-plugin-svgr: "npm:3.2.0" peerDependencies: - "@gear-js/api": 0.35.2 - "@gear-js/react-hooks": 0.9.3 + "@gear-js/api": 0.36.5 + "@gear-js/react-hooks": 0.10.2 react: 18.2.0 react-dom: 18.2.0 languageName: unknown @@ -2033,7 +2036,8 @@ __metadata: vite-plugin-dts: "npm:3.5.1" vite-plugin-svgr: "npm:3.2.0" peerDependencies: - "@gear-js/react-hooks": 0.9.2 + "@gear-js/api": 0.36.5 + "@gear-js/react-hooks": 0.10.2 react: 18.2.0 react-dom: 18.2.0 languageName: unknown @@ -2435,7 +2439,7 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.0.2, @eslint/eslintrc@npm:^2.0.3, @eslint/eslintrc@npm:^2.1.0, @eslint/eslintrc@npm:^2.1.1, @eslint/eslintrc@npm:^2.1.2, @eslint/eslintrc@npm:^2.1.4": +"@eslint/eslintrc@npm:^2.0.3, @eslint/eslintrc@npm:^2.1.0, @eslint/eslintrc@npm:^2.1.1, @eslint/eslintrc@npm:^2.1.2, @eslint/eslintrc@npm:^2.1.4": version: 2.1.4 resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: @@ -2452,13 +2456,6 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.39.0": - version: 8.39.0 - resolution: "@eslint/js@npm:8.39.0" - checksum: bb7ed9c22b998e8c765d87b12225ae046ae4c571c5c88d1012908c3ae1ae28e6248ebc98aed66b08334a8a9e43420bcc31a0e7f80173dafa6cc97f59735512e6 - languageName: node - linkType: hard - "@eslint/js@npm:8.43.0": version: 8.43.0 resolution: "@eslint/js@npm:8.43.0" @@ -2570,27 +2567,35 @@ __metadata: languageName: node linkType: hard -"@gear-js/api@npm:0.35.2": - version: 0.35.2 - resolution: "@gear-js/api@npm:0.35.2" +"@gear-js/api@npm:0.36.5": + version: 0.36.5 + resolution: "@gear-js/api@npm:0.36.5" peerDependencies: - "@polkadot/api": 10.10.1 - "@polkadot/wasm-crypto": 7.2.2 + "@polkadot/api": 10.11.2 + "@polkadot/wasm-crypto": 7.3.2 rxjs: 7.8.1 - checksum: 4d931fa304e3f249e5f158d2ea20e8c8f1ba8d27598c3eea857ff5db0139eb71f7bd8376fd9b8ce080fc3d089def84bab7a46722262834e8f946afbc1cd10948 + checksum: 4cf50c0dc3b4697b402fd8ee2692fd9c2c07ecd61d9c73811f2aedd27ecf4685683a877196d638799acc94571b0eef7b155b9b2149ed4820019579e6e538cdf4 languageName: node linkType: hard -"@gear-js/react-hooks@npm:0.9.4": - version: 0.9.4 - resolution: "@gear-js/react-hooks@npm:0.9.4" +"@gear-js/react-hooks@npm:0.10.2": + version: 0.10.2 + resolution: "@gear-js/react-hooks@npm:0.10.2" + dependencies: + "@polkadot/api-derive": "npm:10.11.2" + "@polkadot/extension-inject": "npm:0.46.5" + "@polkadot/util": "npm:12.5.1" + "@substrate/connect": "npm:0.7.33" + bignumber.js: "npm:9.1.2" + nanoid: "npm:5.0.1" + react-transition-group: "npm:4.4.5" peerDependencies: - "@gear-js/api": 0.35.2 - "@polkadot/api": 10.10.1 + "@gear-js/api": 0.36.3 + "@polkadot/api": 10.11.2 "@polkadot/extension-dapp": 0.46.5 react: 18.2.0 - react-transition-group: 4.4.5 - checksum: 60d74a335a647ebe13434cbe63cac34cf75a3da8e9b3cc20e39ba76d306024b93d02d7b0e8d4db82cd24c110a1673d0d1653301cab717c58ea5cd999d0c58bde + react-dom: 18.2.0 + checksum: 8ffd27dccefcb1952a95b112b91ea18813e0ad9bd7171bc8249cbe0b8e4d15655faa287a9b52bd1ba35be776117f1171befd4a614985e80489f9d1b7f1e67429 languageName: node linkType: hard @@ -2715,7 +2720,7 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.10, @humanwhocodes/config-array@npm:^0.11.13, @humanwhocodes/config-array@npm:^0.11.8": +"@humanwhocodes/config-array@npm:^0.11.10, @humanwhocodes/config-array@npm:^0.11.13": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: @@ -3766,21 +3771,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-augment@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/api-augment@npm:10.10.1" - dependencies: - "@polkadot/api-base": "npm:10.10.1" - "@polkadot/rpc-augment": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-augment": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: a50ae11bc9214c56ec9dccaf56562069a63ecdae175958b97d28e85ca65eed7f0b8b4e6686f1bb04535cfed07e68cf39ec0177d4bfc2a616c50b3d210cd8b940 - languageName: node - linkType: hard - "@polkadot/api-augment@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/api-augment@npm:10.11.2" @@ -3796,19 +3786,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-base@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/api-base@npm:10.10.1" - dependencies: - "@polkadot/rpc-core": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 1fb82045a022dae5a31c24a07933024b3d2b41ecd18a30bb9c1b2a7bda8ca3efd304e8c3c1e7ab6010de3b1de5add080d4863ae9a17e9be9e4c363f5e5b93ef8 - languageName: node - linkType: hard - "@polkadot/api-base@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/api-base@npm:10.11.2" @@ -3822,24 +3799,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-derive@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/api-derive@npm:10.10.1" - dependencies: - "@polkadot/api": "npm:10.10.1" - "@polkadot/api-augment": "npm:10.10.1" - "@polkadot/api-base": "npm:10.10.1" - "@polkadot/rpc-core": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - "@polkadot/util-crypto": "npm:^12.5.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 66ff51bec79181894cd190e5447097a093ca89e186d53be9dbe1a42d75a32ba69cc736fee69e652f4ce978960af1fb112cdb332390eebe34b370c3d2126c3e2e - languageName: node - linkType: hard - "@polkadot/api-derive@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/api-derive@npm:10.11.2" @@ -3858,31 +3817,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/api@npm:10.10.1" - dependencies: - "@polkadot/api-augment": "npm:10.10.1" - "@polkadot/api-base": "npm:10.10.1" - "@polkadot/api-derive": "npm:10.10.1" - "@polkadot/keyring": "npm:^12.5.1" - "@polkadot/rpc-augment": "npm:10.10.1" - "@polkadot/rpc-core": "npm:10.10.1" - "@polkadot/rpc-provider": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-augment": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/types-create": "npm:10.10.1" - "@polkadot/types-known": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - "@polkadot/util-crypto": "npm:^12.5.1" - eventemitter3: "npm:^5.0.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 29754dedc8d0fc3fab2acf4d8e4c99a274a42d7394a0214b17219f4b33d6b96b70ecb033d50fdd9f7e62d83126b97cd9b27be87ea4edeca79296bb40cdc254a2 - languageName: node - linkType: hard - "@polkadot/api@npm:10.11.2, @polkadot/api@npm:^10.9.1": version: 10.11.2 resolution: "@polkadot/api@npm:10.11.2" @@ -3970,7 +3904,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/keyring@npm:^12.3.1, @polkadot/keyring@npm:^12.5.1, @polkadot/keyring@npm:^12.6.1, @polkadot/keyring@npm:^12.6.2": +"@polkadot/keyring@npm:^12.3.1, @polkadot/keyring@npm:^12.6.1, @polkadot/keyring@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/keyring@npm:12.6.2" dependencies: @@ -4006,17 +3940,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:12.5.1": - version: 12.5.1 - resolution: "@polkadot/networks@npm:12.5.1" - dependencies: - "@polkadot/util": "npm:12.5.1" - "@substrate/ss58-registry": "npm:^1.43.0" - tslib: "npm:^2.6.2" - checksum: 1aa40e06fcaa6ff5e08b994ee5f8ccaa298bd6ceeed064341c8be4cbde11c7b751f87436015e80ab51377282c9fb43655fa92829312e6ca08de7bd008240f537 - languageName: node - linkType: hard - "@polkadot/networks@npm:12.6.1": version: 12.6.1 resolution: "@polkadot/networks@npm:12.6.1" @@ -4028,7 +3951,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:12.6.2, @polkadot/networks@npm:^12.3.1, @polkadot/networks@npm:^12.5.1, @polkadot/networks@npm:^12.6.1, @polkadot/networks@npm:^12.6.2": +"@polkadot/networks@npm:12.6.2, @polkadot/networks@npm:^12.3.1, @polkadot/networks@npm:^12.6.1, @polkadot/networks@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/networks@npm:12.6.2" dependencies: @@ -4115,19 +4038,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-augment@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/rpc-augment@npm:10.10.1" - dependencies: - "@polkadot/rpc-core": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: 1d52c080f9f6302a7b94885224dd407bfc781915b685d9c8079990588b4fb3f2f45435cac9074892109700700818424054992d2fb5650d4b55d2e4b60e84d98e - languageName: node - linkType: hard - "@polkadot/rpc-augment@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/rpc-augment@npm:10.11.2" @@ -4141,20 +4051,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-core@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/rpc-core@npm:10.10.1" - dependencies: - "@polkadot/rpc-augment": "npm:10.10.1" - "@polkadot/rpc-provider": "npm:10.10.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 7d8b70f1595aead35f4a0fe1d89c5ae2c3b113d3ec2231b1e415e93a4e4cf7133305b3c29fb16ad94a5e35f5c6d26765d3d0f51e044678c43b6a14d41e0f6759 - languageName: node - linkType: hard - "@polkadot/rpc-core@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/rpc-core@npm:10.11.2" @@ -4169,30 +4065,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-provider@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/rpc-provider@npm:10.10.1" - dependencies: - "@polkadot/keyring": "npm:^12.5.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-support": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - "@polkadot/util-crypto": "npm:^12.5.1" - "@polkadot/x-fetch": "npm:^12.5.1" - "@polkadot/x-global": "npm:^12.5.1" - "@polkadot/x-ws": "npm:^12.5.1" - "@substrate/connect": "npm:0.7.33" - eventemitter3: "npm:^5.0.1" - mock-socket: "npm:^9.3.1" - nock: "npm:^13.3.4" - tslib: "npm:^2.6.2" - dependenciesMeta: - "@substrate/connect": - optional: true - checksum: 494669912e29669e5e845a4f48eb17a50aa1730d09123386f0ab330d8122b43f78faa8382fa0afbd63d486863235701f6e8cd358e87f10f2bad102e526c70a7c - languageName: node - linkType: hard - "@polkadot/rpc-provider@npm:10.11.2, @polkadot/rpc-provider@npm:^10.9.1": version: 10.11.2 resolution: "@polkadot/rpc-provider@npm:10.11.2" @@ -4217,18 +4089,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-augment@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types-augment@npm:10.10.1" - dependencies: - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: 719a82a2bc16aeefc715167c8b4f96594a5cfab3a961e0a799caf937597154eee212ffc8467392a24afa825fdf71ad47a21f52f0d4a034935cb14d8bb9bffde2 - languageName: node - linkType: hard - "@polkadot/types-augment@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/types-augment@npm:10.11.2" @@ -4241,17 +4101,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-codec@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types-codec@npm:10.10.1" - dependencies: - "@polkadot/util": "npm:^12.5.1" - "@polkadot/x-bigint": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: 39bbfeb9dc90d3821e51feae64c976b25a680c692e5cfec7aa0ae71c6c1fa33dd948b5b4ab339d20a87acb3786b2e3381e8dc060de18d65adf9156bdb4d834f5 - languageName: node - linkType: hard - "@polkadot/types-codec@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/types-codec@npm:10.11.2" @@ -4263,17 +4112,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-create@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types-create@npm:10.10.1" - dependencies: - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: f5c3b7c46d5b1f428f7b96437e9b1d4b8ac40febf81a16e35bf8bc63dff2ff75ec0e798badf459ca7ecac85b1fc585c00af7f686257f88e3a6fb24c7e8f08417 - languageName: node - linkType: hard - "@polkadot/types-create@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/types-create@npm:10.11.2" @@ -4285,20 +4123,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-known@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types-known@npm:10.10.1" - dependencies: - "@polkadot/networks": "npm:^12.5.1" - "@polkadot/types": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/types-create": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: 9cee702315485ae01d3f4dea8f8f35cc26ac4d2a406d9d4fc403aef4e2b3446bab75b5d412b948ea98613873df54bba4905ec6203c450fa067185f110835d135 - languageName: node - linkType: hard - "@polkadot/types-known@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/types-known@npm:10.11.2" @@ -4313,16 +4137,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-support@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types-support@npm:10.10.1" - dependencies: - "@polkadot/util": "npm:^12.5.1" - tslib: "npm:^2.6.2" - checksum: 35133b9880776138e521fa2dde6cc1c3e117e8883b4af7b0688a4f3046b3a5f47ceff7308f591f2603346af638a97d618c6b0033b2bdb41d7c0e26e4df1a4826 - languageName: node - linkType: hard - "@polkadot/types-support@npm:10.11.2": version: 10.11.2 resolution: "@polkadot/types-support@npm:10.11.2" @@ -4333,22 +4147,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types@npm:10.10.1": - version: 10.10.1 - resolution: "@polkadot/types@npm:10.10.1" - dependencies: - "@polkadot/keyring": "npm:^12.5.1" - "@polkadot/types-augment": "npm:10.10.1" - "@polkadot/types-codec": "npm:10.10.1" - "@polkadot/types-create": "npm:10.10.1" - "@polkadot/util": "npm:^12.5.1" - "@polkadot/util-crypto": "npm:^12.5.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: b348d09594e0576ea911c1dbf18e81d9d3a4b73fe32747626573931d94695533a00dca3ab18801a851c0fa6580daed055dba3e25f9afc78e9c0d66eab7628a30 - languageName: node - linkType: hard - "@polkadot/types@npm:10.11.2, @polkadot/types@npm:^10.9.1": version: 10.11.2 resolution: "@polkadot/types@npm:10.11.2" @@ -4492,26 +4290,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:12.5.1": - version: 12.5.1 - resolution: "@polkadot/util-crypto@npm:12.5.1" - dependencies: - "@noble/curves": "npm:^1.2.0" - "@noble/hashes": "npm:^1.3.2" - "@polkadot/networks": "npm:12.5.1" - "@polkadot/util": "npm:12.5.1" - "@polkadot/wasm-crypto": "npm:^7.2.2" - "@polkadot/wasm-util": "npm:^7.2.2" - "@polkadot/x-bigint": "npm:12.5.1" - "@polkadot/x-randomvalues": "npm:12.5.1" - "@scure/base": "npm:^1.1.3" - tslib: "npm:^2.6.2" - peerDependencies: - "@polkadot/util": 12.5.1 - checksum: dad38ba6592d2b85e88ef29508f4ef161ba88cfaffb5d9e687f06bccae4482409908bfcb6d4565ee34bc443b1cd1e9aa696147d86f671e5d219feaedc5d84fdc - languageName: node - linkType: hard - "@polkadot/util-crypto@npm:12.6.1": version: 12.6.1 resolution: "@polkadot/util-crypto@npm:12.6.1" @@ -4532,7 +4310,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:12.6.2, @polkadot/util-crypto@npm:^12.3.1, @polkadot/util-crypto@npm:^12.3.2, @polkadot/util-crypto@npm:^12.5.1, @polkadot/util-crypto@npm:^12.6.1, @polkadot/util-crypto@npm:^12.6.2": +"@polkadot/util-crypto@npm:12.6.2, @polkadot/util-crypto@npm:^12.3.1, @polkadot/util-crypto@npm:^12.3.2, @polkadot/util-crypto@npm:^12.6.1, @polkadot/util-crypto@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/util-crypto@npm:12.6.2" dependencies: @@ -4612,7 +4390,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util@npm:12.6.2, @polkadot/util@npm:^12.3.1, @polkadot/util@npm:^12.3.2, @polkadot/util@npm:^12.5.1, @polkadot/util@npm:^12.6.1, @polkadot/util@npm:^12.6.2": +"@polkadot/util@npm:12.6.2, @polkadot/util@npm:^12.3.1, @polkadot/util@npm:^12.3.2, @polkadot/util@npm:^12.6.1, @polkadot/util@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/util@npm:12.6.2" dependencies: @@ -4748,7 +4526,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/wasm-crypto@npm:^7.0.3, @polkadot/wasm-crypto@npm:^7.2.1, @polkadot/wasm-crypto@npm:^7.2.2, @polkadot/wasm-crypto@npm:^7.3.1, @polkadot/wasm-crypto@npm:^7.3.2": +"@polkadot/wasm-crypto@npm:^7.0.3, @polkadot/wasm-crypto@npm:^7.2.1, @polkadot/wasm-crypto@npm:^7.3.1, @polkadot/wasm-crypto@npm:^7.3.2": version: 7.3.2 resolution: "@polkadot/wasm-crypto@npm:7.3.2" dependencies: @@ -4776,7 +4554,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/wasm-util@npm:7.3.2, @polkadot/wasm-util@npm:^7.2.1, @polkadot/wasm-util@npm:^7.2.2, @polkadot/wasm-util@npm:^7.3.1, @polkadot/wasm-util@npm:^7.3.2": +"@polkadot/wasm-util@npm:7.3.2, @polkadot/wasm-util@npm:^7.2.1, @polkadot/wasm-util@npm:^7.3.1, @polkadot/wasm-util@npm:^7.3.2": version: 7.3.2 resolution: "@polkadot/wasm-util@npm:7.3.2" dependencies: @@ -4827,7 +4605,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-bigint@npm:12.6.2, @polkadot/x-bigint@npm:^12.5.1, @polkadot/x-bigint@npm:^12.6.2": +"@polkadot/x-bigint@npm:12.6.2, @polkadot/x-bigint@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/x-bigint@npm:12.6.2" dependencies: @@ -4837,7 +4615,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-fetch@npm:^12.5.1, @polkadot/x-fetch@npm:^12.6.2": +"@polkadot/x-fetch@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/x-fetch@npm:12.6.2" dependencies: @@ -4884,7 +4662,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-global@npm:12.6.2, @polkadot/x-global@npm:^12.3.2, @polkadot/x-global@npm:^12.5.1, @polkadot/x-global@npm:^12.6.2": +"@polkadot/x-global@npm:12.6.2, @polkadot/x-global@npm:^12.3.2, @polkadot/x-global@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/x-global@npm:12.6.2" dependencies: @@ -4916,19 +4694,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-randomvalues@npm:12.5.1": - version: 12.5.1 - resolution: "@polkadot/x-randomvalues@npm:12.5.1" - dependencies: - "@polkadot/x-global": "npm:12.5.1" - tslib: "npm:^2.6.2" - peerDependencies: - "@polkadot/util": 12.5.1 - "@polkadot/wasm-util": "*" - checksum: 30c6d007cc4bc1db28ced432e6177b70e3b92f6f6f752e36d5c78fb27c089261e6cf55e7a2782ac4bd8a8315b265efc702108fe6b4a711516e79f6249ccfe8d4 - languageName: node - linkType: hard - "@polkadot/x-randomvalues@npm:12.6.1": version: 12.6.1 resolution: "@polkadot/x-randomvalues@npm:12.6.1" @@ -5055,7 +4820,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-ws@npm:^12.5.1, @polkadot/x-ws@npm:^12.6.2": +"@polkadot/x-ws@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/x-ws@npm:12.6.2" dependencies: @@ -6451,7 +6216,7 @@ __metadata: languageName: node linkType: hard -"@substrate/ss58-registry@npm:^1.39.0, @substrate/ss58-registry@npm:^1.40.0, @substrate/ss58-registry@npm:^1.43.0, @substrate/ss58-registry@npm:^1.44.0": +"@substrate/ss58-registry@npm:^1.39.0, @substrate/ss58-registry@npm:^1.40.0, @substrate/ss58-registry@npm:^1.44.0": version: 1.46.0 resolution: "@substrate/ss58-registry@npm:1.46.0" checksum: 29a3d554dcb99b98a9fec4a876ed11d2187c939f38f57f5de1907fe67bfa8081d77f8b7740605ac5a4199d428653c6109b513e986752973e2efcda6bc91f8afc @@ -7516,15 +7281,6 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:18.2.8": - version: 18.2.8 - resolution: "@types/react-dom@npm:18.2.8" - dependencies: - "@types/react": "npm:*" - checksum: a2afa9f626751f2314ddbecb5503122912ff646827b1c81b7b230ce04083c43b71c82b02244526eb88c1795019033364a4afee0f7f719e46116a51ca8e287613 - languageName: node - linkType: hard - "@types/react-dom@npm:^18.0.0": version: 18.2.18 resolution: "@types/react-dom@npm:18.2.18" @@ -7552,17 +7308,6 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:18.2.24": - version: 18.2.24 - resolution: "@types/react@npm:18.2.24" - dependencies: - "@types/prop-types": "npm:*" - "@types/scheduler": "npm:*" - csstype: "npm:^3.0.2" - checksum: a83c7ae0010b265012ef038e3e00e4708c27c523f0aa0631e44f934e9c5338a51b6db1901f91d8ba10d3dc292a3a200b2cb5e47430cde58fbf988969866fe75a - languageName: node - linkType: hard - "@types/react@npm:18.2.33": version: 18.2.33 resolution: "@types/react@npm:18.2.33" @@ -7730,30 +7475,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/eslint-plugin@npm:5.59.1" - dependencies: - "@eslint-community/regexpp": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:5.59.1" - "@typescript-eslint/type-utils": "npm:5.59.1" - "@typescript-eslint/utils": "npm:5.59.1" - debug: "npm:^4.3.4" - grapheme-splitter: "npm:^1.0.4" - ignore: "npm:^5.2.0" - natural-compare-lite: "npm:^1.4.0" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 39b45b35617b47df6242791f67ee53dafe8d973c0ea452cfb6d8f5883b7ee6c8a5056110c53b91fa941c81294110ea2049f082da53b45fe42deafbeec1f9fbdf - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/eslint-plugin@npm:5.61.0" @@ -7882,23 +7603,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/parser@npm:5.59.1" - dependencies: - "@typescript-eslint/scope-manager": "npm:5.59.1" - "@typescript-eslint/types": "npm:5.59.1" - "@typescript-eslint/typescript-estree": "npm:5.59.1" - debug: "npm:^4.3.4" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 59a9076ac1b547bbc36517cbb8201489541670023c4647d2f21f5b5172ab097c83e3b7d2652ebafe9a0efba673e09056fa8f4f2c1eae656d8328ec9084e31629 - languageName: node - linkType: hard - "@typescript-eslint/parser@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/parser@npm:5.61.0" @@ -7997,16 +7701,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/scope-manager@npm:5.59.1" - dependencies: - "@typescript-eslint/types": "npm:5.59.1" - "@typescript-eslint/visitor-keys": "npm:5.59.1" - checksum: 0b661e8d7221b6f6c83029127ddfac811f857dacd4bf1d7c70d9ed3c6d5f862da9596f03947d6e9bce6f18ba26d310a07732f70450e16fbd70b54ac74e5df81f - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/scope-manager@npm:5.61.0" @@ -8073,23 +7767,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/type-utils@npm:5.59.1" - dependencies: - "@typescript-eslint/typescript-estree": "npm:5.59.1" - "@typescript-eslint/utils": "npm:5.59.1" - debug: "npm:^4.3.4" - tsutils: "npm:^3.21.0" - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: fe4ab0609529d2bc2d1a1a6f0aed667448342194c81bf2766b6f015086c37679da57ac9392489f0bd734e7cb49609353b580de96e88b4968ccf3ab7d203aa8ca - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/type-utils@npm:5.61.0" @@ -8165,13 +7842,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/types@npm:5.59.1" - checksum: 28c128906bf7a2aaef48db056f75db494007047e60b1bfb9f2dc663aaf5d70f34f4cef51bf4330194cb83144156131aa825e321253519aea1f08f8405d7a0b78 - languageName: node - linkType: hard - "@typescript-eslint/types@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/types@npm:5.61.0" @@ -8225,24 +7895,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/typescript-estree@npm:5.59.1" - dependencies: - "@typescript-eslint/types": "npm:5.59.1" - "@typescript-eslint/visitor-keys": "npm:5.59.1" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 856bcc61c8ec69c979f139ad1bfff965d1f1fe72bfcedee8a62be2b24c5b8b1a1bcd874e83b7f235cd5cadf88936da203064f64dd99de0aa63697228e8109c6f - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/typescript-estree@npm:5.61.0" @@ -8350,24 +8002,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/utils@npm:5.59.1" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.59.1" - "@typescript-eslint/types": "npm:5.59.1" - "@typescript-eslint/typescript-estree": "npm:5.59.1" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 366fcca9bb39ed74a5fac696fda1e12dc8ef9b0c6bc84afcf2da738052ff0921513fccccae7df219bf8f2fd3a81a0438cba70aedbe0b0545f1d47fbf9d766f30 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/utils@npm:5.61.0" @@ -8449,16 +8083,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.59.1": - version: 5.59.1 - resolution: "@typescript-eslint/visitor-keys@npm:5.59.1" - dependencies: - "@typescript-eslint/types": "npm:5.59.1" - eslint-visitor-keys: "npm:^3.3.0" - checksum: f2d48ba4adf19f6b34306b281886e0dfc8bd7b3a6ecf5f65ff2bd104aa01f3b706904a6fd5b8f97eddec11d503d81275ee946f418ce3ee27c947826b2e7aaccf - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:5.61.0": version: 5.61.0 resolution: "@typescript-eslint/visitor-keys@npm:5.61.0" @@ -9809,15 +9433,15 @@ __metadata: "@dapps-frontend/hooks": "workspace:*" "@dapps-frontend/signless-transactions": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@gear-js/vara-ui": "npm:0.0.6" "@mantine/form": "npm:6.0.15" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" - "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/react-identicon": "npm:3.1.4" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@radix-ui/react-dropdown-menu": "npm:2.0.5" "@radix-ui/react-scroll-area": "npm:1.0.4" @@ -9877,6 +9501,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:9.1.2": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: e17786545433f3110b868725c449fa9625366a6e675cd70eb39b60938d6adbd0158cb4b3ad4f306ce817165d37e63f4aa3098ba4110db1d9a3b9f66abfbaf10d + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -10532,7 +10163,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.0.0": +"clsx@npm:2.1.0, clsx@npm:^2.0.0": version: 2.1.0 resolution: "clsx@npm:2.1.0" checksum: c09c00ad14f638366ca814097e6cab533dfa1972a358da5b557be487168acbb25b4c1395e89ffa842a8a61ba87a462d2b4885bc9d4f8410b598f3cb339599cdb @@ -12483,14 +12114,14 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@mantine/form": "npm:4.2.12" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -12910,7 +12541,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 @@ -12978,56 +12609,6 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.39.0": - version: 8.39.0 - resolution: "eslint@npm:8.39.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@eslint-community/regexpp": "npm:^4.4.0" - "@eslint/eslintrc": "npm:^2.0.2" - "@eslint/js": "npm:8.39.0" - "@humanwhocodes/config-array": "npm:^0.11.8" - "@humanwhocodes/module-importer": "npm:^1.0.1" - "@nodelib/fs.walk": "npm:^1.2.8" - ajv: "npm:^6.10.0" - chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.2" - debug: "npm:^4.3.2" - doctrine: "npm:^3.0.0" - escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^7.2.0" - eslint-visitor-keys: "npm:^3.4.0" - espree: "npm:^9.5.1" - esquery: "npm:^1.4.2" - esutils: "npm:^2.0.2" - fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^6.0.1" - find-up: "npm:^5.0.0" - glob-parent: "npm:^6.0.2" - globals: "npm:^13.19.0" - grapheme-splitter: "npm:^1.0.4" - ignore: "npm:^5.2.0" - import-fresh: "npm:^3.0.0" - imurmurhash: "npm:^0.1.4" - is-glob: "npm:^4.0.0" - is-path-inside: "npm:^3.0.3" - js-sdsl: "npm:^4.1.4" - js-yaml: "npm:^4.1.0" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - levn: "npm:^0.4.1" - lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" - natural-compare: "npm:^1.4.0" - optionator: "npm:^0.9.1" - strip-ansi: "npm:^6.0.1" - strip-json-comments: "npm:^3.1.0" - text-table: "npm:^0.2.0" - bin: - eslint: bin/eslint.js - checksum: 34679da06fbc9ee75d13de57864589537e7460408c923510029b87cdf9f52fec2eb7f85cebbbff7ed15de0b37b7b14969efb036804f774aa4455809c9ccea2cb - languageName: node - linkType: hard - "eslint@npm:8.43.0": version: 8.43.0 resolution: "eslint@npm:8.43.0" @@ -13315,7 +12896,7 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.3.2, espree@npm:^9.4.0, espree@npm:^9.5.1, espree@npm:^9.5.2, espree@npm:^9.6.0, espree@npm:^9.6.1": +"espree@npm:^9.3.2, espree@npm:^9.4.0, espree@npm:^9.5.2, espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" dependencies: @@ -14066,20 +13647,19 @@ __metadata: version: 0.0.0-use.local resolution: "frontend@workspace:apps/w3bstreaming" dependencies: - "@craco/craco": "npm:7.1.0" "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@gear-js/vara-ui": "npm:0.0.6" "@headlessui/react": "npm:1.7.17" "@mantine/form": "npm:6.0.15" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/extension-inject": "npm:0.46.5" - "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/react-identicon": "npm:3.1.4" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/util-crypto": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" @@ -14089,11 +13669,11 @@ __metadata: "@testing-library/user-event": "npm:13.5.0" "@types/lodash.merge": "npm:4.6.7" "@types/node": "npm:18.17.15" - "@types/react": "npm:18.2.24" + "@types/react": "npm:18.2.33" "@types/react-datepicker": "npm:4.11.2" - "@types/react-dom": "npm:18.2.8" + "@types/react-dom": "npm:18.2.14" "@typescript-eslint/parser": "npm:6.7.3" - assert: "npm:2.1.0" + assert: "npm:2.0.0" axios: "npm:1.4.0" babel-polyfill: "npm:6.26.0" buffer: "npm:6.0.3" @@ -14113,6 +13693,7 @@ __metadata: moment-timezone: "npm:0.5.43" rc-time-picker: "npm:^3.7.3" react: "npm:^18.2.0" + react-app-rewired: "npm:2.2.1" react-datepicker: "npm:4.15.0" react-dom: "npm:18.2.0" react-dropzone: "npm:14.2.3" @@ -14124,7 +13705,6 @@ __metadata: sass: "npm:1.62.0" socket.io: "npm:4.6.1" socket.io-client: "npm:4.6.1" - types: "npm:*" typescript: "npm:4.9.5" video.js: "npm:8.3.0" web-vitals: "npm:2.1.4" @@ -14273,15 +13853,15 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui/react": "npm:^1.7.17" "@mantine/form": "npm:6.0.8" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@radix-ui/react-scroll-area": "npm:1.0.4" @@ -14296,7 +13876,7 @@ __metadata: "@typescript-eslint/parser": "npm:6.7.3" assert: "npm:2.0.0" buffer: "npm:6.0.3" - clsx: "npm:^1.2.1" + clsx: "npm:2.1.0" eslint: "npm:8.16.0" eslint-config-airbnb: "npm:19.0.4" eslint-config-airbnb-typescript: "npm:17.0.0" @@ -14326,14 +13906,14 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.4.0" "@mantine/form": "npm:4.2.12" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -14372,16 +13952,16 @@ __metadata: version: 0.0.0-use.local resolution: "gear-app@workspace:templates/gear-app-starter" dependencies: - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui/react": "npm:^1.7.17" "@mantine/form": "npm:^5.10.5" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/extension-inject": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/util-crypto": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" @@ -16898,13 +16478,6 @@ __metadata: languageName: node linkType: hard -"js-sdsl@npm:^4.1.4": - version: 4.4.2 - resolution: "js-sdsl@npm:4.4.2" - checksum: 50707728fc31642164f4d83c8087f3750aaa99c450b008b19e236a1f190c9e48f9fc799615c341f9ca2c0803b15ab6f48d92a9cc3e6ffd20065cba7d7e742b92 - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -18057,6 +17630,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:5.0.1": + version: 5.0.1 + resolution: "nanoid@npm:5.0.1" + bin: + nanoid: bin/nanoid.js + checksum: 7d5d267d8b1b14fe51f4fea41a7fc3baefad881438f776d6693eeb73139ea1afa69a60a220c18cf3ec6f1b51bf44a6a4dfcca4778ae113d8b221da7de03a40fa + languageName: node + linkType: hard + "nanoid@npm:^3.0.2, nanoid@npm:^3.1.20, nanoid@npm:^3.1.23, nanoid@npm:^3.3.4, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -18127,13 +17709,13 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.4.0" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -18176,12 +17758,12 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@mantine/form": "npm:6.0.8" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@radix-ui/react-scroll-area": "npm:1.0.4" @@ -18229,7 +17811,7 @@ __metadata: languageName: node linkType: hard -"nock@npm:^13.3.4, nock@npm:^13.4.0": +"nock@npm:^13.4.0": version: 13.5.0 resolution: "nock@npm:13.5.0" dependencies: @@ -18354,13 +17936,13 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.4.0" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -20292,15 +19874,15 @@ __metadata: "@craco/craco": "npm:7.1.0" "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui/react": "npm:1.7.17" "@mantine/form": "npm:6.0.15" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@radix-ui/react-scroll-area": "npm:1.0.4" "@types/lodash.isequal": "npm:4.5.6" @@ -21572,19 +21154,6 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.58.3": - version: 1.58.3 - resolution: "sass@npm:1.58.3" - dependencies: - chokidar: "npm:>=3.0.0 <4.0.0" - immutable: "npm:^4.0.0" - source-map-js: "npm:>=0.6.2 <2.0.0" - bin: - sass: sass.js - checksum: 8ed6d3e19422642b56c1e3d09e36307746f4515cd3a902994ec4d0ce6e5d32a09811f377842945a8cffb12646fa607fe46b387131f42488cb5f74e51b425e754 - languageName: node - linkType: hard - "sass@npm:1.62.0": version: 1.62.0 resolution: "sass@npm:1.62.0" @@ -22626,14 +22195,14 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.4.0" "@mantine/form": "npm:4.2.12" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -22772,14 +22341,14 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@mantine/form": "npm:4.2.12" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -22989,18 +22558,17 @@ __metadata: "@dapps-frontend/hooks": "workspace:*" "@dapps-frontend/ui": "workspace:*" "@fireworks-js/react": "npm:2.10.7" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui-float/react": "npm:0.10.1" "@headlessui/react": "npm:1.7.7" "@mantine/form": "npm:5.10.0" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" - "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/react-identicon": "npm:3.1.4" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" - "@polkadot/util-crypto": "npm:12.5.1" "@polkadot/wasm-crypto": "npm:7.2.2" "@radix-ui/react-scroll-area": "npm:1.0.4" "@types/react": "npm:18.2.33" @@ -23046,15 +22614,15 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui-float/react": "npm:0.11.0" "@headlessui/react": "npm:1.7.13" "@mantine/form": "npm:5.10.0" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@radix-ui/react-scroll-area": "npm:1.0.3" @@ -23139,30 +22707,35 @@ __metadata: resolution: "tequila-train@workspace:apps/tequila-train" dependencies: "@dapps-frontend/error-tracking": "workspace:*" + "@dapps-frontend/hooks": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui/react": "npm:1.7.13" "@mantine/form": "npm:6.0.19" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/extension-inject": "npm:0.46.5" - "@polkadot/react-identicon": "npm:3.5.1" - "@polkadot/types": "npm:10.10.1" + "@polkadot/react-identicon": "npm:3.1.4" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" - "@polkadot/util-crypto": "npm:12.5.1" "@polkadot/wasm-crypto": "npm:7.2.2" "@radix-ui/react-scroll-area": "npm:1.0.3" + "@testing-library/jest-dom": "npm:5.16.4" + "@testing-library/react": "npm:13.2.0" + "@testing-library/user-event": "npm:13.5.0" + "@types/jest": "npm:27.5.1" + "@types/node": "npm:18.15.11" "@types/react": "npm:18.2.33" "@types/react-dom": "npm:18.2.14" - "@typescript-eslint/eslint-plugin": "npm:5.59.1" - "@typescript-eslint/parser": "npm:5.59.1" + "@typescript-eslint/eslint-plugin": "npm:5.25.0" + "@typescript-eslint/parser": "npm:5.25.0" assert: "npm:2.0.0" autoprefixer: "npm:10.4.14" buffer: "npm:6.0.3" clsx: "npm:1.2.1" - eslint: "npm:8.39.0" + eslint: "npm:8.16.0" eslint-config-airbnb: "npm:19.0.4" eslint-config-airbnb-typescript: "npm:17.0.0" eslint-config-prettier: "npm:8.8.0" @@ -23183,7 +22756,8 @@ __metadata: sass: "npm:1.62.0" tailwind-merge: "npm:1.12.0" tailwindcss: "npm:3.3.1" - typescript: "npm:4.9.5" + typescript: "npm:5.0.2" + web-vitals: "npm:3.3.1" languageName: unknown linkType: soft @@ -23289,14 +22863,13 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@headlessui/react": "npm:1.7.17" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" - "@polkadot/util-crypto": "npm:12.3.2" "@radix-ui/react-dialog": "npm:1.0.4" "@radix-ui/react-scroll-area": "npm:1.0.4" "@types/lodash.isequal": "npm:4.5.6" @@ -23304,6 +22877,7 @@ __metadata: "@types/react": "npm:18.2.33" "@types/react-dom": "npm:18.2.14" "@vitejs/plugin-react-swc": "npm:3.3.2" + assert: "npm:2.0.0" autoprefixer: "npm:10.4.15" buffer: "npm:6.0.3" class-variance-authority: "npm:0.6.1" @@ -23322,7 +22896,7 @@ __metadata: react-router-dom: "npm:6.10.0" react-transition-group: "npm:4.4.5" rollup-plugin-visualizer: "npm:5.9.2" - sass: "npm:1.58.3" + sass: "npm:1.62.0" socket.io-client: "npm:4.7.2" tailwindcss: "npm:3.3.3" typescript: "npm:4.9.5" @@ -24149,12 +23723,12 @@ __metadata: dependencies: "@dapps-frontend/error-tracking": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@headlessui/react": "npm:1.7.14" "@mantine/form": "npm:6.0.10" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.5.1" "@radix-ui/react-scroll-area": "npm:1.0.3" @@ -24195,16 +23769,16 @@ __metadata: resolution: "varatube@workspace:apps/varatube" dependencies: "@dapps-frontend/error-tracking": "workspace:*" - "@dapps-frontend/signless-transactions": "workspace:*" + "@dapps-frontend/hooks": "workspace:*" "@dapps-frontend/ui": "workspace:*" - "@gear-js/api": "npm:0.35.2" - "@gear-js/react-hooks": "npm:0.9.4" + "@gear-js/api": "npm:0.36.5" + "@gear-js/react-hooks": "npm:0.10.2" "@gear-js/ui": "npm:0.5.21" "@mantine/form": "npm:4.2.12" - "@polkadot/api": "npm:10.10.1" + "@polkadot/api": "npm:10.11.2" "@polkadot/extension-dapp": "npm:0.46.5" "@polkadot/react-identicon": "npm:3.1.4" - "@polkadot/types": "npm:10.10.1" + "@polkadot/types": "npm:10.11.2" "@polkadot/util": "npm:12.3.2" "@polkadot/wasm-crypto": "npm:7.2.2" "@testing-library/jest-dom": "npm:5.16.4" @@ -24237,7 +23811,7 @@ __metadata: react-transition-group: "npm:4.4.5" sass: "npm:1.62.0" simplebar-react: "npm:3.2.1" - typescript: "npm:4.9.5" + typescript: "npm:5.0.2" web-vitals: "npm:3.3.1" languageName: unknown linkType: soft From 29ad6f80f85b6393a4388c42a2646dfab786eb19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:00:12 +0300 Subject: [PATCH 03/17] Bump the deps group in /contracts with 6 updates (#252) --- contracts/Cargo.lock | 142 +++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index e8650ff30..463055106 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -92,9 +92,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "getrandom 0.2.12", @@ -150,9 +150,9 @@ checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "array-bytes" @@ -362,7 +362,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -902,18 +902,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstyle", "clap_lex", @@ -921,9 +921,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cmake" @@ -1343,7 +1343,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1469,7 +1469,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1491,7 +1491,7 @@ checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" dependencies = [ "darling_core 0.20.5", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2020,7 +2020,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2041,7 +2041,7 @@ dependencies = [ "darling 0.20.5", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2163,7 +2163,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2367,7 +2367,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2379,7 +2379,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2389,7 +2389,7 @@ source = "git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v1 dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2540,7 +2540,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2699,7 +2699,7 @@ version = "1.1.0" source = "git+https://github.com/gear-tech/gear?tag=v1.1.0#c8c09fc84385c17a1327033833acaba6a5a0d416" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2868,9 +2868,9 @@ dependencies = [ name = "gear-lib" version = "1.1.0" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "gstd", - "indexmap 2.2.2", + "indexmap 2.2.3", "primitive-types", ] @@ -2879,7 +2879,7 @@ name = "gear-lib-derive" version = "1.1.0" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -3162,7 +3162,7 @@ checksum = "3e3a341d27b765d12cff7968acf86ccc2085c89b8b656f63f1f7cdd3d3924c94" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -3191,7 +3191,7 @@ dependencies = [ "gear-core-errors 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "gsdk-codegen", "hex", - "indexmap 2.2.2", + "indexmap 2.2.3", "jsonrpsee 0.16.3", "log", "parity-scale-codec", @@ -3215,7 +3215,7 @@ checksum = "6ce25dd799a0ba1ec70839fb16ab0b93da1c8275bc093586c1b4154da032ca3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -3247,7 +3247,7 @@ checksum = "2555e0bd35a1b6a651e774d8d150a13f9ccab2658debe564344120c4acd170b0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -3305,7 +3305,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.2", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -3351,7 +3351,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", ] [[package]] @@ -3360,7 +3360,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "allocator-api2", "serde", ] @@ -3665,9 +3665,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -4150,7 +4150,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -4164,7 +4164,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -4175,7 +4175,7 @@ checksum = "d710e1214dffbab3b5dacb21475dde7d6ed84c69ff722b3a47a782668d44fbac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -4186,7 +4186,7 @@ checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -4638,9 +4638,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -4672,7 +4672,7 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -4975,7 +4975,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -5167,7 +5167,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -5360,7 +5360,7 @@ checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -5941,7 +5941,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "cfg-if", "hashbrown 0.13.2", ] @@ -6101,7 +6101,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -6577,7 +6577,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -6809,7 +6809,7 @@ source = "git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v1 dependencies = [ "quote", "sp-core-hashing 9.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v1.0.0-canary)", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -6820,7 +6820,7 @@ checksum = "c7f531814d2f16995144c74428830ccf7d94ff4a7749632b83ad8199b181140c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -6830,7 +6830,7 @@ source = "git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v1 dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -6841,7 +6841,7 @@ checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7114,7 +7114,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7126,7 +7126,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7139,7 +7139,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7299,7 +7299,7 @@ version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4eeb7ef23f79eba8609db79ef9cef242f994f1f87a3c0387b4b5f177fda74" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "hash-db", "hashbrown 0.13.2", "lazy_static", @@ -7322,7 +7322,7 @@ name = "sp-trie" version = "22.0.0" source = "git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v1.0.0-canary#e4b3ebf0ed497e9d121a2e272d238fa87269b756" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "hash-db", "hashbrown 0.13.2", "lazy_static", @@ -7365,7 +7365,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7640,7 +7640,7 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn 2.0.48", + "syn 2.0.49", "thiserror", "tokio", ] @@ -7671,7 +7671,7 @@ dependencies = [ "darling 0.20.5", "proc-macro-error", "subxt-codegen", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7758,9 +7758,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" dependencies = [ "proc-macro2", "quote", @@ -7946,7 +7946,7 @@ checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -7957,7 +7957,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -8064,7 +8064,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -8130,7 +8130,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.2", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", @@ -8143,7 +8143,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.2.2", + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -8195,7 +8195,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -8525,7 +8525,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", "wasm-bindgen-shared", ] @@ -8547,7 +8547,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9465,7 +9465,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -9485,5 +9485,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] From 3c18a6397a6d912392a22a482616830e38b51220 Mon Sep 17 00:00:00 2001 From: YauheniDraichykau <64776571+YauheniDraichykau@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:16:08 +0300 Subject: [PATCH 04/17] Galactic express: updates workflow file (#255) --- .github/workflows/Release-galactic-express.yml | 1 + .github/workflows/STG-galactic-express.yml | 1 + frontend/apps/galactic-express/Dockerfile | 2 ++ 3 files changed, 4 insertions(+) diff --git a/.github/workflows/Release-galactic-express.yml b/.github/workflows/Release-galactic-express.yml index cce54023b..d9ee7d022 100644 --- a/.github/workflows/Release-galactic-express.yml +++ b/.github/workflows/Release-galactic-express.yml @@ -71,6 +71,7 @@ jobs: tags: ${{ needs.prepair.outputs.image_name }} build-args: | REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_GALACTIC }} REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_GALEX }} deploy-to-k8s: diff --git a/.github/workflows/STG-galactic-express.yml b/.github/workflows/STG-galactic-express.yml index 61ecd052e..077fa766a 100644 --- a/.github/workflows/STG-galactic-express.yml +++ b/.github/workflows/STG-galactic-express.yml @@ -76,6 +76,7 @@ jobs: tags: ${{ needs.prepair.outputs.image_name }} build-args: | REACT_APP_NODE_ADDRESS=${{ secrets.REACT_APP_NODE_ADDRESS }} + REACT_APP_CONTRACT_ADDRESS=${{ secrets.REACT_APP_CONTRACT_ADDRESS_GALACTIC }} REACT_APP_SENTRY_DSN=${{ secrets.REACT_SENTRY_DSN_GALEX }} deploy-to-k8s: diff --git a/frontend/apps/galactic-express/Dockerfile b/frontend/apps/galactic-express/Dockerfile index 653bb1d1d..1a284bbc5 100644 --- a/frontend/apps/galactic-express/Dockerfile +++ b/frontend/apps/galactic-express/Dockerfile @@ -16,8 +16,10 @@ RUN apk update RUN apk add xsel ARG REACT_APP_NODE_ADDRESS \ + REACT_APP_CONTRACT_ADDRESS \ REACT_APP_SENTRY_DSN ENV REACT_APP_NODE_ADDRESS=${REACT_APP_NODE_ADDRESS} \ + REACT_APP_CONTRACT_ADDRESS \ REACT_APP_SENTRY_DSN=${REACT_APP_SENTRY_DSN} WORKDIR /frontend/apps/galactic-express From 86e9cacb172f8704054860834debef4c370b2ee3 Mon Sep 17 00:00:00 2001 From: Yauheni Draichykau Date: Mon, 19 Feb 2024 17:23:18 +0100 Subject: [PATCH 05/17] Galactic-express: Fixes dockerfile --- frontend/apps/galactic-express/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/apps/galactic-express/Dockerfile b/frontend/apps/galactic-express/Dockerfile index 1a284bbc5..6df408455 100644 --- a/frontend/apps/galactic-express/Dockerfile +++ b/frontend/apps/galactic-express/Dockerfile @@ -19,7 +19,7 @@ ARG REACT_APP_NODE_ADDRESS \ REACT_APP_CONTRACT_ADDRESS \ REACT_APP_SENTRY_DSN ENV REACT_APP_NODE_ADDRESS=${REACT_APP_NODE_ADDRESS} \ - REACT_APP_CONTRACT_ADDRESS \ + REACT_APP_CONTRACT_ADDRESS=${REACT_APP_CONTRACT_ADDRESS} \ REACT_APP_SENTRY_DSN=${REACT_APP_SENTRY_DSN} WORKDIR /frontend/apps/galactic-express From 507e430d288b9b05c884076399295b17a369270d Mon Sep 17 00:00:00 2001 From: Yauheni Draichykau Date: Tue, 20 Feb 2024 16:37:31 +0100 Subject: [PATCH 06/17] Updates galactic express --- frontend/apps/galactic-express/src/atoms.ts | 3 +- .../src/components/layout/header/Header.tsx | 16 ++- .../CancelGameButton.module.scss | 9 ++ .../cancel-game-button/CancelGameButton.tsx | 18 ++-- .../features/session/components/form/Form.tsx | 5 +- .../GameCancelledModal.scss} | 0 .../GameCancelledModal.tsx | 28 ++++++ .../components/game-cancelled-modal/index.ts | 3 + .../GameNotFoundModal.tsx | 27 ------ .../TextModal.module.scss | 21 ++++ .../game-not-found-modal/TextModal.tsx | 27 ++++++ .../components/game-not-found-modal/index.ts | 4 +- .../components/session/Session.module.scss | 15 +++ .../session/components/session/Session.tsx | 25 +++-- .../session/components/start/Start.tsx | 41 +++++--- .../win-status/WinStatus.module.scss | 9 +- .../components/win-status/WinStatus.tsx | 14 ++- .../src/features/session/types.ts | 41 +++++--- .../enter-contract-address/RequestGame.tsx | 26 ++--- .../galactic-express/src/pages/home/Home.tsx | 97 ++++++++++++------- 20 files changed, 298 insertions(+), 131 deletions(-) rename frontend/apps/galactic-express/src/features/session/components/{game-not-found-modal/GameNotFoundModal.module.scss => game-cancelled-modal/GameCancelledModal.scss} (100%) create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/index.ts delete mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.module.scss create mode 100644 frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.tsx diff --git a/frontend/apps/galactic-express/src/atoms.ts b/frontend/apps/galactic-express/src/atoms.ts index 1207612d5..a7b5877ab 100644 --- a/frontend/apps/galactic-express/src/atoms.ts +++ b/frontend/apps/galactic-express/src/atoms.ts @@ -1,9 +1,10 @@ import { atom } from 'jotai'; +import { RegistrationStatus } from 'features/session/types'; export const CURRENT_GAME_ATOM = atom(''); export const PLAYER_NAME_ATOM = atom(null); -export const PLAYER_INITIAL_STATUS_ATOM = atom<'Finished' | 'Registered' | null>(null); +export const REGISTRATION_STATUS = atom('registration'); export const IS_LOADING = atom(false); diff --git a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx index 0e8be75ee..8a4beb241 100644 --- a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx +++ b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx @@ -4,8 +4,19 @@ import { ReactComponent as GalexSVG } from 'assets/images/logo.svg'; import { ReactComponent as VaraSVG } from 'assets/images/logo-vara.svg'; import { cx } from 'utils'; import styles from './Header.module.scss'; +import { useLaunchState } from 'features/session'; +import { useAccount } from '@gear-js/react-hooks'; +import { CancelGameButton } from 'features/session/components/cancel-game-button'; function Header() { + const { account } = useAccount(); + const state = useLaunchState(); + const { admin, stage } = state || {}; + + const isUserAdmin = admin === account?.decodedAddress; + const isRegistration = Object.keys(stage || {})[0] === 'Registration'; + const participants = stage?.Registration || stage?.Results?.participants; + return ( } - className={{ header: styles.header, content: styles.container }} - /> + className={{ header: styles.header, content: styles.container }}> + {isUserAdmin && isRegistration && } + ); } diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss index b356287cb..5ad84f571 100644 --- a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.module.scss @@ -5,6 +5,15 @@ top: 0; left: 20px; } + +.buttonWrapperAdmin { + position: relative; + display: flex; + justify-content: end; + padding-right: 32px; + flex: 1; +} + .button.button.button { background: #F24A4A12; color: #EB5757; diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx index dca312d48..c53967f67 100644 --- a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx @@ -1,20 +1,21 @@ import { ReactComponent as CrossIconSVG } from 'assets/images/icons/cross-icon.svg'; -import { useAtom } from 'jotai'; +import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { Button } from '@gear-js/vara-ui'; import { useAccount } from '@gear-js/react-hooks'; import { useLaunchMessage } from 'features/session/hooks'; import { Participant } from 'features/session/types'; -import { IS_LOADING } from 'atoms'; +import { IS_LOADING, REGISTRATION_STATUS } from 'atoms'; import styles from './CancelGameButton.module.scss'; +import clsx from 'clsx'; type Props = { isAdmin: boolean; - userAddress: string; participants: Participant[]; }; -function CancelGameButton({ isAdmin, participants, userAddress }: Props) { +function CancelGameButton({ isAdmin, participants }: Props) { const { meta: isMeta, message: sendMessage } = useLaunchMessage(); + const setRegistrationStatus = useSetAtom(REGISTRATION_STATUS); const [isLoading, setIsLoading] = useAtom(IS_LOADING); const { account } = useAccount(); @@ -28,6 +29,7 @@ function CancelGameButton({ isAdmin, participants, userAddress }: Props) { const onInBlock = () => { setIsLoading(false); + setRegistrationStatus('registration'); }; const handleClick = () => { @@ -53,10 +55,10 @@ function CancelGameButton({ isAdmin, participants, userAddress }: Props) { } }; - return isRegistered || isAdmin ? ( -
      + return ( +
      - ) : null; + ); } export { CancelGameButton }; diff --git a/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx b/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx index cfbd97216..4902533e9 100644 --- a/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/form/Form.tsx @@ -5,6 +5,7 @@ import { Button } from '@gear-js/ui'; import { useForm } from '@mantine/form'; import { Card } from 'components'; import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; +import { RegistrationStatus } from 'features/session/types'; import { ReactComponent as RocketSVG } from '../../assets/rocket.svg'; import { INITIAL_VALUES, VALIDATE, WEATHERS } from '../../consts'; import { useLaunchMessage } from '../../hooks'; @@ -16,9 +17,7 @@ type Props = { weather: string; bid: string | undefined; isAdmin: boolean; - setRegistrationStatus: Dispatch< - SetStateAction<'registration' | 'success' | 'error' | 'NotEnoughParticipants' | 'MaximumPlayersReached'> - >; + setRegistrationStatus: Dispatch>; }; function Form({ weather, bid, isAdmin, setRegistrationStatus }: Props) { diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss b/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.scss similarity index 100% rename from frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.module.scss rename to frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.scss diff --git a/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.tsx b/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.tsx new file mode 100644 index 000000000..d945b74e5 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/GameCancelledModal.tsx @@ -0,0 +1,28 @@ +import { Modal } from 'components/layout/modal'; +import { Button } from '@gear-js/vara-ui'; +import styles from './GameCancelledModal.module.scss'; + +type Props = { + admin: string; + onClose: () => void; +}; + +function GameCancelledModal({ admin, onClose }: Props) { + return ( + +
      +

      + Game administrator {admin} has ended the game. All spent VARA tokens for the entry fee will be refunded. +

      + +
      +
      + ); +} + +export { GameCancelledModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/index.ts b/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/index.ts new file mode 100644 index 000000000..0dda51359 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-cancelled-modal/index.ts @@ -0,0 +1,3 @@ +import { GameCancelledModal } from './GameCancelledModal'; + +export { GameCancelledModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx deleted file mode 100644 index 673805351..000000000 --- a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/GameNotFoundModal.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Modal } from 'components/layout/modal'; -import { Button } from '@gear-js/vara-ui'; -import styles from './GameNotFoundModal.module.scss'; - -type Props = { - onClose: () => void; -}; - -export type JoinModalFormValues = { - name: string; -}; - -function GameNotFoundModal({ onClose }: Props) { - return ( - -
      -

      - Please check the entered address. It's possible the game has been canceled or does not exist. -

      - -
      -
      - ); -} - -export { GameNotFoundModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.module.scss b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.module.scss new file mode 100644 index 000000000..2d55a1dbc --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.module.scss @@ -0,0 +1,21 @@ +.modal { + min-width: 670px; +} + +.container { + display: flex; + flex-direction: column; + gap: 28px; +} + +.text { + font-size: 14px; +} + +.button { + width: 45%; +} + +.modalHeader { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.tsx b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.tsx new file mode 100644 index 000000000..7db6ae950 --- /dev/null +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/TextModal.tsx @@ -0,0 +1,27 @@ +import { Modal } from 'components/layout/modal'; +import { Button } from '@gear-js/vara-ui'; +import styles from './TextModal.module.scss'; + +type Props = { + heading: string; + text: string; + onClose: () => void; +}; + +export type JoinModalFormValues = { + name: string; +}; + +function TextModal({ heading, text, onClose }: Props) { + return ( + +
      +

      {text}

      + +
      +
      + ); +} + +export { TextModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts index 36b74b523..a8c3b7608 100644 --- a/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts +++ b/frontend/apps/galactic-express/src/features/session/components/game-not-found-modal/index.ts @@ -1,3 +1,3 @@ -import { GameNotFoundModal } from './GameNotFoundModal'; +import { TextModal } from './TextModal'; -export { GameNotFoundModal }; +export { TextModal }; diff --git a/frontend/apps/galactic-express/src/features/session/components/session/Session.module.scss b/frontend/apps/galactic-express/src/features/session/components/session/Session.module.scss index f9f446d32..c2e40b286 100644 --- a/frontend/apps/galactic-express/src/features/session/components/session/Session.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/session/Session.module.scss @@ -95,3 +95,18 @@ } } } + +.courtain { + position: absolute; + width: 50%; + height: 100%; + right: 0; +} + +.courtainGreen { + background: radial-gradient(circle, rgba(2,0,36,0) 0%, rgba(111,207,151,0.3) 0%, rgba(0,0,0,0) 75%); +} + +.courtainRed { + background: radial-gradient(circle, rgba(2,0,36,0) 0%, rgba(235,87,87,0.3) 0%, rgba(0,0,0,0) 75%); +} \ No newline at end of file diff --git a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx index 277df6217..c2b2bf1f7 100644 --- a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx @@ -11,6 +11,7 @@ import { Traits } from '../traits'; import { Radar } from '../radar'; import { Table } from '../table'; import styles from './Session.module.scss'; +import clsx from 'clsx'; type Props = { session: SessionType; @@ -97,9 +98,11 @@ function Session({ session, turns, rankings, userId, participants, admin }: Prop )); const sortRanks = () => { - const sortedRanks = rankings.sort((rankA, rankB) => - Number(withoutCommas(rankA[1])) < Number(withoutCommas(rankB[1])) ? 1 : -1, - ); + const isAllZeros = rankings.every((rank) => rank[1] === '0'); + + const sortedRanks = isAllZeros + ? [] + : rankings.sort((rankA, rankB) => (Number(withoutCommas(rankA[1])) < Number(withoutCommas(rankB[1])) ? 1 : -1)); return sortedRanks; }; @@ -119,6 +122,8 @@ function Session({ session, turns, rankings, userId, participants, admin }: Prop }; }; + const definedWinners = defineWinners(); + return (
      @@ -163,11 +168,19 @@ function Session({ session, turns, rankings, userId, participants, admin }: Prop currentEvents={getEvents()} currentRound={roundIndex} roundsCount={roundsCount} - isWinner={defineWinners().isUserWinner} - winners={defineWinners().winners} - userRank={defineWinners().userRank} + isWinner={definedWinners.isUserWinner} + winners={definedWinners.winners} + userRank={definedWinners.userRank} admin={admin} /> +
      item[0]).includes(userId || '0x') + ? styles.courtainGreen + : styles.courtainRed, + )} + />
      ); } diff --git a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx index b4d853a82..5c174cab9 100644 --- a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from 'react'; import clsx from 'clsx'; import { HexString, UserMessageSent, encodeAddress } from '@gear-js/api'; import { Button } from '@gear-js/ui'; -import { useSetAtom } from 'jotai'; -import { CURRENT_GAME_ATOM } from 'atoms'; +import { useAtom, useSetAtom } from 'jotai'; +import { CURRENT_GAME_ATOM, REGISTRATION_STATUS } from 'atoms'; import { ADDRESS } from 'consts'; import { Bytes } from '@polkadot/types'; import { useAccount, useApi } from '@gear-js/react-hooks'; @@ -31,14 +31,20 @@ type Props = { bid: string | undefined; }; +type DecodedReplyOk = { + playerId: string; +}; + type DecodedReply = { Err: string; + Ok: Record & 'GameCanceled'; }; function Start({ participants, session, isUserAdmin, userAddress, adminAddress, bid, adminName }: Props) { const { api } = useApi(); const { account } = useAccount(); const { decodedAddress } = account || {}; + const [registrationStatus, setRegistrationStatus] = useAtom(REGISTRATION_STATUS); const setCurrentGame = useSetAtom(CURRENT_GAME_ATOM); const { altitude, weather, reward, sessionId } = session; const playersCount = participants?.length ? participants.length + 1 : 1; @@ -46,10 +52,6 @@ function Start({ participants, session, isUserAdmin, userAddress, adminAddress, const containerClassName = clsx(styles.container, decodedAddress ? styles.smallMargin : styles.largeMargin); - const [registrationStatus, setRegistrationStatus] = useState< - 'registration' | 'success' | 'error' | 'NotEnoughParticipants' | 'MaximumPlayersReached' - >('registration'); - const meta = useEscrowMetadata(); const getDecodedPayload = (payload: Bytes) => { if (meta?.types.handle.output) { @@ -75,12 +77,27 @@ function Start({ participants, session, isUserAdmin, userAddress, adminAddress, if (isOwner && isEscrowProgram) { const reply = getDecodedReply(payload); - // console.log(reply); + if (reply?.Err) { if (reply.Err === 'NotEnoughParticipants' || reply.Err === 'MaximumPlayersReached') { setRegistrationStatus(reply.Err); - } else { - setRegistrationStatus('error'); + return; + } + + setRegistrationStatus('error'); + } + } + + if (destination.toHex() === adminAddress) { + const reply = getDecodedReply(payload); + + if (reply.Ok) { + if (reply.Ok.PlayerDeleted?.playerId === account?.decodedAddress) { + setRegistrationStatus('PlayerRemoved'); + } + + if (reply.Ok === 'GameCanceled' && !isUserAdmin) { + setRegistrationStatus('GameCanceled'); } } } @@ -110,7 +127,7 @@ function Start({ participants, session, isUserAdmin, userAddress, adminAddress,
      -

      Session #{sessionId}

      +

      Session

      Registration

      @@ -167,8 +184,8 @@ function Start({ participants, session, isUserAdmin, userAddress, adminAddress,
      - - + {isRegistered && !isUserAdmin && } + earth
      ); diff --git a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.module.scss b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.module.scss index cebce6d6e..9e3c68b33 100644 --- a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.module.scss +++ b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.module.scss @@ -11,14 +11,7 @@ justify-content: center; align-items: center; z-index: 1; -} - -.win { - border: 1px solid #2bd07180; -} - -.lose { - background: #0a0a0fcc; + background: #0A0A0FCC; } .title { diff --git a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx index 58ac9c26c..ff232cc3d 100644 --- a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx @@ -1,11 +1,12 @@ -import { useAtomValue } from 'jotai'; +import { useSetAtom } from 'jotai'; import { cx } from 'utils'; import { useAccount } from '@gear-js/react-hooks'; import { Button } from '@gear-js/ui'; import { useLaunchMessage } from 'features/session/hooks'; import { shortenString } from 'features/session/utils'; -import { Rank, RankWithName } from 'features/session/types'; +import { RankWithName } from 'features/session/types'; import styles from './WinStatus.module.scss'; +import { REGISTRATION_STATUS } from 'atoms'; type Props = { type: 'win' | 'lose'; @@ -16,19 +17,24 @@ type Props = { function WinStatus({ type, userRank, winners, admin }: Props) { const { meta, message: sendNewSessionMessage } = useLaunchMessage(); + const setRegistrationStatus = useSetAtom(REGISTRATION_STATUS); const { account } = useAccount(); const isAdmin = admin === account?.decodedAddress; + const onInBlock = () => { + setRegistrationStatus('registration'); + }; + const handleCreateNewSession = () => { if (!meta) { return; } if (isAdmin) { - sendNewSessionMessage({ payload: { CancelGame: null } }); + sendNewSessionMessage({ payload: { CancelGame: null }, onInBlock }); } else { - sendNewSessionMessage({ payload: { LeaveGame: null } }); + sendNewSessionMessage({ payload: { LeaveGame: null }, onInBlock }); } }; diff --git a/frontend/apps/galactic-express/src/features/session/types.ts b/frontend/apps/galactic-express/src/features/session/types.ts index 9374be3c6..b5117b38b 100644 --- a/frontend/apps/galactic-express/src/features/session/types.ts +++ b/frontend/apps/galactic-express/src/features/session/types.ts @@ -35,21 +35,23 @@ type Rank = [HexString, string]; type RankWithName = [`0x${string}`, string, string]; -type LaunchState = { - Game: { - admin: HexString; - stage: { - Registration: Participant[]; - Results: Results; - }; - master: string; - altitude: string; - weather: string; - reward: string; - sessionId: string; - bid: string; - adminName: string; +type State = { + admin: HexString; + stage: { + Registration: Participant[]; + Results: Results; }; + master: string; + altitude: string; + weather: string; + reward: string; + sessionId: string; + bid: string; + adminName: string; +}; + +type LaunchState = { + Game: State; }; type TurnParticipant = [ @@ -72,8 +74,18 @@ type PlayerInfo = { PlayerInfo: PlayerStatus; }; +type RegistrationStatus = + | 'registration' + | 'success' + | 'error' + | 'NotEnoughParticipants' + | 'MaximumPlayersReached' + | 'PlayerRemoved' + | 'GameCanceled'; + export type { LaunchState, + State, Event, Participant, Turns, @@ -83,4 +95,5 @@ export type { PlayerStatus, PlayerInfo, RankWithName, + RegistrationStatus, }; diff --git a/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx b/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx index 4255da1f8..d151c19bf 100644 --- a/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx +++ b/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx @@ -1,11 +1,11 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { WalletNew as Wallet } from '@dapps-frontend/ui'; import { Button } from '@gear-js/vara-ui'; import { cx } from 'utils'; import { ReactComponent as VaraSVG } from 'assets/images/icons/vara-coin.svg'; import { ReactComponent as TVaraSVG } from 'assets/images/icons/tvara-coin.svg'; import { useSetAtom, useAtom } from 'jotai'; -import { CURRENT_GAME_ATOM, IS_LOADING, PLAYER_NAME_ATOM } from 'atoms'; +import { CURRENT_GAME_ATOM, IS_LOADING, PLAYER_NAME_ATOM, REGISTRATION_STATUS } from 'atoms'; import { useLaunchMessage } from 'features/session/hooks'; import metaTxt from 'assets/meta/galactic_express_meta.txt'; import { useAccount, useAccountDeriveBalancesAll, useApi, useBalanceFormat, withoutCommas } from '@gear-js/react-hooks'; @@ -17,7 +17,7 @@ import { ADDRESS } from 'consts'; import { useProgramMetadata } from 'hooks'; import { LaunchState, Participant } from 'features/session/types'; import { JoinModalFormValues } from 'features/session/components/game-found-modal/GameFoundModal'; -import { GameNotFoundModal } from 'features/session/components/game-not-found-modal'; +import { TextModal } from 'features/session/components/game-not-found-modal'; import { GameIntro } from '../game-intro'; import styles from './RequestGame.module.scss'; @@ -25,13 +25,6 @@ export interface ContractFormValues { [key: string]: string; } -type Props = { - doesSessionExist: boolean; - isUserAdmin: boolean; - isStateComing: boolean; - participants: Participant[]; -}; - type Status = 'creating' | 'joining' | null; type CreateFormValues = { @@ -56,6 +49,7 @@ function RequestGame() { const meta = useProgramMetadata(metaTxt); const setCurrentGame = useSetAtom(CURRENT_GAME_ATOM); const setPlayerName = useSetAtom(PLAYER_NAME_ATOM); + const setRegistrationStatus = useSetAtom(REGISTRATION_STATUS); const [status, setStatus] = useState(null); const [isLoading, setIsLoading] = useAtom(IS_LOADING); const existentialDeposit = Number(getFormattedBalanceValue(api?.existentialDeposit.toNumber() || 0).toFixed()); @@ -168,6 +162,10 @@ function RequestGame() { setGameNotFoundModal(false); }; + useEffect(() => { + setRegistrationStatus('registration'); + }, []); + return (
      @@ -266,7 +264,13 @@ function RequestGame() { onClose={handleCloseFoundModal} /> )} - {gameNotFoundModal && } + {gameNotFoundModal && ( + + )}
      ); } diff --git a/frontend/apps/galactic-express/src/pages/home/Home.tsx b/frontend/apps/galactic-express/src/pages/home/Home.tsx index 0abe1ff44..5734a1281 100644 --- a/frontend/apps/galactic-express/src/pages/home/Home.tsx +++ b/frontend/apps/galactic-express/src/pages/home/Home.tsx @@ -1,10 +1,17 @@ +import { useEffect, useState } from 'react'; +import { useAtom } from 'jotai'; +import { REGISTRATION_STATUS } from 'atoms'; import { useAccount } from '@gear-js/react-hooks'; import { Start, Session, useLaunchState } from 'features/session'; import { Welcome } from 'features/welcome/components/welcome'; import { RequestGame } from 'features/welcome/components/enter-contract-address'; import { SessionPassedInfo } from 'features/session/components/session-passed-info'; +import { TextModal } from 'features/session/components/game-not-found-modal'; function Home() { + const [registrationStatus, setRegistrationStatus] = useAtom(REGISTRATION_STATUS); + const [isPlayerRemovedModalOpen, setIsPlayerRemovedModalOpen] = useState(false); + const [isGameCancelledModalOpen, setIsGameCancelledModalOpen] = useState(false); const { account } = useAccount(); const state = useLaunchState(); const { admin, stage, sessionId, altitude, weather, reward, bid, adminName } = state || {}; @@ -17,60 +24,84 @@ function Home() { const isUserAdmin = admin === account?.decodedAddress; + useEffect(() => { + if (registrationStatus === 'PlayerRemoved') { + setIsPlayerRemovedModalOpen(true); + } + + if (registrationStatus === 'GameCanceled') { + setIsGameCancelledModalOpen(true); + } + }, [registrationStatus]); + + const handleCloseModal = () => { + setIsPlayerRemovedModalOpen(false); + setIsGameCancelledModalOpen(false); + setRegistrationStatus('registration'); + }; + return ( <> - {!state && !isSessionEnded && ( + {!state && ( )} {!!state && ( <> - {true ? ( + {!isSessionEnded && ( + + )} + {isSessionEnded && ( <> - {!isSessionEnded && ( - item[0]).includes(account?.decodedAddress || '0x') ? ( + - )} - {isSessionEnded && ( - <> - {rankings?.map((item) => item[0]).includes(account?.decodedAddress || '0x') ? ( - - ) : ( - - )} - + ) : ( + )} - ) : ( -

      Waiting for session to start...

      )} )} + {isPlayerRemovedModalOpen && ( + + )} + {isGameCancelledModalOpen && ( + + )} ); } From d8b441e973bdd64efb3844019f61ad930d2192ca Mon Sep 17 00:00:00 2001 From: Yauheni Draichykau Date: Tue, 20 Feb 2024 16:59:39 +0100 Subject: [PATCH 07/17] Galactic-express: Fixes eslint errors --- .../galactic-express/src/components/layout/header/Header.tsx | 2 +- .../components/cancel-game-button/CancelGameButton.tsx | 4 ++-- .../src/features/session/components/session/Session.tsx | 2 +- .../src/features/session/components/start/Start.tsx | 2 ++ .../src/features/session/components/win-status/WinStatus.tsx | 2 +- .../welcome/components/enter-contract-address/RequestGame.tsx | 2 ++ 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx index 8a4beb241..7df3e4ae7 100644 --- a/frontend/apps/galactic-express/src/components/layout/header/Header.tsx +++ b/frontend/apps/galactic-express/src/components/layout/header/Header.tsx @@ -3,10 +3,10 @@ import { Header as CommonHeader, MenuHandler } from '@dapps-frontend/ui'; import { ReactComponent as GalexSVG } from 'assets/images/logo.svg'; import { ReactComponent as VaraSVG } from 'assets/images/logo-vara.svg'; import { cx } from 'utils'; -import styles from './Header.module.scss'; import { useLaunchState } from 'features/session'; import { useAccount } from '@gear-js/react-hooks'; import { CancelGameButton } from 'features/session/components/cancel-game-button'; +import styles from './Header.module.scss'; function Header() { const { account } = useAccount(); diff --git a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx index c53967f67..998137c82 100644 --- a/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/cancel-game-button/CancelGameButton.tsx @@ -1,12 +1,12 @@ import { ReactComponent as CrossIconSVG } from 'assets/images/icons/cross-icon.svg'; -import { useAtom, useAtomValue, useSetAtom } from 'jotai'; +import { useAtom, useSetAtom } from 'jotai'; import { Button } from '@gear-js/vara-ui'; import { useAccount } from '@gear-js/react-hooks'; import { useLaunchMessage } from 'features/session/hooks'; import { Participant } from 'features/session/types'; import { IS_LOADING, REGISTRATION_STATUS } from 'atoms'; -import styles from './CancelGameButton.module.scss'; import clsx from 'clsx'; +import styles from './CancelGameButton.module.scss'; type Props = { isAdmin: boolean; diff --git a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx index c2b2bf1f7..9cd82a187 100644 --- a/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/session/Session.tsx @@ -1,5 +1,6 @@ import { Button } from '@gear-js/ui'; import { CSSProperties, useState } from 'react'; +import clsx from 'clsx'; import { withoutCommas } from '@gear-js/react-hooks'; import { HexString } from '@gear-js/api'; import { Container } from 'components'; @@ -11,7 +12,6 @@ import { Traits } from '../traits'; import { Radar } from '../radar'; import { Table } from '../table'; import styles from './Session.module.scss'; -import clsx from 'clsx'; type Props = { session: SessionType; diff --git a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx index 5c174cab9..62f7b8d77 100644 --- a/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/start/Start.tsx @@ -121,6 +121,8 @@ function Start({ participants, session, isUserAdmin, userAddress, adminAddress, if (registrationStatus === 'NotEnoughParticipants' && participants.length) { setRegistrationStatus('registration'); } + + // eslint-disable-next-line react-hooks/exhaustive-deps }, [participants, registrationStatus]); return ( diff --git a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx index ff232cc3d..06b4570b4 100644 --- a/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx +++ b/frontend/apps/galactic-express/src/features/session/components/win-status/WinStatus.tsx @@ -1,12 +1,12 @@ import { useSetAtom } from 'jotai'; import { cx } from 'utils'; +import { REGISTRATION_STATUS } from 'atoms'; import { useAccount } from '@gear-js/react-hooks'; import { Button } from '@gear-js/ui'; import { useLaunchMessage } from 'features/session/hooks'; import { shortenString } from 'features/session/utils'; import { RankWithName } from 'features/session/types'; import styles from './WinStatus.module.scss'; -import { REGISTRATION_STATUS } from 'atoms'; type Props = { type: 'win' | 'lose'; diff --git a/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx b/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx index d151c19bf..4aa610aa7 100644 --- a/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx +++ b/frontend/apps/galactic-express/src/features/welcome/components/enter-contract-address/RequestGame.tsx @@ -164,6 +164,8 @@ function RequestGame() { useEffect(() => { setRegistrationStatus('registration'); + + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( From 4ce49f4381b94ef5f6e47a9fd978818c538d7cda Mon Sep 17 00:00:00 2001 From: MedovTimur <62596970+MedovTimur@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:49:23 +0300 Subject: [PATCH 08/17] Update galactic express (#231) --- contracts/galactic-express/.DS_Store | Bin 6148 -> 6148 bytes contracts/galactic-express/io/src/lib.rs | 84 +++- contracts/galactic-express/src/lib.rs | 371 +++++++++++++----- contracts/galactic-express/tests/test.rs | 203 +++++----- .../galactic-express/tests/utils/common.rs | 15 +- contracts/galactic-express/tests/utils/mod.rs | 96 +++-- .../galactic-express/tests/utils/prelude.rs | 7 +- 7 files changed, 522 insertions(+), 254 deletions(-) diff --git a/contracts/galactic-express/.DS_Store b/contracts/galactic-express/.DS_Store index 9e29d53a625e4c88804ac773c90c3fd868ebc321..866c1fe2fe9b6b945e536944ed51fbb369e7c75a 100644 GIT binary patch delta 197 zcmZoMXfc@JFUZEgz`)4BAi%(o!l1`c#8A93VKt*DNR)#iiJ_DslOYEtGg*O2UX+W$ znIRFVE}bDCsJ;Y9<}&0=4q%d3WCE(q_sq#pPRhwo0_q1E$=D5~_5Ona1HZII rhGL-ZWHfa@k<|g&tU!}ffhtQFicwWErEl(Hu4S3n(6E`E<1aq|^F1-9 delta 364 zcmZoMXfc@JFUZQkz`)4BAi%&-#E{64%uup1VKt*DNR$J}%V)@AD1pgLR$!8^=U_+! z@*(1>K-m<}oc!dZoctu94xk|n>w#GBKNtX6AXCuPCzTf$K-KR^D#*z!E-^5;#>m9X z!pg?Z!Op?W5gVM5UmjeNSW;T-lvorE;)Uer=OlsI^*~NyQdnkcdAxv#bADb)VrE`y z5m-lNN-9uIOn7EqN`ARheraAxF<5gjM1q5pgEL-0qPp71%s@xM*u; + type State = InOut; } -pub const PARTICIPANTS: usize = 4; +pub const MAX_PARTICIPANTS: usize = 4; pub const TURNS: usize = 3; /// Represents a range of the minimum & the maximum reward for a session. @@ -31,26 +31,37 @@ pub const MAX_FUEL: u8 = 100; // maximum payload value that can be entered by the user pub const MAX_PAYLOAD: u8 = 100; +#[derive(Encode, Decode, TypeInfo)] +pub enum StateQuery { + All, + GetGame { player_id: ActorId }, +} + +#[derive(Encode, Decode, TypeInfo)] +pub enum StateReply { + All(State), + Game(Option), +} + #[derive(Encode, Decode, TypeInfo, Debug)] pub struct State { - pub admin: ActorId, - pub session: Session, - pub is_session_ended: bool, - pub participants: Vec<(ActorId, Participant)>, - pub turns: Vec>, - pub rankings: Vec<(ActorId, u128)>, + pub games: Vec<(ActorId, GameState)>, + pub player_to_game_id: Vec<(ActorId, ActorId)>, } -#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq)] -pub struct Session { - pub session_id: u128, +#[derive(Encode, Decode, TypeInfo, Debug)] +pub struct GameState { + pub admin: ActorId, + pub admin_name: String, pub altitude: u16, pub weather: Weather, pub reward: u128, + pub stage: StageState, + pub bid: u128, } #[derive(Encode, Decode, TypeInfo, Debug)] -pub enum Stage { +pub enum StageState { Registration(Vec<(ActorId, Participant)>), Results(Results), } @@ -59,26 +70,53 @@ pub enum Stage { pub struct Results { pub turns: Vec>, pub rankings: Vec<(ActorId, u128)>, + pub participants: Vec<(ActorId, Participant)>, } #[derive(Encode, Decode, TypeInfo)] pub enum Action { - ChangeAdmin(ActorId), - CreateNewSession, - Register(Participant), - StartGame(Participant), + CreateNewSession { + name: String, + }, + Register { + creator: ActorId, + participant: Participant, + }, + CancelRegistration, + DeletePlayer { + player_id: ActorId, + }, + CancelGame, + LeaveGame, + StartGame { + fuel_amount: u8, + payload_amount: u8, + }, } #[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq)] pub enum Event { - GameFinished(Results), AdminChanged(ActorId, ActorId), - NewSession(Session), + NewSessionCreated { + altitude: u16, + weather: Weather, + reward: u128, + bid: u128, + }, Registered(ActorId, Participant), + RegistrationCanceled, + PlayerDeleted { + player_id: ActorId, + }, + GameCanceled, + GameFinished(Results), + GameLeft, } -#[derive(Encode, Decode, TypeInfo, Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Encode, Decode, TypeInfo, Clone, Debug, Default, PartialEq, Eq)] pub struct Participant { + pub id: ActorId, + pub name: String, pub fuel_amount: u8, pub payload_amount: u8, } @@ -124,12 +162,18 @@ pub enum Weather { pub enum Error { StateUninitaliazed, GstdError(String), - AccessDenied, SessionEnded, FuelOrPayloadOverload, SessionFull, NotEnoughParticipants, TxManager(TransactionManagerError), + NoSuchGame, + WrongBid, + NoSuchPlayer, + Unregistered, + AlreadyRegistered, + SeveralRegistrations, + NotForAdmin, } impl From for Error { diff --git a/contracts/galactic-express/src/lib.rs b/contracts/galactic-express/src/lib.rs index 718f579b9..a5485997b 100644 --- a/contracts/galactic-express/src/lib.rs +++ b/contracts/galactic-express/src/lib.rs @@ -5,7 +5,7 @@ use gear_lib::tx_manager::TransactionManager; use gstd::{ collections::HashMap, errors::Error as GstdError, - exec, iter, msg, + exec, msg, ops::{Add, Rem, Sub}, prelude::*, ActorId, @@ -82,8 +82,15 @@ impl Default for Stage { #[derive(Default)] struct Contract { + games: HashMap, + player_to_game_id: HashMap, +} + +#[derive(Default)] +pub struct Game { admin: ActorId, - session_id: u128, + admin_name: String, + bid: u128, altitude: u16, weather: Weather, reward: u128, @@ -91,16 +98,25 @@ struct Contract { } impl Contract { - fn check_admin(&self) -> Result<(), Error> { - check_admin(self.admin) - } + fn create_new_session(&mut self, name: String) -> Result { + let msg_src = msg::source(); + let msg_value = msg::value(); + + if self.player_to_game_id.contains_key(&msg_src) { + return Err(Error::SeveralRegistrations); + } - fn create_new_session(&mut self) -> Result { - let stage = &mut self.stage; + let game = self.games.entry(msg_src).or_insert_with(|| Game { + admin: msg_src, + admin_name: name, + bid: msg_value, + ..Default::default() + }); + + let stage = &mut game.stage; match stage { Stage::Registration(participants) => { - check_admin(self.admin)?; participants.clear(); } Stage::Results { .. } => *stage = Stage::Registration(HashMap::new()), @@ -108,7 +124,7 @@ impl Contract { let mut random = Random::new()?; - self.weather = match random.next() % (Weather::Tornado as u8 + 1) { + game.weather = match random.next() % (Weather::Tornado as u8 + 1) { 0 => Weather::Clear, 1 => Weather::Cloudy, 2 => Weather::Rainy, @@ -117,57 +133,159 @@ impl Contract { 5 => Weather::Tornado, _ => unreachable!(), }; - self.altitude = random.generate(TURN_ALTITUDE.0, TURN_ALTITUDE.1) * TURNS as u16; - self.reward = random.generate(REWARD.0, REWARD.1); - - Ok(Event::NewSession(Session { - session_id: self.session_id, - altitude: self.altitude, - weather: self.weather, - reward: self.reward, - })) + game.altitude = random.generate(TURN_ALTITUDE.0, TURN_ALTITUDE.1) * TURNS as u16; + game.reward = random.generate(REWARD.0, REWARD.1); + self.player_to_game_id.insert(msg_src, msg_src); + + Ok(Event::NewSessionCreated { + altitude: game.altitude, + weather: game.weather, + reward: game.reward, + bid: msg_value, + }) } - fn register(&mut self, participant: Participant) -> Result { - let msg_source = msg::source(); + fn cancel_game(&mut self) -> Result { + let msg_src = msg::source(); + let game = self.games.get(&msg_src).ok_or(Error::NoSuchGame)?; - if msg_source == self.admin { - return Err(Error::AccessDenied); + match &game.stage { + Stage::Registration(players) => { + players.iter().for_each(|(id, _)| { + send_value(*id, game.bid); + self.player_to_game_id.remove(id); + }); + } + Stage::Results(results) => { + results.rankings.iter().for_each(|(id, _)| { + self.player_to_game_id.remove(id); + }); + } } - if let Stage::Results(_) = self.stage { - return Err(Error::SessionEnded); + + self.player_to_game_id.remove(&msg_src); + self.games.remove(&msg_src); + Ok(Event::GameCanceled) + } + + fn leave_game(&mut self) -> Result { + let msg_src = msg::source(); + self.player_to_game_id.remove(&msg_src); + Ok(Event::GameLeft) + } + + fn register( + &mut self, + creator: ActorId, + participant: Participant, + msg_source: ActorId, + msg_value: u128, + ) -> Result { + if self.player_to_game_id.contains_key(&msg_source) { + return Err(Error::SeveralRegistrations); + } + + if let Some(game) = self.games.get_mut(&creator) { + if msg_value != game.bid { + return Err(Error::WrongBid); + } + if let Stage::Results(_) = game.stage { + return Err(Error::SessionEnded); + } + + let participants = game.stage.mut_participants()?; + + if participants.contains_key(&msg_source) { + return Err(Error::AlreadyRegistered); + } + + if participants.len() >= MAX_PARTICIPANTS - 1 { + return Err(Error::SessionFull); + } + + participant.check()?; + participants.insert(msg_source, participant.clone()); + self.player_to_game_id.insert(msg_source, creator); + + Ok(Event::Registered(msg_source, participant)) + } else { + Err(Error::NoSuchGame) } + } - let participants = self.stage.mut_participants()?; + fn cancel_register(&mut self) -> Result { + let msg_source = msg::source(); - if participants.len() >= PARTICIPANTS - 1 { - return Err(Error::SessionFull); + let creator = self + .player_to_game_id + .get(&msg_source) + .ok_or(Error::Unregistered)?; + let game = self.games.get_mut(creator).ok_or(Error::NoSuchGame)?; + + if msg_source != game.admin { + let participants = game.stage.mut_participants()?; + if participants.contains_key(&msg_source) { + send_value(msg_source, game.bid); + participants.remove(&msg_source).expect("Critical error"); + self.player_to_game_id.remove(&msg_source); + } else { + return Err(Error::NoSuchPlayer); + } + Ok(Event::RegistrationCanceled) + } else { + Err(Error::NotForAdmin) } + } + fn delete_player(&mut self, player_id: ActorId) -> Result { + let msg_source = msg::source(); - participant.check()?; - participants.insert(msg_source, participant); + if let Some(game) = self.games.get_mut(&msg_source) { + if let Stage::Results(_) = game.stage { + return Err(Error::SessionEnded); + } + + let participants = game.stage.mut_participants()?; - Ok(Event::Registered(msg_source, participant)) + if participants.contains_key(&player_id) { + send_value(player_id, game.bid); + participants.remove(&player_id).expect("Critical error"); + self.player_to_game_id.remove(&player_id); + } else { + return Err(Error::NoSuchPlayer); + } + + Ok(Event::PlayerDeleted { player_id }) + } else { + Err(Error::NoSuchGame) + } } - async fn start_game(&mut self, mut participant: Participant) -> Result { - self.check_admin()?; + async fn start_game(&mut self, fuel_amount: u8, payload_amount: u8) -> Result { + let msg_source = msg::source(); + + let game = self.games.get_mut(&msg_source).ok_or(Error::NoSuchGame)?; + + if fuel_amount > MAX_FUEL || payload_amount > MAX_PAYLOAD { + return Err(Error::FuelOrPayloadOverload); + } + let participant = Participant { + id: msg_source, + name: game.admin_name.clone(), + fuel_amount, + payload_amount, + }; - let participants = self.stage.mut_participants()?; + let participants = game.stage.mut_participants()?; if participants.is_empty() { return Err(Error::NotEnoughParticipants); } - - participant.check()?; + participants.insert(msg_source, participant); let mut random = Random::new()?; let mut turns = HashMap::new(); - for (actor, participant) in participants - .into_iter() - .chain(iter::once((&msg::source(), &mut participant))) - { + for (actor, participant) in participants.into_iter() { let mut actor_turns = Vec::with_capacity(TURNS); let mut remaining_fuel = participant.fuel_amount; @@ -176,7 +294,7 @@ impl Contract { turn_index, remaining_fuel, &mut random, - self.weather, + game.weather, participant.payload_amount, ) { Ok(fuel_left) => { @@ -209,7 +327,7 @@ impl Contract { Turn::Alive { fuel_left, payload_amount, - } => (*payload_amount as u128 + *fuel_left as u128) * self.altitude as u128, + } => (*payload_amount as u128 + *fuel_left as u128) * game.altitude as u128, Turn::Destroyed(_) => 0, }, ) @@ -229,26 +347,40 @@ impl Contract { } } + let max_value = scores.iter().map(|(_, value)| value).max().unwrap(); + let winners: Vec<_> = scores + .iter() + .filter_map(|(actor_id, value)| { + if value == max_value { + Some(*actor_id) + } else { + None + } + }) + .collect(); + let prize = game.bid * scores.len() as u128 / winners.len() as u128; + + if game.bid != 0 { + winners.iter().for_each(|id| { + send_value(*id, prize); + }); + } + let participants = participants + .iter() + .map(|(id, participant)| (*id, participant.clone())) + .collect(); + let results = Results { turns: io_turns, - rankings: scores, + rankings: scores.clone(), + participants, }; - - self.session_id = self.session_id.wrapping_add(1); - self.stage = Stage::Results(results.clone()); + game.stage = Stage::Results(results.clone()); Ok(Event::GameFinished(results)) } } -fn check_admin(admin: ActorId) -> Result<(), Error> { - if msg::source() != admin { - Err(Error::AccessDenied) - } else { - Ok(()) - } -} - fn turn( turn: usize, remaining_fuel: u8, @@ -294,6 +426,12 @@ fn turn( Ok(new_remaining_fuel) } +fn send_value(destination: ActorId, value: u128) { + if value != 0 { + msg::send_with_gas(destination, "", 0, value).expect("Error in sending value"); + } +} + #[no_mangle] extern fn init() { msg::reply(process_init(), 0).expect("failed to encode or reply from `main()`"); @@ -303,7 +441,6 @@ fn process_init() -> Result<(), Error> { unsafe { STATE = Some(( Contract { - admin: msg::source(), ..Default::default() }, TransactionManager::new(), @@ -323,71 +460,101 @@ async fn process_main() -> Result { let (contract, _tx_manager) = state_mut()?; match action { - Action::ChangeAdmin(actor) => { - contract.check_admin()?; - - let old_admin = contract.admin; - - contract.admin = actor; - - Ok(Event::AdminChanged(old_admin, contract.admin)) + Action::CreateNewSession { name } => contract.create_new_session(name), + Action::Register { + creator, + participant, + } => { + let msg_source = msg::source(); + let msg_value = msg::value(); + let reply = contract.register(creator, participant, msg_source, msg_value); + if reply.is_err() { + send_value(msg_source, msg_value); + } + reply } - Action::CreateNewSession => contract.create_new_session(), - Action::Register(participant) => contract.register(participant), - Action::StartGame(participant) => contract.start_game(participant).await, + Action::CancelRegistration => contract.cancel_register(), + Action::DeletePlayer { player_id } => contract.delete_player(player_id), + Action::CancelGame => contract.cancel_game(), + Action::LeaveGame => contract.leave_game(), + Action::StartGame { + fuel_amount, + payload_amount, + } => contract.start_game(fuel_amount, payload_amount).await, } } #[no_mangle] extern fn state() { let (state, _tx_manager) = unsafe { STATE.take().expect("Unexpected error in taking state") }; - msg::reply::(state.into(), 0) - .expect("Failed to encode or reply with `State` from `state()`"); + let query: StateQuery = msg::load().expect("Unable to load the state query"); + let reply = match query { + StateQuery::All => StateReply::All(state.into()), + StateQuery::GetGame { player_id } => { + let game_state = state + .player_to_game_id + .get(&player_id) + .and_then(|creator_id| state.games.get(creator_id)) + .map(|game| { + let stage = match &game.stage { + Stage::Registration(participants_data) => StageState::Registration( + participants_data.clone().into_iter().collect(), + ), + Stage::Results(results) => StageState::Results(results.clone()), + }; + + GameState { + admin: game.admin, + admin_name: game.admin_name.clone(), + altitude: game.altitude, + weather: game.weather, + reward: game.reward, + stage, + bid: game.bid, + } + }); + + StateReply::Game(game_state) + } + }; + msg::reply(reply, 0).expect("Unable to share the state"); } impl From for State { fn from(value: Contract) -> Self { let Contract { - admin, - session_id, - altitude, - weather, - reward, - stage, + games, + player_to_game_id, } = value; - let is_session_ended: bool; - let participants: Vec<(ActorId, Participant)>; - let turns: Vec>; - let rankings: Vec<(ActorId, u128)>; + let games = games + .into_iter() + .map(|(id, game)| { + let stage = match game.stage { + Stage::Registration(participants_data) => { + StageState::Registration(participants_data.into_iter().collect()) + } + Stage::Results(results) => StageState::Results(results), + }; + + let game_state = GameState { + admin: game.admin, + admin_name: game.admin_name.clone(), + altitude: game.altitude, + weather: game.weather, + reward: game.reward, + stage, + bid: game.bid, + }; + (id, game_state) + }) + .collect(); - match stage { - Stage::Registration(participants_data) => { - is_session_ended = false; - participants = participants_data.into_iter().collect(); - turns = vec![vec![]]; - rankings = Vec::new(); - } - Stage::Results(results) => { - is_session_ended = true; - participants = Vec::new(); - turns = results.turns; - rankings = results.rankings; - } - }; + let player_to_game_id = player_to_game_id.into_iter().collect(); Self { - admin, - session: Session { - session_id, - altitude, - weather, - reward, - }, - is_session_ended, - participants, - turns, - rankings, + games, + player_to_game_id, } } } diff --git a/contracts/galactic-express/tests/test.rs b/contracts/galactic-express/tests/test.rs index c7f38d0ef..3df67eeec 100644 --- a/contracts/galactic-express/tests/test.rs +++ b/contracts/galactic-express/tests/test.rs @@ -5,123 +5,138 @@ mod utils; #[test] fn test() { let system = utils::initialize_system(); + let mut rockets = GalEx::initialize(&system, ADMIN); - for admin_id in ADMINS { - let mut rockets = GalEx::initialize(&system, admin_id); - if let State { - admin, - session: Session { session_id: 0, .. }, - is_session_ended: true, - participants, - turns, - rankings, - } = rockets.state() - { - assert_eq!(admin, admin_id.into()); - assert_eq!((participants, turns, rankings), (vec![], vec![], vec![])); - } else { - unreachable!() - } - - for (session_id, starter) in [admin_id, PLAYERS[0]].into_iter().enumerate() { - rockets - .create_new_session(starter) - .succeed(session_id as u128); - - let player = Participant { - fuel_amount: 42, - payload_amount: 20, - }; - - for player_id in PLAYERS { - rockets - .register(player_id, player) - .succeed((player_id, player)); - } - #[allow(irrefutable_let_patterns)] - if let State { participants, .. } = rockets.state() { - assert_eq!( - HashMap::from_iter( - PLAYERS - .into_iter() - .map(|player_id| (player_id.into(), player)) - ), - participants.into_iter().collect::>(), - ); - } else { - unreachable!() - } - - rockets - .start_game(admin_id, player) - .succeed(PLAYERS.into_iter().chain(iter::once(admin_id)).collect()); - } + let bid = 11_000_000_000_000; + system.mint_to(ADMIN, bid); + rockets + .create_new_session(ADMIN, "admin".to_string(), bid) + .succeed(0, 0); + + for player_id in PLAYERS { + let player = Participant { + id: player_id.into(), + name: "player".to_string(), + fuel_amount: 42, + payload_amount: 20, + }; + system.mint_to(player_id, bid); + rockets + .register(player_id, ADMIN.into(), player.clone(), bid) + .succeed((player_id, player), 0); + } + + let state = rockets.state().expect("Unexpected invalid state."); + + if let StageState::Registration(participants) = &state.games[0].1.stage { + assert_eq!(participants.len(), 3); + } + + rockets + .start_game(ADMIN, 42, 20) + .succeed(PLAYERS.into_iter().chain(iter::once(ADMIN)).collect(), 3); // 3 since three players win and msg::send_with_gas is sent to them + + let state = rockets.state().expect("Unexpected invalid state."); + + if let StageState::Results(results) = &state.games[0].1.stage { + assert_eq!(results.rankings.len(), 4); } } +#[test] +fn cancel_register_and_delete_player() { + let system = utils::initialize_system(); + let mut rockets = GalEx::initialize(&system, ADMIN); + + let bid = 11_000_000_000_000; + system.mint_to(ADMIN, bid); + rockets + .create_new_session(ADMIN, "admin".to_string(), bid) + .succeed(0_u128, 0); + + for player_id in PLAYERS { + let player = Participant { + id: player_id.into(), + name: "player".to_string(), + fuel_amount: 42, + payload_amount: 20, + }; + system.mint_to(player_id, bid); + rockets + .register(player_id, ADMIN.into(), player.clone(), bid) + .succeed((player_id, player), 0); + } + + let state = rockets.state().expect("Unexpected invalid state."); + + if let StageState::Registration(participants) = &state.games[0].1.stage { + assert_eq!(participants.len(), 3); + } + assert_eq!(state.player_to_game_id.len(), 4); + + drop(rockets.cancel_register(PLAYERS[0])); + + let state = rockets.state().expect("Unexpected invalid state."); + + if let StageState::Registration(participants) = &state.games[0].1.stage { + assert_eq!(participants.len(), 2); + } + assert_eq!(state.player_to_game_id.len(), 3); + + drop(rockets.delete_player(ADMIN, PLAYERS[1].into())); + + let state = rockets.state().expect("Unexpected invalid state."); + + if let StageState::Registration(participants) = &state.games[0].1.stage { + assert_eq!(participants.len(), 1); + } + assert_eq!(state.player_to_game_id.len(), 2); +} + #[test] fn errors() { let system = utils::initialize_system(); - let mut rockets = GalEx::initialize(&system, ADMINS[0]); + let mut rockets = GalEx::initialize(&system, ADMIN); rockets - .change_admin(PLAYERS[0], PLAYERS[0]) - .failed(Error::AccessDenied); - rockets - .change_admin(ADMINS[0], ADMINS[1]) - .succeed((ADMINS[0], ADMINS[1])); + .register(PLAYERS[0], ADMIN.into(), Default::default(), 0) + .failed(Error::NoSuchGame, 0); rockets - .register(PLAYERS[0], Default::default()) - .failed(Error::SessionEnded); + .create_new_session(ADMIN, "admin".to_string(), 0) + .succeed(0, 0); + rockets - .start_game(PLAYERS[0], Default::default()) - .failed(Error::AccessDenied); + .register(ADMIN, ADMIN.into(), Default::default(), 0) + .failed(Error::SeveralRegistrations, 0); - rockets.create_new_session(ADMINS[1]).succeed(0); + rockets + .start_game(PLAYERS[0], 42, 20) + .failed(Error::NoSuchGame, 0); rockets - .start_game(ADMINS[1], Default::default()) - .failed(Error::NotEnoughParticipants); + .start_game(ADMIN, 42, 20) + .failed(Error::NotEnoughParticipants, 0); for player in PLAYERS { rockets - .register(player, Default::default()) - .succeed((player, Default::default())); + .register(player, ADMIN.into(), Default::default(), 0) + .succeed((player, Default::default()), 0); } rockets - .start_game( - ADMINS[1], - Participant { - fuel_amount: 101, - payload_amount: 100, - }, - ) - .failed(Error::FuelOrPayloadOverload); - rockets - .start_game( - ADMINS[1], - Participant { - fuel_amount: 100, - payload_amount: 101, - }, - ) - .failed(Error::FuelOrPayloadOverload); + .start_game(ADMIN, 101, 100) + .failed(Error::FuelOrPayloadOverload, 0); + rockets - .start_game( - ADMINS[1], - Participant { - fuel_amount: 101, - payload_amount: 101, - }, - ) - .failed(Error::FuelOrPayloadOverload); + .start_game(ADMIN, 100, 101) + .failed(Error::FuelOrPayloadOverload, 0); rockets - .register(ADMINS[1], Default::default()) - .failed(Error::AccessDenied); + .start_game(ADMIN, 101, 101) + .failed(Error::FuelOrPayloadOverload, 0); + rockets - .register(FOREIGN_USER, Default::default()) - .failed(Error::SessionFull); + .register(FOREIGN_USER, ADMIN.into(), Default::default(), 0) + .failed(Error::SessionFull, 0); } diff --git a/contracts/galactic-express/tests/utils/common.rs b/contracts/galactic-express/tests/utils/common.rs index f2877fa8d..92b7ba32a 100644 --- a/contracts/galactic-express/tests/utils/common.rs +++ b/contracts/galactic-express/tests/utils/common.rs @@ -38,16 +38,19 @@ impl>(&self.result).unwrap_err(), + decode::>(&self.result, index).unwrap_err(), error ); } #[track_caller] - pub fn succeed(self, value: Check) -> CheckResult { - (self.check)(decode::>(&self.result).unwrap(), value) + pub fn succeed(self, value: Check, index: usize) -> CheckResult { + (self.check)( + decode::>(&self.result, index).unwrap(), + value, + ) } } @@ -87,8 +90,8 @@ fn assert_contains(result: &InnerRunResult, payload: impl Encode) { assert!(result.contains(&Log::builder().payload(payload))); } -fn decode(result: &InnerRunResult) -> T { - match T::decode(&mut result.log()[0].payload()) { +fn decode(result: &InnerRunResult, index: usize) -> T { + match T::decode(&mut result.log()[index].payload()) { Ok(ok) => ok, Err(_) => std::panic!("{}", String::from_utf8_lossy(result.log()[0].payload())), } diff --git a/contracts/galactic-express/tests/utils/mod.rs b/contracts/galactic-express/tests/utils/mod.rs index fd44d384a..1e75f0262 100644 --- a/contracts/galactic-express/tests/utils/mod.rs +++ b/contracts/galactic-express/tests/utils/mod.rs @@ -10,8 +10,8 @@ pub mod prelude; pub use common::initialize_system; pub const FOREIGN_USER: u64 = 1029384756123; -pub const ADMINS: [u64; 2] = [123, 321]; -pub const PLAYERS: [u64; 3] = [1234, 4321, 2332]; +pub const ADMIN: u64 = 10; +pub const PLAYERS: [u64; 3] = [12, 13, 14]; type GalExResult = RunResult; @@ -33,29 +33,25 @@ impl<'a> GalEx<'a> { InitResult::<_, Error>::new(Self(program), result, is_active).succeed() } - pub fn change_admin( + pub fn create_new_session( &mut self, from: u64, - actor: impl Into, - ) -> GalExResult<(u64, u64)> { + name: String, + bid: u128, + ) -> GalExResult { RunResult::new( - self.0.send(from, Action::ChangeAdmin(actor.into())), - |event, (old, new)| assert_eq!(Event::AdminChanged(old.into(), new.into()), event), - ) - } - - pub fn create_new_session(&mut self, from: u64) -> GalExResult { - RunResult::new( - self.0.send(from, Action::CreateNewSession), - |event, session_id| { - if let Event::NewSession(session) = event { - assert_eq!(session.session_id, session_id); + self.0 + .send_with_value(from, Action::CreateNewSession { name }, bid), + |event, _id| { + if let Event::NewSessionCreated { + altitude, reward, .. + } = event + { assert!(((TURN_ALTITUDE.0 * (TURNS as u16)) ..(TURN_ALTITUDE.1 * (TURNS as u16))) - .contains(&session.altitude)); - assert!((REWARD.0..REWARD.1).contains(&session.reward)); - - session + .contains(&altitude)); + assert!((REWARD.0..REWARD.1).contains(&reward)); + reward } else { unreachable!() } @@ -66,27 +62,65 @@ impl<'a> GalEx<'a> { pub fn register( &mut self, from: u64, + creator: ActorId, participant: Participant, + bid: u128, ) -> GalExResult<(u64, Participant)> { RunResult::new( - self.0.send(from, Action::Register(participant)), + self.0.send_with_value( + from, + Action::Register { + creator, + participant, + }, + bid, + ), |event, (actor, participant)| { assert_eq!(Event::Registered(actor.into(), participant), event) }, ) } - pub fn start_game(&mut self, from: u64, participant: Participant) -> GalExResult> { + pub fn cancel_register(&mut self, from: u64) -> GalExResult<(u64, Participant)> { + RunResult::new( + self.0.send(from, Action::CancelRegistration), + |event, (_actor, _participant)| assert_eq!(Event::RegistrationCanceled, event), + ) + } + + pub fn delete_player( + &mut self, + from: u64, + player_id: ActorId, + ) -> GalExResult<(u64, Participant)> { + RunResult::new( + self.0.send(from, Action::DeletePlayer { player_id }), + |_, _| {}, + ) + } + + pub fn start_game( + &mut self, + from: u64, + fuel_amount: u8, + payload_amount: u8, + ) -> GalExResult> { RunResult::new( - self.0.send(from, Action::StartGame(participant)), + self.0.send( + from, + Action::StartGame { + fuel_amount, + payload_amount, + }, + ), |event, players| { if let Event::GameFinished(results) = event { assert!(results.turns.len() == TURNS); - assert!(results.rankings.len() == PARTICIPANTS); + assert!(results.rankings.len() == MAX_PARTICIPANTS); assert!(results .turns .iter() - .all(|players| players.len() == PARTICIPANTS)); + .all(|players| players.len() == MAX_PARTICIPANTS)); let players: HashSet = players.into_iter().map(|p| p.into()).collect(); @@ -105,7 +139,15 @@ impl<'a> GalEx<'a> { ) } - pub fn state(&self) -> State { - self.0.read_state(0).unwrap() + pub fn state(&self) -> Option { + let reply = self + .0 + .read_state(StateQuery::All) + .expect("Unexpected invalid state."); + if let StateReply::All(state) = reply { + Some(state) + } else { + None + } } } diff --git a/contracts/galactic-express/tests/utils/prelude.rs b/contracts/galactic-express/tests/utils/prelude.rs index 8ba16b037..ed2994b73 100644 --- a/contracts/galactic-express/tests/utils/prelude.rs +++ b/contracts/galactic-express/tests/utils/prelude.rs @@ -1,6 +1,3 @@ -pub use super::{ - common::{initialize_system, Program}, - GalEx, ADMINS, FOREIGN_USER, PLAYERS, -}; +pub use super::{GalEx, ADMIN, FOREIGN_USER, PLAYERS}; pub use galactic_express_io::*; -pub use gstd::{collections::*, prelude::*, ActorId}; +pub use gstd::prelude::*; From 072474815bc0bc24b237590c9f546a7b51650ebc Mon Sep 17 00:00:00 2001 From: YauheniDraichykau <64776571+YauheniDraichykau@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:03:47 +0300 Subject: [PATCH 09/17] Signless: Adds possibility to choose session duration, small refactoring (#262) Co-authored-by: Yauheni --- .../create-session-modal.tsx | 53 +++++++++--------- .../signless-transactions.module.css | 2 +- .../signless-transactions.tsx | 17 ++++-- .../signless-transactions/src/consts.ts | 54 +++++++++++++++++++ .../src/hooks/use-create-session.ts | 4 +- .../signless-transactions/src/utils.ts | 9 ++-- 6 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 frontend/packages/signless-transactions/src/consts.ts diff --git a/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx b/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx index 5cf6fd009..f03815493 100644 --- a/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx +++ b/frontend/packages/signless-transactions/src/components/create-session-modal/create-session-modal.tsx @@ -1,8 +1,8 @@ -import { Button, Input, Modal, ModalProps } from '@gear-js/vara-ui'; +import { Button, Input, Modal, ModalProps, Select } from '@gear-js/vara-ui'; import { useApi, useBalanceFormat } from '@gear-js/react-hooks'; import { GearKeyring, decodeAddress } from '@gear-js/api'; import { KeyringPair, KeyringPair$Json } from '@polkadot/keyring/types'; -import { useEffect, useMemo, useState } from 'react'; +import { ChangeEvent, useEffect, useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useSignlessTransactions } from '../../context'; import { getMilliseconds } from '../../utils'; @@ -10,21 +10,21 @@ import { EnableSessionModal } from '../enable-session-modal'; import styles from './create-session-modal.module.css'; import { SignlessParams } from '../signless-params-list'; import { AccountPair } from '../account-pair'; +import { + ACTIONS, + BALANCE_VALUE_TO_ISSUE_VOUCHER, + BALANCE_VALUE_TO_START_GAME, + DEFAULT_VALUES, + DURATIONS, + REQUIRED_MESSAGE, +} from '@/consts'; type Props = Pick; -const DEFAULT_VALUES = { password: '' }; -const REQUIRED_MESSAGE = 'Field is required'; - -const DURATION_MINUTES = 5; -const BALANCE_VALUE_TO_START_GAME = 20; -const BALANCE_VALUE_TO_ISSUE_VOUCHER = 5; -const ACTIONS = ['StartGame', 'Turn']; - function CreateSessionModal({ close }: Props) { const { api } = useApi(); const { getChainBalanceValue, getFormattedBalance } = useBalanceFormat(); - + const [durationMinutes, setDurationMinutes] = useState(DURATIONS[0].value); const { register, handleSubmit, formState } = useForm({ defaultValues: DEFAULT_VALUES }); const { errors } = formState; @@ -67,7 +67,7 @@ function CreateSessionModal({ close }: Props) { setIsLoading(true); const { password } = values; - const duration = getMilliseconds(DURATION_MINUTES); + const duration = getMilliseconds(durationMinutes); const key = decodeAddress(pair.address); const allowedActions = ACTIONS; @@ -90,6 +90,10 @@ function CreateSessionModal({ close }: Props) { createSession({ duration, key, allowedActions }, issueVoucherValue, { onSuccess, onFinally }); }; + const handleSelectChange = (e: ChangeEvent) => { + setDurationMinutes(Number(e.target.value)); + }; + return ( <> @@ -103,24 +107,23 @@ function CreateSessionModal({ close }: Props) { heading: 'Voucher to issue:', value: `${formattedIssueVoucherValue.value} ${formattedIssueVoucherValue.unit}`, }, - { - heading: 'Session duration:', - value: `${DURATION_MINUTES} min`, - }, ]} />
      {!storagePair && ( - + <> + + )}
      diff --git a/frontend/packages/signless-transactions/src/consts.ts b/frontend/packages/signless-transactions/src/consts.ts new file mode 100644 index 000000000..6603aaee2 --- /dev/null +++ b/frontend/packages/signless-transactions/src/consts.ts @@ -0,0 +1,54 @@ +export const DEFAULT_VALUES = { password: '' }; +export const REQUIRED_MESSAGE = 'Field is required'; + +export const BALANCE_VALUE_TO_START_GAME = 20; +export const BALANCE_VALUE_TO_ISSUE_VOUCHER = 5; +export const ACTIONS = ['StartGame', 'Turn']; + +export const DURATIONS: { label: string; value: number; selected?: boolean }[] = [ + { + label: '5 minutes', + value: 5, + selected: true, + }, + { + label: '10 minutes', + value: 10, + }, + { + label: '30 minutes', + value: 30, + }, + { + label: '1 hour', + value: 60, + }, + { + label: '3 hours', + value: 180, + }, + { + label: '6 hours', + value: 360, + }, + { + label: '12 hours', + value: 720, + }, + { + label: '1 day', + value: 1440, + }, + { + label: '2 days', + value: 2880, + }, + { + label: '3 days', + value: 4320, + }, + { + label: '1 week', + value: 10080, + }, +]; diff --git a/frontend/packages/signless-transactions/src/hooks/use-create-session.ts b/frontend/packages/signless-transactions/src/hooks/use-create-session.ts index e9354fe26..6a6b51f42 100644 --- a/frontend/packages/signless-transactions/src/hooks/use-create-session.ts +++ b/frontend/packages/signless-transactions/src/hooks/use-create-session.ts @@ -33,7 +33,7 @@ function useCreateSession(programId: HexString, metadata: ProgramMetadata | unde return { destination, payload, gasLimit }; }; - const deleteSession = async (key: HexString, pair?: KeyringPair) => { + const deleteSession = async (key: HexString, pair?: KeyringPair, _options?: Options) => { if (!isApiReady) throw new Error('API is not initialized'); if (!metadata) throw new Error('Metadata not found'); @@ -60,7 +60,7 @@ function useCreateSession(programId: HexString, metadata: ProgramMetadata | unde const txs = [extrinsic, revokeExtrrinsic]; - batchSignAndSend(txs, { onError }); + batchSignAndSend(txs, { ..._options, onError }); }; const createSession = async (session: Session, voucherValue: number, _options: Options) => { diff --git a/frontend/packages/signless-transactions/src/utils.ts b/frontend/packages/signless-transactions/src/utils.ts index c96eb6787..156e07da5 100644 --- a/frontend/packages/signless-transactions/src/utils.ts +++ b/frontend/packages/signless-transactions/src/utils.ts @@ -50,12 +50,15 @@ const getMilliseconds = (minutes: number) => minutes * MULTIPLIER.MS * MULTIPLIE const getDoubleDigits = (value: number) => (value < 10 ? `0${value}` : value); -const getHMS = (ms: number) => { +const getDHMS = (ms: number) => { const seconds = Math.floor((ms / MULTIPLIER.MS) % MULTIPLIER.SECONDS); const minutes = Math.floor((ms / (MULTIPLIER.MS * MULTIPLIER.SECONDS)) % MULTIPLIER.MINUTES); const hours = Math.floor((ms / (MULTIPLIER.MS * MULTIPLIER.SECONDS * MULTIPLIER.MINUTES)) % MULTIPLIER.HOURS); + const days = Math.floor(ms / (MULTIPLIER.MS * MULTIPLIER.SECONDS * MULTIPLIER.MINUTES * MULTIPLIER.HOURS)); - return `${getDoubleDigits(hours)}:${getDoubleDigits(minutes)}:${getDoubleDigits(seconds)}`; + return `${days ? `${days} days, ` : ''}${getDoubleDigits(hours)}:${getDoubleDigits(minutes)}:${getDoubleDigits( + seconds, + )}`; }; const shortenString = (str: string, length: number): string => `${str.slice(0, length)}...${str.slice(-length)}`; @@ -106,4 +109,4 @@ const copyToClipboard = async ({ } }; -export { getMilliseconds, getHMS, getVaraAddress, shortenString, copyToClipboard }; +export { getMilliseconds, getDHMS, getVaraAddress, shortenString, copyToClipboard }; From 37cac000fa8e3f4a66f547c3f5db28ac05184843 Mon Sep 17 00:00:00 2001 From: sergey filyanin Date: Thu, 29 Feb 2024 17:42:48 +0100 Subject: [PATCH 10/17] Update STG-tictac.yml --- .github/workflows/STG-tictac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/STG-tictac.yml b/.github/workflows/STG-tictac.yml index df0dbd631..e88f08f81 100644 --- a/.github/workflows/STG-tictac.yml +++ b/.github/workflows/STG-tictac.yml @@ -47,7 +47,7 @@ jobs: - name: Set IMAGE_NAME id: image run: | - image_name=${{ env.REGISTRY }}:${{ env.branch_name }}-${{ env.sha_short }} + image_name=${{ env.REGISTRY }}-${{ env.KUBE_DEPLOYMENT_PREFIX }}:${{ env.branch_name }}-${{ env.sha_short }} echo "image_name=$image_name" >> $GITHUB_OUTPUT build-and-push-image: From 6aa5c303d4254f3520f50ce1bd93d3a334ac9ca5 Mon Sep 17 00:00:00 2001 From: MedovTimur <62596970+MedovTimur@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:15:04 +0300 Subject: [PATCH 11/17] Update tequila train (#220) --- contracts/tequila-train/io/src/lib.rs | 327 +++++++++++------ contracts/tequila-train/io/src/test.rs | 24 +- contracts/tequila-train/src/lib.rs | 482 ++++++++++++++++++++----- contracts/tequila-train/tests/test.rs | 436 ++++++++++++++++------ 4 files changed, 930 insertions(+), 339 deletions(-) diff --git a/contracts/tequila-train/io/src/lib.rs b/contracts/tequila-train/io/src/lib.rs index 5935ce178..a112a9634 100644 --- a/contracts/tequila-train/io/src/lib.rs +++ b/contracts/tequila-train/io/src/lib.rs @@ -1,36 +1,46 @@ #![no_std] -use gmeta::{In, InOut, Metadata, Out}; +use gmeta::{In, InOut, Metadata}; use gstd::{ collections::{BTreeMap, BTreeSet}, + exec, msg, prelude::*, ActorId, }; -#[cfg(not(test))] -use gstd::{exec, msg}; - pub struct ContractMetadata; impl Metadata for ContractMetadata { - type Init = In; + type Init = In; type Handle = InOut>; type Others = (); type Reply = (); type Signal = (); - type State = Out; + type State = InOut; } -#[derive(Encode, Decode, TypeInfo)] -pub struct Init { - pub players_limit: Option, -} #[derive(Debug, Clone, Encode, Decode, TypeInfo)] pub struct GameLauncherState { - pub game_state: Option, - pub players: Vec<(ActorId, String)>, - pub is_started: bool, - pub maybe_limit: Option, + pub games: Vec<(ActorId, Game)>, + pub players_to_game_id: Vec<(ActorId, ActorId)>, + pub config: Config, +} + +#[derive(Encode, Decode, TypeInfo)] +pub enum StateQuery { + All, + GetGame { player_id: ActorId }, +} +#[derive(Encode, Decode, TypeInfo)] +pub enum StateReply { + All(GameLauncherState), + Game(Option<(Game, Option)>), +} + +#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo)] +pub struct Config { + pub time_to_move: u32, + pub gas_to_check_game: u64, } #[derive( @@ -100,58 +110,43 @@ pub fn build_tile_collection() -> Vec { .collect() } -#[derive(Encode, Decode, TypeInfo, Debug)] -pub struct Players { - pub players: Vec<(ActorId, String)>, -} - -impl Players { - pub fn get(&self) -> Vec<(ActorId, String)> { - self.players.clone() - } - - pub fn count(&self) -> u64 { - self.players.len() as u64 - } -} - -impl From<[(ActorId, String); N]> for Players { - fn from(s: [(ActorId, String); N]) -> Players { - Players { - players: s.to_vec(), - } - } -} - -impl From<&[(ActorId, String)]> for Players { - fn from(s: &[(ActorId, String)]) -> Players { - Players { - players: s.to_vec(), - } - } -} - -#[derive(Encode, Decode, TypeInfo, Debug)] +#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq)] pub enum Command { - Skip, + CreateGame, + Skip { + creator: ActorId, + }, Place { + creator: ActorId, tile_id: u32, track_id: u32, remove_train: bool, }, Register { - player: ActorId, - name: String, + creator: ActorId, + }, + CancelRegistration { + creator: ActorId, + }, + DeletePlayer { + player_id: ActorId, + }, + CheckGame { + game_id: ActorId, + last_activity_time: u64, }, StartGame, - RestartGame( - /// Optional players limit. - Option, - ), + CancelGame, + LeaveGame, } #[derive(Encode, Decode, TypeInfo, Clone, Debug)] pub enum Event { + GameFinished { + winners: Vec, + all_participants: Vec, + }, + GameCreated, Skipped, Placed { tile_id: u32, @@ -160,32 +155,41 @@ pub enum Event { }, Registered { player: ActorId, - name: String, - }, - GameStarted, - GameRestarted { - players_limit: Option, }, - GameFinished { - winner: ActorId, + RegistrationCanceled, + PlayerDeleted { + player_id: ActorId, }, - GameStalled, + GameStarted, + GameCanceled, + GameLeft, + Checked, } #[derive(Debug, Clone, Encode, Decode, TypeInfo)] pub enum Error { - WrongPlayersCount, GameHasAlreadyStarted, GameHasNotStartedYet, - NameAlreadyExistsOrYouRegistered, + YouAlreadyRegistered, LimitHasBeenReached, - GameStalled, GameFinished, - NotYourTurn, + NotYourTurnOrYouLose, InvalidTile, InvalidTileId, InvalidTileOwner, InvalidTrack, + AlreadyExists, + GameDoesNotExist, + NotRegistered, + YouLose, + WrongBid, + NoSuchPlayer, + StateIsNotPlaying, + SeveralGames, + YouAreAdmin, + NotEnoughPlayers, + GameIsGoing, + OnlyProgramCanSend, } #[derive(Debug, TypeInfo, Encode, Decode, Clone, Default)] @@ -195,8 +199,24 @@ pub struct TrackData { } #[derive(Debug, TypeInfo, Encode, Decode, Clone, Default)] +pub struct Game { + pub admin: ActorId, + pub game_state: Option, + pub initial_players: Vec, + pub state: State, + pub is_started: bool, + pub bid: u128, +} + +#[derive(Encode, Decode, TypeInfo, Debug, Clone)] +pub struct Player { + pub id: ActorId, + pub lose: bool, +} + +#[derive(Debug, TypeInfo, Encode, Decode, Clone)] pub struct GameState { - pub players: Vec<(ActorId, String)>, + pub players: Vec, pub tracks: Vec, pub shots: Vec, pub start_tile: u32, @@ -204,14 +224,14 @@ pub struct GameState { pub tile_to_player: BTreeMap, pub tiles: Vec, pub remaining_tiles: BTreeSet, - pub state: State, + pub time_to_move: u32, + pub last_activity_time: u64, } -#[derive(Clone, Debug, Encode, Decode, TypeInfo, Default)] +#[derive(Clone, Debug, Encode, Decode, Default, TypeInfo, PartialEq, Eq)] pub enum State { Playing, - Stalled, - Winner((ActorId, String)), + Winners(Vec), #[default] Registration, } @@ -300,15 +320,14 @@ fn give_tiles_until_double( impl GameState { // TODO: cover it with tests - pub fn new(initial_data: &Players) -> Option { - // Check that players amount is allowed - let players_amount = initial_data.players.len(); - if !(2..=8).contains(&players_amount) { - return None; - } + pub fn new( + initial_players: Vec, + time_to_move: u32, + block_timestamp: u64, + ) -> Option { + let players_amount = initial_players.len(); let mut tile_to_player: BTreeMap = Default::default(); - // Build all possible tiles let tiles = build_tile_collection(); let mut remaining_tiles: BTreeSet = Default::default(); @@ -318,7 +337,7 @@ impl GameState { // Spread tiles to players let tiles_per_person = tiles_per_person(players_amount); - for player_index in 0..initial_data.players.len() { + for player_index in 0..initial_players.len() { for _ in 1..=tiles_per_person { let tile_id = get_random_from_set(&remaining_tiles); remaining_tiles.remove(&tile_id); @@ -352,38 +371,57 @@ impl GameState { // Remove starting tile from set tile_to_player.remove(&start_tile); + let players = initial_players + .into_iter() + .map(|id| Player { id, lose: false }) + .collect(); + + let current_player = (start_player + 1) % players_amount as u32; + Some(GameState { - players: initial_data.players.clone(), + players, tracks: vec![Default::default(); players_amount], shots: vec![0u32; players_amount], start_tile, - current_player: Self::next_player_impl(players_amount, start_player), + current_player, tile_to_player, tiles, remaining_tiles, - state: State::Playing, + time_to_move, + last_activity_time: block_timestamp, }) } - pub fn state(&self) -> State { - self.state.clone() - } - - pub fn skip_turn(&mut self, player: ActorId) -> Result { + pub fn skip_turn(&mut self, player: ActorId, bid: u128) -> Result { let i = self.current_player as usize; - if self.players[i].0 != player { - return Err(Error::NotYourTurn); + + if self.players[i].id != player { + return Err(Error::NotYourTurnOrYouLose); + } + + let count_players_is_live = self.players.iter().filter(|&player| !player.lose).count(); + let time = exec::block_timestamp(); + if count_players_is_live == 1 { + self.last_activity_time = time; + send_value(player, bid * self.players.len() as u128); + let all_participants = self.players.iter().map(|player| player.id).collect(); + return Ok(Event::GameFinished { + winners: vec![player], + all_participants, + }); } self.tracks[i].has_train = true; - if let Some(event) = self.post_actions() { + if let Some(event) = self.post_actions(bid) { + self.last_activity_time = time; return Ok(event); } + self.last_activity_time = time; Ok(Event::Skipped) } - fn post_actions(&mut self) -> Option { + fn post_actions(&mut self, bid: u128) -> Option { // check if the current player wins let remaining_tiles = self .tile_to_player @@ -391,16 +429,21 @@ impl GameState { .filter(|&player| *player == self.current_player) .count(); if remaining_tiles == 0 { - let (winner, name) = self.players[self.current_player as usize].clone(); - self.state = State::Winner((winner, name)); - return Some(Event::GameFinished { winner }); + let player = self.players[self.current_player as usize].clone(); + send_value(player.id, bid * self.players.len() as u128); + let all_participants = self.players.iter().map(|player| player.id).collect(); + return Some(Event::GameFinished { + winners: vec![player.id], + all_participants, + }); } // check if any next player is able to make a turn let players_to_check = self.players.len(); let check_result = (0..players_to_check).try_fold(self.current_player, |player, _| { - let next_player = self.next_player(player); - + let next_player = self + .next_player(player) + .expect("Error: there is no next player"); let remaining_tiles = self .tile_to_player .iter() @@ -420,8 +463,7 @@ impl GameState { self.current_player = next_player; return None; } - - if self.tracks[player_index].has_train { + if !self.remaining_tiles.is_empty() && self.tracks[player_index].has_train { // give the player randomly chosen tile let tile_id = get_random_from_set(&self.remaining_tiles); self.remaining_tiles.remove(&tile_id); @@ -437,24 +479,34 @@ impl GameState { Some(next_player) }); - if check_result.is_some() { - // no one can make turn. Game is over - self.state = State::Stalled; - return Some(Event::GameStalled); + if check_result.is_some() && self.remaining_tiles.is_empty() { + // no one can make turn. Point scoring + let winners = self.point_scoring(); + + let prize = bid * self.players.len() as u128 / winners.len() as u128; + + if bid != 0 { + winners.iter().for_each(|player| { + send_value(*player, prize); + }); + } + let all_participants = self.players.iter().map(|player| player.id).collect(); + return Some(Event::GameFinished { + winners, + all_participants, + }); } None } - fn next_player(&self, current_player: u32) -> u32 { - Self::next_player_impl(self.players.len(), current_player) - } - - fn next_player_impl(player_count: usize, current_player: u32) -> u32 { - let i = current_player as usize + 1; - match i < player_count { - true => i as u32, - false => 0, + pub fn next_player(&self, current_player: u32) -> Option { + for i in 1..=self.players.len() { + let index = (current_player as usize + i) % self.players.len(); + if !self.players[index].lose { + return Some(index as u32); + } } + None } // Helper function to check if any of the tiles can be put on any track. @@ -477,10 +529,23 @@ impl GameState { tile_id: u32, track_id: u32, remove_train: bool, + bid: u128, ) -> Result { let i = self.current_player as usize; - if self.players[i].0 != player { - return Err(Error::NotYourTurn); + + if self.players[i].id != player { + return Err(Error::NotYourTurnOrYouLose); + } + let count_players_is_live = self.players.iter().filter(|&player| !player.lose).count(); + let time = exec::block_timestamp(); + if count_players_is_live == 1 { + self.last_activity_time = time; + send_value(player, bid * self.players.len() as u128); + let all_participants = self.players.iter().map(|player| player.id).collect(); + return Ok(Event::GameFinished { + winners: vec![player], + all_participants, + }); } // check player owns the tile @@ -517,10 +582,13 @@ impl GameState { // remove tile from player's set self.tile_to_player.remove(&tile_id); + self.last_activity_time = exec::block_timestamp(); - if let Some(event) = self.post_actions() { + if let Some(event) = self.post_actions(bid) { + self.last_activity_time = time; return Ok(event); } + self.last_activity_time = time; Ok(Event::Placed { tile_id, track_id, @@ -546,6 +614,39 @@ impl GameState { None } + fn point_scoring(&self) -> Vec { + let mut scores: BTreeMap = BTreeMap::new(); + + for (tile, player) in &self.tile_to_player { + if !self.players[*player as usize].lose { + let tile_score = + self.tiles[*tile as usize].left as u8 + self.tiles[*tile as usize].right as u8; + scores + .entry(self.players[*player as usize].id) + .and_modify(|scores| *scores += tile_score as u16) + .or_insert(tile_score as u16); + } + } + + let min_score = scores.values().min().cloned(); + + scores + .iter() + .filter_map(|(actor_id, &score)| { + if Some(score) == min_score { + Some(*actor_id) + } else { + None + } + }) + .collect() + } +} + +pub fn send_value(destination: ActorId, value: u128) { + if value != 0 { + msg::send_with_gas(destination, "", 0, value).expect("Error in sending value"); + } } #[cfg(test)] diff --git a/contracts/tequila-train/io/src/test.rs b/contracts/tequila-train/io/src/test.rs index 8d09b01cc..99e320cf6 100644 --- a/contracts/tequila-train/io/src/test.rs +++ b/contracts/tequila-train/io/src/test.rs @@ -139,6 +139,7 @@ fn test_give_tiles_until_double_2() { fn test_give_tiles_until_double_3() { let players_amount = 5; let mut remaining_tiles: BTreeSet = Default::default(); + // println!("!@#"); let tiles = vec![ Tile::new(Face::Zero, Face::One), @@ -174,31 +175,12 @@ fn test_give_tiles_until_double_3() { assert_eq!(matching_tile_id.unwrap().1, 4); } -#[test] -fn test_game_state_fail_init() { - let actor1 = ActorId::new([1u8; 32]); - let players = Players { - players: vec![(actor1, "A".to_owned())], - }; - let game_state = GameState::new(&players); - assert!(game_state.is_none()); - - let players = Players { - players: vec![(actor1, "B".to_owned()); 9], - }; - let game_state = GameState::new(&players); - assert!(game_state.is_none()); -} - #[test] fn test_game_state() { let actor1 = ActorId::new([1u8; 32]); let actor2 = ActorId::new([2u8; 32]); - let players = Players { - players: vec![(actor1, "A".to_owned()), (actor2, "B".to_owned())], - }; - - let game_state = GameState::new(&players).unwrap(); + let players = vec![actor1, actor2]; + let game_state = GameState::new(players, 30_000, 10).unwrap(); let mut counters = (0u32, 0u32); for player_id in game_state.tile_to_player.values() { diff --git a/contracts/tequila-train/src/lib.rs b/contracts/tequila-train/src/lib.rs index 3f6a546db..9a4ffdadf 100644 --- a/contracts/tequila-train/src/lib.rs +++ b/contracts/tequila-train/src/lib.rs @@ -1,108 +1,288 @@ #![no_std] -use gstd::{msg, prelude::*, ActorId}; +use gstd::{collections::HashMap, exec, msg, prelude::*, ActorId}; use tequila_train_io::*; #[derive(Debug, Default)] pub struct GameLauncher { - pub game_state: Option, - pub players: Vec<(ActorId, String)>, - pub is_started: bool, - pub maybe_limit: Option, + pub games: HashMap, + pub players_to_game_id: HashMap, + pub config: Config, } -/// All game initializing logic is inside `GameState` constructor. static mut GAME_LAUNCHER: Option = None; impl GameLauncher { - fn check_limit_range(maybe_limit: Option) -> Result<(), Error> { - if let Some(limit) = maybe_limit { - if !(2..=8).contains(&limit) { - return Err(Error::WrongPlayersCount); - } + // creating a game session, after this action other users can register to the game using the creator's address. + pub fn create_game(&mut self, msg_source: ActorId, msg_value: u128) -> Result { + if self.players_to_game_id.contains_key(&msg_source) { + return Err(Error::SeveralGames); } - Ok(()) + + let mut game = Game { + admin: msg_source, + bid: msg_value, + ..Default::default() + }; + game.initial_players.push(msg_source); + self.games.insert(msg_source, game); + self.players_to_game_id.insert(msg_source, msg_source); + Ok(Event::GameCreated) } - fn check_players_count(&self) -> Result<(), Error> { - if !(2..=8).contains(&(self.players.len() as u32)) { - return Err(Error::WrongPlayersCount); + // Start the game and send a delayed message to check the timer + pub fn start(&mut self) -> Result { + let msg_src = msg::source(); + let game = self + .games + .get_mut(&msg_src) + .ok_or(Error::GameDoesNotExist)?; + + if game.is_started { + return Err(Error::GameHasAlreadyStarted); + } + if game.initial_players.len() < 2 { + return Err(Error::NotEnoughPlayers); } - Ok(()) + + game.is_started = true; + game.game_state = GameState::new( + game.initial_players.clone(), + self.config.time_to_move, + exec::block_timestamp(), + ); + game.state = State::Playing; + + // send a delayed message to check if the current player has made a move within the `config.time_to_move` limit + msg::send_with_gas_delayed( + exec::program_id(), + Command::CheckGame { + game_id: msg_src, + last_activity_time: game.game_state.clone().unwrap().last_activity_time, + }, + self.config.gas_to_check_game, + 0, + self.config.time_to_move / 3000, + ) + .expect("Error in sending delayed message"); + Ok(Event::GameStarted) } - pub fn new_with_limit(limit: u64) -> Self { - Self::check_limit_range(Some(limit)).expect("The limit should lie in the range [2,8]"); + // This function can only be called by the program itself + // and is used to check if the player has made a move within the time limit. + // If the player has not made a move, we mark the player as lost, + // change the current player and send the same delayed message to check another player. + // If only one player survives, we make that player the winner + pub fn check_game( + &mut self, + game_id: ActorId, + last_activity_time: u64, + ) -> Result { + let program_id = exec::program_id(); + if msg::source() != program_id { + return Err(Error::OnlyProgramCanSend); + } + let game = self + .games + .get_mut(&game_id) + .ok_or(Error::GameDoesNotExist)?; - GameLauncher { - maybe_limit: Some(limit), - ..Default::default() + let game_state = game + .game_state + .as_mut() + .ok_or(Error::GameHasNotStartedYet)?; + + // use the `last_activity_time` variable as an identifier of whether a move has been made + if game_state.last_activity_time == last_activity_time { + let current_player = game_state.current_player; + game_state.players[current_player as usize].lose = true; + // count how many players are left in the game + let count_players_is_live = game_state + .players + .iter() + .filter(|&player| !player.lose) + .count(); + + if count_players_is_live > 1 { + // change the current player to the next player who has not dropped out of the game + game_state.current_player = game_state + .next_player(current_player) + .expect("Live players more than 0"); + // change the time of last activity + game_state.last_activity_time = exec::block_timestamp(); + msg::send_delayed( + program_id, + Command::CheckGame { + game_id, + last_activity_time: game_state.last_activity_time, + }, + 0, + self.config.time_to_move / 3000, + ) + .expect("Error in sending delayed message"); + } else { + let winner_index = game_state + .next_player(current_player) + .expect("Live players more than 0"); + let winner = game_state.players[winner_index as usize].id; + let prize = game.bid; + if game.bid != 0 { + send_value(winner, prize * game.initial_players.len() as u128); + } + + game.state = State::Winners(vec![winner]); + msg::send( + winner, + Ok::(Event::GameFinished { + winners: vec![winner], + all_participants: game.initial_players.clone(), + }), + 0, + ) + .expect("Error in sending message"); + } } + Ok(Event::Checked) } - pub fn start(&mut self) -> Result { - if self.is_started { + // Registration for the game with the rate specified by the admin at the create of the game + // The address of the game creator is used as a game identifier + pub fn register( + &mut self, + msg_source: ActorId, + msg_value: u128, + creator: ActorId, + ) -> Result { + if self.players_to_game_id.contains_key(&msg_source) { + return Err(Error::SeveralGames); + } + let game = self + .games + .get_mut(&creator) + .ok_or(Error::GameDoesNotExist)?; + + if game.is_started { return Err(Error::GameHasAlreadyStarted); } - self.check_players_count()?; - self.is_started = true; - self.game_state = GameState::new(&Players { - players: self.players.clone(), - }); + if msg_value != game.bid { + return Err(Error::WrongBid); + } - assert!(self.game_state.is_some()); - Ok(Event::GameStarted) + if game.initial_players.contains(&msg_source) { + return Err(Error::YouAlreadyRegistered); + } + + if game.initial_players.len() >= 8 { + return Err(Error::LimitHasBeenReached); + } + + game.initial_players.push(msg_source); + self.players_to_game_id.insert(msg_source, creator); + Ok(Event::Registered { player: msg_source }) } - pub fn restart(&mut self, maybe_limit: Option) -> Result { - if !self.is_started { - return Err(Error::GameHasNotStartedYet); + // A registered player has the opportunity to leave the game and get his bet back + pub fn cancel_register(&mut self, creator: ActorId) -> Result { + let game = self + .games + .get_mut(&creator) + .ok_or(Error::GameDoesNotExist)?; + + let msg_src = msg::source(); + + if msg_src == game.admin { + return Err(Error::YouAreAdmin); + } + + if game.is_started { + return Err(Error::GameHasAlreadyStarted); + } + if !game.initial_players.contains(&msg_src) { + return Err(Error::NoSuchPlayer); + } + + send_value(msg_src, game.bid); + + if let Some(index_to_remove) = game.initial_players.iter().position(|id| id == &msg_src) { + game.initial_players.remove(index_to_remove); } - Self::check_limit_range(maybe_limit)?; + self.players_to_game_id.remove(&msg_src); - self.is_started = false; - self.game_state = None; - self.maybe_limit = maybe_limit; - self.players.clear(); - Ok(Event::GameRestarted { - players_limit: maybe_limit, - }) + Ok(Event::RegistrationCanceled) } - pub fn register(&mut self, player: ActorId, name: String) -> Result { - if self.is_started { + // An admin can forcibly remove a player at the moment of registration after which the bet is refunded + pub fn delete_player(&mut self, player_id: ActorId) -> Result { + let msg_src = msg::source(); + + let game = self + .games + .get_mut(&msg_src) + .ok_or(Error::GameDoesNotExist)?; + + if msg_src == player_id { + return Err(Error::YouAreAdmin); + } + + if game.is_started { return Err(Error::GameHasAlreadyStarted); } - if self.players.iter().any(|(p, n)| p == &player || n == &name) { - return Err(Error::NameAlreadyExistsOrYouRegistered); + if !game.initial_players.contains(&player_id) { + return Err(Error::NoSuchPlayer); } - if let Some(limit) = self.maybe_limit { - if (self.players.len() as u64) >= limit { - return Err(Error::LimitHasBeenReached); + send_value(player_id, game.bid); + let index_to_remove = game + .initial_players + .iter() + .position(|x| x == &player_id) + .expect("Critical Error"); + game.initial_players.remove(index_to_remove); + self.players_to_game_id.remove(&player_id); + + Ok(Event::PlayerDeleted { player_id }) + } + // The admin can forcibly end the game with all the players' bets going back + pub fn cancel_game(&mut self) -> Result { + let msg_src = msg::source(); + + let game = self + .games + .get_mut(&msg_src) + .ok_or(Error::GameDoesNotExist)?; + + // if the game is at the registration stage or the game is still in progress, + // we must refund everyone their bets and and remove them from the list of games + game.initial_players.iter().for_each(|id| { + if (game.state == State::Playing || game.state == State::Registration) && game.bid != 0 + { + send_value(*id, game.bid); } - } else if self.players.len() >= 8 { - return Err(Error::LimitHasBeenReached); - } + self.players_to_game_id.remove(id); + }); + + self.games.remove(&msg_src); - self.players.push((player, name.clone())); - Ok(Event::Registered { player, name }) + Ok(Event::GameCanceled) + } + // leave the game (uses when the game has already passed to remove yourself from the list of `players_to_game_id`) + pub fn leave_game(&mut self) -> Result { + self.players_to_game_id.remove(&msg::source()); + Ok(Event::GameLeft) } } #[no_mangle] extern fn init() { - let Init { players_limit } = msg::load().expect("Unexpected invalid payload."); - - unsafe { - GAME_LAUNCHER = Some(if let Some(limit) = players_limit { - GameLauncher::new_with_limit(limit) - } else { - GameLauncher::default() - }) - } + let config: Config = msg::load().expect("Unable to decode the initial msg"); + let game_launcher = GameLauncher { + config, + ..Default::default() + }; + + unsafe { GAME_LAUNCHER = Some(game_launcher) }; } #[no_mangle] @@ -118,43 +298,139 @@ fn process_handle() -> Result { .expect("The contract is not initialized") }; - if let Some(game_state) = &game_launcher.game_state { - match game_state.state() { - State::Stalled => { - return Err(Error::GameStalled); - } - State::Winner(_winner) => { - return Err(Error::GameFinished); - } - _ => (), - }; - } - let command: Command = msg::load().expect("Unexpected invalid command payload."); - let player = msg::source(); match command { - Command::Skip => { - if let Some(game_state) = &mut game_launcher.game_state { - game_state.skip_turn(player) + Command::CreateGame => { + let msg_source = msg::source(); + let msg_value = msg::value(); + let reply = game_launcher.create_game(msg_source, msg_value); + if reply.is_err() { + send_value(msg_source, msg_value); + } + reply + } + Command::Skip { creator } => { + let game = game_launcher + .games + .get_mut(&creator) + .ok_or(Error::GameDoesNotExist)?; + + // a move can only be made with State::Playing + if game.state != State::Playing { + return Err(Error::StateIsNotPlaying); + } + let player = msg::source(); + // a non-registered player cannot make a move + if !game.initial_players.contains(&player) { + return Err(Error::NotRegistered); + } + if let Some(game_state) = &mut game.game_state { + let result = game_state.skip_turn(player, game.bid); + match result { + // if the game is over, we change the game state + Ok(Event::GameFinished { ref winners, .. }) => { + game.state = State::Winners(winners.clone()); + } + // if the move is successful, we must send a delayed message to check if the next player has made a move + Ok(Event::Skipped) => { + msg::send_with_gas_delayed( + exec::program_id(), + Command::CheckGame { + game_id: creator, + last_activity_time: game + .game_state + .clone() + .unwrap() + .last_activity_time, + }, + game_launcher.config.gas_to_check_game, + 0, + game_launcher.config.time_to_move / 3000, + ) + .expect("Error in sending delayed message"); + } + _ => (), + } + + result } else { Err(Error::GameHasNotStartedYet) } } Command::Place { + creator, tile_id, track_id, remove_train, } => { - if let Some(game_state) = &mut game_launcher.game_state { - game_state.make_turn(player, tile_id, track_id, remove_train) + let game = game_launcher + .games + .get_mut(&creator) + .ok_or(Error::GameDoesNotExist)?; + + // a move can only be made with State::Playing + if game.state != State::Playing { + return Err(Error::GameHasNotStartedYet); + } + + let player = msg::source(); + // a non-registered player cannot make a move + if !game.initial_players.contains(&player) { + return Err(Error::NotRegistered); + } + if let Some(game_state) = &mut game.game_state { + let result = + game_state.make_turn(player, tile_id, track_id, remove_train, game.bid); + match result { + // if the game is over, we change the game state + Ok(Event::GameFinished { ref winners, .. }) => { + game.state = State::Winners(winners.clone()); + } + // if the move is successful, we must send a delayed message to check if the next player has made a move + Ok(Event::Placed { .. }) => { + msg::send_with_gas_delayed( + exec::program_id(), + Command::CheckGame { + game_id: creator, + last_activity_time: game + .game_state + .clone() + .unwrap() + .last_activity_time, + }, + game_launcher.config.gas_to_check_game, + 0, + game_launcher.config.time_to_move / 3000, + ) + .expect("Error in sending delayed message"); + } + _ => (), + } + + result } else { Err(Error::GameHasNotStartedYet) } } - Command::Register { player, name } => game_launcher.register(player, name), + Command::Register { creator } => { + let msg_source = msg::source(); + let msg_value = msg::value(); + let reply = game_launcher.register(msg_source, msg_value, creator); + if reply.is_err() { + send_value(msg_source, msg_value); + } + reply + } + Command::CancelRegistration { creator } => game_launcher.cancel_register(creator), + Command::DeletePlayer { player_id } => game_launcher.delete_player(player_id), + Command::CheckGame { + game_id, + last_activity_time, + } => game_launcher.check_game(game_id, last_activity_time), Command::StartGame => game_launcher.start(), - Command::RestartGame(maybe_limit) => game_launcher.restart(maybe_limit), + Command::CancelGame => game_launcher.cancel_game(), + Command::LeaveGame => game_launcher.leave_game(), } } @@ -165,25 +441,47 @@ extern fn state() { .take() .expect("Game launcher is not initialized") }; + let query: StateQuery = msg::load().expect("Unable to load the state query"); + let reply = match query { + StateQuery::All => StateReply::All(game_launcher.into()), + StateQuery::GetGame { player_id } => { + if let Some(creator_id) = game_launcher.players_to_game_id.get(&player_id) { + let game_reply = game_launcher + .games + .get(creator_id) + .map(|game| { + let last_activity_time_diff = game.game_state.as_ref().and_then(|state| { + (game_launcher.config.time_to_move as u64) + .checked_sub(exec::block_timestamp() - state.last_activity_time) + }); + (game.clone(), last_activity_time_diff) + }) + .map(Some) + .unwrap_or(None); - msg::reply::(game_launcher.into(), 0) - .expect("Failed to encode or reply with the game state"); + StateReply::Game(game_reply) + } else { + StateReply::Game(None) + } + } + }; + msg::reply(reply, 0).expect("Failed to encode or reply with the game state"); } impl From for GameLauncherState { fn from( GameLauncher { - game_state, - players, - is_started, - maybe_limit, + games, + config, + players_to_game_id, }: GameLauncher, ) -> Self { + let games = games.into_iter().collect(); + let players_to_game_id = players_to_game_id.into_iter().collect(); Self { - game_state, - players, - is_started, - maybe_limit, + games, + config, + players_to_game_id, } } } diff --git a/contracts/tequila-train/tests/test.rs b/contracts/tequila-train/tests/test.rs index 92ab65c37..41e337839 100644 --- a/contracts/tequila-train/tests/test.rs +++ b/contracts/tequila-train/tests/test.rs @@ -2,77 +2,114 @@ use gstd::{ActorId, Encode}; use gtest::{Program, System}; use tequila_train_io::*; +pub const PLAYERS: [u64; 3] = [10, 11, 12]; + pub trait TestFunc { - fn register(&self, from: u64, player: ActorId, name: String, error: Option); + fn create_game(&self, from: u64, bid: u128, error: Option); + fn register(&self, from: u64, bid: u128, creator: ActorId, error: Option); + fn cancel_register(&self, from: u64, creator: ActorId, error: Option); + fn delete_player(&self, from: u64, player_id: ActorId, error: Option); + fn cancel_game(&self, from: u64, error: Option); fn start_game(&self, from: u64, error: Option); - fn restart_game(&self, from: u64, players_limit: Option, error: Option); - fn skip(&self, from: u64, error: Option); + fn skip(&self, from: u64, creator: ActorId, error: Option); fn place( &self, from: u64, + creator: ActorId, tile_id: u32, track_id: u32, remove_train: bool, error: Option, ); + fn get_all_state(&self) -> Option; + fn get_game_state(&self, creator_id: ActorId) -> Option<(Game, Option)>; } impl TestFunc for Program<'_> { - fn register(&self, from: u64, player: ActorId, name: String, error: Option) { - let result = self.send( - from, - Command::Register { - player, - name: name.clone(), - }, - ); + fn create_game(&self, from: u64, bid: u128, error: Option) { + let result = self.send_with_value(from, Command::CreateGame, bid); assert!(!result.main_failed()); let reply = if let Some(error) = error { Err(error) } else { - Ok(Event::Registered { player, name }) + Ok(Event::GameCreated) }; assert!(result.contains(&(from, reply.encode()))); } - fn start_game(&self, from: u64, error: Option) { - let result = self.send(from, Command::StartGame); + fn register(&self, from: u64, bid: u128, creator: ActorId, error: Option) { + let result = self.send_with_value(from, Command::Register { creator }, bid); assert!(!result.main_failed()); - let reply: Result; - if let Some(error) = error { - reply = Err(error); - assert!(result.contains(&(from, reply.encode()))); + let reply = if let Some(error) = error { + Err(error) } else { - reply = Ok(Event::GameStarted); - assert!(result.contains(&(from, reply.encode()))); - } + Ok(Event::Registered { + player: from.into(), + }) + }; + assert!(result.contains(&(from, reply.encode()))); } - fn restart_game(&self, from: u64, players_limit: Option, error: Option) { - let result = self.send(from, Command::RestartGame(players_limit)); + fn cancel_register(&self, from: u64, creator: ActorId, error: Option) { + let result = self.send(from, Command::CancelRegistration { creator }); assert!(!result.main_failed()); - let reply: Result; - if let Some(error) = error { - reply = Err(error); - assert!(result.contains(&(from, reply.encode()))); + let reply = if let Some(error) = error { + Err(error) } else { - reply = Ok(Event::GameRestarted { players_limit }); - assert!(result.contains(&(from, reply.encode()))); - } + Ok(Event::RegistrationCanceled) + }; + assert!(result.contains(&(from, reply.encode()))); } - fn skip(&self, from: u64, error: Option) { - let result = self.send(from, Command::Skip); + fn delete_player(&self, from: u64, player_id: ActorId, error: Option) { + let result = self.send(from, Command::DeletePlayer { player_id }); + let res = &result.decoded_log::>(); + println!("RES: {:?}", res); assert!(!result.main_failed()); - let reply: Result; - if let Some(error) = error { - reply = Err(error); - assert!(result.contains(&(from, reply.encode()))); + let reply = if let Some(error) = error { + Err(error) } else { - reply = Ok(Event::Skipped); - assert!(result.contains(&(from, reply.encode()))); - } + Ok(Event::PlayerDeleted { player_id }) + }; + assert!(result.contains(&(from, reply.encode()))); + } + fn cancel_game(&self, from: u64, error: Option) { + let result = self.send(from, Command::CancelGame); + let res = &result.decoded_log::>(); + println!("RES: {:?}", res); + assert!(!result.main_failed()); + let reply = if let Some(error) = error { + Err(error) + } else { + Ok(Event::GameCanceled) + }; + assert!(result.contains(&(from, reply.encode()))); + } + fn start_game(&self, from: u64, error: Option) { + let result = self.send(from, Command::StartGame); + assert!(!result.main_failed()); + let res = &result.decoded_log::>(); + println!("RES: {:?}", res); + let reply = if let Some(error) = error { + Err(error) + } else { + Ok(Event::GameStarted) + }; + assert!(result.contains(&(from, reply.encode()))); + } + fn skip(&self, from: u64, creator: ActorId, error: Option) { + let result = self.send(from, Command::Skip { creator }); + assert!(!result.main_failed()); + let res = &result.decoded_log::>(); + println!("RES: {:?}", res); + let reply = if let Some(error) = error { + Err(error) + } else { + Ok(Event::Skipped) + }; + assert!(result.contains(&(from, reply.encode()))); } fn place( &self, from: u64, + creator: ActorId, tile_id: u32, track_id: u32, remove_train: bool, @@ -81,23 +118,44 @@ impl TestFunc for Program<'_> { let result = self.send( from, Command::Place { + creator, tile_id, track_id, remove_train, }, ); + let res = &result.decoded_log::>(); + println!("RES: {:?}", res); assert!(!result.main_failed()); - let reply: Result; - if let Some(error) = error { - reply = Err(error); - assert!(result.contains(&(from, reply.encode()))); + let reply = if let Some(error) = error { + Err(error) } else { - reply = Ok(Event::Placed { + Ok(Event::Placed { tile_id, track_id, remove_train, - }); - assert!(result.contains(&(from, reply.encode()))); + }) + }; + assert!(result.contains(&(from, reply.encode()))); + } + fn get_all_state(&self) -> Option { + let reply = self + .read_state(StateQuery::All) + .expect("Unexpected invalid state."); + if let StateReply::All(state) = reply { + Some(state) + } else { + None + } + } + fn get_game_state(&self, player_id: ActorId) -> Option<(Game, Option)> { + let reply = self + .read_state(StateQuery::GetGame { player_id }) + .expect("Unexpected invalid state."); + if let StateReply::Game(state) = reply { + state + } else { + None } } } @@ -109,47 +167,174 @@ fn success_test() { let program = Program::current_opt(&system); - let result = program.send(2, 0); + let config = Config { + time_to_move: 30_000, + gas_to_check_game: 200_000_000_000, + }; + + let result = program.send(2, config); assert!(!result.main_failed()); - program.register(2, 0.into(), "A".to_owned(), None); - program.register(2, 1.into(), "B".to_owned(), None); - program.start_game(2, None); + program.create_game(PLAYERS[0], 0, None); + + program.register(PLAYERS[1], 0, PLAYERS[0].into(), None); + program.register(PLAYERS[2], 0, PLAYERS[0].into(), None); + program.start_game(PLAYERS[0], None); let state: GameLauncherState = program - .read_state(0) + .get_all_state() .expect("Unexpected invalid game state."); - assert_eq!( - state - .game_state - .expect("Invalid game state. Game is not initialized.") - .players, - vec![(0.into(), "A".to_owned()), (1.into(), "B".to_owned())] + println!("STATE: {:?}", state); + + let game = state.games[0].1.game_state.clone().unwrap(); + let current_player = game.current_player; + + program.skip(PLAYERS[current_player as usize], PLAYERS[0].into(), None); + + system.spend_blocks(3); + let current_player = (current_player + 1) as usize % PLAYERS.len(); + program.skip(PLAYERS[current_player], PLAYERS[0].into(), None); + + system.spend_blocks(8); + let state = program + .get_game_state(PLAYERS[0].into()) + .expect("Unexpected invalid game state."); + println!("STATE: {:?}", state); + system.spend_blocks(2); + let current_player = (current_player + 1) % PLAYERS.len(); + program.skip( + PLAYERS[current_player], + PLAYERS[0].into(), + Some(Error::NotYourTurnOrYouLose), ); +} +#[test] +fn cancel_register() { + let system = System::new(); + + system.init_logger(); + + let program = Program::current_opt(&system); + + let config = Config { + time_to_move: 30_000, + gas_to_check_game: 200_000_000_000, + }; + + let result = program.send(2, config); + assert!(!result.main_failed()); + + let bid = 11_000_000_000_000; + system.mint_to(PLAYERS[0], bid); + program.create_game(PLAYERS[0], bid, None); - program.restart_game(2, None, None); - program.register(2, 2.into(), "C".to_owned(), None); - program.register(2, 3.into(), "D".to_owned(), None); - program.start_game(2, None); + system.mint_to(PLAYERS[1], bid); + program.register(PLAYERS[1], bid, PLAYERS[0].into(), None); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, 0); let state: GameLauncherState = program - .read_state(0) + .get_all_state() .expect("Unexpected invalid game state."); + assert_eq!(state.games[0].1.initial_players.len(), 2); + + program.cancel_register(PLAYERS[1], PLAYERS[0].into(), None); + system.claim_value_from_mailbox(PLAYERS[1]); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, bid); + + let state: GameLauncherState = program + .get_all_state() + .expect("Unexpected invalid game state."); + assert_eq!(state.games[0].1.initial_players.len(), 1); + assert_eq!(state.players_to_game_id.len(), 1); +} + +#[test] +fn delete_player() { + let system = System::new(); + + system.init_logger(); + + let program = Program::current_opt(&system); + + let config = Config { + time_to_move: 30_000, + gas_to_check_game: 200_000_000_000, + }; + + let result = program.send(2, config); + assert!(!result.main_failed()); + + let bid = 11_000_000_000_000; + system.mint_to(PLAYERS[0], bid); + program.create_game(PLAYERS[0], bid, None); + + system.mint_to(PLAYERS[1], bid); + program.register(PLAYERS[1], bid, PLAYERS[0].into(), None); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, 0); + + let state: GameLauncherState = program + .get_all_state() + .expect("Unexpected invalid game state."); + assert_eq!(state.games[0].1.initial_players.len(), 2); + + program.delete_player(PLAYERS[0], PLAYERS[1].into(), None); + system.claim_value_from_mailbox(PLAYERS[1]); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, bid); - assert_eq!( - state - .game_state - .expect("Invalid game state. Game is not initialized.") - .players, - vec![(2.into(), "C".to_owned()), (3.into(), "D".to_owned())] - ); - program.place(2, 27, 0, false, None); let state: GameLauncherState = program - .read_state(0) + .get_all_state() .expect("Unexpected invalid game state."); - assert!(!state.game_state.unwrap().tracks[0].tiles.is_empty()); - program.skip(2, None); - program.skip(3, None); + assert_eq!(state.games[0].1.initial_players.len(), 1); + assert_eq!(state.players_to_game_id.len(), 1); +} + +#[test] +fn cancel_game() { + let system = System::new(); + + system.init_logger(); + + let program = Program::current_opt(&system); + + let config = Config { + time_to_move: 30_000, + gas_to_check_game: 200_000_000_000, + }; + + let result = program.send(2, config); + assert!(!result.main_failed()); + + let bid = 11_000_000_000_000; + system.mint_to(PLAYERS[0], bid); + program.create_game(PLAYERS[0], bid, None); + + system.mint_to(PLAYERS[1], bid); + program.register(PLAYERS[1], bid, PLAYERS[0].into(), None); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, 0); + + let state: GameLauncherState = program + .get_all_state() + .expect("Unexpected invalid game state."); + assert!(!state.games.is_empty()); + + program.cancel_game(PLAYERS[0], None); + system.claim_value_from_mailbox(PLAYERS[1]); + let balance = system.balance_of(PLAYERS[1]); + assert_eq!(balance, bid); + system.claim_value_from_mailbox(PLAYERS[0]); + let balance = system.balance_of(PLAYERS[0]); + assert_eq!(balance, bid); + + let state: GameLauncherState = program + .get_all_state() + .expect("Unexpected invalid game state."); + assert!(state.games.is_empty()); + assert_eq!(state.players_to_game_id.len(), 0); } #[test] @@ -160,55 +345,80 @@ fn failures_test() { let program = Program::current_opt(&system); - let result = program.send(2, Some(2_u64)); + let config = Config { + time_to_move: 30_000, + gas_to_check_game: 200_000_000_000, + }; + + let result = program.send(2, config); assert!(!result.main_failed()); - program.start_game(2, Some(Error::WrongPlayersCount)); - program.restart_game(2, None, Some(Error::GameHasNotStartedYet)); - program.register(2, 0.into(), "A".to_owned(), None); + // After each error, a balance check will be made to verify the balance return + + // Сan't create multiple games + let bid = 11_000_000_000_000; + system.mint_to(PLAYERS[0], 2 * bid); + program.create_game(PLAYERS[0], bid, None); + program.create_game(PLAYERS[0], bid, Some(Error::SeveralGames)); + system.claim_value_from_mailbox(PLAYERS[0]); + assert_eq!(system.balance_of(PLAYERS[0]), bid); + + // You can't play one game and be an admin in another game + system.mint_to(PLAYERS[1], 2 * bid); + program.register(PLAYERS[1], bid, PLAYERS[0].into(), None); + program.create_game(PLAYERS[1], bid, Some(Error::SeveralGames)); + system.claim_value_from_mailbox(PLAYERS[1]); + assert_eq!(system.balance_of(PLAYERS[1]), bid); + + // A non-existent game id has been entered + system.mint_to(PLAYERS[2], 2 * bid); program.register( - 2, - 1.into(), - "A".to_owned(), - Some(Error::NameAlreadyExistsOrYouRegistered), + PLAYERS[2], + bid, + PLAYERS[1].into(), + Some(Error::GameDoesNotExist), ); + system.claim_value_from_mailbox(PLAYERS[2]); + assert_eq!(system.balance_of(PLAYERS[2]), 2 * bid); + // Wrong bid program.register( - 2, - 0.into(), - "B".to_owned(), - Some(Error::NameAlreadyExistsOrYouRegistered), + PLAYERS[2], + bid - 1, + PLAYERS[0].into(), + Some(Error::WrongBid), ); - program.register(2, 1.into(), "B".to_owned(), None); + system.claim_value_from_mailbox(PLAYERS[2]); + assert_eq!(system.balance_of(PLAYERS[2]), 2 * bid); + // Already registered program.register( - 2, - 3.into(), - "C".to_owned(), - Some(Error::LimitHasBeenReached), + PLAYERS[1], + bid, + PLAYERS[0].into(), + Some(Error::SeveralGames), ); - - program.start_game(2, None); + system.claim_value_from_mailbox(PLAYERS[1]); + assert_eq!(system.balance_of(PLAYERS[1]), bid); + // Registered In Another Game + program.create_game(PLAYERS[2], bid, None); program.register( - 2, - 3.into(), - "C".to_owned(), - Some(Error::GameHasAlreadyStarted), + PLAYERS[1], + bid, + PLAYERS[2].into(), + Some(Error::SeveralGames), ); - program.start_game(2, Some(Error::GameHasAlreadyStarted)); + system.claim_value_from_mailbox(PLAYERS[1]); + assert_eq!(system.balance_of(PLAYERS[1]), bid); - let state: GameLauncherState = program - .read_state(0) - .expect("Unexpected invalid game state."); - assert_eq!( - state - .game_state - .expect("Invalid game state. Game is not initialized.") - .players, - vec![(0.into(), "A".to_owned()), (1.into(), "B".to_owned())] - ); + // Admin try cancel register + program.cancel_register(PLAYERS[0], PLAYERS[0].into(), Some(Error::YouAreAdmin)); + + // No Such Player in registration list + program.cancel_register(PLAYERS[2], PLAYERS[0].into(), Some(Error::NoSuchPlayer)); + + // players less than 2 + program.start_game(PLAYERS[2], Some(Error::NotEnoughPlayers)); - program.restart_game(2, Some(10), Some(Error::WrongPlayersCount)); - program.place(3, 0, 0, false, Some(Error::NotYourTurn)); - program.place(0, 3, 0, false, Some(Error::InvalidTileId)); - program.place(0, 1, 1, false, Some(Error::InvalidTrack)); - program.place(0, 1, 0, false, Some(Error::InvalidTile)); + // the game has already started + program.start_game(PLAYERS[0], None); + program.start_game(PLAYERS[0], Some(Error::GameHasAlreadyStarted)); } From 93dc12ea6e7ce2c23893ede1d98f12a145b2e001 Mon Sep 17 00:00:00 2001 From: AndrePanin Date: Thu, 7 Mar 2024 14:16:35 +0100 Subject: [PATCH 12/17] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12565f691..d5e69951d 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@

      Hit the :star: button to keep up with our daily progress!

      -:wave: This repository serves as a comprehensive collection of decentralized application (dApp) examples, showcasing various real-world use cases and implementations based on **[Gear Protocol](https://gear-tech.io/)**. Developers can explore and experiment with these examples to gain hands-on experience in building decentralized applications using cutting-edge technologies and industry best practices. +:wave: This repository serves as a comprehensive collection of decentralized application (dApp) examples, showcasing various real-world use cases and implementations based on **[Gear Protocol](https://gear-tech.io/)**. Developers can explore and experiment with these examples, running them in the **[Vara Network](http://vara.network/)** to gain hands-on experience in building decentralized applications using cutting-edge technologies and industry best practices. :scroll: A `contracts` folder houses the source codes of the smart contracts on Rust that power the decentralized applications. :computer: A dedicated `frontend` folder contains examples of React web applications. These applications serve as live demonstrations illustrating how the frontend interacts seamlessly with programs running on the blockchain. -:point_right: Anyone can build and upload their programs to the Vara Network Testnet via **[Gear Idea](https://idea.gear-tech.io/)** portal. It is recommended for testing and as a preparatory step prior to deploying your application on the Vara Network mainnet. +:point_right: Anyone can build and upload their programs to the Vara Network Testnet via **[Gear Idea](https://idea.gear-tech.io/programs?node=wss%3A%2F%2Ftestnet.vara.network)** portal. It is recommended for testing and as a preparatory step prior to deploying your application on the Vara Network mainnet. :globe_with_meridians: Join our [developer community](https://discord.gg/RyYBKXrrPn) and contribute to the evolution of the decentralized application landscape. From 82a096914b4e9bd5eb94753eb322ad281d51df06 Mon Sep 17 00:00:00 2001 From: YauheniDraichykau <64776571+YauheniDraichykau@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:36:03 +0300 Subject: [PATCH 13/17] Yd/signless gasless updates (#278) Co-authored-by: Yauheni --- .../components/wallet-change/WalletChange.tsx | 8 +- .../battleship/src/pages/login.module.scss | 8 ++ frontend/apps/battleship/src/pages/login.tsx | 24 +++- .../gasless-transactions/package.json | 4 +- .../src/assets/icons/gas-station-line.svg | 10 ++ .../src/assets/icons/power.svg | 3 + .../enable-session/enable-session.module.css | 76 ++++++++++++ .../enable-session/enable-session.tsx | 83 +++++++++++++ .../src/components/enable-session/index.ts | 3 + .../gasless-transactions.module.css | 26 ++++ .../gasless-transactions.tsx | 29 +++++ .../components/gasless-transactions/index.ts | 3 + .../src/components/index.ts | 2 + .../src/context/consts.ts | 4 + .../src/context/index.tsx | 25 +++- .../gasless-transactions/src/context/types.ts | 4 + .../gasless-transactions/src/env.d.ts | 8 ++ .../gasless-transactions/src/index.ts | 3 +- .../gasless-transactions/vite-env.d.ts | 1 + .../gasless-transactions/vite.config.ts | 3 +- .../create-session-modal.tsx | 57 +++++---- .../enable-session-modal.tsx | 4 +- .../enable-session/enable-session.module.css | 76 ++++++++++++ .../enable-session/enable-session.tsx | 115 ++++++++++++++++++ .../src/components/enable-session/index.ts | 3 + .../src/components/index.ts | 3 +- .../signless-transactions.module.css | 20 ++- .../signless-transactions.tsx | 77 ++++++++---- .../src/context/consts.ts | 3 + .../src/context/index.tsx | 43 +++++-- .../src/context/types.ts | 3 + .../signless-transactions/src/index.ts | 3 +- .../components/menu-handler/menu-handler.tsx | 1 + .../components/menu-options/menu-options.tsx | 5 +- .../ui/src/components/switcher/index.ts | 3 + .../components/switcher/switcher.module.css | 111 +++++++++++++++++ .../ui/src/components/switcher/switcher.tsx | 31 +++++ frontend/packages/ui/src/index.ts | 15 ++- frontend/yarn.lock | 2 +- 39 files changed, 822 insertions(+), 80 deletions(-) create mode 100644 frontend/packages/gasless-transactions/src/assets/icons/gas-station-line.svg create mode 100644 frontend/packages/gasless-transactions/src/assets/icons/power.svg create mode 100644 frontend/packages/gasless-transactions/src/components/enable-session/enable-session.module.css create mode 100644 frontend/packages/gasless-transactions/src/components/enable-session/enable-session.tsx create mode 100644 frontend/packages/gasless-transactions/src/components/enable-session/index.ts create mode 100644 frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.module.css create mode 100644 frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.tsx create mode 100644 frontend/packages/gasless-transactions/src/components/gasless-transactions/index.ts create mode 100644 frontend/packages/gasless-transactions/src/components/index.ts create mode 100644 frontend/packages/signless-transactions/src/components/enable-session/enable-session.module.css create mode 100644 frontend/packages/signless-transactions/src/components/enable-session/enable-session.tsx create mode 100644 frontend/packages/signless-transactions/src/components/enable-session/index.ts create mode 100644 frontend/packages/ui/src/components/switcher/index.ts create mode 100644 frontend/packages/ui/src/components/switcher/switcher.module.css create mode 100644 frontend/packages/ui/src/components/switcher/switcher.tsx diff --git a/frontend/apps/battleship/src/features/wallet/components/wallet-change/WalletChange.tsx b/frontend/apps/battleship/src/features/wallet/components/wallet-change/WalletChange.tsx index 8c3347b1b..78ce52774 100644 --- a/frontend/apps/battleship/src/features/wallet/components/wallet-change/WalletChange.tsx +++ b/frontend/apps/battleship/src/features/wallet/components/wallet-change/WalletChange.tsx @@ -10,6 +10,7 @@ import { useWallet } from '../../hooks'; import styles from './WalletChange.module.scss'; import { MenuOptions } from '@dapps-frontend/ui'; import { SignlessTransactions } from '@dapps-frontend/signless-transactions'; +import { GaslessTransactions } from '@dapps-frontend/gasless-transactions'; type Props = { onClose(): void; @@ -52,7 +53,12 @@ export function WalletChange({ onClose, openConnectWallet }: Props) { return (
      - }]} /> + }, + { key: 'gasless', option: }, + ]} + />
        {getAccounts()}
      diff --git a/frontend/apps/battleship/src/pages/login.module.scss b/frontend/apps/battleship/src/pages/login.module.scss index 60cfd42ed..b4cca5f50 100644 --- a/frontend/apps/battleship/src/pages/login.module.scss +++ b/frontend/apps/battleship/src/pages/login.module.scss @@ -22,6 +22,14 @@ @include gap(48px); } +.controlsWrapper { + width: 80%; +} + +.startGameButton { + width: 100%; +} + .bottom { position: absolute; bottom: 0; diff --git a/frontend/apps/battleship/src/pages/login.tsx b/frontend/apps/battleship/src/pages/login.tsx index d076a52b1..594f6c841 100644 --- a/frontend/apps/battleship/src/pages/login.tsx +++ b/frontend/apps/battleship/src/pages/login.tsx @@ -8,8 +8,10 @@ import { Heading } from '@/components/ui/heading'; import { Text } from '@/components/ui/text'; import { TextGradient } from '@/components/ui/text-gradient'; import { WalletConnect } from '@/features/wallet'; - import styles from './login.module.scss'; +import { EnableSession as EnableSignlessSession } from '@dapps-frontend/signless-transactions'; +import { EnableSession as EnableGaslessSession } from '@dapps-frontend/gasless-transactions'; +import { Checkbox } from '@gear-js/vara-ui'; export default function Login() { const navigation = useNavigate(); @@ -38,12 +40,24 @@ export default function Login() {
      - + <> +
      + +
      +
      + +
      +
      + +
      +
      - S +
      diff --git a/frontend/packages/gasless-transactions/package.json b/frontend/packages/gasless-transactions/package.json index ce77466a1..eb3c9f8b0 100644 --- a/frontend/packages/gasless-transactions/package.json +++ b/frontend/packages/gasless-transactions/package.json @@ -10,7 +10,6 @@ "preview": "vite preview" }, "peerDependencies": { - "@dapps-frontend/signless-transactions": "workspace:*", "@gear-js/api": "0.36.5", "@gear-js/react-hooks": "0.10.2", "react": "18.2.0", @@ -28,7 +27,8 @@ "eslint-plugin-react-refresh": "0.4.3", "typescript": "5.0.2", "vite": "4.4.5", - "vite-plugin-dts": "3.5.1" + "vite-plugin-dts": "3.5.1", + "vite-plugin-svgr": "3.2.0" }, "files": [ "dist" diff --git a/frontend/packages/gasless-transactions/src/assets/icons/gas-station-line.svg b/frontend/packages/gasless-transactions/src/assets/icons/gas-station-line.svg new file mode 100644 index 000000000..efe540ce2 --- /dev/null +++ b/frontend/packages/gasless-transactions/src/assets/icons/gas-station-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/packages/gasless-transactions/src/assets/icons/power.svg b/frontend/packages/gasless-transactions/src/assets/icons/power.svg new file mode 100644 index 000000000..0a3b5201d --- /dev/null +++ b/frontend/packages/gasless-transactions/src/assets/icons/power.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.module.css b/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.module.css new file mode 100644 index 000000000..5b5e56fcc --- /dev/null +++ b/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.module.css @@ -0,0 +1,76 @@ +.enableButton { + display: flex; + align-items: center; + gap: 10px; + background: transparent; + margin: 0; + padding: 0; + cursor: pointer; + font-size: 16px; + font-weight: 400; + line-height: 19.2px; +} + +.closeButton { + background: #E9E9E9; + display: flex; + justify-content: center; +} + +.switchContainer { + display: flex; + gap: 10px; + width: 100%; +} + +.switcherWrapper { + padding-top: 7px; +} + +.contentWrapper { + display: flex; + flex-direction: column; +} + +.headingWrapper { + display: flex; + align-items: center; + gap: 10px; +} + +.heading { + font-size: 14px; + font-weight: 700; + line-height: 26px; + letter-spacing: 0.03em; + text-align: center; +} + +.descr { + font-size: 12px; + font-weight: 400; + line-height: 12px; + letter-spacing: 0.15000000596046448px; + text-align: left; + color: #898B94; +} + +.loader { + width: 16px; + height: 16px; + border: 2px dotted #000000; + border-radius: 50%; + display: inline-block; + position: relative; + box-sizing: border-box; + animation: rotation 2s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.tsx b/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.tsx new file mode 100644 index 000000000..6831e2a0f --- /dev/null +++ b/frontend/packages/gasless-transactions/src/components/enable-session/enable-session.tsx @@ -0,0 +1,83 @@ +import { Button, Checkbox } from '@gear-js/vara-ui'; +import styles from './enable-session.module.css'; +import { useGaslessTransactions } from '../..'; +import { ReactComponent as GaslessSVG } from '../../assets/icons/gas-station-line.svg'; +import { ReactComponent as PowerSVG } from '../../assets/icons/power.svg'; + +type Props = { + type: 'button' | 'switcher'; +}; + +function EnableSession({ type }: Props) { + const { isAvailable, isLoading, isActive, setIsActive } = useGaslessTransactions(); + + const handleSwitcherChange = (e: React.ChangeEvent) => { + if (e.target.checked) { + setIsActive(true); + } else { + setIsActive(false); + } + }; + + const handleActivateSession = () => { + setIsActive(true); + }; + + const handleDisableSession = () => { + setIsActive(false); + }; + + return ( + <> + {type === 'button' && ( + <> + {isActive ? ( +
      + + )} + {!session && storagePair && ( + <> +
      +

      Your Signless Session is expired

      + {pair ? ( +
      +
      + ) : ( + + )} - ) : ( -
      @@ -77,7 +78,7 @@ function EnableSession({ type }: Props) {
      )} - ); + ) : null; } export { EnableSession }; diff --git a/frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.tsx b/frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.tsx index 0a4629e2a..34abaa0ff 100644 --- a/frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.tsx +++ b/frontend/packages/gasless-transactions/src/components/gasless-transactions/gasless-transactions.tsx @@ -6,11 +6,11 @@ import { ReactComponent as GaslessSVG } from '../../assets/icons/gas-station-lin function GaslessTransactions() { const { account } = useAccount(); - const { isActive } = useGaslessTransactions(); + const { voucherId } = useGaslessTransactions(); return account ? (
      - {isActive && ( + {voucherId && ( <>
      @@ -21,7 +21,7 @@ function GaslessTransactions() {
      )} - {!isActive && } + {!voucherId && }
      ) : null; } diff --git a/frontend/packages/gasless-transactions/src/context/index.tsx b/frontend/packages/gasless-transactions/src/context/index.tsx index ca2504625..8b7dc30c6 100644 --- a/frontend/packages/gasless-transactions/src/context/index.tsx +++ b/frontend/packages/gasless-transactions/src/context/index.tsx @@ -40,22 +40,21 @@ function GaslessTransactionsProvider({ backendAddress, programId, voucherLimit, if (data?.error) { console.log(`Voucher is not fetched - ${data.error}`); - setIsAvailable(false); + setIsActive(false); return undefined; } if (!data.voucherId) { - setIsAvailable(false); + setIsActive(false); return undefined; } - setIsAvailable(true); return data.voucherId; } catch (error: any) { console.log('Error when fetching voucher'); console.log(error); - setIsAvailable(false); + setIsActive(false); setIsLoading(false); return undefined; @@ -100,17 +99,53 @@ function GaslessTransactionsProvider({ backendAddress, programId, voucherLimit, // eslint-disable-next-line react-hooks/exhaustive-deps }, [balance]); + const checkProgramStatus = async () => { + setIsLoading(true); + + try { + const response = await fetch(`${backendAddress}gasless/voucher/${programId}/status`); + + const data = await response.json(); + setIsLoading(false); + + if (data.enabled) { + setIsAvailable(true); + return; + } + + if (!data.enabled) { + setIsAvailable(false); + return; + } + + console.log(`Backend is not available`); + setIsAvailable(false); + + console.log(data); + } catch (error: any) { + console.log('Error when fetching voucher'); + console.log(error); + setIsAvailable(false); + setIsLoading(false); + } + }; + useEffect(() => { - if (account?.address) { - setIsLoading(true); - fetchVoucherId(); + if (account?.decodedAddress) { + setIsAvailable(false); + setIsActive(false); + setVoucherId(undefined); + + checkProgramStatus(); } - }, [account?.address]); + }, [account?.decodedAddress]); useEffect(() => { - setIsAvailable(false); - setVoucherId(undefined); - }, [account?.address]); + if (account?.decodedAddress && isAvailable && isActive && !voucherId) { + setIsLoading(true); + fetchVoucherId(); + } + }, [isAvailable, isActive, voucherId, account?.decodedAddress]); useEffect(() => { if (voucherId) { diff --git a/frontend/packages/signless-transactions/src/components/enable-session/enable-session.tsx b/frontend/packages/signless-transactions/src/components/enable-session/enable-session.tsx index ff9f4507c..b86b47d6c 100644 --- a/frontend/packages/signless-transactions/src/components/enable-session/enable-session.tsx +++ b/frontend/packages/signless-transactions/src/components/enable-session/enable-session.tsx @@ -1,5 +1,6 @@ import { Button, Checkbox } from '@gear-js/vara-ui'; import { useState } from 'react'; +import { useAccount } from '@gear-js/react-hooks'; import { ReactComponent as SignlessSVG } from '../../assets/icons/signless.svg'; import { ReactComponent as PowerSVG } from '../../assets/icons/power.svg'; import styles from './enable-session.module.css'; @@ -12,6 +13,7 @@ type Props = { }; function EnableSession({ type }: Props) { + const { account } = useAccount(); const { isAvailable, pair, session, deletePair, deleteSession } = useSignlessTransactions(); const [isLoading, setIsLoading] = useState(false); const [isCreateSessionModalOpen, setIsCreateSessionModalOpen] = useState(false); @@ -52,7 +54,7 @@ function EnableSession({ type }: Props) { } }; - return ( + return account?.decodedAddress ? ( <> {type === 'button' && ( <> @@ -109,7 +111,7 @@ function EnableSession({ type }: Props) { {isCreateSessionModalOpen && } {isEnableSessionModalOpen && } - ); + ) : null; } export { EnableSession }; From df5ee3353299bf9b82739e6e4cd3b36c3801e379 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 11 Mar 2024 12:29:23 +0300 Subject: [PATCH 16/17] Increases z-index in controls wrapper --- frontend/apps/battleship/src/pages/login.module.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/apps/battleship/src/pages/login.module.scss b/frontend/apps/battleship/src/pages/login.module.scss index b4cca5f50..fd5ad1733 100644 --- a/frontend/apps/battleship/src/pages/login.module.scss +++ b/frontend/apps/battleship/src/pages/login.module.scss @@ -24,6 +24,8 @@ .controlsWrapper { width: 80%; + position: relative; + z-index: 1; } .startGameButton { From 27b894e76d5861dff123f1e47576c9b5259fa5c8 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 11 Mar 2024 12:37:38 +0300 Subject: [PATCH 17/17] Gasless: Fixes styles --- frontend/packages/gasless-transactions/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/packages/gasless-transactions/vite.config.ts b/frontend/packages/gasless-transactions/vite.config.ts index 815198a93..ff47e0c10 100644 --- a/frontend/packages/gasless-transactions/vite.config.ts +++ b/frontend/packages/gasless-transactions/vite.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ external: ['react', 'react-dom', '@gear-js/api', '@gear-js/react-hooks', '@dapps-frontend/signless-transactions'], output: { globals: { react: 'React', 'react-dom': 'ReactDOM' }, + intro: 'import "./style.css";', }, }, },
    #{cellName}#{cellName}
    {rowIndex + 1}{rowIndex + 1} {cellName === 'playerAddress' ? ( <> {shortenString(row[cellName as keyof TableData], 4)} {isYourAddress(row[cellName]) && ( - (You) + (You) )} ) : ( @@ -64,6 +83,16 @@ function ParticipantsTable({ data, userAddress }: Props) { ), )} + {isUserAdmin && ( + + +