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

Adding the import account feature #9

Merged
merged 13 commits into from
Sep 18, 2024
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import ERC from "./components/main/ERC/ERC.vue";
import { onMounted, ref, onUnmounted } from "vue";
import passwordPage from "./components/passwordPage.vue";
import { useStore } from 'vuex'

const open = ref(false)
const store = useStore()

onMounted(() => {
const isOpen = localStorage.getItem('open-wallet')
Expand All @@ -19,6 +21,7 @@
if (document.hidden) {
open.value = false;
localStorage.setItem('open-wallet', 'false');
store.dispatch('clearPassword')
}
};

Expand Down
6 changes: 5 additions & 1 deletion src/components/passwordPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
<script setup lang="ts">
import { ref, defineEmits, onMounted } from 'vue'
import CryptoJS from 'crypto-js'
import { useStore} from 'vuex'

const store = useStore()
const password = ref('')
const havePassword = ref(false)
const emit = defineEmits(['isOpen'])

const createPassword = () => {
const hashedPassword = CryptoJS.SHA256(password.value).toString()
localStorage.setItem('password', hashedPassword)
localStorage.setItem('open-wallet', 'true')

store.dispatch('addPassword', password.value)
emit('isOpen')
}

Expand All @@ -36,6 +39,7 @@ const submitPassword = () => {
if (hashedInputPassword === storedPassword) {
localStorage.setItem('open-wallet', 'true')
emit('isOpen')
store.dispatch('addPassword', password.value)
} else {
alert('Incorrect password')
password.value = ''
Expand Down
33 changes: 33 additions & 0 deletions src/components/wallet/importCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
export default {
name: 'importCard',
props: {
mnemonicNumber: {
type: Number,
required: true
},
modelValue: {
type: String,
required: true,
default: ''
}
},
emits: ['update:modelValue'],
methods: {
updateValue(event: Event) {
const value = (event.target as HTMLInputElement).value
this.$emit('update:modelValue', value)
}
}
}
</script>

<template>
<input
type="text"
:placeholder="`Word ` + mnemonicNumber"
class="input input-bordered w-full max-w-xs"
:value="modelValue"
@input="updateValue"
/>
</template>
44 changes: 44 additions & 0 deletions src/components/wallet/importPopUp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import importCard from './importCard.vue'

export default {
name: "importPopUp",
components: {
importCard
},
data() {
return {
mnemonicWords: Array(12).fill('')
}
},
mounted() {
this.$el.focus()
},
methods: {
closeWithoutMnemonic() {
this.$emit('close')
},
closeWithMnemonic() {
this.$emit('close', this.mnemonicWords)
}
}
}
</script>

<template>
<div
class="relative bg-background-gray rounded-lg w-2/3 h-2/3 p-6 md:p-8 lg:p-10 flex flex-col z-5000"
@keydown.esc="closeWithoutMnemonic"
tabindex="0"
>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 flex-1">
<importCard
v-for="index in 12"
:key="index"
:mnemonic-number="index"
v-model="mnemonicWords[index - 1]"
/>
</div>
<button @click="closeWithMnemonic" class="btn btn-xs sm:btn-sm md:btn-md lg:btn-lg w-auto self-end bg-blue-500 hover:bg-blue-700">Import</button>
</div>
</template>
2 changes: 1 addition & 1 deletion src/components/wallet/mnemonicPopUp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default {

<template>
<div
class="relative bg-background-gray rounded-lg w-2/3 h-2/3 p-6 md:p-8 lg:p-10 flex flex-col z-10"
class="relative bg-background-gray rounded-lg w-2/3 h-2/3 p-6 md:p-8 lg:p-10 flex flex-col z-5000"
@keydown.esc="$emit('close')"
tabindex="0"
>
Expand Down
111 changes: 85 additions & 26 deletions src/components/wallet/verticalNavbar.vue
Original file line number Diff line number Diff line change
@@ -1,58 +1,106 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { mapGetters, mapActions } from 'vuex';
import walletCard from '@/components/wallet/walletCard.vue';
import mnemonicPopUp from '@/components/wallet/mnemonicPopUp.vue';
import { generateWallet } from '@/utils/wallet';
import type { WalletClient } from 'viem';
import { defineComponent } from 'vue'
import { mapGetters, mapActions } from 'vuex'
import walletCard from '@/components/wallet/walletCard.vue'
import mnemonicPopUp from '@/components/wallet/mnemonicPopUp.vue'
import { generateWallet, generateWalletFromMnemonic } from '@/utils/wallet'
import type { WalletClient } from 'viem'
import importPopUp from './importPopUp.vue'
import { encryption } from '@/utils/crypto'

interface Account {
name: string;
address: string;
mnemonic: string;
wallet: WalletClient;
name: string
address: string
wallet: WalletClient
}

export default defineComponent({
name: 'NavBar',
components: {
walletCard,
mnemonicPopUp,
importPopUp
},
computed: {
...mapGetters(['accounts', 'selectedAccount', 'accountReducedAddresses']),
...mapGetters(['accounts', 'selectedAccount', 'accountReducedAddresses', 'password', 'walletCounter']),
visibleMnemonicAccounts(): Account[] {
return this.accounts.filter(
(account: Account) => this.selectedAccount === account.address && this.isMnemonicVisible
);
)
},
},
data() {
return {
mnemonic: '',
isMnemonicVisible: false,
};
isImportVisible: false
}
},
methods: {
...mapActions(['addAccount', 'selectAccount']),
...mapActions(['addAccount', 'selectAccount', 'initializeAccountsFromLocalStorage']),
addAccountToStore() {
const { newWallet, mnemonic } = generateWallet();
const { newWallet, mnemonic, privateKey } = generateWallet(this.walletCounter)
const newAccount: Account = {
name: `Account ${this.accounts.length + 1}`,
name: `Account ${this.walletCounter + 1}`,
address: newWallet.account.address,
mnemonic: mnemonic,
wallet: newWallet
};
this.addAccount(newAccount);
this.isMnemonicVisible = true;
}
const accountExist = this.accounts.some(
(account: Account) => account.address === newWallet.account.address
)
if (accountExist) {
console.warn("Account already exists")
this.isMnemonicVisible = false
return
}
this.mnemonic = mnemonic
this.addAccount(newAccount)
this.isMnemonicVisible = true
localStorage.setItem(`privateKeyAccount${this.walletCounter}`, encryption(privateKey, this.password))
},
selectAccountInStore(address: string) {
this.selectAccount(address);
this.selectAccount(address)
},
closePopUp() {
this.isMnemonicVisible = false;
closeMnemonicPopUp() {
this.isMnemonicVisible = false
this.mnemonic = ''
},
showImportPopUp() {
this.isImportVisible = true
},
closeImportPopUp(mnemonicWords: string[]) {
if (mnemonicWords && mnemonicWords.every((word) => word !== '')) {
console.log("Importing wallet...")
const { newWallet, privateKey }= generateWalletFromMnemonic(mnemonicWords)
if (newWallet === null) {
this.isImportVisible = false
return
}
const accountExist = this.accounts.some(
(account: Account) => account.address === newWallet.account.address
)
if (accountExist) {
console.warn("Account already exists")
this.isImportVisible = false
return
}
const newAccount: Account = {
name: `Account ${this.walletCounter + 1}`,
address: newWallet.account.address,
wallet: newWallet
}
this.addAccount(newAccount)
localStorage.setItem(`privateKeyAccount${this.walletCounter}`, encryption(privateKey, this.password))
}
this.isImportVisible = false
}
},
});
mounted() {
if (this.password && this.accounts.length === 0) {
this.initializeAccountsFromLocalStorage()
}
}
})
</script>

