diff --git a/package-lock.json b/package-lock.json
index d073e83cd4..4b7758cdd6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,7 @@
"packages": {
"": {
"name": "ambire-wallet",
- "version": "0.7.7",
+ "version": "0.7.8",
"hasInstallScript": true,
"dependencies": {
"@0x/subproviders": "^6.6.5",
@@ -31,7 +31,7 @@
"@web3-react/abstract-connector": "^6.0.7",
"@web3-react/types": "^6.0.7",
"adex-protocol-eth": "git+https://git@github.com/AmbireTech/adex-protocol-eth.git#2606263234a7e01ece1af9826e6084fd800dae8f",
- "ambire-common": "npm:@ambire/common@1.0.17",
+ "ambire-common": "npm:@ambire/common@1.0.19",
"bip44-constants": "^128.0.0",
"blockies-ts": "^1.0.0",
"chart.js": "^3.9.1",
@@ -13586,9 +13586,9 @@
},
"node_modules/ambire-common": {
"name": "@ambire/common",
- "version": "1.0.17",
- "resolved": "https://registry.npmjs.org/@ambire/common/-/common-1.0.17.tgz",
- "integrity": "sha512-3gZylNEx6oZmFfCLB7KfnYmnLtaKlpXVO63VklBwg0jiJTmF7n0L9T4ZaATcXnofjV1NTCeIVEMCMUL9NtXNaQ==",
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/@ambire/common/-/common-1.0.19.tgz",
+ "integrity": "sha512-jqcRtzS3VqaLS+Vdd8yywHd8cbDZ3pTWvn+m+QQh6NjU+GZwDOfuseEY5/iz4k3MbXgJw2Reos/z6jW9hMv/EA==",
"peerDependencies": {
"@ambire/signature-validator": "^1.0.3",
"@ensdomains/eth-ens-namehash": "^2.0.15",
@@ -67849,9 +67849,9 @@
"integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ=="
},
"ambire-common": {
- "version": "npm:@ambire/common@1.0.17",
- "resolved": "https://registry.npmjs.org/@ambire/common/-/common-1.0.17.tgz",
- "integrity": "sha512-3gZylNEx6oZmFfCLB7KfnYmnLtaKlpXVO63VklBwg0jiJTmF7n0L9T4ZaATcXnofjV1NTCeIVEMCMUL9NtXNaQ==",
+ "version": "npm:@ambire/common@1.0.19",
+ "resolved": "https://registry.npmjs.org/@ambire/common/-/common-1.0.19.tgz",
+ "integrity": "sha512-jqcRtzS3VqaLS+Vdd8yywHd8cbDZ3pTWvn+m+QQh6NjU+GZwDOfuseEY5/iz4k3MbXgJw2Reos/z6jW9hMv/EA==",
"requires": {}
},
"amdefine": {
diff --git a/package.json b/package.json
index 5acbd1e5f7..a659df43db 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"@web3-react/abstract-connector": "^6.0.7",
"@web3-react/types": "^6.0.7",
"adex-protocol-eth": "git+https://git@github.com/AmbireTech/adex-protocol-eth.git#2606263234a7e01ece1af9826e6084fd800dae8f",
- "ambire-common": "npm:@ambire/common@1.0.17",
+ "ambire-common": "npm:@ambire/common@1.0.19",
"bip44-constants": "^128.0.0",
"blockies-ts": "^1.0.0",
"chart.js": "^3.9.1",
diff --git a/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.js b/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.js
new file mode 100644
index 0000000000..8e05f558e6
--- /dev/null
+++ b/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.js
@@ -0,0 +1,114 @@
+import { useCallback, useEffect, useState } from 'react'
+import cn from 'classnames'
+
+import { useModals } from 'hooks'
+
+import { ReactComponent as AmbireLogo } from 'resources/logo-new.svg'
+import { ReactComponent as CopyIcon } from 'resources/icons/copy-new.svg'
+import { ReactComponent as CloseIcon } from 'resources/icons/close.svg'
+import { ReactComponent as ChromeWebStore } from 'resources/chrome-web-store.svg'
+
+import { useToasts } from 'hooks/toasts'
+import styles from './ExtensionInviteCodeModal.module.scss'
+
+const CAN_CLOSE_AFTER_MS = 5400
+
+const ExtensionInviteCodeModal = ({ inviteCode, waitForClose = true }) => {
+ const { hideModal } = useModals()
+ const { addToast } = useToasts()
+ const [remainingTime, setRemainingTime] = useState(CAN_CLOSE_AFTER_MS)
+ const [canClose, setCanClose] = useState(!waitForClose)
+
+ const handleCloseModal = useCallback(() => {
+ if (!canClose) return
+
+ hideModal()
+ }, [canClose, hideModal])
+
+ useEffect(() => {
+ const startingTime = Date.now()
+
+ const interval = setInterval(() => {
+ const elapsedTime = Date.now() - startingTime
+ const newRemainingTime = CAN_CLOSE_AFTER_MS - elapsedTime
+ setRemainingTime(newRemainingTime)
+
+ if (newRemainingTime <= 0) {
+ setCanClose(true)
+ clearInterval(interval)
+ }
+ }, 500)
+
+ return () => {
+ clearInterval(interval)
+ }
+ }, [])
+
+ const handleCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(inviteCode)
+ addToast('Invite code copied to clipboard')
+ } catch {
+ addToast('Failed to copy invite code to clipboard', { error: true })
+ }
+ }, [addToast, inviteCode])
+
+ return (
+
+
+
+
+
+
+ {!canClose ? (
+
+ {remainingTime > 500 ? Math.round(remainingTime / 1000) : 1}
+
+ ) : (
+
+ )}
+
+
+
+
+
Hey!
+
+ We are onboarding the existing Ambire community first to our newest product - the Ambire
+ browser extension.
+
+
+ Claim this exclusive invitation code to get early access and start collecting XP for our
+ launch campaign before everyone else 🤫
+
+
+
+ Invitation code
+ {inviteCode}
+
+
+
+
+ Go to Chrome Web Store, install the extension and use the invitation code to log in.
+
+
+
+
+
+
+
+ )
+}
+
+export default ExtensionInviteCodeModal
diff --git a/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.module.scss b/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.module.scss
new file mode 100644
index 0000000000..d4e1fc44ae
--- /dev/null
+++ b/src/components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal.module.scss
@@ -0,0 +1,268 @@
+@import 'variables.scss';
+
+$max-modal-height: 55.375rem;
+$min-modal-height: 34.375rem;
+
+.wrapper {
+ z-index: 52;
+ position: relative;
+ max-height: $max-modal-height;
+ min-height: $min-modal-height;
+ max-width: 37.75rem;
+ width: 100%;
+ border-radius: 1.125rem;
+ border: 1px $c-fiord;
+ background: $c-ebony-clay;
+}
+
+// Reset
+.wrapper p,
+.wrapper span {
+ margin: 0;
+}
+
+.header {
+ overflow: hidden;
+ position: relative;
+ display: flex;
+ justify-content: center;
+ padding: 2rem;
+ background: linear-gradient(82deg, $c-electric-violet, #353d6e);
+ border-radius: 0.75rem 0.75rem 0 0;
+
+ .headerPrimaryGradient {
+ position: absolute;
+ left: -20%;
+ top: -35%;
+ z-index: 2;
+ width: 20rem;
+ height: 13.75rem;
+ background-color: #8b3dff;
+ opacity: 0.66;
+ filter: blur(4.375rem);
+ }
+
+ .headerSecondaryGradient {
+ position: absolute;
+ left: 60%;
+ bottom: 30%;
+ z-index: 2;
+ width: 26.25rem;
+ height: 18.5rem;
+ background-color: #56f6c1;
+ opacity: 0.48;
+ filter: blur(6.25rem);
+ }
+
+ .closeWrapper {
+ position: absolute;
+ right: 1rem;
+ top: 1rem;
+ z-index: 4;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+
+ .remainingTime {
+ color: #fff;
+ font-size: 0.875rem;
+ opacity: 0.6;
+ user-select: none;
+ }
+ .closeIcon {
+ cursor: pointer;
+
+ :global(#background) {
+ display: none;
+ }
+ }
+
+ &:not(.closeIconEnabled) {
+ .closeIcon {
+ opacity: 0.4;
+ cursor: not-allowed;
+ }
+ }
+ }
+
+ .headerLogo {
+ position: relative;
+ z-index: 3;
+ }
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ padding: 2rem;
+ gap: 2.5rem;
+}
+
+.textWrapper {
+ .text {
+ font-size: 1.125rem;
+ line-height: 1.7;
+ margin-bottom: 1rem;
+ }
+ .text:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.codeWrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ .codeTitle {
+ display: block;
+ color: #bbbde4;
+ font-size: 1rem;
+ margin-bottom: 1rem;
+ }
+ .code {
+ display: block;
+ margin-bottom: 2rem;
+ border: none;
+ width: 18ch;
+ background: repeating-linear-gradient(
+ 90deg,
+ $c-scampi 0,
+ $c-scampi 1ch,
+ transparent 0,
+ transparent 1.5ch
+ )
+ 0 100%/98% 1px no-repeat;
+ color: $c-turquoise; // font: 36px monospace;
+ font-size: 2.5rem;
+ font-family: monospace;
+ letter-spacing: 0.5ch;
+ }
+ .copyButton {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.5rem 1.5rem;
+ background: rgba(235, 236, 255, 0.08);
+ border-radius: 1.5rem;
+ cursor: pointer;
+
+ svg,
+ span {
+ color: #bbbde4;
+ transition: $basic-transition;
+ }
+
+ svg {
+ width: 1.125rem;
+ height: 1.125rem;
+ }
+
+ &:hover {
+ background: rgba(235, 236, 255, 0.15);
+
+ svg,
+ span {
+ color: #fff;
+ }
+ }
+ }
+}
+
+.storeWrapper {
+ .storeText {
+ font-size: 1.125rem;
+ margin-bottom: 2rem;
+ }
+ .storeLink {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: fit-content;
+ gap: 0.5rem;
+ padding: 1rem 3rem;
+ margin: 0 auto;
+ border-radius: 4rem;
+ border: 1px solid $c-scampi;
+ background: rgba(0, 0, 0, 0.3);
+ cursor: pointer;
+
+ svg {
+ width: 9.25rem;
+ height: 2rem;
+ }
+
+ &:hover {
+ background-color: #0e0e0e;
+ }
+ }
+}
+
+@mixin SmallScreen {
+ .header {
+ padding: 1.5rem;
+ }
+ .content {
+ gap: 1.5rem;
+ }
+ .storeWrapper .storeText,
+ .textWrapper .text {
+ font-size: 0.875rem;
+ }
+ .storeWrapper .storeText {
+ margin-bottom: 1rem;
+ }
+ .codeWrapper {
+ .codeTitle {
+ font-size: 0.875rem;
+ margin-bottom: 0.5rem;
+ }
+ .code {
+ margin-bottom: 1.5rem;
+ }
+ }
+}
+
+@media screen and (max-height: calc(#{$max-modal-height} + 100px)) {
+ @include SmallScreen;
+}
+
+@include sm-breakpoint {
+ @include SmallScreen;
+ .header,
+ .content {
+ padding: 1.5rem;
+ }
+
+ .codeWrapper {
+ .code {
+ font-size: 2rem;
+ }
+ }
+}
+
+@include xs-breakpoint {
+ .header,
+ .content {
+ padding: 1rem;
+ }
+
+ .header {
+ .headerLogo {
+ width: 3.75rem;
+ width: 4rem;
+ height: auto;
+ }
+ .closeWrapper {
+ top: 0.25rem;
+ right: 0.25rem;
+ }
+ }
+}
+
+@media screen and (max-width: 350px) {
+ .codeWrapper .code {
+ font-size: 1.5rem;
+ }
+}
diff --git a/src/components/Modals/WalletTokenModal/WalletTokenModal.js b/src/components/Modals/WalletTokenModal/WalletTokenModal.js
index c1fcdcd040..2a9b33eb81 100644
--- a/src/components/Modals/WalletTokenModal/WalletTokenModal.js
+++ b/src/components/Modals/WalletTokenModal/WalletTokenModal.js
@@ -77,8 +77,8 @@ const WalletTokenModal = ({ accountId, claimableWalletToken, rewards, network })
our browser extension
- . Following a recent governance vote, early users $WALLET rewards are no longer available
- in the Web and Mobile versions of Ambire Wallet.
+ . Following a recent governance vote, early users $WALLET rewards are no longer awarded in
+ the Web and Mobile versions of Ambire Wallet.
-
+
)}
diff --git a/src/components/Wallet/TopBar/Accounts/Accounts.js b/src/components/Wallet/TopBar/Accounts/Accounts.js
index e9b5ee1efa..3468db7395 100644
--- a/src/components/Wallet/TopBar/Accounts/Accounts.js
+++ b/src/components/Wallet/TopBar/Accounts/Accounts.js
@@ -173,7 +173,10 @@ const Accounts = ({
)}
-
{id}
+
>
diff --git a/src/components/Wallet/Transfer/Transfer.test.jsx b/src/components/Wallet/Transfer/Transfer.test.jsx
index 8365a40b7f..c69c1493c3 100644
--- a/src/components/Wallet/Transfer/Transfer.test.jsx
+++ b/src/components/Wallet/Transfer/Transfer.test.jsx
@@ -97,7 +97,8 @@ test('can send token', async () => {
addressBook={addressBook}
selectedAcc={userAddress}
addRequest={addRequest}
- />)
+ />
+ )
const selectAnAsset = screen.getByText('Select an asset')
await user.click(selectAnAsset)
const selectMatic = screen.getByText('MATIC')
diff --git a/src/components/Wallet/Wallet.js b/src/components/Wallet/Wallet.js
index 913c691db6..77526defc7 100644
--- a/src/components/Wallet/Wallet.js
+++ b/src/components/Wallet/Wallet.js
@@ -10,6 +10,7 @@ import unsupportedDApps from 'ambire-common/src/constants/unsupportedDApps'
import PermissionsModal from 'components/Modals/PermissionsModal/PermissionsModal'
import UnsupportedDAppsModal from 'components/Modals/UnsupportedDAppsModal/UnsupportedDAppsModal'
import { Loading } from 'components/common'
+import ExtensionInviteCodeModal from 'components/Modals/ExtensionInviteCodeModal/ExtensionInviteCodeModal'
import SideBar from './SideBar/SideBar'
import TopBar from './TopBar/TopBar'
import DappsCatalog from './DappsCatalog/DappsCatalog'
@@ -42,6 +43,10 @@ export default function Wallet(props) {
key: 'dAppsAdvancedMode',
defaultValue: []
})
+ const [extensionInviteCodeModalSeenBy, setExtensionInviteCodeModalSeenBy] = useLocalStorage({
+ key: 'extensionInviteCodeModalSeenBy',
+ defaultValue: []
+ })
const routes = [
{
@@ -220,9 +225,9 @@ export default function Wallet(props) {
const LoggedInGuard = () => (!isLoggedIn ? : null)
- const handlePermissionsModal = useCallback(async () => {
+ const handleDisplayInitialModal = useCallback(async () => {
const account = props.accounts.find(({ id }) => id === props.selectedAcc)
- if (!account) return
+ if (!account || !arePermissionsLoaded) return
const relayerIdentityURL = `${props.relayerURL}/identity/${account.id}`
@@ -243,23 +248,47 @@ export default function Wallet(props) {
/>
)
- const isMobile = navigator.platform.includes('Android') || navigator.platform.includes('iOS')
- if ((showCauseOfEmail || showCauseOfPermissions || showCauseOfBackupOptout) && !isMobile)
+ if (showCauseOfEmail || showCauseOfPermissions || showCauseOfBackupOptout) {
+ const isMobile = navigator.platform.includes('Android') || navigator.platform.includes('iOS')
+ if (isMobile) return
+
showModal(permissionsModal, { disableClose: true })
+ return
+ }
+ const key = props.rewardsData?.rewards.extensionKey?.key
+ const used = props.rewardsData?.rewards.extensionKey?.used
+ const rewardsAccountAddr = props.rewardsData?.rewards.accountAddr
+
+ if (!key || used || rewardsAccountAddr !== account.id) return
+
+ const isSeen = extensionInviteCodeModalSeenBy.includes(account.id)
+
+ if (!isSeen) {
+ setExtensionInviteCodeModalSeenBy((prev) => [...prev, account.id])
+ }
+
+ showModal(, {
+ disableClose: !isSeen
+ })
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [
props.accounts,
props.relayerURL,
props.onAddAccount,
props.showThankYouPage,
+ props.rewardsData?.rewards.extensionKey?.key,
+ props.rewardsData?.rewards.extensionKey?.used,
+ props.rewardsData?.rewards.accountAddr,
props.selectedAcc,
arePermissionsLoaded,
isClipboardGranted,
isNoticationsGranted,
modalHidden,
- showModal
+ showModal,
+ setExtensionInviteCodeModalSeenBy
])
- useEffect(() => handlePermissionsModal(), [handlePermissionsModal])
+ useEffect(() => handleDisplayInitialModal(), [handleDisplayInitialModal])
// On pathname change (i.e. navigating to different page), always scroll to top
useEffect(() => {
diff --git a/src/resources/chrome-web-store.svg b/src/resources/chrome-web-store.svg
new file mode 100644
index 0000000000..b21f08cad9
--- /dev/null
+++ b/src/resources/chrome-web-store.svg
@@ -0,0 +1,198 @@
+
\ No newline at end of file
diff --git a/src/resources/icons/copy-new.svg b/src/resources/icons/copy-new.svg
new file mode 100644
index 0000000000..d38fd6e698
--- /dev/null
+++ b/src/resources/icons/copy-new.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/src/resources/logo-new.svg b/src/resources/logo-new.svg
new file mode 100644
index 0000000000..8e43297111
--- /dev/null
+++ b/src/resources/logo-new.svg
@@ -0,0 +1,72 @@
+
\ No newline at end of file
diff --git a/src/resources/logo-v1.svg b/src/resources/logo-v1.svg
new file mode 100644
index 0000000000..4e0db2cd24
--- /dev/null
+++ b/src/resources/logo-v1.svg
@@ -0,0 +1,92 @@
+
\ No newline at end of file