Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

➕ Add connect MetaMask modal #752

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions client/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export const setNetworkId = id => ({ type: SET_NETWORK_ID, id })
export const SET_PLAYER_ADDRESS = "SET_PLAYER_ADDRESS";
export const setPlayerAddress = address => ({ type: SET_PLAYER_ADDRESS, address })

export const SET_GAME_READ_ONLY = "SET_GAME_READ_ONLY";
export const setGameReadOnly = readOnly => ({ type: SET_GAME_READ_ONLY, readOnly })

export const LOAD_GAME_DATA = "LOAD_GAME_DATA";
export const loadGamedata = () => ({ type: LOAD_GAME_DATA, levels: undefined })

Expand Down
75 changes: 66 additions & 9 deletions client/src/containers/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
deprecationDate,
} from "../utils/networkDeprecation";
import { Helmet } from "react-helmet";

import { store } from "./../store";
import * as actions from "../../src/actions";

class App extends React.Component {
constructor() {
Expand Down Expand Up @@ -61,6 +62,35 @@ class App extends React.Component {
this.props.navigate(`${constants.PATH_LEVEL_ROOT}${target}`);
}

async continueAnyway() {
const deployWindow = document.querySelectorAll(".deploy-window-bg");
deployWindow[0].style.display = "none";
}

async continueInReadOnly() {
store.dispatch(actions.loadGamedata());
store.dispatch(actions.setGameReadOnly(true));
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "none";
}

async displayConnectionWindow() {
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "block";
}

async requestAccounts() {
await window.ethereum.request({ method: "eth_requestAccounts" });
const accountConnectionWindow = document.querySelectorAll(
".account-connection-window-bg"
);
accountConnectionWindow[0].style.display = "none";
}

render() {
let language = localStorage.getItem("lang");
let strings = loadTranslations(language);
Expand Down Expand Up @@ -117,11 +147,6 @@ class App extends React.Component {
}
}

async function continueAnyway() {
const deployWindow = document.querySelectorAll(".deploy-window-bg");
deployWindow[0].style.display = "none";
}

return (
<div className="appcontainer">
<Helmet>
Expand Down Expand Up @@ -180,21 +205,53 @@ class App extends React.Component {
/>
<ul>
<button
onClick={() => this.navigateToFirstIncompleteLevel()}
onClick={() => {
if (!store.getState().gamedata.readOnly) {
this.navigateToFirstIncompleteLevel();
} else {
this.displayConnectionWindow();
}
}}
className="buttons"
>
{strings.playNow}
</button>
</ul>
</section>
{/*not Account Connected window*/}
<div className="account-connection-window-bg">
<div className="account-connection-window">
<button
className="account-connection-close-x fas fa-x "
onClick={this.continueInReadOnly}
>
</button>
<h1>{randBadIcon()}</h1>
<br />
<h2>{strings.accountNotConnectedTitle}</h2>
<br />
<p>{strings.accountNotConnectedMessage}</p>
<br />
<div className="choice-buttons">
<button
className="buttons"
onClick={async () => {
await this.requestAccounts();
window.location.reload();
}}
>
{strings.connectAccount}
</button>
</div>
</div>
</div>
{/*not Deployed window*/}
<div className="deploy-window-bg">
{!networkOnDeprecationOrDeprecated(this.state.chainId) ? (
<div className="deploy-window">
{/*deploy window*/}
<h1>{randGoodIcon()}</h1>
<h2>{strings.deployMessageTitle}</h2>
<br />
{strings.deployMessage}
{supportedNetworksList(supportedNetworks)}
<p className="deploy-note">{strings.deployConfirmation}</p>
Expand Down Expand Up @@ -225,7 +282,7 @@ class App extends React.Component {
{strings.switchToSepolia}
</button>
{!isDeprecatedNetwork(this.state.chainId) && (
<button className="buttons" onClick={continueAnyway}>
<button className="buttons" onClick={this.continueAnyway}>
{strings.continueAnyway}
</button>
)}
Expand Down
93 changes: 54 additions & 39 deletions client/src/containers/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import onClickOutside from "react-onclickoutside";
import { connect } from "react-redux";
import { withRouter } from "../hoc/withRouter";
import { Link } from "react-router-dom";
import { Link, Navigate } from "react-router-dom";
import { bindActionCreators } from "redux";
import * as actions from "../actions";
import * as constants from "../constants";
Expand All @@ -11,6 +11,7 @@ import PropTypes from "prop-types";
import { ProgressBar } from "react-loader-spinner";
import { svgFilter } from "../utils/svg";
import LeaderIcon from "../components/leaderboard/LeaderIcon";
import { store } from "../store";
// import parse from "html-react-parser";

class Header extends React.Component {
Expand All @@ -24,7 +25,7 @@ class Header extends React.Component {
multiDDOpen: false,
};

if (this.props.web3) {
if (this.props.web3 && !store.getState().gamedata.readOnly) {
window.ethereum.request({ method: "eth_chainId" }).then((id) => {
this.setState({ chainId: Number(id) });
});
Expand Down Expand Up @@ -325,7 +326,8 @@ class Header extends React.Component {
</Link>
</div>
{window.location.pathname === constants.PATH_ROOT &&
!!this.props.web3 && (
!!this.props.web3 &&
!store.getState().gamedata.readOnly && (
<Link
onClick={() => this.toggleDropdownState()}
to={constants.PATH_LEADERBOARD}
Expand All @@ -342,45 +344,58 @@ class Header extends React.Component {
className="element-in-row toggle --small"
type="checkbox"
/>
<div className="connect-button">
{store.getState().gamedata.readOnly && (
<button
className="buttons"
onClick={async () => {
await window.ethereum.request({ method: "eth_requestAccounts" });
window.location.reload();
}}
>
{strings.connectAccount}
</button>
)}
</div>
</div>
</div>

<div
className={`single-dropdown --${
this.props.web3 && "--hidden"
}`}
>
<p onClick={() => this.setActiveTab(2)}>
<i className="fas fa-network-wired"></i>
<span>{strings.Networks}</span>
</p>
<div className={this.getDDClassName(2)}>
{Object.values(constants.NETWORKS_INGAME).map(
(network, index) => {
if (network && network.name !== "local") {
if (Number(network.id) === this.state.chainId)
return false; // filter out current network
return (
<div
key={index}
onClick={(e) => {
e.preventDefault();
this.changeNetwork(network);
}}
className="dropdown-pill"
>
<a id={network.name} key={network.name} href="/">
{network.name}
</a>
</div>
);
{this.props.web3 && !store.getState().gamedata.readOnly && (
Copy link
Member

@ericglau ericglau Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once in read-only mode, it's not obvious how to exit read-only mode, especially if the network list is hidden. This could be addressed by doing one or more of the following if in read-only (but I don't think these are a must for this first iteration):

  • Adding a button here that brings back the MetaMask modal (or even just directly connects)
  • If the user clicks the "Play now!" button on the main page, this is an indication that the user actually wants to connect. In this case, exit read-only mode (or bring back the MetaMask modal, or directly connect)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like both options! thanks for the suggestion!

Copy link
Member Author

@GianfrancoBazzani GianfrancoBazzani Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a new button here that is displayed only if the game is in readonly mode and allows to connect the account without passing through the modal.

image

Added also modal trigger when user clicks "Play now!". Let me know if there's any issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works great now, thanks a lot for addressing these so quickly!

<div className={`single-dropdown`}>
<p onClick={() => this.setActiveTab(2)}>
<i className="fas fa-network-wired"></i>
<span>{strings.Networks}</span>
</p>
<div className={this.getDDClassName(2)}>
{Object.values(constants.NETWORKS_INGAME).map(
(network, index) => {
if (network && network.name !== "local") {
if (Number(network.id) === this.state.chainId)
return false; // filter out current network
return (
<div
key={index}
onClick={(e) => {
e.preventDefault();
this.changeNetwork(network);
}}
className="dropdown-pill"
>
<a
id={network.name}
key={network.name}
href="/"
>
{network.name}
</a>
</div>
);
}
return null;
}
return null;
}
)}
)}
</div>
</div>
</div>

)}
<div className="single-dropdown">
<p onClick={() => this.setActiveTab(1)}>
<i className="fas fa-globe-americas"></i>
Expand Down Expand Up @@ -438,7 +453,7 @@ class Header extends React.Component {
wrapperClass="progress-bar-wrapper"
visible={true}
/>
{!this.props.web3 && (
{!this.props.web3 && !store.getState().gamedata.readOnly && (
<div
style={{ backgroundColor: "#eddfd6", border: "none" }}
className="alert alert-warning"
Expand Down
3 changes: 2 additions & 1 deletion client/src/containers/Level.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getLevelKey } from "../utils/contractutil";
import { deployAndRegisterLevel } from "../utils/deploycontract";
import { svgFilter } from "../utils/svg";
import { Helmet } from 'react-helmet';
import { store } from "../store";

class Level extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -331,7 +332,7 @@ class Level extends React.Component {
)}

{/* DEPLOY OR CREATE */}
{this.props.web3 && <button
{ !store.getState().gamedata.readOnly && this.props.web3 && <button
type="button"
className="button-actions"
onClick={
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ar/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "مصادر",
"submitInstance": "تسليم النسخة",
"getNewInstance": "احصل على نسخة جديدة",
"accountNotConnectedTitle": "حساب MetaMask غير متصل",
"accountNotConnectedMessage": "لبدء مغامرتك، قم بتوصيل محفظة MetaMask الخاصة بك! تتفاعل لعبتنا بشكل مباشر مع عقود Ethernaut على السلسلة، مما يعني أن أفعالك في اللعبة مسجلة على blockchain. من خلال ربط حساب MetaMask الخاص بك، يمكنك التفاعل بأمان مع هذه العقود الذكية، مما يسمح لك بحل التحديات والتقدم في اللعبة.",
"connectAccount": "يتصل",
"deployMessageTitle": "لم يتم نشر اللعبة",
"deployMessage": "تدُعم اللُعبة حاليًا هذه الشبكات فقط:",
"deprecatedNetwork": "شبكة عفا عليها الزمن",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/en/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Sources",
"submitInstance": "Submit instance",
"getNewInstance": "Get new instance",
"accountNotConnectedTitle": "MetaMask account not connected",
"accountNotConnectedMessage": "To begin your adventure, connect your MetaMask wallet! Our game interacts directly with Ethernaut on-chain contracts, which means that your actions in the game are recorded on the blockchain. By linking your MetaMask account, you can securely engage with these smart contracts, allowing you to solve challenges, and progress in the game.",
"connectAccount": "Connect",
GianfrancoBazzani marked this conversation as resolved.
Show resolved Hide resolved
"deployMessageTitle": "Game not deployed",
"deprecatedNetwork": "Network deprecated",
"networkBeingDeprecated": "Network being deprecated",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/es/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Código",
"submitInstance": "Comprueba instancia",
"getNewInstance": "Nueva instancia",
"accountNotConnectedTitle": "Cuenta MetaMask no conectada",
"accountNotConnectedMessage": "Para comenzar tu aventura, conecta tu billetera MetaMask. Nuestro juego interactúa directamente con los contratos en cadena de Ethernaut, lo que significa que tus acciones en el juego quedan registradas en la cadena de bloques. Al vincular tu cuenta MetaMask, puedes interactuar de forma segura con estos contratos inteligentes, lo que te permite resolver desafíos y progresar en el juego.",
"connectAccount": "Conectar",
"deployMessageTitle": "Juego no desplegado",
"deprecatedNetwork": "Red obsoleta",
"networkBeingDeprecated": "Red en desuso",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/fr/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Sources",
"submitInstance": "Soumettre une instance",
"getNewInstance": "Créer une nouvelle instance",
"accountNotConnectedTitle": "Compte MetaMask non connecté",
"accountNotConnectedMessage": "Pour commencer votre aventure, connectez votre portefeuille MetaMask ! Notre jeu interagit directement avec les contrats on-chain d'Ethernaut, ce qui signifie que vos actions dans le jeu sont enregistrées sur la blockchain. En liant votre compte MetaMask, vous pouvez interagir en toute sécurité avec ces contrats intelligents, vous permettant de résoudre des défis et de progresser dans le jeu.",
"connectAccount": "Connecter",
"deployMessageTitle": "Jeu non déployé",
"deprecatedNetwork": "Réseau obsolète",
"networkBeingDeprecated": "Réseau désaffecté",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ja/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "ソース",
"submitInstance": "インスタンスの提出",
"getNewInstance": "インスタンスの生成",
"accountNotConnectedTitle": "MetaMask アカウントが接続されていません",
"accountNotConnectedMessage": "冒険を始めるには、MetaMask ウォレットを接続してください。私たちのゲームは Ethernaut オンチェーン コントラクトと直接やり取りします。つまり、ゲーム内でのアクションはブロックチェーンに記録されます。MetaMask アカウントをリンクすることで、これらのスマート コントラクトに安全に関与し、課題を解決してゲームを進めることができます。",
"connectAccount": "接続する",
"deployMessageTitle": "ゲームが展開されていません",
"deployMessage": "現在、ゲームはこれらのネットワークのみをサポートしています:",
"deprecatedNetwork": "ネットワークは放棄されています",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/pt_br/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Código",
"submitInstance": "Enviar instância",
"getNewInstance": "Obter nova instância",
"accountNotConnectedTitle": "Conta MetaMask não ligada",
"accountNotConnectedMessage": "Para começar a sua aventura, ligue a sua carteira MetaMask! O nosso jogo interage diretamente com os contratos on-chain do Ethernaut, o que significa que as suas ações no jogo são registadas na blockchain. Ao ligar a sua conta MetaMask, pode interagir com segurança com estes contratos inteligentes, permitindo-lhe resolver desafios e progredir no jogo.",
"connectAccount": "Ligar",
"deployMessageTitle": "Jogo não implantado",
"deprecatedNetwork": "Rede obsoleta",
"networkBeingDeprecated": "Rede sendo obsoleta",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/ru/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Исходный код",
"submitInstance": "Отправить инстанс на проверку",
"getNewInstance": "Создать новый инстанс",
"accountNotConnectedTitle": "Учетная запись MetaMask не подключена",
"accountNotConnectedMessage": "Чтобы начать приключение, подключите свой кошелек MetaMask! Наша игра напрямую взаимодействует с ончейн-контрактами Ethernaut, что означает, что ваши действия в игре записываются в блокчейн. Подключив свой аккаунт MetaMask, вы можете безопасно взаимодействовать с этими смарт-контрактами, что позволит вам решать задачи и продвигаться в игре.",
"connectAccount": "Bağlamak",
"deployMessageTitle": "Игра не развернута",
"deprecatedNetwork": "заброшенная сеть",
"networkBeingDeprecated": "сеть в процессе закрытия",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/tr/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "Kaynaklar",
"submitInstance": "Durum gönder",
"getNewInstance": "Yeni durum al",
"accountNotConnectedTitle": "MetaMask hesabı bağlı değil",
"accountNotConnectedMessage": "Maceranıza başlamak için MetaMask cüzdanınızı bağlayın! Oyunumuz doğrudan Ethernaut zincir üstü sözleşmeleriyle etkileşime girer, bu da oyundaki eylemlerinizin blok zincirinde kaydedildiği anlamına gelir. MetaMask hesabınızı bağlayarak bu akıllı sözleşmelerle güvenli bir şekilde etkileşime girebilir, zorlukları çözebilir ve oyunda ilerleyebilirsiniz.",
"connectAccount": "Bağlamak",
"deployMessageTitle": "Oyun dağıtılmadı",
"deprecatedNetwork": "Ağ kullanımdan kaldırıldı",
"networkBeingDeprecated": "Ağ kullanımdan kaldırılıyor",
Expand Down
3 changes: 3 additions & 0 deletions client/src/gamedata/zh_cn/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"sources": "源码",
"submitInstance": "提交该实例",
"getNewInstance": "生成新实例",
"accountNotConnectedTitle": "MetaMask 帐户未连接",
"accountNotConnectedMessage": "要开始您的冒险,请连接您的 MetaMask 钱包!我们的游戏直接与 Ethernaut 链上合约交互,这意味着您在游戏中的行为会记录在区块链上。通过链接您的 MetaMask 帐户,您可以安全地与这些智能合约互动,从而让您解决​​挑战并在游戏中取得进展。",
"connectAccount": "连接",
"deployMessageTitle": "Game not deployed",
"deprecatedNetwork": "Network deprecated",
"networkBeingDeprecated": "Network being deprecated",
Expand Down
Loading
Loading