<template>
Expand All @@ -76,19 +124,30 @@ export default defineComponent({
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
</svg>
</div>
<div @click="showImportPopUp" class="bg-background-gray rounded-2xl p-3 flex items-center justify-center border-gray-600 border hover:bg-gray-600">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M12 2a1 1 0 011 1v10.586l3.293-3.293a1 1 0 011.414 1.414l-5 5a1 1 0 01-1.414 0l-5-5a1 1 0 011.414-1.414L11 13.586V3a1 1 0 011-1z"/>
<path d="M5 18a1 1 0 011-1h12a1 1 0 110 2H6a1 1 0 01-1-1z"/>
</svg>
</div>
</div>
</div>
<div class="flex items-center cursor-pointer p-4 w-full">
<img src="@/components/icons/setting_icon.png" alt="setting icon" class="h-6 w-6 m-4" />
<span class="text-2xl">Settings</span>
</div>
</div>
<div v-if="isImportVisible" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<importPopUp
@close="closeImportPopUp"
/>
</div>
<div v-if="isMnemonicVisible" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<mnemonicPopUp
v-for="account in visibleMnemonicAccounts"
:key="account.address"
:mnemonic="account.mnemonic"
@close="closePopUp"
:mnemonic="mnemonic"
@close="closeMnemonicPopUp"
/>
</div>
</template>
Loading
Loading