Skip to content

Commit

Permalink
feat(wallet): add reset functionality and optimize settings dropdown …
Browse files Browse the repository at this point in the history
…and lock screen display
  • Loading branch information
AricRedemption committed Apr 19, 2024
1 parent 44462f6 commit 98eadd6
Show file tree
Hide file tree
Showing 24 changed files with 171 additions and 148 deletions.
1 change: 1 addition & 0 deletions src/assets/icons-v3/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons-v3/setting.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed src/assets/images/metalet-logo.png
Binary file not shown.
103 changes: 53 additions & 50 deletions src/components/ResetModal.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
<script lang="ts" setup>
import { XMarkIcon } from '@heroicons/vue/20/solid'
import accountManager from '@/lib/account'
import passwordManager from '../lib/password'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/solid'
const router = useRouter()
import useStorage from '@/lib/storage'
import { PASSWORD_KEY } from '@/lib/password'
import { Checkbox } from '@/components/ui/checkbox'
import { XMarkIcon } from '@heroicons/vue/20/solid'
import { V3_WALLETS_STORAGE_KEY } from '@/lib/wallet'
const props = defineProps<{
defineProps<{
show: boolean
}>()
const emit = defineEmits(['update:show'])
const isCovered = ref(true)
const passwordInputType = computed(() => (isCovered.value ? 'password' : 'text'))
const password = ref('')
const failed = ref(false)
const storage = useStorage()
const disconnect = async () => {
// 检查密码是否正确
const isCorrect = await passwordManager.check(password.value)
if (!isCorrect) {
failed.value = true
return
}
const emit = defineEmits(['update:show'])
const resetText = ref('')
const checked1 = ref(false)
const checked2 = ref(false)
const checked3 = ref(false)
await accountManager.removeCurrent()
const checkedAll = computed(() => checked1.value && checked2.value && checked3.value && resetText.value === 'RESET')
const disconnect = async () => {
emit('update:show', false)
// 返回账号列表页
router.push('/accounts')
await storage.delete(PASSWORD_KEY)
await storage.delete(V3_WALLETS_STORAGE_KEY)
window.location.reload()
}
</script>

Expand All @@ -40,48 +35,56 @@ const disconnect = async () => {
<div class="fixed inset-0 isolate z-50 flex items-end bg-black/20 backdrop-blur-sm" v-show="show">
<div class="grow rounded-t-xl bg-white p-4 pb-8">
<div class="flex items-center justify-between border-b border-gray-100 pb-4">
<span class="text-base">Reset Account</span>
<span class="text-base">Reset Wallet</span>
<button class="text-gray-400 hover:text-gray-500" @click="$emit('update:show', false)" type="button">
<XMarkIcon class="h-5 w-5" />
</button>
</div>

<div class="pt-4">
<p class="text-sm text-gray-500">
This action will reset your current account from Metalet. Make sure you have your seed phrase backed up.
</p>
<p class="mt-1 text-sm text-gray-500">You have to enter your password to confirm this action.</p>

<div class="relative mt-4">
<input
:type="passwordInputType"
class="w-full rounded-md border bg-gray-100 p-4 pr-12 text-sm text-gray-700"
:class="failed ? 'border-red-500' : 'border-transparent'"
v-model="password"
placeholder="Password"
/>

<div class="absolute right-0 top-0 flex h-full items-center pr-4">
<button @click="isCovered = !isCovered">
<EyeIcon v-if="isCovered" class="h-5 w-5 text-gray-400 transition hover:text-blue-500" />
<EyeSlashIcon v-else class="h-5 w-5 text-gray-400 transition hover:text-blue-500" />
</button>
<div class="pt-4 space-y-4">
<div class="flex flex-col gap-2 text-ss">
<div class="p-3.5 bg-gray-secondary rounded-lg flex items-center gap-x-3">
<Checkbox id="tips1" v-model:checked="checked1" />
<p>We will not store or help you retrieve your password.</p>
</div>
<div class="p-3.5 bg-gray-secondary rounded-lg flex items-center gap-x-3">
<Checkbox id="tips1" v-model:checked="checked2" />
<p>
If you forget your password, you can reset your wallet. It is also possible to re-import the wallet
using a mnemonic phrase or private key.
</p>
</div>
<div class="p-3.5 bg-gray-secondary rounded-lg flex items-center gap-x-3">
<Checkbox id="tips1" v-model:checked="checked3" />
<p>
Note that resetting the wallet without backup will permanently lose all assets. Before resetting, be
sure to back up all wallets.
</p>
</div>
</div>

<div class="mt-2" v-if="failed">
<p class="text-sm text-red-500" v-if="failed">Password is incorrect</p>
<div class="space-y-2">
<p>
To confirm wallet reset, please enter:
<span class="text-red-500">RESET</span>
</p>
<input
type="text"
class="w-full rounded-lg focus:outline-blue-primary border p-4 text-sm"
v-model="resetText"
placeholder="RESET"
/>
</div>

<div class="mt-8 grid grid-cols-2 gap-x-4">
<div class="grid grid-cols-2 gap-x-4">
<button class="reset-button border-gray-700 text-gray-700" @click="$emit('update:show', false)">
Cancel
</button>
<button
class="reset-button border-red-600 bg-red-600 text-red-50"
:class="[password.length ? 'cursor-pointer' : 'cursor-not-allowed opacity-50 saturate-50']"
@click="disconnect"
:disabled="!password.length"
:disabled="!checkedAll"
class="reset-button border-red-600 bg-red-600 text-red-50"
:class="[checkedAll ? 'cursor-pointer' : 'cursor-not-allowed opacity-50 saturate-50']"
>
Reset
</button>
Expand Down
38 changes: 16 additions & 22 deletions src/components/headers/SettingMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import { ref } from 'vue'
import password from '@/lib/password'
import { useRouter } from 'vue-router'
import ResetModal from '../ResetModal.vue'
import SettingIcon from '@/assets/icons/setting-v3.svg'
import LockIcon from '@/assets/icons-v3/lock.svg'
import ResetModal from '@/components/ResetModal.vue'
import SettingIcon from '@/assets/icons-v3/setting.svg'
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
const router = useRouter()
Expand All @@ -12,48 +13,41 @@ const hasPassword = ref(false)
password.has().then((has) => {
hasPassword.value = has
})
const lock = async () => {
await password.lock()
router.push('/lock')
}
const setPassword = () => {
router.push('/wallet/set-password')
}
const showResetModal = ref(false)
const toAccountList = () => {
router.push('/accounts')
}
const toSetting = () => {
router.push('/settings')
}
</script>

<template>
<Menu as="div" class="relative z-[1] transition-all duration-200">
<MenuButton class="relative flex items-center gap-x-0.5 py-1 hover:text-blue-primary">
<SettingIcon class="cursor-pointer" />
<SettingIcon class="cursor-pointer w-[22px]" />
</MenuButton>

<MenuItems class="absolute right-0 rounded-md border border-gray-100 bg-white text-xs shadow-md">
<MenuItem>
<button class="menu-item" @click="toSetting">Setting</button>
<router-link to="/settings" class="menu-item">
<SettingIcon class="w-4.5" />
<span>Setting</span>
</router-link>
</MenuItem>

<MenuItem>
<button class="menu-item" @click="lock" v-if="hasPassword">Lock</button>
<button class="menu-item" @click="setPassword" v-else>Set Password</button>
<router-link to="/lock" class="menu-item" v-if="hasPassword">
<LockIcon class="w-4.5" />
<span>Lock</span>
</router-link>
<router-link to="/wallet/set-password" class="menu-item" v-else>Set Password</router-link>
</MenuItem>

<MenuItem>
<MenuItem v-if="false">
<button class="menu-item" @click="toAccountList">Add / Switch Account</button>
</MenuItem>

<!-- disconnect button -->
<MenuItem v-if="hasPassword">
<MenuItem v-if="hasPassword && false">
<button class="menu-item" @click="showResetModal = true">Reset Account</button>
</MenuItem>
</MenuItems>
Expand All @@ -64,7 +58,7 @@ const toSetting = () => {

<style scoped lang="css">
.menu-item {
@apply w-full whitespace-nowrap rounded-inherit p-4 text-left capitalize hover:bg-gray-50 hover:text-blue-500;
@apply flex items-center gap-x-3 w-full whitespace-nowrap rounded-inherit p-4 text-left capitalize hover:bg-gray-50 hover:text-blue-500;
}
.reset-button {
Expand Down
2 changes: 1 addition & 1 deletion src/components/headers/TheHeader.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { useRoute } from 'vue-router'
import MetaletLogoImg from '@/assets/images/metalet-logo.png?url'
import MetaletLogoImg from '@/assets/images/metalet-logo-v3.svg?url'
import MetaletWordImg from '@/assets/images/metalet-word.svg?url'
import AccountMenu from './AccountMenu.vue'
import ServiceMenu from './ServiceMenu.vue'
Expand Down
2 changes: 1 addition & 1 deletion src/data/logos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type Chain } from '@/lib/types'
import BtcLogoImg from '../assets/images/btc-logo.svg?url'
import SpaceLogoImg from '../assets/icons-v3/space.svg?url'

import MetaletLogo from '../assets/images/metalet-logo.png?url'
import MetaletLogo from '../assets/images/metalet-logo-v3.svg?url'
import MvcSwapLogo from '../assets/images/mvcswap-logo.png?url'
import MVCUSDTLogo from '../assets/images/usdt-logo.jpg?url'
import ShowCoinLogo from '../assets/images/sc-logo.svg?url'
Expand Down
4 changes: 4 additions & 0 deletions src/lib/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const prettifyTxId = (txId: string, useDigits = 6) => {
return `${txId.slice(0, useDigits)}...${txId.slice(-useDigits)}`
}

export const prettifyAddress = (txId: string, useDigits = 6) => {
return `${txId.slice(0, useDigits)}...${txId.slice(-useDigits)}`
}

export const prettifyBalance = (balance: number, symbol: string = 'SPACE'): string => {
if (!balance) return `0 ${symbol}`

Expand Down
6 changes: 3 additions & 3 deletions src/lib/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import useStorage from './storage'
import { notifyBg } from './notify-bg'

const Locked_Key = 'locked'
const Password_Key = 'password'
export const PASSWORD_KEY = 'password'

const storage = useStorage()

export async function getPassword() {
return await storage.get(Password_Key)
return await storage.get(PASSWORD_KEY)
}

export async function hasPassword() {
Expand All @@ -24,7 +24,7 @@ export async function checkPassword(credential: string) {
export async function setPassword(password: string) {
// const hashed = hash(password)
const hashed = CryptoJS.SHA256(password).toString()
await storage.set(Password_Key, hashed)
await storage.set(PASSWORD_KEY, hashed)
}

export async function lock() {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { toast } from '@/components/ui/toast'
import { getCurrentAccountId } from './account'

const CURRENT_WALLET_ID = 'currentWalletId'
const V3_WALLETS_STORAGE_KEY = 'wallets_v3'
export const V3_WALLETS_STORAGE_KEY = 'wallets_v3'

const storage = useStorage()

Expand Down
47 changes: 25 additions & 22 deletions src/pages/lock/Index.vue
Original file line number Diff line number Diff line change
@@ -1,70 +1,73 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import MetaletLogoImg from '@/assets/images/metalet-logo.png?url'
import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/solid'
import passwordManager from '@/lib/password'
import { useRouter } from 'vue-router'
import passwordManager from '@/lib/password'
import { setLastLockTime } from '@/lib/lock'
import ResetModal from '@/components/ResetModal.vue'
import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/solid'
import MetaletLogo from '@/assets/images/metalet-logo-v3.svg?url'
const password = ref('')
const router = useRouter()
const isCovered = ref(true)
const showResetModal = ref(false)
const passwordInputType = computed(() => (isCovered.value ? 'password' : 'text'))
const failed = ref(false)
const tryUnlock = async () => {
// 检验输入的密码是否正确
const isCorrect = await passwordManager.check(password.value)
if (isCorrect) {
// 如果正确,解锁
await passwordManager.unlock(password.value)
await setLastLockTime()
// 跳转到钱包页面
router.push('/wallet')
} else {
// 如果不正确,提示密码错误
failed.value = true
}
}
</script>

<template>
<div class="mt-12">
<div>
<img class="mx-auto h-20 w-20" :src="MetaletLogoImg" alt="metalet-logo" />
</div>
<div class="pt-20">
<img class="mx-auto w-[130px]" :src="MetaletLogo" alt="metalet-logo" />

<div class="mt-4 text-center">
<div class="mt-5 text-center">
<h1 class="text-3xl font-extrabold">Metalet</h1>
<p class="mt-2 text-lg text-gray-400">Welcome Back</p>
<p class="mt-2 text-sm text-gray-primary">Welcome Back</p>
</div>

<div class="mt-12">
<h4 class="mb-2 text-sm">Password</h4>
<div class="relative">
<input
:type="passwordInputType"
class="w-full rounded-md border bg-gray-100 p-4 pr-12 text-sm text-gray-700"
:class="failed ? 'border-red-500' : 'border-transparent'"
v-model="password"
:type="passwordInputType"
:class="[
'block w-full rounded-md border border-gray-soft outline-blue-primary p-4 pr-12',
{ 'border-red-500': failed },
]"
/>
<div class="absolute right-0 top-0 flex h-full items-center pr-4">
<button @click="isCovered = !isCovered">
<EyeIcon v-if="isCovered" class="h-5 w-5 text-gray-400 transition hover:text-blue-500" />
<EyeSlashIcon v-else class="h-5 w-5 text-gray-400 transition hover:text-blue-500" />
</button>
</div>
<p v-if="failed" class="absolute -bottom-8 left-0 text-sm text-red-500">Incorrect password. Try again.</p>
</div>
</div>

<div class="mt-4">
<button class="gradient-bg w-full rounded-md py-4 text-base leading-none text-white" @click="tryUnlock">
<div class="mt-14 flex flex-col items-center justify-center">
<button
@click="tryUnlock"
:class="[
'bg-blue-primary w-61.5 rounded-3xl py-4 text-ss text-white',
{ 'opacity-50 cursor-not-allowed': !password },
]"
>
Unlock
</button>
<button @click="showResetModal = true" class="mt-4 text-ss text-gray-primary">Forget password?</button>
</div>

<div class="mt-2" v-if="failed">
<p class="text-sm text-red-500">Password is incorrect</p>
</div>
<ResetModal v-model:show="showResetModal" />
</div>
</template>
2 changes: 1 addition & 1 deletion src/pages/migrate/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { mvc } from 'meta-contract'
import { useRouter } from 'vue-router'
import { setNetwork } from '@/lib/network'
import MetaletLogoImg from '@/assets/images/metalet-logo.png?url'
import MetaletLogoImg from '@/assets/images/metalet-logo-v3.svg?url'
import { deriveAllAddresses, type AddressType } from '@/lib/bip32-deriver'
import { addAccount, getAccounts, getLegacyAccounts } from '@/lib/account'
Expand Down
Loading

0 comments on commit 98eadd6

Please sign in to comment.